Files
tv/mod/scrape.js
T
2026-08-26 19:24:53 -05:00

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 };