diff --git a/src/extractor/SaveFiles.test.ts b/src/extractor/SaveFiles.test.ts new file mode 100644 index 0000000..3d2cd7d --- /dev/null +++ b/src/extractor/SaveFiles.test.ts @@ -0,0 +1,21 @@ +import winston from 'winston'; +import { createTestContext } from '../test'; +import { CountryCode } from '../types'; +import { FetcherMock } from '../utils'; +import { ExtractorRegistry } from './ExtractorRegistry'; +import { SaveFiles } from './SaveFiles'; + +const logger = winston.createLogger({ transports: [new winston.transports.Console({ level: 'nope' })] }); +const extractorRegistry = new ExtractorRegistry(logger, [new SaveFiles(new FetcherMock(`${__dirname}/__fixtures__/SaveFiles`))]); + +const ctx = createTestContext(); + +describe('SafeFiles', () => { + test('savefiles', async () => { + expect(await extractorRegistry.handle(ctx, new URL('https://savefiles.com/ip0k0dj2g0i3'), CountryCode.en)).toMatchSnapshot(); + }); + + test('streamhls /e/', async () => { + expect(await extractorRegistry.handle(ctx, new URL('https://streamhls.to/e/ip0k0dj2g0i3'), CountryCode.en)).toMatchSnapshot(); + }); +}); diff --git a/src/extractor/SaveFiles.ts b/src/extractor/SaveFiles.ts new file mode 100644 index 0000000..c18e555 --- /dev/null +++ b/src/extractor/SaveFiles.ts @@ -0,0 +1,51 @@ +import * as cheerio from 'cheerio'; +import { Context, CountryCode, Format, UrlResult } from '../types'; +import { Fetcher } from '../utils'; +import { Extractor } from './Extractor'; + +export class SaveFiles extends Extractor { + public readonly id = 'savefiles'; + + public readonly label = 'SaveFiles'; + + private readonly fetcher: Fetcher; + + public constructor(fetcher: Fetcher) { + super(); + + this.fetcher = fetcher; + } + + public supports(_ctx: Context, url: URL): boolean { + return null !== url.host.match(/savefiles|streamhls/); + } + + public override normalize(url: URL): URL { + return new URL(url.href.replace('/e/', '/')); + } + + protected async extractInternal(ctx: Context, url: URL, countryCode: CountryCode): Promise { + const html = await this.fetcher.text(ctx, url); + + const fileMatch = html.match(/file:"(.*?)"/) as string[]; + const sizeMatch = html.match(/\[\d{3,}x(\d{3,})/) as string[]; + + const $ = cheerio.load(html); + const title = $('.download-title').text().trim(); + + return [ + { + url: new URL(fileMatch[1] as string), + format: Format.hls, + label: this.label, + sourceId: `${this.id}_${countryCode}`, + ttl: this.ttl, + meta: { + countryCodes: [countryCode], + title, + height: parseInt(sizeMatch[1] as string), + }, + }, + ]; + }; +} diff --git a/src/extractor/__fixtures__/SaveFiles/https:savefiles.comip0k0dj2g0i3 b/src/extractor/__fixtures__/SaveFiles/https:savefiles.comip0k0dj2g0i3 new file mode 100644 index 0000000..8e86657 --- /dev/null +++ b/src/extractor/__fixtures__/SaveFiles/https:savefiles.comip0k0dj2g0i3 @@ -0,0 +1,1655 @@ + + + + + + + + + + + + + Watch King The Hill S14E01 1080p HEVC x265 Gusta + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + + + + + + + +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ + + + + + + +
+
+
+ + + + + + + +
+
+
+ + +
+
+
+

+ King Of The Hill S14E01 1080p HEVC x265-MeGusta + +

+
+
+ on Aug 6, 2025 +
+ + +
+
+
+
+
+

uploaded with File Uploader (z-o-o-m.eu)

+
+ + +
+ +
+
+
+
+ + + + + + + + + + + +
+ +
+ +
+
+ + +
+
+
+
+ + +
+
+ +
+ +
+ + +
+
+ +
+
+ + + + + + + +
+ + + + + + + +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + \ No newline at end of file diff --git a/src/extractor/__fixtures__/SaveFiles/https:streamhls.toip0k0dj2g0i3 b/src/extractor/__fixtures__/SaveFiles/https:streamhls.toip0k0dj2g0i3 new file mode 100644 index 0000000..e122430 --- /dev/null +++ b/src/extractor/__fixtures__/SaveFiles/https:streamhls.toip0k0dj2g0i3 @@ -0,0 +1,1655 @@ + + + + + + + + + + + + + Watch King The Hill S14E01 1080p HEVC x265 Gusta + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + + + + + + + +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ + + + + + + +
+
+
+ + + + + + + +
+
+
+ + +
+
+
+

+ King Of The Hill S14E01 1080p HEVC x265-MeGusta + +

+
+
+ on Aug 6, 2025 +
+ + +
+
+
+
+
+

uploaded with File Uploader (z-o-o-m.eu)

+
+ + +
+ +
+
+
+
+ + + + + + + + + + + +
+ +
+ +
+
+ + +
+
+
+
+ + +
+
+ +
+ +
+ + +
+
+ +
+
+ + + + + + + +
+ + + + + + + +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + \ No newline at end of file diff --git a/src/extractor/__snapshots__/SaveFiles.test.ts.snap b/src/extractor/__snapshots__/SaveFiles.test.ts.snap new file mode 100644 index 0000000..0823297 --- /dev/null +++ b/src/extractor/__snapshots__/SaveFiles.test.ts.snap @@ -0,0 +1,39 @@ +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing + +exports[`SafeFiles savefiles 1`] = ` +[ + { + "format": "hls", + "label": "SaveFiles", + "meta": { + "countryCodes": [ + "en", + ], + "height": 720, + "title": "King Of The Hill S14E01 1080p HEVC x265-MeGusta", + }, + "sourceId": "savefiles_en", + "ttl": 900000, + "url": "https://s3.savefiles.com/hls2/01/00153/,ip0k0dj2g0i3_n,lang/eng/ip0k0dj2g0i3_eng,.urlset/master.m3u8?t=Emm2ipTW9Db4fBNcQqc8qNju3Yysg0GjGoYiC0FB-G4&s=1755078897&e=43200&v=406428&srv=s1&i=0.0&sp=0&fr=ip0k0dj2g0i3", + }, +] +`; + +exports[`SafeFiles streamhls /e/ 1`] = ` +[ + { + "format": "hls", + "label": "SaveFiles", + "meta": { + "countryCodes": [ + "en", + ], + "height": 720, + "title": "King Of The Hill S14E01 1080p HEVC x265-MeGusta", + }, + "sourceId": "savefiles_en", + "ttl": 900000, + "url": "https://s3.savefiles.com/hls2/01/00153/,ip0k0dj2g0i3_n,lang/eng/ip0k0dj2g0i3_eng,.urlset/master.m3u8?t=Emm2ipTW9Db4fBNcQqc8qNju3Yysg0GjGoYiC0FB-G4&s=1755078897&e=43200&v=406428&srv=s1&i=0.0&sp=0&fr=ip0k0dj2g0i3", + }, +] +`; diff --git a/src/extractor/index.ts b/src/extractor/index.ts index 16fe077..1eb94ac 100644 --- a/src/extractor/index.ts +++ b/src/extractor/index.ts @@ -6,6 +6,7 @@ import { Extractor } from './Extractor'; import { Fastream } from './Fastream'; import { KinoGer } from './KinoGer'; import { Mixdrop } from './Mixdrop'; +import { SaveFiles } from './SaveFiles'; import { Soaper } from './Soaper'; import { StreamEmbed } from './StreamEmbed'; import { Streamtape } from './Streamtape'; @@ -23,6 +24,7 @@ export const createExtractors = (fetcher: Fetcher): Extractor[] => [ new Fastream(fetcher), new KinoGer(fetcher), new Mixdrop(fetcher), + new SaveFiles(fetcher), new Soaper(fetcher), new StreamEmbed(fetcher), new Streamtape(fetcher),