diff --git a/assets/css/extra.css b/assets/css/extra.css
index 7534119..29567e6 100644
--- a/assets/css/extra.css
+++ b/assets/css/extra.css
@@ -186,6 +186,12 @@ input[type="text"],
background-color: var(--box-main);
}
+#ytDlpArgBox {
+ margin: 15px 15px;
+ padding: 20px 15px;
+ border-radius: 15px;
+ background-color: var(--box-main);
+}
#path {
font-family: "JetBrains";
font-size: medium;
@@ -331,3 +337,14 @@ body::-webkit-scrollbar-thumb {
#proxyTxt:invalid {
border: 2px solid var(--redBtn);
}
+
+#customArgsInput {
+ padding: 8px;
+ width: 94%;
+ margin: auto;
+ outline: none;
+ font-family: "JetBrains";
+ border-radius: 8px;
+ resize: none;
+ overflow: hidden;
+}
diff --git a/html/index.html b/html/index.html
index e1bbcc3..bfbab0c 100644
--- a/html/index.html
+++ b/html/index.html
@@ -160,7 +160,7 @@
diff --git a/main.js b/main.js
index dc37332..14b01a5 100644
--- a/main.js
+++ b/main.js
@@ -18,7 +18,7 @@ process.env.ELECTRON_DISABLE_SECURITY_WARNINGS = "true";
autoUpdater.autoDownload = false;
const USER_DATA_PATH = app.getPath("userData");
-const CONFIG_FILE_PATH = path.join(USER_DATA_PATH, "config.json");
+const CONFIG_FILE_PATH = path.join(USER_DATA_PATH, "ytdownloader.json");
const appState = {
/** @type {BrowserWindow | null} */
@@ -199,14 +199,14 @@ function createTray() {
const contextMenu = Menu.buildFromTemplate([
{
- label: i18n("Open app"),
+ label: i18n("openApp"),
click: () => {
appState.mainWindow?.show();
if (app.dock) app.dock.show();
},
},
{
- label: i18n("Paste video link"),
+ label: i18n("pasteVideoLink"),
click: async () => {
const text = clipboard.readText();
appState.mainWindow?.show();
@@ -226,7 +226,7 @@ function createTray() {
},
},
{
- label: i18n("Download playlist"),
+ label: i18n("downloadPlaylistButton"),
click: () => {
appState.indexPageIsOpen = false;
appState.mainWindow?.loadFile("html/playlist.html");
@@ -235,7 +235,7 @@ function createTray() {
},
},
{
- label: i18n("Quit"),
+ label: i18n("quit"),
click: () => {
app.quit();
},
@@ -445,9 +445,9 @@ function registerAutoUpdaterEvents() {
autoUpdater.on("update-available", async (info) => {
const dialogOpts = {
type: "info",
- buttons: [i18n("Update"), i18n("No")],
+ buttons: [i18n("update"), i18n("no")],
title: "Update Available",
- message: `A new version (${info.version}) is available. Do you want to update?`,
+ message: i18n("updateAvailablePrompt"),
detail:
info.releaseNotes?.toString().replace(/<[^>]*>?/gm, "") ||
"No details available.",
@@ -464,9 +464,9 @@ function registerAutoUpdaterEvents() {
autoUpdater.on("update-downloaded", async () => {
const dialogOpts = {
type: "info",
- buttons: [i18n("Restart"), i18n("Later")],
+ buttons: [i18n("restart"), i18n("later")],
title: "Update Ready",
- message: "The update has been downloaded. Install and restart now?",
+ message: i18n("installAndRestartPrompt"),
};
const {response} = await dialog.showMessageBox(
appState.mainWindow,
@@ -481,7 +481,7 @@ function registerAutoUpdaterEvents() {
console.error("Auto-update error:", error);
dialog.showErrorBox(
"Update Error",
- `An error occurred during the update process: ${error.message}`
+ i18n("updateError")
);
});
}
@@ -501,21 +501,6 @@ async function loadConfiguration() {
try {
const fileContent = await fs.readFile(CONFIG_FILE_PATH, "utf8");
appState.config = JSON.parse(fileContent);
-
- if (appState.config && typeof appState.config.bounds === "string") {
- console.log("Old config format detected. Migrating...");
-
- try {
- appState.config.bounds = JSON.parse(appState.config.bounds);
- } catch (migrationError) {
- console.error(
- "Failed to migrate from old config. Resetting to default.",
- migrationError
- );
-
- appState.config.bounds = {width: 1024, height: 768};
- }
- }
} catch (error) {
console.log(
"Could not load config file, using defaults.",
diff --git a/src/preferences.js b/src/preferences.js
index 7229869..9c173a5 100644
--- a/src/preferences.js
+++ b/src/preferences.js
@@ -24,7 +24,7 @@ if (rightToLeft == "true") {
let downloadPath = localStorage.getItem("downloadPath");
getId("path").textContent = downloadPath;
-const {ipcRenderer} = require("electron");
+const {ipcRenderer, shell} = require("electron");
/**
*
* @param {string} id
@@ -162,6 +162,24 @@ getId("proxyTxt").addEventListener("change", () => {
localStorage.setItem("proxy", proxy);
});
+// Custom yt-dlp args
+const ytDlpArgsInput = getId("customArgsInput");
+let customYtDlpArgs = localStorage.getItem("customYtDlpArgs");
+if (customYtDlpArgs) {
+ ytDlpArgsInput.value = customYtDlpArgs;
+ ytDlpArgsInput.style.height = ytDlpArgsInput.scrollHeight + "px";
+}
+ytDlpArgsInput.addEventListener("input", () => {
+ customYtDlpArgs = getId("customArgsInput").value;
+ localStorage.setItem("customYtDlpArgs", customYtDlpArgs.trim());
+ ytDlpArgsInput.style.height = "auto";
+ ytDlpArgsInput.style.height = ytDlpArgsInput.scrollHeight + "px";
+});
+
+getId("learnMoreLink").addEventListener("click", () => {
+ shell.openExternal("https://github.com/aandrew-me/ytDownloader/wiki/Custom-yt%E2%80%90dlp-options")
+})
+
// Reload
function reload() {
ipcRenderer.send("reload");
diff --git a/src/renderer.js b/src/renderer.js
index bb81833..1306dc1 100644
--- a/src/renderer.js
+++ b/src/renderer.js
@@ -73,6 +73,7 @@ const CONSTANTS = {
CONFIG_PATH: "configPath",
AUTO_UPDATE: "autoUpdate",
CLOSE_TO_TRAY: "closeToTray",
+ YT_DLP_CUSTOM_ARGS: "customYtDlpArgs",
},
};
@@ -116,7 +117,7 @@ class YtDownloaderApp {
showMoreFormats: false,
proxy: "",
browserForCookies: "",
- configPath: "",
+ customYtDlpArgs: "",
},
downloadControllers: new Map(),
downloadedItems: new Set(),
@@ -368,11 +369,25 @@ class YtDownloaderApp {
}
// Priority 4: Default location or download
+ const ytDlpPath = await this.ensureYtDlpBinary(defaultYtDlpPath)
+ return ytDlpPath
+ }
+
+ /**
+ * Checks for the presence of the yt-dlp binary at the default path.
+ * If not found, it attempts to download it from GitHub.
+ *
+ * @param {string} defaultYtDlpPath The expected path to the yt-dlp binary.
+ * @returns {Promise} A promise that resolves with the path to the yt-dlp binary.
+ * @throws {Error} Throws an error if the download fails.
+ */
+ async ensureYtDlpBinary(defaultYtDlpPath) {
try {
await promises.access(defaultYtDlpPath);
return defaultYtDlpPath;
} catch {
console.log("yt-dlp not found, downloading...");
+
$(CONSTANTS.DOM_IDS.POPUP_BOX).style.display = "block";
$(CONSTANTS.DOM_IDS.POPUP_SVG).style.display = "inline";
document.querySelector("#popupBox p").textContent = i18n.__(
@@ -411,6 +426,16 @@ class YtDownloaderApp {
);
$(CONSTANTS.DOM_IDS.POPUP_SVG).style.display = "none";
+
+ const tryAgainBtn = document.createElement("button");
+ tryAgainBtn.id = "tryBtn";
+ tryAgainBtn.textContent = i18n.__("tryAgain")
+ tryAgainBtn.addEventListener("click", () => {
+ // TODO: Improve it
+ ipcRenderer.send("reload");
+ });
+ document.getElementById("popup").appendChild(tryAgainBtn)
+
throw new Error("Failed to download yt-dlp.");
}
}
@@ -505,14 +530,22 @@ class YtDownloaderApp {
localStorage.getItem(
CONSTANTS.LOCAL_STORAGE_KEYS.BROWSER_COOKIES
) || "";
- prefs.configPath =
- localStorage.getItem(CONSTANTS.LOCAL_STORAGE_KEYS.CONFIG_PATH) ||
- "";
+ prefs.customYtDlpArgs =
+ localStorage.getItem(
+ CONSTANTS.LOCAL_STORAGE_KEYS.YT_DLP_CUSTOM_ARGS
+ ) || "";
+ this.state.downloadDir = localStorage.getItem(
+ CONSTANTS.LOCAL_STORAGE_KEYS.DOWNLOAD_PATH
+ );
const maxDownloads = Number(
localStorage.getItem(CONSTANTS.LOCAL_STORAGE_KEYS.MAX_DOWNLOADS)
);
this.state.maxActiveDownloads = maxDownloads >= 1 ? maxDownloads : 5;
+
+ // Update UI with loaded settings
+ $(CONSTANTS.DOM_IDS.CUSTOM_ARGS_INPUT).value = prefs.customYtDlpArgs;
+ $(CONSTANTS.DOM_IDS.PATH_DISPLAY).textContent = this.state.downloadDir;
}
/**
diff --git a/translations/en.json b/translations/en.json
index 73b14d5..88842cb 100644
--- a/translations/en.json
+++ b/translations/en.json
@@ -157,5 +157,7 @@
"updatedYtdlp": "Updated yt-dlp",
"ytDlpUpdateRequired": "If yt-dlp is updating, wait for the update to finish. If you have installed yt-dlp by yourself, please update it.",
"failedToDeleteHistoryItem": "Failed to delete history item",
- "customArgsTxt": "Custom yt-dlp arguments"
+ "customArgsTxt": "Set custom yt-dlp options.",
+ "learnMore": "Learn more",
+ "updateError": "An error occurred during the update process"
}