UNPKG

brella-transition-bin

Version:
203 lines (202 loc) 9 kB
#!/usr/bin/env node "use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const fs = __importStar(require("fs")); const commander_1 = require("commander"); const sanitize_filename_1 = __importDefault(require("sanitize-filename")); const process_1 = require("process"); const os_1 = require("os"); const path_1 = __importDefault(require("path")); const brella_transition_1 = __importDefault(require("brella-transition")); const import_1 = __importDefault(require("@brillout/import")); const skia_canvas_1 = require("skia-canvas"); const crypto_1 = require("crypto"); commander_1.program .option("-W, --width <number>", "width of the canvas", "1920") .option("-H, --height <number>", "height of the canvas", "1080") .option("-o, --output <string>", "name of output file", "brella.webm") .option("--output-image", "output every frame as an image file") .option("--output-dir <string>", "directory to output image files to") .option("--force-output-dir", "output to directory even if the directory already exists") .option("--brella <number>", "maximum amount of brella", "30") .option("--ribs <numbers>", "possible number of ribs, separated by commas", "6,8") .option("--retries <number>", "maximum retries before choosing to overlap, -1 to allow indefinite retries", "1000000") .option("-r, --fps <number>", "framerate of the transition", "60") .option("--attack <number>", "frames of brella opening/closing", "15") .option("--hold <number>", "frames of brella staying opened", "30") .option("--rotate <number>", "radian angle to apply to the brella every frame", "0.01") .option("-h, --hue <numbers>", "HUE angle range in degrees, separated by comma", "0,360") .option("-s, --saturation <numbers>", "saturation range in percentage, separated by comma", "80,100") .option("-l, --lightness <numbers>", "lightness range in percentage, separated by comma", "50,50"); const options = commander_1.program.parse().opts(); let attack = parseInt(options.attack); if (isNaN(attack) || attack < 0) { console.log("Attack must be a positive integer"); (0, process_1.exit)(1); } let hold = parseInt(options.hold); if (isNaN(hold) || hold < 0) { console.log("Hold must be a positive integer"); (0, process_1.exit)(1); } if (!attack && !hold) { console.log("One of attack or hold must be positive"); (0, process_1.exit)(1); } let rotate = parseFloat(options.rotate); if (isNaN(rotate)) { console.log("Rotate must be a number"); (0, process_1.exit)(1); } const ribs = options.ribs.split(",").map((x) => parseInt(x)); if (ribs.some(x => isNaN(x) || x < 3)) { console.log("Ribs must be numbers >= 3"); (0, process_1.exit)(1); } let hue = options.hue.split(",").map((x) => parseInt(x)); if (!hue.length || hue.some(x => isNaN(x))) { console.log("HUE angle range must be numbers"); (0, process_1.exit)(1); } hue = hue.map(x => x == 360 || x == -360 ? x : (x < 0 ? (x % 360) + 360 : x % 360)).sort(); let saturation = options.saturation.split(",").map((x) => parseInt(x)); if (!saturation.length || saturation.some(x => isNaN(x))) { console.log("Saturation range must be numbers"); (0, process_1.exit)(1); } saturation = saturation.map(x => x < 0 ? 0 : (x > 100 ? 100 : x)).sort(); let lightness = options.lightness.split(",").map((x) => parseInt(x)); if (!lightness.length || lightness.some(x => isNaN(x))) { console.log("Lightness range must be numbers"); (0, process_1.exit)(1); } lightness = lightness.map(x => x < 0 ? 0 : (x > 100 ? 100 : x)).sort(); const outName = (0, sanitize_filename_1.default)(options.output); if (outName != options.output) { console.log("Output file name is not valid"); (0, process_1.exit)(1); } const fps = parseInt(options.fps); if (isNaN(fps)) { console.log("Frame rate must be a number"); (0, process_1.exit)(1); } if (options.outputImage && options.outputDir && fs.existsSync(options.outputDir) && !options.forceOutputDir) { console.log(`Output directory ${options.outputDir} already exists`); (0, process_1.exit)(1); } let tmpDir; if (options.outputImage) { if (options.outputDir) { fs.mkdirSync(options.outputDir, { recursive: true }); tmpDir = options.outputDir; } else tmpDir = fs.mkdtempSync(path_1.default.join((0, os_1.tmpdir)(), "brella-")); } (0, import_1.default)("ffmpeg-stream").then((_a) => __awaiter(void 0, [_a], void 0, function* ({ Converter }) { const converter = new Converter(); const converterInput = converter.createInputStream({ r: fps, f: "image2pipe" }); let realOutName = outName; const outNameArr = outName.split("."); while (fs.existsSync(realOutName)) { outNameArr[outNameArr.length - 1] = Array.from((0, crypto_1.randomBytes)(4)).map(byte => byte.toString(16).padStart(2, "0")).join(""); realOutName = outNameArr.join(".") + ".webm"; } if (realOutName != outName) console.log(`${outName} already exists. Will instead output to ${realOutName}`); converter.createOutputToFile(realOutName, { lossless: 1, vcodec: "libvpx-vp9", pix_fmt: "yuva420p" }); const converting = converter.run(); const canvas = new skia_canvas_1.Canvas(parseInt(options.width), parseInt(options.height)); const ctx = canvas.getContext("2d"); const transition = new brella_transition_1.default({ brellaMax: parseInt(options.brella), brellaRetries: parseInt(options.retries), brellaRibs: ribs, frameAttack: attack, frameHold: hold, frameRotate: rotate, colorHue: hue, colorSaturation: saturation, colorLightness: lightness }); transition.activate(); const pad = Math.floor(Math.log10(transition.estimatedFrames)); let counter = 0; // Keep drawing if some brellas are still active while (transition.isActive()) { // Clear the canvas ctx.clearRect(0, 0, canvas.width, canvas.height); // Draw the frame transition.render(ctx); // Get the PNG stream const png = yield canvas.png; // Write each frame to a PNG file if (tmpDir) { const name = counter.toString().padStart(4, "0"); fs.writeFile(`${tmpDir}/frame-${name}.png`, png, (err) => { if (err) console.error(err); }); } // Pipe frame to FFMpeg converter yield new Promise((res, rej) => { converterInput.write(png, (err) => { if (err) rej(err); else res(); }); }); counter++; process.stdout.clearLine(0); process.stdout.cursorTo(0); process.stdout.write(`[${Math.floor(counter * 100 / transition.estimatedFrames).toString().padStart(3, "0")}%] Rendered ${counter.toString().padStart(pad, "0")} / ${transition.estimatedFrames} frames`); } process.stdout.write("\n"); converterInput.end(); console.log("Finalizing output..."); yield converting; }));