Skip to content
This repository has been archived by the owner on Feb 16, 2025. It is now read-only.

Commit

Permalink
Added linux support.
Browse files Browse the repository at this point in the history
  • Loading branch information
cheezos committed Jan 10, 2022
1 parent a58723c commit 4a473e8
Show file tree
Hide file tree
Showing 14 changed files with 648 additions and 4,917 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
node_modules
dist
ffmpeg2pass-0.log
ffmpeg2pass-0.log.mbtree
video-compressor-build*

6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
## Big Slime Video Compressor
## Slugnasty Video Compressor

A beautiful, simple video compressor.

Expand All @@ -12,8 +12,8 @@ A beautiful, simple video compressor.

### Preview

![Screenshot 1](https://github.com/big-slime/video-compressor/blob/main/preview.gif)
![Screenshot 1](https://github.com/slugnasty/video-compressor/blob/main/preview.gif)

### Love this app?

[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/bigslime)
[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/slugnasty)
Binary file added dist/ffmpeg.exe
Binary file not shown.
Binary file added dist/ffprobe.exe
Binary file not shown.
141 changes: 141 additions & 0 deletions dist/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.compressQueue = exports.mainWindow = void 0;
const electron_1 = require("electron");
const utils_1 = require("./utils");
const fluent_ffmpeg_1 = __importDefault(require("fluent-ffmpeg"));
let videoData = [];
let currentIndex = 0;
let currentProgress = 1;
let totalProgress = 1;
let cmd = null;
electron_1.app.on("ready", () => {
exports.mainWindow = new electron_1.BrowserWindow({
width: 350,
height: 500,
webPreferences: {
contextIsolation: false,
nodeIntegration: true,
preload: __dirname + "/preload.js",
},
show: false,
resizable: false,
autoHideMenuBar: true,
});
exports.mainWindow.loadFile("./index.html");
// mainWindow.webContents.openDevTools();
exports.mainWindow.on("ready-to-show", () => {
exports.mainWindow.show();
(0, utils_1.getFFmpeg)();
});
console.log("Window ready.");
});
electron_1.app.on("window-all-closed", () => {
if (process.platform !== "darwin") {
killFFmpeg();
electron_1.app.quit();
}
});
electron_1.ipcMain.on("droppedVideos", (event, vids) => {
videoData = (0, utils_1.getVideoData)(vids);
});
electron_1.ipcMain.on("requestCompress", (event, removeAudio, h265, minBitrate, targetFileSize) => {
currentIndex = 0;
currentProgress = 0;
compressQueue(exports.mainWindow, videoData, removeAudio, h265, minBitrate, targetFileSize)
.then(() => {
event.reply("compressionComplete");
new electron_1.Notification({ title: "Compression complete!", body: "Your new videos are located in the same folder." }).show();
})
.catch((err) => {
event.reply("compressionError", err);
new electron_1.Notification({ title: "Error!", body: "There was an error during compression." }).show();
});
});
electron_1.ipcMain.on("requestAbort", (event) => {
killFFmpeg();
});
function compressQueue(window, videoData, removeAudio, h265, minBitrate, targetFileSize) {
return new Promise((resolve, reject) => {
window.webContents.send("compressionStart");
totalProgress = videoData.length * 2;
const compress = () => {
console.log(`Compressing ${currentIndex + 1}/${videoData.length}...`);
(0, utils_1.getVideoDuration)(videoData[currentIndex].base)
.then((duration) => {
const bitrate = (0, utils_1.getCalculatedVideoBitrate)(duration, minBitrate, targetFileSize);
compressVideo(window, videoData[currentIndex], bitrate, removeAudio, h265)
.then(() => {
if (currentIndex + 1 < videoData.length) {
currentIndex += 1;
compress();
}
else {
resolve(true);
}
})
.catch((err) => {
reject(err);
});
})
.catch((err) => {
reject(err);
});
};
compress();
});
}
exports.compressQueue = compressQueue;
function compressVideo(window, videoData, bitrate, removeAudio, h265) {
return new Promise((resolve, reject) => {
cmd = (0, fluent_ffmpeg_1.default)();
let audioArg = "-b:a 128k";
let codec = "-c:v libx264";
if (removeAudio) {
audioArg = "-an";
}
if (h265) {
codec = "-c:v libx265";
}
let pass1 = [`-y`, codec, `-b:v ${bitrate}k`, `-pass 1`, `-an`, `-f mp4`];
let pass2 = [codec, `-b:v ${bitrate}k`, `-pass 2`, `-c:a aac`, audioArg];
cmd.setFfmpegPath((0, utils_1.getFFmpeg)()[0]);
cmd.setFfprobePath((0, utils_1.getFFmpeg)()[1]);
cmd.input(videoData.base);
cmd.outputOptions(pass1);
cmd.output(`${videoData.path}temp`);
cmd.on("end", () => {
currentProgress += 1;
window.webContents.send("progressUpdate", currentProgress, totalProgress);
cmd = (0, fluent_ffmpeg_1.default)();
cmd.setFfmpegPath((0, utils_1.getFFmpeg)()[0]);
cmd.setFfprobePath((0, utils_1.getFFmpeg)()[1]);
cmd.input(videoData.base);
cmd.outputOptions(pass2);
cmd.output(`${videoData.path}${videoData.name}-compressed${videoData.ext}`);
cmd.on("end", () => {
currentProgress += 1;
window.webContents.send("progressUpdate", currentProgress, totalProgress);
console.log(`Compressed ${videoData.name}`);
resolve(true);
});
cmd.on("error", (err) => {
reject(err);
});
cmd.run();
});
cmd.on("error", (err) => {
reject(err);
});
cmd.run();
});
}
function killFFmpeg() {
if (cmd != null) {
cmd.kill();
console.log("Killed FFmpeg process.");
}
}
1 change: 1 addition & 0 deletions dist/preload.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"use strict";
95 changes: 95 additions & 0 deletions dist/renderer/renderer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const electron_1 = require("electron");
const btnCompress = document.getElementById("btn-compress");
const btnAbort = document.getElementById("btn-abort");
const btnSupport = document.getElementById("btn-support");
const btnGithub = document.getElementById("btn-github");
const lblStatus = document.getElementById("lbl-status");
const lblStatusSmall = document.getElementById("lbl-status-small");
const dropZone = document.getElementById("drop-zone");
const progressBar = document.getElementById("progress-bar");
const checkRemoveAudio = document.getElementById("check-remove-audio");
const checkH265 = document.getElementById("check-h265");
const inputMinBitrate = document.getElementById("input-min-bitrate");
const inputFileSize = document.getElementById("input-file-size");
let videoPaths = [];
btnCompress?.addEventListener("click", () => {
const removeAudio = checkRemoveAudio.checked;
const h265 = checkH265.checked;
const minBitrate = parseFloat(inputMinBitrate.value) || 100;
const fileSize = parseFloat(inputFileSize.value) || 8.0;
btnCompress.disabled = true;
electron_1.ipcRenderer.send("requestCompress", removeAudio, h265, minBitrate, fileSize);
});
btnAbort?.addEventListener("click", () => {
electron_1.ipcRenderer.send("requestAbort");
});
btnSupport?.addEventListener("click", () => {
electron_1.shell.openExternal("https://ko-fi.com/slugnasty");
});
btnGithub?.addEventListener("click", () => {
electron_1.shell.openExternal("https://github.com/slugnasty/video-compressor");
});
dropZone?.addEventListener("dragover", (event) => {
event.stopPropagation();
event.preventDefault();
});
dropZone?.addEventListener("drop", (event) => {
event.stopPropagation();
event.preventDefault();
videoPaths = [];
const files = event.dataTransfer?.files;
for (const file of files) {
videoPaths.push(file.path);
}
lblStatus.innerText = `${videoPaths.length} video(s) ready to compress.`;
lblStatusSmall.innerText = "Click 'Compress' to begin.";
btnCompress.disabled = false;
electron_1.ipcRenderer.send("droppedVideos", videoPaths);
});
electron_1.ipcRenderer.on("message", (event, bigText, smallText) => {
lblStatus.innerText = bigText;
if (smallText) {
lblStatusSmall.innerText = smallText;
}
});
electron_1.ipcRenderer.on("progressUpdate", (event, currentValue, totalValue) => {
const progress = Math.round((currentValue / totalValue) * 100);
console.log(`Progress: ${progress}`);
lblStatusSmall.innerText = `Progress: ${progress}%`;
progressBar.style.width = `${progress}%`;
});
electron_1.ipcRenderer.on("compressionStart", (event) => {
lblStatus.innerText = "Compressing, please wait...";
lblStatusSmall.innerText = "Large videos can take a long time!";
btnCompress.disabled = true;
btnAbort.disabled = false;
checkRemoveAudio.disabled = true;
checkH265.disabled = true;
inputMinBitrate.disabled = true;
inputFileSize.disabled = true;
progressBar.style.width = "0%";
});
electron_1.ipcRenderer.on("compressionComplete", (event) => {
lblStatus.innerText = "Compression complete, enjoy!";
lblStatusSmall.innerText = "Please consider supporting my work :)";
btnCompress.disabled = true;
btnAbort.disabled = true;
checkRemoveAudio.disabled = false;
checkH265.disabled = false;
inputMinBitrate.disabled = false;
inputFileSize.disabled = false;
progressBar.style.width = "100%";
});
electron_1.ipcRenderer.on("compressionError", (event, err) => {
lblStatus.innerText = err;
lblStatusSmall.innerText = "";
btnCompress.disabled = true;
btnAbort.disabled = true;
checkRemoveAudio.disabled = false;
checkH265.disabled = false;
inputMinBitrate.disabled = false;
inputFileSize.disabled = false;
progressBar.style.width = "100%";
});
65 changes: 65 additions & 0 deletions dist/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getFFmpeg = exports.getCalculatedVideoBitrate = exports.getVideoDuration = exports.getVideoData = void 0;
const electron_1 = require("electron");
const path_1 = require("path");
const child_process_1 = require("child_process");
const fluent_ffmpeg_1 = __importDefault(require("fluent-ffmpeg"));
function getVideoData(videoPaths) {
let videoData = [];
for (let base of videoPaths) {
const name = (0, path_1.parse)(base).name;
const ext = (0, path_1.parse)(base).ext;
const path = base.split(name)[0];
videoData.push({ base: base, path: path, name: name, ext: ext });
}
return videoData;
}
exports.getVideoData = getVideoData;
function getVideoDuration(videoPath) {
return new Promise((resolve, reject) => {
const cmd = (0, fluent_ffmpeg_1.default)();
cmd.setFfmpegPath(getFFmpeg()[0]);
cmd.setFfprobePath(getFFmpeg()[1]);
cmd.input(videoPath);
cmd.ffprobe((err, metadata) => {
if (err) {
reject(err);
}
else {
const duration = metadata.format.duration;
console.log(`Video duration: ${duration}`);
resolve(duration);
}
});
});
}
exports.getVideoDuration = getVideoDuration;
function getCalculatedVideoBitrate(duration, minBitrate, targetFileSize) {
const magic = Math.max((targetFileSize * 8192.0) / (1.048576 * duration) - 128, minBitrate);
console.log(`Calculated bitrate: ${magic}`);
return magic;
}
exports.getCalculatedVideoBitrate = getCalculatedVideoBitrate;
function getFFmpeg() {
let ffmpegPath = __dirname + "/ffmpeg.exe";
let ffprobePath = __dirname + "/ffprobe.exe";
if (process.platform === "linux") {
const cmd = (0, child_process_1.spawnSync)("which", ["ffmpeg"], { encoding: "utf8" });
if (cmd.stdout != "") {
ffmpegPath = "ffmpeg";
ffprobePath = "ffprobe";
}
else {
require("./main").mainWindow.webContents.send("message", "FFmpeg missing!", "Please install FFmpeg before continuing.");
new electron_1.Notification({ title: "FFmpeg missing!", body: "Please install FFmpeg before continuing." }).show();
ffmpegPath = "null";
ffprobePath = "null";
}
}
return [ffmpegPath, ffprobePath];
}
exports.getFFmpeg = getFFmpeg;
34 changes: 28 additions & 6 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="https://bootswatch.com/5/darkly/bootstrap.min.css" />
<title>Big Slime Video Compressor</title>
<title>Slugnasty Video Compressor</title>
</head>

<body style="overflow: hidden">
Expand Down Expand Up @@ -69,7 +69,13 @@ <h5 id="lbl-status" class="text-muted">Drop your videos here.</h5>
<!-- Minimum Bitrate -->
<div style="display: flex; flex-flow: row; justify-content: center; margin: 5px">
<div style="margin: 3px">
<input id="input-min-bitrate" class="form-control form-control-sm" style="text-align: center; width: 200px" type="text" placeholder="Minimum bitrate (def: 100)" />
<input
id="input-min-bitrate"
class="form-control form-control-sm"
style="text-align: center; width: 200px"
type="text"
placeholder="Minimum bitrate (def: 100)"
/>
</div>
<div style="margin: 3px">
<label class="form-check-label" for="flexSwitchCheckDefault">Kbps</label>
Expand All @@ -79,7 +85,13 @@ <h5 id="lbl-status" class="text-muted">Drop your videos here.</h5>
<!-- File Size -->
<div style="display: flex; flex-flow: row; justify-content: center; margin: 5px">
<div style="margin: 3px">
<input id="input-file-size" class="form-control form-control-sm" style="text-align: center; width: 200px" type="text" placeholder="Target file size (def: 8)" />
<input
id="input-file-size"
class="form-control form-control-sm"
style="text-align: center; width: 200px"
type="text"
placeholder="Target file size (def: 8)"
/>
</div>
<div style="margin: 3px">
<label class="form-check-label" for="flexSwitchCheckDefault">MB</label>
Expand All @@ -88,11 +100,21 @@ <h5 id="lbl-status" class="text-muted">Drop your videos here.</h5>
</div>

<!-- Footer -->
<div style="position: absolute; display: flex; justify-content: center; bottom: 0; left: 0; width: 100%; height: 25px; background: rgb(25, 25, 25)">
<button id="btn-support" class="text-muted" style="background: none; border: none; margin-left: 25px; margin-right: 25px; padding: 0; cursor: pointer">
<div
style="position: absolute; display: flex; justify-content: center; bottom: 0; left: 0; width: 100%; height: 25px; background: rgb(25, 25, 25)"
>
<button
id="btn-support"
class="text-muted"
style="background: none; border: none; margin-left: 25px; margin-right: 25px; padding: 0; cursor: pointer"
>
<small class="text-muted">Buy me a coffee?</small>
</button>
<button id="btn-github" class="text-muted" style="background: none; border: none; margin-left: 25px; margin-right: 25px; padding: 0; cursor: pointer">
<button
id="btn-github"
class="text-muted"
style="background: none; border: none; margin-left: 25px; margin-right: 25px; padding: 0; cursor: pointer"
>
<small class="text-muted">Github</small>
</button>
</div>
Expand Down
Loading

0 comments on commit 4a473e8

Please sign in to comment.