From ede86bf924df0946ec27837d89fad95556cdf247 Mon Sep 17 00:00:00 2001 From: GSTAR <44748406+GLlgGL@users.noreply.github.com> Date: Tue, 28 Oct 2025 16:36:30 +0100 Subject: [PATCH 01/33] Create TurboVIPlay.ts --- src/extractor/TurboVIPlay.ts | 116 +++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 src/extractor/TurboVIPlay.ts diff --git a/src/extractor/TurboVIPlay.ts b/src/extractor/TurboVIPlay.ts new file mode 100644 index 0000000..1930e74 --- /dev/null +++ b/src/extractor/TurboVIPlay.ts @@ -0,0 +1,116 @@ +import * as cheerio from 'cheerio'; +import { NotFoundError } from '../error'; +import { Context, Format, Meta, UrlResult } from '../types'; +import { Extractor } from './Extractor'; +import { guessHeightFromPlaylist } from '../utils/height'; + +export class TurboVIPlay extends Extractor { + public readonly id = 'turboviplay'; + public readonly label = 'TurboVIPlay'; + public override readonly ttl: number = 10800000; // 3h + + private domains = [ + 'turboviplay.com', + 'emturbovid.com', + 'tuborstb.co', + 'javggvideo.xyz', + 'stbturbo.xyz', + 'turbovidhls.com', + ]; + + public supports(_ctx: Context, url: URL): boolean { + return this.domains.some((d) => url.host.includes(d)); + } + + public override normalize(url: URL): URL { + return new URL(url.href.replace(/\/(e|d|embed-)\//, '/t/')); + } + + protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise { + const headers = { Referer: 'https://turbovidhls.com' }; + + const html = await this.fetcher.text(ctx, url, { headers }); + + if (!html || html.includes('File Not Found') || html.includes('Pending in queue')) { + throw new NotFoundError(); + } + + const match = html.match(/(?:urlPlay|data-hash)\s*=\s*['"](?[^"']+)/); + const mediaUrl = match?.groups?.['url']; + if (!mediaUrl) { + throw new NotFoundError('Video link not found'); + } + + let finalUrl = mediaUrl; + if (finalUrl.startsWith('//')) finalUrl = 'https:' + finalUrl; + else if (finalUrl.startsWith('/')) finalUrl = url.origin + finalUrl; + + const resolvedUrl = await this.followRedirect(ctx, finalUrl, headers); + + const playableUrl = await this.resolveHlsMaster(ctx, resolvedUrl, headers); + + console.log(`[TurboVIPlay] Final playable URL: ${playableUrl}`); + + let height: number | undefined; + try { + height = await guessHeightFromPlaylist(ctx, this.fetcher, new URL(playableUrl), { headers }); + } catch { + height = undefined; + } + + const $ = cheerio.load(html); + const title = $('title').text().trim() || this.label; + + return [ + { + url: new URL(playableUrl), + format: Format.hls, + label: this.label, + sourceId: `${this.id}_${meta.countryCodes?.join('_') ?? 'all'}`, + ttl: this.ttl, + requestHeaders: headers, + meta: { + ...meta, + title, + height, + }, + }, + ]; + } + + private async followRedirect(ctx: Context, link: string, headers: Record): Promise { + try { + const resp = await this.fetcher.head(ctx, new URL(link), { headers, redirect: 'follow' }); + if (resp && typeof resp['url'] === 'string') return String(resp['url']); + } catch { + try { + const resp = await this.fetcher.fetch(ctx, new URL(link), { headers, redirect: 'follow' }); + if (resp && typeof resp['url'] === 'string') return String(resp['url']); + } catch { + // ignore + } + } + return link; + } + + private async resolveHlsMaster(ctx: Context, url: string, headers: Record): Promise { + const text = await this.fetcher.text(ctx, new URL(url), { headers }); + if (!text || !text.includes('#EXT-X-STREAM-INF')) return url; + + const matches = Array.from( + text.matchAll(/#EXT-X-STREAM-INF:.*RESOLUTION=(\d+)x(\d+)[^\n]*\n([^\n]+)/g) + ); + + if (matches.length === 0) return url; + + matches.sort((a, b) => parseInt(b[2] ?? '0', 10) - parseInt(a[2] ?? '0', 10)); + + const best = matches[0]?.[3]; + if (!best) return url; + + const base = new URL(url); + if (best.startsWith('http')) return best; + if (best.startsWith('/')) return new URL(best, base.origin).href; + return new URL(best, base.href).href; + } +} -- 2.45.2 From ebf272af1745536a9a77bb83e7058197fffc932e Mon Sep 17 00:00:00 2001 From: GSTAR <44748406+GLlgGL@users.noreply.github.com> Date: Tue, 28 Oct 2025 20:00:42 +0100 Subject: [PATCH 02/33] Update TurboVIPlay.ts --- src/extractor/TurboVIPlay.ts | 62 ++++++++++-------------------------- 1 file changed, 16 insertions(+), 46 deletions(-) diff --git a/src/extractor/TurboVIPlay.ts b/src/extractor/TurboVIPlay.ts index 1930e74..34c8156 100644 --- a/src/extractor/TurboVIPlay.ts +++ b/src/extractor/TurboVIPlay.ts @@ -29,46 +29,52 @@ export class TurboVIPlay extends Extractor { protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise { const headers = { Referer: 'https://turbovidhls.com' }; + // Fetch HTML page const html = await this.fetcher.text(ctx, url, { headers }); - if (!html || html.includes('File Not Found') || html.includes('Pending in queue')) { throw new NotFoundError(); } + // Extract media URL from page const match = html.match(/(?:urlPlay|data-hash)\s*=\s*['"](?[^"']+)/); const mediaUrl = match?.groups?.['url']; if (!mediaUrl) { throw new NotFoundError('Video link not found'); } + // Normalize URL let finalUrl = mediaUrl; if (finalUrl.startsWith('//')) finalUrl = 'https:' + finalUrl; else if (finalUrl.startsWith('/')) finalUrl = url.origin + finalUrl; - const resolvedUrl = await this.followRedirect(ctx, finalUrl, headers); - - const playableUrl = await this.resolveHlsMaster(ctx, resolvedUrl, headers); - - console.log(`[TurboVIPlay] Final playable URL: ${playableUrl}`); + // Try to resolve redirect inline + try { + const resp = await this.fetcher.fetch(ctx, new URL(finalUrl), { headers, redirect: 'follow' }); + if (resp?.url) finalUrl = resp.url; + } catch { + // fallback to original finalUrl + } + // Optionally guess height from playlist let height: number | undefined; try { - height = await guessHeightFromPlaylist(ctx, this.fetcher, new URL(playableUrl), { headers }); + height = await guessHeightFromPlaylist(ctx, this.fetcher, new URL(finalUrl), { headers }); } catch { height = undefined; } + // Extract title const $ = cheerio.load(html); const title = $('title').text().trim() || this.label; return [ { - url: new URL(playableUrl), + url: new URL(finalUrl), format: Format.hls, label: this.label, sourceId: `${this.id}_${meta.countryCodes?.join('_') ?? 'all'}`, ttl: this.ttl, - requestHeaders: headers, + requestHeaders: headers, meta: { ...meta, title, @@ -77,40 +83,4 @@ export class TurboVIPlay extends Extractor { }, ]; } - - private async followRedirect(ctx: Context, link: string, headers: Record): Promise { - try { - const resp = await this.fetcher.head(ctx, new URL(link), { headers, redirect: 'follow' }); - if (resp && typeof resp['url'] === 'string') return String(resp['url']); - } catch { - try { - const resp = await this.fetcher.fetch(ctx, new URL(link), { headers, redirect: 'follow' }); - if (resp && typeof resp['url'] === 'string') return String(resp['url']); - } catch { - // ignore - } - } - return link; - } - - private async resolveHlsMaster(ctx: Context, url: string, headers: Record): Promise { - const text = await this.fetcher.text(ctx, new URL(url), { headers }); - if (!text || !text.includes('#EXT-X-STREAM-INF')) return url; - - const matches = Array.from( - text.matchAll(/#EXT-X-STREAM-INF:.*RESOLUTION=(\d+)x(\d+)[^\n]*\n([^\n]+)/g) - ); - - if (matches.length === 0) return url; - - matches.sort((a, b) => parseInt(b[2] ?? '0', 10) - parseInt(a[2] ?? '0', 10)); - - const best = matches[0]?.[3]; - if (!best) return url; - - const base = new URL(url); - if (best.startsWith('http')) return best; - if (best.startsWith('/')) return new URL(best, base.origin).href; - return new URL(best, base.href).href; - } -} +} \ No newline at end of file -- 2.45.2 From e201821fcd4befc71efbc094f5f2927a9dd7b710 Mon Sep 17 00:00:00 2001 From: GSTAR <44748406+GLlgGL@users.noreply.github.com> Date: Tue, 28 Oct 2025 22:36:39 +0100 Subject: [PATCH 03/33] Update TurboVIPlay.ts --- src/extractor/TurboVIPlay.ts | 104 +++++++++++++++++------------------ 1 file changed, 49 insertions(+), 55 deletions(-) diff --git a/src/extractor/TurboVIPlay.ts b/src/extractor/TurboVIPlay.ts index 34c8156..b1cf4a7 100644 --- a/src/extractor/TurboVIPlay.ts +++ b/src/extractor/TurboVIPlay.ts @@ -1,5 +1,4 @@ import * as cheerio from 'cheerio'; -import { NotFoundError } from '../error'; import { Context, Format, Meta, UrlResult } from '../types'; import { Extractor } from './Extractor'; import { guessHeightFromPlaylist } from '../utils/height'; @@ -27,60 +26,55 @@ export class TurboVIPlay extends Extractor { } protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise { - const headers = { Referer: 'https://turbovidhls.com' }; + // Fetch HTML page + const html = await this.fetcher.text(ctx, url); - // Fetch HTML page - const html = await this.fetcher.text(ctx, url, { headers }); - if (!html || html.includes('File Not Found') || html.includes('Pending in queue')) { - throw new NotFoundError(); - } - - // Extract media URL from page - const match = html.match(/(?:urlPlay|data-hash)\s*=\s*['"](?[^"']+)/); - const mediaUrl = match?.groups?.['url']; - if (!mediaUrl) { - throw new NotFoundError('Video link not found'); - } - - // Normalize URL - let finalUrl = mediaUrl; - if (finalUrl.startsWith('//')) finalUrl = 'https:' + finalUrl; - else if (finalUrl.startsWith('/')) finalUrl = url.origin + finalUrl; - - // Try to resolve redirect inline - try { - const resp = await this.fetcher.fetch(ctx, new URL(finalUrl), { headers, redirect: 'follow' }); - if (resp?.url) finalUrl = resp.url; - } catch { - // fallback to original finalUrl - } - - // Optionally guess height from playlist - let height: number | undefined; - try { - height = await guessHeightFromPlaylist(ctx, this.fetcher, new URL(finalUrl), { headers }); - } catch { - height = undefined; - } - - // Extract title - const $ = cheerio.load(html); - const title = $('title').text().trim() || this.label; - - return [ - { - url: new URL(finalUrl), - format: Format.hls, - label: this.label, - sourceId: `${this.id}_${meta.countryCodes?.join('_') ?? 'all'}`, - ttl: this.ttl, - requestHeaders: headers, - meta: { - ...meta, - title, - height, - }, - }, - ]; + // Extract media URL from page + const match = html.match(/(?:urlPlay|data-hash)\s*=\s*['"](?[^"']+)/); + const mediaUrl = match?.groups?.['url']; + if (!mediaUrl) { + throw new Error('Video link not found'); } + + // Normalize URL + let finalUrl = mediaUrl; + if (finalUrl.startsWith('//')) finalUrl = 'https:' + finalUrl; + else if (finalUrl.startsWith('/')) finalUrl = url.origin + finalUrl; + + // Try to resolve redirect inline + try { + const resp = await this.fetcher.fetch(ctx, new URL(finalUrl)); + if (resp?.url) finalUrl = resp.url; + } catch { + // fallback to original finalUrl + } + + // Optionally guess height from playlist + let height: number | undefined; + try { + height = await guessHeightFromPlaylist(ctx, this.fetcher, new URL(finalUrl)); + } catch { + height = undefined; + } + + // Extract title + const $ = cheerio.load(html); + const title = $('title').text().trim() || this.label; + + return [ + { + url: new URL(finalUrl), + format: Format.hls, + label: this.label, + sourceId: `${this.id}_${meta.countryCodes?.join('_') ?? 'all'}`, + ttl: this.ttl, + requestHeaders: { Referer: url.origin }, + meta: { + ...meta, + title, + height, + }, + }, + ]; +} } \ No newline at end of file -- 2.45.2 From 9d90f121643b4279e813a18fa0265deed722f4bb Mon Sep 17 00:00:00 2001 From: GSTAR <44748406+GLlgGL@users.noreply.github.com> Date: Wed, 29 Oct 2025 12:55:55 +0100 Subject: [PATCH 04/33] Update TurboVIPlay.ts --- src/extractor/TurboVIPlay.ts | 96 ++++++++++++++++++++---------------- 1 file changed, 54 insertions(+), 42 deletions(-) diff --git a/src/extractor/TurboVIPlay.ts b/src/extractor/TurboVIPlay.ts index b1cf4a7..deeb400 100644 --- a/src/extractor/TurboVIPlay.ts +++ b/src/extractor/TurboVIPlay.ts @@ -1,4 +1,5 @@ import * as cheerio from 'cheerio'; +import { NotFoundError } from '../error'; import { Context, Format, Meta, UrlResult } from '../types'; import { Extractor } from './Extractor'; import { guessHeightFromPlaylist } from '../utils/height'; @@ -6,7 +7,7 @@ import { guessHeightFromPlaylist } from '../utils/height'; export class TurboVIPlay extends Extractor { public readonly id = 'turboviplay'; public readonly label = 'TurboVIPlay'; - public override readonly ttl: number = 10800000; // 3h + public override readonly ttl = 10800000; // 3h private domains = [ 'turboviplay.com', @@ -26,55 +27,66 @@ export class TurboVIPlay extends Extractor { } protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise { - // Fetch HTML page - const html = await this.fetcher.text(ctx, url); + const headers = { Referer: url.origin }; - // Extract media URL from page - const match = html.match(/(?:urlPlay|data-hash)\s*=\s*['"](?[^"']+)/); - const mediaUrl = match?.groups?.['url']; - if (!mediaUrl) { - throw new Error('Video link not found'); - } + // Fetch HTML page + const html = await this.fetcher.text(ctx, url, { headers }); + if (!html || html.includes('File Not Found') || html.includes('Pending in queue')) { + throw new NotFoundError(); + } - // Normalize URL - let finalUrl = mediaUrl; - if (finalUrl.startsWith('//')) finalUrl = 'https:' + finalUrl; - else if (finalUrl.startsWith('/')) finalUrl = url.origin + finalUrl; + // Extract media URL + const match = html.match(/(?:urlPlay|data-hash)\s*=\s*['"](?[^"']+)/); + const mediaUrl = match?.groups?.['url']; + if (!mediaUrl) throw new NotFoundError('Video link not found'); - // Try to resolve redirect inline - try { - const resp = await this.fetcher.fetch(ctx, new URL(finalUrl)); - if (resp?.url) finalUrl = resp.url; - } catch { - // fallback to original finalUrl - } + // Normalize URL + const masterUrl = mediaUrl.startsWith('//') ? 'https:' + mediaUrl : + mediaUrl.startsWith('/') ? url.origin + mediaUrl : + mediaUrl; - // Optionally guess height from playlist - let height: number | undefined; - try { - height = await guessHeightFromPlaylist(ctx, this.fetcher, new URL(finalUrl)); - } catch { - height = undefined; - } + let hlsUrl = masterUrl; - // Extract title - const $ = cheerio.load(html); - const title = $('title').text().trim() || this.label; + // Parse master playlist to pick highest resolution variant + try { + const playlistText = await this.fetcher.text(ctx, new URL(masterUrl), { headers }); + if (playlistText.includes('#EXT-X-STREAM-INF')) { + const variants = Array.from( + playlistText.matchAll(/#EXT-X-STREAM-INF:.*RESOLUTION=(\d+)x(\d+)[^\n]*\n([^\n]+)/g) + ); + if (variants.length) { + variants.sort((a, b) => parseInt(b[2] ?? '0', 10) - parseInt(a[2] ?? '0', 10)); + const best = variants[0]?.[3]; + if (best) { + const base = new URL(masterUrl); + hlsUrl = best.startsWith('http') ? best : + best.startsWith('/') ? new URL(best, base.origin).href : + new URL(best, base.href).href; + } + } + } + } catch { + // fallback to masterUrl + } - return [ - { - url: new URL(finalUrl), + // Guess height from master playlist + let height: number | undefined; + try { + height = await guessHeightFromPlaylist(ctx, this.fetcher, new URL(masterUrl), { headers }); + } catch {} + + // Extract title + const $ = cheerio.load(html); + const title = $('title').text().trim() || this.label; + + return [{ + url: new URL(hlsUrl), format: Format.hls, label: this.label, sourceId: `${this.id}_${meta.countryCodes?.join('_') ?? 'all'}`, ttl: this.ttl, - requestHeaders: { Referer: url.origin }, - meta: { - ...meta, - title, - height, - }, - }, - ]; + requestHeaders: headers, + meta: { ...meta, title, height }, + }]; + } } -} \ No newline at end of file -- 2.45.2 From 70c66ce4f9a18dcfdca30e67d10197602ee699d3 Mon Sep 17 00:00:00 2001 From: GSTAR <44748406+GLlgGL@users.noreply.github.com> Date: Fri, 31 Oct 2025 21:32:14 +0100 Subject: [PATCH 05/33] Update TurboVIPlay.ts --- src/extractor/TurboVIPlay.ts | 119 +++++++++++++++++++---------------- 1 file changed, 65 insertions(+), 54 deletions(-) diff --git a/src/extractor/TurboVIPlay.ts b/src/extractor/TurboVIPlay.ts index deeb400..f324e60 100644 --- a/src/extractor/TurboVIPlay.ts +++ b/src/extractor/TurboVIPlay.ts @@ -26,67 +26,78 @@ export class TurboVIPlay extends Extractor { return new URL(url.href.replace(/\/(e|d|embed-)\//, '/t/')); } - protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise { - const headers = { Referer: url.origin }; + protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise { + const headers: Record = { Referer: url.origin }; - // Fetch HTML page - const html = await this.fetcher.text(ctx, url, { headers }); - if (!html || html.includes('File Not Found') || html.includes('Pending in queue')) { - throw new NotFoundError(); - } - // Extract media URL - const match = html.match(/(?:urlPlay|data-hash)\s*=\s*['"](?[^"']+)/); - const mediaUrl = match?.groups?.['url']; - if (!mediaUrl) throw new NotFoundError('Video link not found'); + // Fetch HTML page + const html = await this.fetcher.text(ctx, url, headers); + if (!html || html.includes('File Not Found') || html.includes('Pending in queue')) { + throw new NotFoundError(); + } - // Normalize URL - const masterUrl = mediaUrl.startsWith('//') ? 'https:' + mediaUrl : - mediaUrl.startsWith('/') ? url.origin + mediaUrl : - mediaUrl; + // Extract media URL + const match = html.match(/(?:urlPlay|data-hash)\s*=\s*['"](?[^"']+)/); + const mediaUrl = match?.groups?.['url']; + if (!mediaUrl) throw new NotFoundError('Video link not found'); - let hlsUrl = masterUrl; + // Normalize URL + const masterUrl = mediaUrl.startsWith('//') + ? 'https:' + mediaUrl + : mediaUrl.startsWith('/') + ? url.origin + mediaUrl + : mediaUrl; - // Parse master playlist to pick highest resolution variant - try { - const playlistText = await this.fetcher.text(ctx, new URL(masterUrl), { headers }); - if (playlistText.includes('#EXT-X-STREAM-INF')) { - const variants = Array.from( - playlistText.matchAll(/#EXT-X-STREAM-INF:.*RESOLUTION=(\d+)x(\d+)[^\n]*\n([^\n]+)/g) - ); - if (variants.length) { - variants.sort((a, b) => parseInt(b[2] ?? '0', 10) - parseInt(a[2] ?? '0', 10)); - const best = variants[0]?.[3]; - if (best) { - const base = new URL(masterUrl); - hlsUrl = best.startsWith('http') ? best : - best.startsWith('/') ? new URL(best, base.origin).href : - new URL(best, base.href).href; - } + let hlsUrl = masterUrl; + + // Parse master playlist to pick highest resolution variant + try { + const playlistText = await this.fetcher.text(ctx, new URL(masterUrl), headers); + + if (playlistText.includes('#EXT-X-STREAM-INF')) { + const variants = Array.from( + playlistText.matchAll(/#EXT-X-STREAM-INF:.*RESOLUTION=(\d+)x(\d+)[^\n]*\n([^\n]+)/g) + ); + + if (variants.length) { + // Sort by height descending + variants.sort((a, b) => parseInt(b[2] ?? '0', 10) - parseInt(a[2] ?? '0', 10)); + const best = variants[0]?.[3]; + + if (best) { + const base = new URL(masterUrl); + hlsUrl = best.startsWith('http') + ? best + : best.startsWith('/') + ? new URL(best, base.origin).href + : new URL(best, base.href).href; } } - } catch { - // fallback to masterUrl } - - // Guess height from master playlist - let height: number | undefined; - try { - height = await guessHeightFromPlaylist(ctx, this.fetcher, new URL(masterUrl), { headers }); - } catch {} - - // Extract title - const $ = cheerio.load(html); - const title = $('title').text().trim() || this.label; - - return [{ - url: new URL(hlsUrl), - format: Format.hls, - label: this.label, - sourceId: `${this.id}_${meta.countryCodes?.join('_') ?? 'all'}`, - ttl: this.ttl, - requestHeaders: headers, - meta: { ...meta, title, height }, - }]; + } catch { + // fallback to masterUrl } + + // Guess height from master playlist + let height: number | undefined; + try { + height = await guessHeightFromPlaylist(ctx, this.fetcher, new URL(masterUrl), new URL(masterUrl), headers); + } catch { + // ignore + } + + // Extract title + const $ = cheerio.load(html); + const title = $('title').text().trim() || this.label; + + return [{ + url: new URL(hlsUrl), + format: Format.hls, + label: this.label, + sourceId: `${this.id}_${meta.countryCodes?.join('_') ?? 'all'}`, + ttl: this.ttl, + requestHeaders: headers, + meta: { ...meta, title, height }, + }]; } +} \ No newline at end of file -- 2.45.2 From cc8153de12467397ba8fdbeba678d8b3435da95c Mon Sep 17 00:00:00 2001 From: GSTAR <44748406+GLlgGL@users.noreply.github.com> Date: Wed, 5 Nov 2025 10:26:59 +0100 Subject: [PATCH 06/33] Create DiziShqip.ts --- src/source/DiziShqip.ts | 101 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 src/source/DiziShqip.ts diff --git a/src/source/DiziShqip.ts b/src/source/DiziShqip.ts new file mode 100644 index 0000000..4e4a5d7 --- /dev/null +++ b/src/source/DiziShqip.ts @@ -0,0 +1,101 @@ +import * as cheerio from 'cheerio'; +import { ContentType } from 'stremio-addon-sdk'; +import { Context, CountryCode } from '../types'; +import { Fetcher, getTmdbId, getTmdbNameAndYear, Id, TmdbId } from '../utils'; +import { Source, SourceResult } from './Source'; + +export class DiziShqip extends Source { + public readonly id = 'dizishqip'; + public readonly label = 'DiziShqip'; + public readonly contentTypes: ContentType[] = ['movie', 'series']; + public readonly countryCodes: CountryCode[] = [CountryCode.al]; + public readonly baseUrl = 'https://dizishqip.tv'; + private readonly fetcher: Fetcher; + + public constructor(fetcher: Fetcher) { + super(); + this.fetcher = fetcher; + } + + public async handleInternal(ctx: Context, _type: string, id: Id): Promise { + const tmdbId = await getTmdbId(ctx, this.fetcher, id); + const [name] = await getTmdbNameAndYear(ctx, this.fetcher, tmdbId, 'tr'); + + const pageUrl = await this.fetchPageUrl(ctx, tmdbId, 'tr'); + if (!pageUrl) return []; + + let episodeUrl: URL = pageUrl; + if (tmdbId.season && tmdbId.episode) { + const fetchedEpisodeUrl = await this.fetchEpisodeUrl(ctx, pageUrl, tmdbId); + if (!fetchedEpisodeUrl) return []; + episodeUrl = fetchedEpisodeUrl; + } + + const html = await this.fetcher.text(ctx, episodeUrl); + const $ = cheerio.load(html); + + const iframeUrls = $('#player2 iframe') + .map((_i, el) => $(el).attr('src')) + .get() + .filter(Boolean) + .map((src) => new URL(src, this.baseUrl)); + + if (iframeUrls.length === 0) return []; + + return iframeUrls.map((iframeUrl) => ({ + url: iframeUrl, + meta: { + referer: episodeUrl.href, + countryCodes: [CountryCode.al], + title: `${name} – Episodi ${tmdbId.episode ?? ''}`, + }, + })); + } + + private readonly fetchPageUrl = async (ctx: Context, tmdbId: TmdbId, language: string): Promise => { + const [name] = await getTmdbNameAndYear(ctx, this.fetcher, tmdbId, language); + const normalizeSearch = (title: string) => + title + .normalize('NFD') + .replace(/[\u0300-\u036f]/g, '') // remove accents + .replace(/[^\p{L}\p{N}\s]/gu, '') // remove all punctuation (:, !, ?, etc.) + .replace(/\s+/g, ' ') // collapse multiple spaces + .trim() + .replace(/ı/g, 'i') + .replace(/ş/g, 's') + .replace(/ç/g, 'c') + .replace(/ğ/g, 'g') + .replace(/ö/g, 'o') + .replace(/ü/g, 'u') + .replace(/İ/g, 'I') + .replace(/Ş/g, 'S') + .replace(/Ç/g, 'C') + .replace(/Ğ/g, 'G') + .replace(/Ö/g, 'O') + .replace(/Ü/g, 'U'); + + const search = normalizeSearch(name).toLowerCase(); +const searchUrl = new URL(`/?s=${encodeURIComponent(search)}`, this.baseUrl); + const html = await this.fetcher.text(ctx, searchUrl); + const $ = cheerio.load(html); + + const seriesHref = $('.ml-item a[href*="/series/"]').first().attr('href'); + if (seriesHref) return new URL(seriesHref, this.baseUrl); + + const firstEpisode = $('.ml-item a[href*="/episode/"]').first().attr('href'); + if (firstEpisode) return new URL(firstEpisode, this.baseUrl); + + return undefined; + }; + + private async fetchEpisodeUrl(ctx: Context, pageUrl: URL, tmdbId: TmdbId): Promise { + const html = await this.fetcher.text(ctx, pageUrl); + const $ = cheerio.load(html); + + const episodeLink = $(`a[href*="episodi-${tmdbId.episode}"]`).attr('href'); + if (episodeLink) return new URL(episodeLink, this.baseUrl); + + const guessed = `${pageUrl.href.replace(/\/$/, '')}-episodi-${tmdbId.episode}/`; + return new URL(guessed); + } +} -- 2.45.2 From c42ec58c3e7fb2f732f9a741f557e83ca67294eb Mon Sep 17 00:00:00 2001 From: GSTAR <44748406+GLlgGL@users.noreply.github.com> Date: Wed, 5 Nov 2025 10:30:23 +0100 Subject: [PATCH 07/33] Delete src/source/DiziShqip.ts --- src/source/DiziShqip.ts | 101 ---------------------------------------- 1 file changed, 101 deletions(-) delete mode 100644 src/source/DiziShqip.ts diff --git a/src/source/DiziShqip.ts b/src/source/DiziShqip.ts deleted file mode 100644 index 4e4a5d7..0000000 --- a/src/source/DiziShqip.ts +++ /dev/null @@ -1,101 +0,0 @@ -import * as cheerio from 'cheerio'; -import { ContentType } from 'stremio-addon-sdk'; -import { Context, CountryCode } from '../types'; -import { Fetcher, getTmdbId, getTmdbNameAndYear, Id, TmdbId } from '../utils'; -import { Source, SourceResult } from './Source'; - -export class DiziShqip extends Source { - public readonly id = 'dizishqip'; - public readonly label = 'DiziShqip'; - public readonly contentTypes: ContentType[] = ['movie', 'series']; - public readonly countryCodes: CountryCode[] = [CountryCode.al]; - public readonly baseUrl = 'https://dizishqip.tv'; - private readonly fetcher: Fetcher; - - public constructor(fetcher: Fetcher) { - super(); - this.fetcher = fetcher; - } - - public async handleInternal(ctx: Context, _type: string, id: Id): Promise { - const tmdbId = await getTmdbId(ctx, this.fetcher, id); - const [name] = await getTmdbNameAndYear(ctx, this.fetcher, tmdbId, 'tr'); - - const pageUrl = await this.fetchPageUrl(ctx, tmdbId, 'tr'); - if (!pageUrl) return []; - - let episodeUrl: URL = pageUrl; - if (tmdbId.season && tmdbId.episode) { - const fetchedEpisodeUrl = await this.fetchEpisodeUrl(ctx, pageUrl, tmdbId); - if (!fetchedEpisodeUrl) return []; - episodeUrl = fetchedEpisodeUrl; - } - - const html = await this.fetcher.text(ctx, episodeUrl); - const $ = cheerio.load(html); - - const iframeUrls = $('#player2 iframe') - .map((_i, el) => $(el).attr('src')) - .get() - .filter(Boolean) - .map((src) => new URL(src, this.baseUrl)); - - if (iframeUrls.length === 0) return []; - - return iframeUrls.map((iframeUrl) => ({ - url: iframeUrl, - meta: { - referer: episodeUrl.href, - countryCodes: [CountryCode.al], - title: `${name} – Episodi ${tmdbId.episode ?? ''}`, - }, - })); - } - - private readonly fetchPageUrl = async (ctx: Context, tmdbId: TmdbId, language: string): Promise => { - const [name] = await getTmdbNameAndYear(ctx, this.fetcher, tmdbId, language); - const normalizeSearch = (title: string) => - title - .normalize('NFD') - .replace(/[\u0300-\u036f]/g, '') // remove accents - .replace(/[^\p{L}\p{N}\s]/gu, '') // remove all punctuation (:, !, ?, etc.) - .replace(/\s+/g, ' ') // collapse multiple spaces - .trim() - .replace(/ı/g, 'i') - .replace(/ş/g, 's') - .replace(/ç/g, 'c') - .replace(/ğ/g, 'g') - .replace(/ö/g, 'o') - .replace(/ü/g, 'u') - .replace(/İ/g, 'I') - .replace(/Ş/g, 'S') - .replace(/Ç/g, 'C') - .replace(/Ğ/g, 'G') - .replace(/Ö/g, 'O') - .replace(/Ü/g, 'U'); - - const search = normalizeSearch(name).toLowerCase(); -const searchUrl = new URL(`/?s=${encodeURIComponent(search)}`, this.baseUrl); - const html = await this.fetcher.text(ctx, searchUrl); - const $ = cheerio.load(html); - - const seriesHref = $('.ml-item a[href*="/series/"]').first().attr('href'); - if (seriesHref) return new URL(seriesHref, this.baseUrl); - - const firstEpisode = $('.ml-item a[href*="/episode/"]').first().attr('href'); - if (firstEpisode) return new URL(firstEpisode, this.baseUrl); - - return undefined; - }; - - private async fetchEpisodeUrl(ctx: Context, pageUrl: URL, tmdbId: TmdbId): Promise { - const html = await this.fetcher.text(ctx, pageUrl); - const $ = cheerio.load(html); - - const episodeLink = $(`a[href*="episodi-${tmdbId.episode}"]`).attr('href'); - if (episodeLink) return new URL(episodeLink, this.baseUrl); - - const guessed = `${pageUrl.href.replace(/\/$/, '')}-episodi-${tmdbId.episode}/`; - return new URL(guessed); - } -} -- 2.45.2 From 6eb28c8b0f6bab0a537e41a0a431afb1f85306a8 Mon Sep 17 00:00:00 2001 From: GSTAR <44748406+GLlgGL@users.noreply.github.com> Date: Thu, 6 Nov 2025 20:36:54 +0100 Subject: [PATCH 08/33] Update Streamtape.ts --- src/extractor/Streamtape.ts | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/extractor/Streamtape.ts b/src/extractor/Streamtape.ts index c8994d6..813ec21 100644 --- a/src/extractor/Streamtape.ts +++ b/src/extractor/Streamtape.ts @@ -9,11 +9,22 @@ import { Extractor } from './Extractor'; export class Streamtape extends Extractor { public readonly id = 'streamtape'; + public readonly label = 'Streamtape(MFP)'; - public readonly label = 'Streamtape (via MediaFlow Proxy)'; + // ✅ Add the domains list here + public readonly domains = [ + 'streamtape.com', 'strtape.cloud', 'streamtape.net', 'streamta.pe', 'streamtape.site', + 'strcloud.link', 'strcloud.club', 'strtpe.link', 'streamtape.cc', 'scloud.online', 'stape.fun', + 'streamadblockplus.com', 'shavetape.cash', 'streamtape.to', 'streamta.site', + 'streamadblocker.xyz', 'tapewithadblock.org', 'adblocktape.wiki', 'antiadtape.com', + 'streamtape.xyz', 'tapeblocker.com', 'streamnoads.com', 'tapeadvertisement.com', + 'tapeadsenjoyer.com', 'watchadsontape.com' + ]; public supports(ctx: Context, url: URL): boolean { - return null !== url.host.match(/streamtape/) && supportsMediaFlowProxy(ctx); + // ✅ Match any of the known domains dynamically + const isSupportedDomain = this.domains.some(domain => url.hostname.endsWith(domain)); + return isSupportedDomain && supportsMediaFlowProxy(ctx); } public override normalize(url: URL): URL { @@ -29,7 +40,6 @@ export class Streamtape extends Extractor { const html = await this.fetcher.text(ctx, url, { headers }); const sizeMatch = html.match(/([\d.]+ ?[GM]B)/) as string[]; - const $ = cheerio.load(html); const title = $('meta[name="og:title"]').attr('content') as string; @@ -43,9 +53,9 @@ export class Streamtape extends Extractor { meta: { ...meta, title, - bytes: bytes.parse(sizeMatch[1] as string) as number, + bytes: bytes.parse(sizeMatch?.[1] ?? '0') as number, }, }, ]; }; -} +} \ No newline at end of file -- 2.45.2 From 088427bbf4a3a265dc43d24153432a1c35f7da8f Mon Sep 17 00:00:00 2001 From: GSTAR <44748406+GLlgGL@users.noreply.github.com> Date: Fri, 7 Nov 2025 07:33:55 +0100 Subject: [PATCH 09/33] Update Streamtape.ts --- src/extractor/Streamtape.ts | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/src/extractor/Streamtape.ts b/src/extractor/Streamtape.ts index 813ec21..ebf8622 100644 --- a/src/extractor/Streamtape.ts +++ b/src/extractor/Streamtape.ts @@ -9,23 +9,14 @@ import { Extractor } from './Extractor'; export class Streamtape extends Extractor { public readonly id = 'streamtape'; - public readonly label = 'Streamtape(MFP)'; - // ✅ Add the domains list here - public readonly domains = [ - 'streamtape.com', 'strtape.cloud', 'streamtape.net', 'streamta.pe', 'streamtape.site', - 'strcloud.link', 'strcloud.club', 'strtpe.link', 'streamtape.cc', 'scloud.online', 'stape.fun', - 'streamadblockplus.com', 'shavetape.cash', 'streamtape.to', 'streamta.site', - 'streamadblocker.xyz', 'tapewithadblock.org', 'adblocktape.wiki', 'antiadtape.com', - 'streamtape.xyz', 'tapeblocker.com', 'streamnoads.com', 'tapeadvertisement.com', - 'tapeadsenjoyer.com', 'watchadsontape.com' - ]; + public readonly label = 'Streamtape (via MediaFlow Proxy)'; public supports(ctx: Context, url: URL): boolean { - // ✅ Match any of the known domains dynamically - const isSupportedDomain = this.domains.some(domain => url.hostname.endsWith(domain)); - return isSupportedDomain && supportsMediaFlowProxy(ctx); - } + return null !== url.host.match( + /(streamtape|strtape\.cloud|streamtape\.net|streamta\.pe|streamtape\.site|strcloud\.link|strcloud\.club|strtpe\.link|streamtape\.cc|scloud\.online|stape\.fun|streamadblockplus\.com|shavetape\.cash|streamtape\.to|streamta\.site|streamadblocker\.xyz|tapewithadblock\.org|adblocktape\.wiki|antiadtape\.com|streamtape\.xyz|tapeblocker\.com|streamnoads\.com|tapeadvertisement\.com|tapeadsenjoyer\.com|watchadsontape\.com)/ + ) && supportsMediaFlowProxy(ctx); +} public override normalize(url: URL): URL { return new URL(url.href.replace('/e/', '/v/')); @@ -40,6 +31,7 @@ export class Streamtape extends Extractor { const html = await this.fetcher.text(ctx, url, { headers }); const sizeMatch = html.match(/([\d.]+ ?[GM]B)/) as string[]; + const $ = cheerio.load(html); const title = $('meta[name="og:title"]').attr('content') as string; @@ -53,7 +45,7 @@ export class Streamtape extends Extractor { meta: { ...meta, title, - bytes: bytes.parse(sizeMatch?.[1] ?? '0') as number, + bytes: bytes.parse(sizeMatch[1] as string) as number, }, }, ]; -- 2.45.2 From 94163e38cfb03c756ad1fe265138695f4a6ecc36 Mon Sep 17 00:00:00 2001 From: GSTAR <44748406+GLlgGL@users.noreply.github.com> Date: Fri, 7 Nov 2025 09:59:53 +0100 Subject: [PATCH 10/33] Update Streamtape.ts --- src/extractor/Streamtape.ts | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/src/extractor/Streamtape.ts b/src/extractor/Streamtape.ts index ebf8622..c8bd098 100644 --- a/src/extractor/Streamtape.ts +++ b/src/extractor/Streamtape.ts @@ -13,9 +13,37 @@ export class Streamtape extends Extractor { public readonly label = 'Streamtape (via MediaFlow Proxy)'; public supports(ctx: Context, url: URL): boolean { - return null !== url.host.match( - /(streamtape|strtape\.cloud|streamtape\.net|streamta\.pe|streamtape\.site|strcloud\.link|strcloud\.club|strtpe\.link|streamtape\.cc|scloud\.online|stape\.fun|streamadblockplus\.com|shavetape\.cash|streamtape\.to|streamta\.site|streamadblocker\.xyz|tapewithadblock\.org|adblocktape\.wiki|antiadtape\.com|streamtape\.xyz|tapeblocker\.com|streamnoads\.com|tapeadvertisement\.com|tapeadsenjoyer\.com|watchadsontape\.com)/ - ) && supportsMediaFlowProxy(ctx); + const supportedDomain = + null !== url.host.match(/streamtape/) || + [ + 'streamtape.com', + 'strtape.cloud', + 'streamtape.net', + 'streamta.pe', + 'streamtape.site', + 'strcloud.link', + 'strcloud.club', + 'strtpe.link', + 'streamtape.cc', + 'scloud.online', + 'stape.fun', + 'streamadblockplus.com', + 'shavetape.cash', + 'streamtape.to', + 'streamta.site', + 'streamadblocker.xyz', + 'tapewithadblock.org', + 'adblocktape.wiki', + 'antiadtape.com', + 'streamtape.xyz', + 'tapeblocker.com', + 'streamnoads.com', + 'tapeadvertisement.com', + 'tapeadsenjoyer.com', + 'watchadsontape.com', + ].includes(url.host); + + return supportedDomain && supportsMediaFlowProxy(ctx); } public override normalize(url: URL): URL { -- 2.45.2 From 866859c6b4d1c0803ab538be3b5a4e6793010e08 Mon Sep 17 00:00:00 2001 From: webstreamr <210764791+webstreamr@users.noreply.github.com> Date: Fri, 7 Nov 2025 19:57:02 +0900 Subject: [PATCH 11/33] Update src/extractor/Streamtape.ts --- src/extractor/Streamtape.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/extractor/Streamtape.ts b/src/extractor/Streamtape.ts index c8bd098..6f2c41c 100644 --- a/src/extractor/Streamtape.ts +++ b/src/extractor/Streamtape.ts @@ -16,7 +16,6 @@ export class Streamtape extends Extractor { const supportedDomain = null !== url.host.match(/streamtape/) || [ - 'streamtape.com', 'strtape.cloud', 'streamtape.net', 'streamta.pe', -- 2.45.2 From 3834b1db007bdc7d1549e76cecb326b951fb4ad0 Mon Sep 17 00:00:00 2001 From: webstreamr <210764791+webstreamr@users.noreply.github.com> Date: Fri, 7 Nov 2025 19:57:11 +0900 Subject: [PATCH 12/33] Update src/extractor/Streamtape.ts --- src/extractor/Streamtape.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/extractor/Streamtape.ts b/src/extractor/Streamtape.ts index 6f2c41c..5abda83 100644 --- a/src/extractor/Streamtape.ts +++ b/src/extractor/Streamtape.ts @@ -17,7 +17,6 @@ export class Streamtape extends Extractor { null !== url.host.match(/streamtape/) || [ 'strtape.cloud', - 'streamtape.net', 'streamta.pe', 'streamtape.site', 'strcloud.link', -- 2.45.2 From 1e83c12af8fded013547e8793f069ffd6dfdb7ec Mon Sep 17 00:00:00 2001 From: webstreamr <210764791+webstreamr@users.noreply.github.com> Date: Fri, 7 Nov 2025 19:57:20 +0900 Subject: [PATCH 13/33] Update src/extractor/Streamtape.ts --- src/extractor/Streamtape.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/extractor/Streamtape.ts b/src/extractor/Streamtape.ts index 5abda83..10f9733 100644 --- a/src/extractor/Streamtape.ts +++ b/src/extractor/Streamtape.ts @@ -18,7 +18,6 @@ export class Streamtape extends Extractor { [ 'strtape.cloud', 'streamta.pe', - 'streamtape.site', 'strcloud.link', 'strcloud.club', 'strtpe.link', -- 2.45.2 From 3b1b2378389a1d1732a8bfb0bc7f84ecb14a276c Mon Sep 17 00:00:00 2001 From: webstreamr <210764791+webstreamr@users.noreply.github.com> Date: Fri, 7 Nov 2025 19:57:26 +0900 Subject: [PATCH 14/33] Update src/extractor/Streamtape.ts --- src/extractor/Streamtape.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/extractor/Streamtape.ts b/src/extractor/Streamtape.ts index 10f9733..40deb9e 100644 --- a/src/extractor/Streamtape.ts +++ b/src/extractor/Streamtape.ts @@ -21,7 +21,6 @@ export class Streamtape extends Extractor { 'strcloud.link', 'strcloud.club', 'strtpe.link', - 'streamtape.cc', 'scloud.online', 'stape.fun', 'streamadblockplus.com', -- 2.45.2 From ad375992b67934465675e144d89e78f5898139e0 Mon Sep 17 00:00:00 2001 From: webstreamr <210764791+webstreamr@users.noreply.github.com> Date: Fri, 7 Nov 2025 19:57:35 +0900 Subject: [PATCH 15/33] Update src/extractor/Streamtape.ts --- src/extractor/Streamtape.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/extractor/Streamtape.ts b/src/extractor/Streamtape.ts index 40deb9e..e3cd42a 100644 --- a/src/extractor/Streamtape.ts +++ b/src/extractor/Streamtape.ts @@ -25,7 +25,6 @@ export class Streamtape extends Extractor { 'stape.fun', 'streamadblockplus.com', 'shavetape.cash', - 'streamtape.to', 'streamta.site', 'streamadblocker.xyz', 'tapewithadblock.org', -- 2.45.2 From f05fa5443ad6d27fd2fcbbb3e426383d4454aebe Mon Sep 17 00:00:00 2001 From: webstreamr <210764791+webstreamr@users.noreply.github.com> Date: Fri, 7 Nov 2025 19:57:43 +0900 Subject: [PATCH 16/33] Update src/extractor/Streamtape.ts --- src/extractor/Streamtape.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/extractor/Streamtape.ts b/src/extractor/Streamtape.ts index e3cd42a..c4a2133 100644 --- a/src/extractor/Streamtape.ts +++ b/src/extractor/Streamtape.ts @@ -30,7 +30,6 @@ export class Streamtape extends Extractor { 'tapewithadblock.org', 'adblocktape.wiki', 'antiadtape.com', - 'streamtape.xyz', 'tapeblocker.com', 'streamnoads.com', 'tapeadvertisement.com', -- 2.45.2 From 55b05156d338dd35d424e55bf1fe1863fffea5d7 Mon Sep 17 00:00:00 2001 From: GSTAR <44748406+GLlgGL@users.noreply.github.com> Date: Fri, 7 Nov 2025 15:33:20 +0100 Subject: [PATCH 17/33] Delete src/extractor/TurboVIPlay.ts --- src/extractor/TurboVIPlay.ts | 103 ----------------------------------- 1 file changed, 103 deletions(-) delete mode 100644 src/extractor/TurboVIPlay.ts diff --git a/src/extractor/TurboVIPlay.ts b/src/extractor/TurboVIPlay.ts deleted file mode 100644 index f324e60..0000000 --- a/src/extractor/TurboVIPlay.ts +++ /dev/null @@ -1,103 +0,0 @@ -import * as cheerio from 'cheerio'; -import { NotFoundError } from '../error'; -import { Context, Format, Meta, UrlResult } from '../types'; -import { Extractor } from './Extractor'; -import { guessHeightFromPlaylist } from '../utils/height'; - -export class TurboVIPlay extends Extractor { - public readonly id = 'turboviplay'; - public readonly label = 'TurboVIPlay'; - public override readonly ttl = 10800000; // 3h - - private domains = [ - 'turboviplay.com', - 'emturbovid.com', - 'tuborstb.co', - 'javggvideo.xyz', - 'stbturbo.xyz', - 'turbovidhls.com', - ]; - - public supports(_ctx: Context, url: URL): boolean { - return this.domains.some((d) => url.host.includes(d)); - } - - public override normalize(url: URL): URL { - return new URL(url.href.replace(/\/(e|d|embed-)\//, '/t/')); - } - - protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise { - const headers: Record = { Referer: url.origin }; - - - // Fetch HTML page - const html = await this.fetcher.text(ctx, url, headers); - if (!html || html.includes('File Not Found') || html.includes('Pending in queue')) { - throw new NotFoundError(); - } - - // Extract media URL - const match = html.match(/(?:urlPlay|data-hash)\s*=\s*['"](?[^"']+)/); - const mediaUrl = match?.groups?.['url']; - if (!mediaUrl) throw new NotFoundError('Video link not found'); - - // Normalize URL - const masterUrl = mediaUrl.startsWith('//') - ? 'https:' + mediaUrl - : mediaUrl.startsWith('/') - ? url.origin + mediaUrl - : mediaUrl; - - let hlsUrl = masterUrl; - - // Parse master playlist to pick highest resolution variant - try { - const playlistText = await this.fetcher.text(ctx, new URL(masterUrl), headers); - - if (playlistText.includes('#EXT-X-STREAM-INF')) { - const variants = Array.from( - playlistText.matchAll(/#EXT-X-STREAM-INF:.*RESOLUTION=(\d+)x(\d+)[^\n]*\n([^\n]+)/g) - ); - - if (variants.length) { - // Sort by height descending - variants.sort((a, b) => parseInt(b[2] ?? '0', 10) - parseInt(a[2] ?? '0', 10)); - const best = variants[0]?.[3]; - - if (best) { - const base = new URL(masterUrl); - hlsUrl = best.startsWith('http') - ? best - : best.startsWith('/') - ? new URL(best, base.origin).href - : new URL(best, base.href).href; - } - } - } - } catch { - // fallback to masterUrl - } - - // Guess height from master playlist - let height: number | undefined; - try { - height = await guessHeightFromPlaylist(ctx, this.fetcher, new URL(masterUrl), new URL(masterUrl), headers); - } catch { - // ignore - } - - // Extract title - const $ = cheerio.load(html); - const title = $('title').text().trim() || this.label; - - return [{ - url: new URL(hlsUrl), - format: Format.hls, - label: this.label, - sourceId: `${this.id}_${meta.countryCodes?.join('_') ?? 'all'}`, - ttl: this.ttl, - requestHeaders: headers, - meta: { ...meta, title, height }, - }]; -} -} \ No newline at end of file -- 2.45.2 From 81fdef638821718315482d38512ddf726c201acb Mon Sep 17 00:00:00 2001 From: GSTAR <44748406+GLlgGL@users.noreply.github.com> Date: Sat, 8 Nov 2025 08:44:05 +0100 Subject: [PATCH 18/33] Update Streamtape.ts --- src/extractor/Streamtape.ts | 58 ++++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 27 deletions(-) diff --git a/src/extractor/Streamtape.ts b/src/extractor/Streamtape.ts index c4a2133..24561e8 100644 --- a/src/extractor/Streamtape.ts +++ b/src/extractor/Streamtape.ts @@ -13,38 +13,42 @@ export class Streamtape extends Extractor { public readonly label = 'Streamtape (via MediaFlow Proxy)'; public supports(ctx: Context, url: URL): boolean { - const supportedDomain = - null !== url.host.match(/streamtape/) || - [ - 'strtape.cloud', - 'streamta.pe', - 'strcloud.link', - 'strcloud.club', - 'strtpe.link', - 'scloud.online', - 'stape.fun', - 'streamadblockplus.com', - 'shavetape.cash', - 'streamta.site', - 'streamadblocker.xyz', - 'tapewithadblock.org', - 'adblocktape.wiki', - 'antiadtape.com', - 'tapeblocker.com', - 'streamnoads.com', - 'tapeadvertisement.com', - 'tapeadsenjoyer.com', - 'watchadsontape.com', - ].includes(url.host); + const supportedDomain = + null !== url.host.match(/streamtape/) || + [ + 'strtape.cloud', + 'streamta.pe', + 'strcloud.link', + 'strcloud.club', + 'strtpe.link', + 'scloud.online', + 'stape.fun', + 'streamadblockplus.com', + 'shavetape.cash', + 'streamta.site', + 'streamadblocker.xyz', + 'tapewithadblock.org', + 'adblocktape.wiki', + 'antiadtape.com', + 'tapeblocker.com', + 'streamnoads.com', + 'tapeadvertisement.com', + 'tapeadsenjoyer.com', + 'watchadsontape.com', + ].includes(url.host); - return supportedDomain && supportsMediaFlowProxy(ctx); -} + return supportedDomain && supportsMediaFlowProxy(ctx); + } public override normalize(url: URL): URL { return new URL(url.href.replace('/e/', '/v/')); } - protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise { + protected async extractInternal( + ctx: Context, + url: URL, + meta: Meta, + ): Promise { const headers = { Referer: meta.referer ?? url.href }; // Only needed to properly find non-existing files via 404 response @@ -71,5 +75,5 @@ export class Streamtape extends Extractor { }, }, ]; - }; + } } \ No newline at end of file -- 2.45.2 From fc36babd5eb3f48f4c32af63c9c28256c1ba1fe3 Mon Sep 17 00:00:00 2001 From: GSTAR <44748406+GLlgGL@users.noreply.github.com> Date: Sat, 8 Nov 2025 08:46:54 +0100 Subject: [PATCH 19/33] Update Streamtape.ts --- src/extractor/Streamtape.ts | 50 ++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/src/extractor/Streamtape.ts b/src/extractor/Streamtape.ts index 24561e8..e01acbe 100644 --- a/src/extractor/Streamtape.ts +++ b/src/extractor/Streamtape.ts @@ -13,32 +13,32 @@ export class Streamtape extends Extractor { public readonly label = 'Streamtape (via MediaFlow Proxy)'; public supports(ctx: Context, url: URL): boolean { - const supportedDomain = - null !== url.host.match(/streamtape/) || - [ - 'strtape.cloud', - 'streamta.pe', - 'strcloud.link', - 'strcloud.club', - 'strtpe.link', - 'scloud.online', - 'stape.fun', - 'streamadblockplus.com', - 'shavetape.cash', - 'streamta.site', - 'streamadblocker.xyz', - 'tapewithadblock.org', - 'adblocktape.wiki', - 'antiadtape.com', - 'tapeblocker.com', - 'streamnoads.com', - 'tapeadvertisement.com', - 'tapeadsenjoyer.com', - 'watchadsontape.com', - ].includes(url.host); + const supportedDomain + = null !== url.host.match(/streamtape/) + || [ + 'strtape.cloud', + 'streamta.pe', + 'strcloud.link', + 'strcloud.club', + 'strtpe.link', + 'scloud.online', + 'stape.fun', + 'streamadblockplus.com', + 'shavetape.cash', + 'streamta.site', + 'streamadblocker.xyz', + 'tapewithadblock.org', + 'adblocktape.wiki', + 'antiadtape.com', + 'tapeblocker.com', + 'streamnoads.com', + 'tapeadvertisement.com', + 'tapeadsenjoyer.com', + 'watchadsontape.com', + ].includes(url.host); - return supportedDomain && supportsMediaFlowProxy(ctx); - } + return supportedDomain && supportsMediaFlowProxy(ctx); +} public override normalize(url: URL): URL { return new URL(url.href.replace('/e/', '/v/')); -- 2.45.2 From e711edbd2b6d116775866fd92d88d64f08c3ccd5 Mon Sep 17 00:00:00 2001 From: GSTAR <44748406+GLlgGL@users.noreply.github.com> Date: Sat, 8 Nov 2025 08:50:40 +0100 Subject: [PATCH 20/33] Update Streamtape.ts --- src/extractor/Streamtape.ts | 51 +++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/src/extractor/Streamtape.ts b/src/extractor/Streamtape.ts index e01acbe..8e53fbc 100644 --- a/src/extractor/Streamtape.ts +++ b/src/extractor/Streamtape.ts @@ -13,32 +13,33 @@ export class Streamtape extends Extractor { public readonly label = 'Streamtape (via MediaFlow Proxy)'; public supports(ctx: Context, url: URL): boolean { - const supportedDomain - = null !== url.host.match(/streamtape/) - || [ - 'strtape.cloud', - 'streamta.pe', - 'strcloud.link', - 'strcloud.club', - 'strtpe.link', - 'scloud.online', - 'stape.fun', - 'streamadblockplus.com', - 'shavetape.cash', - 'streamta.site', - 'streamadblocker.xyz', - 'tapewithadblock.org', - 'adblocktape.wiki', - 'antiadtape.com', - 'tapeblocker.com', - 'streamnoads.com', - 'tapeadvertisement.com', - 'tapeadsenjoyer.com', - 'watchadsontape.com', - ].includes(url.host); + const supportedDomain + = null !== url.host.match(/streamtape/) + || + [ + 'strtape.cloud', + 'streamta.pe', + 'strcloud.link', + 'strcloud.club', + 'strtpe.link', + 'scloud.online', + 'stape.fun', + 'streamadblockplus.com', + 'shavetape.cash', + 'streamta.site', + 'streamadblocker.xyz', + 'tapewithadblock.org', + 'adblocktape.wiki', + 'antiadtape.com', + 'tapeblocker.com', + 'streamnoads.com', + 'tapeadvertisement.com', + 'tapeadsenjoyer.com', + 'watchadsontape.com', + ].includes(url.host); - return supportedDomain && supportsMediaFlowProxy(ctx); -} + return supportedDomain && supportsMediaFlowProxy(ctx); + } public override normalize(url: URL): URL { return new URL(url.href.replace('/e/', '/v/')); -- 2.45.2 From d198656c34548468e1b0a3c9b476744f2f41b7ad Mon Sep 17 00:00:00 2001 From: GSTAR <44748406+GLlgGL@users.noreply.github.com> Date: Sat, 8 Nov 2025 08:52:02 +0100 Subject: [PATCH 21/33] Update Streamtape.ts --- src/extractor/Streamtape.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/extractor/Streamtape.ts b/src/extractor/Streamtape.ts index 8e53fbc..24561e8 100644 --- a/src/extractor/Streamtape.ts +++ b/src/extractor/Streamtape.ts @@ -13,9 +13,8 @@ export class Streamtape extends Extractor { public readonly label = 'Streamtape (via MediaFlow Proxy)'; public supports(ctx: Context, url: URL): boolean { - const supportedDomain - = null !== url.host.match(/streamtape/) - || + const supportedDomain = + null !== url.host.match(/streamtape/) || [ 'strtape.cloud', 'streamta.pe', -- 2.45.2 From 56d43e4e3c117aafb398621aa1fd357d20459900 Mon Sep 17 00:00:00 2001 From: GSTAR <44748406+GLlgGL@users.noreply.github.com> Date: Sat, 8 Nov 2025 08:53:17 +0100 Subject: [PATCH 22/33] Update Streamtape.ts --- src/extractor/Streamtape.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/extractor/Streamtape.ts b/src/extractor/Streamtape.ts index 24561e8..bea1f2b 100644 --- a/src/extractor/Streamtape.ts +++ b/src/extractor/Streamtape.ts @@ -76,4 +76,4 @@ export class Streamtape extends Extractor { }, ]; } -} \ No newline at end of file +} -- 2.45.2 From cbed3a78ff4f469322286664296a0f535cd9a98a Mon Sep 17 00:00:00 2001 From: GSTAR <44748406+GLlgGL@users.noreply.github.com> Date: Sat, 8 Nov 2025 08:58:21 +0100 Subject: [PATCH 23/33] Update Streamtape.ts --- src/extractor/Streamtape.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/extractor/Streamtape.ts b/src/extractor/Streamtape.ts index bea1f2b..dc3a933 100644 --- a/src/extractor/Streamtape.ts +++ b/src/extractor/Streamtape.ts @@ -13,9 +13,9 @@ export class Streamtape extends Extractor { public readonly label = 'Streamtape (via MediaFlow Proxy)'; public supports(ctx: Context, url: URL): boolean { - const supportedDomain = - null !== url.host.match(/streamtape/) || - [ + const supportedDomain + = null !== url.host.match(/streamtape/) + || [ 'strtape.cloud', 'streamta.pe', 'strcloud.link', -- 2.45.2 From 0efb006a3d1ca996b377cabdfba674d6546d3bea Mon Sep 17 00:00:00 2001 From: GSTAR <44748406+GLlgGL@users.noreply.github.com> Date: Sat, 8 Nov 2025 09:00:45 +0100 Subject: [PATCH 24/33] Update Streamtape.ts --- src/extractor/Streamtape.ts | 100 +++++++++--------------------------- 1 file changed, 24 insertions(+), 76 deletions(-) diff --git a/src/extractor/Streamtape.ts b/src/extractor/Streamtape.ts index dc3a933..0868022 100644 --- a/src/extractor/Streamtape.ts +++ b/src/extractor/Streamtape.ts @@ -1,79 +1,27 @@ -import bytes from 'bytes'; -import * as cheerio from 'cheerio'; -import { Context, Format, Meta, UrlResult } from '../types'; -import { - buildMediaFlowProxyExtractorRedirectUrl, - supportsMediaFlowProxy, -} from '../utils'; -import { Extractor } from './Extractor'; - -export class Streamtape extends Extractor { - public readonly id = 'streamtape'; - - public readonly label = 'Streamtape (via MediaFlow Proxy)'; - - public supports(ctx: Context, url: URL): boolean { +public supports(ctx: Context, url: URL): boolean { const supportedDomain - = null !== url.host.match(/streamtape/) - || [ - 'strtape.cloud', - 'streamta.pe', - 'strcloud.link', - 'strcloud.club', - 'strtpe.link', - 'scloud.online', - 'stape.fun', - 'streamadblockplus.com', - 'shavetape.cash', - 'streamta.site', - 'streamadblocker.xyz', - 'tapewithadblock.org', - 'adblocktape.wiki', - 'antiadtape.com', - 'tapeblocker.com', - 'streamnoads.com', - 'tapeadvertisement.com', - 'tapeadsenjoyer.com', - 'watchadsontape.com', - ].includes(url.host); + = null !== url.host.match(/streamtape/) + || [ + 'strtape.cloud', + 'streamta.pe', + 'strcloud.link', + 'strcloud.club', + 'strtpe.link', + 'scloud.online', + 'stape.fun', + 'streamadblockplus.com', + 'shavetape.cash', + 'streamta.site', + 'streamadblocker.xyz', + 'tapewithadblock.org', + 'adblocktape.wiki', + 'antiadtape.com', + 'tapeblocker.com', + 'streamnoads.com', + 'tapeadvertisement.com', + 'tapeadsenjoyer.com', + 'watchadsontape.com', + ].includes(url.host); return supportedDomain && supportsMediaFlowProxy(ctx); - } - - public override normalize(url: URL): URL { - return new URL(url.href.replace('/e/', '/v/')); - } - - protected async extractInternal( - ctx: Context, - url: URL, - meta: Meta, - ): Promise { - const headers = { Referer: meta.referer ?? url.href }; - - // Only needed to properly find non-existing files via 404 response - await this.fetcher.text(ctx, new URL(url.href.replace('/v/', '/e/')), { headers }); - - const html = await this.fetcher.text(ctx, url, { headers }); - - const sizeMatch = html.match(/([\d.]+ ?[GM]B)/) as string[]; - - const $ = cheerio.load(html); - const title = $('meta[name="og:title"]').attr('content') as string; - - return [ - { - url: buildMediaFlowProxyExtractorRedirectUrl(ctx, 'Streamtape', url, headers), - format: Format.mp4, - label: this.label, - sourceId: `${this.id}_${meta.countryCodes?.join('_')}`, - ttl: this.ttl, - meta: { - ...meta, - title, - bytes: bytes.parse(sizeMatch[1] as string) as number, - }, - }, - ]; - } -} + } \ No newline at end of file -- 2.45.2 From 540e6311f65a7981a80ed5ce626434d99c16eb68 Mon Sep 17 00:00:00 2001 From: GSTAR <44748406+GLlgGL@users.noreply.github.com> Date: Sat, 8 Nov 2025 09:01:46 +0100 Subject: [PATCH 25/33] Update Streamtape.ts --- src/extractor/Streamtape.ts | 103 +++++++++++++++++++++++++++--------- 1 file changed, 77 insertions(+), 26 deletions(-) diff --git a/src/extractor/Streamtape.ts b/src/extractor/Streamtape.ts index 0868022..2fe4101 100644 --- a/src/extractor/Streamtape.ts +++ b/src/extractor/Streamtape.ts @@ -1,27 +1,78 @@ -public supports(ctx: Context, url: URL): boolean { - const supportedDomain - = null !== url.host.match(/streamtape/) - || [ - 'strtape.cloud', - 'streamta.pe', - 'strcloud.link', - 'strcloud.club', - 'strtpe.link', - 'scloud.online', - 'stape.fun', - 'streamadblockplus.com', - 'shavetape.cash', - 'streamta.site', - 'streamadblocker.xyz', - 'tapewithadblock.org', - 'adblocktape.wiki', - 'antiadtape.com', - 'tapeblocker.com', - 'streamnoads.com', - 'tapeadvertisement.com', - 'tapeadsenjoyer.com', - 'watchadsontape.com', - ].includes(url.host); +import bytes from 'bytes'; +import * as cheerio from 'cheerio'; +import { Context, Format, Meta, UrlResult } from '../types'; +import { + buildMediaFlowProxyExtractorRedirectUrl, + supportsMediaFlowProxy, +} from '../utils'; +import { Extractor } from './Extractor'; - return supportedDomain && supportsMediaFlowProxy(ctx); - } \ No newline at end of file +export class Streamtape extends Extractor { + public readonly id = 'streamtape'; + + public readonly label = 'Streamtape (via MediaFlow Proxy)'; + + public supports(ctx: Context, url: URL): boolean { + const supportedDomain + = null !== url.host.match(/streamtape/) + || [ + 'strtape.cloud', + 'streamta.pe', + 'strcloud.link', + 'strcloud.club', + 'strtpe.link', + 'scloud.online', + 'stape.fun', + 'streamadblockplus.com', + 'shavetape.cash', + 'streamta.site', + 'streamadblocker.xyz', + 'tapewithadblock.org', + 'adblocktape.wiki', + 'antiadtape.com', + 'tapeblocker.com', + 'streamnoads.com', + 'tapeadvertisement.com', + 'tapeadsenjoyer.com', + 'watchadsontape.com', + ].includes(url.host); + + return supportedDomain && supportsMediaFlowProxy(ctx); + } + + public override normalize(url: URL): URL { + return new URL(url.href.replace('/e/', '/v/')); + } + + protected async extractInternal( + ctx: Context, + url: URL, + meta: Meta, + ): Promise { + const headers = { Referer: meta.referer ?? url.href }; + + await this.fetcher.text(ctx, new URL(url.href.replace('/v/', '/e/')), { headers }); + + const html = await this.fetcher.text(ctx, url, { headers }); + + const sizeMatch = html.match(/([\d.]+ ?[GM]B)/) as string[]; + + const $ = cheerio.load(html); + const title = $('meta[name="og:title"]').attr('content') as string; + + return [ + { + url: buildMediaFlowProxyExtractorRedirectUrl(ctx, 'Streamtape', url, headers), + format: Format.mp4, + label: this.label, + sourceId: `${this.id}_${meta.countryCodes?.join('_')}`, + ttl: this.ttl, + meta: { + ...meta, + title, + bytes: bytes.parse(sizeMatch[1] as string) as number, + }, + }, + ]; + } +} \ No newline at end of file -- 2.45.2 From 27911d38d7057dd57e1af32e11260827626db5c8 Mon Sep 17 00:00:00 2001 From: GSTAR <44748406+GLlgGL@users.noreply.github.com> Date: Sat, 8 Nov 2025 09:03:48 +0100 Subject: [PATCH 26/33] Update Streamtape.ts --- src/extractor/Streamtape.ts | 119 ++++++++++++++++++------------------ 1 file changed, 60 insertions(+), 59 deletions(-) diff --git a/src/extractor/Streamtape.ts b/src/extractor/Streamtape.ts index 2fe4101..e01acbe 100644 --- a/src/extractor/Streamtape.ts +++ b/src/extractor/Streamtape.ts @@ -2,77 +2,78 @@ import bytes from 'bytes'; import * as cheerio from 'cheerio'; import { Context, Format, Meta, UrlResult } from '../types'; import { - buildMediaFlowProxyExtractorRedirectUrl, - supportsMediaFlowProxy, + buildMediaFlowProxyExtractorRedirectUrl, + supportsMediaFlowProxy, } from '../utils'; import { Extractor } from './Extractor'; export class Streamtape extends Extractor { - public readonly id = 'streamtape'; + public readonly id = 'streamtape'; - public readonly label = 'Streamtape (via MediaFlow Proxy)'; + public readonly label = 'Streamtape (via MediaFlow Proxy)'; - public supports(ctx: Context, url: URL): boolean { - const supportedDomain - = null !== url.host.match(/streamtape/) - || [ - 'strtape.cloud', - 'streamta.pe', - 'strcloud.link', - 'strcloud.club', - 'strtpe.link', - 'scloud.online', - 'stape.fun', - 'streamadblockplus.com', - 'shavetape.cash', - 'streamta.site', - 'streamadblocker.xyz', - 'tapewithadblock.org', - 'adblocktape.wiki', - 'antiadtape.com', - 'tapeblocker.com', - 'streamnoads.com', - 'tapeadvertisement.com', - 'tapeadsenjoyer.com', - 'watchadsontape.com', - ].includes(url.host); + public supports(ctx: Context, url: URL): boolean { + const supportedDomain + = null !== url.host.match(/streamtape/) + || [ + 'strtape.cloud', + 'streamta.pe', + 'strcloud.link', + 'strcloud.club', + 'strtpe.link', + 'scloud.online', + 'stape.fun', + 'streamadblockplus.com', + 'shavetape.cash', + 'streamta.site', + 'streamadblocker.xyz', + 'tapewithadblock.org', + 'adblocktape.wiki', + 'antiadtape.com', + 'tapeblocker.com', + 'streamnoads.com', + 'tapeadvertisement.com', + 'tapeadsenjoyer.com', + 'watchadsontape.com', + ].includes(url.host); - return supportedDomain && supportsMediaFlowProxy(ctx); - } + return supportedDomain && supportsMediaFlowProxy(ctx); +} - public override normalize(url: URL): URL { - return new URL(url.href.replace('/e/', '/v/')); - } + public override normalize(url: URL): URL { + return new URL(url.href.replace('/e/', '/v/')); + } - protected async extractInternal( - ctx: Context, - url: URL, - meta: Meta, - ): Promise { - const headers = { Referer: meta.referer ?? url.href }; + protected async extractInternal( + ctx: Context, + url: URL, + meta: Meta, + ): Promise { + const headers = { Referer: meta.referer ?? url.href }; - await this.fetcher.text(ctx, new URL(url.href.replace('/v/', '/e/')), { headers }); + // Only needed to properly find non-existing files via 404 response + await this.fetcher.text(ctx, new URL(url.href.replace('/v/', '/e/')), { headers }); - const html = await this.fetcher.text(ctx, url, { headers }); + const html = await this.fetcher.text(ctx, url, { headers }); - const sizeMatch = html.match(/([\d.]+ ?[GM]B)/) as string[]; + const sizeMatch = html.match(/([\d.]+ ?[GM]B)/) as string[]; - const $ = cheerio.load(html); - const title = $('meta[name="og:title"]').attr('content') as string; + const $ = cheerio.load(html); + const title = $('meta[name="og:title"]').attr('content') as string; - return [ - { - url: buildMediaFlowProxyExtractorRedirectUrl(ctx, 'Streamtape', url, headers), - format: Format.mp4, - label: this.label, - sourceId: `${this.id}_${meta.countryCodes?.join('_')}`, - ttl: this.ttl, - meta: { - ...meta, - title, - bytes: bytes.parse(sizeMatch[1] as string) as number, - }, - }, - ]; - } + return [ + { + url: buildMediaFlowProxyExtractorRedirectUrl(ctx, 'Streamtape', url, headers), + format: Format.mp4, + label: this.label, + sourceId: `${this.id}_${meta.countryCodes?.join('_')}`, + ttl: this.ttl, + meta: { + ...meta, + title, + bytes: bytes.parse(sizeMatch[1] as string) as number, + }, + }, + ]; + } } \ No newline at end of file -- 2.45.2 From e470e2600bddf4f2bf87c8abce3224632df0883b Mon Sep 17 00:00:00 2001 From: GSTAR <44748406+GLlgGL@users.noreply.github.com> Date: Sat, 8 Nov 2025 09:08:52 +0100 Subject: [PATCH 27/33] Update Streamtape.ts --- src/extractor/Streamtape.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/extractor/Streamtape.ts b/src/extractor/Streamtape.ts index e01acbe..8163655 100644 --- a/src/extractor/Streamtape.ts +++ b/src/extractor/Streamtape.ts @@ -13,9 +13,9 @@ export class Streamtape extends Extractor { public readonly label = 'Streamtape (via MediaFlow Proxy)'; public supports(ctx: Context, url: URL): boolean { - const supportedDomain + const supportedDomain = null !== url.host.match(/streamtape/) - || [ + || [ 'strtape.cloud', 'streamta.pe', 'strcloud.link', -- 2.45.2 From ca09cede9d791ec9bb5c0d3a77b951d6cb741378 Mon Sep 17 00:00:00 2001 From: GSTAR <44748406+GLlgGL@users.noreply.github.com> Date: Sat, 8 Nov 2025 09:14:35 +0100 Subject: [PATCH 28/33] Update Streamtape.ts --- src/extractor/Streamtape.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/extractor/Streamtape.ts b/src/extractor/Streamtape.ts index 8163655..25f3cf2 100644 --- a/src/extractor/Streamtape.ts +++ b/src/extractor/Streamtape.ts @@ -13,8 +13,7 @@ export class Streamtape extends Extractor { public readonly label = 'Streamtape (via MediaFlow Proxy)'; public supports(ctx: Context, url: URL): boolean { - const supportedDomain - = null !== url.host.match(/streamtape/) + const supportedDomain = null !== url.host.match(/streamtape/) || [ 'strtape.cloud', 'streamta.pe', @@ -38,8 +37,7 @@ export class Streamtape extends Extractor { ].includes(url.host); return supportedDomain && supportsMediaFlowProxy(ctx); -} - + public override normalize(url: URL): URL { return new URL(url.href.replace('/e/', '/v/')); } @@ -76,4 +74,4 @@ export class Streamtape extends Extractor { }, ]; } -} \ No newline at end of file +} -- 2.45.2 From 9fa318cbfac6a2b53203f72c8f33607734fed155 Mon Sep 17 00:00:00 2001 From: GSTAR <44748406+GLlgGL@users.noreply.github.com> Date: Sat, 8 Nov 2025 09:18:37 +0100 Subject: [PATCH 29/33] Update Streamtape.ts --- src/extractor/Streamtape.ts | 47 +++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/src/extractor/Streamtape.ts b/src/extractor/Streamtape.ts index 25f3cf2..1c9c4e7 100644 --- a/src/extractor/Streamtape.ts +++ b/src/extractor/Streamtape.ts @@ -15,29 +15,30 @@ export class Streamtape extends Extractor { public supports(ctx: Context, url: URL): boolean { const supportedDomain = null !== url.host.match(/streamtape/) || [ - 'strtape.cloud', - 'streamta.pe', - 'strcloud.link', - 'strcloud.club', - 'strtpe.link', - 'scloud.online', - 'stape.fun', - 'streamadblockplus.com', - 'shavetape.cash', - 'streamta.site', - 'streamadblocker.xyz', - 'tapewithadblock.org', - 'adblocktape.wiki', - 'antiadtape.com', - 'tapeblocker.com', - 'streamnoads.com', - 'tapeadvertisement.com', - 'tapeadsenjoyer.com', - 'watchadsontape.com', - ].includes(url.host); + 'strtape.cloud', + 'streamta.pe', + 'strcloud.link', + 'strcloud.club', + 'strtpe.link', + 'scloud.online', + 'stape.fun', + 'streamadblockplus.com', + 'shavetape.cash', + 'streamta.site', + 'streamadblocker.xyz', + 'tapewithadblock.org', + 'adblocktape.wiki', + 'antiadtape.com', + 'tapeblocker.com', + 'streamnoads.com', + 'tapeadvertisement.com', + 'tapeadsenjoyer.com', + 'watchadsontape.com', + ].includes(url.host); + + return supportedDomain && supportsMediaFlowProxy(ctx); + } - return supportedDomain && supportsMediaFlowProxy(ctx); - public override normalize(url: URL): URL { return new URL(url.href.replace('/e/', '/v/')); } @@ -74,4 +75,4 @@ export class Streamtape extends Extractor { }, ]; } -} +} \ No newline at end of file -- 2.45.2 From 06b48e795cd05567b19a9fde2f1af9a6bc6f5e51 Mon Sep 17 00:00:00 2001 From: GSTAR <44748406+GLlgGL@users.noreply.github.com> Date: Sat, 8 Nov 2025 09:18:48 +0100 Subject: [PATCH 30/33] Update Streamtape.ts --- src/extractor/Streamtape.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/extractor/Streamtape.ts b/src/extractor/Streamtape.ts index 1c9c4e7..7e1688d 100644 --- a/src/extractor/Streamtape.ts +++ b/src/extractor/Streamtape.ts @@ -75,4 +75,4 @@ export class Streamtape extends Extractor { }, ]; } -} \ No newline at end of file +} -- 2.45.2 From 2f679982a6c62f5bee845dc7ef325ef00fcf624a Mon Sep 17 00:00:00 2001 From: webstreamr <210764791+webstreamr@users.noreply.github.com> Date: Sat, 8 Nov 2025 22:49:09 +0900 Subject: [PATCH 31/33] Apply suggestions from code review --- src/extractor/Streamtape.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/extractor/Streamtape.ts b/src/extractor/Streamtape.ts index 7e1688d..5e6f431 100644 --- a/src/extractor/Streamtape.ts +++ b/src/extractor/Streamtape.ts @@ -74,5 +74,5 @@ export class Streamtape extends Extractor { }, }, ]; - } + }; } -- 2.45.2 From 44c710003570019ee86bba4bfde520a08ad67cc7 Mon Sep 17 00:00:00 2001 From: webstreamr <210764791+webstreamr@users.noreply.github.com> Date: Fri, 28 Nov 2025 16:54:25 +0100 Subject: [PATCH 32/33] Update src/extractor/Streamtape.ts --- src/extractor/Streamtape.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/extractor/Streamtape.ts b/src/extractor/Streamtape.ts index 5e6f431..cf116d0 100644 --- a/src/extractor/Streamtape.ts +++ b/src/extractor/Streamtape.ts @@ -43,11 +43,7 @@ export class Streamtape extends Extractor { return new URL(url.href.replace('/e/', '/v/')); } - protected async extractInternal( - ctx: Context, - url: URL, - meta: Meta, - ): Promise { + protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise { const headers = { Referer: meta.referer ?? url.href }; // Only needed to properly find non-existing files via 404 response -- 2.45.2 From 907b672ce47b184473a2ad3b101491a815f1b318 Mon Sep 17 00:00:00 2001 From: webstreamr <210764791+webstreamr@users.noreply.github.com> Date: Fri, 28 Nov 2025 16:54:52 +0100 Subject: [PATCH 33/33] Update src/extractor/Streamtape.ts --- src/extractor/Streamtape.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/extractor/Streamtape.ts b/src/extractor/Streamtape.ts index cf116d0..bbe3be6 100644 --- a/src/extractor/Streamtape.ts +++ b/src/extractor/Streamtape.ts @@ -43,7 +43,7 @@ export class Streamtape extends Extractor { return new URL(url.href.replace('/e/', '/v/')); } - protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise { + protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise { const headers = { Referer: meta.referer ?? url.href }; // Only needed to properly find non-existing files via 404 response -- 2.45.2