refactor: define member accessiblity explicitly

This commit is contained in:
WebStreamr 2025-06-13 09:42:06 +00:00
parent b4b8581383
commit bdae1ff346
No known key found for this signature in database
32 changed files with 157 additions and 152 deletions

View file

@ -13,4 +13,9 @@ export default tseslint.config(
{
ignores: ['./dist'],
},
{
rules: {
'@typescript-eslint/explicit-member-accessibility': 'error',
},
},
);

View file

@ -9,7 +9,7 @@ export class ConfigureController {
private readonly sources: Source[];
constructor(sources: Source[]) {
public constructor(sources: Source[]) {
this.router = Router();
this.sources = sources;

View file

@ -8,7 +8,7 @@ export class ManifestController {
private readonly sources: Source[];
constructor(sources: Source[]) {
public constructor(sources: Source[]) {
this.router = Router();
this.sources = sources;

View file

@ -12,7 +12,7 @@ export class StreamController {
private readonly sources: Source[];
private readonly streamResolver: StreamResolver;
constructor(logger: winston.Logger, sources: Source[], streams: StreamResolver) {
public constructor(logger: winston.Logger, sources: Source[], streams: StreamResolver) {
this.router = Router();
this.logger = logger;

View file

@ -4,7 +4,7 @@ export class BlockedError extends Error {
public readonly reason: BlockedReason;
public readonly headers: Record<string, string[] | string | undefined>;
constructor(reason: BlockedReason, headers: Record<string, string[] | string | undefined>) {
public constructor(reason: BlockedReason, headers: Record<string, string[] | string | undefined>) {
super();
this.reason = reason;

View file

@ -3,7 +3,7 @@ export class HttpError extends Error {
public readonly statusText: string;
public readonly headers: Record<string, string[] | string | undefined>;
constructor(status: number, statusText: string, headers: Record<string, string[] | string | undefined>) {
public constructor(status: number, statusText: string, headers: Record<string, string[] | string | undefined>) {
super();
this.status = status;

View file

@ -6,27 +6,27 @@ import { Context, CountryCode, UrlResult } from '../types';
import { NotFoundError } from '../error';
export class DoodStream implements Extractor {
readonly id = 'doodstream';
public readonly id = 'doodstream';
readonly label = 'DoodStream';
public readonly label = 'DoodStream';
readonly ttl = 900000; // 15m
public readonly ttl = 900000; // 15m
private readonly fetcher: Fetcher;
constructor(fetcher: Fetcher) {
public constructor(fetcher: Fetcher) {
this.fetcher = fetcher;
}
readonly supports = (_ctx: Context, url: URL): boolean => null !== url.host.match(/dood|do[0-9]go|dooodster|dooood/);
public readonly supports = (_ctx: Context, url: URL): boolean => null !== url.host.match(/dood|do[0-9]go|dooodster|dooood/);
readonly normalize = (url: URL): URL => {
public readonly normalize = (url: URL): URL => {
const videoId = url.pathname.split('/').slice(-1)[0] as string;
return new URL(`http://dood.to/e/${videoId}`);
};
readonly extract = async (ctx: Context, url: URL, countryCode: CountryCode): Promise<UrlResult[]> => {
public readonly extract = async (ctx: Context, url: URL, countryCode: CountryCode): Promise<UrlResult[]> => {
const html = await this.fetcher.text(ctx, new URL(url));
const passMd5Match = html.match(/\/pass_md5\/[\w-]+\/([\w-]+)/);

View file

@ -6,23 +6,23 @@ import { Context, CountryCode, UrlResult } from '../types';
import { NotFoundError } from '../error';
export class Dropload implements Extractor {
readonly id = 'dropload';
public readonly id = 'dropload';
readonly label = 'Dropload';
public readonly label = 'Dropload';
readonly ttl = 900000; // 15m
public readonly ttl = 900000; // 15m
private readonly fetcher: Fetcher;
constructor(fetcher: Fetcher) {
public constructor(fetcher: Fetcher) {
this.fetcher = fetcher;
}
readonly supports = (_ctx: Context, url: URL): boolean => null !== url.host.match(/dropload/);
public readonly supports = (_ctx: Context, url: URL): boolean => null !== url.host.match(/dropload/);
readonly normalize = (url: URL): URL => new URL(url.href.replace('/e/', '/').replace('/embed-', '/'));
public readonly normalize = (url: URL): URL => new URL(url.href.replace('/e/', '/').replace('/embed-', '/'));
readonly extract = async (ctx: Context, url: URL, countryCode: CountryCode): Promise<UrlResult[]> => {
public readonly extract = async (ctx: Context, url: URL, countryCode: CountryCode): Promise<UrlResult[]> => {
const html = await this.fetcher.text(ctx, url);
if (html.includes('File Not Found')) {

View file

@ -3,23 +3,23 @@ import { Fetcher } from '../utils';
import { Context, CountryCode, UrlResult } from '../types';
export class ExternalUrl implements Extractor {
readonly id = 'external';
public readonly id = 'external';
readonly label = 'External';
public readonly label = 'External';
readonly ttl = 3600000; // 1h
public readonly ttl = 3600000; // 1h
private readonly fetcher: Fetcher;
constructor(fetcher: Fetcher) {
public constructor(fetcher: Fetcher) {
this.fetcher = fetcher;
}
readonly supports = (ctx: Context, url: URL): boolean => !('excludeExternalUrls' in ctx.config) && null !== url.host.match(/.*/);
public readonly supports = (ctx: Context, url: URL): boolean => !('excludeExternalUrls' in ctx.config) && null !== url.host.match(/.*/);
readonly normalize = (url: URL): URL => url;
public readonly normalize = (url: URL): URL => url;
readonly extract = async (ctx: Context, url: URL, countryCode: CountryCode, title: string | undefined): Promise<UrlResult[]> => {
public readonly extract = async (ctx: Context, url: URL, countryCode: CountryCode, title: string | undefined): Promise<UrlResult[]> => {
try {
// Make sure the URL is accessible, but avoid causing noise and delays doing this
await this.fetcher.head(ctx, url, { noFlareSolverr: true, timeout: 1000 });

View file

@ -9,13 +9,13 @@ export class ExtractorRegistry {
private readonly extractors: Extractor[];
private readonly urlResultCache: TTLCache<string, UrlResult[]>;
constructor(logger: winston.Logger, extractors: Extractor[]) {
public constructor(logger: winston.Logger, extractors: Extractor[]) {
this.logger = logger;
this.extractors = extractors;
this.urlResultCache = new TTLCache({ max: 1024 });
}
readonly handle = async (ctx: Context, url: URL, countryCode: CountryCode, title?: string | undefined): Promise<UrlResult[]> => {
public readonly handle = async (ctx: Context, url: URL, countryCode: CountryCode, title?: string | undefined): Promise<UrlResult[]> => {
const extractor = this.extractors.find(extractor => extractor.supports(ctx, url));
if (!extractor) {
return [];

View file

@ -4,23 +4,23 @@ import { Fetcher } from '../utils';
import { Context, CountryCode, UrlResult } from '../types';
export class Fsst implements Extractor {
readonly id = 'fsst';
public readonly id = 'fsst';
readonly label = 'Fsst';
public readonly label = 'Fsst';
readonly ttl = 900000; // 15m
public readonly ttl = 900000; // 15m
private readonly fetcher: Fetcher;
constructor(fetcher: Fetcher) {
public constructor(fetcher: Fetcher) {
this.fetcher = fetcher;
}
readonly supports = (_ctx: Context, url: URL): boolean => null !== url.host.match(/fsst/);
public readonly supports = (_ctx: Context, url: URL): boolean => null !== url.host.match(/fsst/);
readonly normalize = (url: URL): URL => url;
public readonly normalize = (url: URL): URL => url;
readonly extract = async (ctx: Context, url: URL, countryCode: CountryCode): Promise<UrlResult[]> => {
public readonly extract = async (ctx: Context, url: URL, countryCode: CountryCode): Promise<UrlResult[]> => {
const html = await this.fetcher.text(ctx, url);
const $ = cheerio.load(html);

View file

@ -5,23 +5,23 @@ import { Context, CountryCode, UrlResult } from '../types';
/** @see https://github.com/Gujal00/ResolveURL/blob/master/script.module.resolveurl/lib/resolveurl/plugins/kinoger.py */
export class KinoGer implements Extractor {
readonly id = 'kinoger';
public readonly id = 'kinoger';
readonly label = 'KinoGer';
public readonly label = 'KinoGer';
readonly ttl = 900000; // 15m
public readonly ttl = 900000; // 15m
private readonly fetcher: Fetcher;
constructor(fetcher: Fetcher) {
public constructor(fetcher: Fetcher) {
this.fetcher = fetcher;
}
readonly supports = (_ctx: Context, url: URL): boolean => null !== url.host.match(/kinoger\.re|shiid4u\.upn\.one|moflix\.upns\.xyz|player\.upn\.one|wasuytm\.store|ultrastream\.online/);
public readonly supports = (_ctx: Context, url: URL): boolean => null !== url.host.match(/kinoger\.re|shiid4u\.upn\.one|moflix\.upns\.xyz|player\.upn\.one|wasuytm\.store|ultrastream\.online/);
readonly normalize = (url: URL): URL => new URL(`${url.origin}/api/v1/video?id=${url.hash.slice(1)}`);
public readonly normalize = (url: URL): URL => new URL(`${url.origin}/api/v1/video?id=${url.hash.slice(1)}`);
readonly extract = async (ctx: Context, url: URL, countryCode: CountryCode): Promise<UrlResult[]> => {
public readonly extract = async (ctx: Context, url: URL, countryCode: CountryCode): Promise<UrlResult[]> => {
const hexData = await this.fetcher.text(ctx, url, { headers: { 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36' } });
const encrypted = Buffer.from(hexData, 'hex');

View file

@ -8,23 +8,23 @@ interface SoaperInfoResponsePartial {
}
export class Soaper implements Extractor {
readonly id = 'soaper';
public readonly id = 'soaper';
readonly label = 'Soaper';
public readonly label = 'Soaper';
readonly ttl = 900000; // 15m
public readonly ttl = 900000; // 15m
private readonly fetcher: Fetcher;
constructor(fetcher: Fetcher) {
public constructor(fetcher: Fetcher) {
this.fetcher = fetcher;
}
readonly supports = (_ctx: Context, url: URL): boolean => null !== url.host.match(/soaper/) && null !== url.pathname.match(/^\/(episode|movie)_/);
public readonly supports = (_ctx: Context, url: URL): boolean => null !== url.host.match(/soaper/) && null !== url.pathname.match(/^\/(episode|movie)_/);
readonly normalize = (url: URL): URL => url;
public readonly normalize = (url: URL): URL => url;
readonly extract = async (ctx: Context, url: URL, countryCode: CountryCode, title: string | undefined): Promise<UrlResult[]> => {
public readonly extract = async (ctx: Context, url: URL, countryCode: CountryCode, title: string | undefined): Promise<UrlResult[]> => {
const movieOrEpisodeId = (url.pathname.match(/\/\w+_(\w+)/) as string[])[1] as string;
const form = new URLSearchParams();

View file

@ -6,23 +6,23 @@ import { Context, CountryCode, UrlResult } from '../types';
import { NotFoundError } from '../error';
export class SuperVideo implements Extractor {
readonly id = 'supervideo';
public readonly id = 'supervideo';
readonly label = 'SuperVideo';
public readonly label = 'SuperVideo';
readonly ttl = 900000; // 15m
public readonly ttl = 900000; // 15m
private readonly fetcher: Fetcher;
constructor(fetcher: Fetcher) {
public constructor(fetcher: Fetcher) {
this.fetcher = fetcher;
}
readonly supports = (_ctx: Context, url: URL): boolean => null !== url.host.match(/supervideo/);
public readonly supports = (_ctx: Context, url: URL): boolean => null !== url.host.match(/supervideo/);
readonly normalize = (url: URL): URL => new URL(url.href.replace('/e/', '/').replace('/embed-', '/'));
public readonly normalize = (url: URL): URL => new URL(url.href.replace('/e/', '/').replace('/embed-', '/'));
readonly extract = async (ctx: Context, url: URL, countryCode: CountryCode): Promise<UrlResult[]> => {
public readonly extract = async (ctx: Context, url: URL, countryCode: CountryCode): Promise<UrlResult[]> => {
const html = await this.fetcher.text(ctx, url);
if (html.includes('This video can be watched as embed only')) {

View file

@ -6,23 +6,23 @@ import { Context, CountryCode, UrlResult } from '../types';
import { NotFoundError } from '../error';
export class VidSrc implements Extractor {
readonly id = 'vidsrc';
public readonly id = 'vidsrc';
readonly label = 'VidSrc';
public readonly label = 'VidSrc';
readonly ttl = 900000; // 15m
public readonly ttl = 900000; // 15m
private readonly fetcher: Fetcher;
constructor(fetcher: Fetcher) {
public constructor(fetcher: Fetcher) {
this.fetcher = fetcher;
}
readonly supports = (_ctx: Context, url: URL): boolean => null !== url.host.match(/vidsrc/);
public readonly supports = (_ctx: Context, url: URL): boolean => null !== url.host.match(/vidsrc/);
readonly normalize = (url: URL): URL => url;
public readonly normalize = (url: URL): URL => url;
readonly extract = async (ctx: Context, url: URL, countryCode: CountryCode): Promise<UrlResult[]> => {
public readonly extract = async (ctx: Context, url: URL, countryCode: CountryCode): Promise<UrlResult[]> => {
const html = await this.fetcher.text(ctx, url);
const $ = cheerio.load(html);

View file

@ -5,21 +5,21 @@ import { Fetcher, getImdbId, Id, ImdbId } from '../utils';
import { Context, CountryCode } from '../types';
export class CineHDPlus implements Source {
readonly id = 'cinehdplus';
public readonly id = 'cinehdplus';
readonly label = 'CineHDPlus';
public readonly label = 'CineHDPlus';
readonly contentTypes: ContentType[] = ['series'];
public readonly contentTypes: ContentType[] = ['series'];
readonly countryCodes: CountryCode[] = [CountryCode.es, CountryCode.mx];
public readonly countryCodes: CountryCode[] = [CountryCode.es, CountryCode.mx];
private readonly fetcher: Fetcher;
constructor(fetcher: Fetcher) {
public constructor(fetcher: Fetcher) {
this.fetcher = fetcher;
}
readonly handle = async (ctx: Context, _type: string, id: Id): Promise<SourceResult[]> => {
public readonly handle = async (ctx: Context, _type: string, id: Id): Promise<SourceResult[]> => {
const imdbId = await getImdbId(ctx, this.fetcher, id);
const seriesPageUrl = await this.fetchSeriesPageUrl(ctx, imdbId);

View file

@ -5,21 +5,21 @@ import { Fetcher, getImdbId, Id, ImdbId } from '../utils';
import { Context, CountryCode } from '../types';
export class Eurostreaming implements Source {
readonly id = 'eurostreaming';
public readonly id = 'eurostreaming';
readonly label = 'Eurostreaming';
public readonly label = 'Eurostreaming';
readonly contentTypes: ContentType[] = ['series'];
public readonly contentTypes: ContentType[] = ['series'];
readonly countryCodes: CountryCode[] = [CountryCode.it];
public readonly countryCodes: CountryCode[] = [CountryCode.it];
private readonly fetcher: Fetcher;
constructor(fetcher: Fetcher) {
public constructor(fetcher: Fetcher) {
this.fetcher = fetcher;
}
readonly handle = async (ctx: Context, _type: string, id: Id): Promise<SourceResult[]> => {
public readonly handle = async (ctx: Context, _type: string, id: Id): Promise<SourceResult[]> => {
const imdbId = await getImdbId(ctx, this.fetcher, id);
const seriesPageUrl = await this.fetchSeriesPageUrl(ctx, imdbId);

View file

@ -4,21 +4,21 @@ import { Fetcher, getTmdbId, Id } from '../utils';
import { Context, CountryCode } from '../types';
export class Frembed implements Source {
readonly id = 'frembed';
public readonly id = 'frembed';
readonly label = 'Frembed';
public readonly label = 'Frembed';
readonly contentTypes: ContentType[] = ['series'];
public readonly contentTypes: ContentType[] = ['series'];
readonly countryCodes: CountryCode[] = [CountryCode.fr];
public readonly countryCodes: CountryCode[] = [CountryCode.fr];
private readonly fetcher: Fetcher;
constructor(fetcher: Fetcher) {
public constructor(fetcher: Fetcher) {
this.fetcher = fetcher;
}
readonly handle = async (ctx: Context, _type: string, id: Id): Promise<SourceResult[]> => {
public readonly handle = async (ctx: Context, _type: string, id: Id): Promise<SourceResult[]> => {
const tmdbId = await getTmdbId(ctx, this.fetcher, id);
const apiUrl = new URL(`https://frembed.space/api/series?id=${tmdbId.id}&sa=${tmdbId.season}&epi=${tmdbId.episode}&idType=tmdb`);

View file

@ -5,21 +5,21 @@ import { Fetcher, getImdbId, Id } from '../utils';
import { Context, CountryCode } from '../types';
export class FrenchCloud implements Source {
readonly id = 'frenchcloud';
public readonly id = 'frenchcloud';
readonly label = 'FrenchCloud';
public readonly label = 'FrenchCloud';
readonly contentTypes: ContentType[] = ['movie'];
public readonly contentTypes: ContentType[] = ['movie'];
readonly countryCodes: CountryCode[] = [CountryCode.fr];
public readonly countryCodes: CountryCode[] = [CountryCode.fr];
private readonly fetcher: Fetcher;
constructor(fetcher: Fetcher) {
public constructor(fetcher: Fetcher) {
this.fetcher = fetcher;
}
readonly handle = async (ctx: Context, _type: string, id: Id): Promise<SourceResult[]> => {
public readonly handle = async (ctx: Context, _type: string, id: Id): Promise<SourceResult[]> => {
const imdbId = await getImdbId(ctx, this.fetcher, id);
const pageUrl = new URL(`https://frenchcloud.cam/movie/${imdbId.id}`);

View file

@ -5,23 +5,23 @@ import { Fetcher, getTmdbId, getTmdbMovieDetails, getTmdbTvDetails, Id, TmdbId }
import { Context, CountryCode } from '../types';
export class KinoGer implements Source {
readonly id = 'kinoger';
public readonly id = 'kinoger';
readonly label = 'KinoGer';
public readonly label = 'KinoGer';
readonly contentTypes: ContentType[] = ['movie', 'series'];
public readonly contentTypes: ContentType[] = ['movie', 'series'];
readonly countryCodes: CountryCode[] = [CountryCode.de];
public readonly countryCodes: CountryCode[] = [CountryCode.de];
private readonly baseUrl = 'https://kinoger.com';
private readonly fetcher: Fetcher;
constructor(fetcher: Fetcher) {
public constructor(fetcher: Fetcher) {
this.fetcher = fetcher;
}
readonly handle = async (ctx: Context, _type: string, id: Id): Promise<SourceResult[]> => {
public readonly handle = async (ctx: Context, _type: string, id: Id): Promise<SourceResult[]> => {
const tmdbId = await getTmdbId(ctx, this.fetcher, id);
const [keyword, year] = await this.getKeywordAndYear(ctx, tmdbId);

View file

@ -5,21 +5,21 @@ import { Fetcher, getImdbId, Id } from '../utils';
import { Context, CountryCode } from '../types';
export class MeineCloud implements Source {
readonly id = 'meinecloud';
public readonly id = 'meinecloud';
readonly label = 'MeineCloud';
public readonly label = 'MeineCloud';
readonly contentTypes: ContentType[] = ['movie'];
public readonly contentTypes: ContentType[] = ['movie'];
readonly countryCodes: CountryCode[] = [CountryCode.de];
public readonly countryCodes: CountryCode[] = [CountryCode.de];
private readonly fetcher: Fetcher;
constructor(fetcher: Fetcher) {
public constructor(fetcher: Fetcher) {
this.fetcher = fetcher;
}
readonly handle = async (ctx: Context, _type: string, id: Id): Promise<SourceResult[]> => {
public readonly handle = async (ctx: Context, _type: string, id: Id): Promise<SourceResult[]> => {
const imdbId = await getImdbId(ctx, this.fetcher, id);
const pageUrl = new URL(`https://meinecloud.click/movie/${imdbId.id}`);

View file

@ -5,21 +5,21 @@ import { Fetcher, getImdbId, Id } from '../utils';
import { Context, CountryCode } from '../types';
export class MostraGuarda implements Source {
readonly id = 'mostraguarda';
public readonly id = 'mostraguarda';
readonly label = 'MostraGuarda';
public readonly label = 'MostraGuarda';
readonly contentTypes: ContentType[] = ['movie'];
public readonly contentTypes: ContentType[] = ['movie'];
readonly countryCodes: CountryCode[] = [CountryCode.it];
public readonly countryCodes: CountryCode[] = [CountryCode.it];
private readonly fetcher: Fetcher;
constructor(fetcher: Fetcher) {
public constructor(fetcher: Fetcher) {
this.fetcher = fetcher;
}
readonly handle = async (ctx: Context, _type: string, id: Id): Promise<SourceResult[]> => {
public readonly handle = async (ctx: Context, _type: string, id: Id): Promise<SourceResult[]> => {
const imdbId = await getImdbId(ctx, this.fetcher, id);
const pageUrl = new URL(`https://mostraguarda.stream/movie/${imdbId.id}`);

View file

@ -5,23 +5,23 @@ import { Fetcher, getTmdbId, getTmdbMovieDetails, getTmdbTvDetails, Id, TmdbId }
import { Context, CountryCode } from '../types';
export class Soaper implements Source {
readonly id = 'soaper';
public readonly id = 'soaper';
readonly label = 'Soaper';
public readonly label = 'Soaper';
readonly contentTypes: ContentType[] = ['movie', 'series'];
public readonly contentTypes: ContentType[] = ['movie', 'series'];
readonly countryCodes: CountryCode[] = [CountryCode.en];
public readonly countryCodes: CountryCode[] = [CountryCode.en];
private readonly baseUrl = 'https://soaper.live';
private readonly fetcher: Fetcher;
constructor(fetcher: Fetcher) {
public constructor(fetcher: Fetcher) {
this.fetcher = fetcher;
}
readonly handle = async (ctx: Context, _type: string, id: Id): Promise<SourceResult[]> => {
public readonly handle = async (ctx: Context, _type: string, id: Id): Promise<SourceResult[]> => {
const tmdbId = await getTmdbId(ctx, this.fetcher, id);
const [keyword, year, hrefPrefix] = await this.getKeywordYearAndHrefPrefix(ctx, tmdbId);

View file

@ -5,21 +5,21 @@ import { ImdbId, Fetcher, getImdbId, Id } from '../utils';
import { Context, CountryCode } from '../types';
export class StreamKiste implements Source {
readonly id = 'streamkiste';
public readonly id = 'streamkiste';
readonly label = 'StreamKiste';
public readonly label = 'StreamKiste';
readonly contentTypes: ContentType[] = ['series'];
public readonly contentTypes: ContentType[] = ['series'];
readonly countryCodes: CountryCode[] = [CountryCode.de];
public readonly countryCodes: CountryCode[] = [CountryCode.de];
private readonly fetcher: Fetcher;
constructor(fetcher: Fetcher) {
public constructor(fetcher: Fetcher) {
this.fetcher = fetcher;
}
readonly handle = async (ctx: Context, _type: string, id: Id): Promise<SourceResult[]> => {
public readonly handle = async (ctx: Context, _type: string, id: Id): Promise<SourceResult[]> => {
const imdbId = await getImdbId(ctx, this.fetcher, id);
const seriesPageUrl = await this.fetchSeriesPageUrl(ctx, imdbId);

View file

@ -5,21 +5,21 @@ import { Fetcher, getImdbId, Id } from '../utils';
import { Context, CountryCode } from '../types';
export class VerHdLink implements Source {
readonly id = 'verhdlink';
public readonly id = 'verhdlink';
readonly label = 'VerHdLink';
public readonly label = 'VerHdLink';
readonly contentTypes: ContentType[] = ['movie'];
public readonly contentTypes: ContentType[] = ['movie'];
readonly countryCodes: CountryCode[] = [CountryCode.es, CountryCode.mx];
public readonly countryCodes: CountryCode[] = [CountryCode.es, CountryCode.mx];
private readonly fetcher: Fetcher;
constructor(fetcher: Fetcher) {
public constructor(fetcher: Fetcher) {
this.fetcher = fetcher;
}
readonly handle = async (ctx: Context, _type: string, id: Id): Promise<SourceResult[]> => {
public readonly handle = async (ctx: Context, _type: string, id: Id): Promise<SourceResult[]> => {
const imdbId = await getImdbId(ctx, this.fetcher, id);
const pageUrl = new URL(`https://verhdlink.cam/movie/${imdbId.id}`);

View file

@ -4,23 +4,23 @@ import { Fetcher, getImdbId, Id } from '../utils';
import { Context, CountryCode } from '../types';
export class VidSrc implements Source {
readonly id = 'vidsrc';
public readonly id = 'vidsrc';
readonly label = 'VidSrc';
public readonly label = 'VidSrc';
readonly contentTypes: ContentType[] = ['movie', 'series'];
public readonly contentTypes: ContentType[] = ['movie', 'series'];
readonly countryCodes: CountryCode[] = [CountryCode.en];
public readonly countryCodes: CountryCode[] = [CountryCode.en];
private readonly baseUrl = 'https://vidsrc.xyz';
private readonly fetcher: Fetcher;
constructor(fetcher: Fetcher) {
public constructor(fetcher: Fetcher) {
this.fetcher = fetcher;
}
readonly handle = async (ctx: Context, _type: string, id: Id): Promise<SourceResult[]> => {
public readonly handle = async (ctx: Context, _type: string, id: Id): Promise<SourceResult[]> => {
const imdbId = await getImdbId(ctx, this.fetcher, id);
const url = imdbId.season

View file

@ -58,21 +58,21 @@ export class Fetcher {
private readonly hostQueueCount = new Map<string, number>();
private readonly countMutex = new Mutex();
constructor(logger: winston.Logger) {
public constructor(logger: winston.Logger) {
this.logger = logger;
this.httpCache = new TTLCache();
}
readonly text = async (ctx: Context, url: URL, init?: CustomRequestInit): Promise<string> => {
public readonly text = async (ctx: Context, url: URL, init?: CustomRequestInit): Promise<string> => {
return (await this.cachedFetch(ctx, url, init)).body;
};
readonly textPost = async (ctx: Context, url: URL, body: string, init?: CustomRequestInit): Promise<string> => {
public readonly textPost = async (ctx: Context, url: URL, body: string, init?: CustomRequestInit): Promise<string> => {
return (await this.cachedFetch(ctx, url, { ...init, method: 'POST', body })).body;
};
readonly head = async (ctx: Context, url: URL, init?: CustomRequestInit): Promise<CachePolicy.Headers> => {
public readonly head = async (ctx: Context, url: URL, init?: CustomRequestInit): Promise<CachePolicy.Headers> => {
return (await this.cachedFetch(ctx, url, { ...init, method: 'HEAD' })).policy.responseHeaders();
};

View file

@ -64,31 +64,31 @@ describe('resolve', () => {
test('adds error info', async () => {
class MockHandler implements Source {
readonly id = 'mockhandler';
public readonly id = 'mockhandler';
readonly label = 'MockHandler';
public readonly label = 'MockHandler';
readonly contentTypes: ContentType[] = ['movie'];
public readonly contentTypes: ContentType[] = ['movie'];
readonly countryCodes: CountryCode[] = [CountryCode.de];
public readonly countryCodes: CountryCode[] = [CountryCode.de];
readonly handle = async (): Promise<SourceResult[]> => {
public readonly handle = async (): Promise<SourceResult[]> => {
return [{ countryCode: CountryCode.de, url: new URL('https://example.com') }];
};
}
class MockExtractor implements Extractor {
readonly id = 'mockextractor';
public readonly id = 'mockextractor';
readonly label = 'MockExtractor';
public readonly label = 'MockExtractor';
readonly ttl = 1;
public readonly ttl = 1;
readonly supports = (): boolean => true;
public readonly supports = (): boolean => true;
readonly normalize = (url: URL): URL => url;
public readonly normalize = (url: URL): URL => url;
readonly extract = async (): Promise<UrlResult[]> =>
public readonly extract = async (): Promise<UrlResult[]> =>
[
{
url: new URL('https://example.com'),

View file

@ -18,12 +18,12 @@ export class StreamResolver {
private readonly logger: winston.Logger;
private readonly extractorRegistry: ExtractorRegistry;
constructor(logger: winston.Logger, extractorRegistry: ExtractorRegistry) {
public constructor(logger: winston.Logger, extractorRegistry: ExtractorRegistry) {
this.logger = logger;
this.extractorRegistry = extractorRegistry;
}
readonly resolve = async (ctx: Context, sources: Source[], type: ContentType, id: Id): Promise<ResolveResponse> => {
public readonly resolve = async (ctx: Context, sources: Source[], type: ContentType, id: Id): Promise<ResolveResponse> => {
if (sources.length === 0) {
return {
streams: [

View file

@ -9,23 +9,23 @@ const { Fetcher } = jest.requireActual('../Fetcher');
class MockedFetcher {
private readonly fetcher: typeof Fetcher;
constructor() {
public constructor() {
this.fetcher = new Fetcher(winston.createLogger({ transports: [new winston.transports.Console()] }));
}
readonly text = async (ctx: Context, url: URL, init?: RequestInit): Promise<string> => {
public readonly text = async (ctx: Context, url: URL, init?: RequestInit): Promise<string> => {
const path = `${__dirname}/../__fixtures__/Fetcher/${this.slugifyUrl(url)}`;
return this.fetch(path, ctx, url, init);
};
readonly textPost = async (ctx: Context, url: URL, body: string, init?: RequestInit): Promise<string> => {
public readonly textPost = async (ctx: Context, url: URL, body: string, init?: RequestInit): Promise<string> => {
const path = `${__dirname}/../__fixtures__/Fetcher/post-${this.slugifyUrl(url)}-${slugify(body)}`;
return this.fetch(path, ctx, url, { ...init, method: 'POST', body });
};
readonly head = async (ctx: Context, url: URL, init?: RequestInit): Promise<unknown> => {
public readonly head = async (ctx: Context, url: URL, init?: RequestInit): Promise<unknown> => {
const path = `${__dirname}/../__fixtures__/Fetcher/head-${this.slugifyUrl(url)}`;
return JSON.parse(await this.fetch(path, ctx, url, { ...init, method: 'HEAD' }));

View file

@ -3,7 +3,7 @@ export class ImdbId {
public readonly season: number | undefined;
public readonly episode: number | undefined;
constructor(id: string, season: number | undefined, episode: number | undefined) {
public constructor(id: string, season: number | undefined, episode: number | undefined) {
this.id = id;
this.season = season;
this.episode = episode;

View file

@ -3,7 +3,7 @@ export class TmdbId {
public readonly season: number | undefined;
public readonly episode: number | undefined;
constructor(id: number, season: number | undefined, episode: number | undefined) {
public constructor(id: number, season: number | undefined, episode: number | undefined) {
this.id = id;
this.season = season;
this.episode = episode;