diff --git a/dist/script/index.js b/dist/script/index.js index 1f4043c..eb6e440 100644 --- a/dist/script/index.js +++ b/dist/script/index.js @@ -420,6 +420,9 @@ class StepRenderer extends AbstractRenderer { .inputOptions([ '-f concat', // concat frames from the frame list '-safe 0' // to prevent errors related to unsafe filenames + ]) + .outputOptions([ + `-vf fps=fps=${this.args.fps}`, // Framerate ]) .on('progress', (progress) => { statsPrinter.print(progress); @@ -509,10 +512,37 @@ class RealTimeRecorder extends AbstractRecorder { } } +class FPSTicker { + interval; + lastTime = 0; + onTick = () => { }; + timeoutId = null; + constructor(fps) { + this.interval = 1000 / fps; + } + start(onTick = () => { }) { + this.onTick = onTick; + this.lastTime = Date.now(); + this.tick(); + } + stop() { + clearTimeout(this.timeoutId); + } + tick() { + const now = Date.now(); + const deltaTime = now - this.lastTime; + if (deltaTime >= this.interval) { + this.lastTime = now - (deltaTime % this.interval); // Adjust for drift + this.onTick(deltaTime); + } + this.timeoutId = setTimeout(() => this.tick(), this.interval - (Date.now() - this.lastTime)); + } +} + class RealTimeRenderer extends AbstractRenderer { inputStream = null; - intervalId = null; lastFrame; + ticker; constructor(args) { super(args); const empty = new pngjs.PNG({ @@ -521,6 +551,7 @@ class RealTimeRenderer extends AbstractRenderer { colorType: 6, }); this.lastFrame = pngjs.PNG.sync.write(empty); + this.ticker = new FPSTicker(args.fps); } startEncoding() { this.inputStream = new stream.PassThrough(); @@ -532,6 +563,9 @@ class RealTimeRenderer extends AbstractRenderer { '-pix_fmt yuva444p10le', // Lossless setting `-s ${this.args.videoWidth}x${this.args.videoHeight}`, // Frame size `-r ${this.args.fps}`, // Framerate + ]) + .outputOptions([ + `-vf fps=fps=${this.args.fps}`, // Framerate ]) .on('start', () => { console.log('FFmpeg process started.'); @@ -547,16 +581,15 @@ class RealTimeRenderer extends AbstractRenderer { }); command.run(); // Produce frames in required rate - const intervalDuration = Math.round(1000 / 30); - this.intervalId = setInterval(() => { + this.ticker.start(() => { this.inputStream.write(this.lastFrame); - }, intervalDuration); + }); } addFrame(frame) { this.lastFrame = frame; } endEncoding() { - clearTimeout(this.intervalId); + this.ticker.stop(); this.inputStream.end(); } } @@ -593,8 +626,6 @@ class StepRecorder extends AbstractRecorder { } this.progressBar.increment(); } - // Finish with en empty frame - this.renderer.addEmptyFrame(); this.progressBar.stop(); await this.renderer.endEncoding(); } diff --git a/dist/script/index.js.map b/dist/script/index.js.map index 23ef668..5328d81 100644 --- a/dist/script/index.js.map +++ b/dist/script/index.js.map @@ -1 +1 @@ -{"version":3,"file":"index.js","sources":["../../node_modules/@commander-js/extra-typings/index.js","../../node_modules/@commander-js/extra-typings/esm.mjs","../../src/script/assets.ts","../../src/script/cli.ts","../../src/script/work-dir.ts","../../src/script/stats-printer.ts","../../src/script/abstract-renderer.ts","../../src/script/step-renderer.ts","../../src/script/abstract-recorder.ts","../../src/script/real-time-recorder.ts","../../src/script/real-time-renderer.ts","../../src/script/step-recorder.ts","../../src/common/timecodes.ts","../../src/common/captions.ts","../../src/common/web-server.ts","../../src/script/index.ts"],"sourcesContent":["const commander = require('commander');\n\nexports = module.exports = {};\n\n// Return a different global program than commander,\n// and don't also return it as default export.\nexports.program = new commander.Command();\n\n/**\n * Expose classes. The FooT versions are just types, so return Commander original implementations!\n */\n\nexports.Argument = commander.Argument;\nexports.Command = commander.Command;\nexports.CommanderError = commander.CommanderError;\nexports.Help = commander.Help;\nexports.InvalidArgumentError = commander.InvalidArgumentError;\nexports.InvalidOptionArgumentError = commander.InvalidArgumentError; // Deprecated\nexports.Option = commander.Option;\n\n// In Commander, the create routines end up being aliases for the matching\n// methods on the global program due to the (deprecated) legacy default export.\n// Here we roll our own, the way Commander might in future.\nexports.createCommand = (name) => new commander.Command(name);\nexports.createOption = (flags, description) =>\n new commander.Option(flags, description);\nexports.createArgument = (name, description) =>\n new commander.Argument(name, description);\n","import extraTypingsCommander from './index.js';\n\n// wrapper to provide named exports for ESM.\nexport const {\n program,\n createCommand,\n createArgument,\n createOption,\n CommanderError,\n InvalidArgumentError,\n InvalidOptionArgumentError, // deprecated old name\n Command,\n Argument,\n Option,\n Help,\n} = extraTypingsCommander;\n","import * as path from 'path';\n\nexport const assetsFolder = path.join(__dirname, '..', '..', 'assets');\nexport const defaultStylesCss = path.join(assetsFolder, 'captions.css');\nexport const indexHtml = path.join(assetsFolder, 'index.html');\n\nexport const indexJs = path.join(__dirname, '..', 'player', 'index.js');\n\nexport const nodeModules = path.join(__dirname, '..', '..', 'node_modules');\n","import {Command} from '@commander-js/extra-typings';\nimport packageJson from '../../package.json';\nimport {defaultStylesCss} from './assets';\nimport * as cliProgress from 'cli-progress';\nimport * as path from 'path';\n\nexport interface Args {\n srtInputFile: string;\n movOutputFile: string;\n videoWidth: number;\n videoHeight: number;\n fps: number;\n styleFile: string;\n css3Animations: boolean;\n isPreview: boolean;\n}\n\nfunction parseIntAndAssert(...assertions: ((v: number) => void)[]): (v: string) => number {\n return (value: string) => {\n const int = parseInt(value, 10);\n assertions.forEach(assertion => assertion(int));\n return int;\n }\n}\n\nfunction assertPositive(option: string): (v: number) => void {\n return (value: number) => {\n if (value < 0) {\n throw new Error(`${option} should be positive!`);\n }\n };\n}\n\nfunction assertMinMax(option: string, min: number, max: number): (v: number) => void {\n return (value: number) => {\n if (value < min || value > max) {\n throw new Error(`${option} should be between ${min} and ${max}!`);\n }\n };\n}\n\nfunction assertFileExtension(ext: string): (v: string) => void {\n return (value: string) => {\n if (!value.endsWith(ext)) {\n throw new Error(`File should have extension ${ext}!`);\n }\n return value;\n };\n}\n\nconst program = new Command();\n\nprogram\n .name('pupcaps')\n .description('Tool to add stylish captions to your video.')\n .version(packageJson.version)\n .argument('', 'Path to the input SubRip Subtitle (.srt) file.', assertFileExtension('.srt'))\n .option('-o, --output ',\n 'Full or relative path where the created Films Apple QuickTime (MOV) file should be written. ' +\n 'By default, it will be saved in the same directory as the input subtitle file.',\n assertFileExtension('.mov'))\n .option('-w, --width ',\n 'Width of the video in pixels.',\n parseIntAndAssert(assertPositive('Width')),\n 1080)\n .option('-h, --height ',\n 'Height of the video in pixels.',\n parseIntAndAssert(assertPositive('Height')),\n 1920)\n .option('-r, --fps ',\n 'Specifies the frame rate (FPS) of the output video. Valid values are between 1 and 60.',\n parseIntAndAssert(assertMinMax('FPS', 1, 60)),\n 30)\n .option('-s, --style ',\n 'Full or relative path to the styles .css file. ' +\n 'If not provided, default styles for captions will be used.',\n assertFileExtension('.css'))\n .option('-a, --animate',\n 'Records captions with CSS3 animations. ' +\n 'Note: The recording will run for the entire duration of the video. ' +\n 'Use this option only if your captions involve CSS3 animations.')\n .option('--preview',\n 'Prevents the script from generating a video file. ' +\n 'Instead, captions are displayed in the browser for debugging and preview purposes.')\n .action((inputFile, options: any) => {\n if (!options.output) {\n const fileBasename = (inputFile as any as string).slice(0, -4);\n options.output = `${fileBasename}.mov`;\n }\n\n if (!options.style) {\n options.style = defaultStylesCss;\n } else {\n options.style = path.resolve(options.style);\n }\n });\n\nexport function parseArgs(): Args {\n program.parse();\n const opts = program.opts() as any;\n\n return {\n srtInputFile: program.args[0],\n movOutputFile: opts.output,\n videoWidth: opts.width,\n videoHeight: opts.height,\n fps: opts.fps,\n styleFile: opts.style,\n css3Animations: opts.animate,\n isPreview: opts.preview,\n };\n}\n\nexport function printArgs(args: Args) {\n const styles = args.styleFile === defaultStylesCss\n ? '(Default)'\n : args.styleFile;\n\n const srt = `\n Output: ${args.movOutputFile}\n Width: ${args.videoWidth} px\n Height: ${args.videoHeight} px\n FPS: ${args.fps}\n Styles: ${styles}\n Animations: ${ args.css3Animations ? 'yes' : 'no' }\n `;\n\n console.log(srt);\n}\n\nexport function createProgressBar(): cliProgress.SingleBar {\n return new cliProgress.SingleBar({\n format: 'Progress |{bar}| {percentage}% || {value}/{total} Captions',\n barCompleteChar: '\\u2588',\n barIncompleteChar: '\\u2591',\n hideCursor: true,\n }, cliProgress.Presets.shades_classic);\n}","import * as tmp from 'tmp';\nimport * as path from 'path';\nimport {writeFileSync, symlinkSync, rmSync, mkdirSync} from 'fs';\nimport {Caption} from '../common/captions';\nimport {Args} from './cli';\nimport {indexHtml, indexJs, nodeModules} from './assets';\nimport {PlayerArgs} from '../common/player-args';\n\nexport class WorkDir {\n private readonly workDir = tmp.dirSync({ template: 'pupcaps-XXXXXX' });\n\n constructor(private readonly captions: Caption[],\n private readonly args: Args) {\n }\n\n public setup(): string {\n const index = path.join(this.workDir.name, 'index.html');\n\n symlinkSync(indexHtml, index);\n symlinkSync(indexJs, path.join(this.workDir.name, 'index.js'));\n symlinkSync(this.args.styleFile, path.join(this.workDir.name, 'captions.css'));\n symlinkSync(nodeModules, path.join(this.workDir.name, 'node_modules'));\n\n this.setupCaptions();\n this.setupPlayerArgs();\n this.setupVideoSizeCss();\n\n mkdirSync(this.screenShotsDir);\n\n return index;\n }\n\n public clear() {\n rmSync(this.workDir.name, { recursive: true, force: true });\n }\n\n public get screenShotsDir(): string {\n return path.join(this.workDir.name, 'screenshots');\n }\n\n public get rootDir(): string {\n return this.workDir.name;\n }\n\n private setupVideoSizeCss() {\n const css= `#video {\n width: ${this.args.videoWidth}px;\n height: ${this.args.videoHeight}px;\n }`;\n const videoSizeFile = path.join(this.workDir.name, 'video.size.css');\n\n writeFileSync(videoSizeFile, css);\n }\n\n private setupCaptions() {\n const captionsJs = 'window.captions = ' + JSON.stringify(this.captions, null, 2);\n const captionsJsFile = path.join(this.workDir.name, 'captions.js');\n\n writeFileSync(captionsJsFile, captionsJs);\n }\n\n private setupPlayerArgs() {\n const playerArgs: PlayerArgs = {\n isPreview: this.args.isPreview,\n };\n const argsJs = 'window.playerArgs = ' + JSON.stringify(playerArgs, null, 2);\n const argsJsFile = path.join(this.workDir.name, 'player.args.js');\n\n writeFileSync(argsJsFile, argsJs);\n }\n}","export class StatsPrinter {\n private statsPrinted = false;\n\n public print(stats: Object) {\n const lines = Object\n .entries(stats)\n .map(([key, value]) => `${key}: ${value}`);\n\n if (this.statsPrinted) {\n process.stdout.write(`\\x1b[${lines.length}A`); // Move up N lines\n }\n\n lines.forEach((line) => {\n process.stdout.write(`\\r${line.padEnd(40)}\\n`); // Ensure the line is fully overwritten\n });\n\n this.statsPrinted = true;\n }\n}","import ffmpeg, { setFfmpegPath } from 'fluent-ffmpeg';\nimport {Args} from './cli';\n\n(() => {\n try {\n const ffmpegInstaller = require('@ffmpeg-installer/ffmpeg');\n setFfmpegPath(ffmpegInstaller!.path);\n } catch (error) {\n console.warn('Impossible to install FFMpeg. Use system-provided ffmpeg.');\n }\n})();\n\nexport abstract class AbstractRenderer {\n protected constructor(protected readonly args : Args) {\n }\n\n public abstract startEncoding(): void;\n public abstract endEncoding(): void;\n\n protected baseFfmpegCommand(): ffmpeg.FfmpegCommand {\n return ffmpeg()\n .outputOptions([\n '-c:v prores_ks', // codec for Films Apple QuickTime (MOV)\n '-profile:v 4444', // enable the best quality\n '-pix_fmt yuva444p10le', // lossless setting\n '-q:v 0', // lossless setting\n '-vendor ap10' // ensures the output MOV file is compatible with Apple QuickTime\n ])\n .output(this.args.movOutputFile);\n }\n}","import {Args} from './cli';\nimport {PNG, PNGWithMetadata} from 'pngjs';\nimport * as path from 'path';\nimport {appendFileSync, writeFileSync} from 'fs';\nimport {WorkDir} from './work-dir';\nimport {Caption} from '../common/captions';\nimport {StatsPrinter} from './stats-printer';\nimport {AbstractRenderer} from './abstract-renderer';\n\nexport class StepRenderer extends AbstractRenderer {\n private readonly framesFileName: string;\n private readonly emptyFrameFileName: string;\n\n constructor(args : Args,\n private readonly workDir: WorkDir) {\n super(args);\n this.framesFileName = path.join(workDir.screenShotsDir, 'frames.txt');\n this.emptyFrameFileName = path.join(workDir.screenShotsDir, 'empty.png');\n }\n\n public startEncoding() {\n const empty = new PNG({\n width: this.args.videoWidth,\n height: this.args.videoHeight,\n colorType: 6,\n });\n writeFileSync(this.emptyFrameFileName, PNG.sync.write(empty));\n }\n\n public addEmptyFrame(durationMs?: number) {\n let frameDef = `file '${this.emptyFrameFileName}'\\n`;\n\n if (durationMs) {\n const durationSec = durationMs / 1000;\n frameDef += `duration ${durationSec}\\n`;\n }\n\n appendFileSync(this.framesFileName, frameDef, 'utf8');\n }\n\n public addFrame(caption: Caption, png: PNGWithMetadata) {\n const screenShotFileName = path.join(this.workDir.screenShotsDir, `screenshot_${caption.index}.png`);\n writeFileSync(screenShotFileName, PNG.sync.write(png));\n\n const durationSec = (caption.endTimeMs - caption.startTimeMs) / 1000;\n\n appendFileSync(\n this.framesFileName,\n `file '${screenShotFileName}'\\nduration ${durationSec}\\n`,\n 'utf8');\n }\n\n public async endEncoding() {\n console.log(`Encoding ${this.args.movOutputFile}...\\n`);\n const statsPrinter = new StatsPrinter();\n\n await new Promise((resolve, reject) => {\n this.baseFfmpegCommand()\n .input(this.framesFileName)\n .inputOptions([\n '-f concat', // concat frames from the frame list\n '-safe 0' // to prevent errors related to unsafe filenames\n ])\n .on('progress', (progress: Object) => {\n statsPrinter.print(progress);\n })\n .on('end', () => {\n console.log(`${this.args.movOutputFile} encoded`);\n resolve(this.args.movOutputFile);\n })\n .on('error', (err: any) => {\n reject(err);\n })\n .run();\n });\n }\n}","import * as puppeteer from 'puppeteer';\nimport {Args} from './cli';\n\nexport abstract class AbstractRecorder {\n protected browser: puppeteer.Browser | null = null;\n protected page: puppeteer.Page | null = null;\n\n protected constructor(protected readonly args: Args) {\n }\n\n public abstract recordCaptionsVideo(indexHtml: string): Promise;\n\n protected async launchBrowser(indexHtml: string): Promise {\n this.browser = await puppeteer.launch({\n args: [\n '--disable-web-security', // Disable CORS\n '--allow-file-access-from-files', // Allow file access\n ],\n headless: true,\n });\n this.page = await this.browser.newPage();\n await this.page.goto(`file://${indexHtml}`);\n await this.page.setViewport({\n width: this.args.videoWidth,\n height: this.args.videoHeight,\n });\n await this.page.evaluate(() => {\n return window.ready;\n });\n\n return this.page.$('#video');\n }\n}","import * as puppeteer from 'puppeteer';\nimport {RealTimeRenderer} from './real-time-renderer';\nimport {Args} from './cli';\nimport {AbstractRecorder} from './abstract-recorder';\n\nexport class RealTimeRecorder extends AbstractRecorder {\n constructor(args: Args,\n private readonly videoRenderer: RealTimeRenderer) {\n super(args);\n }\n\n public async recordCaptionsVideo(indexHtml: string) {\n try {\n await this.launchBrowser(indexHtml);\n const cdpSession = await this.page!.createCDPSession();\n\n await cdpSession.send(\n 'Emulation.setDefaultBackgroundColorOverride',\n { color: { r: 0, g: 0, b: 0, a: 0 } }\n );\n await cdpSession.send('Animation.setPlaybackRate', {\n playbackRate: 1,\n });\n\n cdpSession.on('Page.screencastFrame',\n (frame) => this.handleScreenCastFrame(cdpSession, frame));\n\n this.videoRenderer.startEncoding();\n\n await cdpSession.send('Page.startScreencast', {\n everyNthFrame: 1,\n format: 'png',\n quality: 100,\n });\n\n await this.page!.evaluate(() => {\n return new Promise((resolve) => {\n window.Player.onStop = resolve;\n window.Player.play();\n })\n });\n\n await cdpSession.send('Page.stopScreencast');\n\n this.videoRenderer.endEncoding();\n } catch (error) {\n console.error('Error during Puppeteer operation:', error);\n } finally {\n await this.browser?.close();\n }\n }\n\n private async handleScreenCastFrame(cdpSession: puppeteer.CDPSession,\n frame: puppeteer.Protocol.Page.ScreencastFrameEvent) {\n const { sessionId, data } = frame;\n await cdpSession.send('Page.screencastFrameAck', { sessionId });\n const frameBuffer = Buffer.from(data, 'base64');\n this.videoRenderer.addFrame(frameBuffer);\n }\n}","import {PassThrough} from 'stream';\nimport {PNG} from 'pngjs';\nimport {Args} from './cli';\nimport {StatsPrinter} from './stats-printer';\nimport {AbstractRenderer} from './abstract-renderer';\n\nexport class RealTimeRenderer extends AbstractRenderer {\n private inputStream: PassThrough | null = null;\n private intervalId: NodeJS.Timeout | null = null;\n private lastFrame: Buffer;\n\n constructor(args: Args) {\n super(args);\n const empty = new PNG({\n width: this.args.videoWidth,\n height: this.args.videoHeight,\n colorType: 6,\n });\n this.lastFrame = PNG.sync.write(empty);\n }\n\n public startEncoding() {\n this.inputStream = new PassThrough();\n const statsPrinter = new StatsPrinter();\n\n const command = this.baseFfmpegCommand()\n .input(this.inputStream)\n .inputOptions([\n '-f image2pipe', // Format of input frames\n '-pix_fmt yuva444p10le', // Lossless setting\n `-s ${this.args.videoWidth}x${this.args.videoHeight}`, // Frame size\n `-r ${this.args.fps}`, // Framerate\n ])\n .on('start', () => {\n console.log('FFmpeg process started.');\n })\n .on('progress', (progress) => {\n statsPrinter.print(progress);\n })\n .on('end', () => {\n console.log('FFmpeg process completed.');\n })\n .on('error', (err) => {\n console.error('An error occurred:', err.message);\n });\n\n command.run();\n\n // Produce frames in required rate\n const intervalDuration = Math.round(1000 / 30);\n this.intervalId = setInterval(() => {\n this.inputStream!.write(this.lastFrame);\n }, intervalDuration);\n }\n\n public addFrame(frame: Buffer) {\n this.lastFrame = frame;\n }\n\n public endEncoding() {\n clearTimeout(this.intervalId!);\n this.inputStream!.end();\n }\n}","import * as puppeteer from 'puppeteer';\nimport * as cliProgress from 'cli-progress';\nimport {PNG, PNGWithMetadata} from 'pngjs';\nimport {Caption} from '../common/captions';\nimport {StepRenderer} from './step-renderer';\nimport {Args} from './cli';\nimport {AbstractRecorder} from './abstract-recorder';\n\nexport class StepRecorder extends AbstractRecorder {\n constructor(args: Args,\n private readonly captions: Caption[],\n private readonly renderer: StepRenderer,\n private readonly progressBar: cliProgress.SingleBar) {\n super(args);\n }\n\n public async recordCaptionsVideo(indexHtml: string) {\n this.progressBar.start(this.captions.length, 0);\n\n try {\n const videoElem = await this.launchBrowser(indexHtml);\n\n this.renderer.startEncoding();\n\n // Add empty frame before captions starts\n const beginningTime = this.captions[0].startTimeMs;\n this.renderer.addEmptyFrame(beginningTime);\n\n for (let i = 0; i < this.captions.length; i++) {\n const caption = this.captions[i];\n\n await this.nextStep();\n\n const screenShot = await this.takeScreenShot(videoElem!);\n this.renderer.addFrame(caption, screenShot);\n\n // Add delay before the next frame\n if (i < this.captions.length - 1) {\n const idleDelay = this.captions[i + 1].startTimeMs - caption.endTimeMs;\n if (idleDelay) {\n this.renderer.addEmptyFrame(idleDelay);\n }\n }\n\n this.progressBar.increment();\n }\n\n // Finish with en empty frame\n this.renderer.addEmptyFrame();\n\n this.progressBar.stop();\n await this.renderer.endEncoding();\n } catch (error) {\n console.error('Error during Puppeteer operation:', error);\n } finally {\n await this.browser?.close();\n }\n }\n\n private async nextStep() {\n await this.page!.evaluate(() => {\n window.Player.next();\n });\n }\n\n private async takeScreenShot(elem: puppeteer.ElementHandle): Promise {\n const screenshotBuffer = await elem.screenshot({\n encoding: 'binary',\n omitBackground: true,\n });\n return PNG.sync.read(Buffer.from(screenshotBuffer));\n }\n}","export class Timecode {\n public readonly hours: number;\n public readonly minutes: number;\n public readonly seconds: number;\n public readonly millis: number;\n\n constructor(millis: number) {\n this.millis = millis % 1000;\n\n this.hours = Math.floor(millis / 3_600_000);\n const remainingMillisAfterHours = millis % 3_600_000;\n this.minutes = Math.floor(remainingMillisAfterHours / 60_000);\n const remainingMillisAfterMinutes = remainingMillisAfterHours % 60_000;\n this.seconds = Math.floor(remainingMillisAfterMinutes / 1000);\n }\n\n public get hh(): string {\n return String(this.hours).padStart(2, '0');\n }\n\n public get mm(): string {\n return String(this.minutes).padStart(2, '0');\n }\n\n public get ss(): string {\n return String(this.seconds).padStart(2, '0');\n }\n public get SSS(): string {\n return String(this.millis).padStart(3, '0');\n }\n\n public get asString(): string {\n return `${this.hh}:${this.mm}:${this.ss},${this.SSS}`;\n }\n}\n\nexport function toMillis(timecodes: string): number {\n const parts = timecodes.split(/[:,]/).map(Number);\n\n const hours = parts[0];\n const minutes = parts[1];\n const seconds = parts[2];\n const milliseconds = parts[3];\n\n return hours * 3_600_000 // hours to millis\n + minutes * 60_000 // minutes to millis\n + seconds * 1000 // second to millis\n + milliseconds;\n}","import {toMillis} from './timecodes';\n\nconst indexLinePattern = /^\\d+$/;\nconst timecodesLinePattern = /^(\\d{2}:\\d{2}:\\d{2},\\d{3}) --> (\\d{2}:\\d{2}:\\d{2},\\d{3})$/;\nconst highlightedWordPattern = /^\\[(.+)](?:\\((\\w+)\\))?$/;\n\nexport interface Word {\n rawWord: string;\n isHighlighted: boolean;\n isBeforeHighlighted: boolean;\n isAfterHighlighted: boolean;\n highlightClass?: string;\n}\n\nexport interface Caption {\n index: number;\n startTimeMs: number;\n endTimeMs: number;\n words: Word[];\n}\n\nexport function haveSameWords(caption1: Caption, caption2: Caption): boolean {\n if (caption1.words.length != caption2.words.length) {\n return false;\n }\n\n for (let i =0; i < caption1.words.length; i++) {\n if (caption1.words[i].rawWord != caption2.words[i].rawWord) {\n return false;\n }\n }\n\n return true;\n}\n\nexport function readCaptions(srtContent: string): Caption[] {\n const lines = srtContent.split('\\n');\n const captions: Caption[] = [];\n\n let index: number = 0;\n let timecodesStart: string | null = null;\n let timecodesEnd: string | null = null;\n\n for (const line of lines) {\n let match;\n if ((match = line.match(indexLinePattern))) {\n index = Number(line);\n } else if ((match = line.match(timecodesLinePattern))) {\n timecodesStart = match[1];\n timecodesEnd = match[2];\n } else if (line.length) {\n const start = toMillis(timecodesStart!);\n const end = toMillis(timecodesEnd!);\n\n const words = readWords(line);\n\n captions.push({\n index,\n words,\n startTimeMs: start,\n endTimeMs: end,\n });\n }\n }\n\n return captions;\n}\n\nexport function readWords(text: string): Word[] {\n const words = splitText(text);\n const highlightedIndex = words.findIndex(word => word.match(highlightedWordPattern));\n\n const res: Word[] = [];\n\n for (let i = 0; i < words.length; i++) {\n const word = words[i];\n const match = word.match(highlightedWordPattern);\n const rawWord = match ? match[1] : word;\n const highlightClass = match && match[2] ? match[2] : null;\n\n const isHighlighted = Boolean(match);\n const isBeforeHighlighted = Boolean(~highlightedIndex && !isHighlighted && i < highlightedIndex);\n const isAfterHighlighted = Boolean(~highlightedIndex && !isHighlighted && i > highlightedIndex);\n\n const wordObject: Word = {\n rawWord,\n isHighlighted,\n isBeforeHighlighted,\n isAfterHighlighted,\n };\n\n if (highlightClass) {\n wordObject.highlightClass = highlightClass;\n }\n\n res.push(wordObject);\n }\n\n return res;\n}\n\nexport function splitText(text: string): string[] {\n const words: string[] = [];\n\n let currentWord = '';\n let isCurrentHighlighted = false;\n\n for (let i = 0; i < text.length; i++) {\n const char = text[i];\n const isWhitespace = /^\\s$/.test(char);\n const isPunctuation = /[,.!?]/.test(char);\n\n if (!isWhitespace) {\n if (!isPunctuation) {\n currentWord += char;\n switch (char) {\n case '[':\n case '(':\n isCurrentHighlighted = true;\n break;\n case ']':\n case ')':\n isCurrentHighlighted = false;\n break;\n }\n } else {\n if (currentWord) {\n currentWord += char;\n } else {\n // Attach punctuation mark to the previous word\n words[words.length - 1] += ' ' + char;\n }\n }\n } else {\n // char is a whitespace\n if (isCurrentHighlighted) {\n currentWord += char;\n } else if (currentWord) {\n words.push(currentWord);\n currentWord = '';\n }\n }\n }\n\n if (currentWord) {\n words.push(currentWord);\n }\n\n return words;\n}","import {createServer} from 'http-server';\n\nexport class WebServer {\n constructor(private readonly rootDir: string) {\n }\n\n public async start(relativePath = '') {\n return new Promise(async (resolve, reject) => {\n try {\n const server = createServer({ root: this.rootDir });\n const port = await WebServer.getFreePort();\n\n server.listen(port, async () => {\n try {\n const childProcess = await WebServer.openUrl(`http://127.0.0.1:${port}${relativePath}`);\n\n childProcess.on('close', () => {\n server.close(() => {\n resolve();\n });\n });\n } catch (error) {\n reject(error);\n }\n });\n } catch (error) {\n reject(error);\n }\n });\n }\n\n private static async getFreePort(): Promise {\n const { default: getPort } = await import('get-port');\n return getPort();\n }\n\n private static async openUrl(url: string) {\n const { default: open } = await import('open');\n return open(url, { wait: true });\n }\n}","import {readFileSync} from 'fs';\nimport {Args, createProgressBar, parseArgs, printArgs} from './cli';\nimport {WorkDir} from './work-dir';\nimport {StepRenderer} from './step-renderer';\nimport {RealTimeRecorder} from './real-time-recorder';\nimport {RealTimeRenderer} from './real-time-renderer';\nimport {StepRecorder} from './step-recorder';\nimport {AbstractRecorder} from './abstract-recorder';\nimport {Caption, readCaptions} from '../common/captions';\nimport {WebServer} from '../common/web-server';\n\nfunction parseCaptions(srtCaptionsFile: string): Caption[] {\n const captionsSrc = readFileSync(srtCaptionsFile, 'utf-8');\n return readCaptions(captionsSrc);\n}\n\nfunction createRecorder(args: Args, captions: Caption[], workDir: WorkDir): AbstractRecorder {\n if (args.css3Animations) {\n const realTimeRenderer = new RealTimeRenderer(args);\n return new RealTimeRecorder(args, realTimeRenderer);\n } else {\n const progressBar = createProgressBar();\n const stepRenderer = new StepRenderer(args, workDir);\n return new StepRecorder(args, captions, stepRenderer, progressBar);\n }\n}\n\nconst cliArgs = parseArgs();\nconst captions = parseCaptions(cliArgs.srtInputFile);\nconst workDir = new WorkDir(captions, cliArgs);\n\n(async () => {\n try {\n const indexHtml = workDir.setup();\n printArgs(cliArgs);\n\n if (!cliArgs.isPreview) {\n const recorder = createRecorder(cliArgs, captions, workDir);\n await recorder.recordCaptionsVideo(indexHtml);\n } else {\n console.log('Launching preview server...');\n const previewServer = new WebServer(workDir.rootDir);\n await previewServer.start();\n }\n console.log('Done!');\n } catch (err) {\n console.error('Error occurred:', err);\n } finally {\n workDir.clear();\n }\n})();\n"],"names":["program","path","cliProgress","tmp","symlinkSync","mkdirSync","rmSync","writeFileSync","setFfmpegPath","PNG","appendFileSync","puppeteer","PassThrough","createServer","readFileSync"],"mappings":"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","x_google_ignoreList":[0,1]} \ No newline at end of file +{"version":3,"file":"index.js","sources":["../../node_modules/@commander-js/extra-typings/index.js","../../node_modules/@commander-js/extra-typings/esm.mjs","../../src/script/assets.ts","../../src/script/cli.ts","../../src/script/work-dir.ts","../../src/script/stats-printer.ts","../../src/script/abstract-renderer.ts","../../src/script/step-renderer.ts","../../src/script/abstract-recorder.ts","../../src/script/real-time-recorder.ts","../../src/script/fps-ticker.ts","../../src/script/real-time-renderer.ts","../../src/script/step-recorder.ts","../../src/common/timecodes.ts","../../src/common/captions.ts","../../src/common/web-server.ts","../../src/script/index.ts"],"sourcesContent":["const commander = require('commander');\n\nexports = module.exports = {};\n\n// Return a different global program than commander,\n// and don't also return it as default export.\nexports.program = new commander.Command();\n\n/**\n * Expose classes. The FooT versions are just types, so return Commander original implementations!\n */\n\nexports.Argument = commander.Argument;\nexports.Command = commander.Command;\nexports.CommanderError = commander.CommanderError;\nexports.Help = commander.Help;\nexports.InvalidArgumentError = commander.InvalidArgumentError;\nexports.InvalidOptionArgumentError = commander.InvalidArgumentError; // Deprecated\nexports.Option = commander.Option;\n\n// In Commander, the create routines end up being aliases for the matching\n// methods on the global program due to the (deprecated) legacy default export.\n// Here we roll our own, the way Commander might in future.\nexports.createCommand = (name) => new commander.Command(name);\nexports.createOption = (flags, description) =>\n new commander.Option(flags, description);\nexports.createArgument = (name, description) =>\n new commander.Argument(name, description);\n","import extraTypingsCommander from './index.js';\n\n// wrapper to provide named exports for ESM.\nexport const {\n program,\n createCommand,\n createArgument,\n createOption,\n CommanderError,\n InvalidArgumentError,\n InvalidOptionArgumentError, // deprecated old name\n Command,\n Argument,\n Option,\n Help,\n} = extraTypingsCommander;\n","import * as path from 'path';\n\nexport const assetsFolder = path.join(__dirname, '..', '..', 'assets');\nexport const defaultStylesCss = path.join(assetsFolder, 'captions.css');\nexport const indexHtml = path.join(assetsFolder, 'index.html');\n\nexport const indexJs = path.join(__dirname, '..', 'player', 'index.js');\n\nexport const nodeModules = path.join(__dirname, '..', '..', 'node_modules');\n","import {Command} from '@commander-js/extra-typings';\nimport packageJson from '../../package.json';\nimport {defaultStylesCss} from './assets';\nimport * as cliProgress from 'cli-progress';\nimport * as path from 'path';\n\nexport interface Args {\n srtInputFile: string;\n movOutputFile: string;\n videoWidth: number;\n videoHeight: number;\n fps: number;\n styleFile: string;\n css3Animations: boolean;\n isPreview: boolean;\n}\n\nfunction parseIntAndAssert(...assertions: ((v: number) => void)[]): (v: string) => number {\n return (value: string) => {\n const int = parseInt(value, 10);\n assertions.forEach(assertion => assertion(int));\n return int;\n }\n}\n\nfunction assertPositive(option: string): (v: number) => void {\n return (value: number) => {\n if (value < 0) {\n throw new Error(`${option} should be positive!`);\n }\n };\n}\n\nfunction assertMinMax(option: string, min: number, max: number): (v: number) => void {\n return (value: number) => {\n if (value < min || value > max) {\n throw new Error(`${option} should be between ${min} and ${max}!`);\n }\n };\n}\n\nfunction assertFileExtension(ext: string): (v: string) => void {\n return (value: string) => {\n if (!value.endsWith(ext)) {\n throw new Error(`File should have extension ${ext}!`);\n }\n return value;\n };\n}\n\nconst program = new Command();\n\nprogram\n .name('pupcaps')\n .description('Tool to add stylish captions to your video.')\n .version(packageJson.version)\n .argument('', 'Path to the input SubRip Subtitle (.srt) file.', assertFileExtension('.srt'))\n .option('-o, --output ',\n 'Full or relative path where the created Films Apple QuickTime (MOV) file should be written. ' +\n 'By default, it will be saved in the same directory as the input subtitle file.',\n assertFileExtension('.mov'))\n .option('-w, --width ',\n 'Width of the video in pixels.',\n parseIntAndAssert(assertPositive('Width')),\n 1080)\n .option('-h, --height ',\n 'Height of the video in pixels.',\n parseIntAndAssert(assertPositive('Height')),\n 1920)\n .option('-r, --fps ',\n 'Specifies the frame rate (FPS) of the output video. Valid values are between 1 and 60.',\n parseIntAndAssert(assertMinMax('FPS', 1, 60)),\n 30)\n .option('-s, --style ',\n 'Full or relative path to the styles .css file. ' +\n 'If not provided, default styles for captions will be used.',\n assertFileExtension('.css'))\n .option('-a, --animate',\n 'Records captions with CSS3 animations. ' +\n 'Note: The recording will run for the entire duration of the video. ' +\n 'Use this option only if your captions involve CSS3 animations.')\n .option('--preview',\n 'Prevents the script from generating a video file. ' +\n 'Instead, captions are displayed in the browser for debugging and preview purposes.')\n .action((inputFile, options: any) => {\n if (!options.output) {\n const fileBasename = (inputFile as any as string).slice(0, -4);\n options.output = `${fileBasename}.mov`;\n }\n\n if (!options.style) {\n options.style = defaultStylesCss;\n } else {\n options.style = path.resolve(options.style);\n }\n });\n\nexport function parseArgs(): Args {\n program.parse();\n const opts = program.opts() as any;\n\n return {\n srtInputFile: program.args[0],\n movOutputFile: opts.output,\n videoWidth: opts.width,\n videoHeight: opts.height,\n fps: opts.fps,\n styleFile: opts.style,\n css3Animations: opts.animate,\n isPreview: opts.preview,\n };\n}\n\nexport function printArgs(args: Args) {\n const styles = args.styleFile === defaultStylesCss\n ? '(Default)'\n : args.styleFile;\n\n const srt = `\n Output: ${args.movOutputFile}\n Width: ${args.videoWidth} px\n Height: ${args.videoHeight} px\n FPS: ${args.fps}\n Styles: ${styles}\n Animations: ${ args.css3Animations ? 'yes' : 'no' }\n `;\n\n console.log(srt);\n}\n\nexport function createProgressBar(): cliProgress.SingleBar {\n return new cliProgress.SingleBar({\n format: 'Progress |{bar}| {percentage}% || {value}/{total} Captions',\n barCompleteChar: '\\u2588',\n barIncompleteChar: '\\u2591',\n hideCursor: true,\n }, cliProgress.Presets.shades_classic);\n}","import * as tmp from 'tmp';\nimport * as path from 'path';\nimport {writeFileSync, symlinkSync, rmSync, mkdirSync} from 'fs';\nimport {Caption} from '../common/captions';\nimport {Args} from './cli';\nimport {indexHtml, indexJs, nodeModules} from './assets';\nimport {PlayerArgs} from '../common/player-args';\n\nexport class WorkDir {\n private readonly workDir = tmp.dirSync({ template: 'pupcaps-XXXXXX' });\n\n constructor(private readonly captions: Caption[],\n private readonly args: Args) {\n }\n\n public setup(): string {\n const index = path.join(this.workDir.name, 'index.html');\n\n symlinkSync(indexHtml, index);\n symlinkSync(indexJs, path.join(this.workDir.name, 'index.js'));\n symlinkSync(this.args.styleFile, path.join(this.workDir.name, 'captions.css'));\n symlinkSync(nodeModules, path.join(this.workDir.name, 'node_modules'));\n\n this.setupCaptions();\n this.setupPlayerArgs();\n this.setupVideoSizeCss();\n\n mkdirSync(this.screenShotsDir);\n\n return index;\n }\n\n public clear() {\n rmSync(this.workDir.name, { recursive: true, force: true });\n }\n\n public get screenShotsDir(): string {\n return path.join(this.workDir.name, 'screenshots');\n }\n\n public get rootDir(): string {\n return this.workDir.name;\n }\n\n private setupVideoSizeCss() {\n const css= `#video {\n width: ${this.args.videoWidth}px;\n height: ${this.args.videoHeight}px;\n }`;\n const videoSizeFile = path.join(this.workDir.name, 'video.size.css');\n\n writeFileSync(videoSizeFile, css);\n }\n\n private setupCaptions() {\n const captionsJs = 'window.captions = ' + JSON.stringify(this.captions, null, 2);\n const captionsJsFile = path.join(this.workDir.name, 'captions.js');\n\n writeFileSync(captionsJsFile, captionsJs);\n }\n\n private setupPlayerArgs() {\n const playerArgs: PlayerArgs = {\n isPreview: this.args.isPreview,\n };\n const argsJs = 'window.playerArgs = ' + JSON.stringify(playerArgs, null, 2);\n const argsJsFile = path.join(this.workDir.name, 'player.args.js');\n\n writeFileSync(argsJsFile, argsJs);\n }\n}","export class StatsPrinter {\n private statsPrinted = false;\n\n public print(stats: Object) {\n const lines = Object\n .entries(stats)\n .map(([key, value]) => `${key}: ${value}`);\n\n if (this.statsPrinted) {\n process.stdout.write(`\\x1b[${lines.length}A`); // Move up N lines\n }\n\n lines.forEach((line) => {\n process.stdout.write(`\\r${line.padEnd(40)}\\n`); // Ensure the line is fully overwritten\n });\n\n this.statsPrinted = true;\n }\n}","import ffmpeg, { setFfmpegPath } from 'fluent-ffmpeg';\nimport {Args} from './cli';\n\n(() => {\n try {\n const ffmpegInstaller = require('@ffmpeg-installer/ffmpeg');\n setFfmpegPath(ffmpegInstaller!.path);\n } catch (error) {\n console.warn('Impossible to install FFMpeg. Use system-provided ffmpeg.');\n }\n})();\n\nexport abstract class AbstractRenderer {\n protected constructor(protected readonly args : Args) {\n }\n\n public abstract startEncoding(): void;\n public abstract endEncoding(): void;\n\n protected baseFfmpegCommand(): ffmpeg.FfmpegCommand {\n return ffmpeg()\n .outputOptions([\n '-c:v prores_ks', // codec for Films Apple QuickTime (MOV)\n '-profile:v 4444', // enable the best quality\n '-pix_fmt yuva444p10le', // lossless setting\n '-q:v 0', // lossless setting\n '-vendor ap10' // ensures the output MOV file is compatible with Apple QuickTime\n ])\n .output(this.args.movOutputFile);\n }\n}","import {Args} from './cli';\nimport {PNG, PNGWithMetadata} from 'pngjs';\nimport * as path from 'path';\nimport {appendFileSync, writeFileSync} from 'fs';\nimport {WorkDir} from './work-dir';\nimport {Caption} from '../common/captions';\nimport {StatsPrinter} from './stats-printer';\nimport {AbstractRenderer} from './abstract-renderer';\n\nexport class StepRenderer extends AbstractRenderer {\n private readonly framesFileName: string;\n private readonly emptyFrameFileName: string;\n\n constructor(args : Args,\n private readonly workDir: WorkDir) {\n super(args);\n this.framesFileName = path.join(workDir.screenShotsDir, 'frames.txt');\n this.emptyFrameFileName = path.join(workDir.screenShotsDir, 'empty.png');\n }\n\n public startEncoding() {\n const empty = new PNG({\n width: this.args.videoWidth,\n height: this.args.videoHeight,\n colorType: 6,\n });\n writeFileSync(this.emptyFrameFileName, PNG.sync.write(empty));\n }\n\n public addEmptyFrame(durationMs?: number) {\n let frameDef = `file '${this.emptyFrameFileName}'\\n`;\n\n if (durationMs) {\n const durationSec = durationMs / 1000;\n frameDef += `duration ${durationSec}\\n`;\n }\n\n appendFileSync(this.framesFileName, frameDef, 'utf8');\n }\n\n public addFrame(caption: Caption, png: PNGWithMetadata) {\n const screenShotFileName = path.join(this.workDir.screenShotsDir, `screenshot_${caption.index}.png`);\n writeFileSync(screenShotFileName, PNG.sync.write(png));\n\n const durationSec = (caption.endTimeMs - caption.startTimeMs) / 1000;\n\n appendFileSync(\n this.framesFileName,\n `file '${screenShotFileName}'\\nduration ${durationSec}\\n`,\n 'utf8');\n }\n\n public async endEncoding() {\n console.log(`Encoding ${this.args.movOutputFile}...\\n`);\n const statsPrinter = new StatsPrinter();\n\n await new Promise((resolve, reject) => {\n this.baseFfmpegCommand()\n .input(this.framesFileName)\n .inputOptions([\n '-f concat', // concat frames from the frame list\n '-safe 0' // to prevent errors related to unsafe filenames\n ])\n .outputOptions([\n `-vf fps=fps=${this.args.fps}`, // Framerate\n ])\n .on('progress', (progress: Object) => {\n statsPrinter.print(progress);\n })\n .on('end', () => {\n console.log(`${this.args.movOutputFile} encoded`);\n resolve(this.args.movOutputFile);\n })\n .on('error', (err: any) => {\n reject(err);\n })\n .run();\n });\n }\n}","import * as puppeteer from 'puppeteer';\nimport {Args} from './cli';\n\nexport abstract class AbstractRecorder {\n protected browser: puppeteer.Browser | null = null;\n protected page: puppeteer.Page | null = null;\n\n protected constructor(protected readonly args: Args) {\n }\n\n public abstract recordCaptionsVideo(indexHtml: string): Promise;\n\n protected async launchBrowser(indexHtml: string): Promise {\n this.browser = await puppeteer.launch({\n args: [\n '--disable-web-security', // Disable CORS\n '--allow-file-access-from-files', // Allow file access\n ],\n headless: true,\n });\n this.page = await this.browser.newPage();\n await this.page.goto(`file://${indexHtml}`);\n await this.page.setViewport({\n width: this.args.videoWidth,\n height: this.args.videoHeight,\n });\n await this.page.evaluate(() => {\n return window.ready;\n });\n\n return this.page.$('#video');\n }\n}","import * as puppeteer from 'puppeteer';\nimport {RealTimeRenderer} from './real-time-renderer';\nimport {Args} from './cli';\nimport {AbstractRecorder} from './abstract-recorder';\n\nexport class RealTimeRecorder extends AbstractRecorder {\n constructor(args: Args,\n private readonly videoRenderer: RealTimeRenderer) {\n super(args);\n }\n\n public async recordCaptionsVideo(indexHtml: string) {\n try {\n await this.launchBrowser(indexHtml);\n const cdpSession = await this.page!.createCDPSession();\n\n await cdpSession.send(\n 'Emulation.setDefaultBackgroundColorOverride',\n { color: { r: 0, g: 0, b: 0, a: 0 } }\n );\n await cdpSession.send('Animation.setPlaybackRate', {\n playbackRate: 1,\n });\n\n cdpSession.on('Page.screencastFrame',\n (frame) => this.handleScreenCastFrame(cdpSession, frame));\n\n this.videoRenderer.startEncoding();\n\n await cdpSession.send('Page.startScreencast', {\n everyNthFrame: 1,\n format: 'png',\n quality: 100,\n });\n\n await this.page!.evaluate(() => {\n return new Promise((resolve) => {\n window.Player.onStop = resolve;\n window.Player.play();\n })\n });\n\n await cdpSession.send('Page.stopScreencast');\n\n this.videoRenderer.endEncoding();\n } catch (error) {\n console.error('Error during Puppeteer operation:', error);\n } finally {\n await this.browser?.close();\n }\n }\n\n private async handleScreenCastFrame(cdpSession: puppeteer.CDPSession,\n frame: puppeteer.Protocol.Page.ScreencastFrameEvent) {\n const { sessionId, data } = frame;\n await cdpSession.send('Page.screencastFrameAck', { sessionId });\n const frameBuffer = Buffer.from(data, 'base64');\n this.videoRenderer.addFrame(frameBuffer);\n }\n}","export class FPSTicker {\n private readonly interval: number;\n private lastTime: number = 0;\n private onTick: (deltaTime: number) => void = () => {};\n private timeoutId: NodeJS.Timeout | null = null;\n\n constructor(fps: number) {\n this.interval = 1000 / fps;\n }\n\n public start(onTick: (deltaTime: number) => void = () => {}) {\n this.onTick = onTick;\n this.lastTime = Date.now();\n this.tick();\n }\n\n public stop() {\n clearTimeout(this.timeoutId!);\n }\n\n private tick() {\n const now = Date.now();\n const deltaTime = now - this.lastTime;\n\n if (deltaTime >= this.interval) {\n this.lastTime = now - (deltaTime % this.interval); // Adjust for drift\n this.onTick(deltaTime);\n }\n\n this.timeoutId = setTimeout(() => this.tick(), this.interval - (Date.now() - this.lastTime));\n }\n}","import {PassThrough} from 'stream';\nimport {PNG} from 'pngjs';\nimport {Args} from './cli';\nimport {StatsPrinter} from './stats-printer';\nimport {AbstractRenderer} from './abstract-renderer';\nimport {FPSTicker} from './fps-ticker';\n\nexport class RealTimeRenderer extends AbstractRenderer {\n private inputStream: PassThrough | null = null;\n private lastFrame: Buffer;\n private readonly ticker: FPSTicker;\n\n constructor(args: Args) {\n super(args);\n const empty = new PNG({\n width: this.args.videoWidth,\n height: this.args.videoHeight,\n colorType: 6,\n });\n this.lastFrame = PNG.sync.write(empty);\n this.ticker = new FPSTicker(args.fps);\n }\n\n public startEncoding() {\n this.inputStream = new PassThrough();\n const statsPrinter = new StatsPrinter();\n\n const command = this.baseFfmpegCommand()\n .input(this.inputStream)\n .inputOptions([\n '-f image2pipe', // Format of input frames\n '-pix_fmt yuva444p10le', // Lossless setting\n `-s ${this.args.videoWidth}x${this.args.videoHeight}`, // Frame size\n `-r ${this.args.fps}`, // Framerate\n ])\n .outputOptions([\n `-vf fps=fps=${this.args.fps}`, // Framerate\n ])\n .on('start', () => {\n console.log('FFmpeg process started.');\n })\n .on('progress', (progress) => {\n statsPrinter.print(progress);\n })\n .on('end', () => {\n console.log('FFmpeg process completed.');\n })\n .on('error', (err) => {\n console.error('An error occurred:', err.message);\n });\n\n command.run();\n\n // Produce frames in required rate\n this.ticker.start(() => {\n this.inputStream!.write(this.lastFrame);\n });\n }\n\n public addFrame(frame: Buffer) {\n this.lastFrame = frame;\n }\n\n public endEncoding() {\n this.ticker.stop();\n this.inputStream!.end();\n }\n}","import * as puppeteer from 'puppeteer';\nimport * as cliProgress from 'cli-progress';\nimport {PNG, PNGWithMetadata} from 'pngjs';\nimport {Caption} from '../common/captions';\nimport {StepRenderer} from './step-renderer';\nimport {Args} from './cli';\nimport {AbstractRecorder} from './abstract-recorder';\n\nexport class StepRecorder extends AbstractRecorder {\n constructor(args: Args,\n private readonly captions: Caption[],\n private readonly renderer: StepRenderer,\n private readonly progressBar: cliProgress.SingleBar) {\n super(args);\n }\n\n public async recordCaptionsVideo(indexHtml: string) {\n this.progressBar.start(this.captions.length, 0);\n\n try {\n const videoElem = await this.launchBrowser(indexHtml);\n\n this.renderer.startEncoding();\n\n // Add empty frame before captions starts\n const beginningTime = this.captions[0].startTimeMs;\n this.renderer.addEmptyFrame(beginningTime);\n\n for (let i = 0; i < this.captions.length; i++) {\n const caption = this.captions[i];\n\n await this.nextStep();\n\n const screenShot = await this.takeScreenShot(videoElem!);\n this.renderer.addFrame(caption, screenShot);\n\n // Add delay before the next frame\n if (i < this.captions.length - 1) {\n const idleDelay = this.captions[i + 1].startTimeMs - caption.endTimeMs;\n if (idleDelay) {\n this.renderer.addEmptyFrame(idleDelay);\n }\n }\n\n this.progressBar.increment();\n }\n\n this.progressBar.stop();\n await this.renderer.endEncoding();\n } catch (error) {\n console.error('Error during Puppeteer operation:', error);\n } finally {\n await this.browser?.close();\n }\n }\n\n private async nextStep() {\n await this.page!.evaluate(() => {\n window.Player.next();\n });\n }\n\n private async takeScreenShot(elem: puppeteer.ElementHandle): Promise {\n const screenshotBuffer = await elem.screenshot({\n encoding: 'binary',\n omitBackground: true,\n });\n return PNG.sync.read(Buffer.from(screenshotBuffer));\n }\n}","export class Timecode {\n public readonly hours: number;\n public readonly minutes: number;\n public readonly seconds: number;\n public readonly millis: number;\n\n constructor(millis: number) {\n this.millis = millis % 1000;\n\n this.hours = Math.floor(millis / 3_600_000);\n const remainingMillisAfterHours = millis % 3_600_000;\n this.minutes = Math.floor(remainingMillisAfterHours / 60_000);\n const remainingMillisAfterMinutes = remainingMillisAfterHours % 60_000;\n this.seconds = Math.floor(remainingMillisAfterMinutes / 1000);\n }\n\n public get hh(): string {\n return String(this.hours).padStart(2, '0');\n }\n\n public get mm(): string {\n return String(this.minutes).padStart(2, '0');\n }\n\n public get ss(): string {\n return String(this.seconds).padStart(2, '0');\n }\n public get SSS(): string {\n return String(this.millis).padStart(3, '0');\n }\n\n public get asString(): string {\n return `${this.hh}:${this.mm}:${this.ss},${this.SSS}`;\n }\n}\n\nexport function toMillis(timecodes: string): number {\n const parts = timecodes.split(/[:,]/).map(Number);\n\n const hours = parts[0];\n const minutes = parts[1];\n const seconds = parts[2];\n const milliseconds = parts[3];\n\n return hours * 3_600_000 // hours to millis\n + minutes * 60_000 // minutes to millis\n + seconds * 1000 // second to millis\n + milliseconds;\n}","import {toMillis} from './timecodes';\n\nconst indexLinePattern = /^\\d+$/;\nconst timecodesLinePattern = /^(\\d{2}:\\d{2}:\\d{2},\\d{3}) --> (\\d{2}:\\d{2}:\\d{2},\\d{3})$/;\nconst highlightedWordPattern = /^\\[(.+)](?:\\((\\w+)\\))?$/;\n\nexport interface Word {\n rawWord: string;\n isHighlighted: boolean;\n isBeforeHighlighted: boolean;\n isAfterHighlighted: boolean;\n highlightClass?: string;\n}\n\nexport interface Caption {\n index: number;\n startTimeMs: number;\n endTimeMs: number;\n words: Word[];\n}\n\nexport function haveSameWords(caption1: Caption, caption2: Caption): boolean {\n if (caption1.words.length != caption2.words.length) {\n return false;\n }\n\n for (let i =0; i < caption1.words.length; i++) {\n if (caption1.words[i].rawWord != caption2.words[i].rawWord) {\n return false;\n }\n }\n\n return true;\n}\n\nexport function readCaptions(srtContent: string): Caption[] {\n const lines = srtContent.split('\\n');\n const captions: Caption[] = [];\n\n let index: number = 0;\n let timecodesStart: string | null = null;\n let timecodesEnd: string | null = null;\n\n for (const line of lines) {\n let match;\n if ((match = line.match(indexLinePattern))) {\n index = Number(line);\n } else if ((match = line.match(timecodesLinePattern))) {\n timecodesStart = match[1];\n timecodesEnd = match[2];\n } else if (line.length) {\n const start = toMillis(timecodesStart!);\n const end = toMillis(timecodesEnd!);\n\n const words = readWords(line);\n\n captions.push({\n index,\n words,\n startTimeMs: start,\n endTimeMs: end,\n });\n }\n }\n\n return captions;\n}\n\nexport function readWords(text: string): Word[] {\n const words = splitText(text);\n const highlightedIndex = words.findIndex(word => word.match(highlightedWordPattern));\n\n const res: Word[] = [];\n\n for (let i = 0; i < words.length; i++) {\n const word = words[i];\n const match = word.match(highlightedWordPattern);\n const rawWord = match ? match[1] : word;\n const highlightClass = match && match[2] ? match[2] : null;\n\n const isHighlighted = Boolean(match);\n const isBeforeHighlighted = Boolean(~highlightedIndex && !isHighlighted && i < highlightedIndex);\n const isAfterHighlighted = Boolean(~highlightedIndex && !isHighlighted && i > highlightedIndex);\n\n const wordObject: Word = {\n rawWord,\n isHighlighted,\n isBeforeHighlighted,\n isAfterHighlighted,\n };\n\n if (highlightClass) {\n wordObject.highlightClass = highlightClass;\n }\n\n res.push(wordObject);\n }\n\n return res;\n}\n\nexport function splitText(text: string): string[] {\n const words: string[] = [];\n\n let currentWord = '';\n let isCurrentHighlighted = false;\n\n for (let i = 0; i < text.length; i++) {\n const char = text[i];\n const isWhitespace = /^\\s$/.test(char);\n const isPunctuation = /[,.!?]/.test(char);\n\n if (!isWhitespace) {\n if (!isPunctuation) {\n currentWord += char;\n switch (char) {\n case '[':\n case '(':\n isCurrentHighlighted = true;\n break;\n case ']':\n case ')':\n isCurrentHighlighted = false;\n break;\n }\n } else {\n if (currentWord) {\n currentWord += char;\n } else {\n // Attach punctuation mark to the previous word\n words[words.length - 1] += ' ' + char;\n }\n }\n } else {\n // char is a whitespace\n if (isCurrentHighlighted) {\n currentWord += char;\n } else if (currentWord) {\n words.push(currentWord);\n currentWord = '';\n }\n }\n }\n\n if (currentWord) {\n words.push(currentWord);\n }\n\n return words;\n}","import {createServer} from 'http-server';\n\nexport class WebServer {\n constructor(private readonly rootDir: string) {\n }\n\n public async start(relativePath = '') {\n return new Promise(async (resolve, reject) => {\n try {\n const server = createServer({ root: this.rootDir });\n const port = await WebServer.getFreePort();\n\n server.listen(port, async () => {\n try {\n const childProcess = await WebServer.openUrl(`http://127.0.0.1:${port}${relativePath}`);\n\n childProcess.on('close', () => {\n server.close(() => {\n resolve();\n });\n });\n } catch (error) {\n reject(error);\n }\n });\n } catch (error) {\n reject(error);\n }\n });\n }\n\n private static async getFreePort(): Promise {\n const { default: getPort } = await import('get-port');\n return getPort();\n }\n\n private static async openUrl(url: string) {\n const { default: open } = await import('open');\n return open(url, { wait: true });\n }\n}","import {readFileSync} from 'fs';\nimport {Args, createProgressBar, parseArgs, printArgs} from './cli';\nimport {WorkDir} from './work-dir';\nimport {StepRenderer} from './step-renderer';\nimport {RealTimeRecorder} from './real-time-recorder';\nimport {RealTimeRenderer} from './real-time-renderer';\nimport {StepRecorder} from './step-recorder';\nimport {AbstractRecorder} from './abstract-recorder';\nimport {Caption, readCaptions} from '../common/captions';\nimport {WebServer} from '../common/web-server';\n\nfunction parseCaptions(srtCaptionsFile: string): Caption[] {\n const captionsSrc = readFileSync(srtCaptionsFile, 'utf-8');\n return readCaptions(captionsSrc);\n}\n\nfunction createRecorder(args: Args, captions: Caption[], workDir: WorkDir): AbstractRecorder {\n if (args.css3Animations) {\n const realTimeRenderer = new RealTimeRenderer(args);\n return new RealTimeRecorder(args, realTimeRenderer);\n } else {\n const progressBar = createProgressBar();\n const stepRenderer = new StepRenderer(args, workDir);\n return new StepRecorder(args, captions, stepRenderer, progressBar);\n }\n}\n\nconst cliArgs = parseArgs();\nconst captions = parseCaptions(cliArgs.srtInputFile);\nconst workDir = new WorkDir(captions, cliArgs);\n\n(async () => {\n try {\n const indexHtml = workDir.setup();\n printArgs(cliArgs);\n\n if (!cliArgs.isPreview) {\n const recorder = createRecorder(cliArgs, captions, workDir);\n await recorder.recordCaptionsVideo(indexHtml);\n } else {\n console.log('Launching preview server...');\n const previewServer = new WebServer(workDir.rootDir);\n await previewServer.start();\n }\n console.log('Done!');\n } catch (err) {\n console.error('Error occurred:', err);\n } finally {\n workDir.clear();\n }\n})();\n"],"names":["program","path","cliProgress","tmp","symlinkSync","mkdirSync","rmSync","writeFileSync","setFfmpegPath","PNG","appendFileSync","puppeteer","PassThrough","createServer","readFileSync"],"mappings":"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","x_google_ignoreList":[0,1]} \ No newline at end of file diff --git a/src/script/fps-ticker.ts b/src/script/fps-ticker.ts new file mode 100644 index 0000000..c7fd7e0 --- /dev/null +++ b/src/script/fps-ticker.ts @@ -0,0 +1,32 @@ +export class FPSTicker { + private readonly interval: number; + private lastTime: number = 0; + private onTick: (deltaTime: number) => void = () => {}; + private timeoutId: NodeJS.Timeout | null = null; + + constructor(fps: number) { + this.interval = 1000 / fps; + } + + public start(onTick: (deltaTime: number) => void = () => {}) { + this.onTick = onTick; + this.lastTime = Date.now(); + this.tick(); + } + + public stop() { + clearTimeout(this.timeoutId!); + } + + private tick() { + const now = Date.now(); + const deltaTime = now - this.lastTime; + + if (deltaTime >= this.interval) { + this.lastTime = now - (deltaTime % this.interval); // Adjust for drift + this.onTick(deltaTime); + } + + this.timeoutId = setTimeout(() => this.tick(), this.interval - (Date.now() - this.lastTime)); + } +} \ No newline at end of file diff --git a/src/script/real-time-renderer.ts b/src/script/real-time-renderer.ts index e39f18e..cbef83e 100644 --- a/src/script/real-time-renderer.ts +++ b/src/script/real-time-renderer.ts @@ -3,11 +3,12 @@ import {PNG} from 'pngjs'; import {Args} from './cli'; import {StatsPrinter} from './stats-printer'; import {AbstractRenderer} from './abstract-renderer'; +import {FPSTicker} from './fps-ticker'; export class RealTimeRenderer extends AbstractRenderer { private inputStream: PassThrough | null = null; - private intervalId: NodeJS.Timeout | null = null; private lastFrame: Buffer; + private readonly ticker: FPSTicker; constructor(args: Args) { super(args); @@ -17,6 +18,7 @@ export class RealTimeRenderer extends AbstractRenderer { colorType: 6, }); this.lastFrame = PNG.sync.write(empty); + this.ticker = new FPSTicker(args.fps); } public startEncoding() { @@ -31,6 +33,9 @@ export class RealTimeRenderer extends AbstractRenderer { `-s ${this.args.videoWidth}x${this.args.videoHeight}`, // Frame size `-r ${this.args.fps}`, // Framerate ]) + .outputOptions([ + `-vf fps=fps=${this.args.fps}`, // Framerate + ]) .on('start', () => { console.log('FFmpeg process started.'); }) @@ -47,10 +52,9 @@ export class RealTimeRenderer extends AbstractRenderer { command.run(); // Produce frames in required rate - const intervalDuration = Math.round(1000 / 30); - this.intervalId = setInterval(() => { + this.ticker.start(() => { this.inputStream!.write(this.lastFrame); - }, intervalDuration); + }); } public addFrame(frame: Buffer) { @@ -58,7 +62,7 @@ export class RealTimeRenderer extends AbstractRenderer { } public endEncoding() { - clearTimeout(this.intervalId!); + this.ticker.stop(); this.inputStream!.end(); } } \ No newline at end of file diff --git a/src/script/step-recorder.ts b/src/script/step-recorder.ts index 432e2fb..a5505f9 100644 --- a/src/script/step-recorder.ts +++ b/src/script/step-recorder.ts @@ -45,9 +45,6 @@ export class StepRecorder extends AbstractRecorder { this.progressBar.increment(); } - // Finish with en empty frame - this.renderer.addEmptyFrame(); - this.progressBar.stop(); await this.renderer.endEncoding(); } catch (error) { diff --git a/src/script/step-renderer.ts b/src/script/step-renderer.ts index 0ca4224..29d37ba 100644 --- a/src/script/step-renderer.ts +++ b/src/script/step-renderer.ts @@ -61,6 +61,9 @@ export class StepRenderer extends AbstractRenderer { '-f concat', // concat frames from the frame list '-safe 0' // to prevent errors related to unsafe filenames ]) + .outputOptions([ + `-vf fps=fps=${this.args.fps}`, // Framerate + ]) .on('progress', (progress: Object) => { statsPrinter.print(progress); })