const cp = require("child_process");
const os = require("os");
let ffmpeg;
if (os.platform() === "win32") {
ffmpeg = `"${__dirname}\\..\\ffmpeg.exe"`;
} else if (os.platform() === "freebsd") {
try {
ffmpeg = cp.execSync("which ffmpeg").toString("utf8").split("\n")[0].trim();
} catch (error) {
console.log(error)
showPopup("No ffmpeg found in PATH.");
}
}
else {
ffmpeg = `"${__dirname}/../ffmpeg"`;
}
const fs = require("fs");
/////////////////////////////////////
// Do not change the lines at the top
/////////////////////////////////////
const path = require("path");
const {shell, ipcRenderer, clipboard} = require("electron");
const {default: YTDlpWrap} = require("yt-dlp-wrap-plus");
const {constants} = require("fs/promises");
// Directories
const homedir = os.homedir();
let appdir = path.join(homedir, "Downloads");
if (os.platform() === "linux") {
try {
const xdgDownloadDir = cp
.execSync("xdg-user-dir DOWNLOAD")
.toString()
.trim();
if (xdgDownloadDir.length > 1) {
appdir = xdgDownloadDir;
console.log("xdg download dir:", xdgDownloadDir);
}
} catch (err) {}
}
const hiddenDir = path.join(homedir, ".ytDownloader");
const i18n = new (require("../translations/i18n"))();
fs.mkdir(hiddenDir, {recursive: true}, () => {});
// System tray
const trayEnabled = localStorage.getItem("closeToTray");
if (trayEnabled == "true") {
console.log("Tray is Enabled");
ipcRenderer.send("useTray", true);
}
// Ignore Python warnings
process.env.PYTHONWARNINGS = "error";
// Download directory
let downloadDir = "";
// Global variables
let title, onlyvideo, id, thumbnail, ytdlp, duration, extractor_key;
let audioExtensionList = [];
let rangeCmd = "";
let subs = "";
let subLangs;
let rangeOption = "--download-sections";
let cookieArg = "";
let browser = "";
let maxActiveDownloads = 5;
let showMoreFormats = false;
let configArg = "";
let configTxt = "";
let proxy = "";
let downloadedItemList = []
if (localStorage.getItem("configPath")) {
configArg = "--config-location";
configTxt = `"${localStorage.getItem("configPath")}"`;
}
function checkMaxDownloads() {
if (localStorage.getItem("maxActiveDownloads")) {
const number = Number(localStorage.getItem("maxActiveDownloads"));
if (number < 1) {
maxActiveDownloads = 1;
} else {
maxActiveDownloads = number;
}
}
}
checkMaxDownloads();
// Check for auto updates
let autoUpdate = true;
const autoUpdateStatus = localStorage.getItem("autoUpdate");
if (autoUpdateStatus) {
if (autoUpdateStatus == "false") {
autoUpdate = false;
}
}
ipcRenderer.send("autoUpdate", autoUpdate);
let currentDownloads = 0;
let controllers = new Object();
// Video and audio preferences
let preferredVideoQuality = 720;
let preferredAudioQuality = "";
let preferredVideoCodec = "avc1";
/**
*
* @param {string} id
*/
function getId(id) {
return document.getElementById(id);
}
function downloadPathSelection() {
let localPath = localStorage.getItem("downloadPath");
if (localPath) {
downloadDir = localPath;
try {
fs.accessSync(localPath, constants.W_OK);
downloadDir = localPath;
} catch (err) {
console.log(
"Unable to write to download directory. Switching to default one."
);
console.log(err);
downloadDir = appdir;
localStorage.setItem("downloadPath", appdir);
}
} else {
downloadDir = appdir;
localStorage.setItem("downloadPath", appdir);
}
getId("path").textContent = downloadDir;
fs.mkdir(downloadDir, {recursive: true}, () => {});
}
downloadPathSelection();
// Checking for yt-dlp
let ytDlp;
let ytdlpPath = path.join(os.homedir(), ".ytDownloader", "ytdlp");
// Use system yt-dlp for freebsd
if (os.platform() === "freebsd") {
try {
ytdlpPath = cp.execSync('which yt-dlp').toString("utf8").split("\n")[0].trim();
} catch (error) {
console.log(error)
getId("incorrectMsg").textContent = i18n.__(
"No yt-dlp found in PATH. Make sure you have the full executable. App will not work"
);
}
}
// ytdlp download path
let ytdlpDownloadPath;
if (os.platform() == "win32") {
ytdlpDownloadPath = path.join(os.homedir(), ".ytDownloader", "ytdlp.exe");
} else {
ytdlpDownloadPath = path.join(os.homedir(), ".ytDownloader", "ytdlp");
}
// Downloading yt-dlp
async function downloadYtdlp() {
document.querySelector("#popupBox p").textContent = i18n.__(
"Please wait, necessary files are being downloaded"
);
getId("popupSvg").style.display = "inline";
// Downloading appropriate version of yt-dlp
await YTDlpWrap.downloadFromGithub(ytdlpDownloadPath);
localStorage.setItem("fullYtdlpBinPresent", "true");
getId("popupBox").style.display = "none";
ytDlp = ytdlpPath;
ytdlp = new YTDlpWrap(`"${ytDlp}"`);
localStorage.setItem("ytdlp", ytDlp);
getId("pasteUrl").style.display = "inline-block";
console.log("yt-dlp bin Path: " + ytDlp);
}
// Checking if yt-dlp has been installed by user
// TODO: Remove this after some time
const fullYtdlpBinIsPresent = !!localStorage.getItem("fullYtdlpBinPresent");
const removedOldMacYtdlp = !!localStorage.getItem("removedOldMacYtdlp")
// Removing old yt-dlp for macos
if (os.platform() === "darwin" && !removedOldMacYtdlp) {
if (fs.existsSync(ytdlpPath)) {
fs.rmSync(ytdlpPath)
localStorage.setItem('removedOldMacYtdlp', "true")
}
}
cp.exec(`"${ytdlpPath}" --version`, (error, _stdout, _stderr) => {
if ((error || !fullYtdlpBinIsPresent) && os.platform() !== "freebsd") {
getId("popupBox").style.display = "block";
process.on("uncaughtException", (reason, promise) => {
document.querySelector("#popupBox p").textContent = i18n.__(
"Failed to download necessary files. Please check your network and try again"
);
getId("popupSvg").style.display = "none";
getId("popup").innerHTML += ``;
console.log("Failed to download yt-dlp");
getId("tryBtn").addEventListener("click", () => {
getId("popup").removeChild(getId("popup").lastChild);
downloadYtdlp();
});
});
downloadYtdlp();
} else {
console.log("yt-dlp binary is present in PATH");
ytDlp = ytdlpPath;
ytdlp = new YTDlpWrap(`"${ytDlp}"`);
localStorage.setItem("ytdlp", ytDlp);
cp.spawn(`${ytDlp}`, ["-U"]).stdout.on("data", (data) =>
console.log(data.toString("utf8"))
);
getId("pasteUrl").style.display = "inline-block";
console.log("yt-dlp bin Path: " + ytDlp);
ipcRenderer.send("ready-for-links");
}
});
function defaultVideoToggle() {
let defaultWindow = "video";
if (localStorage.getItem("defaultWindow")) {
defaultWindow = localStorage.getItem("defaultWindow");
}
if (defaultWindow == "video") {
selectVideo();
} else {
selectAudio();
}
}
// Pasting url from clipboard
function pasteUrl() {
defaultVideoToggle();
hideHidden();
getId("loadingWrapper").style.display = "flex";
getId("incorrectMsg").textContent = "";
const url = clipboard.readText();
getInfo(url);
}
function pasteFromTray(url) {
defaultVideoToggle();
hideHidden();
getId("loadingWrapper").style.display = "flex";
getId("incorrectMsg").textContent = "";
getInfo(url);
}
getId("closeHidden").addEventListener("click", () => {
hideHidden();
getId("loadingWrapper").style.display = "none";
});
document.addEventListener("keydown", (event) => {
if (event.ctrlKey && event.key == "v" && document.activeElement.tagName !== "INPUT") {
pasteUrl();
}
});
getId("pasteUrl").addEventListener("click", () => {
pasteUrl();
});
// Getting video info
/**
*
* @param {string} url
*/
async function getInfo(url) {
audioExtensionList = [];
let selected = false;
onlyvideo = false;
let audioIsPresent = false;
downloadPathSelection();
// Cleaning text
getId("videoFormatSelect").innerHTML = "";
getId("audioFormatSelect").innerHTML = "";
getId("audioForVideoFormatSelect").innerHTML = ``;
const startTime = getId("startTime");
startTime.value = "";
getId("endTime").value = "";
getId("errorBtn").style.display = "none";
getId("errorDetails").style.display = "none";
getId("errorDetails").textContent = "";
if (localStorage.getItem("preferredVideoQuality")) {
preferredVideoQuality = Number(
localStorage.getItem("preferredVideoQuality")
);
}
if (localStorage.getItem("preferredAudioQuality")) {
preferredAudioQuality = localStorage.getItem("preferredAudioQuality");
getId("extractSelection").value = preferredAudioQuality;
}
if (localStorage.getItem("preferredVideoCodec")) {
preferredVideoCodec = localStorage.getItem("preferredVideoCodec");
}
if (localStorage.getItem("showMoreFormats") === "true") {
showMoreFormats = true;
} else {
showMoreFormats = false;
}
proxy = getLocalStorageItem("proxy");
// Whether to use browser cookies or not
if (localStorage.getItem("browser")) {
browser = localStorage.getItem("browser");
}
if (browser) {
cookieArg = "--cookies-from-browser";
} else {
cookieArg = "";
}
let validInfo = true;
let info = "";
// Twitter/X compatibility
url = url.replace("//x.com/", "//twitter.com/")
const infoOptions = [
"-j",
"--no-playlist",
"--no-warnings",
proxy ? "--no-check-certificate" : "",
proxy ? "--proxy" : "",
proxy,
cookieArg,
browser,
configArg,
configTxt,
`"${url}"`,
].filter(item => item)
const infoProcess = cp.spawn(
`"${ytDlp}"`,
infoOptions,
{
shell: true,
}
);
infoProcess.stdout.on("data", (data) => {
info += data;
});
infoProcess.stderr.on("data", (error) => {
validInfo = false;
// Error message handling
console.log(error.toString("utf8"));
getId("loadingWrapper").style.display = "none";
getId("incorrectMsg").textContent = i18n.__(
"Some error has occurred. Check your network and use correct URL"
);
getId("errorBtn").style.display = "inline-block";
getId("errorDetails").innerHTML = `
URL: ${url}
${error.toString("utf8")}
`;
getId("errorDetails").title = i18n.__("Click to copy");
});
infoProcess.on("close", () => {
if (validInfo) {
/**
* @typedef {import("./types").info} info
* @type {info}
*/
const parsedInfo = JSON.parse(info);
console.log(parsedInfo);
title = `${parsedInfo.title} [${parsedInfo.id}]`;
id = parsedInfo.id;
thumbnail = parsedInfo.thumbnail;
duration = parsedInfo.duration;
extractor_key = parsedInfo.extractor_key
/**
* @typedef {import("./types").format} format
* @type {format[]}
*/
const formats = parsedInfo.formats || [];
console.log(formats);
/**
* @type {HTMLInputElement[]}
*/
// @ts-ignore
const urlElements = document.querySelectorAll(".url");
urlElements.forEach((element) => {
element.value = url;
});
getId("loadingWrapper").style.display = "none";
getId("hidden").style.display = "inline-block";
getId("hidden").classList.add("scaleUp");
getId("title").innerHTML =
`${i18n.__("Title ")}: ` +
``;
let audioSize = 0;
let defaultVideoFormat = 144;
let videoFormatCodecs = {};
let preferredAudioFormatLength = 0;
let preferredAudioFormatCount = 0;
let maxAudioFormatNoteLength = 10;
// Initially going through all formats
// Getting approx size of audio file and checking if audio is present
for (let format of formats) {
// Find the item with the preferred video format
if (
format.height <= preferredVideoQuality &&
format.height >= defaultVideoFormat &&
(format.video_ext !== "none" &&
!(
format.video_ext === "mp4" &&
format.vcodec &&
format.vcodec.split(".")[0] === "vp09"
))
&& (!showMoreFormats ? format.video_ext !== "webm" : true)
) {
defaultVideoFormat = format.height;
// Creating a list of available codecs for the required video height
if (!videoFormatCodecs[format.height]) {
videoFormatCodecs[format.height] = {codecs: []};
}
if (format.vcodec) {
videoFormatCodecs[format.height].codecs.push(
format.vcodec.split(".")[0]
);
}
}
// Going through audio list
if (
format.audio_ext !== "none" ||
(format.acodec !== "none" && format.video_ext === "none")
) {
audioIsPresent = true;
onlyvideo = true;
audioSize =
Number(format.filesize || format.filesize_approx) /
1000000;
if (!audioExtensionList.includes(format.audio_ext)) {
audioExtensionList.push(format.audio_ext);
}
if (format.format_note && format.format_note.length > maxAudioFormatNoteLength) {
maxAudioFormatNoteLength = format.format_note.length
}
}
if (
format.audio_ext === preferredAudioQuality ||
format.acodec === preferredAudioQuality
) {
preferredAudioFormatLength++;
}
}
const availableCodecs = videoFormatCodecs[defaultVideoFormat]
? videoFormatCodecs[defaultVideoFormat].codecs
: [];
if (!availableCodecs.includes(preferredVideoCodec)) {
preferredVideoCodec =
availableCodecs[availableCodecs.length - 1];
}
for (let format of formats) {
let size;
let selectedText = "";
let audioSelectedText = "";
if (
format.height == defaultVideoFormat &&
format.vcodec &&
format.vcodec.split(".")[0] === preferredVideoCodec &&
!selected &&
(format.video_ext !== "none" &&
!(
format.video_ext === "mp4" &&
format.vcodec &&
format.vcodec.split(".")[0] === "vp09"
))
&& (!showMoreFormats ? format.video_ext !== "webm" : true)
) {
selectedText = " selected ";
selected = true;
}
if (format.filesize || format.filesize_approx) {
size = (
Number(format.filesize || format.filesize_approx) /
1000000
).toFixed(2);
} else {
// if (format.tbr) {
// size = (
// (format.tbr * 50 * duration) /
// 1000000
// ).toFixed(2);
// } else {
// }
size = i18n.__("Unknown size");
}
// For videos
if (
(format.video_ext !== "none" &&
!(
format.video_ext === "mp4" &&
format.vcodec &&
format.vcodec.split(".")[0] === "vp09"
))
&& (!showMoreFormats ? format.video_ext !== "webm" : true)
) {
if (size !== i18n.__("Unknown size")) {
size = (Number(size) + 0 || Number(audioSize)).toFixed(
1
);
size = size + " " + i18n.__("MB");
}
const format_id =
format.format_id +
"|" +
format.ext +
"|" +
(format.height || "NO");
// Video codec
const vcodec =
format.vcodec && showMoreFormats
? format.vcodec.split(".")[0]
: "";
let spaceAfterVcodec = showMoreFormats
? " ".repeat(5 - vcodec.length)
: "";
showMoreFormats
? (spaceAfterVcodec += "| ")
: (spaceAfterVcodec += "");
// Quality
const quality =
(format.height
? format.height +
"p" +
(format.fps == 60 ? "60" : "")
: "") ||
format.format_note ||
format.resolution ||
format.format_id ||
"Unknown quality";
const spaceAfterQuality = " ".repeat(
quality.length <= 8 && 8 - quality.length > 0
? 8 - quality.length
: 1
);
// Extension
const extension = format.ext;
// Format and Quality Options
const element =
"";
getId("videoFormatSelect").innerHTML += element;
}
// For audios
else if (
format.audio_ext !== "none" ||
(format.acodec !== "none" && format.video_ext === "none")
) {
if (!showMoreFormats && format.audio_ext === "webm") {
continue;
}
size =
size !== i18n.__("Unknown size")
? size + " MB"
: i18n.__("Unknown size");
let audio_ext;
if (format.audio_ext === "webm") {
audio_ext = "opus";
} else {
audio_ext = format.audio_ext;
}
if (
format.audio_ext === preferredAudioQuality ||
format.acodec === preferredAudioQuality
) {
preferredAudioFormatCount += 1;
if (
preferredAudioFormatCount ===
preferredAudioFormatLength
) {
audioSelectedText = " selected ";
}
}
const format_id = format.format_id + "|" + audio_ext;
/**@type {string} */
let formatNote = (i18n.__(format.format_note) ||
i18n.__("Unknown quality"));
formatNote = formatNote.padEnd(maxAudioFormatNoteLength, "\xa0")
const element =
"";
getId("audioFormatSelect").innerHTML += element;
getId("audioForVideoFormatSelect").innerHTML += element;
}
// Both audio and video available
else if (
format.audio_ext !== "none" ||
(format.acodec !== "none" && format.video_ext !== "none")
) {
// Skip them
}
// When there is no audio
if (audioIsPresent === false) {
getId("audioPresent").style.display = "none";
} else {
getId("audioPresent").style.display = "block";
}
}
}
});
}
// Video download event
getId("videoDownload").addEventListener("click", (event) => {
checkMaxDownloads();
hideHidden();
console.log(`Current:${currentDownloads} Max:${maxActiveDownloads}`);
if (currentDownloads < maxActiveDownloads) {
manageAdvanced(duration);
download("video");
currentDownloads++;
} else {
// Handling active downloads for video
manageAdvanced(duration);
const range1 = rangeOption;
const range2 = rangeCmd;
const subs1 = subs;
const subs2 = subLangs;
const url1 = getId("url").value;
const thumb1 = thumbnail;
const title1 = title;
const randId = Math.random().toFixed(10).toString().slice(2);
const item = `
${i18n.__("Download pending")}
${i18n.__("Download pending")}
${i18n.__("Download pending")}