refactor: simplify id usage and imdb <-> tmdb transformations
This commit is contained in:
parent
8724e04860
commit
fbae3924e5
24 changed files with 107 additions and 168 deletions
|
|
@ -2,7 +2,7 @@ import { Request, Response, Router } from 'express';
|
|||
import winston from 'winston';
|
||||
import { Handler } from '../handler';
|
||||
import { Config, Context } from '../types';
|
||||
import { envIsProd, StreamResolver } from '../utils';
|
||||
import { envIsProd, ImdbId, StreamResolver } from '../utils';
|
||||
import { ContentType } from 'stremio-addon-sdk';
|
||||
|
||||
export class StreamController {
|
||||
|
|
@ -37,7 +37,7 @@ export class StreamController {
|
|||
|
||||
const handlers = this.handlers.filter(handler => handler.countryCodes.filter(countryCode => countryCode in ctx.config).length);
|
||||
|
||||
const { streams, ttl } = await this.streamResolver.resolve(ctx, handlers, type, id);
|
||||
const { streams, ttl } = await this.streamResolver.resolve(ctx, handlers, type, ImdbId.fromString(id));
|
||||
|
||||
if (ttl && envIsProd()) {
|
||||
res.setHeader('Cache-Control', `max-age=${ttl / 1000}, public`);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import winston from 'winston';
|
||||
import { CineHDPlus } from './CineHDPlus';
|
||||
import { ExtractorRegistry } from '../extractor';
|
||||
import { Fetcher } from '../utils';
|
||||
import { Fetcher, ImdbId } from '../utils';
|
||||
import { Context } from '../types';
|
||||
jest.mock('../utils/Fetcher');
|
||||
|
||||
|
|
@ -12,31 +12,26 @@ const handler = new CineHDPlus(fetcher, new ExtractorRegistry(logger, fetcher));
|
|||
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 () => {
|
||||
const streams = await handler.handle(ctx, 'series', 'kitsu:123');
|
||||
expect(streams).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('handles non-existent series gracefully', async () => {
|
||||
const streams = await handler.handle(ctx, 'series', 'tt12345678:1:1');
|
||||
const streams = await handler.handle(ctx, 'series', new ImdbId('tt12345678', 1, 1));
|
||||
expect(streams).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('handle imdb black mirror s2e3 (mx)', async () => {
|
||||
const streams = (await handler.handle(ctx, 'series', 'tt2085059:2:3')).filter(stream => stream !== undefined);
|
||||
const streams = (await handler.handle(ctx, 'series', new ImdbId('tt2085059', 2, 3))).filter(stream => stream !== undefined);
|
||||
expect(streams).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('handle imdb babylon 5 s2e3 (es)', async () => {
|
||||
const streams = (await handler.handle(ctx, 'series', 'tt0105946:2:3')).filter(stream => stream !== undefined);
|
||||
const streams = (await handler.handle(ctx, 'series', new ImdbId('tt0105946', 2, 3))).filter(stream => stream !== undefined);
|
||||
expect(streams).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('does not return mx results for es and vice-versa', async () => {
|
||||
const streamsEs = (await handler.handle({ ...ctx, ...{ config: { es: 'on' } } }, 'series', 'tt2085059:2:3')).filter(stream => stream !== undefined);
|
||||
const streamsEs = (await handler.handle({ ...ctx, ...{ config: { es: 'on' } } }, 'series', new ImdbId('tt2085059', 2, 3))).filter(stream => stream !== undefined);
|
||||
expect(streamsEs).toHaveLength(0);
|
||||
|
||||
const streamsMx = (await handler.handle({ ...ctx, ...{ config: { mx: 'on' } } }, 'series', 'tt0105946:2:3')).filter(stream => stream !== undefined);
|
||||
const streamsMx = (await handler.handle({ ...ctx, ...{ config: { mx: 'on' } } }, 'series', new ImdbId('tt0105946', 2, 3))).filter(stream => stream !== undefined);
|
||||
expect(streamsMx).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { ContentType } from 'stremio-addon-sdk';
|
||||
import * as cheerio from 'cheerio';
|
||||
import { Handler } from './types';
|
||||
import { Fetcher, ImdbId } from '../utils';
|
||||
import { Fetcher, getImdbId, Id, ImdbId } from '../utils';
|
||||
import { ExtractorRegistry } from '../extractor';
|
||||
import { Context, CountryCode } from '../types';
|
||||
|
||||
|
|
@ -22,12 +22,8 @@ export class CineHDPlus implements Handler {
|
|||
this.extractorRegistry = extractorRegistry;
|
||||
}
|
||||
|
||||
readonly handle = async (ctx: Context, _type: string, id: string) => {
|
||||
if (!id.startsWith('tt')) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const imdbId = ImdbId.fromString(id);
|
||||
readonly handle = async (ctx: Context, _type: string, id: Id) => {
|
||||
const imdbId = await getImdbId(ctx, this.fetcher, id);
|
||||
|
||||
const seriesPageUrl = await this.fetchSeriesPageUrl(ctx, imdbId);
|
||||
if (!seriesPageUrl) {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import winston from 'winston';
|
||||
import { Eurostreaming } from './Eurostreaming';
|
||||
import { ExtractorRegistry } from '../extractor';
|
||||
import { Fetcher } from '../utils';
|
||||
import { Fetcher, ImdbId } from '../utils';
|
||||
import { Context } from '../types';
|
||||
jest.mock('../utils/Fetcher');
|
||||
|
||||
|
|
@ -12,18 +12,13 @@ const handler = new Eurostreaming(fetcher, new ExtractorRegistry(logger, fetcher
|
|||
const ctx: Context = { id: 'id', ip: '127.0.0.1', config: { it: 'on' } };
|
||||
|
||||
describe('Eurostreaming', () => {
|
||||
test('does not handle non imdb series', async () => {
|
||||
const streams = await handler.handle(ctx, 'series', 'kitsu:123');
|
||||
expect(streams).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('handles non-existent series gracefully', async () => {
|
||||
const streams = await handler.handle(ctx, 'series', 'tt12345678:1:1');
|
||||
const streams = await handler.handle(ctx, 'series', new ImdbId('tt12345678', 1, 1));
|
||||
expect(streams).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('handle imdb black mirror s2e4', async () => {
|
||||
const streams = (await handler.handle(ctx, 'series', 'tt2085059:2:4')).filter(stream => stream !== undefined);
|
||||
const streams = (await handler.handle(ctx, 'series', new ImdbId('tt2085059', 2, 4))).filter(stream => stream !== undefined);
|
||||
expect(streams).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { ContentType } from 'stremio-addon-sdk';
|
||||
import * as cheerio from 'cheerio';
|
||||
import { Handler } from './types';
|
||||
import { ImdbId, Fetcher } from '../utils';
|
||||
import { ImdbId, Fetcher, Id, getImdbId } from '../utils';
|
||||
import { ExtractorRegistry } from '../extractor';
|
||||
import { Context, CountryCode } from '../types';
|
||||
|
||||
|
|
@ -22,12 +22,8 @@ export class Eurostreaming implements Handler {
|
|||
this.extractorRegistry = extractorRegistry;
|
||||
}
|
||||
|
||||
readonly handle = async (ctx: Context, _type: string, id: string) => {
|
||||
if (!id.startsWith('tt')) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const imdbId = ImdbId.fromString(id);
|
||||
readonly handle = async (ctx: Context, _type: string, id: Id) => {
|
||||
const imdbId = await getImdbId(ctx, this.fetcher, id);
|
||||
|
||||
const seriesPageUrl = await this.fetchSeriesPageUrl(ctx, imdbId);
|
||||
if (!seriesPageUrl) {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import winston from 'winston';
|
||||
import { Frembed } from './Frembed';
|
||||
import { ExtractorRegistry } from '../extractor';
|
||||
import { Fetcher } from '../utils';
|
||||
import { Fetcher, ImdbId, TmdbId } from '../utils';
|
||||
import { Context } from '../types';
|
||||
jest.mock('../utils/Fetcher');
|
||||
|
||||
|
|
@ -12,18 +12,13 @@ const handler = new Frembed(fetcher, new ExtractorRegistry(logger, fetcher));
|
|||
const ctx: Context = { id: 'id', ip: '127.0.0.1', config: { fr: 'on' } };
|
||||
|
||||
describe('Frembed', () => {
|
||||
test('does not handle non imdb and tmdb series', async () => {
|
||||
const streams = await handler.handle(ctx, 'series', 'kitsu:123');
|
||||
expect(streams).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('handle imdb black mirror s4e2', async () => {
|
||||
const streams = (await handler.handle(ctx, 'series', 'tt2085059:4:2')).filter(stream => stream !== undefined);
|
||||
const streams = (await handler.handle(ctx, 'series', new ImdbId('tt2085059', 4, 2))).filter(stream => stream !== undefined);
|
||||
expect(streams).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('handle tmdb black mirror s4e2', async () => {
|
||||
const streams = (await handler.handle(ctx, 'series', '42009:4:2')).filter(stream => stream !== undefined);
|
||||
const streams = (await handler.handle(ctx, 'series', new TmdbId(42009, 4, 2))).filter(stream => stream !== undefined);
|
||||
|
||||
const streamsWithoutTtl = streams.map((stream) => {
|
||||
delete stream.ttl;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { ContentType } from 'stremio-addon-sdk';
|
||||
import { Handler } from './types';
|
||||
import { Fetcher, getTmdbIdFromImdbId, ImdbId, TmdbId } from '../utils';
|
||||
import { Fetcher, getTmdbId, Id } from '../utils';
|
||||
import { ExtractorRegistry } from '../extractor';
|
||||
import { Context, CountryCode } from '../types';
|
||||
|
||||
|
|
@ -21,15 +21,8 @@ export class Frembed implements Handler {
|
|||
this.extractorRegistry = extractorRegistry;
|
||||
}
|
||||
|
||||
readonly handle = async (ctx: Context, _type: string, id: string) => {
|
||||
let tmdbId: TmdbId;
|
||||
if (id.startsWith('tt')) {
|
||||
tmdbId = await getTmdbIdFromImdbId(ctx, this.fetcher, ImdbId.fromString(id));
|
||||
} else if (/^\d+:/.test(id)) {
|
||||
tmdbId = TmdbId.fromString(id);
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
readonly handle = async (ctx: Context, _type: string, id: Id) => {
|
||||
const tmdbId = await getTmdbId(ctx, this.fetcher, id);
|
||||
|
||||
const apiUrl = new URL(`https://frembed.space/api/series?id=${tmdbId.id}&sa=${tmdbId.series}&epi=${tmdbId.episode}&idType=tmdb`);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import winston from 'winston';
|
||||
import { FrenchCloud } from './FrenchCloud';
|
||||
import { Fetcher } from '../utils';
|
||||
import { Fetcher, ImdbId } from '../utils';
|
||||
import { ExtractorRegistry } from '../extractor';
|
||||
import { Context } from '../types';
|
||||
jest.mock('../utils/Fetcher');
|
||||
|
|
@ -12,18 +12,13 @@ const handler = new FrenchCloud(fetcher, new ExtractorRegistry(logger, fetcher))
|
|||
const ctx: Context = { id: 'id', ip: '127.0.0.1', config: { fr: 'on' } };
|
||||
|
||||
describe('FrenchCloud', () => {
|
||||
test('does not handle non imdb movies', async () => {
|
||||
const streams = await handler.handle(ctx, 'movie', 'kitsu:123');
|
||||
expect(streams).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('handles non-existent movies gracefully', async () => {
|
||||
const streams = await handler.handle(ctx, 'movie', 'tt12345678');
|
||||
const streams = await handler.handle(ctx, 'movie', new ImdbId('tt12345678', undefined, undefined));
|
||||
expect(streams).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('handle imdb the devil\'s bath', async () => {
|
||||
const streams = (await handler.handle(ctx, 'movie', 'tt29141112')).filter(stream => stream !== undefined);
|
||||
const streams = (await handler.handle(ctx, 'movie', new ImdbId('tt29141112', undefined, undefined))).filter(stream => stream !== undefined);
|
||||
expect(streams).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { ContentType } from 'stremio-addon-sdk';
|
||||
import * as cheerio from 'cheerio';
|
||||
import { Handler } from './types';
|
||||
import { Fetcher, ImdbId } from '../utils';
|
||||
import { Fetcher, getImdbId, Id } from '../utils';
|
||||
import { ExtractorRegistry } from '../extractor';
|
||||
import { Context, CountryCode } from '../types';
|
||||
|
||||
|
|
@ -22,12 +22,10 @@ export class FrenchCloud implements Handler {
|
|||
this.extractorRegistry = extractorRegistry;
|
||||
}
|
||||
|
||||
readonly handle = async (ctx: Context, _type: string, id: string) => {
|
||||
if (!id.startsWith('tt')) {
|
||||
return [];
|
||||
}
|
||||
readonly handle = async (ctx: Context, _type: string, id: Id) => {
|
||||
const imdbId = await getImdbId(ctx, this.fetcher, id);
|
||||
|
||||
const pageUrl = new URL(`https://frenchcloud.cam/movie/${ImdbId.fromString(id).id}`);
|
||||
const pageUrl = new URL(`https://frenchcloud.cam/movie/${imdbId.id}`);
|
||||
const html = await this.fetcher.text(ctx, pageUrl);
|
||||
|
||||
const $ = cheerio.load(html);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import winston from 'winston';
|
||||
import { KinoKiste } from './KinoKiste';
|
||||
import { ExtractorRegistry } from '../extractor';
|
||||
import { Fetcher } from '../utils';
|
||||
import { Fetcher, ImdbId } from '../utils';
|
||||
import { Context } from '../types';
|
||||
jest.mock('../utils/Fetcher');
|
||||
|
||||
|
|
@ -12,18 +12,13 @@ const handler = new KinoKiste(fetcher, new ExtractorRegistry(logger, fetcher));
|
|||
const ctx: Context = { id: 'id', ip: '127.0.0.1', config: { de: 'on' } };
|
||||
|
||||
describe('KinoKiste', () => {
|
||||
test('does not handle non imdb series', async () => {
|
||||
const streams = await handler.handle(ctx, 'series', 'kitsu:123');
|
||||
expect(streams).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('handles non-existent series gracefully', async () => {
|
||||
const streams = await handler.handle(ctx, 'series', 'tt12345678:1:1');
|
||||
const streams = await handler.handle(ctx, 'series', new ImdbId('tt12345678', 1, 1));
|
||||
expect(streams).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('handle imdb black mirror s2e4', async () => {
|
||||
const streams = (await handler.handle(ctx, 'series', 'tt2085059:2:4')).filter(stream => stream !== undefined);
|
||||
const streams = (await handler.handle(ctx, 'series', new ImdbId('tt2085059', 2, 4))).filter(stream => stream !== undefined);
|
||||
expect(streams).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { ContentType } from 'stremio-addon-sdk';
|
||||
import * as cheerio from 'cheerio';
|
||||
import { Handler } from './types';
|
||||
import { ImdbId, Fetcher } from '../utils';
|
||||
import { ImdbId, Fetcher, getImdbId, Id } from '../utils';
|
||||
import { ExtractorRegistry } from '../extractor';
|
||||
import { Context, CountryCode } from '../types';
|
||||
|
||||
|
|
@ -22,12 +22,8 @@ export class KinoKiste implements Handler {
|
|||
this.extractorRegistry = extractorRegistry;
|
||||
}
|
||||
|
||||
readonly handle = async (ctx: Context, _type: string, id: string) => {
|
||||
if (!id.startsWith('tt')) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const imdbId = ImdbId.fromString(id);
|
||||
readonly handle = async (ctx: Context, _type: string, id: Id) => {
|
||||
const imdbId = await getImdbId(ctx, this.fetcher, id);
|
||||
|
||||
const seriesPageUrl = await this.fetchSeriesPageUrl(ctx, imdbId);
|
||||
if (!seriesPageUrl) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import winston from 'winston';
|
||||
import { MeineCloud } from './MeineCloud';
|
||||
import { Fetcher } from '../utils';
|
||||
import { Fetcher, ImdbId } from '../utils';
|
||||
import { ExtractorRegistry } from '../extractor';
|
||||
import { Context } from '../types';
|
||||
jest.mock('../utils/Fetcher');
|
||||
|
|
@ -12,18 +12,13 @@ const handler = new MeineCloud(fetcher, new ExtractorRegistry(logger, fetcher));
|
|||
const ctx: Context = { id: 'id', ip: '127.0.0.1', config: { de: 'on' } };
|
||||
|
||||
describe('MeineCloud', () => {
|
||||
test('does not handle non imdb movies', async () => {
|
||||
const streams = await handler.handle(ctx, 'movie', 'kitsu:123');
|
||||
expect(streams).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('handles non-existent movies gracefully', async () => {
|
||||
const streams = await handler.handle(ctx, 'movie', 'tt12345678');
|
||||
const streams = await handler.handle(ctx, 'movie', new ImdbId('tt12345678', undefined, undefined));
|
||||
expect(streams).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('handle imdb the devil\'s bath', async () => {
|
||||
const streams = (await handler.handle(ctx, 'movie', 'tt29141112')).filter(stream => stream !== undefined);
|
||||
const streams = (await handler.handle(ctx, 'movie', new ImdbId('tt29141112', undefined, undefined))).filter(stream => stream !== undefined);
|
||||
expect(streams).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { ContentType } from 'stremio-addon-sdk';
|
||||
import * as cheerio from 'cheerio';
|
||||
import { Handler } from './types';
|
||||
import { Fetcher, ImdbId } from '../utils';
|
||||
import { Fetcher, getImdbId, Id } from '../utils';
|
||||
import { ExtractorRegistry } from '../extractor';
|
||||
import { Context, CountryCode } from '../types';
|
||||
|
||||
|
|
@ -22,12 +22,10 @@ export class MeineCloud implements Handler {
|
|||
this.extractorRegistry = extractorRegistry;
|
||||
}
|
||||
|
||||
readonly handle = async (ctx: Context, _type: string, id: string) => {
|
||||
if (!id.startsWith('tt')) {
|
||||
return [];
|
||||
}
|
||||
readonly handle = async (ctx: Context, _type: string, id: Id) => {
|
||||
const imdbId = await getImdbId(ctx, this.fetcher, id);
|
||||
|
||||
const pageUrl = new URL(`https://meinecloud.click/movie/${ImdbId.fromString(id).id}`);
|
||||
const pageUrl = new URL(`https://meinecloud.click/movie/${imdbId.id}`);
|
||||
const html = await this.fetcher.text(ctx, pageUrl);
|
||||
|
||||
const $ = cheerio.load(html);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import winston from 'winston';
|
||||
import { MostraGuarda } from './MostraGuarda';
|
||||
import { Fetcher } from '../utils';
|
||||
import { Fetcher, ImdbId } from '../utils';
|
||||
import { ExtractorRegistry } from '../extractor';
|
||||
import { Context } from '../types';
|
||||
jest.mock('../utils/Fetcher');
|
||||
|
|
@ -12,18 +12,13 @@ const handler = new MostraGuarda(fetcher, new ExtractorRegistry(logger, fetcher)
|
|||
const ctx: Context = { id: 'id', ip: '127.0.0.1', config: { it: 'on' } };
|
||||
|
||||
describe('MostraGuarda', () => {
|
||||
test('does not handle non imdb movies', async () => {
|
||||
const streams = await handler.handle(ctx, 'movie', 'kitsu:123');
|
||||
expect(streams).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('handles non-existent movies gracefully', async () => {
|
||||
const streams = await handler.handle(ctx, 'movie', 'tt12345678');
|
||||
const streams = await handler.handle(ctx, 'movie', new ImdbId('tt12345678', undefined, undefined));
|
||||
expect(streams).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('handle imdb the devil\'s bath', async () => {
|
||||
const streams = (await handler.handle(ctx, 'movie', 'tt29141112')).filter(stream => stream !== undefined);
|
||||
const streams = (await handler.handle(ctx, 'movie', new ImdbId('tt29141112', undefined, undefined))).filter(stream => stream !== undefined);
|
||||
expect(streams).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { ContentType } from 'stremio-addon-sdk';
|
||||
import * as cheerio from 'cheerio';
|
||||
import { Handler } from './types';
|
||||
import { Fetcher, ImdbId } from '../utils';
|
||||
import { Fetcher, getImdbId, Id } from '../utils';
|
||||
import { ExtractorRegistry } from '../extractor';
|
||||
import { Context, CountryCode } from '../types';
|
||||
|
||||
|
|
@ -22,12 +22,10 @@ export class MostraGuarda implements Handler {
|
|||
this.extractorRegistry = extractorRegistry;
|
||||
}
|
||||
|
||||
readonly handle = async (ctx: Context, _type: string, id: string) => {
|
||||
if (!id.startsWith('tt')) {
|
||||
return [];
|
||||
}
|
||||
readonly handle = async (ctx: Context, _type: string, id: Id) => {
|
||||
const imdbId = await getImdbId(ctx, this.fetcher, id);
|
||||
|
||||
const pageUrl = new URL(`https://mostraguarda.stream/movie/${ImdbId.fromString(id).id}`);
|
||||
const pageUrl = new URL(`https://mostraguarda.stream/movie/${imdbId.id}`);
|
||||
const html = await this.fetcher.text(ctx, pageUrl);
|
||||
|
||||
const $ = cheerio.load(html);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import winston from 'winston';
|
||||
import { Soaper } from './Soaper';
|
||||
import { Fetcher } from '../utils';
|
||||
import { Fetcher, ImdbId, TmdbId } from '../utils';
|
||||
import { ExtractorRegistry } from '../extractor';
|
||||
import { Context } from '../types';
|
||||
jest.mock('../utils/Fetcher');
|
||||
|
|
@ -12,33 +12,28 @@ const handler = new Soaper(fetcher, new ExtractorRegistry(logger, fetcher));
|
|||
const ctx: Context = { id: 'id', ip: '127.0.0.1', config: { en: 'on' } };
|
||||
|
||||
describe('Soaper', () => {
|
||||
test('does not handle non imdb and tmdb series', async () => {
|
||||
const streams = await handler.handle(ctx, 'series', 'kitsu:123');
|
||||
expect(streams).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('handles non-existent movies gracefully', async () => {
|
||||
const streams = await handler.handle(ctx, 'movie', 'tt12345678');
|
||||
const streams = await handler.handle(ctx, 'movie', new ImdbId('tt12345678', undefined, undefined));
|
||||
expect(streams).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('handle non-uploaded movie gracefully', async () => {
|
||||
const streams = (await handler.handle(ctx, 'series', 'tt29141112')).filter(stream => stream !== undefined);
|
||||
const streams = (await handler.handle(ctx, 'series', new ImdbId('tt29141112', undefined, undefined))).filter(stream => stream !== undefined);
|
||||
expect(streams).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('handle non-existent episode gracefully', async () => {
|
||||
const streams = (await handler.handle(ctx, 'series', 'tt2085059:99:99')).filter(stream => stream !== undefined);
|
||||
const streams = (await handler.handle(ctx, 'series', new ImdbId('tt2085059', 99, 99))).filter(stream => stream !== undefined);
|
||||
expect(streams).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('handle imdb black mirror s4e2', async () => {
|
||||
const streams = (await handler.handle(ctx, 'series', 'tt2085059:4:2')).filter(stream => stream !== undefined);
|
||||
const streams = (await handler.handle(ctx, 'series', new ImdbId('tt2085059', 4, 2))).filter(stream => stream !== undefined);
|
||||
expect(streams).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('handle tmdb black mirror s4e2', async () => {
|
||||
const streams = (await handler.handle(ctx, 'series', '42009:4:2')).filter(stream => stream !== undefined);
|
||||
const streams = (await handler.handle(ctx, 'series', new TmdbId(42009, 4, 2))).filter(stream => stream !== undefined);
|
||||
|
||||
const streamsWithoutTtl = streams.map((stream) => {
|
||||
delete stream.ttl;
|
||||
|
|
@ -49,7 +44,7 @@ describe('Soaper', () => {
|
|||
});
|
||||
|
||||
test('handle imdb full metal jacket', async () => {
|
||||
const streams = (await handler.handle(ctx, 'series', 'tt0093058')).filter(stream => stream !== undefined);
|
||||
const streams = (await handler.handle(ctx, 'series', new ImdbId('tt0093058', undefined, undefined))).filter(stream => stream !== undefined);
|
||||
expect(streams).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,14 +1,7 @@
|
|||
import { ContentType } from 'stremio-addon-sdk';
|
||||
import * as cheerio from 'cheerio';
|
||||
import { Handler } from './types';
|
||||
import {
|
||||
Fetcher,
|
||||
getTmdbIdFromImdbId,
|
||||
getTmdbMovieDetails,
|
||||
getTmdbTvDetails,
|
||||
ImdbId,
|
||||
TmdbId,
|
||||
} from '../utils';
|
||||
import { Fetcher, getTmdbId, getTmdbMovieDetails, getTmdbTvDetails, Id, TmdbId } from '../utils';
|
||||
import { ExtractorRegistry } from '../extractor';
|
||||
import { Context, CountryCode } from '../types';
|
||||
|
||||
|
|
@ -31,15 +24,8 @@ export class Soaper implements Handler {
|
|||
this.extractorRegistry = extractorRegistry;
|
||||
}
|
||||
|
||||
readonly handle = async (ctx: Context, _type: string, id: string) => {
|
||||
let tmdbId: TmdbId;
|
||||
if (id.startsWith('tt')) {
|
||||
tmdbId = await getTmdbIdFromImdbId(ctx, this.fetcher, ImdbId.fromString(id));
|
||||
} else if (/^\d+:/.test(id)) {
|
||||
tmdbId = TmdbId.fromString(id);
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
readonly handle = async (ctx: Context, _type: string, id: Id) => {
|
||||
const tmdbId = await getTmdbId(ctx, this.fetcher, id);
|
||||
|
||||
const [keyword, year, hrefPrefix] = await this.getKeywordYearAndHrefPrefix(ctx, tmdbId);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import winston from 'winston';
|
||||
import { VerHdLink } from './VerHdLink';
|
||||
import { Fetcher } from '../utils';
|
||||
import { Fetcher, ImdbId } from '../utils';
|
||||
import { ExtractorRegistry } from '../extractor';
|
||||
import { Context } from '../types';
|
||||
jest.mock('../utils/Fetcher');
|
||||
|
|
@ -12,18 +12,13 @@ const handler = new VerHdLink(fetcher, new ExtractorRegistry(logger, fetcher));
|
|||
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 () => {
|
||||
const streams = await handler.handle(ctx, 'movie', 'kitsu:123');
|
||||
expect(streams).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('handles non-existent movies gracefully', async () => {
|
||||
const streams = await handler.handle(ctx, 'movie', 'tt12345678');
|
||||
const streams = await handler.handle(ctx, 'movie', new ImdbId('tt12345678', undefined, undefined));
|
||||
expect(streams).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('handle titanic', async () => {
|
||||
const streams = (await handler.handle(ctx, 'movie', 'tt0120338')).filter(stream => stream !== undefined);
|
||||
const streams = (await handler.handle(ctx, 'movie', new ImdbId('tt0120338', undefined, undefined))).filter(stream => stream !== undefined);
|
||||
expect(streams).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { ContentType } from 'stremio-addon-sdk';
|
||||
import * as cheerio from 'cheerio';
|
||||
import { Handler } from './types';
|
||||
import { Fetcher, ImdbId } from '../utils';
|
||||
import { Fetcher, getImdbId, Id } from '../utils';
|
||||
import { ExtractorRegistry } from '../extractor';
|
||||
import { Context, CountryCode } from '../types';
|
||||
|
||||
|
|
@ -22,12 +22,10 @@ export class VerHdLink implements Handler {
|
|||
this.extractorRegistry = extractorRegistry;
|
||||
}
|
||||
|
||||
readonly handle = async (ctx: Context, _type: string, id: string) => {
|
||||
if (!id.startsWith('tt')) {
|
||||
return [];
|
||||
}
|
||||
readonly handle = async (ctx: Context, _type: string, id: Id) => {
|
||||
const imdbId = await getImdbId(ctx, this.fetcher, id);
|
||||
|
||||
const pageUrl = new URL(`https://verhdlink.cam/movie/${ImdbId.fromString(id).id}`);
|
||||
const pageUrl = new URL(`https://verhdlink.cam/movie/${imdbId.id}`);
|
||||
const html = await this.fetcher.text(ctx, pageUrl);
|
||||
|
||||
const $ = cheerio.load(html);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { ContentType } from 'stremio-addon-sdk';
|
||||
import { Context, CountryCode, UrlResult } from '../types';
|
||||
import { Id } from '../utils';
|
||||
|
||||
export interface Handler {
|
||||
readonly id: string;
|
||||
|
|
@ -10,5 +11,5 @@ export interface Handler {
|
|||
|
||||
readonly countryCodes: CountryCode[];
|
||||
|
||||
readonly handle: (ctx: Context, type: ContentType, id: string) => Promise<(UrlResult | undefined)[]>;
|
||||
readonly handle: (ctx: Context, type: ContentType, id: Id) => Promise<(UrlResult | undefined)[]>;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import {
|
|||
} from './handler';
|
||||
import { ExtractorRegistry } from './extractor';
|
||||
import { ConfigureController, ManifestController, StreamController } from './controller';
|
||||
import { envGet, envIsProd, Fetcher, getImdbIdFromTmdbId, StreamResolver, tmdbFetch } from './utils';
|
||||
import { envGet, envIsProd, Fetcher, StreamResolver, tmdbFetch, TmdbId } from './utils';
|
||||
|
||||
const logger = winston.createLogger({
|
||||
transports: [
|
||||
|
|
@ -95,8 +95,7 @@ const cacheWarmup = async () => {
|
|||
}
|
||||
});
|
||||
for (const id of movieIds) {
|
||||
const imdbId = await getImdbIdFromTmdbId(ctx, fetcher, { id, series: undefined, episode: undefined });
|
||||
await streamResolver.resolve(ctx, handlers, 'movie', imdbId.id);
|
||||
await streamResolver.resolve(ctx, handlers, 'movie', new TmdbId(id, undefined, undefined));
|
||||
}
|
||||
logger.info(`warmed up cache with ${movieIds.length} movies`, ctx);
|
||||
|
||||
|
|
@ -110,8 +109,7 @@ const cacheWarmup = async () => {
|
|||
}
|
||||
});
|
||||
for (const id of tvShowIds) {
|
||||
const imdbId = await getImdbIdFromTmdbId(ctx, fetcher, { id, series: 1, episode: 1 });
|
||||
await streamResolver.resolve(ctx, handlers, 'series', `${imdbId.id}:1:1`);
|
||||
await streamResolver.resolve(ctx, handlers, 'series', new TmdbId(id, 1, 1));
|
||||
}
|
||||
logger.info(`warmed up cache with ${tvShowIds.length} tv shows`, ctx);
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import { Handler, MeineCloud, MostraGuarda } from '../handler';
|
|||
import { Fetcher } from './Fetcher';
|
||||
import { Context, CountryCode, TIMEOUT, UrlResult } from '../types';
|
||||
import { BlockedError, HttpError, NotFoundError, QueueIsFullError } from '../error';
|
||||
import { ImdbId } from './id';
|
||||
jest.mock('../utils/Fetcher');
|
||||
|
||||
const logger = winston.createLogger({ transports: [new winston.transports.Console({ level: 'nope' })] });
|
||||
|
|
@ -20,7 +21,7 @@ const mostraGuarda = new MostraGuarda(fetcher, extractorRegistry);
|
|||
|
||||
describe('resolve', () => {
|
||||
test('returns info as stream if no handlers were configured', async () => {
|
||||
const streams = await streamResolver.resolve(ctx, [], 'movie', 'tt123456789');
|
||||
const streams = await streamResolver.resolve(ctx, [], 'movie', new ImdbId('tt123456789', undefined, undefined));
|
||||
|
||||
expect(streams).toMatchSnapshot();
|
||||
});
|
||||
|
|
@ -28,7 +29,7 @@ describe('resolve', () => {
|
|||
test('returns handler errors as stream', async () => {
|
||||
const fetcherSpy = jest.spyOn(fetcher, 'text').mockRejectedValue('ups, an error occurred.');
|
||||
|
||||
const streams = await streamResolver.resolve(ctx, [meineCloud], 'movie', 'tt123456789');
|
||||
const streams = await streamResolver.resolve(ctx, [meineCloud], 'movie', new ImdbId('tt123456789', undefined, undefined));
|
||||
|
||||
expect(streams).toMatchSnapshot();
|
||||
|
||||
|
|
@ -36,19 +37,19 @@ describe('resolve', () => {
|
|||
});
|
||||
|
||||
test('returns empty array if no handler found anything', async () => {
|
||||
const streams = await streamResolver.resolve(ctx, [meineCloud, mostraGuarda], 'movie', 'tt12345678');
|
||||
const streams = await streamResolver.resolve(ctx, [meineCloud, mostraGuarda], 'movie', new ImdbId('tt12345678', undefined, undefined));
|
||||
|
||||
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');
|
||||
const streams = await streamResolver.resolve(ctx, [meineCloud, mostraGuarda], 'series', new ImdbId('tt12345678', 1, 1));
|
||||
|
||||
expect(streams).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('returns sorted results', async () => {
|
||||
const streams = await streamResolver.resolve(ctx, [meineCloud, mostraGuarda], 'movie', 'tt29141112');
|
||||
const streams = await streamResolver.resolve(ctx, [meineCloud, mostraGuarda], 'movie', new ImdbId('tt29141112', undefined, undefined));
|
||||
expect(streams).toMatchSnapshot();
|
||||
});
|
||||
|
||||
|
|
@ -128,10 +129,10 @@ describe('resolve', () => {
|
|||
};
|
||||
}
|
||||
|
||||
const streams = await streamResolver.resolve(ctx, [new MockHandler()], 'movie', 'tt11655566');
|
||||
const streams = await streamResolver.resolve(ctx, [new MockHandler()], 'movie', new ImdbId('tt11655566', undefined, undefined));
|
||||
expect(streams).toMatchSnapshot();
|
||||
|
||||
const streamsWithoutExternalUrls = await streamResolver.resolve({ ...ctx, config: { ...ctx.config, excludeExternalUrls: 'on' } }, [new MockHandler()], 'movie', 'tt11655566');
|
||||
const streamsWithoutExternalUrls = await streamResolver.resolve({ ...ctx, config: { ...ctx.config, excludeExternalUrls: 'on' } }, [new MockHandler()], 'movie', new ImdbId('tt11655566', undefined, undefined));
|
||||
expect(streamsWithoutExternalUrls).toMatchSnapshot();
|
||||
});
|
||||
|
||||
|
|
@ -144,7 +145,7 @@ describe('resolve', () => {
|
|||
handle: jest.fn().mockRejectedValue(new NotFoundError()),
|
||||
};
|
||||
|
||||
const streams = await streamResolver.resolve(ctx, [mockHandler], 'movie', 'tt12345678');
|
||||
const streams = await streamResolver.resolve(ctx, [mockHandler], 'movie', new ImdbId('tt12345678', undefined, undefined));
|
||||
|
||||
expect(streams).toMatchSnapshot();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import { Handler } from '../handler';
|
|||
import { BlockedError, HttpError, NotFoundError, QueueIsFullError } from '../error';
|
||||
import { flagFromCountryCode, languageFromCountryCode } from './language';
|
||||
import { envGetAppName } from './env';
|
||||
import { Id } from './id';
|
||||
|
||||
interface ResolveResponse {
|
||||
streams: Stream[];
|
||||
|
|
@ -19,7 +20,7 @@ export class StreamResolver {
|
|||
this.logger = logger;
|
||||
}
|
||||
|
||||
readonly resolve = async (ctx: Context, handlers: Handler[], type: ContentType, id: string): Promise<ResolveResponse> => {
|
||||
readonly resolve = async (ctx: Context, handlers: Handler[], type: ContentType, id: Id): Promise<ResolveResponse> => {
|
||||
if (handlers.length === 0) {
|
||||
return {
|
||||
streams: [
|
||||
|
|
|
|||
|
|
@ -1,2 +1,26 @@
|
|||
import { ImdbId } from './ImdbId';
|
||||
import { TmdbId } from './TmdbId';
|
||||
import { getImdbIdFromTmdbId, getTmdbIdFromImdbId } from '../tmdb';
|
||||
import { Fetcher } from '../Fetcher';
|
||||
import { Context } from '../../types';
|
||||
|
||||
export * from './ImdbId';
|
||||
export * from './TmdbId';
|
||||
|
||||
export type Id = ImdbId | TmdbId;
|
||||
|
||||
export const getImdbId = async (ctx: Context, fetcher: Fetcher, id: Id): Promise<ImdbId> => {
|
||||
if (id instanceof TmdbId) {
|
||||
return await getImdbIdFromTmdbId(ctx, fetcher, id);
|
||||
}
|
||||
|
||||
return id;
|
||||
};
|
||||
|
||||
export const getTmdbId = async (ctx: Context, fetcher: Fetcher, id: Id): Promise<TmdbId> => {
|
||||
if (id instanceof ImdbId) {
|
||||
return await getTmdbIdFromImdbId(ctx, fetcher, id);
|
||||
}
|
||||
|
||||
return id;
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in a new issue