chore: cache successful results as long as we safely can
This commit is contained in:
parent
389461415d
commit
b69c9cde03
20 changed files with 283 additions and 161 deletions
|
|
@ -36,7 +36,11 @@ export class StreamController {
|
|||
|
||||
const handlers = this.handlers.filter(handler => handler.countryCodes.filter(countryCode => countryCode in ctx.config).length);
|
||||
|
||||
const streams = await this.streamResolver.resolve(ctx, handlers, type, id);
|
||||
const { streams, ttl } = await this.streamResolver.resolve(ctx, handlers, type, id);
|
||||
|
||||
if (ttl && process.env['NODE_ENV'] === 'production') {
|
||||
res.setHeader('Cache-Control', `max-age=${ttl / 1000}, public`);
|
||||
}
|
||||
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
res.send(JSON.stringify({ streams }));
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ export class DoodStream implements Extractor {
|
|||
url: new URL(`${baseUrl}${randomstring.generate(10)}?token=${token}&expiry=${Date.now()}`),
|
||||
label: this.label,
|
||||
sourceId: `${this.id}_${meta.countryCode.toLowerCase()}`,
|
||||
ttl: this.ttl,
|
||||
meta: {
|
||||
title,
|
||||
...meta,
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ export class Dropload implements Extractor {
|
|||
url: extractUrlFromPacked(html, [/sources:\[{file:"(.*?)"/]),
|
||||
label: this.label,
|
||||
sourceId: `${this.id}_${meta.countryCode.toLowerCase()}`,
|
||||
ttl: this.ttl,
|
||||
meta: {
|
||||
bytes: bytes.parse(sizeMatch[1] as string) as number,
|
||||
height: parseInt(heightMatch[1] as string) as number,
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ export class ExternalUrl implements Extractor {
|
|||
isExternal: true,
|
||||
label: `${url.host}`,
|
||||
sourceId: `${this.id}_${meta.countryCode.toLowerCase()}`,
|
||||
ttl: this.ttl,
|
||||
meta,
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import winston from 'winston';
|
||||
import { ExtractorRegistry } from './ExtractorRegistry';
|
||||
import { Context } from '../types';
|
||||
import { Context, UrlResult } from '../types';
|
||||
import { Fetcher } from '../utils';
|
||||
jest.mock('../utils/Fetcher');
|
||||
|
||||
|
|
@ -31,22 +31,21 @@ describe('ExtractorRegistry', () => {
|
|||
});
|
||||
|
||||
test('returns from memory cache if possible', async () => {
|
||||
const urlResult1 = await extractorRegistry.handle(ctx, new URL('https://dropload.io/lyo2h1snpe5c.html'), { countryCode: 'de' });
|
||||
const urlResult2 = await extractorRegistry.handle(ctx, new URL('https://dropload.io/lyo2h1snpe5c.html'), { countryCode: 'de' });
|
||||
const { ttl: ttl1, ...urlResultRest1 } = await extractorRegistry.handle(ctx, new URL('https://dropload.io/lyo2h1snpe5c.html'), { countryCode: 'de' }) as UrlResult;
|
||||
const { ttl: ttl2, ...urlResultRest2 } = await extractorRegistry.handle(ctx, new URL('https://dropload.io/lyo2h1snpe5c.html'), { countryCode: 'de' }) as UrlResult;
|
||||
|
||||
expect(urlResult2).toBe(urlResult1);
|
||||
expect(urlResultRest1).not.toBeUndefined();
|
||||
expect(urlResultRest2).toStrictEqual(urlResultRest1);
|
||||
|
||||
expect(ttl1).not.toBe(undefined);
|
||||
expect(ttl2).not.toBe(undefined);
|
||||
});
|
||||
|
||||
test('returns from memory cache if possible', async () => {
|
||||
const urlResult1 = await extractorRegistry.handle(ctx, new URL('https://dropload.io/lyo2h1snpe5c.html'), { countryCode: 'de' });
|
||||
const urlResult2 = await extractorRegistry.handle(ctx, new URL('https://dropload.io/lyo2h1snpe5c.html'), { countryCode: 'de' });
|
||||
test('ignores not found errors but caches them', async () => {
|
||||
const urlResult1 = await extractorRegistry.handle(ctx, new URL('https://dropload.io/asdfghijklmn.html'), { countryCode: 'de' });
|
||||
const urlResult2 = await extractorRegistry.handle(ctx, new URL('https://dropload.io/asdfghijklmn.html'), { countryCode: 'de' });
|
||||
|
||||
expect(urlResult2).toBe(urlResult1);
|
||||
});
|
||||
|
||||
test('ignores not found errors', async () => {
|
||||
const urlResult = await extractorRegistry.handle(ctx, new URL('https://dropload.io/asdfghijklmn.html'), { countryCode: 'de' });
|
||||
|
||||
expect(urlResult).toBeUndefined();
|
||||
expect(urlResult1).toBeUndefined();
|
||||
expect(urlResult2).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ export class ExtractorRegistry {
|
|||
readonly handle = async (ctx: Context, url: URL, meta: Meta): Promise<UrlResult | undefined> => {
|
||||
let urlResult = this.urlResultCache.get(url.href);
|
||||
if (this.urlResultCache.has(url.href)) {
|
||||
return urlResult;
|
||||
return urlResult ? { ...urlResult, ttl: this.urlResultCache.getRemainingTTL(url.href) } : undefined;
|
||||
}
|
||||
|
||||
const extractor = this.extractors.find(extractor => extractor.supports(ctx, url));
|
||||
|
|
@ -53,6 +53,7 @@ export class ExtractorRegistry {
|
|||
error,
|
||||
label: url.host,
|
||||
sourceId: `${extractor.id}`,
|
||||
ttl: extractor.ttl,
|
||||
meta,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ export class SuperVideo implements Extractor {
|
|||
url: extractUrlFromPacked(html, [/sources:\[{file:"(.*?)"/]),
|
||||
label: this.label,
|
||||
sourceId: `${this.id}_${meta.countryCode.toLowerCase()}`,
|
||||
ttl: this.ttl,
|
||||
meta: {
|
||||
bytes: bytes.parse(heightAndSizeMatch[2] as string) as number,
|
||||
height: parseInt(heightAndSizeMatch[1] as string) as number,
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ exports[`ExtractorRegistry return external URLs by default 1`] = `
|
|||
"countryCode": "de",
|
||||
},
|
||||
"sourceId": "external_de",
|
||||
"ttl": 3600000,
|
||||
"url": "https://mixdrop.ag/e/3nzwveprim63or6",
|
||||
}
|
||||
`;
|
||||
|
|
@ -21,6 +22,7 @@ exports[`ExtractorRegistry returns error result from extractor 1`] = `
|
|||
"countryCode": "en",
|
||||
},
|
||||
"sourceId": "external",
|
||||
"ttl": 3600000,
|
||||
"url": "https://some-url.test/",
|
||||
}
|
||||
`;
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ exports[`CineHDPlus handle imdb babylon 5 s2e3 (es) 1`] = `
|
|||
"title": "Babylon 5 2x3",
|
||||
},
|
||||
"sourceId": "supervideo_es",
|
||||
"ttl": 900000,
|
||||
"url": "https://hfs294.serversicuro.cc/hls/,dnzpealv5xg4a3gyvavh52jryms77x3wwakzvgy2ly3yfusnblczjnvvweua,.urlset/master.m3u8",
|
||||
},
|
||||
{
|
||||
|
|
@ -22,6 +23,7 @@ exports[`CineHDPlus handle imdb babylon 5 s2e3 (es) 1`] = `
|
|||
"title": "Babylon 5 2x3",
|
||||
},
|
||||
"sourceId": "dropload_es",
|
||||
"ttl": 900000,
|
||||
"url": "https://srv29.dropload.io/hls2/01/00295/957hny8mzrvh_h/master.m3u8?t=VpcZJYlXpp0FJb0Nh0yJR0nyI8-SKB2tGFwpcTx9v2w&s=1747213958&e=14400&f=1476559&i=0.0&sp=0&ii=130.61.236.204",
|
||||
},
|
||||
]
|
||||
|
|
@ -38,6 +40,7 @@ exports[`CineHDPlus handle imdb black mirror s2e3 (mx) 1`] = `
|
|||
"title": "Black Mirror 2x3",
|
||||
},
|
||||
"sourceId": "supervideo_mx",
|
||||
"ttl": 900000,
|
||||
"url": "https://hfs299.serversicuro.cc/hls/,dnzpff262hg4a3gyvcex5ojxt23vrieljzut7xhjydwidlbp4g7nqswkpfwq,.urlset/master.m3u8",
|
||||
},
|
||||
{
|
||||
|
|
@ -49,6 +52,7 @@ exports[`CineHDPlus handle imdb black mirror s2e3 (mx) 1`] = `
|
|||
"title": "Black Mirror 2x3",
|
||||
},
|
||||
"sourceId": "dropload_mx",
|
||||
"ttl": 900000,
|
||||
"url": "https://srv27.dropload.io/hls2/01/00075/t058ulwwkwn6_h/master.m3u8?t=GkktV2WgIMrvrgNLyjn6Bxeo2fArEAz6y9nSi_6dVAs&s=1747213564&e=14400&f=376684&i=0.0&sp=0&ii=130.61.236.204",
|
||||
},
|
||||
]
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ exports[`Eurostreaming handle imdb black mirror s2e4 1`] = `
|
|||
"title": "Black Mirror 2x4",
|
||||
},
|
||||
"sourceId": "supervideo_it",
|
||||
"ttl": 900000,
|
||||
"url": "https://hfs309.serversicuro.cc/hls/dnzpdeoe5xg4a3gyvaqx5ojpswtxjd5a22rklgah7,khdhascykjrixs7huqq,obtfascykj3vna7rk5a,.urlset/master.m3u8",
|
||||
},
|
||||
{
|
||||
|
|
@ -22,6 +23,7 @@ exports[`Eurostreaming handle imdb black mirror s2e4 1`] = `
|
|||
"title": "Black Mirror 2x4",
|
||||
},
|
||||
"sourceId": "supervideo_it",
|
||||
"ttl": 900000,
|
||||
"url": "https://hfs309.serversicuro.cc/hls/dnzpdeoe5xg4a3gyvaqx5ojpswtxjd5a22rklgah7,khdhascykjrixs7huqq,obtfascykj3vna7rk5a,.urlset/master.m3u8",
|
||||
},
|
||||
{
|
||||
|
|
@ -33,6 +35,7 @@ exports[`Eurostreaming handle imdb black mirror s2e4 1`] = `
|
|||
"title": "Black Mirror 2x4",
|
||||
},
|
||||
"sourceId": "dropload_it",
|
||||
"ttl": 900000,
|
||||
"url": "https://srv24.dropload.io/hls2/01/00289/978xyyxi3ldq_h/master.m3u8?t=unq9qxjYC-zgcmoXiSQ6rkMu7qHrSpN_pWOc95IPYwk&s=1747217151&e=14400&f=1448116&i=0.0&sp=0&ii=130.61.236.204",
|
||||
},
|
||||
]
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ exports[`Frembed handle imdb black mirror s4e2 1`] = `
|
|||
"title": "Black Mirror 4x2",
|
||||
},
|
||||
"sourceId": "external",
|
||||
"ttl": 3600000,
|
||||
"url": "https://ahvsh.com/e/83izf7qzwpae,https://netu.frembed.fun/e/0DFgfkcXOsDP,https://likessb.com/e/7yjgl1x56n08.html,https://ds2play.com/e/fzfvfq3ngig0",
|
||||
},
|
||||
{
|
||||
|
|
@ -23,6 +24,7 @@ exports[`Frembed handle imdb black mirror s4e2 1`] = `
|
|||
"Referer": "http://dood.to/",
|
||||
},
|
||||
"sourceId": "doodstream_fr",
|
||||
"ttl": 900000,
|
||||
"url": "https://ee317r.cloudatacdn.com/u5kj63yvxddlsdgge7qremqqka27irwiupc4k7o5cikbh7fdq4j4mwzraf7q/1y4sn4ndoi~mocked-random-string?token=fh3rdlhjvao7chgxndsi3ra7&expiry=639837296000",
|
||||
},
|
||||
{
|
||||
|
|
@ -33,6 +35,7 @@ exports[`Frembed handle imdb black mirror s4e2 1`] = `
|
|||
"title": "Black Mirror 4x2",
|
||||
},
|
||||
"sourceId": "external_fr",
|
||||
"ttl": 3600000,
|
||||
"url": "https://netu.fanstream.us/e/0DFgfkcXOsDP",
|
||||
},
|
||||
{
|
||||
|
|
@ -43,6 +46,7 @@ exports[`Frembed handle imdb black mirror s4e2 1`] = `
|
|||
"title": "Black Mirror 4x2",
|
||||
},
|
||||
"sourceId": "external_fr",
|
||||
"ttl": 3600000,
|
||||
"url": "https://johnalwayssame.com/e/cqy9oue7sv0g",
|
||||
},
|
||||
{
|
||||
|
|
@ -54,6 +58,7 @@ exports[`Frembed handle imdb black mirror s4e2 1`] = `
|
|||
"title": "Black Mirror 4x2",
|
||||
},
|
||||
"sourceId": "external",
|
||||
"ttl": 3600000,
|
||||
"url": "https://ds2play.com/e/fzfvfq3ngig0",
|
||||
},
|
||||
]
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ exports[`FrenchCloud handle imdb the devil's bath 1`] = `
|
|||
"title": "the devils bath enfant pour diable 2024 cam",
|
||||
},
|
||||
"sourceId": "supervideo_fr",
|
||||
"ttl": 900000,
|
||||
"url": "https://hfs292.serversicuro.cc/hls/,dnzpfwdj5dg4a3gyvbwx5lbvtu65tuwysrizwl7puv6u7dkpym3afmw5hnga,.urlset/master.m3u8",
|
||||
},
|
||||
{
|
||||
|
|
@ -22,6 +23,7 @@ exports[`FrenchCloud handle imdb the devil's bath 1`] = `
|
|||
"title": "the-devils-bath-un-enfant-pour-le-diable-2024-cam",
|
||||
},
|
||||
"sourceId": "dropload_fr",
|
||||
"ttl": 900000,
|
||||
"url": "https://srv28.dropload.io/hls2/01/00215/w4yit5wiia9y_h/master.m3u8?t=ujTsd4AYOdgaA6EhuMJDrC-xYzMVEbqrrd3qsoZ8iAY&s=1747054860&e=14400&f=1076096&i=0.0&sp=0&ii=130.61.236.204",
|
||||
},
|
||||
{
|
||||
|
|
@ -31,6 +33,7 @@ exports[`FrenchCloud handle imdb the devil's bath 1`] = `
|
|||
"countryCode": "fr",
|
||||
},
|
||||
"sourceId": "external_fr",
|
||||
"ttl": 3600000,
|
||||
"url": "https://mixdrop.ag/e/l7v73zqrfdj19z",
|
||||
},
|
||||
{
|
||||
|
|
@ -41,6 +44,7 @@ exports[`FrenchCloud handle imdb the devil's bath 1`] = `
|
|||
"countryCode": "fr",
|
||||
},
|
||||
"sourceId": "external",
|
||||
"ttl": 3600000,
|
||||
"url": "https://streamtape.com/e/gjA1OQ4klyHxgJ",
|
||||
},
|
||||
{
|
||||
|
|
@ -53,6 +57,7 @@ exports[`FrenchCloud handle imdb the devil's bath 1`] = `
|
|||
"Referer": "http://dood.to/",
|
||||
},
|
||||
"sourceId": "doodstream_fr",
|
||||
"ttl": 900000,
|
||||
"url": "https://ty1053vs.cloudatacdn.com/u5kjz2cvh7blsdgge4mmgoifjigorrf6o6mlfwqbndqkhkcvu43dcrk7wfga/6zwayqucuf~mocked-random-string?token=y0n5z6g48kft9apq8imziegv&expiry=639837296000",
|
||||
},
|
||||
]
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ exports[`KinoKiste handle imdb black mirror s2e4 1`] = `
|
|||
"title": "Black Mirror 2x4",
|
||||
},
|
||||
"sourceId": "supervideo_de",
|
||||
"ttl": 900000,
|
||||
"url": "https://hfs279.serversicuro.cc/hls/,dnzpervt3xg4a3gyvdix52ttsqpj5jgxs7keazdmhvru2uyin5g7f67a5u4a,.urlset/master.m3u8",
|
||||
},
|
||||
{
|
||||
|
|
@ -22,6 +23,7 @@ exports[`KinoKiste handle imdb black mirror s2e4 1`] = `
|
|||
"title": "Black Mirror 2x4",
|
||||
},
|
||||
"sourceId": "dropload_de",
|
||||
"ttl": 900000,
|
||||
"url": "https://srv34.dropload.io/hls2/01/00007/jvjwrkpijr0f_h/master.m3u8?t=9B5k5sYgDD2kS_549tQ4WYh1o74hO1QR9zzyXrVRqv4&s=1746903685&e=14400&f=35986&srv=srv27&i=0.0&sp=0&ii=130.61.236.204&p1=srv27&p2=srv27",
|
||||
},
|
||||
]
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ exports[`MeineCloud handle imdb the devil's bath 1`] = `
|
|||
"title": "des teufels bad 2024",
|
||||
},
|
||||
"sourceId": "supervideo_de",
|
||||
"ttl": 900000,
|
||||
"url": "https://hfs290.serversicuro.cc/hls/,dnzpfi3d27g4a3gyvbmh5klwtl65qh654hyjtgupd6p56thhyqu6ra3olbfa,.urlset/master.m3u8",
|
||||
},
|
||||
{
|
||||
|
|
@ -22,6 +23,7 @@ exports[`MeineCloud handle imdb the devil's bath 1`] = `
|
|||
"title": "des-teufels-bad-2024",
|
||||
},
|
||||
"sourceId": "dropload_de",
|
||||
"ttl": 900000,
|
||||
"url": "https://srv27.dropload.io/hls2/01/00197/lyo2h1snpe5c_h/master.m3u8?t=qTZsRGIMnUjXsQ4o2LRLlsT4tGG1AdJrRBRDGttNLPM&s=1746903744&e=14400&f=987607&i=0.0&sp=0&ii=130.61.236.204",
|
||||
},
|
||||
{
|
||||
|
|
@ -31,6 +33,7 @@ exports[`MeineCloud handle imdb the devil's bath 1`] = `
|
|||
"countryCode": "de",
|
||||
},
|
||||
"sourceId": "external_de",
|
||||
"ttl": 3600000,
|
||||
"url": "https://mixdrop.ag/e/3nzwveprim63or6",
|
||||
},
|
||||
{
|
||||
|
|
@ -43,6 +46,7 @@ exports[`MeineCloud handle imdb the devil's bath 1`] = `
|
|||
"Referer": "http://dood.to/",
|
||||
},
|
||||
"sourceId": "doodstream_de",
|
||||
"ttl": 900000,
|
||||
"url": "https://aa360cc.cloudatacdn.com/u5kjv4jxytd3sdgge5uogji5dg4huat2pxrm2qibdbm5rtpmbzgb5tornooa/k9wk0js17e~mocked-random-string?token=7uebebipnnhusa4xnyea1er4&expiry=639837296000",
|
||||
},
|
||||
]
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ exports[`MostraGuarda handle imdb the devil's bath 1`] = `
|
|||
"title": "the devils bath 2024 sub ita",
|
||||
},
|
||||
"sourceId": "supervideo_it",
|
||||
"ttl": 900000,
|
||||
"url": "https://hfs279.serversicuro.cc/hls/,dnzpejj427g4a3gyvbth53rxqewqgqqzp3wr6zwutysvud7zroar3cb75d3q,.urlset/master.m3u8",
|
||||
},
|
||||
{
|
||||
|
|
@ -22,6 +23,7 @@ exports[`MostraGuarda handle imdb the devil's bath 1`] = `
|
|||
"title": "the-devils-bath-2024-sd-sub-ita",
|
||||
},
|
||||
"sourceId": "dropload_it",
|
||||
"ttl": 900000,
|
||||
"url": "https://srv34.dropload.io/hls2/01/00200/xsr90y2ltdyx_h/master.m3u8?t=avFOZ-10hDxo6__iaoSUrq7o4yY3ldLw6XDlXxpXF8E&s=1747059001&e=14400&f=1004116&srv=srv24&i=0.0&sp=0&ii=130.61.236.204&p1=srv24&p2=srv24",
|
||||
},
|
||||
{
|
||||
|
|
@ -31,6 +33,7 @@ exports[`MostraGuarda handle imdb the devil's bath 1`] = `
|
|||
"countryCode": "it",
|
||||
},
|
||||
"sourceId": "external_it",
|
||||
"ttl": 3600000,
|
||||
"url": "https://mixdrop.ag/e/vk196d6xfzwwo1",
|
||||
},
|
||||
{
|
||||
|
|
@ -43,6 +46,7 @@ exports[`MostraGuarda handle imdb the devil's bath 1`] = `
|
|||
"Referer": "http://dood.to/",
|
||||
},
|
||||
"sourceId": "doodstream_it",
|
||||
"ttl": 900000,
|
||||
"url": "https://kk892as.cloudatacdn.com/u5kj7j7s27d3sdgge5woezkbi4pu672wktq3aqujw47rbpl3xog2gtqnw2xa/awn9rgotuu~mocked-random-string?token=aw2v2d8uhbrj2ky54d573ujk&expiry=639837296000",
|
||||
},
|
||||
]
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ exports[`VerHdLink handle titanic 1`] = `
|
|||
"title": "titanic 1997 [latino]",
|
||||
},
|
||||
"sourceId": "supervideo_mx",
|
||||
"ttl": 900000,
|
||||
"url": "https://hfs292.serversicuro.cc/hls/,dnzpejlw37g4a3gyvdzh5p3xttjqd3ccheao6qexhjbv4ecdpod7hxlh4a7a,.urlset/master.m3u8",
|
||||
},
|
||||
{
|
||||
|
|
@ -22,6 +23,7 @@ exports[`VerHdLink handle titanic 1`] = `
|
|||
"title": "titanic-1997-[latino]",
|
||||
},
|
||||
"sourceId": "dropload_mx",
|
||||
"ttl": 900000,
|
||||
"url": "https://srv22.dropload.io/hls2/01/00046/jjjx9j5joiz8_h/master.m3u8?t=_sOFVDjKUqBAK_gJtH7YVQnCm6nj7kr4df3PNsKeKAA&s=1747060128&e=14400&f=230703&i=0.0&sp=0&ii=130.61.236.204",
|
||||
},
|
||||
{
|
||||
|
|
@ -31,6 +33,7 @@ exports[`VerHdLink handle titanic 1`] = `
|
|||
"countryCode": "mx",
|
||||
},
|
||||
"sourceId": "external_mx",
|
||||
"ttl": 3600000,
|
||||
"url": "https://mixdrop.ag/e/vn0wx308fq984q",
|
||||
},
|
||||
{
|
||||
|
|
@ -41,6 +44,7 @@ exports[`VerHdLink handle titanic 1`] = `
|
|||
"countryCode": "mx",
|
||||
},
|
||||
"sourceId": "external",
|
||||
"ttl": 3600000,
|
||||
"url": "https://streamtape.com/e/Bjp2vjrdBxsK82",
|
||||
},
|
||||
{
|
||||
|
|
@ -52,6 +56,7 @@ exports[`VerHdLink handle titanic 1`] = `
|
|||
"title": "titanic 1997 2[castellano]",
|
||||
},
|
||||
"sourceId": "supervideo_es",
|
||||
"ttl": 900000,
|
||||
"url": "https://hfs295.serversicuro.cc/hls/,dnzpfv3w37g4a3gyvdzh5kjeyi4vtoyw4x4it5gxl4zzwiv4t3fxkvagrhea,.urlset/master.m3u8",
|
||||
},
|
||||
{
|
||||
|
|
@ -63,6 +68,7 @@ exports[`VerHdLink handle titanic 1`] = `
|
|||
"title": "titanic-1997-2[castellano]",
|
||||
},
|
||||
"sourceId": "dropload_es",
|
||||
"ttl": 900000,
|
||||
"url": "https://srv23.dropload.io/hls2/01/00046/ick4ti66vt6s_h/master.m3u8?t=OUbNiE0qRNUy69JP4AIj1bGcV5kaljyNrAUlCYWGVOs&s=1747060128&e=14400&f=230700&i=0.0&sp=0&ii=130.61.236.204",
|
||||
},
|
||||
{
|
||||
|
|
@ -72,6 +78,7 @@ exports[`VerHdLink handle titanic 1`] = `
|
|||
"countryCode": "es",
|
||||
},
|
||||
"sourceId": "external_es",
|
||||
"ttl": 3600000,
|
||||
"url": "https://mixdrop.ag/e/xokzmv61hrwe8o",
|
||||
},
|
||||
]
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ export interface UrlResult {
|
|||
error?: unknown;
|
||||
label: string;
|
||||
sourceId: string;
|
||||
ttl?: number;
|
||||
meta: Meta;
|
||||
requestHeaders?: Record<string, string>;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,11 +21,7 @@ describe('resolve', () => {
|
|||
test('returns info as stream if no handlers were configured', async () => {
|
||||
const streams = await streamResolver.resolve(ctx, [], 'movie', 'tt123456789');
|
||||
|
||||
expect(streams).toStrictEqual([{
|
||||
name: 'WebStreamr',
|
||||
title: '⚠️ No handlers found. Please re-configure the plugin.',
|
||||
ytId: 'E4WlUXrJgy4',
|
||||
}]);
|
||||
expect(streams).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('returns handler errors as stream', async () => {
|
||||
|
|
@ -33,23 +29,21 @@ describe('resolve', () => {
|
|||
|
||||
const streams = await streamResolver.resolve(ctx, [meineCloud], 'movie', 'tt123456789');
|
||||
|
||||
expect(streams).toStrictEqual([{
|
||||
name: 'WebStreamr',
|
||||
title: '🔗 MeineCloud\n❌ Request failed. Request-id: id.',
|
||||
ytId: 'E4WlUXrJgy4',
|
||||
}]);
|
||||
expect(streams).toMatchSnapshot();
|
||||
|
||||
fetcherSpy.mockRestore();
|
||||
});
|
||||
|
||||
test('returns empty array if no handler found anything', async () => {
|
||||
const streams = await streamResolver.resolve(ctx, [meineCloud, mostraGuarda], 'movie', 'tt12345678');
|
||||
expect(streams).toStrictEqual([]);
|
||||
|
||||
expect(streams).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('returns empty array if no handler supported the type', async () => {
|
||||
const streams = await streamResolver.resolve(ctx, [meineCloud, mostraGuarda], 'series', 'tt12345678:1:1');
|
||||
expect(streams).toStrictEqual([]);
|
||||
|
||||
expect(streams).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('returns sorted results', async () => {
|
||||
|
|
@ -130,6 +124,7 @@ describe('resolve', () => {
|
|||
};
|
||||
|
||||
const streams = await streamResolver.resolve(ctx, [mockHandler], 'movie', 'tt12345678');
|
||||
expect(streams).toStrictEqual([]);
|
||||
|
||||
expect(streams).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -7,6 +7,11 @@ import { Handler } from '../handler';
|
|||
import { BlockedError, NotFoundError, QueueIsFullError } from '../error';
|
||||
import { languageFromCountryCode } from './languageFromCountryCode';
|
||||
|
||||
interface ResolveResponse {
|
||||
streams: Stream[];
|
||||
ttl?: number;
|
||||
}
|
||||
|
||||
export class StreamResolver {
|
||||
private readonly logger: winston.Logger;
|
||||
|
||||
|
|
@ -14,17 +19,22 @@ export class StreamResolver {
|
|||
this.logger = logger;
|
||||
}
|
||||
|
||||
readonly resolve = async (ctx: Context, handlers: Handler[], type: string, id: string): Promise<Stream[]> => {
|
||||
readonly resolve = async (ctx: Context, handlers: Handler[], type: string, id: string): Promise<ResolveResponse> => {
|
||||
if (handlers.length === 0) {
|
||||
return [{
|
||||
name: 'WebStreamr',
|
||||
title: '⚠️ No handlers found. Please re-configure the plugin.',
|
||||
ytId: 'E4WlUXrJgy4',
|
||||
}];
|
||||
return {
|
||||
streams: [
|
||||
{
|
||||
name: 'WebStreamr',
|
||||
title: '⚠️ No handlers found. Please re-configure the plugin.',
|
||||
ytId: 'E4WlUXrJgy4',
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
const streams: Stream[] = [];
|
||||
|
||||
let handlerErrorOccurred = false;
|
||||
const urlResults: UrlResult[] = [];
|
||||
const handlerPromises = handlers.map(async (handler) => {
|
||||
if (!handler.contentTypes.includes(type)) {
|
||||
|
|
@ -41,6 +51,8 @@ export class StreamResolver {
|
|||
return;
|
||||
}
|
||||
|
||||
handlerErrorOccurred = true;
|
||||
|
||||
streams.push({
|
||||
name: process.env['MANIFEST_NAME'] || 'WebStreamr',
|
||||
title: [`🔗 ${handler.label}`, this.logErrorAndReturnNiceString(ctx, handler.id, error)].join('\n'),
|
||||
|
|
@ -83,7 +95,24 @@ export class StreamResolver {
|
|||
})),
|
||||
);
|
||||
|
||||
return streams;
|
||||
const ttl = !handlerErrorOccurred ? this.determineTtl(urlResults) : undefined;
|
||||
|
||||
return {
|
||||
streams,
|
||||
...(ttl && { ttl }),
|
||||
};
|
||||
};
|
||||
|
||||
private readonly determineTtl = (urlResults: UrlResult[]): number | undefined => {
|
||||
if (!urlResults.length) {
|
||||
return 900000; // 15m
|
||||
}
|
||||
|
||||
if (urlResults.some(urlResult => urlResult.ttl === undefined)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return Math.min(...urlResults.map(urlResult => urlResult.ttl as number));
|
||||
};
|
||||
|
||||
private readonly showExternalUrls = (ctx: Context): boolean => !('excludeExternalUrls' in ctx.config);
|
||||
|
|
|
|||
|
|
@ -1,178 +1,231 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`resolve adds error info 1`] = `
|
||||
[
|
||||
{
|
||||
"behaviorHints": {},
|
||||
"externalUrl": "https://example.com/",
|
||||
"name": "WebStreamr N/A ⚠️ external",
|
||||
"title": "🌐 German 🇩🇪
|
||||
{
|
||||
"streams": [
|
||||
{
|
||||
"behaviorHints": {},
|
||||
"externalUrl": "https://example.com/",
|
||||
"name": "WebStreamr N/A ⚠️ external",
|
||||
"title": "🌐 German 🇩🇪
|
||||
🔗 hoster.com
|
||||
⚠️ Request was blocked.",
|
||||
},
|
||||
{
|
||||
"behaviorHints": {},
|
||||
"externalUrl": "https://example2.com/",
|
||||
"name": "WebStreamr N/A ⚠️ external",
|
||||
"title": "🌐 German 🇩🇪
|
||||
},
|
||||
{
|
||||
"behaviorHints": {},
|
||||
"externalUrl": "https://example2.com/",
|
||||
"name": "WebStreamr N/A ⚠️ external",
|
||||
"title": "🌐 German 🇩🇪
|
||||
🔗 hoster.com
|
||||
❌ Request failed. Request-id: id.",
|
||||
},
|
||||
{
|
||||
"behaviorHints": {},
|
||||
"externalUrl": "https://example2.com/",
|
||||
"name": "WebStreamr N/A ⚠️ external",
|
||||
"title": "🌐 German 🇩🇪
|
||||
},
|
||||
{
|
||||
"behaviorHints": {},
|
||||
"externalUrl": "https://example2.com/",
|
||||
"name": "WebStreamr N/A ⚠️ external",
|
||||
"title": "🌐 German 🇩🇪
|
||||
🔗 hoster.com
|
||||
🐢 Request timed out.",
|
||||
},
|
||||
{
|
||||
"behaviorHints": {},
|
||||
"externalUrl": "https://example3.com/",
|
||||
"name": "WebStreamr N/A ⚠️ external",
|
||||
"title": "🌐 German 🇩🇪
|
||||
},
|
||||
{
|
||||
"behaviorHints": {},
|
||||
"externalUrl": "https://example3.com/",
|
||||
"name": "WebStreamr N/A ⚠️ external",
|
||||
"title": "🌐 German 🇩🇪
|
||||
🔗 hoster.com
|
||||
⏳ Request queue is full. Please try again later or consider self-hosting.",
|
||||
},
|
||||
]
|
||||
},
|
||||
],
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`resolve adds error info 2`] = `
|
||||
[
|
||||
{
|
||||
"behaviorHints": {},
|
||||
"name": "WebStreamr N/A",
|
||||
"title": "🌐 German 🇩🇪
|
||||
{
|
||||
"streams": [
|
||||
{
|
||||
"behaviorHints": {},
|
||||
"name": "WebStreamr N/A",
|
||||
"title": "🌐 German 🇩🇪
|
||||
🔗 hoster.com
|
||||
⚠️ Request was blocked.",
|
||||
"ytId": "E4WlUXrJgy4",
|
||||
},
|
||||
{
|
||||
"behaviorHints": {},
|
||||
"name": "WebStreamr N/A",
|
||||
"title": "🌐 German 🇩🇪
|
||||
"ytId": "E4WlUXrJgy4",
|
||||
},
|
||||
{
|
||||
"behaviorHints": {},
|
||||
"name": "WebStreamr N/A",
|
||||
"title": "🌐 German 🇩🇪
|
||||
🔗 hoster.com
|
||||
❌ Request failed. Request-id: id.",
|
||||
"ytId": "E4WlUXrJgy4",
|
||||
},
|
||||
{
|
||||
"behaviorHints": {},
|
||||
"name": "WebStreamr N/A",
|
||||
"title": "🌐 German 🇩🇪
|
||||
"ytId": "E4WlUXrJgy4",
|
||||
},
|
||||
{
|
||||
"behaviorHints": {},
|
||||
"name": "WebStreamr N/A",
|
||||
"title": "🌐 German 🇩🇪
|
||||
🔗 hoster.com
|
||||
🐢 Request timed out.",
|
||||
"ytId": "E4WlUXrJgy4",
|
||||
},
|
||||
{
|
||||
"behaviorHints": {},
|
||||
"name": "WebStreamr N/A",
|
||||
"title": "🌐 German 🇩🇪
|
||||
"ytId": "E4WlUXrJgy4",
|
||||
},
|
||||
{
|
||||
"behaviorHints": {},
|
||||
"name": "WebStreamr N/A",
|
||||
"title": "🌐 German 🇩🇪
|
||||
🔗 hoster.com
|
||||
⏳ Request queue is full. Please try again later or consider self-hosting.",
|
||||
"ytId": "E4WlUXrJgy4",
|
||||
},
|
||||
]
|
||||
"ytId": "E4WlUXrJgy4",
|
||||
},
|
||||
],
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`resolve ignores not found errors 1`] = `
|
||||
{
|
||||
"streams": [],
|
||||
"ttl": 900000,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`resolve returns empty array if no handler found anything 1`] = `
|
||||
{
|
||||
"streams": [],
|
||||
"ttl": 900000,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`resolve returns empty array if no handler supported the type 1`] = `
|
||||
{
|
||||
"streams": [],
|
||||
"ttl": 900000,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`resolve returns handler errors as stream 1`] = `
|
||||
{
|
||||
"streams": [
|
||||
{
|
||||
"name": "WebStreamr",
|
||||
"title": "🔗 MeineCloud
|
||||
❌ Request failed. Request-id: id.",
|
||||
"ytId": "E4WlUXrJgy4",
|
||||
},
|
||||
],
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`resolve returns info as stream if no handlers were configured 1`] = `
|
||||
{
|
||||
"streams": [
|
||||
{
|
||||
"name": "WebStreamr",
|
||||
"title": "⚠️ No handlers found. Please re-configure the plugin.",
|
||||
"ytId": "E4WlUXrJgy4",
|
||||
},
|
||||
],
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`resolve returns sorted results 1`] = `
|
||||
[
|
||||
{
|
||||
"behaviorHints": {
|
||||
"bingeGroup": "webstreamr-dropload_de",
|
||||
"videoSize": 1395864371,
|
||||
},
|
||||
"name": "WebStreamr 1080P",
|
||||
"title": "📂 des-teufels-bad-2024
|
||||
{
|
||||
"streams": [
|
||||
{
|
||||
"behaviorHints": {
|
||||
"bingeGroup": "webstreamr-dropload_de",
|
||||
"videoSize": 1395864371,
|
||||
},
|
||||
"name": "WebStreamr 1080P",
|
||||
"title": "📂 des-teufels-bad-2024
|
||||
💾 1.3 GB
|
||||
🌐 German 🇩🇪
|
||||
🔗 Dropload",
|
||||
"url": "https://srv27.dropload.io/hls2/01/00197/lyo2h1snpe5c_h/master.m3u8?t=qTZsRGIMnUjXsQ4o2LRLlsT4tGG1AdJrRBRDGttNLPM&s=1746903744&e=14400&f=987607&i=0.0&sp=0&ii=130.61.236.204",
|
||||
},
|
||||
{
|
||||
"behaviorHints": {
|
||||
"bingeGroup": "webstreamr-dropload_it",
|
||||
"videoSize": 1181116006,
|
||||
"url": "https://srv27.dropload.io/hls2/01/00197/lyo2h1snpe5c_h/master.m3u8?t=qTZsRGIMnUjXsQ4o2LRLlsT4tGG1AdJrRBRDGttNLPM&s=1746903744&e=14400&f=987607&i=0.0&sp=0&ii=130.61.236.204",
|
||||
},
|
||||
"name": "WebStreamr 720P",
|
||||
"title": "📂 the-devils-bath-2024-sd-sub-ita
|
||||
{
|
||||
"behaviorHints": {
|
||||
"bingeGroup": "webstreamr-dropload_it",
|
||||
"videoSize": 1181116006,
|
||||
},
|
||||
"name": "WebStreamr 720P",
|
||||
"title": "📂 the-devils-bath-2024-sd-sub-ita
|
||||
💾 1.1 GB
|
||||
🌐 Italian 🇮🇹
|
||||
🔗 Dropload",
|
||||
"url": "https://srv34.dropload.io/hls2/01/00200/xsr90y2ltdyx_h/master.m3u8?t=avFOZ-10hDxo6__iaoSUrq7o4yY3ldLw6XDlXxpXF8E&s=1747059001&e=14400&f=1004116&srv=srv24&i=0.0&sp=0&ii=130.61.236.204&p1=srv24&p2=srv24",
|
||||
},
|
||||
{
|
||||
"behaviorHints": {
|
||||
"bingeGroup": "webstreamr-supervideo_it",
|
||||
"videoSize": 1181116006,
|
||||
"url": "https://srv34.dropload.io/hls2/01/00200/xsr90y2ltdyx_h/master.m3u8?t=avFOZ-10hDxo6__iaoSUrq7o4yY3ldLw6XDlXxpXF8E&s=1747059001&e=14400&f=1004116&srv=srv24&i=0.0&sp=0&ii=130.61.236.204&p1=srv24&p2=srv24",
|
||||
},
|
||||
"name": "WebStreamr 720P",
|
||||
"title": "📂 the devils bath 2024 sub ita
|
||||
{
|
||||
"behaviorHints": {
|
||||
"bingeGroup": "webstreamr-supervideo_it",
|
||||
"videoSize": 1181116006,
|
||||
},
|
||||
"name": "WebStreamr 720P",
|
||||
"title": "📂 the devils bath 2024 sub ita
|
||||
💾 1.1 GB
|
||||
🌐 Italian 🇮🇹
|
||||
🔗 SuperVideo",
|
||||
"url": "https://hfs279.serversicuro.cc/hls/,dnzpejj427g4a3gyvbth53rxqewqgqqzp3wr6zwutysvud7zroar3cb75d3q,.urlset/master.m3u8",
|
||||
},
|
||||
{
|
||||
"behaviorHints": {
|
||||
"bingeGroup": "webstreamr-supervideo_de",
|
||||
"videoSize": 1073741824,
|
||||
"url": "https://hfs279.serversicuro.cc/hls/,dnzpejj427g4a3gyvbth53rxqewqgqqzp3wr6zwutysvud7zroar3cb75d3q,.urlset/master.m3u8",
|
||||
},
|
||||
"name": "WebStreamr 720P",
|
||||
"title": "📂 des teufels bad 2024
|
||||
{
|
||||
"behaviorHints": {
|
||||
"bingeGroup": "webstreamr-supervideo_de",
|
||||
"videoSize": 1073741824,
|
||||
},
|
||||
"name": "WebStreamr 720P",
|
||||
"title": "📂 des teufels bad 2024
|
||||
💾 1 GB
|
||||
🌐 German 🇩🇪
|
||||
🔗 SuperVideo",
|
||||
"url": "https://hfs290.serversicuro.cc/hls/,dnzpfi3d27g4a3gyvbmh5klwtl65qh654hyjtgupd6p56thhyqu6ra3olbfa,.urlset/master.m3u8",
|
||||
},
|
||||
{
|
||||
"behaviorHints": {
|
||||
"bingeGroup": "webstreamr-doodstream_de",
|
||||
"notWebReady": true,
|
||||
"proxyHeaders": {
|
||||
"request": {
|
||||
"Referer": "http://dood.to/",
|
||||
"url": "https://hfs290.serversicuro.cc/hls/,dnzpfi3d27g4a3gyvbmh5klwtl65qh654hyjtgupd6p56thhyqu6ra3olbfa,.urlset/master.m3u8",
|
||||
},
|
||||
{
|
||||
"behaviorHints": {
|
||||
"bingeGroup": "webstreamr-doodstream_de",
|
||||
"notWebReady": true,
|
||||
"proxyHeaders": {
|
||||
"request": {
|
||||
"Referer": "http://dood.to/",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"name": "WebStreamr N/A",
|
||||
"title": "📂 des-teufels-bad-2024
|
||||
"name": "WebStreamr N/A",
|
||||
"title": "📂 des-teufels-bad-2024
|
||||
🌐 German 🇩🇪
|
||||
🔗 DoodStream",
|
||||
"url": "https://aa360cc.cloudatacdn.com/u5kjv4jxytd3sdgge5uogji5dg4huat2pxrm2qibdbm5rtpmbzgb5tornooa/k9wk0js17e~mocked-random-string?token=7uebebipnnhusa4xnyea1er4&expiry=639837296000",
|
||||
},
|
||||
{
|
||||
"behaviorHints": {
|
||||
"bingeGroup": "webstreamr-doodstream_it",
|
||||
"notWebReady": true,
|
||||
"proxyHeaders": {
|
||||
"request": {
|
||||
"Referer": "http://dood.to/",
|
||||
"url": "https://aa360cc.cloudatacdn.com/u5kjv4jxytd3sdgge5uogji5dg4huat2pxrm2qibdbm5rtpmbzgb5tornooa/k9wk0js17e~mocked-random-string?token=7uebebipnnhusa4xnyea1er4&expiry=639837296000",
|
||||
},
|
||||
{
|
||||
"behaviorHints": {
|
||||
"bingeGroup": "webstreamr-doodstream_it",
|
||||
"notWebReady": true,
|
||||
"proxyHeaders": {
|
||||
"request": {
|
||||
"Referer": "http://dood.to/",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"name": "WebStreamr N/A",
|
||||
"title": "📂 the-devils-bath-2024-sd-sub-ita
|
||||
"name": "WebStreamr N/A",
|
||||
"title": "📂 the-devils-bath-2024-sd-sub-ita
|
||||
🌐 Italian 🇮🇹
|
||||
🔗 DoodStream",
|
||||
"url": "https://kk892as.cloudatacdn.com/u5kj7j7s27d3sdgge5woezkbi4pu672wktq3aqujw47rbpl3xog2gtqnw2xa/awn9rgotuu~mocked-random-string?token=aw2v2d8uhbrj2ky54d573ujk&expiry=639837296000",
|
||||
},
|
||||
{
|
||||
"behaviorHints": {
|
||||
"bingeGroup": "webstreamr-external_de",
|
||||
"url": "https://kk892as.cloudatacdn.com/u5kj7j7s27d3sdgge5woezkbi4pu672wktq3aqujw47rbpl3xog2gtqnw2xa/awn9rgotuu~mocked-random-string?token=aw2v2d8uhbrj2ky54d573ujk&expiry=639837296000",
|
||||
},
|
||||
"externalUrl": "https://mixdrop.ag/e/3nzwveprim63or6",
|
||||
"name": "WebStreamr N/A ⚠️ external",
|
||||
"title": "🌐 German 🇩🇪
|
||||
{
|
||||
"behaviorHints": {
|
||||
"bingeGroup": "webstreamr-external_de",
|
||||
},
|
||||
"externalUrl": "https://mixdrop.ag/e/3nzwveprim63or6",
|
||||
"name": "WebStreamr N/A ⚠️ external",
|
||||
"title": "🌐 German 🇩🇪
|
||||
🔗 mixdrop.ag",
|
||||
},
|
||||
{
|
||||
"behaviorHints": {
|
||||
"bingeGroup": "webstreamr-external_it",
|
||||
},
|
||||
"externalUrl": "https://mixdrop.ag/e/vk196d6xfzwwo1",
|
||||
"name": "WebStreamr N/A ⚠️ external",
|
||||
"title": "🌐 Italian 🇮🇹
|
||||
{
|
||||
"behaviorHints": {
|
||||
"bingeGroup": "webstreamr-external_it",
|
||||
},
|
||||
"externalUrl": "https://mixdrop.ag/e/vk196d6xfzwwo1",
|
||||
"name": "WebStreamr N/A ⚠️ external",
|
||||
"title": "🌐 Italian 🇮🇹
|
||||
🔗 mixdrop.ag",
|
||||
},
|
||||
]
|
||||
},
|
||||
],
|
||||
"ttl": 900000,
|
||||
}
|
||||
`;
|
||||
|
|
|
|||
Loading…
Reference in a new issue