57 lines
1.6 KiB
JavaScript
57 lines
1.6 KiB
JavaScript
function withTimeout(promise, ms, label) {
|
|
return new Promise((resolve, reject) => {
|
|
const timer = setTimeout(() => reject(new Error(`${label} timed out after ${ms}ms`)), ms);
|
|
promise.then(
|
|
(v) => {
|
|
clearTimeout(timer);
|
|
resolve(v);
|
|
},
|
|
(e) => {
|
|
clearTimeout(timer);
|
|
reject(e);
|
|
}
|
|
);
|
|
});
|
|
}
|
|
|
|
function toStreamArray(raw, providerName) {
|
|
let arr = Array.isArray(raw) ? raw : raw && typeof raw === 'object' && raw.url ? [raw] : [];
|
|
return arr
|
|
.filter((s) => s && typeof s === 'object' && typeof s.url === 'string' && s.url.length > 0)
|
|
.map((s) => ({ ...s, _provider: s._provider || providerName }));
|
|
}
|
|
|
|
async function scrapeAll(providers, media, timeoutMs) {
|
|
const settled = await Promise.allSettled(
|
|
providers.map(async (p) => {
|
|
const raw = await withTimeout(
|
|
Promise.resolve().then(() =>
|
|
p.getStreams(String(media.id), media.type, media.seasonId ?? null, media.episodeId ?? null)
|
|
),
|
|
timeoutMs,
|
|
p.name
|
|
);
|
|
return toStreamArray(raw, p.name);
|
|
})
|
|
);
|
|
|
|
const streams = [];
|
|
const errors = {};
|
|
settled.forEach((r, i) => {
|
|
if (r.status === 'fulfilled') streams.push(...r.value);
|
|
else errors[providers[i].name] = (r.reason && r.reason.message) || String(r.reason);
|
|
});
|
|
|
|
const seen = new Set();
|
|
const unique = [];
|
|
for (const s of streams) {
|
|
const key = `${s._provider}|${s.url}|${s.quality || ''}|${s.name || s.title || ''}`;
|
|
if (seen.has(key)) continue;
|
|
seen.add(key);
|
|
unique.push(s);
|
|
}
|
|
return { streams: unique, errors };
|
|
}
|
|
|
|
module.exports = { scrapeAll, withTimeout };
|