feat: configure languages instead of handlers

just feels more natural and helps maintainence. handlers should be an
internal thing.
This commit is contained in:
WebStreamr 2025-05-17 23:48:34 +02:00
parent d368b3a9de
commit 5b389ada9e
No known key found for this signature in database
16 changed files with 57 additions and 29 deletions

View file

@ -29,13 +29,14 @@ export class StreamController {
const ctx: Context = {
id: res.getHeader('X-Request-ID') as string,
ip: req.ip as string,
config,
};
this.logger.info(`Search stream for type "${type}" and id "${id}"`, ctx);
const selectedHandlers = this.handlers.filter(handler => handler.id in config);
const handlers = this.handlers.filter(handler => handler.languages.filter(language => language in ctx.config).length);
const streams = await this.streamResolver.resolve(ctx, selectedHandlers, type, id);
const streams = await this.streamResolver.resolve(ctx, handlers, type, id);
res.setHeader('Content-Type', 'application/json');
res.send(JSON.stringify({ streams }));

View file

@ -10,7 +10,7 @@ const fetcher = new Fetcher();
const extractorRegistry = new ExtractorRegistry(logger, fetcher);
describe('ExtractorRegistry', () => {
const ctx: Context = { id: 'id', ip: '127.0.0.1' };
const ctx: Context = { id: 'id', ip: '127.0.0.1', config: { de: 'on' } };
test('returns undefined when no extractor can be found', async () => {
const urlResult = await extractorRegistry.handle(ctx, new URL('https://some-url.test'), 'en');

View file

@ -9,7 +9,7 @@ const logger = winston.createLogger({ transports: [new winston.transports.Consol
// @ts-expect-error No constructor args needed
const fetcher = new Fetcher();
const handler = new CineHDPlus(fetcher, new ExtractorRegistry(logger, fetcher));
const ctx: Context = { id: 'id', ip: '127.0.0.1' };
const ctx: Context = { id: 'id', ip: '127.0.0.1', config: { es: 'on', mx: 'on' } };
describe('CineHDPlus', () => {
test('does not handle non imdb series', async () => {

View file

@ -9,7 +9,7 @@ const logger = winston.createLogger({ transports: [new winston.transports.Consol
// @ts-expect-error No constructor args needed
const fetcher = new Fetcher();
const handler = new Eurostreaming(fetcher, new ExtractorRegistry(logger, fetcher));
const ctx: Context = { id: 'id', ip: '127.0.0.1' };
const ctx: Context = { id: 'id', ip: '127.0.0.1', config: { it: 'on' } };
describe('Eurostreaming', () => {
test('does not handle non imdb series', async () => {

View file

@ -9,7 +9,7 @@ const logger = winston.createLogger({ transports: [new winston.transports.Consol
// @ts-expect-error No constructor args needed
const fetcher = new Fetcher();
const handler = new Frembed(fetcher, new ExtractorRegistry(logger, fetcher));
const ctx: Context = { id: 'id', ip: '127.0.0.1' };
const ctx: Context = { id: 'id', ip: '127.0.0.1', config: { fr: 'on' } };
describe('Frembed', () => {
test('does not handle non imdb series', async () => {

View file

@ -9,7 +9,7 @@ const logger = winston.createLogger({ transports: [new winston.transports.Consol
// @ts-expect-error No constructor args needed
const fetcher = new Fetcher();
const handler = new FrenchCloud(fetcher, new ExtractorRegistry(logger, fetcher));
const ctx: Context = { id: 'id', ip: '127.0.0.1' };
const ctx: Context = { id: 'id', ip: '127.0.0.1', config: { fr: 'on' } };
describe('FrenchCloud', () => {
test('does not handle non imdb movies', async () => {

View file

@ -9,7 +9,7 @@ const logger = winston.createLogger({ transports: [new winston.transports.Consol
// @ts-expect-error No constructor args needed
const fetcher = new Fetcher();
const handler = new KinoKiste(fetcher, new ExtractorRegistry(logger, fetcher));
const ctx: Context = { id: 'id', ip: '127.0.0.1' };
const ctx: Context = { id: 'id', ip: '127.0.0.1', config: { de: 'on' } };
describe('KinoKiste', () => {
test('does not handle non imdb series', async () => {

View file

@ -9,7 +9,7 @@ const logger = winston.createLogger({ transports: [new winston.transports.Consol
// @ts-expect-error No constructor args needed
const fetcher = new Fetcher();
const handler = new MeineCloud(fetcher, new ExtractorRegistry(logger, fetcher));
const ctx: Context = { id: 'id', ip: '127.0.0.1' };
const ctx: Context = { id: 'id', ip: '127.0.0.1', config: { de: 'on' } };
describe('MeineCloud', () => {
test('does not handle non imdb movies', async () => {

View file

@ -9,7 +9,7 @@ const logger = winston.createLogger({ transports: [new winston.transports.Consol
// @ts-expect-error No constructor args needed
const fetcher = new Fetcher();
const handler = new MostraGuarda(fetcher, new ExtractorRegistry(logger, fetcher));
const ctx: Context = { id: 'id', ip: '127.0.0.1' };
const ctx: Context = { id: 'id', ip: '127.0.0.1', config: { it: 'on' } };
describe('MostraGuarda', () => {
test('does not handle non imdb movies', async () => {

View file

@ -9,7 +9,7 @@ const logger = winston.createLogger({ transports: [new winston.transports.Consol
// @ts-expect-error No constructor args needed
const fetcher = new Fetcher();
const handler = new VerHdLink(fetcher, new ExtractorRegistry(logger, fetcher));
const ctx: Context = { id: 'id', ip: '127.0.0.1' };
const ctx: Context = { id: 'id', ip: '127.0.0.1', config: { es: 'on', mx: 'on' } };
describe('VerHdLink', () => {
test('does not handle non imdb movies', async () => {

View file

@ -34,9 +34,9 @@ export class VerHdLink implements Handler {
$('._player-mirrors')
.map((_i, el) => {
let countryCode = undefined;
if ($(el).hasClass('latino')) {
if ($(el).hasClass('latino') && 'mx' in ctx.config) {
countryCode = 'mx';
} else if ($(el).hasClass('castellano')) {
} else if ($(el).hasClass('castellano') && 'es' in ctx.config) {
countryCode = 'es';
} else {
return [];

View file

@ -3,6 +3,7 @@ import { Manifest, ManifestConfig } from 'stremio-addon-sdk';
export interface Context {
id: string;
ip: string;
config: Config;
}
export type ManifestWithConfig = Manifest & { config: ManifestConfig[] };

View file

@ -9,7 +9,7 @@ const axiosMock = new AxiosMockAdapter(axios);
const fetcher = new Fetcher(axios, winston.createLogger({ transports: [new winston.transports.Console({ level: 'nope' })] }));
describe('fetch', () => {
const ctx: Context = { id: 'id', ip: '127.0.0.1' };
const ctx: Context = { id: 'id', ip: '127.0.0.1', config: { de: 'on' } };
test('text passes successful response through setting headers', async () => {
axiosMock.onGet().reply(200, 'some text');

View file

@ -8,7 +8,7 @@ jest.mock('../utils/Fetcher');
const logger = winston.createLogger({ transports: [new winston.transports.Console({ level: 'nope' })] });
const streamResolver = new StreamResolver(logger);
const ctx: Context = { id: 'id', ip: '127.0.0.1' };
const ctx: Context = { id: 'id', ip: '127.0.0.1', config: { de: 'on', it: 'on' } };
// @ts-expect-error No constructor args needed
const fetcher = new Fetcher();

View file

@ -1,6 +1,6 @@
import winston from 'winston';
import { buildManifest } from './manifest';
import { KinoKiste } from '../handler';
import { KinoKiste, MeineCloud, VerHdLink } from '../handler';
import { Fetcher } from './Fetcher';
import { ExtractorRegistry } from '../extractor';
@ -16,17 +16,35 @@ describe('buildManifest', () => {
});
test('has unchecked handler without a config', () => {
const manifest = buildManifest([new KinoKiste(fetcher, new ExtractorRegistry(logger, fetcher))], {});
const extractorRegistry = new ExtractorRegistry(logger, fetcher);
const handlers = [
new VerHdLink(fetcher, extractorRegistry),
new KinoKiste(fetcher, extractorRegistry),
new MeineCloud(fetcher, extractorRegistry),
];
expect(manifest.config).toHaveLength(1);
expect(manifest.config[0]?.default).toBeUndefined();
const manifest = buildManifest(handlers, {});
expect(manifest.config).toStrictEqual([
{ key: 'de', type: 'checkbox', title: '🇩🇪 (KinoKiste, MeineCloud)' },
{ key: 'es', type: 'checkbox', title: '🇪🇸 (VerHdLink)' },
{ key: 'mx', type: 'checkbox', title: '🇲🇽 (VerHdLink)' },
]);
});
test('has checked handler with appropriate config', () => {
const kinokiste = new KinoKiste(fetcher, new ExtractorRegistry(logger, fetcher));
const manifest = buildManifest([kinokiste], { [kinokiste.id]: 'on' });
const extractorRegistry = new ExtractorRegistry(logger, fetcher);
const handlers = [
new VerHdLink(fetcher, extractorRegistry),
new KinoKiste(fetcher, extractorRegistry),
new MeineCloud(fetcher, extractorRegistry),
];
const manifest = buildManifest(handlers, { de: 'on' });
expect(manifest.config).toHaveLength(1);
expect(manifest.config[0]?.default).toBe('checked');
expect(manifest.config).toStrictEqual([
{ key: 'de', type: 'checkbox', title: '🇩🇪 (KinoKiste, MeineCloud)', default: 'checked' },
{ key: 'es', type: 'checkbox', title: '🇪🇸 (VerHdLink)' },
{ key: 'mx', type: 'checkbox', title: '🇲🇽 (VerHdLink)' },
]);
});
});

View file

@ -25,14 +25,22 @@ export const buildManifest = (handlers: Handler[], config: Config): ManifestWith
config: [],
};
const languageHandlers: Record<string, Handler[]> = {};
handlers.forEach((handler) => {
manifest.config.push({
key: handler.id,
type: 'checkbox',
title: `${handler.languages.map(language => flag(language)).join(' ')} | ${handler.label} (${handler.contentTypes.map(contentType => contentType.replace('movie', 'movies')).join(', ')})`,
...(handler.id in config && { default: 'checked' }),
});
handler.languages.forEach(language => languageHandlers[language] = [...(languageHandlers[language] ?? []), handler]);
});
const sortedLanguageHandlers = Object.entries(languageHandlers)
.sort(([languageA], [languageB]) => languageA.localeCompare(languageB));
for (const [language, handlers] of sortedLanguageHandlers) {
manifest.config.push({
key: language,
type: 'checkbox',
title: `${flag(language)} (${handlers.map(handler => handler.label).join(', ')})`,
...(language in config && { default: 'checked' }),
});
}
return manifest;
};