From 355521f1a6622db389be29d0505799cc82ce7966 Mon Sep 17 00:00:00 2001 From: GSTAR <44748406+GLlgGL@users.noreply.github.com> Date: Wed, 19 Nov 2025 22:41:33 +0100 Subject: [PATCH 1/7] Create Vidmoly.ts --- src/extractor/Vidmoly.ts | 85 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 src/extractor/Vidmoly.ts diff --git a/src/extractor/Vidmoly.ts b/src/extractor/Vidmoly.ts new file mode 100644 index 0000000..b619694 --- /dev/null +++ b/src/extractor/Vidmoly.ts @@ -0,0 +1,85 @@ +import { NotFoundError } from '../error'; +import { Context, Format, Meta, UrlResult } from '../types'; +import { Extractor } from './Extractor'; +import { + buildMediaFlowProxyExtractorStreamUrl, + supportsMediaFlowProxy, + guessHeightFromPlaylist +} from '../utils'; +import * as cheerio from 'cheerio'; + +export class Vidmoly extends Extractor { + public readonly id = 'Vidmoly'; + public readonly label = 'VidMoly(MFP)'; + public override readonly ttl = 10800000; // 3h + public override viaMediaFlowProxy = true; + + private domains = ['vidmoly.me', 'vidmoly.to', 'vidmoly.net']; + + public supports(ctx: Context, url: URL): boolean { + return this.domains.some(d => url.host.includes(d)) && supportsMediaFlowProxy(ctx); + } + + // Normalize to embed URL + public override normalize(url: URL): URL { + let path = url.pathname.replace(/^\/+/, ''); + if (!path.startsWith('embed-')) path = `embed-${path}`; + if (!path.endsWith('.html')) path += '.html'; + return new URL(`${url.origin}/${path}`); + } + + protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise { + const headers: Record = { Referer: 'https://vidmoly.net' }; + + // --- 1. Fetch embed page for HLS --- + const embedHtml = await this.fetcher.text(ctx, url, headers); + if (!embedHtml || embedHtml.includes('Video not found')) throw new NotFoundError(); + + // Extract HLS URL from embed page + const sourcesMatch = embedHtml.match(/sources:\s*\[\{file:"(?[^"]+)"/); + const mediaUrl = sourcesMatch?.groups?.['url']; + + let height: number | undefined; + if (mediaUrl) { + const streamUrl = mediaUrl.startsWith('//') + ? 'https:' + mediaUrl + : mediaUrl.startsWith('/') + ? url.origin + mediaUrl + : mediaUrl; + + try { + // Pass the actual HLS URL (not MediaFlow) to get best height + height = await guessHeightFromPlaylist(ctx, this.fetcher, new URL(streamUrl), url, headers); + } catch { + // ignore + } + } + + // --- 2. Fetch main page for title --- + const mainUrl = url.href.replace('embed-', ''); + let title = this.label; + try { + const mainHtml = await this.fetcher.text(ctx, new URL(mainUrl), headers); + const $ = cheerio.load(mainHtml); + const vidDiv = $('div.vid-d'); + const innerSpan = vidDiv.find('span span').first(); + title = innerSpan.text().trim() || this.label; + title = title.replace(/\.mp4$/i, ''); + } catch { + // fallback + } + + // --- 3. MediaFlow proxy --- + const proxiedUrl = await buildMediaFlowProxyExtractorStreamUrl(ctx, this.fetcher, this.id, url, headers); + + return [{ + url: proxiedUrl, + format: Format.hls, + label: this.label, + sourceId: `${this.id}_${meta.countryCodes?.join('_') ?? 'all'}`, + ttl: this.ttl, + requestHeaders: headers, + meta: { ...meta, title, height }, + }]; + } +} From 7b8f1d1742fce1b04b9e576b8193b4ef6831b84f Mon Sep 17 00:00:00 2001 From: GSTAR <44748406+GLlgGL@users.noreply.github.com> Date: Wed, 19 Nov 2025 22:46:05 +0100 Subject: [PATCH 2/7] Update Vidmoly.ts --- src/extractor/Vidmoly.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/extractor/Vidmoly.ts b/src/extractor/Vidmoly.ts index b619694..d43cd57 100644 --- a/src/extractor/Vidmoly.ts +++ b/src/extractor/Vidmoly.ts @@ -29,7 +29,9 @@ export class Vidmoly extends Extractor { } protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise { - const headers: Record = { Referer: 'https://vidmoly.net' }; + + const referer = meta.referer ?? url.href; + const headers: Record = { Referer: referer }; // --- 1. Fetch embed page for HLS --- const embedHtml = await this.fetcher.text(ctx, url, headers); From 2fce1e1b6fb271b77d3d4ede0b72c5c1f04828ad Mon Sep 17 00:00:00 2001 From: GSTAR <44748406+GLlgGL@users.noreply.github.com> Date: Wed, 19 Nov 2025 22:49:43 +0100 Subject: [PATCH 3/7] Update index.ts From 6191dceeeaf6252c2c99b21b2c81c3a33b8af425 Mon Sep 17 00:00:00 2001 From: GSTAR <44748406+GLlgGL@users.noreply.github.com> Date: Fri, 28 Nov 2025 17:27:53 +0100 Subject: [PATCH 4/7] Update Vidmoly.ts --- src/extractor/Vidmoly.ts | 110 ++++++++++++++++++++++----------------- 1 file changed, 62 insertions(+), 48 deletions(-) diff --git a/src/extractor/Vidmoly.ts b/src/extractor/Vidmoly.ts index d43cd57..5bd5bec 100644 --- a/src/extractor/Vidmoly.ts +++ b/src/extractor/Vidmoly.ts @@ -1,12 +1,12 @@ +import * as cheerio from 'cheerio'; import { NotFoundError } from '../error'; import { Context, Format, Meta, UrlResult } from '../types'; import { Extractor } from './Extractor'; import { buildMediaFlowProxyExtractorStreamUrl, + guessHeightFromPlaylist, supportsMediaFlowProxy, - guessHeightFromPlaylist } from '../utils'; -import * as cheerio from 'cheerio'; export class Vidmoly extends Extractor { public readonly id = 'Vidmoly'; @@ -29,59 +29,73 @@ export class Vidmoly extends Extractor { } protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise { + const referer = meta.referer ?? url.href; + const headers: Record = { Referer: referer }; - const referer = meta.referer ?? url.href; - const headers: Record = { Referer: referer }; + // --- 1. Fetch embed page --- + const embedHtml = await this.fetcher.text(ctx, url, headers); + if (!embedHtml || embedHtml.includes('Video not found')) throw new NotFoundError(); - // --- 1. Fetch embed page for HLS --- - const embedHtml = await this.fetcher.text(ctx, url, headers); - if (!embedHtml || embedHtml.includes('Video not found')) throw new NotFoundError(); + // Extract HLS URL + const sourcesMatch = embedHtml.match(/sources:\s*\[\{file:"(?[^"]+)"/); + const mediaUrl = sourcesMatch?.groups?.url; - // Extract HLS URL from embed page - const sourcesMatch = embedHtml.match(/sources:\s*\[\{file:"(?[^"]+)"/); - const mediaUrl = sourcesMatch?.groups?.['url']; + let height: number | undefined; - let height: number | undefined; - if (mediaUrl) { - const streamUrl = mediaUrl.startsWith('//') - ? 'https:' + mediaUrl - : mediaUrl.startsWith('/') - ? url.origin + mediaUrl - : mediaUrl; + if (mediaUrl) { + const streamUrl = mediaUrl.startsWith('//') + ? 'https:' + mediaUrl + : mediaUrl.startsWith('/') + ? url.origin + mediaUrl + : mediaUrl; + + try { + height = await guessHeightFromPlaylist( + ctx, + this.fetcher, + new URL(streamUrl), + url, + headers, + ); + } catch { + // ignore + } + } + + // --- 2. Fetch main page for title --- + const mainUrl = url.href.replace('embed-', ''); + let title = this.label; try { - // Pass the actual HLS URL (not MediaFlow) to get best height - height = await guessHeightFromPlaylist(ctx, this.fetcher, new URL(streamUrl), url, headers); + const mainHtml = await this.fetcher.text(ctx, new URL(mainUrl), headers); + const $ = cheerio.load(mainHtml); + const vidDiv = $('div.vid-d'); + const innerSpan = vidDiv.find('span span').first(); + title = innerSpan.text().trim() || this.label; + title = title.replace(/\.mp4$/i, ''); } catch { - // ignore + // fallback } + + // --- 3. MediaFlow proxy --- + const proxiedUrl = await buildMediaFlowProxyExtractorStreamUrl( + ctx, + this.fetcher, + this.id, + url, + headers, + ); + + return [ + { + url: proxiedUrl, + format: Format.hls, + label: this.label, + sourceId: `${this.id}_${meta.countryCodes?.join('_') ?? 'all'}`, + ttl: this.ttl, + requestHeaders: headers, + meta: { ...meta, title, height }, + }, + ]; } - - // --- 2. Fetch main page for title --- - const mainUrl = url.href.replace('embed-', ''); - let title = this.label; - try { - const mainHtml = await this.fetcher.text(ctx, new URL(mainUrl), headers); - const $ = cheerio.load(mainHtml); - const vidDiv = $('div.vid-d'); - const innerSpan = vidDiv.find('span span').first(); - title = innerSpan.text().trim() || this.label; - title = title.replace(/\.mp4$/i, ''); - } catch { - // fallback - } - - // --- 3. MediaFlow proxy --- - const proxiedUrl = await buildMediaFlowProxyExtractorStreamUrl(ctx, this.fetcher, this.id, url, headers); - - return [{ - url: proxiedUrl, - format: Format.hls, - label: this.label, - sourceId: `${this.id}_${meta.countryCodes?.join('_') ?? 'all'}`, - ttl: this.ttl, - requestHeaders: headers, - meta: { ...meta, title, height }, - }]; - } } From ae00b2f5b50fef4cb1e3af74b984dc345ca4dbf2 Mon Sep 17 00:00:00 2001 From: GSTAR <44748406+GLlgGL@users.noreply.github.com> Date: Fri, 28 Nov 2025 17:30:54 +0100 Subject: [PATCH 5/7] Update Vidmoly.ts --- src/extractor/Vidmoly.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/extractor/Vidmoly.ts b/src/extractor/Vidmoly.ts index 5bd5bec..0a6e1d3 100644 --- a/src/extractor/Vidmoly.ts +++ b/src/extractor/Vidmoly.ts @@ -1,12 +1,12 @@ import * as cheerio from 'cheerio'; import { NotFoundError } from '../error'; import { Context, Format, Meta, UrlResult } from '../types'; -import { Extractor } from './Extractor'; import { buildMediaFlowProxyExtractorStreamUrl, guessHeightFromPlaylist, supportsMediaFlowProxy, } from '../utils'; +import { Extractor } from './Extractor'; export class Vidmoly extends Extractor { public readonly id = 'Vidmoly'; From 6143a37d029f90430a38202429bdbb35a961b607 Mon Sep 17 00:00:00 2001 From: GSTAR <44748406+GLlgGL@users.noreply.github.com> Date: Fri, 28 Nov 2025 17:32:32 +0100 Subject: [PATCH 6/7] Update Vidmoly.ts --- src/extractor/Vidmoly.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/extractor/Vidmoly.ts b/src/extractor/Vidmoly.ts index 0a6e1d3..f6ee10a 100644 --- a/src/extractor/Vidmoly.ts +++ b/src/extractor/Vidmoly.ts @@ -38,7 +38,7 @@ export class Vidmoly extends Extractor { // Extract HLS URL const sourcesMatch = embedHtml.match(/sources:\s*\[\{file:"(?[^"]+)"/); - const mediaUrl = sourcesMatch?.groups?.url; + const mediaUrl = sourcesMatch?.groups?.['url']; let height: number | undefined; From d16fc83c24302d8f08c8bbfc297878c7f6bb5767 Mon Sep 17 00:00:00 2001 From: GSTAR <44748406+GLlgGL@users.noreply.github.com> Date: Mon, 1 Dec 2025 09:44:53 +0100 Subject: [PATCH 7/7] Update Vidmoly.ts Give to Caesar what belongs to Caesar. --- src/extractor/Vidmoly.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/extractor/Vidmoly.ts b/src/extractor/Vidmoly.ts index f6ee10a..52daf41 100644 --- a/src/extractor/Vidmoly.ts +++ b/src/extractor/Vidmoly.ts @@ -8,6 +8,7 @@ import { } from '../utils'; import { Extractor } from './Extractor'; +/** @see https://github.com/Gujal00/ResolveURL/blob/master/script.module.resolveurl/lib/resolveurl/plugins/vidmoly.py */ export class Vidmoly extends Extractor { public readonly id = 'Vidmoly'; public readonly label = 'VidMoly(MFP)';