|
|
|
|
@ -18,6 +18,9 @@ const playlistDownloader = {
|
|
|
|
|
playlistName: "",
|
|
|
|
|
originalCount: 0,
|
|
|
|
|
currentDownloadProcess: null,
|
|
|
|
|
currentAbortController: null,
|
|
|
|
|
isDownloading: false,
|
|
|
|
|
isCancelled: false,
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
config: {
|
|
|
|
|
@ -53,6 +56,7 @@ const playlistDownloader = {
|
|
|
|
|
selectLocationBtn: document.getElementById("selectLocation"),
|
|
|
|
|
pathDisplay: document.getElementById("path"),
|
|
|
|
|
openDownloadsBtn: document.getElementById("openDownloads"),
|
|
|
|
|
stopDownloadBtn: document.getElementById("stopDownload"),
|
|
|
|
|
|
|
|
|
|
videoToggle: document.getElementById("videoToggle"),
|
|
|
|
|
audioToggle: document.getElementById("audioToggle"),
|
|
|
|
|
@ -150,7 +154,9 @@ const playlistDownloader = {
|
|
|
|
|
document.activeElement.tagName !== "INPUT" &&
|
|
|
|
|
document.activeElement.tagName !== "TEXTAREA")
|
|
|
|
|
) {
|
|
|
|
|
this.pasteLink();
|
|
|
|
|
if (!this.state.isDownloading) {
|
|
|
|
|
this.pasteLink();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
@ -185,6 +191,9 @@ const playlistDownloader = {
|
|
|
|
|
this.ui.openDownloadsBtn.addEventListener("click", () =>
|
|
|
|
|
this.openDownloadsFolder(),
|
|
|
|
|
);
|
|
|
|
|
this.ui.stopDownloadBtn.addEventListener("click", () =>
|
|
|
|
|
this.stopDownload(),
|
|
|
|
|
);
|
|
|
|
|
this.ui.closeHiddenBtn.addEventListener("click", () =>
|
|
|
|
|
this.hideOptions(true),
|
|
|
|
|
);
|
|
|
|
|
@ -214,6 +223,8 @@ const playlistDownloader = {
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
async startDownload(type) {
|
|
|
|
|
if (this.state.isDownloading) return;
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
this.state.url = this.validateUrl(this.state.url);
|
|
|
|
|
} catch (_) {
|
|
|
|
|
@ -222,49 +233,66 @@ const playlistDownloader = {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await this.updateDynamicConfig();
|
|
|
|
|
this.hideOptions();
|
|
|
|
|
|
|
|
|
|
const controller = new AbortController();
|
|
|
|
|
const baseArgs = this.buildBaseArgs();
|
|
|
|
|
let specificArgs = [];
|
|
|
|
|
try {
|
|
|
|
|
await this.updateDynamicConfig();
|
|
|
|
|
this.hideOptions();
|
|
|
|
|
|
|
|
|
|
this.state.isDownloading = true;
|
|
|
|
|
this.state.isCancelled = false;
|
|
|
|
|
|
|
|
|
|
this.state.currentAbortController = new AbortController();
|
|
|
|
|
const baseArgs = this.buildBaseArgs();
|
|
|
|
|
let specificArgs = [];
|
|
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
|
case "video":
|
|
|
|
|
specificArgs = this.getVideoArgs();
|
|
|
|
|
break;
|
|
|
|
|
case "audio":
|
|
|
|
|
specificArgs = this.getAudioArgs();
|
|
|
|
|
break;
|
|
|
|
|
case "thumbnails":
|
|
|
|
|
specificArgs = this.getThumbnailArgs();
|
|
|
|
|
break;
|
|
|
|
|
case "links":
|
|
|
|
|
specificArgs = this.getLinkArgs();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
|
case "video":
|
|
|
|
|
specificArgs = this.getVideoArgs();
|
|
|
|
|
break;
|
|
|
|
|
case "audio":
|
|
|
|
|
specificArgs = this.getAudioArgs();
|
|
|
|
|
break;
|
|
|
|
|
case "thumbnails":
|
|
|
|
|
specificArgs = this.getThumbnailArgs();
|
|
|
|
|
break;
|
|
|
|
|
case "links":
|
|
|
|
|
specificArgs = this.getLinkArgs();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
const allArgs = [...baseArgs, ...specificArgs, this.state.url].filter(
|
|
|
|
|
Boolean,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const allArgs = [...baseArgs, ...specificArgs, this.state.url].filter(
|
|
|
|
|
Boolean,
|
|
|
|
|
);
|
|
|
|
|
console.log(`Command: ${this.state.ytDlpPath}`, allArgs.join(" "));
|
|
|
|
|
this.state.currentDownloadProcess = this.state.ytDlpWrap.exec(
|
|
|
|
|
allArgs,
|
|
|
|
|
{shell: false},
|
|
|
|
|
this.state.currentAbortController.signal,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
console.log(`Command: ${this.state.ytDlpPath}`, allArgs.join(" "));
|
|
|
|
|
this.state.currentDownloadProcess = this.state.ytDlpWrap.exec(
|
|
|
|
|
allArgs,
|
|
|
|
|
{shell: false},
|
|
|
|
|
controller.signal,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
document.addEventListener("beforeunload", () => {
|
|
|
|
|
if (
|
|
|
|
|
this.state.currentDownloadProcess &&
|
|
|
|
|
!this.state.currentDownloadProcess.killed
|
|
|
|
|
) {
|
|
|
|
|
this.state.currentDownloadProcess.kill();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
// TODO: Avoid duplication of event listeners
|
|
|
|
|
document.addEventListener("beforeunload", () => {
|
|
|
|
|
if (this.state.currentAbortController) {
|
|
|
|
|
try {
|
|
|
|
|
this.state.currentAbortController.abort();
|
|
|
|
|
} catch (e) {}
|
|
|
|
|
}
|
|
|
|
|
if (
|
|
|
|
|
this.state.currentDownloadProcess &&
|
|
|
|
|
this.state.currentDownloadProcess.ytDlpProcess &&
|
|
|
|
|
!this.state.currentDownloadProcess.ytDlpProcess.killed
|
|
|
|
|
) {
|
|
|
|
|
try {
|
|
|
|
|
this.state.currentDownloadProcess.ytDlpProcess.kill();
|
|
|
|
|
} catch (e) {}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
this.handleDownloadEvents(this.state.currentDownloadProcess, type);
|
|
|
|
|
this.handleDownloadEvents(this.state.currentDownloadProcess, type);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
this.showError(error);
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
buildBaseArgs() {
|
|
|
|
|
@ -481,7 +509,29 @@ const playlistDownloader = {
|
|
|
|
|
process.on("close", () => this.finishDownload(count));
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
stopDownload() {
|
|
|
|
|
this.state.isCancelled = true;
|
|
|
|
|
if (this.state.currentAbortController) {
|
|
|
|
|
try {
|
|
|
|
|
this.state.currentAbortController.abort();
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.error("Failed to abort controller:", e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (
|
|
|
|
|
this.state.currentDownloadProcess &&
|
|
|
|
|
this.state.currentDownloadProcess.ytDlpProcess
|
|
|
|
|
) {
|
|
|
|
|
try {
|
|
|
|
|
this.state.currentDownloadProcess.ytDlpProcess.kill();
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.error("Failed to kill ytDlpProcess:", e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
pasteLink() {
|
|
|
|
|
if (this.state.isDownloading) return;
|
|
|
|
|
this.state.url = clipboard.readText();
|
|
|
|
|
this.ui.linkDisplay.textContent = ` ${this.state.url}`;
|
|
|
|
|
this.ui.optionsContainer.style.display = "block";
|
|
|
|
|
@ -571,6 +621,7 @@ const playlistDownloader = {
|
|
|
|
|
this.ui.errorDetails.style.display = "none";
|
|
|
|
|
this.ui.errorDetails.textContent = "";
|
|
|
|
|
this.ui.errorMsgDisplay.style.display = "none";
|
|
|
|
|
this.ui.stopDownloadBtn.style.display = "none";
|
|
|
|
|
|
|
|
|
|
if (!justHide) {
|
|
|
|
|
this.ui.playlistNameDisplay.textContent = `${window.i18n.__(
|
|
|
|
|
@ -578,10 +629,26 @@ const playlistDownloader = {
|
|
|
|
|
)}...`;
|
|
|
|
|
this.ui.pasteLinkBtn.style.display = "none";
|
|
|
|
|
this.ui.openDownloadsBtn.style.display = "inline-block";
|
|
|
|
|
this.ui.stopDownloadBtn.style.display = "inline-block";
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
finishDownload(count) {
|
|
|
|
|
if (!this.state.isDownloading) return;
|
|
|
|
|
this.state.isDownloading = false;
|
|
|
|
|
this.ui.stopDownloadBtn.style.display = "none";
|
|
|
|
|
|
|
|
|
|
if (this.state.isCancelled) {
|
|
|
|
|
this.state.isCancelled = false;
|
|
|
|
|
const lastProgress = document.getElementById(`p${count}`);
|
|
|
|
|
if (lastProgress) {
|
|
|
|
|
lastProgress.textContent = window.i18n.__("cancel");
|
|
|
|
|
}
|
|
|
|
|
this.ui.playlistNameDisplay.textContent = window.i18n.__("cancel");
|
|
|
|
|
this.ui.pasteLinkBtn.style.display = "inline-block";
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const lastProgress = document.getElementById(`p${count}`);
|
|
|
|
|
if (lastProgress)
|
|
|
|
|
lastProgress.textContent = window.i18n.__("fileSaved");
|
|
|
|
|
@ -597,6 +664,17 @@ const playlistDownloader = {
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
showError(error) {
|
|
|
|
|
const wasDownloading = this.state.isDownloading;
|
|
|
|
|
this.state.isDownloading = false;
|
|
|
|
|
this.ui.stopDownloadBtn.style.display = "none";
|
|
|
|
|
|
|
|
|
|
if (this.state.isCancelled && wasDownloading) {
|
|
|
|
|
this.state.isCancelled = false;
|
|
|
|
|
this.ui.playlistNameDisplay.textContent = window.i18n.__("cancel");
|
|
|
|
|
this.ui.pasteLinkBtn.style.display = "inline-block";
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
console.error("Download Error:", error.toString());
|
|
|
|
|
this.ui.pasteLinkBtn.style.display = "inline-block";
|
|
|
|
|
this.ui.openDownloadsBtn.style.display = "none";
|
|
|
|
|
@ -663,6 +741,14 @@ const playlistDownloader = {
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
navigate(type, page) {
|
|
|
|
|
if (this.state.isDownloading) {
|
|
|
|
|
const confirmMsg = window.i18n ? window.i18n.__("cancel_download") + "?" : "Are you sure you want to cancel the download?";
|
|
|
|
|
const choice = confirm(confirmMsg);
|
|
|
|
|
if (!choice) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
this.stopDownload();
|
|
|
|
|
}
|
|
|
|
|
this.closeMenu();
|
|
|
|
|
const event = type === "page" ? "load-page" : "load-win";
|
|
|
|
|
ipcRenderer.send(event, path.join(__dirname, page));
|
|
|
|
|
|