Compare commits
39 commits
Author | SHA1 | Date | |
---|---|---|---|
87f98a586e | |||
74b5f09442 | |||
c28e4559ca | |||
4c2c5f419a | |||
4267038983 | |||
c1168d1d58 | |||
010610bd5c | |||
6c9cd5deb4 | |||
604d7a0268 | |||
e91aed0020 | |||
13a8ef86cb | |||
ac60082b59 | |||
c11f2d1d32 | |||
c1a172d5d7 | |||
44f6b0d48b | |||
112770ec68 | |||
f6feddd4c3 | |||
397c71f9da | |||
812a216902 | |||
56936cda9f | |||
77c0edb15a | |||
1f0d18de51 | |||
06851a2c3c | |||
02a2059552 | |||
e5e8f002d8 | |||
b63a38a21b | |||
41b59d65b0 | |||
2e1179aa27 | |||
0907480386 | |||
01f0d2702f | |||
2f8e02a29d | |||
0010aa0a33 | |||
74b5be4e3b | |||
e1aad3fb1e | |||
f590f77bca | |||
ecb03ded2a | |||
21b39e80bf | |||
55b94d0021 | |||
4f34191d2f |
11 changed files with 509 additions and 963 deletions
|
@ -7,22 +7,35 @@ on:
|
|||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: docker
|
||||
container:
|
||||
image: node
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
npm install --platform=win32 &&
|
||||
apt-get update &&
|
||||
apt-get install -y zip
|
||||
|
||||
- name: Build the app
|
||||
run: npm run make -- --platform=win32
|
||||
- name: Build the app for Windows
|
||||
run: |
|
||||
npm install --platform=win32 &&
|
||||
npm run make -- --platform=win32
|
||||
|
||||
- name: Clear the node packages
|
||||
run: rm -rf node_modules/ package-lock.json
|
||||
|
||||
- name: Build the app for Linux
|
||||
run: |
|
||||
npm install --platform=linux &&
|
||||
npm run make -- --platform=linux
|
||||
|
||||
- name: Create release
|
||||
uses: akkuman/gitea-release-action@v1
|
||||
with:
|
||||
token: ${{ secrets.TOKEN }}
|
||||
files: out/make/zip/win32/x64/dsr-win32-x64-${{ github.ref_name }}.zip
|
||||
files: out/make/zip/**/*.zip
|
||||
draft: true
|
||||
|
|
28
README.md
28
README.md
|
@ -2,8 +2,6 @@
|
|||
|
||||
Tool for sharing video to Discord.
|
||||
|
||||
> This tool was primarily made for video captured by NVidia Shadowplay.
|
||||
|
||||
## Download/Install/Update
|
||||
|
||||
2 choices :
|
||||
|
@ -15,20 +13,27 @@ Tool for sharing video to Discord.
|
|||
irm https://git.mylloon.fr/Anri/dsr/raw/branch/main/install.ps1 | iex
|
||||
```
|
||||
|
||||
### Linux
|
||||
|
||||
Install from AUR: [dsr](https://aur.archlinux.org/packages/dsr)
|
||||
|
||||
## Available flags
|
||||
|
||||
You can add thoses flags in the `Target` field of your Windows shortcut.
|
||||
|
||||
| | |
|
||||
| ------------- | ----------------------------------------------------- |
|
||||
| `/nitro` | Increase the file limit to 500Mo |
|
||||
| `/nitrobasic` | Increase the file limit to 50Mo |
|
||||
| | |
|
||||
| `/nvenc_h264` | Enable NVenc with H.264 encoder (NVidia GPU required) |
|
||||
| `/nvenc_h265` | Enable NVenc with H.265 encoder (NVidia GPU required) |
|
||||
| `/h265` | Enable the H.265 CPU encoder (slow compression) |
|
||||
| | |
|
||||
| ------------- | ------------------------------------------------------ |
|
||||
| `/nitro` | Increase the file limit to 500Mo |
|
||||
| `/nitrobasic` | Increase the file limit to 50Mo |
|
||||
| | |
|
||||
| `/nvenc_h264` | Enable NVenc with H.264 encoder (NVidia GPU required) |
|
||||
| `/nvenc_h265` | Enable NVenc with H.265 encoder (NVidia GPU required) |
|
||||
| `/amd_h264` | Enable AMF using DX11 with H.264 encoder (for AMD GPU) |
|
||||
| `/amd_h265` | Enable AMF using DX11 with H.265 encoder (for AMD GPU) |
|
||||
| `/h265` | Enable the H.265 CPU encoder (slow compression) |
|
||||
|
||||
> NVenc support is experimental, but faster than CPU counterparts.
|
||||
> NVidia and AMD hardware accelerators support is experimental, but faster
|
||||
> than CPU counterparts.
|
||||
|
||||
## More info
|
||||
|
||||
|
@ -38,6 +43,7 @@ You can add thoses flags in the `Target` field of your Windows shortcut.
|
|||
- [x] Defaults to H.264 CPU encoder
|
||||
- [x] If already under the limit, the file won't be compressed
|
||||
- [x] NVenc support
|
||||
- [x] AMD cards acceleration support
|
||||
- [x] Nitro suppport
|
||||
- [x] Merge 2 audio files into one track when recorded with system audio and microphone
|
||||
split up, while keeping the original ones (with conveniant metadata)
|
||||
|
|
BIN
image/icon.png
Normal file
BIN
image/icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 21 KiB |
|
@ -7,12 +7,13 @@ param (
|
|||
|
||||
$path = "$env:LOCALAPPDATA\DSR"
|
||||
$update = Test-Path -Path $path\*
|
||||
$iwa = "-UserAgent 'confOS'"
|
||||
|
||||
# Download
|
||||
$releases = "https://git.mylloon.fr/api/v1/repos/Anri/dsr/releases/latest"
|
||||
$link = (Invoke-WebRequest $releases | ConvertFrom-Json)[0].assets.browser_download_url
|
||||
$link = (Invoke-WebRequest $iwa $releases | ConvertFrom-Json)[0].assets.browser_download_url
|
||||
$archive = "$env:TEMP\dsr.zip"
|
||||
Invoke-WebRequest -Uri $link -OutFile $archive
|
||||
Invoke-WebRequest $iwa -Uri $link -OutFile $archive
|
||||
Remove-Item "$path" -Recurse -ErrorAction SilentlyContinue
|
||||
|
||||
# Close running DSR
|
||||
|
|
1144
package-lock.json
generated
1144
package-lock.json
generated
File diff suppressed because it is too large
Load diff
13
package.json
13
package.json
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "dsr",
|
||||
"version": "1.11.0",
|
||||
"version": "1.14.1",
|
||||
"description": "Discord Video Sharing",
|
||||
"main": "./dist/main.js",
|
||||
"scripts": {
|
||||
|
@ -18,15 +18,15 @@
|
|||
"author": "Mylloon",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"@electron-forge/maker-zip": "^7.4",
|
||||
"@electron-forge/maker-zip": "^7.8",
|
||||
"ffmpeg-static": "^5.2",
|
||||
"ffprobe-static": "^3.1",
|
||||
"typescript": "^5.6"
|
||||
"typescript": "^5.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@electron-forge/cli": "^7.4",
|
||||
"@electron-forge/cli": "^7.8",
|
||||
"@types/ffprobe-static": "^2.0",
|
||||
"electron": "^32.1"
|
||||
"electron": "^37.2"
|
||||
},
|
||||
"config": {
|
||||
"forge": {
|
||||
|
@ -40,7 +40,8 @@
|
|||
{
|
||||
"name": "@electron-forge/maker-zip",
|
||||
"platforms": [
|
||||
"win32"
|
||||
"win32",
|
||||
"linux"
|
||||
]
|
||||
}
|
||||
]
|
||||
|
|
|
@ -1,15 +1,9 @@
|
|||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta
|
||||
http-equiv="Content-Security-Policy"
|
||||
content="default-src 'self'; script-src 'self'"
|
||||
/>
|
||||
<meta
|
||||
http-equiv="X-Content-Security-Policy"
|
||||
content="default-src 'self'; script-src 'self'"
|
||||
/>
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'" />
|
||||
<meta http-equiv="X-Content-Security-Policy" content="default-src 'self'; script-src 'self'" />
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="../css/style.css" />
|
||||
|
||||
|
|
165
src/main.ts
165
src/main.ts
|
@ -13,7 +13,15 @@ import {
|
|||
import path = require("path");
|
||||
|
||||
import ffmpeg = require("ffmpeg-static");
|
||||
const ffmpegPath = `${ffmpeg}`.replace("app.asar", "app.asar.unpacked");
|
||||
let ffmpegPath;
|
||||
try {
|
||||
ffmpegPath = "ffmpeg";
|
||||
require("child_process").execSync(`${ffmpegPath} -version`, {
|
||||
stdio: "ignore",
|
||||
});
|
||||
} catch {
|
||||
ffmpegPath = `${ffmpeg}`.replace("app.asar", "app.asar.unpacked");
|
||||
}
|
||||
|
||||
let error = false;
|
||||
|
||||
|
@ -35,12 +43,14 @@ const registerError = (win: BrowserWindow, err: string) => {
|
|||
printAndDevTool(win, err);
|
||||
};
|
||||
|
||||
const onWindows = process.platform === "win32";
|
||||
|
||||
/** Create a new window */
|
||||
const createWindow = () => {
|
||||
const win = new BrowserWindow({
|
||||
width: 600,
|
||||
height: 340,
|
||||
icon: "./image/icon.ico",
|
||||
icon: "./image/icon." + (onWindows ? "ico" : "png"),
|
||||
autoHideMenuBar: true,
|
||||
webPreferences: {
|
||||
preload: path.join(__dirname, "preload.js"),
|
||||
|
@ -53,7 +63,7 @@ const createWindow = () => {
|
|||
};
|
||||
|
||||
// For notification on Windows
|
||||
if (process.platform === "win32") {
|
||||
if (onWindows) {
|
||||
app.setAppUserModelId(app.name);
|
||||
}
|
||||
|
||||
|
@ -101,7 +111,7 @@ app.whenReady().then(() => {
|
|||
-i "${file}" \
|
||||
-filter_complex "[0:a]amerge=inputs=2[a]" -ac 2 -map 0:v -map "[a]" \
|
||||
-c:v copy \
|
||||
"${tmpFile}"`
|
||||
"${tmpFile}"`,
|
||||
);
|
||||
|
||||
outFile = getNewFilename(file, "(merged audio) ");
|
||||
|
@ -115,7 +125,7 @@ app.whenReady().then(() => {
|
|||
-map 0 -map 1:a -c:v copy \
|
||||
-disposition:a 0 -disposition:a:0 default \
|
||||
${metadataAudio} \
|
||||
"${outFile}"`
|
||||
"${outFile}"`,
|
||||
).catch((e) => registerError(win, e));
|
||||
|
||||
// Delete the temporary video file
|
||||
|
@ -144,75 +154,91 @@ app.whenReady().then(() => {
|
|||
};
|
||||
};
|
||||
|
||||
/** Reduce size of a file */
|
||||
const reduceSize = async (
|
||||
file: string,
|
||||
bitrate: number,
|
||||
audioTracks: number[]
|
||||
) => {
|
||||
const audioBitrate = Math.ceil(
|
||||
audioTracks.reduce((sum, current) => current + sum, 0)
|
||||
);
|
||||
let videoBitrate = bitrate - audioBitrate;
|
||||
/** Reduce size of a file
|
||||
* Returns an empty string in case of failing
|
||||
*/
|
||||
const reduceSize = async (file: string, bitrate: number, audioTracks: number[]) => {
|
||||
const audioBitratePerTrack = 128; // kbps
|
||||
|
||||
const finalFile = getNewFilename(file, "Compressed - ");
|
||||
// Calculate audio bitrate
|
||||
const audioBitrate = audioTracks.length * audioBitratePerTrack;
|
||||
const videoBitrate = bitrate - audioBitrate;
|
||||
let finalFile;
|
||||
|
||||
// Trash the output, depends on the platform
|
||||
const nul = process.platform === "win32" ? "NUL" : "/dev/null";
|
||||
if (videoBitrate > 0) {
|
||||
finalFile = getNewFilename(file, "Compressed - ");
|
||||
|
||||
// Mapping of tracks for FFMPEG, adding 1 for the video stream
|
||||
const mappingTracks = Array(audioTracks.length + 1)
|
||||
.fill("-map 0:")
|
||||
.map((str, index) => {
|
||||
return str + index;
|
||||
})
|
||||
.join(" ");
|
||||
// Trash the output, depends on the platform
|
||||
const nul = onWindows ? "NUL" : "/dev/null";
|
||||
|
||||
let codec = "libx264";
|
||||
let hwAcc = "";
|
||||
// Mapping of tracks for FFMPEG, adding 1 for the video stream
|
||||
const mappingTracks = Array(audioTracks.length + 1)
|
||||
.fill("-map 0:")
|
||||
.map((str, index) => {
|
||||
return str + index;
|
||||
})
|
||||
.join(" ");
|
||||
|
||||
const argv = process.argv;
|
||||
if (argv.includes("/nvenc_h264")) {
|
||||
// Use NVenc H.264
|
||||
codec = "h264_nvenc";
|
||||
hwAcc = "-hwaccel cuda";
|
||||
let codec = "libx264";
|
||||
let hwAcc = "";
|
||||
|
||||
const argv = process.argv;
|
||||
if (argv.includes("/nvenc_h264")) {
|
||||
// Use NVenc H.264
|
||||
codec = "h264_nvenc";
|
||||
hwAcc = onWindows ? "-hwaccel cuda" : "-hwaccel cuda -hwaccel_output_format cuda";
|
||||
}
|
||||
|
||||
if (argv.includes("/amd_h264")) {
|
||||
// Use AMF H.264
|
||||
codec = onWindows ? "h264_amf" : "h264_vaapi";
|
||||
hwAcc = onWindows ? "-hwaccel d3d11va" : "-hwaccel vaapi -hwaccel_output_format vaapi";
|
||||
}
|
||||
|
||||
if (argv.includes("/nvenc_h265")) {
|
||||
// Use NVenc H.265
|
||||
codec = "hevc_nvenc";
|
||||
hwAcc = onWindows ? "-hwaccel cuda" : "-hwaccel cuda -hwaccel_output_format cuda";
|
||||
}
|
||||
|
||||
if (argv.includes("/amd_h265")) {
|
||||
// Use AMF H.265
|
||||
codec = onWindows ? "hevc_amf" : "hevc_vaapi";
|
||||
hwAcc = onWindows ? "-hwaccel d3d11va" : "-hwaccel vaapi -hwaccel_output_format vaapi";
|
||||
}
|
||||
|
||||
if (argv.includes("/h265")) {
|
||||
// Use H.265 encoder
|
||||
codec = "libx265";
|
||||
}
|
||||
|
||||
// Compress the video with AAC audio compression
|
||||
// Add metadata to audio's track
|
||||
await execute(
|
||||
`"${ffmpegPath}" -y ${hwAcc} \
|
||||
-i "${file}" \
|
||||
-c:v ${codec} -b:v ${videoBitrate}k -pass 1 -an -f mp4 \
|
||||
${nul} \
|
||||
&& \
|
||||
"${ffmpegPath}" -y ${hwAcc} \
|
||||
-i "${file}" \
|
||||
-c:v ${codec} -b:v ${videoBitrate}k -pass 2 -c:a aac -b:a ${audioBitratePerTrack}k \
|
||||
${mappingTracks} -f mp4 \
|
||||
-profile:v main \
|
||||
${audioTracks.length === metadataAudioSize ? metadataAudio : ""} \
|
||||
${shareOpt} \
|
||||
"${finalFile}"`,
|
||||
).catch((e) => registerError(win, e));
|
||||
|
||||
// Delete the 2 pass temporary files
|
||||
deleteTwoPassFiles(process.cwd());
|
||||
} else {
|
||||
finalFile = "";
|
||||
}
|
||||
|
||||
if (argv.includes("/nvenc_h265")) {
|
||||
// Use NVenc H.265
|
||||
codec = "hevc_nvenc";
|
||||
hwAcc = "-hwaccel cuda";
|
||||
}
|
||||
|
||||
if (argv.includes("/h265")) {
|
||||
// Use H.265 encoder
|
||||
codec = "libx265";
|
||||
}
|
||||
|
||||
// Compress the video
|
||||
// Add metadata to audio's track
|
||||
await execute(
|
||||
`"${ffmpegPath}" -y ${hwAcc} \
|
||||
-i "${file}" \
|
||||
-c:v ${codec} -b:v ${videoBitrate}k -pass 1 -an -f mp4 \
|
||||
${nul} \
|
||||
&& \
|
||||
"${ffmpegPath}" -y ${hwAcc} \
|
||||
-i "${file}" \
|
||||
-c:v ${codec} -b:v ${videoBitrate}k -pass 2 -c:a copy \
|
||||
${mappingTracks} -f mp4 \
|
||||
-profile:v main \
|
||||
${audioTracks.length === metadataAudioSize ? metadataAudio : ""} \
|
||||
${shareOpt} \
|
||||
"${finalFile}"`
|
||||
).catch((e) => registerError(win, e));
|
||||
|
||||
// Delete the old video file
|
||||
deleteFile(file);
|
||||
|
||||
// Delete the 2 pass temporary files
|
||||
deleteTwoPassFiles(process.cwd());
|
||||
|
||||
return finalFile;
|
||||
};
|
||||
|
||||
|
@ -227,7 +253,7 @@ app.whenReady().then(() => {
|
|||
-map 0 -codec copy \
|
||||
${shareOpt} \
|
||||
${nbTracks === metadataAudioSize ? metadataAudio : ""} \
|
||||
"${finalFile}"`
|
||||
"${finalFile}"`,
|
||||
).catch((e) => registerError(win, e));
|
||||
|
||||
// Delete the old video file
|
||||
|
@ -238,17 +264,16 @@ app.whenReady().then(() => {
|
|||
|
||||
/* Context bridge */
|
||||
ipcMain.handle("argv", () => process.argv);
|
||||
ipcMain.handle("cwd", process.cwd);
|
||||
ipcMain.handle("allowedExtensions", () => moviesFilter);
|
||||
ipcMain.handle("getFilename", (_, filepath: string) => getFilename(filepath));
|
||||
ipcMain.handle("askFiles", () => askFiles());
|
||||
ipcMain.handle("mergeAudio", (_, file: string) => mergeAudio(file));
|
||||
ipcMain.handle(
|
||||
"reduceSize",
|
||||
(_, file: string, bitrate: number, audioTracks: number[]) =>
|
||||
reduceSize(file, bitrate, audioTracks)
|
||||
ipcMain.handle("reduceSize", (_, file: string, bitrate: number, audioTracks: number[]) =>
|
||||
reduceSize(file, bitrate, audioTracks),
|
||||
);
|
||||
ipcMain.handle("moveMetadata", (_, file: string, nbTracks: number) =>
|
||||
moveMetadata(file, nbTracks)
|
||||
moveMetadata(file, nbTracks),
|
||||
);
|
||||
ipcMain.handle("exit", () => (error ? {} : app.quit()));
|
||||
ipcMain.handle("confirmation", (_, text: string) => confirmation(text));
|
||||
|
|
|
@ -8,9 +8,9 @@ ipcRenderer.on("error", (_, err) => {
|
|||
/* Context bridge */
|
||||
contextBridge.exposeInMainWorld("internals", {
|
||||
argv: () => ipcRenderer.invoke("argv"),
|
||||
cwd: () => ipcRenderer.invoke("cwd"),
|
||||
allowedExtensions: () => ipcRenderer.invoke("allowedExtensions"),
|
||||
getFilename: (filepath: string) =>
|
||||
ipcRenderer.invoke("getFilename", filepath),
|
||||
getFilename: (filepath: string) => ipcRenderer.invoke("getFilename", filepath),
|
||||
askFiles: () => ipcRenderer.invoke("askFiles"),
|
||||
mergeAudio: (file: string) => ipcRenderer.invoke("mergeAudio", file),
|
||||
reduceSize: (file: string, bitrate: number, audioTracks: number[]) =>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/** Context bridge types */
|
||||
let internals: {
|
||||
argv: () => Promise<string[]>;
|
||||
cwd: () => Promise<string>;
|
||||
allowedExtensions: () => Promise<{
|
||||
extensions: string[];
|
||||
}>;
|
||||
|
@ -13,11 +14,7 @@ let internals: {
|
|||
size: number;
|
||||
audioTracks: number[];
|
||||
}>;
|
||||
reduceSize: (
|
||||
file: string,
|
||||
bitrate: number,
|
||||
audioTracks: number[]
|
||||
) => Promise<string>;
|
||||
reduceSize: (file: string, bitrate: number, audioTracks: number[]) => Promise<string>;
|
||||
moveMetadata: (file: string, nbTracks: number) => Promise<string>;
|
||||
confirmation: (text: string) => Promise<void>;
|
||||
};
|
||||
|
@ -25,9 +22,17 @@ let internals: {
|
|||
/** Search for files */
|
||||
const getFiles = async () => {
|
||||
const allowedExtensions = (await internals.allowedExtensions()).extensions;
|
||||
const currentDir = await internals.cwd();
|
||||
const argvFiles = (await internals.argv())
|
||||
.slice(1)
|
||||
.filter((element) => !element.startsWith("/"));
|
||||
.filter((element) => {
|
||||
if (element.startsWith("/")) {
|
||||
// Slash either fullpath files, or commands args
|
||||
return element.startsWith(currentDir);
|
||||
}
|
||||
return true;
|
||||
})
|
||||
.map((element) => element.split("/").pop());
|
||||
|
||||
if (argvFiles.length > 0) {
|
||||
const files = argvFiles;
|
||||
|
@ -35,9 +40,7 @@ const getFiles = async () => {
|
|||
// Exit if a file isn't supported in the list
|
||||
if (
|
||||
files.filter((file) =>
|
||||
allowedExtensions.some((ext) =>
|
||||
file.toLowerCase().endsWith(ext.toLowerCase())
|
||||
)
|
||||
allowedExtensions.some((ext) => file.toLowerCase().endsWith(ext.toLowerCase())),
|
||||
).length !== files.length
|
||||
) {
|
||||
await internals.exit();
|
||||
|
@ -75,11 +78,7 @@ enum Mode {
|
|||
}
|
||||
|
||||
/** Update the message to the user */
|
||||
const updateMessage = (
|
||||
message: string,
|
||||
load: boolean = false,
|
||||
mode: Mode = Mode.Write
|
||||
) => {
|
||||
const updateMessage = (message: string, load: boolean = false, mode: Mode = Mode.Write) => {
|
||||
switch (mode) {
|
||||
case Mode.Write:
|
||||
document.getElementById("message").innerText = message;
|
||||
|
@ -92,9 +91,7 @@ const updateMessage = (
|
|||
default:
|
||||
break;
|
||||
}
|
||||
document.getElementById("load").style.visibility = load
|
||||
? "visible"
|
||||
: "hidden";
|
||||
document.getElementById("load").style.visibility = load ? "visible" : "hidden";
|
||||
};
|
||||
|
||||
/** Main function */
|
||||
|
@ -103,20 +100,16 @@ const main = async () => {
|
|||
updateMessage("Récupération des fichiers...");
|
||||
const files = await getFiles();
|
||||
let processedFiles = "";
|
||||
let numberOfUncompressableFiles = 0;
|
||||
|
||||
// Iterate over all the retrieved files
|
||||
for (const [idx, file] of files.entries()) {
|
||||
const counter = `${idx + 1}/${files.length}`;
|
||||
const filename = await internals.getFilename(file);
|
||||
updateMessage(
|
||||
`${counter} - Mélange des pistes audios de ${filename}...`,
|
||||
true
|
||||
);
|
||||
updateMessage(`${counter} - Mélange des pistes audios de ${filename}...`, true);
|
||||
const newFile = await internals.mergeAudio(file);
|
||||
let finalTitle = newFile.title;
|
||||
updateMessage(
|
||||
`${counter} - Taille calculée : ~${Math.round(newFile.size)}Mio`
|
||||
);
|
||||
updateMessage(`${counter} - Taille calculée : ~${Math.round(newFile.size)}Mio`);
|
||||
|
||||
// Compress video if needed
|
||||
if (newFile.size > maxSizeDiscord) {
|
||||
|
@ -128,34 +121,39 @@ const main = async () => {
|
|||
updateMessage(
|
||||
`\nFichier trop lourd, compression en cours... (bitrate total = ${bitrate}kbps)`,
|
||||
true,
|
||||
Mode.Append
|
||||
Mode.Append,
|
||||
);
|
||||
|
||||
// Compress the video and change the title to the new one
|
||||
finalTitle = await internals.reduceSize(
|
||||
newFile.title,
|
||||
bitrate,
|
||||
newFile.audioTracks
|
||||
);
|
||||
finalTitle = await internals.reduceSize(newFile.title, bitrate, newFile.audioTracks);
|
||||
} else {
|
||||
updateMessage(`\nPréparation pour le partage...`, true, Mode.Append);
|
||||
|
||||
// Move the metadata to make it playable before everything is downloaded
|
||||
finalTitle = await internals.moveMetadata(
|
||||
newFile.title,
|
||||
newFile.audioTracks.length
|
||||
);
|
||||
finalTitle = await internals.moveMetadata(newFile.title, newFile.audioTracks.length);
|
||||
}
|
||||
|
||||
// Append title to the list of processed files
|
||||
processedFiles += `\n- ${finalTitle}`;
|
||||
updateMessage(`Fichier ${counter} traités.`);
|
||||
if (finalTitle.length > 0) {
|
||||
processedFiles += `\n- ${finalTitle}`;
|
||||
updateMessage(`Fichier ${counter} traités.`);
|
||||
} else {
|
||||
processedFiles += `\n- ${file} [incompressable]`;
|
||||
updateMessage(`Fichier ${counter} trop large pour être compressé.`);
|
||||
numberOfUncompressableFiles++;
|
||||
}
|
||||
}
|
||||
|
||||
let errorMessage = "";
|
||||
if (numberOfUncompressableFiles > 0) {
|
||||
errorMessage += `\nNombre de fichier incompressable : ${numberOfUncompressableFiles}.`;
|
||||
}
|
||||
|
||||
// Send confirmation to the user that we're done
|
||||
await internals.confirmation(
|
||||
`${files.length} fichiers traités : ${processedFiles}`
|
||||
`${files.length} fichiers traités : ${processedFiles}` + errorMessage,
|
||||
);
|
||||
|
||||
await internals.exit();
|
||||
};
|
||||
|
||||
|
|
|
@ -25,9 +25,7 @@ export const getVideoDuration = (file: string) => {
|
|||
export const getNumberOfAudioTracks = (file: string): number[] => {
|
||||
const command = `"${ffprobePath}" -v error -show_entries stream=bit_rate -select_streams a -of json "${file}"`;
|
||||
const result = child_process.execSync(command, { encoding: "utf8" });
|
||||
return JSON.parse(result).streams.map(
|
||||
(v: { bit_rate: string }) => Number(v.bit_rate) / 1000
|
||||
);
|
||||
return JSON.parse(result).streams.map((v: { bit_rate: string }) => Number(v.bit_rate) / 1000);
|
||||
};
|
||||
|
||||
/** Print an error to the console and open the dev tool panel */
|
||||
|
@ -37,9 +35,7 @@ export const printAndDevTool = (win: BrowserWindow, err: string) => {
|
|||
};
|
||||
|
||||
/** Run a command asynchronously */
|
||||
export const execute = (
|
||||
command: string
|
||||
): Promise<{ stdout: string; stderr: string }> => {
|
||||
export const execute = (command: string): Promise<{ stdout: string; stderr: string }> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const process = child_process.exec(command, (error, stdout, stderr) => {
|
||||
if (error) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue