fix(extractor): support embed-only VOE urls
This commit is contained in:
parent
e5a5c62106
commit
a36d152b61
8 changed files with 266 additions and 6 deletions
|
|
@ -21,4 +21,8 @@ describe('Voe', () => {
|
|||
test('encoding error', async () => {
|
||||
expect(await extractorRegistry.handle(ctx, new URL('https://voe.sx/e/c2yxvit4f6bz'))).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('embed only urls which otherwise lead to 404', async () => {
|
||||
expect(await extractorRegistry.handle(ctx, new URL('https://mikaylaarealike.com/e/gqlhm9hbobwu'))).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import * as cheerio from 'cheerio';
|
|||
import { NotFoundError } from '../error';
|
||||
import { Context, Format, Meta, UrlResult } from '../types';
|
||||
import {
|
||||
buildMediaFlowProxyExtractorStreamUrl,
|
||||
buildMediaFlowProxyExtractorStreamUrl, guessHeightFromPlaylist,
|
||||
supportsMediaFlowProxy,
|
||||
} from '../utils';
|
||||
import { Extractor } from './Extractor';
|
||||
|
|
@ -81,6 +81,7 @@ export class Voe extends Extractor {
|
|||
'maxfinishseveral.com',
|
||||
'metagnathtuggers.com',
|
||||
'michaelapplysome.com',
|
||||
'mikaylaarealike.com',
|
||||
'nathanfromsubject.com',
|
||||
'nectareousoverelate.com',
|
||||
'nonesnanking.com',
|
||||
|
|
@ -121,7 +122,18 @@ export class Voe extends Extractor {
|
|||
protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise<UrlResult[]> {
|
||||
const headers = { Referer: meta.referer ?? url.href };
|
||||
|
||||
const html = await this.fetcher.text(ctx, url, { headers });
|
||||
let html: string;
|
||||
try {
|
||||
html = await this.fetcher.text(ctx, url, { headers });
|
||||
} catch (error) {
|
||||
/* istanbul ignore next */
|
||||
if (error instanceof NotFoundError && !url.href.includes('/e/')) {
|
||||
return await this.extractInternal(ctx, new URL(`/e${url.pathname}`, url.origin), meta);
|
||||
}
|
||||
|
||||
/* istanbul ignore next */
|
||||
throw error;
|
||||
}
|
||||
|
||||
const redirectMatch = html.match(/window\.location\.href\s*=\s*'([^']+)/);
|
||||
if (redirectMatch && redirectMatch[1]) {
|
||||
|
|
@ -136,10 +148,13 @@ export class Voe extends Extractor {
|
|||
const title = $('meta[name="description"]').attr('content')?.trim().replace(/^Watch /, '').replace(/ at VOE$/, '').trim();
|
||||
|
||||
const sizeMatch = html.matchAll(/[\d.]+ ?[GM]B/g).toArray().at(-1);
|
||||
const heightMatch = html.match(/<b>(\d{3,})p<\/b>/);
|
||||
const size = sizeMatch ? bytes.parse(sizeMatch[0] as string) as number : /* istanbul ignore next */null;
|
||||
|
||||
const playlistUrl = await buildMediaFlowProxyExtractorStreamUrl(ctx, this.fetcher, 'Voe', url, headers);
|
||||
|
||||
const heightMatch = html.match(/<b>(\d{3,})p<\/b>/);
|
||||
const height = heightMatch ? parseInt(heightMatch[1] as string) : await guessHeightFromPlaylist(ctx, this.fetcher, playlistUrl, url);
|
||||
|
||||
return [
|
||||
{
|
||||
url: playlistUrl,
|
||||
|
|
@ -149,9 +164,9 @@ export class Voe extends Extractor {
|
|||
ttl: this.ttl,
|
||||
meta: {
|
||||
...meta,
|
||||
height,
|
||||
title,
|
||||
...(sizeMatch && { bytes: bytes.parse(sizeMatch[0] as string) as number }),
|
||||
...(heightMatch && { height: parseInt(heightMatch[1] as string) }),
|
||||
...(size && size > 16777216 && { bytes: size }),
|
||||
},
|
||||
},
|
||||
];
|
||||
|
|
|
|||
10
src/extractor/__fixtures__/Voe/https:mediaflow.test.org-cafbcf38a7db8a9fa26e61ab5818b00a
generated
Normal file
10
src/extractor/__fixtures__/Voe/https:mediaflow.test.org-cafbcf38a7db8a9fa26e61ab5818b00a
generated
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
#EXTM3U
|
||||
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=2360882,RESOLUTION=1920x800,FRAME-RATE=24.000,CODECS="avc1.640028,mp4a.40.2",VIDEO-RANGE=SDR
|
||||
https://mediaflow.test.org/proxy/hls/manifest.m3u8?api_password=test&h_user-agent=Mozilla%2F5.0+%28Windows+NT+10.0%3B+Win64%3B+x64%29+AppleWebKit%2F537.36+%28KHTML%2C+like+Gecko%29+Chrome%2F136.0.0.0+Safari%2F537.36&h_referer=https%3A%2F%2Fmegakino.zone%2Ffilms%2F5643-weapons-die-stunde-des-verschwindens.html&d=https%3A%2F%2Fcdn-d26zysohjhaqkqbo.edgeon-bandwidth.com%2Fengine%2Fhls2-c%2F01%2F15323%2Fgqlhm9hbobwu_%2Ch%2Cn%2Cl%2C.urlset%2Findex-f1-v1-a1.m3u8%3Ft%3DLo_hAZk_EyHI5_dLs3ZUoZw1c0MuAxFI2yP1RpQ6-uc%26s%3D1762027963%26e%3D14400%26f%3D76616583%26node%3Dia36tgOsJ%2FbrWvSiHYab9Xb5naFXt8hAaWo1zPiAuGM%3D%26i%3D104.28%26sp%3D2500%26asn%3D13335%26q%3Dh%2Cn%2Cl%26rq%3D9aiQDIsNP9QQ9F2Pz2IqIMbDFUWO16qX8WvcWPet
|
||||
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=1339419,RESOLUTION=1728x720,FRAME-RATE=24.000,CODECS="avc1.640020,mp4a.40.2",VIDEO-RANGE=SDR
|
||||
https://mediaflow.test.org/proxy/hls/manifest.m3u8?api_password=test&h_user-agent=Mozilla%2F5.0+%28Windows+NT+10.0%3B+Win64%3B+x64%29+AppleWebKit%2F537.36+%28KHTML%2C+like+Gecko%29+Chrome%2F136.0.0.0+Safari%2F537.36&h_referer=https%3A%2F%2Fmegakino.zone%2Ffilms%2F5643-weapons-die-stunde-des-verschwindens.html&d=https%3A%2F%2Fcdn-d26zysohjhaqkqbo.edgeon-bandwidth.com%2Fengine%2Fhls2-c%2F01%2F15323%2Fgqlhm9hbobwu_%2Ch%2Cn%2Cl%2C.urlset%2Findex-f2-v1-a1.m3u8%3Ft%3DLo_hAZk_EyHI5_dLs3ZUoZw1c0MuAxFI2yP1RpQ6-uc%26s%3D1762027963%26e%3D14400%26f%3D76616583%26node%3Dia36tgOsJ%2FbrWvSiHYab9Xb5naFXt8hAaWo1zPiAuGM%3D%26i%3D104.28%26sp%3D2500%26asn%3D13335%26q%3Dh%2Cn%2Cl%26rq%3D9aiQDIsNP9QQ9F2Pz2IqIMbDFUWO16qX8WvcWPet
|
||||
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=818334,RESOLUTION=1152x480,FRAME-RATE=24.000,CODECS="avc1.64001f,mp4a.40.2",VIDEO-RANGE=SDR
|
||||
https://mediaflow.test.org/proxy/hls/manifest.m3u8?api_password=test&h_user-agent=Mozilla%2F5.0+%28Windows+NT+10.0%3B+Win64%3B+x64%29+AppleWebKit%2F537.36+%28KHTML%2C+like+Gecko%29+Chrome%2F136.0.0.0+Safari%2F537.36&h_referer=https%3A%2F%2Fmegakino.zone%2Ffilms%2F5643-weapons-die-stunde-des-verschwindens.html&d=https%3A%2F%2Fcdn-d26zysohjhaqkqbo.edgeon-bandwidth.com%2Fengine%2Fhls2-c%2F01%2F15323%2Fgqlhm9hbobwu_%2Ch%2Cn%2Cl%2C.urlset%2Findex-f3-v1-a1.m3u8%3Ft%3DLo_hAZk_EyHI5_dLs3ZUoZw1c0MuAxFI2yP1RpQ6-uc%26s%3D1762027963%26e%3D14400%26f%3D76616583%26node%3Dia36tgOsJ%2FbrWvSiHYab9Xb5naFXt8hAaWo1zPiAuGM%3D%26i%3D104.28%26sp%3D2500%26asn%3D13335%26q%3Dh%2Cn%2Cl%26rq%3D9aiQDIsNP9QQ9F2Pz2IqIMbDFUWO16qX8WvcWPet
|
||||
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=47743,RESOLUTION=1920x800,CODECS="avc1.640028",URI="https://mediaflow.test.org/proxy/hls/manifest.m3u8?api_password=test&h_user-agent=Mozilla%2F5.0+%28Windows+NT+10.0%3B+Win64%3B+x64%29+AppleWebKit%2F537.36+%28KHTML%2C+like+Gecko%29+Chrome%2F136.0.0.0+Safari%2F537.36&h_referer=https%3A%2F%2Fmegakino.zone%2Ffilms%2F5643-weapons-die-stunde-des-verschwindens.html&d=https%3A%2F%2Fcdn-d26zysohjhaqkqbo.edgeon-bandwidth.com%2Fengine%2Fhls2-c%2F01%2F15323%2Fgqlhm9hbobwu_%2Ch%2Cn%2Cl%2C.urlset%2Fiframes-f1-v1-a1.m3u8%3Ft%3DLo_hAZk_EyHI5_dLs3ZUoZw1c0MuAxFI2yP1RpQ6-uc%26s%3D1762027963%26e%3D14400%26f%3D76616583%26node%3Dia36tgOsJ%2FbrWvSiHYab9Xb5naFXt8hAaWo1zPiAuGM%3D%26i%3D104.28%26sp%3D2500%26asn%3D13335%26q%3Dh%2Cn%2Cl%26rq%3D9aiQDIsNP9QQ9F2Pz2IqIMbDFUWO16qX8WvcWPet",VIDEO-RANGE=SDR
|
||||
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=31901,RESOLUTION=1728x720,CODECS="avc1.640020",URI="https://mediaflow.test.org/proxy/hls/manifest.m3u8?api_password=test&h_user-agent=Mozilla%2F5.0+%28Windows+NT+10.0%3B+Win64%3B+x64%29+AppleWebKit%2F537.36+%28KHTML%2C+like+Gecko%29+Chrome%2F136.0.0.0+Safari%2F537.36&h_referer=https%3A%2F%2Fmegakino.zone%2Ffilms%2F5643-weapons-die-stunde-des-verschwindens.html&d=https%3A%2F%2Fcdn-d26zysohjhaqkqbo.edgeon-bandwidth.com%2Fengine%2Fhls2-c%2F01%2F15323%2Fgqlhm9hbobwu_%2Ch%2Cn%2Cl%2C.urlset%2Fiframes-f2-v1-a1.m3u8%3Ft%3DLo_hAZk_EyHI5_dLs3ZUoZw1c0MuAxFI2yP1RpQ6-uc%26s%3D1762027963%26e%3D14400%26f%3D76616583%26node%3Dia36tgOsJ%2FbrWvSiHYab9Xb5naFXt8hAaWo1zPiAuGM%3D%26i%3D104.28%26sp%3D2500%26asn%3D13335%26q%3Dh%2Cn%2Cl%26rq%3D9aiQDIsNP9QQ9F2Pz2IqIMbDFUWO16qX8WvcWPet",VIDEO-RANGE=SDR
|
||||
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=20190,RESOLUTION=1152x480,CODECS="avc1.64001f",URI="https://mediaflow.test.org/proxy/hls/manifest.m3u8?api_password=test&h_user-agent=Mozilla%2F5.0+%28Windows+NT+10.0%3B+Win64%3B+x64%29+AppleWebKit%2F537.36+%28KHTML%2C+like+Gecko%29+Chrome%2F136.0.0.0+Safari%2F537.36&h_referer=https%3A%2F%2Fmegakino.zone%2Ffilms%2F5643-weapons-die-stunde-des-verschwindens.html&d=https%3A%2F%2Fcdn-d26zysohjhaqkqbo.edgeon-bandwidth.com%2Fengine%2Fhls2-c%2F01%2F15323%2Fgqlhm9hbobwu_%2Ch%2Cn%2Cl%2C.urlset%2Fiframes-f3-v1-a1.m3u8%3Ft%3DLo_hAZk_EyHI5_dLs3ZUoZw1c0MuAxFI2yP1RpQ6-uc%26s%3D1762027963%26e%3D14400%26f%3D76616583%26node%3Dia36tgOsJ%2FbrWvSiHYab9Xb5naFXt8hAaWo1zPiAuGM%3D%26i%3D104.28%26sp%3D2500%26asn%3D13335%26q%3Dh%2Cn%2Cl%26rq%3D9aiQDIsNP9QQ9F2Pz2IqIMbDFUWO16qX8WvcWPet",VIDEO-RANGE=SDR
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"destination_url":"https://cdn-thhhlmzuxvpehc9r.orbitcache.com/engine/hls2-c/01/11526/qqfyi04w52mj_,l,.urlset/master.m3u8?t=g2zuS_OAHTtjrVe0bHp2xYcX7PpDxg8ebRIHTmF3H1c&s=1759861731&e=14400&f=57634053&node=UOx5276IumOH4BADikx4rlHC+ibdAWZ+mo07sx3WfaU=&i=104.28&sp=2500&asn=13335&q=l&rq=FxkyWdISXylA0UaLSPNcXWkDrksjs0pkAzy0GvIS","request_headers":{"user-agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36","referer":"https://jilliandescribecompany.com/qqfyi04w52mj"},"mediaflow_proxy_url":"https://mediaflow.test.org/proxy/hls/manifest.m3u8","query_params":{"api_password":"test"}}
|
||||
205
src/extractor/__fixtures__/Voe/https:mikaylaarealike.comegqlhm9hbobwu
generated
Normal file
205
src/extractor/__fixtures__/Voe/https:mikaylaarealike.comegqlhm9hbobwu
generated
Normal file
File diff suppressed because one or more lines are too long
1
src/extractor/__fixtures__/Voe/https:mikaylaarealike.comgqlhm9hbobwu.error
generated
Normal file
1
src/extractor/__fixtures__/Voe/https:mikaylaarealike.comgqlhm9hbobwu.error
generated
Normal file
|
|
@ -0,0 +1 @@
|
|||
Fetcher error: 404: Not Found
|
||||
|
|
@ -1,5 +1,22 @@
|
|||
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||
|
||||
exports[`Voe embed only urls which otherwise lead to 404 1`] = `
|
||||
[
|
||||
{
|
||||
"format": "hls",
|
||||
"label": "VOE (via MediaFlow Proxy)",
|
||||
"meta": {
|
||||
"countryCodes": [],
|
||||
"height": 800,
|
||||
"title": "Weapons.2025.German.DL.1080p.WEB.H264-ZeroTwo_encoded.mp4",
|
||||
},
|
||||
"sourceId": "voe_",
|
||||
"ttl": 900000,
|
||||
"url": "https://mediaflow.test.org/proxy/hls/manifest.m3u8?api_password=test&h_user-agent=Mozilla%2F5.0+%28Windows+NT+10.0%3B+Win64%3B+x64%29+AppleWebKit%2F537.36+%28KHTML%2C+like+Gecko%29+Chrome%2F136.0.0.0+Safari%2F537.36&h_referer=https%3A%2F%2Fjilliandescribecompany.com%2Fqqfyi04w52mj&d=https%3A%2F%2Fcdn-thhhlmzuxvpehc9r.orbitcache.com%2Fengine%2Fhls2-c%2F01%2F11526%2Fqqfyi04w52mj_%2Cl%2C.urlset%2Fmaster.m3u8%3Ft%3Dg2zuS_OAHTtjrVe0bHp2xYcX7PpDxg8ebRIHTmF3H1c%26s%3D1759861731%26e%3D14400%26f%3D57634053%26node%3DUOx5276IumOH4BADikx4rlHC%2BibdAWZ%2Bmo07sx3WfaU%3D%26i%3D104.28%26sp%3D2500%26asn%3D13335%26q%3Dl%26rq%3DFxkyWdISXylA0UaLSPNcXWkDrksjs0pkAzy0GvIS",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`Voe encoding error 1`] = `[]`;
|
||||
|
||||
exports[`Voe jilliandescribecompany 1`] = `
|
||||
|
|
@ -28,6 +45,7 @@ exports[`Voe premium only without resolution 1`] = `
|
|||
"meta": {
|
||||
"bytes": 363216240,
|
||||
"countryCodes": [],
|
||||
"height": 800,
|
||||
"title": "268836--21ba9fb2-97f9-4566-b5eb-3d9cef7674cf--zrxn--2352322-voesx.mp4",
|
||||
},
|
||||
"sourceId": "voe_",
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import CachePolicy from 'http-cache-semantics';
|
|||
import slugify from 'slugify';
|
||||
import { RequestInit } from 'undici';
|
||||
import winston from 'winston';
|
||||
import { NotFoundError } from '../error';
|
||||
import { Context } from '../types';
|
||||
import { envGet } from './env';
|
||||
import { Fetcher, HttpCacheItem } from './Fetcher';
|
||||
|
|
@ -67,7 +68,12 @@ export class FetcherMock extends Fetcher {
|
|||
const isHead = init?.method === 'HEAD';
|
||||
|
||||
if (fs.existsSync(errorPath)) {
|
||||
throw new Error(fs.readFileSync(errorPath).toString());
|
||||
const message = fs.readFileSync(errorPath).toString();
|
||||
if (message.includes('404: Not Found')) {
|
||||
throw new NotFoundError(message);
|
||||
}
|
||||
|
||||
throw new Error(message);
|
||||
} else if (fs.existsSync(path)) {
|
||||
const data = fs.readFileSync(path).toString();
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue