From ca0db32d196170493684fd4eac4f91879f0560e0 Mon Sep 17 00:00:00 2001 From: WebStreamr <210764791+webstreamr@users.noreply.github.com> Date: Sun, 13 Jul 2025 19:21:13 +0000 Subject: [PATCH] feat(source): add Movix --- src/index.ts | 3 +- src/source/Movix.test.ts | 23 +++ src/source/Movix.ts | 45 ++++++ .../https:api.movix.siteapitmdbmovie3176 | 1 + ...movix.siteapitmdbtv42009season4andepisode2 | 1 + src/source/__snapshots__/Movix.test.ts.snap | 136 ++++++++++++++++++ src/source/index.ts | 1 + 7 files changed, 209 insertions(+), 1 deletion(-) create mode 100644 src/source/Movix.test.ts create mode 100644 src/source/Movix.ts create mode 100644 src/source/__fixtures__/Movix/https:api.movix.siteapitmdbmovie3176 create mode 100644 src/source/__fixtures__/Movix/https:api.movix.siteapitmdbtv42009season4andepisode2 create mode 100644 src/source/__snapshots__/Movix.test.ts.snap diff --git a/src/index.ts b/src/index.ts index 0f7662b..2a972a8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,7 +5,7 @@ import { v4 as uuidv4 } from 'uuid'; import winston from 'winston'; import { ConfigureController, ManifestController, StreamController } from './controller'; import { createExtractors, ExtractorRegistry } from './extractor'; -import { CineHDPlus, Cuevana, Eurostreaming, Frembed, FrenchCloud, KinoGer, MegaKino, MeineCloud, MostraGuarda, Soaper, Source, StreamKiste, VerHdLink, VidSrc, VixSrc } from './source'; +import { CineHDPlus, Cuevana, Eurostreaming, Frembed, FrenchCloud, KinoGer, MegaKino, MeineCloud, MostraGuarda, Movix, Soaper, Source, StreamKiste, VerHdLink, VidSrc, VixSrc } from './source'; import { envGet, envIsProd, Fetcher, StreamResolver } from './utils'; const logger = winston.createLogger({ @@ -47,6 +47,7 @@ const sources: Source[] = [ // FR new Frembed(fetcher), new FrenchCloud(fetcher), + new Movix(fetcher), // IT new Eurostreaming(fetcher), new MostraGuarda(fetcher), diff --git a/src/source/Movix.test.ts b/src/source/Movix.test.ts new file mode 100644 index 0000000..447dccd --- /dev/null +++ b/src/source/Movix.test.ts @@ -0,0 +1,23 @@ +import { createTestContext } from '../test'; +import { FetcherMock, TmdbId } from '../utils'; +import { Movix } from './Movix'; + +const ctx = createTestContext({ fr: 'on' }); + +describe('Movix', () => { + let handler: Movix; + + beforeEach(() => { + handler = new Movix(new FetcherMock(`${__dirname}/__fixtures__/Movix`)); + }); + + test('handle tmdb black mirror s4e2', async () => { + const streams = await handler.handle(ctx, 'series', new TmdbId(42009, 4, 2)); + expect(streams).toMatchSnapshot(); + }); + + test('handle battle royal', async () => { + const streams = await handler.handle(ctx, 'movie', new TmdbId(3176, undefined, undefined)); + expect(streams).toMatchSnapshot(); + }); +}); diff --git a/src/source/Movix.ts b/src/source/Movix.ts new file mode 100644 index 0000000..fb98f2b --- /dev/null +++ b/src/source/Movix.ts @@ -0,0 +1,45 @@ +import { ContentType } from 'stremio-addon-sdk'; +import { Context, CountryCode } from '../types'; +import { Fetcher, getTmdbId, Id } from '../utils'; +import { Source, SourceResult } from './types'; + +interface MovixApiData { + player_links: { decoded_url: string }[]; +} + +export class Movix implements Source { + public readonly id = 'movix'; + + public readonly label = 'Movix'; + + public readonly contentTypes: ContentType[] = ['movie', 'series']; + + public readonly countryCodes: CountryCode[] = [CountryCode.fr]; + + private readonly baseUrl = 'https://api.movix.site'; + + private readonly fetcher: Fetcher; + + public constructor(fetcher: Fetcher) { + this.fetcher = fetcher; + } + + public async handle(ctx: Context, _type: string, id: Id): Promise { + const tmdbId = await getTmdbId(ctx, this.fetcher, id); + + const apiUrl = tmdbId.season + ? new URL(`/api/tmdb/tv/${tmdbId.id}?season=${tmdbId.season}&episode=${tmdbId.episode}`, this.baseUrl) + : new URL(`/api/tmdb/movie/${tmdbId.id}`, this.baseUrl); + + const json = JSON.parse(await this.fetcher.text(ctx, apiUrl)); + const data: MovixApiData = tmdbId.season ? json['current_episode'] : json; + + const urls: URL[] = data['player_links'].map(({ decoded_url }) => new URL(decoded_url)); + + const title = tmdbId.season + ? `${json['tmdb_details']['title']} ${tmdbId.season}x${tmdbId.episode}` + : json['tmdb_details']['title']; + + return urls.map(url => ({ countryCode: CountryCode.fr, title, url })); + }; +} diff --git a/src/source/__fixtures__/Movix/https:api.movix.siteapitmdbmovie3176 b/src/source/__fixtures__/Movix/https:api.movix.siteapitmdbmovie3176 new file mode 100644 index 0000000..092cbbb --- /dev/null +++ b/src/source/__fixtures__/Movix/https:api.movix.siteapitmdbmovie3176 @@ -0,0 +1 @@ +{"tmdb_details":{"id":3176,"title":"Battle Royale","original_title":"バトル・ロワイアル","release_date":"2000-12-16","poster_path":"/laUmjb39U6glMrOKyaco4QPeGaC.jpg","backdrop_path":"/amBvmIshdsSkOtvVIgxl7YSQ9Dg.jpg","overview":"Dans un avenir proche, les élèves de la classe B de 3ème du collège Shiroiwa ont été amenés sur une île déserte par une armée mystérieuse. Un adulte surgit tout à coup devant eux : leur ancien professeur Kitano. Il leur annonce qu'ils vont participer à un jeu de massacre dont la règle consiste à s'entretuer. Seul le dernier des survivants pourra regagner son foyer. Kitano leur présente deux nouveaux élèves très inquiétants. Des coups de feu retentissent pour convaincre les incrédules. Selon la loi de réforme de l'éducation pour le nouveau siècle, ce sacrifice permettra de former des adultes sains. Abandonnés chacun à son sort avec de la nourriture et une arme, les adolescents disposent d'un délai de trois jours pour s'entretuer.","vote_average":7.284},"iframe_src":"https://lecteurvideo.com/embed.php?id=17959&ads=true","player_links":[{"decoded_url":"https://lecteur6.com/video/18085327b86002fc604c323b9a07f997","quality":"Vidéo HD / PAS DE PUBLICITE 2","language":"French"},{"decoded_url":"https://vidoza.net/embed-55hvynbdyqvc.html","quality":"Vid Vidéo 6 / PAS DE PUBLICITE","language":"French"},{"decoded_url":"https://wishonly.site/e/kykhuv6tscs8","quality":"Wish Vidéo 4 / PAS DE PUBLICITE","language":"French"},{"decoded_url":"https://darkibox.com/embed-5ghz7myucfjq.html","quality":"Darki Vidéo HD / PAS DE PUBLICITE","language":"French"},{"decoded_url":"https://supervideo.cc/e/uieh17bid0ce","quality":"Super Vidéo HD / PAS DE PUBLICITE","language":"French"},{"decoded_url":"https://dood.li/e/gha75crrm8nf","quality":"Doodstream Vidéo 9 / Le serveur vidéo contient des publicités","language":"French"},{"decoded_url":"https://filemoon.sx/e/c5lhlypfasmm","quality":"FileMon Vidéo 10 / Le serveur vidéo contient des publicités","language":"French"},{"decoded_url":"https://voe.sx/e/aghpmosgkhsu","quality":"VoeSx Vidéo 11 / Le serveur vidéo contient des publicités","language":"French"},{"decoded_url":"http://vidmoly.me/embed-79w00r2qv88n.html","quality":"VidMoly Vidéo 12 / Le serveur vidéo contient des publicités","language":"French"},{"decoded_url":"https://waaw.to/f/JWzt5eJSbpu8","quality":"Netu Vidéo 14 / Le serveur vidéo contient des publicités","language":"French"},{"decoded_url":"https://veev.to/e/lu4c24e3aetf","quality":"Veed / Le serveur vidéo contient des publicités","language":"French"},{"decoded_url":"https://listeamed.net/e/eL2157Kzw8m5rw4","quality":"VidGuard / Le serveur vidéo contient des publicités","language":"French"}]} \ No newline at end of file diff --git a/src/source/__fixtures__/Movix/https:api.movix.siteapitmdbtv42009season4andepisode2 b/src/source/__fixtures__/Movix/https:api.movix.siteapitmdbtv42009season4andepisode2 new file mode 100644 index 0000000..ef1f217 --- /dev/null +++ b/src/source/__fixtures__/Movix/https:api.movix.siteapitmdbtv42009season4andepisode2 @@ -0,0 +1 @@ +{"tmdb_details":{"id":42009,"title":"Black Mirror","original_title":"Black Mirror","release_date":"2011-12-04","poster_path":"/9acfIYfBuB4GFVROipM9YrqxsXd.jpg","backdrop_path":"/dg3OindVAGZBjlT3xYKqIAdukPL.jpg","overview":"Les histoires tordues se succèdent dans cette série d'anthologie hallucinante qui révèle les plus grandes innovations de l'humanité, mais aussi ses côtés les plus sombres.","vote_average":8.284},"seasons":[{"season_number":1,"name":"Black Mirror - Season 1","data_id":"73065","post_id":"19799","episodes":[]},{"season_number":2,"name":"Black Mirror - Season 2","data_id":"73066","post_id":"19799","episodes":[]},{"season_number":3,"name":"Black Mirror - Season 3","data_id":"73067","post_id":"19799","episodes":[]},{"season_number":4,"name":"Black Mirror - Season 4","data_id":"73068","post_id":"19799","episodes":[]},{"season_number":5,"name":"Black Mirror - Season 5","data_id":"73069","post_id":"19799","episodes":[]},{"season_number":6,"name":"Black Mirror - Season 6","data_id":"73070","post_id":"19799","episodes":[]},{"season_number":7,"name":"Black Mirror - Season 7","data_id":"229573","post_id":"19799","episodes":[]}],"current_episode":{"season_number":4,"episode_number":2,"title":"Black Mirror","iframe_src":"https://lecteurvideo.com/embed.php?id=17320&ads=true","player_links":[{"decoded_url":"https://uqload.net/embed-xjmhb7rbzdjh.html","quality":"Uqload Vidéo 8 / Le serveur vidéo contient des publicités","language":"French"},{"decoded_url":"https://lulustream.com/e/c3defa2cbtmm","quality":"LuluStream / PAS DE PUBLICITE","language":"French"},{"decoded_url":"https://wishonly.site/e/bzfdmjlrsqxk","quality":"Wish Vidéo 4 / PAS DE PUBLICITE","language":"French"},{"decoded_url":"https://vidoza.net/embed-i2e8gn4m64e1.html","quality":"Vid Vidéo 6 / PAS DE PUBLICITE","language":"French"},{"decoded_url":"https://filemoon.sx/e/0iuzfpt5cbx6","quality":"FileMon Vidéo 10 / Le serveur vidéo contient des publicités","language":"French"},{"decoded_url":"https://voe.sx/e/mustkdlxo67t","quality":"VoeSx Vidéo 11 / Le serveur vidéo contient des publicités","language":"French"},{"decoded_url":"http://vidmoly.me/embed-2rls953tvhxc.html","quality":"VidMoly Vidéo 12 / Le serveur vidéo contient des publicités","language":"French"},{"decoded_url":"https://waaw.to/f/GdQ6mHpxPdd8","quality":"Netu Vidéo 14 / Le serveur vidéo contient des publicités","language":"French"},{"decoded_url":"https://veev.to/e/nifwerilwo0i","quality":"Veed / Le serveur vidéo contient des publicités","language":"French"},{"decoded_url":"https://listeamed.net/e/8ozgENePwdLxmjA","quality":"VidGuard / Le serveur vidéo contient des publicités","language":"French"},{"decoded_url":"https://coflix.upn.one/#x63hig","quality":"coflix.upn","language":"French"},{"decoded_url":"https://do7go.com/e/2zn4y7ueq1ta","quality":"do7go","language":"French"},{"decoded_url":"https://movearnpre.com/embed/eiel2p4z74kp","quality":"movearnpre","language":"French"}]}} \ No newline at end of file diff --git a/src/source/__snapshots__/Movix.test.ts.snap b/src/source/__snapshots__/Movix.test.ts.snap new file mode 100644 index 0000000..c97af7a --- /dev/null +++ b/src/source/__snapshots__/Movix.test.ts.snap @@ -0,0 +1,136 @@ +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing + +exports[`Movix handle battle royal 1`] = ` +[ + { + "countryCode": "fr", + "title": "Battle Royale", + "url": "https://lecteur6.com/video/18085327b86002fc604c323b9a07f997", + }, + { + "countryCode": "fr", + "title": "Battle Royale", + "url": "https://vidoza.net/embed-55hvynbdyqvc.html", + }, + { + "countryCode": "fr", + "title": "Battle Royale", + "url": "https://wishonly.site/e/kykhuv6tscs8", + }, + { + "countryCode": "fr", + "title": "Battle Royale", + "url": "https://darkibox.com/embed-5ghz7myucfjq.html", + }, + { + "countryCode": "fr", + "title": "Battle Royale", + "url": "https://supervideo.cc/e/uieh17bid0ce", + }, + { + "countryCode": "fr", + "title": "Battle Royale", + "url": "https://dood.li/e/gha75crrm8nf", + }, + { + "countryCode": "fr", + "title": "Battle Royale", + "url": "https://filemoon.sx/e/c5lhlypfasmm", + }, + { + "countryCode": "fr", + "title": "Battle Royale", + "url": "https://voe.sx/e/aghpmosgkhsu", + }, + { + "countryCode": "fr", + "title": "Battle Royale", + "url": "http://vidmoly.me/embed-79w00r2qv88n.html", + }, + { + "countryCode": "fr", + "title": "Battle Royale", + "url": "https://waaw.to/f/JWzt5eJSbpu8", + }, + { + "countryCode": "fr", + "title": "Battle Royale", + "url": "https://veev.to/e/lu4c24e3aetf", + }, + { + "countryCode": "fr", + "title": "Battle Royale", + "url": "https://listeamed.net/e/eL2157Kzw8m5rw4", + }, +] +`; + +exports[`Movix handle tmdb black mirror s4e2 1`] = ` +[ + { + "countryCode": "fr", + "title": "Black Mirror 4x2", + "url": "https://uqload.net/embed-xjmhb7rbzdjh.html", + }, + { + "countryCode": "fr", + "title": "Black Mirror 4x2", + "url": "https://lulustream.com/e/c3defa2cbtmm", + }, + { + "countryCode": "fr", + "title": "Black Mirror 4x2", + "url": "https://wishonly.site/e/bzfdmjlrsqxk", + }, + { + "countryCode": "fr", + "title": "Black Mirror 4x2", + "url": "https://vidoza.net/embed-i2e8gn4m64e1.html", + }, + { + "countryCode": "fr", + "title": "Black Mirror 4x2", + "url": "https://filemoon.sx/e/0iuzfpt5cbx6", + }, + { + "countryCode": "fr", + "title": "Black Mirror 4x2", + "url": "https://voe.sx/e/mustkdlxo67t", + }, + { + "countryCode": "fr", + "title": "Black Mirror 4x2", + "url": "http://vidmoly.me/embed-2rls953tvhxc.html", + }, + { + "countryCode": "fr", + "title": "Black Mirror 4x2", + "url": "https://waaw.to/f/GdQ6mHpxPdd8", + }, + { + "countryCode": "fr", + "title": "Black Mirror 4x2", + "url": "https://veev.to/e/nifwerilwo0i", + }, + { + "countryCode": "fr", + "title": "Black Mirror 4x2", + "url": "https://listeamed.net/e/8ozgENePwdLxmjA", + }, + { + "countryCode": "fr", + "title": "Black Mirror 4x2", + "url": "https://coflix.upn.one/#x63hig", + }, + { + "countryCode": "fr", + "title": "Black Mirror 4x2", + "url": "https://do7go.com/e/2zn4y7ueq1ta", + }, + { + "countryCode": "fr", + "title": "Black Mirror 4x2", + "url": "https://movearnpre.com/embed/eiel2p4z74kp", + }, +] +`; diff --git a/src/source/index.ts b/src/source/index.ts index 469b5fa..69d7f2c 100644 --- a/src/source/index.ts +++ b/src/source/index.ts @@ -7,6 +7,7 @@ export * from './KinoGer'; export * from './MegaKino'; export * from './MeineCloud'; export * from './MostraGuarda'; +export * from './Movix'; export * from './Soaper'; export * from './StreamKiste'; export * from './VerHdLink';