From f9bf73f6d76c1308179586defe734ab56ff9180e Mon Sep 17 00:00:00 2001 From: Djkato Date: Sun, 29 May 2022 01:58:41 +0200 Subject: [PATCH] Changes to encoding, UI fixes --- README.md | 3 + bin/index.js | 5 +- lib/encoder.js | 79 ++++++++++++++++-------- lib/settingsManager.js | 134 ++++------------------------------------- lib/ui.js | 14 ++--- 5 files changed, 80 insertions(+), 155 deletions(-) diff --git a/README.md b/README.md index 3c961e1..f99c238 100644 --- a/README.md +++ b/README.md @@ -28,3 +28,6 @@ How to install(Windows, Linux, MacOS): >2. Add ffmpeg to PATH: `[Win BTN] + R`, type `SystemPropertiesAdvanced`, click `Environment Variables`, under "User variables for (user)" find variable Path, click on it and edit, in the now open window click `new`, and paste `C:\Program Files\DMC\`. >3. If the command `ffmpeg` in cmd works, you can now drag and drop files onto the binary and have it work! >4. to set performance preset, doubleclick the binary + + +For an amazing read on how to optimize vp9 for file sizes I reccomend this read: https://codeberg.org/deterenkelt/Nadeshiko/wiki/Researches%E2%80%89%E2%80%93%E2%80%89VP9-and-overshooting \ No newline at end of file diff --git a/bin/index.js b/bin/index.js index 0d4e427..ea99f08 100644 --- a/bin/index.js +++ b/bin/index.js @@ -67,10 +67,11 @@ async function main(menu = false) { await checkFF() const ui = new UI(settings.settings, settings.currentSetting, settings.settingsFile, filePaths?.length) - if (menu) savesettings = await ui.startMenu() - //file checks let isListEncodable = true + + if (menu) { savesettings = await ui.startMenu(); isListEncodable = false } + //check if all files exist for (let i = 0; i < filePaths.length; i++) { if (!fs.existsSync(filePaths[i])) { diff --git a/lib/encoder.js b/lib/encoder.js index 039a391..5a714a1 100644 --- a/lib/encoder.js +++ b/lib/encoder.js @@ -1,4 +1,5 @@ const { exec } = require('child_process') +const termkit = require('terminal-kit') class Encoder { currentSetting @@ -50,9 +51,9 @@ class Encoder { async #constructVideoCommand(path, out) { let [duration, resolutionHeight] = await this.#getDurationAndResolution(path) - //Calculates video bitrate to fit right under 8mb 2:6 audio:video - const audioBitRate = Math.round(62000 / 8 * 2 / duration) - const videoBitRate = Math.round(62000 / 8 * 6 / duration) + //Calculates video bitrate to fit right under 8mb 2:6 audio:video. 8Mb * 8 = 64000 - 1000 for overhead, *0.97 to leave space for container. + const audioBitRate = Math.round((63000 / 8 * 2 / duration) * 0.97) + const videoBitRate = Math.round((63000 / 8 * 6 / duration) * 0.97) //if command had argument of anotehr quality setting change to use that setting if (this.encodePresetIndexArg) { @@ -60,8 +61,9 @@ class Encoder { } let command = "" - let crfIndex = 0 let isTwoPass = true + /* REMOVING CRF AS ITS NO LONGER USED -- REPLACE WITH -qmin equivalent + //Compares current video height to CRFMAP to determine optimal CRF while (resolutionHeight > this.currentSetting.crfMap[crfIndex].resolution) { crfIndex++ @@ -71,29 +73,58 @@ class Encoder { break } } - - for (let pass = 1; pass <= 2; pass++) { - command += `ffmpeg -y -i "${path}" -vcodec libvpx-vp9 -acodec libvorbis ` + */ + //realtime doesnt support two pass, so just use real time settings + if (this.currentSetting.deadline == "realtime") { + command += `ffmpeg -y -i "${path}" -vcodec libvpx-vp9 -acodec libopus ` command += `-deadline ${this.currentSetting.deadline} ` + command += `-quality ${this.currentSetting.deadline} ` command += `-cpu-used ${this.currentSetting.cpuUsed} ` - if (this.currentSetting?.bitrateError) { - command += `-b:v ${Math.round(videoBitRate / 100 * this.currentSetting.bitrateError)}k ` - command += `-minrate ${Math.round(videoBitRate)}k ` - command += `-maxrate ${videoBitRate}k ` - } - else { - command += `-b:v ${videoBitRate}k ` - command += `-b:a ${audioBitRate}k ` - command += `-crf ${this.currentSetting.crfMap[crfIndex].crf} ` - } - //realtime doesnt support two pass - if (this.currentSetting.deadline == "realtime") { - command += `-row-mt 1 "${out}.webm"` - isTwoPass = false - break - } - pass == 1 ? command += `-pass 1 -row-mt 1 -f webm NUL && ` : command += `-pass 2 -row-mt 1 "${out}.webm" ` + command += `-undershoot-pct 0 -overshoot-pct 0 ` + command += `-b:v ${Math.round(videoBitRate / 100 * this.currentSetting.bitrateError)}k ` + command += `-minrate ${Math.round(videoBitRate * 0.5)}k ` + command += `-maxrate ${Math.floor(videoBitRate * 1.4)}k ` + command += `-b:a ${audioBitRate}k ` + command += `-tile-columns 2 -threads 6 ` + command += `-qmax 60` + command += `-g 240 ` + command += `-row-mt 1 "${out}.webm" ` + return [command, duration, false] } + + //Pass 1 force to have good deadline and cpu-used 1 + command += `ffmpeg -y -i "${path}" -vcodec libvpx-vp9 -acodec libopus ` + command += `-deadline good ` + command += `-quality good ` + command += `-cpu-used 1 ` + command += `-undershoot-pct 0 -overshoot-pct 0 ` + command += `-b:v ${Math.round(videoBitRate / 100 * this.currentSetting.bitrateError)}k ` + command += `-minrate ${Math.round(videoBitRate * 0.5)}k ` + command += `-maxrate ${Math.floor(videoBitRate * 1.2)}k ` + command += `-b:a ${audioBitRate}k ` + command += `-auto-alt-ref 6 ` + command += `-qmax 60` + command += `-g 240 ` + + command += `-row-mt 1 -pass 1 -f webm NUL && ` + + //Pass 2 take in settings + command += `ffmpeg -y -i "${path}" -vcodec libvpx-vp9 -acodec libopus ` + command += `-deadline ${this.currentSetting.deadline} ` + command += `-quality ${this.currentSetting.deadline} ` + command += `-cpu-used ${this.currentSetting.cpuUsed} ` + command += `-undershoot-pct 0 -overshoot-pct 0 ` + command += `-b:v ${Math.round(videoBitRate / 100 * this.currentSetting.bitrateError)}k ` + command += `-minrate ${Math.round(videoBitRate * 0.5)}k ` + command += `-maxrate ${Math.floor(videoBitRate * 1.4)}k ` + command += `-b:a ${audioBitRate}k ` + command += `-tile-columns 2 -threads 6 ` + command += `-auto-alt-ref 6 ` + command += `-qmax 60` + command += `-g 240 ` + + command += `-row-mt 1 -pass 2 "${out}.webm" ` + return [command, duration, isTwoPass] } diff --git a/lib/settingsManager.js b/lib/settingsManager.js index 80a486a..5f3bad7 100644 --- a/lib/settingsManager.js +++ b/lib/settingsManager.js @@ -34,143 +34,33 @@ class SettingsManager { } async #makeNewSettingsFile() { const settings = ` - { - "currentSetting": 3, +{ + "currentSetting": 2, "presets": [{ "name": "Most efficient 8 megabytes of your life", "cpuUsed": 0, - "deadline": "best", - "bitrateError": 90, - "crfMap": [{ - "resolution": 240, - "crf": 1 - }, { - "resolution": 360, - "crf": 1 - }, { - "resolution": 480, - "crf": 1 - }, { - "resolution": 720, - "crf": 1 - }, { - "resolution": 1080, - "crf": 1 - }, { - "resolution": 1440, - "crf": 1 - }, { - "resolution": 2160, - "crf": 1 - }] + "deadline": "good", + "bitrateError": 95 }, { "name": "I have some time to kill", "cpuUsed": 1, "deadline": "good", - "bitrateError": 90, - "crfMap": [{ - "resolution": 240, - "crf": 20 - }, { - "resolution": 360, - "crf": 20 - }, { - "resolution": 480, - "crf": 20 - }, { - "resolution": 720, - "crf": 20 - }, { - "resolution": 1080, - "crf": 17 - }, { - "resolution": 1440, - "crf": 15 - }, { - "resolution": 2160, - "crf": 10 - }] + "bitrateError": 95 }, { "name": "Mid", "cpuUsed": 3, - "deadline": "good", - "bitrateError": 80, - "crfMap": [{ - "resolution": 240, - "crf": 30 - }, { - "resolution": 360, - "crf": 30 - }, { - "resolution": 480, - "crf": 30 - }, { - "resolution": 720, - "crf": 25 - }, { - "resolution": 1080, - "crf": 20 - }, { - "resolution": 1440, - "crf": 15 - }, { - "resolution": 2160, - "crf": 10 - }] + "deadline": "realtime", + "bitrateError": 90 }, { "name": "I don't like waiting", - "cpuUsed": 4, - "deadline": 100, - "bitrateError": 70, - "crfMap": [{ - "resolution": 240, - "crf": 45 - }, { - "resolution": 360, - "crf": 42 - }, { - "resolution": 480, - "crf": 40 - }, { - "resolution": 720, - "crf": 35 - }, { - "resolution": 1080, - "crf": 30 - }, { - "resolution": 1440, - "crf": 25 - }, { - "resolution": 2160, - "crf": 20 - }] + "cpuUsed": 5, + "deadline": "realtime", + "bitrateError": 80 }, { "name": "I want it, NOW!", - "cpuUsed": 3, + "cpuUsed": 6, "deadline": "realtime", - "bitrateError": 50, - "crfMap": [{ - "resolution": 240, - "crf": 40 - }, { - "resolution": 360, - "crf": 35 - }, { - "resolution": 480, - "crf": 30 - }, { - "resolution": 720, - "crf": 25 - }, { - "resolution": 1080, - "crf": 20 - }, { - "resolution": 1440, - "crf": 15 - }, { - "resolution": 2160, - "crf": 10 - }] + "bitrateError": 70 }] } ` diff --git a/lib/ui.js b/lib/ui.js index 74cac90..4ec5f5e 100644 --- a/lib/ui.js +++ b/lib/ui.js @@ -13,7 +13,7 @@ class UI { this.term = termkit.terminal this.multibar = new cliProgress.MultiBar({ - format: '[{bar}] {percentage}% | output: "{filename}" | ETA: {eta_formatted}s | {duration_formatted} | {value}s/{total}s ', + format: '[{bar}] {percentage}% | output: "{filename}" | ETA: {eta_formatted} | Elapsed: {duration_formatted} | {value}s/{total}s ', align: "left", hideCursor: true, autopadding: true, @@ -100,17 +100,17 @@ class UI { async startMenu() { await this.#menu() } - async #menu(settingsFile) { + async #menu() { let menu = [] for (let i = 0; i < this.settings.presets.length; i++) { menu.push(`${i}. ${this.settings.presets[i].name}`) } - this.term("How to convert: 8mb [optional: -preset {Index}] [filename.extension(s)]\n") - this.term("examples: \n") - this.term.italic(" 8mb -preset 0 file.mp3 file4.mov img.jpg\n") - this.term.italic(" 8mb.exe file34.wav file2.mp3\n\n") + this.term.grey("How to convert: 8mb [optional: -preset {Index}] [filename.extension(s)]\n") + this.term.grey("examples: \n") + this.term.italic.grey(" 8mb -preset 0 file.mp3 file4.mov img.jpg\n") + this.term.italic.grey(" 8mb file34.wav file2.mp3\n\n") this.term.yellow("Hello! This menu is for selecting performance/speed preset.\n") - this.term.yellow("Currently using ").bgMagenta(`"${this.settings.presets[this.settings.currentSetting].name}"`).yellow(" preset") + this.term.yellow("Currently using ").bgMagenta(`"${this.settings.presets[this.settings.currentSetting].name}"`).yellow(" preset\n") this.term.singleColumnMenu(menu, (error, response) => { this.settings.currentSetting = response.selectedIndex this.term.green("\n Using").green.bold(` ${this.settings.presets[this.settings.currentSetting].name} `).green("setting\n")