fix: session-based counters and hoster cancel context menu
- Done/Error counters now use sessionFilesData (survives removeFromQueueOnDone) - Uploaded/Total bytes tracked via session accumulators (never decrease) - Errors no longer shown in Files list (stay in queue for retry) - Right-click context menu: "hoster abbrechen" cancels all jobs for a hoster Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
052bd940f1
commit
2c9726a33d
@ -20,6 +20,10 @@ let queueJobs = []; // { id, file, fileName, hoster, status, bytesUploaded, byte
|
||||
let _jobIndexById = new Map(); // id -> job (O(1) lookup)
|
||||
let _jobIndexByUploadId = new Map(); // uploadId -> job
|
||||
let selectedJobIds = new Set();
|
||||
let _sessionTotalBytes = 0; // Total bytes ever added to queue this session
|
||||
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 queueSortState = { key: 'filename', direction: 'asc' };
|
||||
|
||||
// History state
|
||||
@ -919,6 +923,24 @@ function showContextMenu(x, y) {
|
||||
const startItem = menu.querySelector('[data-action="start-selected"]');
|
||||
if (startItem) startItem.textContent = n > 1 ? `Ausgewählte starten (${n})` : 'Ausgewählte starten';
|
||||
|
||||
// Dynamic "cancel hoster" items
|
||||
const cancelSep = menu.querySelector('.ctx-hoster-cancel-sep');
|
||||
const cancelContainer = menu.querySelector('.ctx-hoster-cancel-items');
|
||||
const activeHosters = [...new Set(queueJobs.filter(j => j.status === 'uploading' || j.status === 'queued' || j.status === 'retrying' || j.status === 'getting-server' || j.status === 'preview').map(j => j.hoster))];
|
||||
cancelContainer.innerHTML = '';
|
||||
if (activeHosters.length > 0) {
|
||||
cancelSep.style.display = '';
|
||||
activeHosters.forEach(h => {
|
||||
const item = document.createElement('div');
|
||||
item.className = 'ctx-item ctx-item-danger';
|
||||
item.dataset.action = `cancel-hoster:${h}`;
|
||||
item.textContent = `${h} abbrechen`;
|
||||
cancelContainer.appendChild(item);
|
||||
});
|
||||
} else {
|
||||
cancelSep.style.display = 'none';
|
||||
}
|
||||
|
||||
menu.style.display = 'block';
|
||||
const menuX = Math.min(x, window.innerWidth - menu.offsetWidth - 5);
|
||||
menu.style.left = menuX + 'px';
|
||||
@ -1116,6 +1138,24 @@ async function handleContextAction(action) {
|
||||
} else if (action === 'always-on-top') {
|
||||
alwaysOnTopState = !alwaysOnTopState;
|
||||
await window.api.setAlwaysOnTop(alwaysOnTopState);
|
||||
} else if (action.startsWith('cancel-hoster:')) {
|
||||
const hoster = action.replace('cancel-hoster:', '');
|
||||
const jobIds = [];
|
||||
for (const job of queueJobs) {
|
||||
if (job.hoster === hoster && (job.status === 'uploading' || job.status === 'queued' || job.status === 'retrying' || job.status === 'getting-server' || job.status === 'preview')) {
|
||||
jobIds.push(job.id);
|
||||
// Mark queued/preview jobs as error immediately
|
||||
if (job.status === 'queued' || job.status === 'preview') {
|
||||
job.status = 'error';
|
||||
job.error = 'Hoster abgebrochen';
|
||||
}
|
||||
}
|
||||
}
|
||||
// Cancel active uploads via IPC
|
||||
if (jobIds.length > 0) await window.api.cancelSelectedJobs(jobIds);
|
||||
renderQueueTable();
|
||||
updateStatusBar();
|
||||
updateQueueActionButtons();
|
||||
} else if (action.startsWith('shutdown-')) {
|
||||
const mode = action.replace('shutdown-', '');
|
||||
await window.api.setShutdownAfterFinish(mode);
|
||||
@ -1302,6 +1342,11 @@ function handleProgress(data) {
|
||||
job.status = data.status;
|
||||
job.bytesUploaded = data.bytesUploaded || 0;
|
||||
job.bytesTotal = data.bytesTotal || job.bytesTotal;
|
||||
// Track session total bytes (survives removeFromQueueOnDone)
|
||||
if (job.bytesTotal > 0 && !_sessionTrackedJobs.has(job.id)) {
|
||||
_sessionTotalBytes += job.bytesTotal;
|
||||
_sessionTrackedJobs.add(job.id);
|
||||
}
|
||||
job.speedKbs = data.speedKbs || 0;
|
||||
job.elapsed = data.elapsed || 0;
|
||||
job.remaining = data.remaining || 0;
|
||||
@ -1317,6 +1362,12 @@ function handleProgress(data) {
|
||||
|
||||
maybeAddSessionFile(job);
|
||||
|
||||
// Track session uploaded bytes (survives removeFromQueueOnDone)
|
||||
if (job.status === 'done' && !_sessionDoneJobs.has(job.id)) {
|
||||
_sessionUploadedBytes += job.bytesTotal || 0;
|
||||
_sessionDoneJobs.add(job.id);
|
||||
}
|
||||
|
||||
// Remove finished jobs from queue immediately if setting is enabled
|
||||
if (job.status === 'done' && config.globalSettings && config.globalSettings.removeFromQueueOnDone) {
|
||||
removeJobFromIndex(job);
|
||||
@ -1541,21 +1592,6 @@ function maybeAddSessionFile(job) {
|
||||
}
|
||||
}
|
||||
|
||||
if (job.status === 'error') {
|
||||
const errorText = `[Fehler] ${job.error || ''}`;
|
||||
if (!sessionFilesData.some((row) => row.isError && row.filename === job.fileName && row.host === job.hoster && row.link === errorText)) {
|
||||
sessionFilesData.push({
|
||||
date: dt.text,
|
||||
dateTs: dt.ts,
|
||||
filename: job.fileName || '',
|
||||
host: job.hoster || '',
|
||||
link: errorText,
|
||||
isError: true,
|
||||
order: sessionFilesData.length
|
||||
});
|
||||
renderRecentUploadsPanel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function applySummaryResults(summary) {
|
||||
@ -1637,15 +1673,26 @@ function updateStatusBar() {
|
||||
|
||||
document.getElementById('sbState').textContent = stateText;
|
||||
document.getElementById('sbSpeed').textContent = formatSpeed(lastUploadStats.globalSpeedKbs || 0);
|
||||
const uploadedSize = Math.max(0, stats.totalSize - stats.remainingSize);
|
||||
document.getElementById('sbTotal').textContent = `${formatSize(uploadedSize)} / ${formatSize(stats.totalSize)}`;
|
||||
// Session-based bytes: survive removeFromQueueOnDone
|
||||
// Uploaded = done jobs (session) + in-progress bytes still in queue
|
||||
let inProgressBytes = 0;
|
||||
for (const job of queueJobs) {
|
||||
if (job.status === 'uploading' || job.status === 'getting-server' || job.status === 'retrying') {
|
||||
inProgressBytes += job.bytesUploaded || 0;
|
||||
}
|
||||
}
|
||||
const uploadedSize = _sessionUploadedBytes + inProgressBytes;
|
||||
const totalSize = Math.max(stats.totalSize, _sessionTotalBytes);
|
||||
document.getElementById('sbTotal').textContent = `${formatSize(uploadedSize)} / ${formatSize(totalSize)}`;
|
||||
document.getElementById('sbEta').textContent = `ETA ${etaSeconds > 0 ? formatTime(etaSeconds) : '--:--'}`;
|
||||
document.getElementById('sbConnections').textContent = `Connections: ${lastUploadStats.activeJobs || 0}`;
|
||||
document.getElementById('sbQueueCount').textContent = `Total: ${stats.total}`;
|
||||
document.getElementById('sbRemainingCount').textContent = `Remaining: ${stats.remaining}`;
|
||||
document.getElementById('sbInProgressCount').textContent = `In Progress: ${stats.inProgress}`;
|
||||
document.getElementById('sbDoneCount').textContent = `Done: ${stats.done}`;
|
||||
document.getElementById('sbErrorCount').textContent = `Error: ${stats.errors}`;
|
||||
const sessionDone = sessionFilesData.filter(r => !r.isError).length;
|
||||
const sessionErrors = sessionFilesData.filter(r => r.isError).length;
|
||||
document.getElementById('sbDoneCount').textContent = `Done: ${sessionDone}`;
|
||||
document.getElementById('sbErrorCount').textContent = `Error: ${sessionErrors}`;
|
||||
}
|
||||
|
||||
// --- Health Check ---
|
||||
|
||||
@ -265,6 +265,8 @@
|
||||
<div class="ctx-separator"></div>
|
||||
<div class="ctx-item" data-action="delete-selected">Entfernen</div>
|
||||
<div class="ctx-item" data-action="delete-all">Alle entfernen</div>
|
||||
<div class="ctx-separator ctx-hoster-cancel-sep" style="display:none"></div>
|
||||
<div class="ctx-hoster-cancel-items"></div>
|
||||
</div>
|
||||
|
||||
<div class="context-menu" id="recentContextMenu" style="display:none">
|
||||
|
||||
@ -605,6 +605,8 @@ body {
|
||||
position: relative;
|
||||
}
|
||||
.ctx-item:hover { background: rgba(102, 126, 234, 0.2); }
|
||||
.ctx-item-danger { color: var(--danger); }
|
||||
.ctx-item-danger:hover { background: rgba(231, 76, 60, 0.2); }
|
||||
.ctx-separator { height: 1px; margin: 4px 8px; background: var(--border); }
|
||||
.ctx-submenu { position: relative; }
|
||||
.ctx-submenu-items {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user