Compare commits

...

2 Commits

Author SHA1 Message Date
Administrator
23dd010a95 release: v2.0.6
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 06:02:46 +01:00
Administrator
0c945e21b8 fix: prevent double-click race condition in upload start
Move `uploading = true` guard to immediately after the check in both
startUpload() and startSelectedUpload(), before any async calls.
Previously the flag was set after await executeHealthCheck(), allowing
a fast double-click to bypass the guard and start duplicate batches.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 06:02:30 +01:00
2 changed files with 13 additions and 7 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "multi-hoster-uploader", "name": "multi-hoster-uploader",
"version": "2.0.5", "version": "2.0.6",
"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

@ -1246,13 +1246,15 @@ function getSelectedJobLinks() {
// --- Upload --- // --- Upload ---
async function startUpload() { async function startUpload() {
if (healthCheckRunning || uploading) return; if (healthCheckRunning || uploading) return;
uploading = true; // set immediately to prevent double-click race
updateQueueActionButtons();
const hosters = getSelectedHosters(); const hosters = getSelectedHosters();
if (hosters.length === 0) { alert('Bitte mindestens einen Hoster auswählen.'); return; } if (hosters.length === 0) { alert('Bitte mindestens einen Hoster auswählen.'); uploading = false; updateQueueActionButtons(); return; }
if (queueJobs.length === 0 && selectedFiles.length > 0) buildQueuePreview(); if (queueJobs.length === 0 && selectedFiles.length > 0) buildQueuePreview();
const jobsToStart = queueJobs.filter((job) => job.status === 'preview' || job.status === 'queued'); const jobsToStart = queueJobs.filter((job) => job.status === 'preview' || job.status === 'queued');
if (jobsToStart.length === 0) return; if (jobsToStart.length === 0) { uploading = false; updateQueueActionButtons(); return; }
// Auto health check — only check hosters that have jobs to start // Auto health check — only check hosters that have jobs to start
if (autoHealthCheckEnabled) { if (autoHealthCheckEnabled) {
@ -1265,10 +1267,12 @@ async function startUpload() {
const errors = rows.filter(r => r.status === 'error'); const errors = rows.filter(r => r.status === 'error');
if (errors.length > 0) { if (errors.length > 0) {
alert(`Auto-Check fehlgeschlagen:\n${errors.map(r => `${r.hoster}: ${r.message}`).join('\n')}\n\nUpload wurde nicht gestartet.`); alert(`Auto-Check fehlgeschlagen:\n${errors.map(r => `${r.hoster}: ${r.message}`).join('\n')}\n\nUpload wurde nicht gestartet.`);
uploading = false; updateQueueActionButtons();
return; return;
} }
} catch (err) { } catch (err) {
alert(`Auto-Check fehlgeschlagen: ${err.message}\nUpload wurde nicht gestartet.`); alert(`Auto-Check fehlgeschlagen: ${err.message}\nUpload wurde nicht gestartet.`);
uploading = false; updateQueueActionButtons();
return; return;
} finally { } finally {
healthCheckRunning = false; healthCheckRunning = false;
@ -1276,7 +1280,6 @@ async function startUpload() {
} }
} }
uploading = true;
queueJobs.forEach(j => { queueJobs.forEach(j => {
if (j.status === 'preview') j.status = 'queued'; if (j.status === 'preview') j.status = 'queued';
}); });
@ -1306,12 +1309,14 @@ async function startUpload() {
async function startSelectedUpload() { async function startSelectedUpload() {
if (healthCheckRunning || uploading) return; if (healthCheckRunning || uploading) return;
uploading = true; // set immediately to prevent double-click race
updateQueueActionButtons();
const hosters = getSelectedHosters(); const hosters = getSelectedHosters();
if (hosters.length === 0) { alert('Bitte mindestens einen Hoster auswählen.'); return; } if (hosters.length === 0) { alert('Bitte mindestens einen Hoster auswählen.'); uploading = false; updateQueueActionButtons(); return; }
const jobsToStart = queueJobs.filter((job) => selectedJobIds.has(job.id) && (job.status === 'preview' || job.status === 'queued')); const jobsToStart = queueJobs.filter((job) => selectedJobIds.has(job.id) && (job.status === 'preview' || job.status === 'queued'));
if (jobsToStart.length === 0) return; if (jobsToStart.length === 0) { uploading = false; updateQueueActionButtons(); return; }
// Auto health check — only check hosters that have jobs to start // Auto health check — only check hosters that have jobs to start
if (autoHealthCheckEnabled) { if (autoHealthCheckEnabled) {
@ -1324,10 +1329,12 @@ async function startSelectedUpload() {
const errors = rows.filter(r => r.status === 'error'); const errors = rows.filter(r => r.status === 'error');
if (errors.length > 0) { if (errors.length > 0) {
alert(`Auto-Check fehlgeschlagen:\n${errors.map(r => `${r.hoster}: ${r.message}`).join('\n')}\n\nUpload wurde nicht gestartet.`); alert(`Auto-Check fehlgeschlagen:\n${errors.map(r => `${r.hoster}: ${r.message}`).join('\n')}\n\nUpload wurde nicht gestartet.`);
uploading = false; updateQueueActionButtons();
return; return;
} }
} catch (err) { } catch (err) {
alert(`Auto-Check fehlgeschlagen: ${err.message}\nUpload wurde nicht gestartet.`); alert(`Auto-Check fehlgeschlagen: ${err.message}\nUpload wurde nicht gestartet.`);
uploading = false; updateQueueActionButtons();
return; return;
} finally { } finally {
healthCheckRunning = false; healthCheckRunning = false;
@ -1335,7 +1342,6 @@ async function startSelectedUpload() {
} }
} }
uploading = true;
jobsToStart.forEach(j => { jobsToStart.forEach(j => {
if (j.status === 'preview') j.status = 'queued'; if (j.status === 'preview') j.status = 'queued';
}); });