Created
August 13, 2022 20:09
-
-
Save FelicitusNeko/e221ab86b42532d3de17201e79faf36c to your computer and use it in GitHub Desktop.
A script I just made to convert lossless OBS .MKV recordings to compressed .MP4 files, also outputting per-track .MP3 files. Requires ffmpeg on path, as well as TeraCopy to move converted files.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Copyright (c) 2022 FelicitusNeko | |
| // | |
| // This software is released under the MIT License. | |
| // https://opensource.org/licenses/MIT | |
| const { readdirSync, writeFileSync, unlinkSync } = require("fs"); | |
| const { spawn } = require("child_process"); | |
| const { join, basename } = require("path"); | |
| const convertTarget = "C:\\Path\\To\\ConvertedFiles"; | |
| const completeTarget = "C:\\Path\\To\\RawFiles"; | |
| const pathTeracopy = "C:\\Program Files\\TeraCopy\\TeraCopy.exe" | |
| const fileList = join(__dirname, "file.list"); | |
| const spawnFfmpeg = (file, output) => new Promise((f, r) => { | |
| const fileIsArray = Array.isArray(file); | |
| if (output == null) { | |
| if (!fileIsArray) output = basename(file, ".mkv"); | |
| else r(new Exception("Output pattern must be specified if array of files is provided")); | |
| } | |
| const params = [ | |
| "-map", "0:v", "-map", "0:a:0", // Grab the video and primary audio track | |
| "-c:v", "libx264", | |
| "-preset", "veryslow", | |
| "-tune", "animation", | |
| "-crf", "20", | |
| "-max_muxing_queue_size", "1024", // TODO: maybe this can be automatically adjusted as needed | |
| "-c:a", "libmp3lame", | |
| "-q:a", "2", | |
| join(convertTarget, `${output}.mp4`), | |
| "-map", "0:a:1", "-vn", // Grab the mic track | |
| "-c:a", "libmp3lame", | |
| "-q:a", "2", | |
| join(convertTarget, `${output}-mic.mp3`), | |
| "-map", "0:a:2", "-vn", // Grab the game audio track | |
| "-c:a", "libmp3lame", | |
| "-q:a", "2", | |
| join(convertTarget, `${output}-game.mp3`), | |
| "-map", "0:a:3", "-vn", // Grab the guest track | |
| "-c:a", "libmp3lame", | |
| "-q:a", "2", | |
| join(convertTarget, `${output}-guest.mp3`) | |
| ]; | |
| if (fileIsArray) params.unshift( | |
| "-protocol_whitelist", "pipe,file", // so that we can pipe in a concat list without creating it | |
| "-f", "concat", | |
| "-safe", "0", // because we're using full path defs, which ffmpeg doesn't like | |
| "-i", "-" // read stdin | |
| ) | |
| else params.unshift("-i", file); | |
| var proc = spawn("ffmpeg.exe", params, { | |
| stdio: ["pipe", "inherit", "inherit"] | |
| }); | |
| if (fileIsArray) { | |
| proc.stdin.write(file.map(i => `file '${join(__dirname, i)}'`).join("\n")); | |
| proc.stdin.end(); | |
| } | |
| proc.on("close", code => code == 0 ? f() : r(code)); | |
| }); | |
| const spawnTeracopy = () => new Promise((f, r) => { | |
| const proc = spawn(pathTeracopy, [ | |
| "Move", | |
| `*${fileList}`, | |
| completeTarget, | |
| ], { stdio: "ignore" }); | |
| proc.on("close", code => code == 0 ? f() : r(code)); | |
| }); | |
| (async () => { | |
| const files = | |
| readdirSync(__dirname, { withFileTypes: true }) | |
| .filter(file => file.isFile() && file.name.endsWith(".mkv")) | |
| .sort((a, b) => a.name.localeCompare(b.name)); | |
| const concats = {}; | |
| for (const file of files) { | |
| const { name } = file, base = basename(name, ".mkv"); | |
| const pattern = /^(.*)\.(\d+)/.exec(base); | |
| if (pattern != null) { | |
| const [_, combineName, part] = pattern; | |
| if (!concats[combineName]) concats[combineName] = []; | |
| concats[combineName][Number.parseInt(part)] = name; | |
| } | |
| else await spawnFfmpeg(name); | |
| } | |
| for (const combineName in concats) { | |
| const parts = concats[combineName].filter(i => i != null); | |
| await spawnFfmpeg(parts, combineName); | |
| } | |
| writeFileSync(fileList, files.map(file => join(__dirname, file.name)).join("\n")); | |
| await spawnTeracopy(); | |
| unlinkSync(fileList); | |
| })(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment