fix: canonicalize Doodstream result links

Accept current Doodstream result domains while keeping returned download and embed URLs on doodstream.com. Add safe confirmation diagnostics and regression coverage.
This commit is contained in:
Sucukdeluxe
2026-08-12 02:43:36 +02:00
parent d3bac698c1
commit d1b36b0aaa
4 changed files with 77 additions and 7 deletions
+43 -3
View File
@@ -7,19 +7,52 @@ const SUPPORTED_HOSTERS = new Set([
]); ]);
const FILE_CODE_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_-]{2,127}$/; const FILE_CODE_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_-]{2,127}$/;
const HOSTER_RESULT_DOMAINS = {
'doodstream.com': ['doodstream.com', 'dood.to', 'dood.la', 'dood.so', 'dsvplay.com']
};
function isExpectedHostUrl(value, expectedHost) { function isExpectedHostUrl(value, expectedHost) {
if (typeof value !== 'string' || value.trim() === '') return false; if (typeof value !== 'string' || value.trim() === '') return false;
try { try {
const url = new URL(value); const url = new URL(value);
const hostname = url.hostname.toLowerCase(); const hostname = url.hostname.toLowerCase();
const acceptedDomains = HOSTER_RESULT_DOMAINS[expectedHost] || [expectedHost];
return (url.protocol === 'http:' || url.protocol === 'https:') return (url.protocol === 'http:' || url.protocol === 'https:')
&& (hostname === expectedHost || hostname.endsWith(`.${expectedHost}`)); && acceptedDomains.some(domain => hostname === domain || hostname.endsWith(`.${domain}`));
} catch { } catch {
return false; return false;
} }
} }
function getUrlHost(value) {
try {
return new URL(value).hostname.toLowerCase();
} catch {
return 'invalid';
}
}
function normalizeDoodstreamUrl(value) {
if (typeof value !== 'string' || value.trim() === '') return value;
try {
const url = new URL(value);
const hostname = url.hostname.toLowerCase();
if (hostname !== 'doodstream.com' && HOSTER_RESULT_DOMAINS['doodstream.com'].includes(hostname)) {
url.hostname = 'doodstream.com';
return url.toString();
}
} catch {}
return value;
}
function normalizeConfirmedResult(result, hoster) {
if (hoster !== 'doodstream.com') return result;
const downloadUrl = normalizeDoodstreamUrl(result.download_url);
const embedUrl = normalizeDoodstreamUrl(result.embed_url);
if (downloadUrl === result.download_url && embedUrl === result.embed_url) return result;
return { ...result, download_url: downloadUrl, embed_url: embedUrl };
}
function assertUploadConfirmation(result, hoster) { function assertUploadConfirmation(result, hoster) {
const expectedHost = typeof hoster === 'string' ? hoster.trim().toLowerCase() : ''; const expectedHost = typeof hoster === 'string' ? hoster.trim().toLowerCase() : '';
const fileCode = typeof result?.file_code === 'string' ? result.file_code.trim() : ''; const fileCode = typeof result?.file_code === 'string' ? result.file_code.trim() : '';
@@ -31,9 +64,16 @@ function assertUploadConfirmation(result, hoster) {
if (SUPPORTED_HOSTERS.has(expectedHost) if (SUPPORTED_HOSTERS.has(expectedHost)
&& FILE_CODE_PATTERN.test(fileCode) && FILE_CODE_PATTERN.test(fileCode)
&& urls.every(value => isExpectedHostUrl(value, expectedHost))) { && urls.every(value => isExpectedHostUrl(value, expectedHost))) {
return result; return normalizeConfirmedResult(result, expectedHost);
} }
throw new Error(`Upload zu ${hoster || 'unbekanntem Hoster'} wurde nicht bestätigt`); const error = new Error(`Upload zu ${hoster || 'unbekanntem Hoster'} wurde nicht bestätigt`);
error.diagnostic = {
payloadSnippet: JSON.stringify({
fileCodeLength: fileCode.length,
urlHosts: urls.map(getUrlHost)
})
};
throw error;
} }
module.exports = { assertUploadConfirmation }; module.exports = { assertUploadConfirmation };
+2 -2
View File
@@ -1,12 +1,12 @@
{ {
"name": "multi-hoster-uploader", "name": "multi-hoster-uploader",
"version": "2.1.13", "version": "2.1.14",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "multi-hoster-uploader", "name": "multi-hoster-uploader",
"version": "2.1.13", "version": "2.1.14",
"dependencies": { "dependencies": {
"chokidar": "^3.6.0", "chokidar": "^3.6.0",
"undici": "^7.29.0", "undici": "^7.29.0",
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "multi-hoster-uploader", "name": "multi-hoster-uploader",
"version": "2.1.13", "version": "2.1.14",
"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": {
+31 -1
View File
@@ -22,6 +22,32 @@ test('accepts upload URLs for every supported hoster and its subdomains', () =>
} }
}); });
test('accepts Doodstream result links on its current public domains', () => {
const result = {
file_code: 'DOODCODE1234',
download_url: 'https://dood.to/d/DOODCODE1234',
embed_url: 'https://dood.la/e/DOODCODE1234'
};
assert.deepEqual(assertUploadConfirmation(result, 'doodstream.com'), {
...result,
download_url: 'https://doodstream.com/d/DOODCODE1234',
embed_url: 'https://doodstream.com/e/DOODCODE1234'
});
});
test('accepts the Doodstream result domain returned by the current upload service', () => {
const result = {
file_code: 'DOODCODE1234',
download_url: 'https://dsvplay.com/d/DOODCODE1234',
embed_url: 'https://dsvplay.com/e/DOODCODE1234'
};
assert.deepEqual(assertUploadConfirmation(result, 'doodstream.com'), {
...result,
download_url: 'https://doodstream.com/d/DOODCODE1234',
embed_url: 'https://doodstream.com/e/DOODCODE1234'
});
});
test('rejects an upload URL from a different domain', () => { test('rejects an upload URL from a different domain', () => {
assert.throws( assert.throws(
() => assertUploadConfirmation({ file_code: 'abc123', download_url: 'https://attacker.invalid/file/abc123' }, 'voe.sx'), () => assertUploadConfirmation({ file_code: 'abc123', download_url: 'https://attacker.invalid/file/abc123' }, 'voe.sx'),
@@ -39,7 +65,11 @@ test('rejects a syntactically invalid file code despite a valid hoster URL', ()
test('rejects a valid hoster URL without a file code', () => { test('rejects a valid hoster URL without a file code', () => {
assert.throws( assert.throws(
() => assertUploadConfirmation({ download_url: 'https://byse.sx/d/abc123' }, 'byse.sx'), () => assertUploadConfirmation({ download_url: 'https://byse.sx/d/abc123' }, 'byse.sx'),
/Upload zu byse\.sx wurde nicht bestätigt/ (err) => {
assert.match(err.message, /Upload zu byse\.sx wurde nicht bestätigt/);
assert.equal(err.diagnostic.payloadSnippet, '{"fileCodeLength":0,"urlHosts":["byse.sx"]}');
return true;
}
); );
}); });