diff --git a/main.js b/main.js index b8304e2..0f61f26 100644 --- a/main.js +++ b/main.js @@ -845,6 +845,45 @@ ipcMain.handle('import-backup', async (_event, password) => { return { ok: true, config: configStore.load() }; }); +ipcMain.handle('read-own-upload-log', () => { + // Read all log files (base + daily logs) and return parsed entries + const entries = []; + const basePath = getBaseLogFilePath(); + const dir = path.dirname(basePath); + const ext = path.extname(basePath); + const name = path.basename(basePath, ext); + + // Collect all matching log files (base + daily variants) + const logFiles = []; + try { + for (const file of fs.readdirSync(dir)) { + if (file.startsWith(name) && file.endsWith(ext)) { + logFiles.push(path.join(dir, file)); + } + } + } catch {} + if (logFiles.length === 0 && fs.existsSync(basePath)) { + logFiles.push(basePath); + } + + for (const logPath of logFiles) { + try { + const content = fs.readFileSync(logPath, 'utf-8'); + for (const line of content.split('\n')) { + const trimmed = line.trim(); + if (!trimmed || trimmed.startsWith('#')) continue; + const parts = trimmed.split('|'); + if (parts.length >= 5) { + const hoster = (parts[1] || '').trim(); + const fileName = (parts[4] || '').trim(); + if (hoster && fileName) entries.push({ hoster, fileName }); + } + } + } catch {} + } + return entries; +}); + ipcMain.handle('import-upload-log', async () => { const { canceled, filePaths } = await dialog.showOpenDialog(mainWindow, { title: 'Upload-Log importieren', diff --git a/preload.js b/preload.js index 705e0be..753d29b 100644 --- a/preload.js +++ b/preload.js @@ -39,6 +39,7 @@ contextBridge.exposeInMainWorld('api', { runHealthCheck: (payload) => ipcRenderer.invoke('run-health-check', payload), // Log import + readOwnUploadLog: () => ipcRenderer.invoke('read-own-upload-log'), importUploadLog: () => ipcRenderer.invoke('import-upload-log'), // Clipboard diff --git a/renderer/app.js b/renderer/app.js index 96a1038..6914a96 100644 --- a/renderer/app.js +++ b/renderer/app.js @@ -55,6 +55,7 @@ async function init() { ensureAccountStatusEntries(); syncSelectedUploadHosters(); restoreQueueStateFromConfig(); + await _autoDeduplicateFromLog(); renderHosterSummary(); renderHosterModal(); renderSettings(); @@ -3281,6 +3282,34 @@ function handleShutdownCountdown(data) { }, 1000); } +// --- Auto-deduplicate restored queue against own upload log on startup --- +async function _autoDeduplicateFromLog() { + if (queueJobs.length === 0) return; + try { + const entries = await window.api.readOwnUploadLog(); + if (!entries || entries.length === 0) return; + const logKeys = new Set(); + for (const entry of entries) { + logKeys.add(`${entry.fileName.toLowerCase()}|${entry.hoster.toLowerCase()}`); + } + let removed = 0; + queueJobs = queueJobs.filter(job => { + const key = `${job.fileName.toLowerCase()}|${job.hoster.toLowerCase()}`; + if (logKeys.has(key)) { + if (job.file && job.hoster) _completedUploadKeys.add(`${job.file}|${job.hoster}`); + removed++; + return false; + } + return true; + }); + if (removed > 0) { + rebuildJobIndex(); + syncSelectedFilesFromQueue(); + window.api.debugLog(`auto-dedup: removed ${removed} already-uploaded jobs from restored queue (${entries.length} log entries)`); + } + } catch {} +} + // --- Log import: remove already-uploaded file+hoster combos from queue --- async function importUploadLog() { const result = await window.api.importUploadLog();