fix: prevent duplicate queue entries after removeFromQueueOnDone

- Track completed uploads in _completedUploadKeys Set so buildQueuePreview
  won't re-create jobs for files already uploaded this session
- Deduplicate queue on restore: when loading pendingQueue, keep only the
  job with the best status per file+hoster pair (removes existing dupes)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Administrator 2026-03-12 05:40:25 +01:00
parent 68e05503f6
commit fecf773caf

View File

@ -34,6 +34,7 @@ let _sessionTotalBytes = 0; // Total bytes ever added to queue this session
let _sessionUploadedBytes = 0; // Bytes fully uploaded this session (done jobs) let _sessionUploadedBytes = 0; // Bytes fully uploaded this session (done jobs)
let _sessionTrackedJobs = new Set(); // Job IDs already counted for totalBytes let _sessionTrackedJobs = new Set(); // Job IDs already counted for totalBytes
let _sessionDoneJobs = new Set(); // Job IDs already counted for uploadedBytes let _sessionDoneJobs = new Set(); // Job IDs already counted for uploadedBytes
let _completedUploadKeys = new Set(); // 'filepath|hoster' keys for done uploads (survives removeFromQueueOnDone)
let queueSortState = { key: 'filename', direction: 'asc' }; let queueSortState = { key: 'filename', direction: 'asc' };
// History state // History state
@ -371,7 +372,7 @@ function restoreQueueStateFromConfig() {
.map(file => ({ path: file.path, name: file.name || file.path.split(/[\\/]/).pop(), size: file.size || 0 })) .map(file => ({ path: file.path, name: file.name || file.path.split(/[\\/]/).pop(), size: file.size || 0 }))
: []; : [];
queueJobs = Array.isArray(pending.queueJobs) const rawJobs = Array.isArray(pending.queueJobs)
? pending.queueJobs ? pending.queueJobs
.filter(job => job && job.fileName && job.hoster) .filter(job => job && job.fileName && job.hoster)
.map(job => ({ .map(job => ({
@ -394,6 +395,18 @@ function restoreQueueStateFromConfig() {
progress: job.status === 'done' ? 1 : 0 progress: job.status === 'done' ? 1 : 0
})) }))
: []; : [];
// Deduplicate: keep the job with the best status for each file+hoster pair
const seen = new Map();
const statusPriority = { done: 0, uploading: 1, queued: 2, preview: 3, error: 4, aborted: 5, skipped: 6 };
for (const job of rawJobs) {
const key = `${job.file}|${job.hoster}`;
const existing = seen.get(key);
if (!existing || (statusPriority[job.status] ?? 9) < (statusPriority[existing.status] ?? 9)) {
seen.set(key, job);
}
}
queueJobs = Array.from(seen.values());
} }
function buildPersistedQueueState() { function buildPersistedQueueState() {
@ -647,7 +660,7 @@ function buildQueuePreview() {
for (const file of selectedFiles) { for (const file of selectedFiles) {
for (const hoster of hosters) { for (const hoster of hosters) {
const key = `${file.path}|${hoster}`; const key = `${file.path}|${hoster}`;
if (!existingKeys.has(key)) { if (!existingKeys.has(key) && !_completedUploadKeys.has(key)) {
const job = { const job = {
id: `preview-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`, id: `preview-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
file: file.path, fileName: file.name, hoster, file: file.path, fileName: file.name, hoster,
@ -1429,6 +1442,11 @@ function handleProgress(data) {
_sessionDoneJobs.add(job.id); _sessionDoneJobs.add(job.id);
} }
// Track completed uploads so they don't get re-queued after removal
if (job.status === 'done') {
_completedUploadKeys.add(`${job.file}|${job.hoster}`);
}
// Remove finished jobs from queue immediately if setting is enabled // Remove finished jobs from queue immediately if setting is enabled
if (job.status === 'done' && config.globalSettings && config.globalSettings.removeFromQueueOnDone) { if (job.status === 'done' && config.globalSettings && config.globalSettings.removeFromQueueOnDone) {
removeJobFromIndex(job); removeJobFromIndex(job);