Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

canvideo

Package Overview
Dependencies
Maintainers
1
Versions
24
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

canvideo - npm Package Compare versions

Comparing version 2.0.0 to 2.1.0

examples/res/frame0.jpg

24

examples/shapes.js
const canvideo = require("../index");
const path = require('path');
var animation = new canvideo.Animation();
canvideo.setTempPath("C://Users/username/Desktop/Trash")
canvideo.setTempPath(path.join(__dirname, "./res"));
var animation = new canvideo.Animation({ width: 200, height: 200 }, 4);
animation.add(new canvideo.Rectangle(50, 50, 100, 100, "cyan"));
animation.add(new canvideo.Rectangle(100, 100, 100, 100, "magenta"));
animation
.addKeyframe(new canvideo.Keyframe(0.25)
.addShape(new canvideo.Rectangle(0, 0, 100, 100, "blue"))
)
.addKeyframe(new canvideo.Keyframe(0.5)
.addShape(new canvideo.Rectangle(50, 50, 100, 100, "cyan"))
)
.addKeyframe(new canvideo.Keyframe(0.75)
.addShape(new canvideo.Rectangle(100, 100, 100, 100, "white"))
)
animation.on("end", () => {
animation.on("done", () => {
console.log("done")
});
animation.on("error", () => {
console.log("error!");
});
animation.export("C://Users/username/Desktop/Trash/output.mp4");
animation.export(path.join(__dirname, "./res/output.mp4"));
import fs = require('fs');
import ffmpeg = require('fluent-ffmpeg');
import events = require('events');
import canvas = require('canvas');
declare namespace canvideo {
interface CodecData {
format: string,
audio: string,
video: string,
duration: string,
video_details: Array<string>
type colorRepresentor = string | [number, number, number];
type color = canvideo.Color | colorRepresentor;
type animationSize = AnimationSize | AnimationSizeShort;
type evenNumber = number;
interface AnimationSize {
width: evenNumber;
height: evenNumber;
}
interface Progress {
frames: number,
currentFps: number,
currentKbps: number,
targetSize: 2,
timemark: string,
percent: number
interface AnimationSizeShort {
w: evenNumber;
h: evenNumber;
}
interface AnimationOptions {
size: animationSize;
fps: number;
}
interface AnimationOptionsAll implements animationSize {
fps: number;
}
type color = canvideo.Color | string | [number, number, number];
export function setTempPath(path: fs.PathLike): void;
export class Color {
constructor(color: color): this;
constructor(color: colorRepresentor): this;
color: number;
value: number;

@@ -33,8 +37,10 @@ static isValid(color: any): boolean;

export class Shape extends Color {
export abstract class Shape {
constructor(color: color): this;
color: Color;
}
export class Rectangle extends Shape {
constructor(x: number, y: number, width: number, height: number, color?: color): Rectangle;
constructor(x: number, y: number, width: number, height: number, color?: color): this;

@@ -45,17 +51,51 @@ x: number;

height: number;
draw(ctx: canvas.CanvasRenderingContext2D): void;
}
export class Keyframe {
constructor(startTime: number): this;
shapes: Array<Shape>;
animation: Animation;
frameNumber: number;
addShape(shape: Shape): this;
render(shapes: Array<Shape>): void;
}
export interface AnimationAfterExport {
on(event: "done", handler: () => void): this;
on(event: "error", handler: () => void): this;
}
export abstract class AnimationAfterExport {
keyframes: Array<Keyframe>;
tempPath: fs.PathLike;
width: evenNumber;
height: evenNumber;
fps: number;
get spf(): number;
}
export interface Animation {
on(event: 'start', handler: (commandLine?: string) => void): this;
on(event: 'codecData', handler: (data?: CodecData) => void): this;
on(event: 'progress', handler: (progress?: Progress) => void): this;
on(event: 'stderr', handler: (stderr?: string) => void): this;
on(event: 'error', handler: (err?: Error, stdout?: string, stderr?: string) => void): this;
on(event: 'end', handler: (stdout?: string, stderr?: string) => void): this;
on(event: "done", handler: () => void): this;
on(event: "error", handler: () => void): this;
}
export class Animation extends ffmpeg {
constructor(): Animation;
export class Animation extends events.EventEmitter {
constructor(width: evenNumber, height: evenNumber, fps: number): this;
constructor(size: animationSize, fps: number): this;
constructor(options: AnimationOptions): this;
constructor(options: AnimationOptionsAll): this;
add(shape: Shape): this;
export(filePath: fs.PathLike, tempPath?: fs.PathLike): this;
keyframes: Array<Keyframe>;
tempPath: fs.PathLike;
width: evenNumber;
height: evenNumber;
fps: number;
get spf(): number;
addKeyFrame(keyframe: Keyframe): this;
export(filePath: fs.PathLike): AnimationAfterExport;
}

@@ -62,0 +102,0 @@ }

@@ -8,2 +8,3 @@ //Main file

const path = require('path');
const EventEmitter = require('events').EventEmitter;

@@ -15,2 +16,6 @@ //Npm Modules

//My Modules
const AsyncLoop = require("./lib/asyncLoop");
const myMath = require("./lib/myMath");
//Set ffmpeg path

@@ -45,3 +50,3 @@ ffmpeg.setFfmpegPath(ffmpegPath);

if (canvideo.Color.isValid(color)) {
this.color = color;
this.value = color;
}

@@ -92,8 +97,47 @@ else {

//Shape
canvideo.Shape = class extends canvideo.Color {
canvideo.Shape = class {
constructor(color) {
super(color);
if (color instanceof canvideo.Color) {
this.color = color;
}
else {
this.color = new canvideo.Color(color);
}
};
}
//Control Point
canvideo.ControlPoint = class {
static defaultSetterX(value) {
this._x = value;
}
static defaultSetterY(value) {
this._y = value;
}
constructor(shape, setterX = defaultSetterX, setterY = defaultSetterY) {
if (shape instanceof canvideo.Shape) {
this.shape = shape;
this.setterX = setterX;
this.setterY = setterY;
this._x = 0;
this._y = 0;
}
else {
throw new TypeError(`Shape: ${shape} is not of type Shape`);
}
}
set x(value) {
return this.setterX(value);
}
get x() {
return this._x;
}
set y(value) {
return this.setterY(value);
}
get y() {
return this._y;
}
}
//Rectangle

@@ -128,58 +172,268 @@ canvideo.Rectangle = class extends canvideo.Shape {

}
draw(ctx) {
ctx.fillStyle = this.color.value;
ctx.fillRect(this.x, this.y, this.width, this.height);
}
}
//Animation class
canvideo.Animation = class extends ffmpeg {
constructor() {
super();
//Frame Class
canvideo.Keyframe = class {
constructor(startTime) {
if (typeof startTime == 'number') {
this.startTime = startTime;
}
else {
throw new TypeError("startTime must be a number.");
}
this.shapes = [];
}
set animation(value) {
if (value instanceof canvideo.Animation) {
this._animation = value;
}
else {
throw new TypeError("Animation is not of type Animation.");
}
}
get animation() {
return this._animation;
}
set frameNumber(value) {
if (typeof value == 'number') {
this._frameNumber = value;
}
else {
throw new TypeError("frameNumber must be a number.");
}
}
get frameNumber() {
return this._frameNumber;
}
add(shape) {
if (shape instanceof canvideo.Rectangle) {
addShape(shape) {
if (shape instanceof canvideo.Shape) {
this.shapes.push(shape);
}
else {
throw new TypeError("shape is not of type Rectangle");
throw new TypeError("shape is not of type Shape.");
}
return this;
}
render(shapes = []) {
if (typeof this.frameNumber == 'number') {
if (shapes instanceof Array) {
for (var i = 0; i < shapes.length; i++) {
if (!(shapes[i] instanceof canvideo.Shape)) {
throw new TypeError(`shapes[${i}] is not a Shape.`);
}
}
}
else {
throw new TypeError("Shapes must be an array of shapes.");
}
export(filePath, tempPath = config.tempPath) {
if (typeof filePath == 'string' || filePath instanceof Buffer || filePath instanceof URL) {
if (path.extname(filePath) !== ".mp4") {
throw new URIError(`File path: ${filePath} must have the extension .mp4.`);
var shapesToRender = shapes.concat(this.shapes);
this.animation.loop.goal += 1;
//Render frame 0
var canvas = createCanvas(this.animation.width, this.animation.height);
var ctx = canvas.getContext('2d');
for (var i = 0; i < shapesToRender.length; i++) {
shapesToRender[i].draw(ctx);
}
var framePath = this.animation.tempPath + "/frame" + this.frameNumber + ".jpg";
canvas.createJPEGStream()
.on("end", () => {
this.animation.loop.emit("result", false);
})
.on("error", err => {
this.animation.loop.emit("result", err, this.frameNumber);
})
.pipe(fs.createWriteStream(framePath));
//Render next keyframe
if (this.frameNumber + 1 < this.animation.keyframes.length) {
this.animation.keyframes[this.frameNumber + 1].render(shapesToRender);
}
else {
//This is the last frame
}
return this;
}
else {
throw new TypeError(`File path: ${filePath} is not a valid path type.`);
throw new TypeError("this.frameNumber is not a number");
}
if (typeof tempPath == 'string' || tempPath instanceof Buffer || tempPath instanceof URL) {
}
}
//Animation class
canvideo.Animation = class extends EventEmitter {
constructor(arg1 = { width: 200, height: 200 }, arg2, arg3) {
//Constructor: width: number, height: number, fps
//Constructor: { width: number, height: number }, fps
//Constructor: { w: number, h: number }, fps
//Constructor: { width: number, height: number, fps: number}
//Constructor: { w: number, h: number, fps: number}
//Constructor: { size: { width: number, height: number }, fps: number }
//Constructor: { size: { w: number, h: number }, fps: number }
var width, height, fps;
function typeCheck(arg1, arg2, arg3) {
if (typeof arg1 == 'number' && typeof arg2 == 'number' && typeof arg3 == 'number') {
width = arg1, height = arg2, fps = arg3;
}
else if (typeof arg1 == 'object' && typeof arg2 == 'number' && typeof arg3 == 'undefined') {
fps = arg2;
if (typeof arg1.width == 'number' && typeof arg1.height == 'number') {
width = arg1.width, height = arg1.height;
}
else if (typeof arg1.w == 'number' && typeof arg1.h == 'number') {
width = arg1.w, height = arg1.h;
}
else {
return false;
}
}
else if (typeof arg1 == 'object' && typeof arg1.fps == 'number' && typeof arg2 == 'undefined' && typeof arg3 == 'undefined') {
fps = arg1.fps;
if (typeof arg1.size == 'object') {
if (typeof arg1.size.width == 'number' && typeof arg1.size.height == 'number') {
width = arg1.size.width, height = arg1.size.height;
}
else if (typeof arg1.size.w == 'number' && typeof arg1.size.h == 'number') {
width = arg1.size.w, height = arg1.size.h;
}
else {
return false;
}
}
else {
if (typeof arg1.width == 'number' && typeof arg1.height == 'number') {
width = arg1.width, height = arg1.height;
}
else if (typeof arg1.w == 'number' && typeof arg1.h == 'number') {
width = arg1.w, height = arg1.h;
}
else {
return false;
}
}
}
else {
console.log("none")
return false;
}
}
if (typeCheck(arg1, arg2, arg3) === false) {
throw new TypeError("Invalid constructor.");
}
if (myMath.isOdd(width)) {
throw new TypeError("width must be an even number.");
}
if (myMath.isOdd(height)) {
throw new TypeError("height must be an even number.");
}
super();
this.keyframes = [];
this.tempPath = config.tempPath;
this.loop = new AsyncLoop();
this.width = width;
this.height = height;
this.fps = fps;
this.exported = false;
this.command = ffmpeg();
this.command.on('end', () => {
this.emit("done");
});
this.command.on('error', () => {
this.emit("error");
});
}
set tempPath(value) {
if (this.exported) {
throw new SyntaxError("Cannot change tempPath after exporting.");
}
if (typeof value == 'string' || value instanceof Buffer || value instanceof URL) {
//Make sure it exists
if (!fs.existsSync(tempPath)) {
throw new URIError(`Path: ${tempPath} does not exist.`);
if (fs.existsSync(value)) {
this._tempPath = value;
}
else {
throw new URIError(`Path: ${value} does not exist.`);
}
}
else {
throw new TypeError(`Path: ${tempPath} is not a valid path type.`);
throw new TypeError(`Path: ${value} is not a valid path type.`);
}
}
get tempPath() {
return this._tempPath;
}
get spf() {
return 1 / this.fps;
}
//Create the canvas
var canvas = createCanvas(200, 200);
var ctx = canvas.getContext('2d');
addKeyframe(keyframe) {
if (this.exported) {
throw new SyntaxError("Cannot add keyframe after exporting.");
}
if (keyframe instanceof canvideo.Keyframe) {
var frameNumber = Math.round(keyframe.startTime * this.fps);
keyframe.animation = this;
keyframe.frameNumber = frameNumber;
this.keyframes[frameNumber] = keyframe;
}
else {
throw new TypeError("keyframe is not of type Keyframe.");
}
for (var i = 0; i < this.shapes.length; i++) {
let { x, y, width, height, color } = this.shapes[i];
ctx.fillStyle = color;
ctx.fillRect(x, y, width, height);
return this;
}
export(filePath) {
if (this.exported) {
throw new SyntaxError("Cannot export twice.");
}
if (typeof filePath == 'string' || filePath instanceof Buffer || filePath instanceof URL) {
if (path.extname(filePath) !== ".mp4") {
throw new URIError(`File path: ${filePath} must have the extension .mp4.`);
}
}
else {
throw new TypeError(`File path: ${filePath} is not a valid path type.`);
}
//Make sure there is a keyframe at 0 seconds
if (!(this.keyframes[0] instanceof canvideo.Keyframe)) {
this.addKeyframe(new canvideo.Keyframe(0));
}
//Save the canvas
canvas.createJPEGStream().pipe(fs.createWriteStream(config.tempPath + "/frame0.jpg"));
//Fill in the blank frames
for (var i = 0; i < this.keyframes.length; i++) {
if (!(this.keyframes[i] instanceof canvideo.Keyframe)) {
this.addKeyframe(new canvideo.Keyframe(i * this.spf));
}
}
this.input(config.tempPath + "/frame%1d.jpg")
.inputFPS(1)
.save(filePath)
.outputFPS(1);
//Render the first frame
this.keyframes[0].render();
this.loop.on("done", errors => {
if (!errors) {
this.command
.input(config.tempPath + "/frame%1d.jpg")
.inputFPS(this.fps)
.save(filePath)
.outputFPS(this.fps);
}
else {
this.emit("error");
}
});
this.exported = true;
return this;

@@ -186,0 +440,0 @@ }

{
"name": "canvideo",
"version": "2.0.0",
"version": "2.1.0",
"description": "\u0016\u0016An open-source tool for Node.js that can make animations and generate videos.",

@@ -5,0 +5,0 @@ "main": "index.js",

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc