This repository has been archived by the owner on Feb 16, 2025. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
14 changed files
with
648 additions
and
4,917 deletions.
There are no files selected for viewing
This file contains 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
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* | ||
|
This file contains 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
Binary file not shown.
Binary file not shown.
This file contains 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
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."); | ||
} | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
"use strict"; |
This file contains 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
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%"; | ||
}); |
This file contains 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
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; |
This file contains 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
Oops, something went wrong.