Compare commits

...

2 Commits

Author SHA1 Message Date
Administrator
6bd49d80b1 release: v2.6.0 2026-03-24 19:36:59 +01:00
Administrator
bf7f35d06c 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>
2026-03-24 19:36:31 +01:00
4 changed files with 70 additions and 1 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

@ -1,6 +1,6 @@
{ {
"name": "multi-hoster-uploader", "name": "multi-hoster-uploader",
"version": "2.5.9", "version": "2.6.0",
"description": "Upload files to doodstream, voe, vidmoly, byse simultaneously", "description": "Upload files to doodstream, voe, vidmoly, byse simultaneously",
"main": "main.js", "main": "main.js",
"scripts": { "scripts": {

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();