Close post-upload and admission race gaps

Treat fallback result fetch and body failures as uncertain Doodstream commits, and coordinate interval timing with slot acquisition so upload starts remain spaced without occupying another host's global slot during the wait.
This commit is contained in:
Sucukdeluxe
2026-08-14 00:07:27 +02:00
parent 28094414a8
commit 0845016efe
4 changed files with 117 additions and 13 deletions
+20 -9
View File
@@ -640,15 +640,26 @@ class DoodstreamUploader {
if (formAction) {
_debugLog(`Fallback: following form action ${safeEndpoint(formAction[1]) || 'unknown endpoint'}`);
const formData = new URLSearchParams(hiddenFields);
const followRes = await this._fetch(formAction[1], {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Referer': BASE_URL + '/'
},
body: formData.toString()
});
const followText = await followRes.text();
let followText;
try {
const followRes = await this._fetch(formAction[1], {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Referer': BASE_URL + '/'
},
body: formData.toString()
});
followText = await followRes.text();
} catch {
throw createTransportError('Doodstream Upload: Redirect-Antwort konnte nicht gelesen werden', {
phase: 'upload-result-submit',
endpoint: formAction[1],
retryable: true,
transientNetwork: true,
remoteCommitUncertain: true
});
}
_debugLog(`Fallback response: ${summarizeResponse(followText, '')}`);
const fallbackCode = this._findFilecodeInHtml(followText);
+8 -3
View File
@@ -1264,20 +1264,24 @@ class UploadManager extends EventEmitter {
const globalSemaphore = this._getGlobalSemaphore();
let hosterSlotAcquired = false;
let globalSlotAcquired = false;
try {
const acquireSlots = async () => {
await hosterSemaphore.acquire(signal);
hosterSlotAcquired = true;
if (globalSemaphore) {
await globalSemaphore.acquire(signal);
globalSlotAcquired = true;
}
};
try {
if (this._swapFailedAccount(task, jobId, path.basename(task.file))) {
retryAdmission = true;
return null;
}
const settings = this._getSettings(task.hoster);
if (settings.timeIntervalSec > 0) {
await this._waitForInterval(task.hoster, settings.timeIntervalSec * 1000, signal);
await this._waitForInterval(task.hoster, settings.timeIntervalSec * 1000, signal, acquireSlots);
} else {
await acquireSlots();
}
try {
return await this._executeUpload(task, progressCb, signal, throttle, fileProbe, context);
@@ -1618,7 +1622,7 @@ class UploadManager extends EventEmitter {
});
}
_waitForInterval(hoster, intervalMs, signal) {
_waitForInterval(hoster, intervalMs, signal, acquireSlots) {
// Serialize interval waits per hoster so concurrent jobs queue up properly
const prev = this.intervalLocks[hoster] || Promise.resolve();
const next = prev.then(async () => {
@@ -1628,6 +1632,7 @@ class UploadManager extends EventEmitter {
if (elapsed < intervalMs) {
await this._sleep(intervalMs - elapsed, signal);
}
await acquireSlots();
this.lastStartTime[hoster] = Date.now();
});
this.intervalLocks[hoster] = next.catch(() => {});