feat: auto-deduplicate queue against upload log on startup

When the app restarts with a restored queue, it now automatically
reads all fileuploader.log files and removes jobs that were already
successfully uploaded in a previous session.

This prevents re-uploading files that completed before a crash/close.
The dedup runs silently before the UI renders — no user action needed.

Also adds 'read-own-upload-log' IPC that reads all log variants
(base + daily logs) without file picker.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Administrator 2026-03-24 19:36:31 +01:00
parent a7b24ec363
commit bf7f35d06c
3 changed files with 69 additions and 0 deletions

39
main.js
View File

@ -845,6 +845,45 @@ ipcMain.handle('import-backup', async (_event, password) => {
return { ok: true, config: configStore.load() }; 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 () => { ipcMain.handle('import-upload-log', async () => {
const { canceled, filePaths } = await dialog.showOpenDialog(mainWindow, { const { canceled, filePaths } = await dialog.showOpenDialog(mainWindow, {
title: 'Upload-Log importieren', title: 'Upload-Log importieren',

View File

@ -39,6 +39,7 @@ contextBridge.exposeInMainWorld('api', {
runHealthCheck: (payload) => ipcRenderer.invoke('run-health-check', payload), runHealthCheck: (payload) => ipcRenderer.invoke('run-health-check', payload),
// Log import // Log import
readOwnUploadLog: () => ipcRenderer.invoke('read-own-upload-log'),
importUploadLog: () => ipcRenderer.invoke('import-upload-log'), importUploadLog: () => ipcRenderer.invoke('import-upload-log'),
// Clipboard // Clipboard

View File

@ -55,6 +55,7 @@ async function init() {
ensureAccountStatusEntries(); ensureAccountStatusEntries();
syncSelectedUploadHosters(); syncSelectedUploadHosters();
restoreQueueStateFromConfig(); restoreQueueStateFromConfig();
await _autoDeduplicateFromLog();
renderHosterSummary(); renderHosterSummary();
renderHosterModal(); renderHosterModal();
renderSettings(); renderSettings();
@ -3281,6 +3282,34 @@ function handleShutdownCountdown(data) {
}, 1000); }, 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 --- // --- Log import: remove already-uploaded file+hoster combos from queue ---
async function importUploadLog() { async function importUploadLog() {
const result = await window.api.importUploadLog(); const result = await window.api.importUploadLog();