Fix duplicate VOE and Vidmoly recovery claims

Share account-scoped recovery claims with login uploader instances and serialize same-title recovery windows. Reserve direct and recovered file codes consistently, reject reused identities, and cover concurrent singleton recovery plus distinct-code and parallel-success cases.
This commit is contained in:
Sucukdeluxe
2026-08-13 22:30:27 +02:00
parent acb43d982e
commit c209abdbbc
5 changed files with 323 additions and 32 deletions
+27 -7
View File
@@ -15,8 +15,9 @@ const RESULT_POLL_DELAY_MS = 2000;
* Fallback when API-based upload fails or is unavailable.
*/
class VoeUploader {
constructor() {
constructor(recoveryClaim = null) {
this.cookies = new Map();
this.recoveryClaim = recoveryClaim;
}
_cookieHeader() {
@@ -449,15 +450,22 @@ class VoeUploader {
const withCode = files.filter(f => f && (f.file_code || f.slug));
const newFiles = withCode.filter(f => !baselineCodes.has(String(f.file_code || f.slug || '').trim()));
const matches = newFiles.filter(file => {
const title = this._normalizeTitle(file.title || file.name || '');
return expectedTitle && title === expectedTitle;
});
const matches = newFiles
.filter(file => {
const title = this._normalizeTitle(file.title || file.name || '');
return expectedTitle && title === expectedTitle;
})
.filter(file => {
const code = String(file.file_code || file.slug || '').trim();
return !this.recoveryClaim
|| typeof this.recoveryClaim.has !== 'function'
|| !this.recoveryClaim.has(code);
});
if (matches.length > 1) return null;
if (matches.length === 1) {
const code = matches[0].file_code || matches[0].slug;
return this._buildUrls(code);
return this._buildUrls(code, 'recovery-poll');
}
if (attempt < RESULT_POLL_ATTEMPTS - 1) {
@@ -477,9 +485,21 @@ class VoeUploader {
.replace(/[^a-z0-9]+/g, '');
}
_buildUrls(fileCode) {
_buildUrls(fileCode, phase = 'upload-result') {
const code = String(fileCode || '').trim();
if (!code) return null;
if (this.recoveryClaim
&& typeof this.recoveryClaim.reserve === 'function'
&& !this.recoveryClaim.reserve(code)) {
const error = createTransportError('VOE Upload-Ergebnis ist bereits einem anderen Upload zugeordnet', {
phase,
endpoint: BASE_URL,
retryable: true,
hosterTransient: true
});
error.remoteIdentityClaimed = true;
throw error;
}
return {
download_url: `${BASE_URL}/${code}`,
embed_url: `${BASE_URL}/e/${code}`,