add size control

This commit is contained in:
Mylloon 2023-07-30 13:44:47 +02:00
parent 9b6c1eaf62
commit c297fc8204
Signed by: Anri
GPG key ID: A82D63DFF8D1317F
5 changed files with 264 additions and 23 deletions

217
package-lock.json generated
View file

@ -10,6 +10,7 @@
"license": "AGPL-3.0-or-later",
"dependencies": {
"ffmpeg-static": "^5.1.0",
"get-video-duration": "^4.1.0",
"typescript": "^5.1.6"
},
"devDependencies": {
@ -471,6 +472,123 @@
"node": ">=10"
}
},
"node_modules/@ffprobe-installer/darwin-arm64": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/@ffprobe-installer/darwin-arm64/-/darwin-arm64-5.0.1.tgz",
"integrity": "sha512-vwNCNjokH8hfkbl6m95zICHwkSzhEvDC3GVBcUp5HX8+4wsX10SP3B+bGur7XUzTIZ4cQpgJmEIAx6TUwRepMg==",
"cpu": [
"arm64"
],
"hasInstallScript": true,
"optional": true,
"os": [
"darwin"
]
},
"node_modules/@ffprobe-installer/darwin-x64": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/@ffprobe-installer/darwin-x64/-/darwin-x64-5.0.0.tgz",
"integrity": "sha512-Zl0UkZ+wW/eyMKBPLTUCcNQch2VDnZz/cBn1DXv3YtCBVbYd9aYzGj4MImdxgWcoE0+GpbfbO6mKGwMq5HCm6A==",
"cpu": [
"x64"
],
"hasInstallScript": true,
"optional": true,
"os": [
"darwin"
]
},
"node_modules/@ffprobe-installer/ffprobe": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/@ffprobe-installer/ffprobe/-/ffprobe-1.4.1.tgz",
"integrity": "sha512-3WJvxU0f4d7IOZdzoVCAj9fYtiQNC6E0521FJFe9iP5Ej8auTXU7TsrUzIAG1CydeQI+BnM3vGog92SCcF9KtA==",
"optionalDependencies": {
"@ffprobe-installer/darwin-arm64": "5.0.1",
"@ffprobe-installer/darwin-x64": "5.0.0",
"@ffprobe-installer/linux-arm": "5.0.0",
"@ffprobe-installer/linux-arm64": "5.0.0",
"@ffprobe-installer/linux-ia32": "5.0.0",
"@ffprobe-installer/linux-x64": "5.0.0",
"@ffprobe-installer/win32-ia32": "5.0.0",
"@ffprobe-installer/win32-x64": "5.0.0"
}
},
"node_modules/@ffprobe-installer/linux-arm": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/@ffprobe-installer/linux-arm/-/linux-arm-5.0.0.tgz",
"integrity": "sha512-mM1PPxP2UX5SUvhy0urcj5U8UolwbYgmnXA/eBWbW78k6N2Wk1COvcHYzOPs6c5yXXL6oshS2rZHU1kowigw7g==",
"cpu": [
"arm"
],
"hasInstallScript": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@ffprobe-installer/linux-arm64": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/@ffprobe-installer/linux-arm64/-/linux-arm64-5.0.0.tgz",
"integrity": "sha512-IwFbzhe1UydR849YXLPP0RMpHgHXSuPO1kznaCHcU5FscFBV5gOZLkdD8e/xrcC8g/nhKqy0xMjn5kv6KkFQlQ==",
"cpu": [
"arm64"
],
"hasInstallScript": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@ffprobe-installer/linux-ia32": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/@ffprobe-installer/linux-ia32/-/linux-ia32-5.0.0.tgz",
"integrity": "sha512-c3bWlWEDMST59SAZycVh0oyc2eNS/CxxeRjoNryGRgqcZX3EJWJJQL1rAXbpQOMLMi8to1RqnmMuwPJgLLjjUA==",
"cpu": [
"ia32"
],
"hasInstallScript": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@ffprobe-installer/linux-x64": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/@ffprobe-installer/linux-x64/-/linux-x64-5.0.0.tgz",
"integrity": "sha512-zgLnWJFvMGCaw1txGtz84sMEQt6mQUzdw86ih9S/kZOWnp06Gj/ams/EXxEkAxgAACCVM6/O0mkDe/6biY5tgA==",
"cpu": [
"x64"
],
"hasInstallScript": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@ffprobe-installer/win32-ia32": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/@ffprobe-installer/win32-ia32/-/win32-ia32-5.0.0.tgz",
"integrity": "sha512-NnDdAZD6ShFXzJeCkAFl2ZjAv7GcJWYudLA+0T/vjZwvskBop+sq1PGfdmVltfFDcdQiomoThRhn9Xiy9ZC71g==",
"cpu": [
"ia32"
],
"optional": true,
"os": [
"win32"
]
},
"node_modules/@ffprobe-installer/win32-x64": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/@ffprobe-installer/win32-x64/-/win32-x64-5.0.0.tgz",
"integrity": "sha512-P4ZMRFxVMnfMsOyTfBM/+nkTodLeOUfXNPo+X1bKEWBiZxRErqX/IHS5sLA0yAH8XmtKZcL7Cu6M26ztGcQYxw==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"win32"
]
},
"node_modules/@isaacs/cliui": {
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
@ -1288,7 +1406,6 @@
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
"integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
"dev": true,
"dependencies": {
"path-key": "^3.1.0",
"shebang-command": "^2.0.0",
@ -2099,6 +2216,71 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/get-video-duration": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/get-video-duration/-/get-video-duration-4.1.0.tgz",
"integrity": "sha512-kDmnD/C/WFHmZx7eV2duUNCwAH6w9YwpigiUHmq7cRNmxuTvcJ/otvcxa4A5GDus2iSZ+CZEA5e0va/r5ZWumw==",
"dependencies": {
"@ffprobe-installer/ffprobe": "^1.4.1",
"execa": "^5.0.0",
"is-stream": "^2.0.0"
}
},
"node_modules/get-video-duration/node_modules/execa": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
"integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
"dependencies": {
"cross-spawn": "^7.0.3",
"get-stream": "^6.0.0",
"human-signals": "^2.1.0",
"is-stream": "^2.0.0",
"merge-stream": "^2.0.0",
"npm-run-path": "^4.0.1",
"onetime": "^5.1.2",
"signal-exit": "^3.0.3",
"strip-final-newline": "^2.0.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sindresorhus/execa?sponsor=1"
}
},
"node_modules/get-video-duration/node_modules/get-stream": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
"integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/get-video-duration/node_modules/is-stream": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
"integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/get-video-duration/node_modules/npm-run-path": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
"integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
"dependencies": {
"path-key": "^3.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/glob": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
@ -2380,6 +2562,14 @@
"node": ">= 6"
}
},
"node_modules/human-signals": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
"integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==",
"engines": {
"node": ">=10.17.0"
}
},
"node_modules/humanize-ms": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz",
@ -2620,8 +2810,7 @@
"node_modules/isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
"dev": true
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="
},
"node_modules/jackspeak": {
"version": "2.2.2",
@ -2901,6 +3090,11 @@
"node": ">=6"
}
},
"node_modules/merge-stream": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
"integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w=="
},
"node_modules/merge2": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
@ -2927,7 +3121,6 @@
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
"integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
"dev": true,
"engines": {
"node": ">=6"
}
@ -3314,7 +3507,6 @@
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
"integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
"dev": true,
"dependencies": {
"mimic-fn": "^2.1.0"
},
@ -3498,7 +3690,6 @@
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
"dev": true,
"engines": {
"node": ">=8"
}
@ -4117,7 +4308,6 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
"dev": true,
"dependencies": {
"shebang-regex": "^3.0.0"
},
@ -4129,7 +4319,6 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
"dev": true,
"engines": {
"node": ">=8"
}
@ -4137,8 +4326,7 @@
"node_modules/signal-exit": {
"version": "3.0.7",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
"dev": true
"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="
},
"node_modules/slice-ansi": {
"version": "3.0.0",
@ -4342,6 +4530,14 @@
"node": ">=0.10.0"
}
},
"node_modules/strip-final-newline": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
"integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
"engines": {
"node": ">=6"
}
},
"node_modules/strip-outer": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz",
@ -4616,7 +4812,6 @@
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
"dev": true,
"dependencies": {
"isexe": "^2.0.0"
},

