From 403051e3cf2cc28249d695a5996dc7b34272bafb Mon Sep 17 00:00:00 2001 From: Sucukdeluxe Date: Sun, 9 Aug 2026 14:43:42 +0200 Subject: [PATCH] feat: move default download directories to desktop --- README.md | 5 +++ package-lock.json | 4 +-- package.json | 2 +- src/main/constants.ts | 8 ++--- src/main/storage.ts | 62 ++++++++++++++++++++++++++++--------- tests/storage.test.ts | 71 +++++++++++++++++++++++++++++++++---------- 6 files changed, 114 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index 82b48d6..c0cc8ea 100644 --- a/README.md +++ b/README.md @@ -301,6 +301,11 @@ This makes it easy to share one URL plus token during support, so current packag Detailed release history is published on [GitHub Releases](https://github.com/Sucukdeluxe/multi-debrid-downloader/releases). +### 2.0.5 + +- Move the default download, extraction, and video collection directories to the desktop under `Multi-Debrid-Downloader`. +- Migrate untouched legacy default directories from `Downloads\RealDebrid` to the new desktop location. + ### 2.0.4 - Rename the online backup action from "create" to "export" for clearer wording. diff --git a/package-lock.json b/package-lock.json index 1970da4..e548dcf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "real-debrid-downloader", - "version": "2.0.4", + "version": "2.0.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "real-debrid-downloader", - "version": "2.0.4", + "version": "2.0.5", "license": "MIT", "dependencies": { "adm-zip": "0.6.0", diff --git a/package.json b/package.json index 566714c..274cdf3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "real-debrid-downloader", - "version": "2.0.4", + "version": "2.0.5", "description": "Desktop downloader", "main": "build/main/main/main.js", "author": "Sucukdeluxe", diff --git a/src/main/constants.ts b/src/main/constants.ts index e8ddeac..4261e99 100644 --- a/src/main/constants.ts +++ b/src/main/constants.ts @@ -37,11 +37,11 @@ export const MAX_LINK_ARTIFACT_BYTES = 256 * 1024; export const SPEED_WINDOW_SECONDS = 1; export const CLIPBOARD_POLL_INTERVAL_MS = 2000; -export const DEFAULT_UPDATE_REPO = "Sucukdeluxe/multi-debrid-downloader"; -export const ONLINE_BACKUP_API_URL = "https://downloader.24-music.de/backup-api"; +export const DEFAULT_UPDATE_REPO = "Sucukdeluxe/multi-debrid-downloader"; +export const ONLINE_BACKUP_API_URL = "https://downloader.24-music.de/backup-api"; export function defaultSettings(): AppSettings { - const baseDir = path.join(os.homedir(), "Downloads", "RealDebrid"); + const baseDir = path.join(os.homedir(), "Desktop", "Multi-Debrid-Downloader"); return { token: "", realDebridUseWebLogin: false, @@ -110,7 +110,7 @@ export function defaultSettings(): AppSettings { hideExtractedItems: true, confirmDeleteSelection: true, backupIncludeDownloads: false, - backupIncludeRemoteDiagnostics: false, + backupIncludeRemoteDiagnostics: false, notifyUrl: "", notifyMention: "", notifyOnPackageCompleted: false, diff --git a/src/main/storage.ts b/src/main/storage.ts index 8350deb..c648122 100644 --- a/src/main/storage.ts +++ b/src/main/storage.ts @@ -1,5 +1,6 @@ import fs from "node:fs"; import fsp from "node:fs/promises"; +import os from "node:os"; import path from "node:path"; import { getDebridLinkApiKeyIds } from "../shared/debrid-link-keys"; import { getMegaDebridAccountIds } from "../shared/mega-debrid-accounts"; @@ -104,6 +105,36 @@ function normalizeAbsoluteDir(value: unknown, fallback: string): string { return path.resolve(text); } +function samePath(left: unknown, right: string): boolean { + const leftText = asText(left); + if (!leftText) { + return false; + } + const leftResolved = path.resolve(leftText); + const rightResolved = path.resolve(right); + return process.platform === "win32" + ? leftResolved.toLowerCase() === rightResolved.toLowerCase() + : leftResolved === rightResolved; +} + +function migrateLegacyDefaultDirectories(settings: AppSettings, defaults: AppSettings): AppSettings { + const legacyBaseDir = path.join(os.homedir(), "Downloads", "RealDebrid"); + const legacyExtractDir = path.join(legacyBaseDir, "_entpackt"); + const legacyMkvLibraryDir = path.join(legacyBaseDir, "_mkv"); + const isUntouchedLegacyConfiguration = samePath(settings.outputDir, legacyBaseDir) + && samePath(settings.extractDir, legacyExtractDir) + && samePath(settings.mkvLibraryDir, legacyMkvLibraryDir); + if (!isUntouchedLegacyConfiguration) { + return settings; + } + return { + ...settings, + outputDir: defaults.outputDir, + extractDir: defaults.extractDir, + mkvLibraryDir: defaults.mkvLibraryDir + }; +} + const DEFAULT_COLUMN_ORDER = ["name", "size", "progress", "hoster", "account", "prio", "status", "speed"]; const ALL_VALID_COLUMNS = new Set([...DEFAULT_COLUMN_ORDER, "added"]); @@ -328,22 +359,23 @@ function normalizeProviderOrder( return result; } -const DEPRECATED_UPDATE_REPO_NAMES = new Set([ - "real-debrid-downloader", - "multi-debrid-downloader" -]); - -function migrateUpdateRepo(raw: string, fallback: string): string { - const trimmed = raw.trim(); - const repoName = trimmed.split("/").filter(Boolean).pop()?.replace(/\.git$/i, "").toLowerCase() || ""; - if (!trimmed || (DEPRECATED_UPDATE_REPO_NAMES.has(repoName) && trimmed.toLowerCase() !== fallback.toLowerCase())) { - return fallback; - } +const DEPRECATED_UPDATE_REPO_NAMES = new Set([ + "real-debrid-downloader", + "multi-debrid-downloader" +]); + +function migrateUpdateRepo(raw: string, fallback: string): string { + const trimmed = raw.trim(); + const repoName = trimmed.split("/").filter(Boolean).pop()?.replace(/\.git$/i, "").toLowerCase() || ""; + if (!trimmed || (DEPRECATED_UPDATE_REPO_NAMES.has(repoName) && trimmed.toLowerCase() !== fallback.toLowerCase())) { + return fallback; + } return trimmed; } export function normalizeSettings(settings: AppSettings): AppSettings { const defaults = defaultSettings(); + const directorySettings = migrateLegacyDefaultDirectories(settings, defaults); const currentUsageDay = getProviderUsageDayKey(); const megaLogin = asText(settings.megaLogin); const megaPassword = asText(settings.megaPassword); @@ -419,15 +451,15 @@ export function normalizeSettings(settings: AppSettings): AppSettings { providerSecondary: normalizeFallbackProvider(settings.providerSecondary, megaDebridPreferApi, megaDebridApiEnabled, megaDebridWebEnabled), providerTertiary: normalizeFallbackProvider(settings.providerTertiary, megaDebridPreferApi, megaDebridApiEnabled, megaDebridWebEnabled), autoProviderFallback: Boolean(settings.autoProviderFallback), - outputDir: normalizeAbsoluteDir(settings.outputDir, defaults.outputDir), + outputDir: normalizeAbsoluteDir(directorySettings.outputDir, defaults.outputDir), packageName: asText(settings.packageName), autoExtract: Boolean(settings.autoExtract), autoRename4sf4sj: Boolean(settings.autoRename4sf4sj), keepGermanAudioOnly: Boolean(settings.keepGermanAudioOnly), germanAudioMode: settings.germanAudioMode === "first" ? "first" : "tag", - extractDir: normalizeAbsoluteDir(settings.extractDir, defaults.extractDir), + extractDir: normalizeAbsoluteDir(directorySettings.extractDir, defaults.extractDir), collectMkvToLibrary: Boolean(settings.collectMkvToLibrary), - mkvLibraryDir: normalizeAbsoluteDir(settings.mkvLibraryDir, defaults.mkvLibraryDir), + mkvLibraryDir: normalizeAbsoluteDir(directorySettings.mkvLibraryDir, defaults.mkvLibraryDir), createExtractSubfolder: Boolean(settings.createExtractSubfolder), hybridExtract: Boolean(settings.hybridExtract), cleanupMode: settings.cleanupMode, @@ -463,7 +495,7 @@ export function normalizeSettings(settings: AppSettings): AppSettings { hideExtractedItems: settings.hideExtractedItems !== undefined ? Boolean(settings.hideExtractedItems) : defaults.hideExtractedItems, confirmDeleteSelection: settings.confirmDeleteSelection !== undefined ? Boolean(settings.confirmDeleteSelection) : defaults.confirmDeleteSelection, backupIncludeDownloads: settings.backupIncludeDownloads !== undefined ? Boolean(settings.backupIncludeDownloads) : defaults.backupIncludeDownloads, - backupIncludeRemoteDiagnostics: settings.backupIncludeRemoteDiagnostics !== undefined ? Boolean(settings.backupIncludeRemoteDiagnostics) : defaults.backupIncludeRemoteDiagnostics, + backupIncludeRemoteDiagnostics: settings.backupIncludeRemoteDiagnostics !== undefined ? Boolean(settings.backupIncludeRemoteDiagnostics) : defaults.backupIncludeRemoteDiagnostics, notifyUrl: asText(settings.notifyUrl) || defaults.notifyUrl, notifyMention: asText(settings.notifyMention) || defaults.notifyMention, notifyOnPackageCompleted: settings.notifyOnPackageCompleted !== undefined ? Boolean(settings.notifyOnPackageCompleted) : defaults.notifyOnPackageCompleted, diff --git a/tests/storage.test.ts b/tests/storage.test.ts index a78626d..18f49d1 100644 --- a/tests/storage.test.ts +++ b/tests/storage.test.ts @@ -17,6 +17,45 @@ afterEach(() => { }); describe("settings storage", () => { + it("defaults download directories to the desktop project folder", () => { + const baseDir = path.join(os.homedir(), "Desktop", "Multi-Debrid-Downloader"); + const defaults = defaultSettings(); + + expect(defaults.outputDir).toBe(baseDir); + expect(defaults.extractDir).toBe(path.join(baseDir, "_entpackt")); + expect(defaults.mkvLibraryDir).toBe(path.join(baseDir, "_mkv")); + }); + + it("migrates an untouched legacy directory configuration", () => { + const legacyBaseDir = path.join(os.homedir(), "Downloads", "RealDebrid"); + const normalized = normalizeSettings({ + ...defaultSettings(), + outputDir: legacyBaseDir, + extractDir: path.join(legacyBaseDir, "_entpackt"), + mkvLibraryDir: path.join(legacyBaseDir, "_mkv") + }); + const defaults = defaultSettings(); + + expect(normalized.outputDir).toBe(defaults.outputDir); + expect(normalized.extractDir).toBe(defaults.extractDir); + expect(normalized.mkvLibraryDir).toBe(defaults.mkvLibraryDir); + }); + + it("keeps custom directory choices during legacy migration", () => { + const legacyBaseDir = path.join(os.homedir(), "Downloads", "RealDebrid"); + const customOutputDir = path.join(os.homedir(), "Videos", "Custom-Downloads"); + const normalized = normalizeSettings({ + ...defaultSettings(), + outputDir: customOutputDir, + extractDir: path.join(legacyBaseDir, "_entpackt"), + mkvLibraryDir: path.join(legacyBaseDir, "_mkv") + }); + + expect(normalized.outputDir).toBe(customOutputDir); + expect(normalized.extractDir).toBe(path.join(legacyBaseDir, "_entpackt")); + expect(normalized.mkvLibraryDir).toBe(path.join(legacyBaseDir, "_mkv")); + }); + it("does not persist provider credentials when rememberToken is disabled", () => { const dir = fs.mkdtempSync(path.join(os.tmpdir(), "rd-store-")); tempDirs.push(dir); @@ -71,7 +110,7 @@ describe("settings storage", () => { expect(loaded.allDebridToken).toBe("all-token"); }); - it("normalizes invalid enum and numeric values", () => { + it("normalizes invalid enum and numeric values", () => { const normalized = normalizeSettings({ ...defaultSettings(), providerPrimary: "invalid-provider" as unknown as AppSettings["providerPrimary"], @@ -105,21 +144,21 @@ describe("settings storage", () => { expect(normalized.outputDir).toBe(defaultSettings().outputDir); expect(normalized.extractDir).toBe(defaultSettings().extractDir); expect(normalized.mkvLibraryDir).toBe(defaultSettings().mkvLibraryDir); - expect(normalized.updateRepo).toBe(defaultSettings().updateRepo); - }); - - it("migrates the previous private update repository to the public release repository", () => { - for (const updateRepo of [ - "legacy-owner/real-debrid-downloader", - "legacy-owner/multi-debrid-downloader", - "legacy-owner/real-debrid-downloader.git" - ]) { - const normalized = normalizeSettings({ ...defaultSettings(), updateRepo }); - expect(normalized.updateRepo).toBe(defaultSettings().updateRepo); - } - }); - - it("normalizes malformed persisted config on load", () => { + expect(normalized.updateRepo).toBe(defaultSettings().updateRepo); + }); + + it("migrates the previous private update repository to the public release repository", () => { + for (const updateRepo of [ + "legacy-owner/real-debrid-downloader", + "legacy-owner/multi-debrid-downloader", + "legacy-owner/real-debrid-downloader.git" + ]) { + const normalized = normalizeSettings({ ...defaultSettings(), updateRepo }); + expect(normalized.updateRepo).toBe(defaultSettings().updateRepo); + } + }); + + it("normalizes malformed persisted config on load", () => { const dir = fs.mkdtempSync(path.join(os.tmpdir(), "rd-store-")); tempDirs.push(dir); const paths = createStoragePaths(dir);