refactor(extractor): introduce internal type to reduce common code

This commit is contained in:
WebStreamr 2026-01-26 13:47:55 +00:00
parent db860c25c6
commit 50a7e5559b
No known key found for this signature in database
26 changed files with 68 additions and 100 deletions

View file

@ -1,7 +1,7 @@
import bytes from 'bytes';
import * as cheerio from 'cheerio';
import { NotFoundError } from '../error';
import { Context, Format, Meta, UrlResult } from '../types';
import { Context, Format, InternalUrlResult, Meta } from '../types';
import { buildMediaFlowProxyExtractorRedirectUrl, supportsMediaFlowProxy } from '../utils';
import { Extractor } from './Extractor';
@ -25,7 +25,7 @@ export class DoodStream extends Extractor {
return new URL(`http://dood.to/e/${videoId}`);
};
protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise<UrlResult[]> {
protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise<InternalUrlResult[]> {
const headers = { Referer: meta.referer ?? url.href };
const html = await this.fetcher.text(ctx, url, { headers });
@ -44,8 +44,6 @@ export class DoodStream extends Extractor {
{
url: buildMediaFlowProxyExtractorRedirectUrl(ctx, 'Doodstream', url, headers),
format: Format.mp4,
label: this.label,
ttl: this.ttl,
meta: {
...meta,
title,

View file

@ -1,7 +1,7 @@
import bytes from 'bytes';
import * as cheerio from 'cheerio';
import { NotFoundError } from '../error';
import { Context, Format, Meta, UrlResult } from '../types';
import { Context, Format, InternalUrlResult, Meta } from '../types';
import { extractUrlFromPacked, guessHeightFromPlaylist } from '../utils';
import { Extractor } from './Extractor';
@ -18,7 +18,7 @@ export class Dropload extends Extractor {
public override readonly normalize = (url: URL): URL => new URL(url.href.replace('/d/', '/').replace('/e/', '/').replace('/embed-', '/'));
protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise<UrlResult[]> {
protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise<InternalUrlResult[]> {
const headers = { Referer: meta.referer ?? url.href };
const html = await this.fetcher.text(ctx, url, { headers });
@ -44,8 +44,6 @@ export class Dropload extends Extractor {
{
url: playlistUrl,
format: Format.hls,
label: this.label,
ttl: this.ttl,
meta: {
...meta,
title,

View file

@ -1,4 +1,4 @@
import { Context, Format, Meta, UrlResult } from '../types';
import { Context, Format, InternalUrlResult, Meta } from '../types';
import { showExternalUrls } from '../utils';
import { Extractor } from './Extractor';
@ -13,14 +13,13 @@ export class ExternalUrl extends Extractor {
return showExternalUrls(ctx.config) && null !== url.host.match(/.*/);
}
protected async extractInternal(_ctx: Context, url: URL, meta: Meta): Promise<UrlResult[]> {
protected async extractInternal(_ctx: Context, url: URL, meta: Meta): Promise<InternalUrlResult[]> {
return [
{
url: url,
format: Format.unknown,
isExternal: true,
label: `${url.host}`,
ttl: this.ttl,
meta,
},
];

View file

@ -1,5 +1,5 @@
import { NotFoundError } from '../error';
import { Context, Format, Meta, UrlResult } from '../types';
import { Context, Format, InternalUrlResult, Meta, UrlResult } from '../types';
import { Fetcher } from '../utils';
export abstract class Extractor {
@ -23,12 +23,16 @@ export abstract class Extractor {
return url;
};
protected abstract extractInternal(ctx: Context, url: URL, meta: Meta): Promise<UrlResult[]>;
protected abstract extractInternal(ctx: Context, url: URL, meta: Meta): Promise<InternalUrlResult[]>;
public async extract(ctx: Context, url: URL, meta: Meta): Promise<UrlResult[]> {
try {
return (await this.extractInternal(ctx, url, meta)).map(
urlResult => ({ ...urlResult, label: this.formatLabel(urlResult.label) }),
urlResult => ({
...urlResult,
label: this.formatLabel(urlResult.label ?? this.label),
ttl: this.ttl,
}),
);
} catch (error) {
if (error instanceof NotFoundError) {

View file

@ -1,6 +1,6 @@
import bytes from 'bytes';
import { NotFoundError } from '../error';
import { Context, Format, Meta, UrlResult } from '../types';
import { Context, Format, InternalUrlResult, Meta } from '../types';
import { buildMediaFlowProxyExtractorStreamUrl, supportsMediaFlowProxy } from '../utils';
import { Extractor } from './Extractor';
@ -19,7 +19,7 @@ export class Fastream extends Extractor {
return new URL(url.href.replace('/e/', '/embed-').replace('/d/', '/embed-'));
}
protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise<UrlResult[]> {
protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise<InternalUrlResult[]> {
const headers = { Referer: meta.referer ?? url.href };
const downloadUrl = new URL(url.href.replace('/embed-', '/d/'));
@ -38,8 +38,6 @@ export class Fastream extends Extractor {
{
url: playlistUrl,
format: Format.hls,
label: this.label,
ttl: this.ttl,
meta: {
...meta,
bytes: bytes.parse(heightAndSizeMatch[2] as string) as number,

View file

@ -1,7 +1,7 @@
import bytes from 'bytes';
import * as cheerio from 'cheerio';
import { NotFoundError } from '../error';
import { Context, Format, Meta, UrlResult } from '../types';
import { Context, Format, InternalUrlResult, Meta } from '../types';
import { buildMediaFlowProxyExtractorStreamUrl, supportsMediaFlowProxy, unpackEval } from '../utils';
import { Extractor } from './Extractor';
@ -62,7 +62,7 @@ export class FileLions extends Extractor {
return new URL(url.href.replace('/v/', '/f/').replace('/download/', '/f/').replace('/file/', '/f/'));
}
protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise<UrlResult[]> {
protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise<InternalUrlResult[]> {
const headers = { Referer: meta.referer ?? url.href };
const html = await this.fetcher.text(ctx, url, { headers });
@ -87,8 +87,6 @@ export class FileLions extends Extractor {
{
url: await buildMediaFlowProxyExtractorStreamUrl(ctx, this.fetcher, 'FileLions', url, headers),
format: Format.hls,
label: this.label,
ttl: this.ttl,
meta: {
...meta,
height: parseInt(heightMatch[1] as string),

View file

@ -1,6 +1,6 @@
import * as cheerio from 'cheerio';
import { NotFoundError } from '../error';
import { Context, Format, Meta, UrlResult } from '../types';
import { Context, Format, InternalUrlResult, Meta } from '../types';
import {
buildMediaFlowProxyExtractorStreamUrl,
supportsMediaFlowProxy,
@ -45,7 +45,7 @@ export class FileMoon extends Extractor {
return new URL(url.href.replace('/e/', '/d/'));
}
protected async extractInternal(ctx: Context, url: URL, meta: Meta, originalUrl?: URL): Promise<UrlResult[]> {
protected async extractInternal(ctx: Context, url: URL, meta: Meta, originalUrl?: URL): Promise<InternalUrlResult[]> {
const headers = { Referer: meta.referer ?? url.href };
const html = await this.fetcher.text(ctx, url, { headers });
@ -72,8 +72,6 @@ export class FileMoon extends Extractor {
{
url: playlistUrl,
format: Format.hls,
label: this.label,
ttl: this.ttl,
meta: {
...meta,
...(heightMatch && { height: parseInt(heightMatch[1] as string) }),

View file

@ -1,5 +1,5 @@
import * as cheerio from 'cheerio';
import { Context, Format, Meta, UrlResult } from '../types';
import { Context, Format, InternalUrlResult, Meta } from '../types';
import { Extractor } from './Extractor';
export class Fsst extends Extractor {
@ -11,7 +11,7 @@ export class Fsst extends Extractor {
return null !== url.host.match(/fsst/);
};
protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise<UrlResult[]> {
protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise<InternalUrlResult[]> {
const headers = { Referer: meta.referer ?? url.href };
const html = await this.fetcher.text(ctx, url, { headers, noProxyHeaders: true });
@ -29,8 +29,6 @@ export class Fsst extends Extractor {
return [{
url: await this.fetcher.getFinalRedirectUrl(ctx, new URL(fileHref), { headers, noProxyHeaders: true }, 1),
format: Format.mp4,
label: this.label,
ttl: this.ttl,
meta: {
...meta,
height: parseInt(heightAndUrlMatch[1] as string),

View file

@ -1,6 +1,6 @@
import bytes from 'bytes';
import * as cheerio from 'cheerio';
import { Context, Format, Meta, UrlResult } from '../types';
import { Context, Format, InternalUrlResult, Meta } from '../types';
import { Extractor } from './Extractor';
export class HubCloud extends Extractor {
@ -14,7 +14,7 @@ export class HubCloud extends Extractor {
return null !== url.host.match(/hubcloud/);
}
protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise<UrlResult[]> {
protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise<InternalUrlResult[]> {
const headers = { Referer: meta.referer ?? url.href };
const redirectHtml = await this.fetcher.text(ctx, url, { headers });
@ -36,7 +36,6 @@ export class HubCloud extends Extractor {
url,
format: Format.unknown,
label: `${this.label} (FSL)`,
ttl: this.ttl,
meta: {
...meta,
bytes: bytes.parse($('#size').text()) as number,
@ -53,7 +52,6 @@ export class HubCloud extends Extractor {
url,
format: Format.unknown,
label: `${this.label} (PixelServer)`,
ttl: this.ttl,
meta: {
...meta,
bytes: bytes.parse($('#size').text()) as number,

View file

@ -1,5 +1,5 @@
import * as cheerio from 'cheerio';
import { Context, Meta, UrlResult } from '../types';
import { Context, InternalUrlResult, Meta } from '../types';
import { Fetcher } from '../utils';
import { Extractor } from './Extractor';
import { HubCloud } from './HubCloud';
@ -23,7 +23,7 @@ export class HubDrive extends Extractor {
return null !== url.host.match(/hubdrive/);
}
protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise<UrlResult[]> {
protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise<InternalUrlResult[]> {
const headers = { Referer: meta.referer ?? url.href };
const html = await this.fetcher.text(ctx, url, { headers });

View file

@ -1,5 +1,5 @@
import crypto from 'crypto';
import { Context, Format, Meta, UrlResult } from '../types';
import { Context, Format, InternalUrlResult, Meta } from '../types';
import { guessHeightFromPlaylist } from '../utils';
import { Extractor } from './Extractor';
@ -43,7 +43,7 @@ export class KinoGer extends Extractor {
return new URL(`${url.origin}/api/v1/video?id=${url.hash.slice(1)}`);
}
protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise<UrlResult[]> {
protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise<InternalUrlResult[]> {
const headers = {
'Origin': url.origin,
'Referer': url.origin + '/',
@ -66,8 +66,6 @@ export class KinoGer extends Extractor {
{
url: m3u8Url,
format: Format.hls,
label: this.label,
ttl: this.ttl,
meta: {
...meta,
height: meta.height ?? await guessHeightFromPlaylist(ctx, this.fetcher, m3u8Url, { headers }),

View file

@ -1,7 +1,7 @@
import bytes from 'bytes';
import * as cheerio from 'cheerio';
import { NotFoundError } from '../error';
import { Context, Format, Meta, UrlResult } from '../types';
import { Context, Format, InternalUrlResult, Meta } from '../types';
import {
buildMediaFlowProxyExtractorStreamUrl,
supportsMediaFlowProxy,
@ -34,7 +34,7 @@ export class LuluStream extends Extractor {
return new URL(`/e/${videoId}`, url);
}
protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise<UrlResult[]> {
protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise<InternalUrlResult[]> {
const headers = { Referer: meta.referer ?? url.href };
const fileUrl = new URL(url.href.replace('/e/', '/d/'));
@ -55,8 +55,6 @@ export class LuluStream extends Extractor {
{
url: playlistUrl,
format: Format.hls,
label: this.label,
ttl: this.ttl,
meta: {
...meta,
title,

View file

@ -1,7 +1,7 @@
import bytes from 'bytes';
import * as cheerio from 'cheerio';
import { NotFoundError } from '../error';
import { Context, Format, Meta, UrlResult } from '../types';
import { Context, Format, InternalUrlResult, Meta } from '../types';
import { buildMediaFlowProxyExtractorRedirectUrl, supportsMediaFlowProxy } from '../utils';
import { Extractor } from './Extractor';
@ -18,7 +18,7 @@ export class Mixdrop extends Extractor {
public override readonly normalize = (url: URL): URL => new URL(url.href.replace('/f/', '/e/'));
protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise<UrlResult[]> {
protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise<InternalUrlResult[]> {
const fileUrl = new URL(url.href.replace('/e/', '/f/'));
const html = await this.fetcher.text(ctx, fileUrl);
@ -35,8 +35,6 @@ export class Mixdrop extends Extractor {
{
url: buildMediaFlowProxyExtractorRedirectUrl(ctx, 'Mixdrop', url),
format: Format.mp4,
label: this.label,
ttl: this.ttl,
meta: {
...meta,
bytes: bytes.parse((sizeMatch[1] as string).replace(',', '')) as number,

View file

@ -1,5 +1,5 @@
import { BlockedError } from '../error';
import { BlockedReason, Context, Format, Meta, UrlResult } from '../types';
import { BlockedReason, Context, Format, InternalUrlResult, Meta } from '../types';
import { guessHeightFromPlaylist } from '../utils';
import { Extractor } from './Extractor';
@ -20,7 +20,7 @@ export class RgShows extends Extractor {
return null !== url.host.match(/rgshows/);
}
protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise<UrlResult[]> {
protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise<InternalUrlResult[]> {
const headers = { 'Referer': 'https://www.rgshows.ru/', 'Origin': 'https://www.rgshows.ru', 'User-Agent': 'Mozilla' };
const data = await this.fetcher.json(ctx, url, { headers }) as RgShowsApiData;
@ -38,8 +38,6 @@ export class RgShows extends Extractor {
{
url: streamUrl,
format: isMp4 ? Format.mp4 : (isHls ? Format.hls : /* istanbul ignore next */ Format.unknown),
label: this.label,
ttl: this.ttl,
meta: {
...meta,
...(isHls && { height: meta.height ?? await guessHeightFromPlaylist(ctx, this.fetcher, streamUrl, { headers }) }),

View file

@ -1,6 +1,6 @@
import * as cheerio from 'cheerio';
import { NotFoundError } from '../error';
import { Context, Format, Meta, UrlResult } from '../types';
import { Context, Format, InternalUrlResult, Meta } from '../types';
import { Extractor } from './Extractor';
export class SaveFiles extends Extractor {
@ -18,7 +18,7 @@ export class SaveFiles extends Extractor {
return new URL(url.href.replace('/e/', '/').replace('/d/', '/'));
}
protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise<UrlResult[]> {
protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise<InternalUrlResult[]> {
const headers = { Referer: meta.referer ?? url.href };
const html = await this.fetcher.text(ctx, url, { headers });
@ -37,8 +37,6 @@ export class SaveFiles extends Extractor {
{
url: new URL(fileMatch[1] as string),
format: Format.hls,
label: this.label,
ttl: this.ttl,
meta: {
...meta,
title,

View file

@ -1,5 +1,5 @@
import { NotFoundError } from '../error';
import { Context, Format, Meta, UrlResult } from '../types';
import { Context, Format, InternalUrlResult, Meta } from '../types';
import { Extractor } from './Extractor';
export class StreamEmbed extends Extractor {
@ -11,7 +11,7 @@ export class StreamEmbed extends Extractor {
return null !== url.host.match(/bullstream|mp4player|watch\.gxplayer/);
}
protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise<UrlResult[]> {
protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise<InternalUrlResult[]> {
const headers = { Referer: meta.referer ?? url.href };
const html = await this.fetcher.text(ctx, url, { headers });
@ -26,8 +26,6 @@ export class StreamEmbed extends Extractor {
{
url: new URL(`/m3u8/${video.uid}/${video.md5}/master.txt?s=1&id=${video.id}&cache=${video.status}`, url.origin),
format: Format.hls,
label: this.label,
ttl: this.ttl,
meta: {
...meta,
height: parseInt(JSON.parse(video.quality)[0]),

View file

@ -1,5 +1,5 @@
import * as cheerio from 'cheerio';
import { Context, Format, Meta, UrlResult } from '../types';
import { Context, Format, InternalUrlResult, Meta } from '../types';
import { guessHeightFromPlaylist } from '../utils';
import { Extractor } from './Extractor';
@ -21,7 +21,7 @@ export class StreamUp extends Extractor {
].includes(url.host);
}
protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise<UrlResult[]> {
protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise<InternalUrlResult[]> {
const headers = { Referer: `${url.origin}/`, Origin: url.origin };
const html = await this.fetcher.text(ctx, url, { headers });
@ -36,8 +36,6 @@ export class StreamUp extends Extractor {
{
url: playlistUrl,
format: Format.hls,
label: this.label,
ttl: this.ttl,
meta: {
...meta,
height: meta.height ?? await guessHeightFromPlaylist(ctx, this.fetcher, playlistUrl),

View file

@ -1,10 +1,7 @@
import bytes from 'bytes';
import * as cheerio from 'cheerio';
import { Context, Format, Meta, UrlResult } from '../types';
import {
buildMediaFlowProxyExtractorRedirectUrl,
supportsMediaFlowProxy,
} from '../utils';
import { Context, Format, InternalUrlResult, Meta } from '../types';
import { buildMediaFlowProxyExtractorRedirectUrl, supportsMediaFlowProxy } from '../utils';
import { Extractor } from './Extractor';
export class Streamtape extends Extractor {
@ -45,7 +42,7 @@ export class Streamtape extends Extractor {
return new URL(url.href.replace('/e/', '/v/'));
}
protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise<UrlResult[]> {
protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise<InternalUrlResult[]> {
const headers = { Referer: meta.referer ?? url.href };
// Only needed to properly find non-existing files via 404 response
@ -62,8 +59,6 @@ export class Streamtape extends Extractor {
{
url: buildMediaFlowProxyExtractorRedirectUrl(ctx, 'Streamtape', url, headers),
format: Format.mp4,
label: this.label,
ttl: this.ttl,
meta: {
...meta,
title,

View file

@ -1,7 +1,7 @@
import bytes from 'bytes';
import * as cheerio from 'cheerio';
import { NotFoundError } from '../error';
import { Context, Format, Meta, UrlResult } from '../types';
import { Context, Format, InternalUrlResult, Meta } from '../types';
import { extractUrlFromPacked, guessHeightFromPlaylist } from '../utils';
import { Extractor } from './Extractor';
@ -20,7 +20,7 @@ export class SuperVideo extends Extractor {
return new URL(url.href.replace('/e/', '/').replace('/k/', '/').replace('/embed-', '/'));
}
protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise<UrlResult[]> {
protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise<InternalUrlResult[]> {
const headers = { Referer: meta.referer ?? url.href };
const html = await this.fetcher.text(ctx, url, { headers });
@ -48,8 +48,6 @@ export class SuperVideo extends Extractor {
{
url: m3u8Url,
format: Format.hls,
label: this.label,
ttl: this.ttl,
meta: {
...meta,
title,

View file

@ -1,6 +1,6 @@
import * as cheerio from 'cheerio';
import { NotFoundError } from '../error';
import { Context, Format, Meta, UrlResult } from '../types';
import { Context, Format, InternalUrlResult, Meta } from '../types';
import { buildMediaFlowProxyExtractorRedirectUrl, supportsMediaFlowProxy } from '../utils';
import { Extractor } from './Extractor';
@ -19,7 +19,7 @@ export class Uqload extends Extractor {
return new URL(url.href.replace('/embed-', '/'));
}
protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise<UrlResult[]> {
protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise<InternalUrlResult[]> {
const html = await this.fetcher.text(ctx, url);
if (/File Not Found/.test(html)) {
@ -35,8 +35,6 @@ export class Uqload extends Extractor {
{
url: buildMediaFlowProxyExtractorRedirectUrl(ctx, 'Uqload', url),
format: Format.mp4,
label: this.label,
ttl: this.ttl,
meta: {
...meta,
title,

View file

@ -1,6 +1,6 @@
import * as cheerio from 'cheerio';
import { BlockedError, TooManyRequestsError } from '../error';
import { Context, Format, Meta, NonEmptyArray, UrlResult } from '../types';
import { Context, Format, InternalUrlResult, Meta, NonEmptyArray } from '../types';
import { Fetcher, guessHeightFromPlaylist } from '../utils';
import { Extractor } from './Extractor';
@ -23,7 +23,7 @@ export class VidSrc extends Extractor {
return null !== url.host.match(/vidsrc|vsrc/);
}
protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise<UrlResult[]> {
protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise<InternalUrlResult[]> {
// While this is a crappy thing to do, they seem to be blocking overly strict IMO
const randomIp = `${Math.floor(Math.random() * 223) + 1}.${Math.floor(Math.random() * 256)}.${Math.floor(Math.random() * 256)}.${Math.floor(Math.random() * 256)}`;
const newCtx = { ...ctx, ip: randomIp };
@ -31,7 +31,7 @@ export class VidSrc extends Extractor {
return this.extractUsingRandomDomain(newCtx, url, meta, [...this.domains]);
};
private async extractUsingRandomDomain(ctx: Context, url: URL, meta: Meta, domains: string[]): Promise<UrlResult[]> {
private async extractUsingRandomDomain(ctx: Context, url: URL, meta: Meta, domains: string[]): Promise<InternalUrlResult[]> {
const domainIndex = Math.floor(Math.random() * domains.length);
const [domain] = domains.splice(domainIndex, 1) as [string];
@ -74,7 +74,6 @@ export class VidSrc extends Extractor {
url: m3u8Url,
format: Format.hls,
label: serverName,
ttl: this.ttl,
meta: {
...meta,
height: await guessHeightFromPlaylist(ctx, this.fetcher, m3u8Url, { headers: { Referer: iframeUrl.href } }),

View file

@ -1,5 +1,5 @@
import * as cheerio from 'cheerio';
import { Context, Format, Meta, UrlResult } from '../types';
import { Context, Format, InternalUrlResult, Meta } from '../types';
import { extractUrlFromPacked, guessHeightFromPlaylist } from '../utils';
import { Extractor } from './Extractor';
@ -18,7 +18,7 @@ export class Vidora extends Extractor {
return new URL(url.href.replace('/embed/', '/'));
}
protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise<UrlResult[]> {
protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise<InternalUrlResult[]> {
const html = await this.fetcher.text(ctx, url);
const $ = cheerio.load(html);
@ -31,8 +31,6 @@ export class Vidora extends Extractor {
{
url: m3u8Url,
format: Format.hls,
label: this.label,
ttl: this.ttl,
meta: {
...meta,
height: meta.height ?? await guessHeightFromPlaylist(ctx, this.fetcher, m3u8Url, { headers }),

View file

@ -1,4 +1,4 @@
import { Context, CountryCode, Format, Meta, UrlResult } from '../types';
import { Context, CountryCode, Format, InternalUrlResult, Meta } from '../types';
import { CustomRequestConfig, guessHeightFromPlaylist, hasMultiEnabled, iso639FromCountryCode } from '../utils';
import { Extractor } from './Extractor';
@ -13,7 +13,7 @@ export class VixSrc extends Extractor {
return null !== url.host.match(/vixsrc/);
}
protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise<UrlResult[]> {
protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise<InternalUrlResult[]> {
const headers = { Referer: url.href };
const html = await this.fetcher.text(ctx, url);
@ -38,8 +38,6 @@ export class VixSrc extends Extractor {
{
url: playlistUrl,
format: Format.hls,
label: this.label,
ttl: this.ttl,
meta: {
...meta,
countryCodes,

View file

@ -1,7 +1,7 @@
import bytes from 'bytes';
import * as cheerio from 'cheerio';
import { NotFoundError } from '../error';
import { Context, Format, Meta, UrlResult } from '../types';
import { Context, Format, InternalUrlResult, Meta } from '../types';
import {
buildMediaFlowProxyExtractorStreamUrl, guessHeightFromPlaylist,
supportsMediaFlowProxy,
@ -125,7 +125,7 @@ export class Voe extends Extractor {
return new URL(`/${url.pathname.replace(/\/+$/, '').split('/').at(-1)}`, url);
}
protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise<UrlResult[]> {
protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise<InternalUrlResult[]> {
const headers = { Referer: meta.referer ?? url.href };
let html: string;
@ -167,8 +167,6 @@ export class Voe extends Extractor {
{
url: playlistUrl,
format: Format.hls,
label: this.label,
ttl: this.ttl,
meta: {
...meta,
height,

View file

@ -1,4 +1,4 @@
import { Context, Format, Meta, UrlResult } from '../types';
import { Context, Format, InternalUrlResult, Meta } from '../types';
import { Extractor } from './Extractor';
export class YouTube extends Extractor {
@ -12,7 +12,7 @@ export class YouTube extends Extractor {
return null !== url.host.match(/youtube/) && url.searchParams.has('v');
}
protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise<UrlResult[]> {
protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise<InternalUrlResult[]> {
const headers = { Referer: meta.referer ?? url.href };
const html = await this.fetcher.text(ctx, url, { headers });
@ -24,8 +24,6 @@ export class YouTube extends Extractor {
url,
format: Format.unknown,
ytId: url.searchParams.get('v') as string,
label: this.label,
ttl: this.ttl,
meta: {
...meta,
title: titleMatch[1] as string,

View file

@ -93,6 +93,17 @@ export enum Format {
unknown = 'unknown',
}
export interface InternalUrlResult {
url: URL;
format: Format;
isExternal?: boolean;
ytId?: string;
error?: unknown;
label?: string;
meta?: Meta;
requestHeaders?: Record<string, string>;
}
export interface UrlResult {
url: URL;
format: Format;