View file

@ -19,6 +19,7 @@
"license": "AGPL-3.0-or-later",
"dependencies": {
"ffmpeg-static": "^5.1.0",
"get-video-duration": "^4.1.0",
"typescript": "^5.1.6"
},
"devDependencies": {

View file

@ -1,8 +1,16 @@
import { BrowserWindow, app, dialog, ipcMain } from "electron";
import { unlink } from "fs";
import { unlink, statSync } from "fs";
import path = require("path");
import ffmpegPath = require("ffmpeg-static");
import child_process = require("child_process");
import { getVideoDurationInSeconds } from "get-video-duration";
const moviesFilter = {
name: "Videos",
extensions: ["mp4", "mkv"],
};
const isWindows = process.platform === "win32";
/** Create a new window */
const createWindow = () => {
@ -20,11 +28,6 @@ const createWindow = () => {
return win;
};
const moviesFilter = {
name: "Videos",
extensions: ["mp4", "mkv"],
};
/* Create a new filename from the OG one */
const getNewFilename = (ogFile: string, part: string) => {
const oldFile = path.parse(ogFile);
@ -32,18 +35,18 @@ const getNewFilename = (ogFile: string, part: string) => {
};
/** Merge all audios track of a video into one */
const mergeAudio = (file: string) => {
const mergeAudio = async (file: string) => {
const tmp_file = getNewFilename(file, "TMP_");
const outFile = getNewFilename(file, "(merged audio) ");
// Merge 2 audio
child_process.execSync(
`${ffmpegPath} -i "${file}" -filter_complex "[0:a]amerge=inputs=2[a]" -ac 1 -map 0:v -map "[a]" -c:v copy -y "${tmp_file}"`
`${ffmpegPath} -y -i "${file}" -filter_complex "[0:a]amerge=inputs=2[a]" -ac 1 -map 0:v -map "[a]" -c:v copy "${tmp_file}"`
);
// Add merged audio as first position to original video
child_process.execSync(
`${ffmpegPath} -i "${tmp_file}" -i "${file}" -map 0 -map 1:a -c:v copy -y "${outFile}"`
`${ffmpegPath} -y -i "${tmp_file}" -i "${file}" -map 0 -map 1:a -c:v copy "${outFile}"`
);
// Delete the temporary file
@ -53,7 +56,31 @@ const mergeAudio = (file: string) => {
}
});
return outFile;
const duration = await getVideoDurationInSeconds(outFile);
const stats = statSync(outFile);
return { title: outFile, size: stats.size / 1024 / 1024, duration };
};
/* Reduce size of a file */
const reduceSize = (file: string, bitrate: number) => {
const audioBitrate = 128;
const videoBitrate = Math.floor(bitrate) - audioBitrate;
/* Trash the output, depends on the platform */
const nul = isWindows ? "NUL" : "/dev/null";
/* AND operator, depends on the platform */
const and = isWindows ? "^" : "&&";
const finalFile = getNewFilename(file, "Compressed - ");
child_process.execSync(
`${ffmpegPath} -y -i "${file}" -c:v libx264 -b:v ${videoBitrate}k -pass 1 -an -f null ${nul} ${and} \
${ffmpegPath} -y -i "${file}" -c:v libx264 -b:v ${videoBitrate}k -pass 2 -c:a aac -b:a ${audioBitrate}k "${finalFile}"`
);
return finalFile;
};
/* Ready to create the window */
@ -78,6 +105,9 @@ app.whenReady().then(() => {
ipcMain.handle("allowedExtensions", () => moviesFilter);
ipcMain.handle("askFile", () => askFile());
ipcMain.handle("mergeAudio", (_, file: string) => mergeAudio(file));
ipcMain.handle("reduceSize", (_, file: string, bitrate: number) =>
reduceSize(file, bitrate)
);
ipcMain.handle("exit", async () => app.quit());
ipcMain.handle("confirmation", async (_, text: string) => confirmation(text));
});

View file

@ -6,6 +6,8 @@ contextBridge.exposeInMainWorld("internals", {
allowedExtensions: () => ipcRenderer.invoke("allowedExtensions"),
askFile: () => ipcRenderer.invoke("askFile"),
mergeAudio: (file: string) => ipcRenderer.invoke("mergeAudio", file),
reduceSize: (file: string, bitrate: number) =>
ipcRenderer.invoke("reduceSize", file, bitrate),
exit: () => ipcRenderer.invoke("exit"),
confirmation: (text: string) => ipcRenderer.invoke("confirmation", text),
});

View file

@ -6,7 +6,10 @@ let internals: {
}>;
askFile: () => Promise<string[] | undefined>;
exit: () => Promise<void>;
mergeAudio: (filename: string) => Promise<string>;
mergeAudio: (
filename: string
) => Promise<{ title: string; duration: number; size: number }>;
reduceSize: (file: string, bitrate: number) => Promise<string>;
confirmation: (text: string) => Promise<void>;
};
@ -30,9 +33,19 @@ const getFile = async () => {
/** Main function */
const main = async () => {
const maxSizeDiscord = 25;
const file = await getFile();
const newFile = await internals.mergeAudio(file);
await internals.confirmation(`File ok @ ${newFile}!`);
let finalTitle = newFile.title;
if (newFile.size > maxSizeDiscord) {
const targetSize = maxSizeDiscord - 2;
finalTitle = await internals.reduceSize(
newFile.title,
// https://trac.ffmpeg.org/wiki/Encode/H.264#twopass
(targetSize * 8388.608) / newFile.duration
);
}
await internals.confirmation(`File ok @ ${finalTitle}!`);
await internals.exit();
};
main();