🐛 fix(queue): deleted jobs reappear after restart

Three root causes fixed:
- handleProgress() re-created deleted jobs from stale progress callbacks
- Queue save was debounced (10s during uploads), deletion lost on app close
- Delete was blocked during active uploads (removed !uploading guard)

Now: deletions save immediately, deleted IDs are tracked to prevent
re-creation, and active uploads are cancelled when their jobs are deleted.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Administrator 2026-03-21 08:46:19 +01:00
parent 5569c690a1
commit 176cadc2dd

View File

@ -35,6 +35,7 @@ let _sessionUploadedBytes = 0; // Bytes fully uploaded this session (done jobs)
let _sessionTrackedJobs = new Set(); // Job IDs already counted for totalBytes
let _sessionDoneJobs = new Set(); // Job IDs already counted for uploadedBytes
let _completedUploadKeys = new Set(); // 'filepath|hoster' keys for done uploads (survives removeFromQueueOnDone)
let _deletedJobIds = new Set(); // IDs of jobs explicitly deleted by user (prevents re-creation from stale progress callbacks)
let queueSortState = { key: 'filename', direction: 'asc' };
// History state
@ -696,6 +697,9 @@ function indexJob(job) {
function removeJobFromIndex(job) {
_jobIndexById.delete(job.id);
if (job.uploadId) _jobIndexByUploadId.delete(job.uploadId);
// Track deletion so handleProgress() won't re-create this job from stale callbacks
_deletedJobIds.add(job.id);
if (job.uploadId) _deletedJobIds.add(job.uploadId);
}
// --- Queue Table Rendering (debounced with virtual scrolling) ---
@ -1150,7 +1154,14 @@ document.addEventListener('keydown', (e) => {
e.preventDefault();
if (selectedRecentIds.size > 0) {
deleteSelectedRecentFiles();
} else if (selectedJobIds.size > 0 && !uploading) {
} else if (selectedJobIds.size > 0) {
const deletedIds = [...selectedJobIds];
// Cancel active uploads for deleted jobs
const activeIds = deletedIds.filter(id => {
const j = _jobIndexById.get(id);
return j && (j.status === 'uploading' || j.status === 'queued' || j.status === 'retrying' || j.status === 'getting-server');
});
if (activeIds.length > 0) window.api.cancelSelectedJobs(activeIds);
queueJobs = queueJobs.filter(j => {
if (selectedJobIds.has(j.id)) { removeJobFromIndex(j); return false; }
return true;
@ -1160,7 +1171,7 @@ document.addEventListener('keydown', (e) => {
renderQueueTable();
if (queueJobs.length === 0) { selectedFiles = []; updateUploadView(); }
updateStatusBar();
persistQueueStateSoon();
persistQueueStateSoon(true);
}
}
}
@ -1184,6 +1195,12 @@ async function handleContextAction(action) {
} else if (action === 'retry-selected') {
retrySelectedJobs();
} else if (action === 'delete-selected') {
// Cancel active uploads for deleted jobs
const activeIds = [...selectedJobIds].filter(id => {
const j = _jobIndexById.get(id);
return j && (j.status === 'uploading' || j.status === 'queued' || j.status === 'retrying' || j.status === 'getting-server');
});
if (activeIds.length > 0) window.api.cancelSelectedJobs(activeIds);
queueJobs = queueJobs.filter(j => {
if (selectedJobIds.has(j.id)) {
removeJobFromIndex(j);
@ -1196,10 +1213,15 @@ async function handleContextAction(action) {
renderQueueTable();
if (queueJobs.length === 0) { selectedFiles = []; updateUploadView(); }
updateStatusBar();
persistQueueStateSoon();
persistQueueStateSoon(true);
} else if (action === 'copy-all-links') {
copyAllLinks();
} else if (action === 'delete-all') {
// Cancel all active uploads
const activeIds = queueJobs
.filter(j => j.status === 'uploading' || j.status === 'queued' || j.status === 'retrying' || j.status === 'getting-server')
.map(j => j.id);
if (activeIds.length > 0) window.api.cancelSelectedJobs(activeIds);
queueJobs.forEach(j => removeJobFromIndex(j));
queueJobs = [];
selectedJobIds.clear();
@ -1208,7 +1230,7 @@ async function handleContextAction(action) {
renderQueueTable();
updateUploadView();
updateStatusBar();
persistQueueStateSoon();
persistQueueStateSoon(true);
} else if (action === 'always-on-top') {
alwaysOnTopState = !alwaysOnTopState;
await window.api.setAlwaysOnTop(alwaysOnTopState);
@ -1406,6 +1428,10 @@ function handleProgress(data) {
}
}
if (!job) {
// Don't re-create jobs that were explicitly deleted by the user
if ((data.jobId && _deletedJobIds.has(data.jobId)) || (data.uploadId && _deletedJobIds.has(data.uploadId))) {
return;
}
job = {
id: data.jobId || data.uploadId || `job-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
uploadId: data.uploadId,