diff --git a/package-lock.json b/package-lock.json index 9c4aa11..20c7924 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "twitch-vod-manager", - "version": "4.2.3", + "version": "4.2.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "twitch-vod-manager", - "version": "4.2.3", + "version": "4.2.4", "license": "MIT", "dependencies": { "axios": "^1.6.0", diff --git a/package.json b/package.json index 748bb81..e281541 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "twitch-vod-manager", - "version": "4.2.3", + "version": "4.2.4", "description": "Twitch VOD Manager - Download Twitch VODs easily", "main": "dist/main.js", "author": "xRangerDE", diff --git a/src/index.html b/src/index.html index 5646237..fbf66f8 100644 --- a/src/index.html +++ b/src/index.html @@ -450,6 +450,10 @@ Duplikate in Queue verhindern +
diff --git a/src/main.ts b/src/main.ts index 6d1b7d9..e965661 100644 --- a/src/main.ts +++ b/src/main.ts @@ -83,6 +83,7 @@ interface Config { smart_queue_scheduler: boolean; performance_mode: PerformanceMode; prevent_duplicate_downloads: boolean; + persist_queue_on_restart: boolean; metadata_cache_minutes: number; } @@ -238,6 +239,7 @@ const defaultConfig: Config = { smart_queue_scheduler: true, performance_mode: DEFAULT_PERFORMANCE_MODE, prevent_duplicate_downloads: true, + persist_queue_on_restart: true, metadata_cache_minutes: DEFAULT_METADATA_CACHE_MINUTES }; @@ -272,6 +274,7 @@ function normalizeConfigTemplates(input: Config): Config { smart_queue_scheduler: input.smart_queue_scheduler !== false, performance_mode: normalizePerformanceMode(input.performance_mode), prevent_duplicate_downloads: input.prevent_duplicate_downloads !== false, + persist_queue_on_restart: input.persist_queue_on_restart !== false, metadata_cache_minutes: normalizeMetadataCacheMinutes(input.metadata_cache_minutes) }; } @@ -300,6 +303,10 @@ function saveConfig(config: Config): void { // QUEUE MANAGEMENT // ========================================== function loadQueue(): QueueItem[] { + if (config.persist_queue_on_restart === false) { + return []; + } + try { if (fs.existsSync(QUEUE_FILE)) { const data = fs.readFileSync(QUEUE_FILE, 'utf-8'); @@ -314,7 +321,22 @@ function loadQueue(): QueueItem[] { let queueSaveTimer: NodeJS.Timeout | null = null; let pendingQueueSnapshot: QueueItem[] | null = null; +function clearQueueFileFromDisk(): void { + try { + if (fs.existsSync(QUEUE_FILE)) { + fs.unlinkSync(QUEUE_FILE); + } + } catch (e) { + console.error('Error clearing queue file:', e); + } +} + function writeQueueToDisk(queue: QueueItem[]): void { + if (config.persist_queue_on_restart === false) { + clearQueueFileFromDisk(); + return; + } + try { fs.writeFileSync(QUEUE_FILE, JSON.stringify(queue, null, 2)); } catch (e) { @@ -323,6 +345,16 @@ function writeQueueToDisk(queue: QueueItem[]): void { } function saveQueue(queue: QueueItem[], force = false): void { + if (config.persist_queue_on_restart === false) { + pendingQueueSnapshot = null; + if (queueSaveTimer) { + clearTimeout(queueSaveTimer); + queueSaveTimer = null; + } + clearQueueFileFromDisk(); + return; + } + pendingQueueSnapshot = queue; if (force) { @@ -3207,6 +3239,7 @@ function setupAutoUpdater() { autoUpdaterInitialized = true; autoUpdater.autoDownload = false; autoUpdater.autoInstallOnAppQuit = true; + autoUpdater.autoRunAppAfterInstall = true; autoUpdater.on('checking-for-update', () => { console.log('Checking for updates...'); @@ -3308,6 +3341,7 @@ ipcMain.handle('save-config', (_, newConfig: Partial) => { const previousClientId = config.client_id; const previousClientSecret = config.client_secret; const previousCacheMinutes = config.metadata_cache_minutes; + const previousPersistQueueOnRestart = config.persist_queue_on_restart; config = normalizeConfigTemplates({ ...config, ...newConfig }); @@ -3321,6 +3355,18 @@ ipcMain.handle('save-config', (_, newConfig: Partial) => { } saveConfig(config); + + if (config.persist_queue_on_restart === false) { + pendingQueueSnapshot = null; + if (queueSaveTimer) { + clearTimeout(queueSaveTimer); + queueSaveTimer = null; + } + clearQueueFileFromDisk(); + } else if (previousPersistQueueOnRestart === false) { + saveQueue(downloadQueue, true); + } + return config; }); @@ -3526,7 +3572,7 @@ ipcMain.handle('download-update', async () => { }); ipcMain.handle('install-update', () => { - autoUpdater.quitAndInstall(false, true); + autoUpdater.quitAndInstall(true, true); }); ipcMain.handle('open-external', async (_, url: string) => { diff --git a/src/renderer-globals.d.ts b/src/renderer-globals.d.ts index 4ef23e3..d71cb44 100644 --- a/src/renderer-globals.d.ts +++ b/src/renderer-globals.d.ts @@ -13,6 +13,7 @@ interface AppConfig { smart_queue_scheduler?: boolean; performance_mode?: 'stability' | 'balanced' | 'speed'; prevent_duplicate_downloads?: boolean; + persist_queue_on_restart?: boolean; metadata_cache_minutes?: number; [key: string]: unknown; } diff --git a/src/renderer-locale-de.ts b/src/renderer-locale-de.ts index 5e7ea05..7fdec11 100644 --- a/src/renderer-locale-de.ts +++ b/src/renderer-locale-de.ts @@ -46,6 +46,7 @@ const UI_TEXT_DE = { performanceModeSpeed: 'Max Geschwindigkeit', smartSchedulerLabel: 'Smart Queue Scheduler aktivieren', duplicatePreventionLabel: 'Duplikate in Queue verhindern', + persistQueueLabel: 'Queue zwischen App-Starts speichern', metadataCacheMinutesLabel: 'Metadata-Cache (Minuten)', filenameTemplatesTitle: 'Dateinamen-Templates', vodTemplateLabel: 'VOD-Template', @@ -208,11 +209,11 @@ const UI_TEXT_DE = { downloadNow: 'Jetzt herunterladen', downloadLabel: 'Download', ready: 'bereit zur Installation!', - installNow: 'Jetzt installieren', + installNow: 'Jetzt installieren & neu starten', modalAvailableTitle: 'Update verfugbar', modalAvailableMessage: 'Version {version} ist verfugbar. Jetzt herunterladen?', modalReadyTitle: 'Update bereit', - modalReadyMessage: 'Version {version} wurde heruntergeladen. Jetzt installieren?', + modalReadyMessage: 'Version {version} wurde heruntergeladen. Jetzt installieren und neu starten?', modalDismiss: 'Nein', modalDownloadConfirm: 'Ja, herunterladen', modalInstallConfirm: 'Ja, installieren', diff --git a/src/renderer-locale-en.ts b/src/renderer-locale-en.ts index 4a28ca5..91bcfeb 100644 --- a/src/renderer-locale-en.ts +++ b/src/renderer-locale-en.ts @@ -46,6 +46,7 @@ const UI_TEXT_EN = { performanceModeSpeed: 'Max Speed', smartSchedulerLabel: 'Enable smart queue scheduler', duplicatePreventionLabel: 'Prevent duplicate queue entries', + persistQueueLabel: 'Keep queue between app restarts', metadataCacheMinutesLabel: 'Metadata Cache (Minutes)', filenameTemplatesTitle: 'Filename Templates', vodTemplateLabel: 'VOD Template', @@ -208,11 +209,11 @@ const UI_TEXT_EN = { downloadNow: 'Download now', downloadLabel: 'Download', ready: 'ready to install!', - installNow: 'Install now', + installNow: 'Install now & restart', modalAvailableTitle: 'Update available', modalAvailableMessage: 'Version {version} is available. Download it now?', modalReadyTitle: 'Update ready', - modalReadyMessage: 'Version {version} has been downloaded. Install it now?', + modalReadyMessage: 'Version {version} has been downloaded. Install and restart now?', modalDismiss: 'No', modalDownloadConfirm: 'Yes, download', modalInstallConfirm: 'Yes, install', diff --git a/src/renderer-settings.ts b/src/renderer-settings.ts index bf77ab9..b680818 100644 --- a/src/renderer-settings.ts +++ b/src/renderer-settings.ts @@ -318,6 +318,7 @@ function collectDownloadSettingsPayload(): Partial { performance_mode: byId('performanceMode').value as 'stability' | 'balanced' | 'speed', smart_queue_scheduler: byId('smartSchedulerToggle').checked, prevent_duplicate_downloads: byId('duplicatePreventionToggle').checked, + persist_queue_on_restart: byId('persistQueueToggle').checked, metadata_cache_minutes: parseInt(byId('metadataCacheMinutes').value, 10) || 10 }; } @@ -358,6 +359,7 @@ function getSettingsFingerprint(payload: Partial): string { effective.performance_mode ?? 'balanced', effective.smart_queue_scheduler !== false, effective.prevent_duplicate_downloads !== false, + effective.persist_queue_on_restart !== false, effective.metadata_cache_minutes ?? 10, effective.filename_template_vod ?? '{title}.mp4', effective.filename_template_parts ?? '{date}_Part{part_padded}.mp4', @@ -373,6 +375,7 @@ function syncSettingsFormFromConfig(): void { byId('performanceMode').value = (config.performance_mode as string) || 'balanced'; byId('smartSchedulerToggle').checked = (config.smart_queue_scheduler as boolean) !== false; byId('duplicatePreventionToggle').checked = (config.prevent_duplicate_downloads as boolean) !== false; + byId('persistQueueToggle').checked = (config.persist_queue_on_restart as boolean) !== false; byId('metadataCacheMinutes').value = String((config.metadata_cache_minutes as number) || 10); byId('vodFilenameTemplate').value = (config.filename_template_vod as string) || '{title}.mp4'; byId('partsFilenameTemplate').value = (config.filename_template_parts as string) || '{date}_Part{part_padded}.mp4'; @@ -481,7 +484,8 @@ function initSettingsAutoSave(): void { 'downloadMode', 'performanceMode', 'smartSchedulerToggle', - 'duplicatePreventionToggle' + 'duplicatePreventionToggle', + 'persistQueueToggle' ] as const; const debouncedSaveIds = [ diff --git a/src/renderer-texts.ts b/src/renderer-texts.ts index 4aefdb3..e291396 100644 --- a/src/renderer-texts.ts +++ b/src/renderer-texts.ts @@ -88,6 +88,7 @@ function applyLanguageToStaticUI(): void { setText('performanceModeSpeed', UI_TEXT.static.performanceModeSpeed); setText('smartSchedulerLabel', UI_TEXT.static.smartSchedulerLabel); setText('duplicatePreventionLabel', UI_TEXT.static.duplicatePreventionLabel); + setText('persistQueueLabel', UI_TEXT.static.persistQueueLabel); setText('metadataCacheMinutesLabel', UI_TEXT.static.metadataCacheMinutesLabel); setText('filenameTemplatesTitle', UI_TEXT.static.filenameTemplatesTitle); setText('vodTemplateLabel', UI_TEXT.static.vodTemplateLabel); diff --git a/src/renderer-updates.ts b/src/renderer-updates.ts index 11ae1c5..f0ee708 100644 --- a/src/renderer-updates.ts +++ b/src/renderer-updates.ts @@ -366,8 +366,10 @@ function refreshUpdateUiTexts(): void { async function checkUpdateSilent(): Promise { try { + shouldOpenUpdateModalOnAvailable = true; await window.api.checkUpdate(); } catch { + shouldOpenUpdateModalOnAvailable = false; // ignore silent updater errors } }