|
|
|
|
@ -3,12 +3,14 @@ const ytdl = require("ytdl-core");
|
|
|
|
|
const cp = require("child_process");
|
|
|
|
|
const os = require("os");
|
|
|
|
|
const ffmpeg = require("ffmpeg-static");
|
|
|
|
|
const path = require("path")
|
|
|
|
|
const path = require("path");
|
|
|
|
|
const { shell, ipcRenderer, clipboard } = require("electron");
|
|
|
|
|
const { get } = require("http");
|
|
|
|
|
const { exit } = require("process");
|
|
|
|
|
|
|
|
|
|
// Directories
|
|
|
|
|
const homedir = os.homedir();
|
|
|
|
|
const appdir = path.join(homedir,".ytDownloader");
|
|
|
|
|
const appdir = path.join(homedir, ".ytDownloader");
|
|
|
|
|
const tempDir = path.join(appdir, "temp");
|
|
|
|
|
fs.mkdirSync(path.join(homedir, ".ytDownloader/temp"), { recursive: true });
|
|
|
|
|
let config;
|
|
|
|
|
@ -20,60 +22,75 @@ function getId(id) {
|
|
|
|
|
return document.getElementById(id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let localPath = localStorage.getItem("downloadPath")
|
|
|
|
|
let localPath = localStorage.getItem("downloadPath");
|
|
|
|
|
|
|
|
|
|
if (localPath){
|
|
|
|
|
downloadDir = localPath
|
|
|
|
|
}
|
|
|
|
|
else{
|
|
|
|
|
downloadDir = appdir
|
|
|
|
|
localStorage.setItem("downloadPath", appdir)
|
|
|
|
|
if (localPath) {
|
|
|
|
|
downloadDir = localPath;
|
|
|
|
|
} else {
|
|
|
|
|
downloadDir = appdir;
|
|
|
|
|
localStorage.setItem("downloadPath", appdir);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Clearing tempDir
|
|
|
|
|
fs.readdirSync(tempDir).forEach((f) => fs.rmSync(`${tempDir}/${f}`));
|
|
|
|
|
fs.readdirSync(tempDir).forEach((f) => fs.rmdir(`${tempDir}/${f}`, () => {}));
|
|
|
|
|
|
|
|
|
|
// Collecting info from youtube
|
|
|
|
|
async function getVideoInfo(url) {
|
|
|
|
|
let info;
|
|
|
|
|
await ytdl.getInfo(url)
|
|
|
|
|
.then((data)=>{
|
|
|
|
|
info = data
|
|
|
|
|
})
|
|
|
|
|
.catch((error)=>{
|
|
|
|
|
|
|
|
|
|
})
|
|
|
|
|
await ytdl
|
|
|
|
|
.getInfo(url)
|
|
|
|
|
.then((data) => {
|
|
|
|
|
info = data;
|
|
|
|
|
})
|
|
|
|
|
.catch((error) => {});
|
|
|
|
|
return info;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Pasting url from clipboard
|
|
|
|
|
let url;
|
|
|
|
|
getId("pasteUrl").addEventListener("click", ()=>{
|
|
|
|
|
url = clipboard.readText()
|
|
|
|
|
console.log(url);
|
|
|
|
|
})
|
|
|
|
|
function defaultVideoToggle() {
|
|
|
|
|
videoToggle.style.backgroundColor = "var(--box-toggleOn)";
|
|
|
|
|
audioToggle.style.backgroundColor = "var(--box-toggle)";
|
|
|
|
|
getId("audioList").style.display = "none";
|
|
|
|
|
getId("videoList").style.display = "block";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//! Pasting url from clipboard
|
|
|
|
|
|
|
|
|
|
function pasteUrl() {
|
|
|
|
|
defaultVideoToggle();
|
|
|
|
|
getId("hidden").style.display = "none";
|
|
|
|
|
getId("loadingWrapper").style.display = "flex";
|
|
|
|
|
getId("incorrectMsg").textContent = "";
|
|
|
|
|
const url = clipboard.readText();
|
|
|
|
|
getInfo(url);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
document.addEventListener("keydown", (event) => {
|
|
|
|
|
if (event.ctrlKey && event.key == "v") {
|
|
|
|
|
pasteUrl();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
getId("pasteUrl").addEventListener("click", () => {
|
|
|
|
|
pasteUrl();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Getting video info
|
|
|
|
|
async function getInfo() {
|
|
|
|
|
incorrectMsg.textContent = "";
|
|
|
|
|
loadingMsg.style.display = "flex";
|
|
|
|
|
async function getInfo(url) {
|
|
|
|
|
getId("videoFormatSelect").innerHTML = "";
|
|
|
|
|
getId("audioFormatSelect").innerHTML = "";
|
|
|
|
|
const url = getId("url").value;
|
|
|
|
|
let info = await getVideoInfo(url)
|
|
|
|
|
let info = await getVideoInfo(url);
|
|
|
|
|
|
|
|
|
|
if (info) {
|
|
|
|
|
let title = info.videoDetails.title;
|
|
|
|
|
let formats = info.formats;
|
|
|
|
|
const title = info.videoDetails.title;
|
|
|
|
|
const formats = info.formats;
|
|
|
|
|
console.log(formats);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const urlElements = document.querySelectorAll(".url");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
urlElements.forEach((element) => {
|
|
|
|
|
element.value = url;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
loadingMsg.style.display = "none";
|
|
|
|
|
getId("hidden").style.display = "inline-block";
|
|
|
|
|
getId("title").innerHTML = "<b>Title</b>: " + title;
|
|
|
|
|
getId("videoList").style.display = "block";
|
|
|
|
|
@ -160,34 +177,39 @@ async function getInfo() {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
loadingMsg.style.display = "none";
|
|
|
|
|
incorrectMsg.textContent =
|
|
|
|
|
getId("loadingWrapper").style.display = "none";
|
|
|
|
|
getId("incorrectMsg").textContent =
|
|
|
|
|
"Some error has occured. Check your connection and use correct URL";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Video download event
|
|
|
|
|
getId("videoDownload").addEventListener("click", (event) => {
|
|
|
|
|
getId("preparingBox").style.display = "flex";
|
|
|
|
|
getId("hidden").style.display = "none";
|
|
|
|
|
clickAnimation("videoDownload");
|
|
|
|
|
download("video");
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Audio download event
|
|
|
|
|
getId("audioDownload").addEventListener("click", (event) => {
|
|
|
|
|
getId("preparingBox").style.display = "flex";
|
|
|
|
|
getId("hidden").style.display = "none";
|
|
|
|
|
clickAnimation("audioDownload");
|
|
|
|
|
download("audio");
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Downloading with ytdl
|
|
|
|
|
function download(type) {
|
|
|
|
|
getId("videoProgressBox").style.display = "none";
|
|
|
|
|
getId("audioProgressBox").style.display = "none";
|
|
|
|
|
|
|
|
|
|
getId("savedMsg").innerHTML = "";
|
|
|
|
|
const url = getId("url").value;
|
|
|
|
|
|
|
|
|
|
const newFolderName = Math.random().toFixed(10).toString().slice(2);
|
|
|
|
|
const newFolderPath = path.join(tempDir, newFolderName);
|
|
|
|
|
|
|
|
|
|
fs.mkdir(newFolderPath, (err) => {
|
|
|
|
|
if (err) {
|
|
|
|
|
console.log("Temp directory couldn't be created");
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
let itag;
|
|
|
|
|
|
|
|
|
|
if (type === "video") {
|
|
|
|
|
@ -198,6 +220,38 @@ function download(type) {
|
|
|
|
|
|
|
|
|
|
// Finding info of the link and downloading
|
|
|
|
|
findInfo(url, itag).then((info) => {
|
|
|
|
|
const id = info.id;
|
|
|
|
|
const newItem = `
|
|
|
|
|
<div class="item" id="${newFolderName}">
|
|
|
|
|
<img src="../assets/images/close.png" onClick="fadeItem('${newFolderName}')" class="itemClose"}" id="${
|
|
|
|
|
newFolderName + ".close"
|
|
|
|
|
}">
|
|
|
|
|
<img src="https://img.youtube.com/vi/${id}/mqdefault.jpg" alt="thumbnail" class="itemIcon">
|
|
|
|
|
|
|
|
|
|
<div class="itemBody">
|
|
|
|
|
<div class="itemTitle">${info.title}</div>
|
|
|
|
|
<div class="itemType">${info.filetype}</div>
|
|
|
|
|
<input disabled type="range" value="0" class="hiddenVideoProgress" id="${
|
|
|
|
|
newFolderName + "vid"
|
|
|
|
|
}"></input>
|
|
|
|
|
<input disabled type="range" value="0" class="hiddenAudioProgress" id="${
|
|
|
|
|
newFolderName + "aud"
|
|
|
|
|
}"></input>
|
|
|
|
|
<div id="${newFolderName + "prog"}" class="itemProgress"></div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
`;
|
|
|
|
|
getId("list").innerHTML += newItem;
|
|
|
|
|
getId("loadingWrapper").style.display = "none";
|
|
|
|
|
let cancelled = false;
|
|
|
|
|
|
|
|
|
|
getId(newFolderName + ".close").addEventListener("click", () => {
|
|
|
|
|
if (getId(newFolderName)) {
|
|
|
|
|
fadeItem(newFolderName);
|
|
|
|
|
}
|
|
|
|
|
cancelled = true;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const format = info.format;
|
|
|
|
|
const extension = info.extension;
|
|
|
|
|
let filename = "";
|
|
|
|
|
@ -231,6 +285,7 @@ function download(type) {
|
|
|
|
|
|
|
|
|
|
// If video
|
|
|
|
|
if (format.hasVideo) {
|
|
|
|
|
getId(newFolderName + "prog").textContent = "Starting...";
|
|
|
|
|
// Temporary audio and video files
|
|
|
|
|
let videoName =
|
|
|
|
|
Math.random().toString("16").slice(2) + "." + extension;
|
|
|
|
|
@ -239,60 +294,107 @@ function download(type) {
|
|
|
|
|
|
|
|
|
|
const arr = [
|
|
|
|
|
new Promise((resolve, reject) => {
|
|
|
|
|
if (cancelled) {
|
|
|
|
|
reject("cancelled");
|
|
|
|
|
}
|
|
|
|
|
// Downloading only video
|
|
|
|
|
ytdl(url, { quality: itag })
|
|
|
|
|
.on("progress", (_, downloaded, size) => {
|
|
|
|
|
videoProgress = (downloaded / size) * 100;
|
|
|
|
|
const videoDownload = ytdl(url, { quality: itag });
|
|
|
|
|
|
|
|
|
|
if (videoProgress != 100) {
|
|
|
|
|
getId("videoProgressBox").style.display =
|
|
|
|
|
"block";
|
|
|
|
|
getId("preparingBox").style.display = "none";
|
|
|
|
|
getId("videoProgress").value = videoProgress;
|
|
|
|
|
}
|
|
|
|
|
if (videoProgress == 100) {
|
|
|
|
|
resolve("video downloaded");
|
|
|
|
|
videoDownload
|
|
|
|
|
.on("progress", (_, downloaded, size) => {
|
|
|
|
|
if (cancelled) {
|
|
|
|
|
console.log("Cancelled video");
|
|
|
|
|
videoDownload.destroy();
|
|
|
|
|
reject();
|
|
|
|
|
} else {
|
|
|
|
|
videoProgress = (downloaded / size) * 100;
|
|
|
|
|
getId(newFolderName + "vid").value =
|
|
|
|
|
videoProgress;
|
|
|
|
|
|
|
|
|
|
if (videoProgress != 100) {
|
|
|
|
|
getId(newFolderName + "prog").textContent =
|
|
|
|
|
"Downloading";
|
|
|
|
|
getId(newFolderName + "vid").style.display =
|
|
|
|
|
"block";
|
|
|
|
|
getId(newFolderName + "aud").style.display =
|
|
|
|
|
"block";
|
|
|
|
|
}
|
|
|
|
|
if (videoProgress == 100) {
|
|
|
|
|
resolve("video downloaded");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.pipe(fs.createWriteStream(path.join(tempDir, videoName)));
|
|
|
|
|
.pipe(
|
|
|
|
|
fs.createWriteStream(
|
|
|
|
|
path.join(newFolderPath, videoName)
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
}),
|
|
|
|
|
|
|
|
|
|
new Promise((resolve, reject) => {
|
|
|
|
|
if (cancelled) {
|
|
|
|
|
reject("cancelled");
|
|
|
|
|
}
|
|
|
|
|
// Downloading only audio
|
|
|
|
|
ytdl(url, {
|
|
|
|
|
const audioDownload = ytdl(url, {
|
|
|
|
|
highWaterMark: 1 << 25,
|
|
|
|
|
quality: audioTag,
|
|
|
|
|
})
|
|
|
|
|
.on("progress", (_, downloaded, size) => {
|
|
|
|
|
audioProgress = (downloaded / size) * 100;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (audioProgress != 100) {
|
|
|
|
|
getId("preparingBox").style.display = "none";
|
|
|
|
|
getId("audioProgress").value = audioProgress;
|
|
|
|
|
} else if (audioProgress == 100) {
|
|
|
|
|
resolve("audio downloaded");
|
|
|
|
|
audioDownload
|
|
|
|
|
.on("progress", (_, downloaded, size) => {
|
|
|
|
|
if (cancelled) {
|
|
|
|
|
console.log("Cancelled audio");
|
|
|
|
|
audioDownload.destroy();
|
|
|
|
|
reject();
|
|
|
|
|
} else {
|
|
|
|
|
audioProgress = (downloaded / size) * 100;
|
|
|
|
|
getId(newFolderName + "aud").value =
|
|
|
|
|
audioProgress || 0;
|
|
|
|
|
|
|
|
|
|
if (audioProgress != 100) {
|
|
|
|
|
} else if (audioProgress == 100) {
|
|
|
|
|
resolve("audio downloaded");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.pipe(fs.createWriteStream(path.join(tempDir, audioName)));
|
|
|
|
|
.pipe(
|
|
|
|
|
fs.createWriteStream(
|
|
|
|
|
path.join(newFolderPath, audioName)
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
}),
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
Promise.all([arr[0], arr[1]])
|
|
|
|
|
.then((response) => {
|
|
|
|
|
getId(newFolderName + "aud").style.display = "none";
|
|
|
|
|
getId(newFolderName + "vid").style.display = "none";
|
|
|
|
|
|
|
|
|
|
cp.exec(
|
|
|
|
|
`"${ffmpeg}" -i "${path.join(tempDir, videoName)}" -i "${
|
|
|
|
|
path.join(tempDir, audioName)
|
|
|
|
|
}" -c copy "${path.join(downloadDir, filename)}"`,
|
|
|
|
|
`"${ffmpeg}" -i "${path.join(
|
|
|
|
|
newFolderPath,
|
|
|
|
|
videoName
|
|
|
|
|
)}" -i "${path.join(
|
|
|
|
|
newFolderPath,
|
|
|
|
|
audioName
|
|
|
|
|
)}" -c copy "${path.join(downloadDir, filename)}"`,
|
|
|
|
|
(error, stdout, stderr) => {
|
|
|
|
|
if (error) {
|
|
|
|
|
console.log(error);
|
|
|
|
|
} else if (stderr) {
|
|
|
|
|
console.log("video saved");
|
|
|
|
|
// Clear temp dir
|
|
|
|
|
fs.readdirSync(tempDir).forEach((f) =>
|
|
|
|
|
fs.rmSync(`${tempDir}/${f}`)
|
|
|
|
|
fs.readdirSync(newFolderPath).forEach((f) =>
|
|
|
|
|
fs.rm(`${newFolderPath}/${f}`, () => {})
|
|
|
|
|
);
|
|
|
|
|
getId(newFolderName + "prog").textContent =
|
|
|
|
|
"Saved successfully";
|
|
|
|
|
afterSave(
|
|
|
|
|
downloadDir,
|
|
|
|
|
filename,
|
|
|
|
|
newFolderName + "prog"
|
|
|
|
|
);
|
|
|
|
|
afterSave(downloadDir, filename);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
@ -304,41 +406,70 @@ function download(type) {
|
|
|
|
|
|
|
|
|
|
// If audio
|
|
|
|
|
else {
|
|
|
|
|
ytdl(url, { quality: itag })
|
|
|
|
|
getId(newFolderName + "prog").textContent = "Starting...";
|
|
|
|
|
|
|
|
|
|
const audioDownloading = ytdl(url, { quality: itag });
|
|
|
|
|
|
|
|
|
|
audioDownloading
|
|
|
|
|
.on("progress", (_, downloaded, size) => {
|
|
|
|
|
const progress = (downloaded / size) * 100;
|
|
|
|
|
|
|
|
|
|
if (progress != 100) {
|
|
|
|
|
getId("preparingBox").style.display = "none";
|
|
|
|
|
getId("audioProgressBox").style.display = "block";
|
|
|
|
|
getId("onlyAudioProgress").value = progress;
|
|
|
|
|
} else if (progress == 100) {
|
|
|
|
|
afterSave(downloadDir, filename);
|
|
|
|
|
if (cancelled) {
|
|
|
|
|
console.log("Cancelled");
|
|
|
|
|
audioDownloading.destroy();
|
|
|
|
|
fs.rm(path.join(downloadDir, filename), (err) => {
|
|
|
|
|
if (err) {
|
|
|
|
|
console.log(err);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const progress = ((downloaded / size) * 100).toFixed(0);
|
|
|
|
|
|
|
|
|
|
getId(
|
|
|
|
|
newFolderName + "prog"
|
|
|
|
|
).textContent = `Progress: ${progress}%`;
|
|
|
|
|
|
|
|
|
|
})
|
|
|
|
|
.on("end", ()=>{
|
|
|
|
|
afterSave(
|
|
|
|
|
downloadDir,
|
|
|
|
|
filename,
|
|
|
|
|
newFolderName + "prog"
|
|
|
|
|
);
|
|
|
|
|
})
|
|
|
|
|
.pipe(fs.createWriteStream(path.join(downloadDir, filename)));
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Removing item
|
|
|
|
|
|
|
|
|
|
function fadeItem(id) {
|
|
|
|
|
let count = 0;
|
|
|
|
|
let opacity = 1;
|
|
|
|
|
const fade = setInterval(() => {
|
|
|
|
|
if (count >= 10) {
|
|
|
|
|
clearInterval(fade);
|
|
|
|
|
if (getId(id)) {
|
|
|
|
|
getId(id).remove();
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
opacity -= 0.1;
|
|
|
|
|
getId(id).style.opacity = opacity;
|
|
|
|
|
count++;
|
|
|
|
|
}
|
|
|
|
|
}, 50);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// After saving video
|
|
|
|
|
|
|
|
|
|
function afterSave(location, filename) {
|
|
|
|
|
function afterSave(location, filename, progressId) {
|
|
|
|
|
const notify = new Notification("ytDownloader", {
|
|
|
|
|
body: "File saved successfully.",
|
|
|
|
|
icon: "../assets/images/icon.png",
|
|
|
|
|
});
|
|
|
|
|
getId("videoProgressBox").style.display = "none";
|
|
|
|
|
getId("audioProgressBox").style.display = "none";
|
|
|
|
|
document.querySelector(".submitBtn").style.display = "inline-block";
|
|
|
|
|
|
|
|
|
|
getId(
|
|
|
|
|
"savedMsg"
|
|
|
|
|
).innerHTML = `File saved. Click to open <b title="Click to open" class="savedMsg">${location}</b>`;
|
|
|
|
|
|
|
|
|
|
getId("savedMsg").addEventListener("click", (e) => {
|
|
|
|
|
getId(progressId).innerHTML = `<b>File saved. Click to Open</b>`;
|
|
|
|
|
getId(progressId).addEventListener("click", () => {
|
|
|
|
|
shell.showItemInFolder(path.join(location, filename));
|
|
|
|
|
log(path.join(location, filename))
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -347,16 +478,21 @@ async function findInfo(url, itag) {
|
|
|
|
|
const data = await ytdl.getInfo(url);
|
|
|
|
|
const format = ytdl.chooseFormat(data.formats, { quality: itag });
|
|
|
|
|
const title = data.videoDetails.title;
|
|
|
|
|
const id = data.videoDetails.videoId;
|
|
|
|
|
let type;
|
|
|
|
|
let extension;
|
|
|
|
|
if (format.hasVideo) {
|
|
|
|
|
extension = format.mimeType.split("; ")[0].split("/")[1];
|
|
|
|
|
type = "Video";
|
|
|
|
|
} else {
|
|
|
|
|
type = "Audio";
|
|
|
|
|
if (format.audioCodec === "mp4a.40.2") {
|
|
|
|
|
extension = "m4a";
|
|
|
|
|
} else {
|
|
|
|
|
extension = format.audioCodec;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
let filetype = type + "/" + extension;
|
|
|
|
|
|
|
|
|
|
let quality;
|
|
|
|
|
if (format.hasVideo) {
|
|
|
|
|
@ -372,6 +508,8 @@ async function findInfo(url, itag) {
|
|
|
|
|
extension: extension,
|
|
|
|
|
quality: quality,
|
|
|
|
|
filename: filename,
|
|
|
|
|
id: id,
|
|
|
|
|
filetype: filetype,
|
|
|
|
|
};
|
|
|
|
|
return info;
|
|
|
|
|
}
|
|
|
|
|
@ -379,9 +517,9 @@ async function findInfo(url, itag) {
|
|
|
|
|
// Opening windows
|
|
|
|
|
|
|
|
|
|
getId("preferenceWin").addEventListener("click", () => {
|
|
|
|
|
ipcRenderer.send("load-page", __dirname + "/preferences.html")
|
|
|
|
|
ipcRenderer.send("load-page", __dirname + "/preferences.html");
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
getId("aboutWin").addEventListener("click", ()=>{
|
|
|
|
|
ipcRenderer.send("load-page", __dirname + "/about.html")
|
|
|
|
|
})
|
|
|
|
|
getId("aboutWin").addEventListener("click", () => {
|
|
|
|
|
ipcRenderer.send("load-page", __dirname + "/about.html");
|
|
|
|
|
});
|
|
|
|
|
|