diff --git a/src/handler/FrenchCloud.test.ts b/src/handler/FrenchCloud.test.ts new file mode 100644 index 0000000..e2fcfdd --- /dev/null +++ b/src/handler/FrenchCloud.test.ts @@ -0,0 +1,48 @@ +import { FrenchCloud } from './FrenchCloud'; +import { Fetcher } from '../utils'; +import { Dropload, EmbedExtractors, SuperVideo } from '../embed-extractor'; +import { Context } from '../types'; +jest.mock('../utils/Fetcher'); + +// @ts-expect-error No constructor args needed +const fetcher = new Fetcher(); +const frenchcloud = new FrenchCloud(fetcher, new EmbedExtractors([new Dropload(fetcher), new SuperVideo(fetcher)])); +const ctx: Context = { ip: '127.0.0.1' }; + +describe('FrenchCloud', () => { + test('does not handle non imdb movies', async () => { + const streams = await frenchcloud.handle(ctx, 'kitsu:123'); + + expect(streams).toHaveLength(0); + }); + + test('handles non-existent movies gracefully', async () => { + const streams = await frenchcloud.handle(ctx, 'tt12345678'); + + expect(streams).toHaveLength(0); + }); + + test('handle imdb the devil\'s bath', async () => { + const streams = await frenchcloud.handle(ctx, 'tt29141112'); + + expect(streams).toHaveLength(2); + expect(streams[0]).toStrictEqual({ + url: expect.any(URL), + label: 'SuperVideo', + sourceId: 'supervideo', + height: 720, + bytes: 966682214, + language: 'fr', + }); + expect(streams[0]?.url.href).toMatch(/^https:\/\/.*?.m3u8/); + expect(streams[1]).toStrictEqual({ + url: expect.any(URL), + label: 'Dropload', + sourceId: 'dropload', + height: 720, + bytes: 966682214, + language: 'fr', + }); + expect(streams[1]?.url.href).toMatch(/^https:\/\/.*?.m3u8/); + }); +}); diff --git a/src/handler/FrenchCloud.ts b/src/handler/FrenchCloud.ts new file mode 100644 index 0000000..e47b9e9 --- /dev/null +++ b/src/handler/FrenchCloud.ts @@ -0,0 +1,41 @@ +import * as cheerio from 'cheerio'; +import { Handler } from './types'; +import { Fetcher, parseImdbId } from '../utils'; +import { EmbedExtractors } from '../embed-extractor'; +import { Context } from '../types'; + +export class FrenchCloud implements Handler { + readonly id = 'frenchcloud'; + + readonly label = 'FrenchCloud'; + + readonly contentTypes = ['movie']; + + readonly languages = ['fr']; + + private readonly fetcher: Fetcher; + private readonly embedExtractors: EmbedExtractors; + + constructor(fetcher: Fetcher, embedExtractors: EmbedExtractors) { + this.fetcher = fetcher; + this.embedExtractors = embedExtractors; + } + + readonly handle = async (ctx: Context, id: string) => { + if (!id.startsWith('tt')) { + return []; + } + + const html = await this.fetcher.text(ctx, new URL(`https://frenchcloud.cam/movie/${parseImdbId(id).id}`)); + + const $ = cheerio.load(html); + + return Promise.all( + $('[data-link!=""]') + .map((_i, el) => new URL(($(el).attr('data-link') as string).replace(/^(https:)?\/\//, 'https://'))) + .toArray() + .filter(embedUrl => embedUrl.host.match(/(dropload|supervideo)/)) + .map(embedUrl => this.embedExtractors.handle(ctx, embedUrl, 'fr')), + ); + }; +} diff --git a/src/handler/index.ts b/src/handler/index.ts index 023ea97..192f38f 100644 --- a/src/handler/index.ts +++ b/src/handler/index.ts @@ -1,3 +1,4 @@ +export * from './FrenchCloud'; export * from './KinoKiste'; export * from './MeineCloud'; export * from './types'; diff --git a/src/index.ts b/src/index.ts index 594ce30..b856c7e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,7 +3,7 @@ import makeFetchHappen from 'make-fetch-happen'; import { flag } from 'country-emoji'; import winston from 'winston'; import { landingTemplate } from './landingTemplate'; -import { Handler, KinoKiste, MeineCloud } from './handler'; +import { FrenchCloud, Handler, KinoKiste, MeineCloud } from './handler'; import { Dropload, EmbedExtractors, SuperVideo } from './embed-extractor'; import { buildManifest, Fetcher } from './utils'; import { Config, UrlResult } from './types'; @@ -40,6 +40,7 @@ const embedExtractors = new EmbedExtractors([ ]); const handlers: Handler[] = [ + new FrenchCloud(fetcher, embedExtractors), new KinoKiste(fetcher, embedExtractors), new MeineCloud(fetcher, embedExtractors), ]; diff --git a/src/utils/__fixtures__/Fetcher/https:dropload.iow4yit5wiia9y.html b/src/utils/__fixtures__/Fetcher/https:dropload.iow4yit5wiia9y.html new file mode 100644 index 0000000..0dda4e0 --- /dev/null +++ b/src/utils/__fixtures__/Fetcher/https:dropload.iow4yit5wiia9y.html @@ -0,0 +1,502 @@ + + + +
+ +
+
+
+