From 30e5ae7121525521a99d52881d51570213eda688 Mon Sep 17 00:00:00 2001 From: frost768 Date: Mon, 29 May 2023 22:10:07 +0300 Subject: [PATCH 01/27] add missing translation keys and polish translation --- src/components/popout/FloatingCard.tsx | 2 +- src/setup/i18n.ts | 4 + src/setup/locales/en/translation.json | 11 +- src/setup/locales/pl/translation.json | 137 ++++++++++++++++++ src/setup/locales/tr/translation.json | 11 +- .../popouts/EpisodeSelectionPopout.tsx | 15 +- 6 files changed, 171 insertions(+), 9 deletions(-) create mode 100644 src/setup/locales/pl/translation.json diff --git a/src/components/popout/FloatingCard.tsx b/src/components/popout/FloatingCard.tsx index b4fd250c..47bfcb59 100644 --- a/src/components/popout/FloatingCard.tsx +++ b/src/components/popout/FloatingCard.tsx @@ -154,7 +154,7 @@ export const FloatingCardView = { className="flex cursor-pointer items-center space-x-2 transition-colors duration-200 hover:text-white" > - Close + {t("videoPlayer.popouts.close")} ); diff --git a/src/setup/i18n.ts b/src/setup/i18n.ts index 6116434a..d3aa261f 100644 --- a/src/setup/i18n.ts +++ b/src/setup/i18n.ts @@ -9,6 +9,7 @@ import en from "./locales/en/translation.json"; import fr from "./locales/fr/translation.json"; import nl from "./locales/nl/translation.json"; import pirate from "./locales/pirate/translation.json"; +import pl from "./locales/pl/translation.json"; import tr from "./locales/tr/translation.json"; import zh from "./locales/zh/translation.json"; @@ -37,6 +38,9 @@ const locales = { pirate: { translation: pirate, }, + pl: { + translation: pl, + }, }; i18n // pass the i18n instance to react-i18next. diff --git a/src/setup/locales/en/translation.json b/src/setup/locales/en/translation.json index d8b81da4..d90b568b 100644 --- a/src/setup/locales/en/translation.json +++ b/src/setup/locales/en/translation.json @@ -71,7 +71,16 @@ "popouts": { "back": "Go back", "sources": "Sources", - "seasons": "Seasons", + "close": "Close", + "seasons": { + "title":"Seasons", + "other": "Other seasons", + "noSeason": "No season" + }, + "episodes": { + "unknown": "Unknown episode", + "noEpisode": "No episode" + }, "captions": "Captions", "playbackSpeed": "Playback speed", "customPlaybackSpeed": "Custom playback speed", diff --git a/src/setup/locales/pl/translation.json b/src/setup/locales/pl/translation.json new file mode 100644 index 00000000..a85d9c70 --- /dev/null +++ b/src/setup/locales/pl/translation.json @@ -0,0 +1,137 @@ +{ + "global": { + "name": "movie-web" + }, + "search": { + "loading_series": "Szukamy twoich ulubionych seriali...", + "loading_movie": "Szukamy twoich ulubionych filmów...", + "loading": "Wczytywanie...", + "allResults": "To wszystko co mamy!", + "noResults": "Nie mogliśmy niczego znaleźć!", + "allFailed": "Nie udało się znaleźć mediów, Spróbuj ponownie!", + "headingTitle": "Wyniki wyszukiwania", + "bookmarks": "Zakładki", + "continueWatching": "Kontynuuj oglądanie", + "title": "Co chciałbyś obejrzeć?", + "placeholder": "Co chciałbyś obejrzeć?" + }, + "media": { + "movie": "Film", + "series": "Serial", + "stopEditing": "Zatrzymaj edycje", + "errors": { + "genericTitle": "Ups, popsuło się!", + "failedMeta": "Nie udało się wczytać metadanych", + "mediaFailed": "Nie udało nam się zarządać mediów, sprawdź połączenie sieciowe i spróbuj ponownie.", + "videoFailed": "Napotkaliśmy błąd podczas odtwarzania rządanego video. Jeśli problem będzie się powtarzać prosimy o zgłoszenie problemu na <0>Serwer Discord lub na <1>GitHub." + } + }, + "seasons": { + "seasonAndEpisode": "S{{season}} E{{episode}}" + }, + "notFound": { + "genericTitle": "Nie znaleziono", + "backArrow": "Wróć na stronę główną", + "media": { + "title": "Nie można znaleźć multimediów", + "description": "Nie mogliśmy znaleźć rządanych multimediów. Albo zostały usunięte, albo grzebałeś przy adresie URL." + }, + "provider": { + "title": "Ten dostawca został wyłączony", + "description": "Mieliśmy problemy z tym dostawcą, albo był zbyt niestabilny, więc musieliśmy go wyłączyć." + }, + "page": { + "title": "Nie można znaleźć tej strony", + "description": "Szukaliśmy wszędzie: w koszu, w szafie a nawet w piwnicy, ale nie byliśmy w stanie znaleźć strony której szukasz." + } + }, + "searchBar": { + "movie": "Filmy", + "series": "Seriale", + "Search": "Szukaj" + }, + "videoPlayer": { + "findingBestVideo": "Szukamy najlepszego video dla ciebie", + "noVideos": "Oj, Nie mogliśmy znaleźć żadnego video", + "loading": "Wczytywanie...", + "backToHome": "Wróć na stronę główną", + "backToHomeShort": "Wróć", + "seasonAndEpisode": "S{{season}} E{{episode}}", + "timeLeft": "Pozostało {{timeLeft}}", + "finishAt": "Zakończ na {{timeFinished, datetime}}", + "buttons": { + "episodes": "Odcinki", + "source": "Źródło", + "captions": "Napisy", + "download": "Pobierz", + "settings": "Ustawienia", + "pictureInPicture": "Obraz w obrazie (PIP)", + "playbackSpeed": "Prędkość odtwarzania" + }, + "popouts": { + "close": "Zamknąć", + "seasons": { + "title":"Sezony", + "other": "Inne sezony", + "noSeason": "Brak sezonu" + }, + "episodes": { + "unknown": "Nieznany odcinki", + "noEpisode": "Brak odcinki" + }, + "back": "Wróć", + "sources": "Źródła", + "captions": "Napisy", + "playbackSpeed": "Prędkość odtwarzania", + "customPlaybackSpeed": "Niestandardowa prędkość odtwarzania", + "captionPreferences": { + "title": "Personalizuj", + "delay": "Opóźnienie", + "fontSize": "Rozmiar", + "opacity": "Przeźroczystość", + "color": "Kolor" + }, + "episode": "E{{index}} - {{title}}", + "noCaptions": "Brak napisów", + "linkedCaptions": "Załączone napisy", + "customCaption": "Napisy niestandardowe", + "uploadCustomCaption": "Załącz", + "noEmbeds": "Nie znaleziono osadzonych mediów dla tego źródła", + + "errors": { + "loadingWentWong": "Coś poszło nie tak {{seasonTitle}}", + "embedsError": "Coś poszło nie tak przy wczytywaniu osadzonych mediów" + }, + "descriptions": { + "sources": "Którego dostawcy chciałbyś używać?", + "embeds": "Wybierz, które video chcesz zobaczyć", + "seasons": "Wybierz, który sezon chcesz obejrzeć", + "episode": "Wybierz odcinek", + "captions": "Zmień język napisów", + "captionPreferences": "Ustaw napisy, tak jak ci to odpowiada", + "playbackSpeed": "Zmień prędkość odtwarzania" + } + }, + "errors": { + "fatalError": "Odtwarzacz napotkał poważny błąd, Prosimy o złoszenie tego na <0>Serwer Discord lub na <1>GitHub." + } + }, + "settings": { + "title": "Ustawienia", + "language": "Język", + "captionLanguage": "Język napisów" + }, + "v3": { + "newSiteTitle": "Nowa wersja została wydana!", + "newDomain": "https://movie-web.app", + "newDomainText": "movie-web przeniesie się wkrótce na nowy adres: <0>https://movie-web.app. Prosimy zaaktualizować swoje zakładki ponieważ <1>stara strona przestanie działać {{date}}.", + "tireless": "Pracowaliśmy niestrudzenie nad tą aktualizacją, Mamy nadzieję że będziecie zadowoleni z tego nad czym pracowaliśmy przez ostatnie parę miesięcy.", + "leaveAnnouncement": "Zabierz mnie tam!" + }, + "casting": { + "casting": "Przesyłanie do urządzenia..." + }, + "errors": { + "offline": "Sprawdź swoje połączenie sieciowe" + } +} diff --git a/src/setup/locales/tr/translation.json b/src/setup/locales/tr/translation.json index 326cc35d..bab6cd1d 100644 --- a/src/setup/locales/tr/translation.json +++ b/src/setup/locales/tr/translation.json @@ -71,7 +71,16 @@ "popouts": { "back": "Geri git", "sources": "Kaynaklar", - "seasons": "Sezonlar", + "close":"Kapat", + "seasons": { + "title":"Sezonlar", + "other": "Diğer sezonlar", + "noSeason": "Sezon yok" + }, + "episodes": { + "unknown": "Bilinmeyen bölüm", + "noEpisode": "Bölüm yok" + }, "captions": "Altyazılar", "playbackSpeed": "Oynatma hızı", "customPlaybackSpeed": "Özel oynatma hızı", diff --git a/src/video/components/popouts/EpisodeSelectionPopout.tsx b/src/video/components/popouts/EpisodeSelectionPopout.tsx index bd152378..c80045bd 100644 --- a/src/video/components/popouts/EpisodeSelectionPopout.tsx +++ b/src/video/components/popouts/EpisodeSelectionPopout.tsx @@ -99,10 +99,10 @@ export function EpisodeSelectionPopout() { <> navigate("/episodes")} - backText={`To ${currentSeasonInfo?.title.toLowerCase()}`} + backText={currentSeasonInfo?.title} /> {currentSeasonInfo @@ -115,12 +115,15 @@ export function EpisodeSelectionPopout() { {season.title} )) - : "No season"} + : t("videoPlayer.popouts.seasons.noSeason")} navigate("/episodes/seasons")} className="flex cursor-pointer items-center space-x-2 transition-colors duration-200 hover:text-white" > - Other seasons + {t("videoPlayer.popouts.seasons.other")} } @@ -181,7 +184,7 @@ export function EpisodeSelectionPopout() { })} )) - : "No episodes"} + : t("videoPlayer.popouts.episodes.noEpisode")} )} From cc4f64032a92f82170de9e687c70e66f1bd17d96 Mon Sep 17 00:00:00 2001 From: Federico Benedetti Date: Sat, 3 Jun 2023 11:55:57 +0200 Subject: [PATCH 02/27] Add Italian language support --- src/setup/i18n.ts | 4 + src/setup/locales/it/translation.json | 128 ++++++++++++++++++++++++++ 2 files changed, 132 insertions(+) create mode 100644 src/setup/locales/it/translation.json diff --git a/src/setup/i18n.ts b/src/setup/i18n.ts index 6116434a..aa69517a 100644 --- a/src/setup/i18n.ts +++ b/src/setup/i18n.ts @@ -11,11 +11,15 @@ import nl from "./locales/nl/translation.json"; import pirate from "./locales/pirate/translation.json"; import tr from "./locales/tr/translation.json"; import zh from "./locales/zh/translation.json"; +import it from "./locales/it/translation.json"; const locales = { en: { translation: en, }, + it: { + translation: it, + }, nl: { translation: nl, }, diff --git a/src/setup/locales/it/translation.json b/src/setup/locales/it/translation.json new file mode 100644 index 00000000..7c28992c --- /dev/null +++ b/src/setup/locales/it/translation.json @@ -0,0 +1,128 @@ +{ + "global": { + "name": "movie-web" + }, + "search": { + "loading_series": "Recupero delle tue serie preferite...", + "loading_movie": "Recupero dei tuoi film preferiti...", + "loading": "Caricamento...", + "allResults": "Ecco tutto ciò che abbiamo!", + "noResults": "Non abbiamo trovato nulla!", + "allFailed": "Impossibile trovare i media, riprova!", + "headingTitle": "Risultati della ricerca", + "bookmarks": "Segnalibri", + "continueWatching": "Continua a guardare", + "title": "Cosa vuoi guardare?", + "placeholder": "Cosa vuoi guardare?" + }, + "media": { + "movie": "Film", + "series": "Serie", + "stopEditing": "Interrompi modifica", + "errors": { + "genericTitle": "Ops, qualcosa si è rotto!", + "failedMeta": "Caricamento dei metadati non riuscito", + "mediaFailed": "Impossibile richiedere il media che hai richiesto, controlla la tua connessione internet e riprova.", + "videoFailed": "Si è verificato un errore durante la riproduzione del video che hai richiesto. Se ciò continua a accadere, segnala il problema sul <0>server Discord o su <1>GitHub." + } + }, + "seasons": { + "seasonAndEpisode": "S{{season}} E{{episode}}" + }, + "notFound": { + "genericTitle": "Non trovato", + "backArrow": "Torna alla home", + "media": { + "title": "Impossibile trovare quel media", + "description": "Non siamo riusciti a trovare il media richiesto. È stato rimosso o hai manomesso l'URL." + }, + "provider": { + "title": "Questo provider è stato disabilitato", + "description": "Abbiamo riscontrato problemi con il provider o era troppo instabile da utilizzare, quindi abbiamo dovuto disabilitarlo." + }, + "page": { + "title": "Impossibile trovare quella pagina", + "description": "Abbiamo cercato ovunque: sotto i bidoni, nell'armadio, dietro il proxy, ma alla fine non siamo riusciti a trovare la pagina che stai cercando." + } + }, + "searchBar": { + "movie": "Film", + "series": "Serie", + "Search": "Cerca" + }, + "videoPlayer": { + "findingBestVideo": "Ricerca del miglior video per te", + "noVideos": "Ops, non è stato possibile trovare alcun video per te", + "loading": "Caricamento...", + "backToHome": "Torna alla home", + "backToHomeShort": "Indietro", + "seasonAndEpisode": "S{{season}} E{{episode}}", + "timeLeft": "{{timeLeft}} rimanente", + "finishAt": "Fine alle {{timeFinished, datetime}}", + "buttons": { + "episodes": "Episodi", + "source": "Fonte", + "captions": "Sottotitoli", + "download": "Download", + "settings": "Impostazioni", + "pictureInPicture": "Picture in Picture", + "playbackSpeed": "Velocità di riproduzione" + }, + "popouts": { + "back": "Torna indietro", + "sources": "Fonti", + "seasons": "Stagioni", + "captions": "Sottotitoli", + "playbackSpeed": "Velocità di riproduzione", + "customPlaybackSpeed": "Velocità di riproduzione personalizzata", + "captionPreferences": { + "title": "Personalizza", + "delay": "Ritardo", + "fontSize": "Dimensione carattere", + "opacity": "Opacità", + "color": "Colore" + }, + "episode": "E{{index}} - {{title}}", + "noCaptions": "Nessun sottotitolo", + "linkedCaptions": "Sottotitoli collegati", + "customCaption": "Sottotitolo personalizzato", + "uploadCustomCaption": "Carica sottotitolo", + "noEmbeds": "Nessun embed è stato trovato per questa fonte", + + "errors": { + "loadingWentWong": "Si è verificato un problema durante il caricamento degli episodi per {{seasonTitle}}", + "embedsError": "Si è verificato un problema durante il caricamento degli embed per questa cosa che ti piace" + }, + "descriptions": { + "sources": "Quale provider desideri utilizzare?", + "embeds": "Scegli quale video visualizzare", + "seasons": "Scegli quale stagione vuoi guardare", + "episode": "Scegli un episodio", + "captions": "Scegli una lingua per i sottotitoli", + "captionPreferences": "Personalizza l'aspetto dei sottotitoli", + "playbackSpeed": "Cambia la velocità di riproduzione" + } + }, + "errors": { + "fatalError": "Il lettore video ha riscontrato un errore fatale, segnalalo sul <0>server Discord o su <1>GitHub." + } + }, + "settings": { + "title": "Impostazioni", + "language": "Lingua", + "captionLanguage": "Lingua dei sottotitoli" + }, + "v3": { + "newSiteTitle": "Nuova versione ora disponibile!", + "newDomain": "https://movie-web.app", + "newDomainText": "movie-web si sposterà presto su un nuovo dominio: <0>https://movie-web.app. Assicurati di aggiornare tutti i tuoi segnalibri poiché <1>il vecchio sito smetterà di funzionare il {{date}}.", + "tireless": "Abbiamo lavorato instancabilmente su questo nuovo aggiornamento, speriamo che ti piaccia quello su cui abbiamo lavorato negli ultimi mesi.", + "leaveAnnouncement": "Portami lì!" + }, + "casting": { + "casting": "Trasmissione su dispositivo in corso..." + }, + "errors": { + "offline": "Controlla la tua connessione internet" + } +} From b7033a31c453f6cfe70117db5042c1a0c645f9a0 Mon Sep 17 00:00:00 2001 From: Federico Benedetti Date: Sat, 3 Jun 2023 12:15:19 +0200 Subject: [PATCH 03/27] Fix locale import position --- src/setup/i18n.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/setup/i18n.ts b/src/setup/i18n.ts index aa69517a..e5f9358f 100644 --- a/src/setup/i18n.ts +++ b/src/setup/i18n.ts @@ -7,11 +7,11 @@ import cs from "./locales/cs/translation.json"; import de from "./locales/de/translation.json"; import en from "./locales/en/translation.json"; import fr from "./locales/fr/translation.json"; +import it from "./locales/it/translation.json"; import nl from "./locales/nl/translation.json"; import pirate from "./locales/pirate/translation.json"; import tr from "./locales/tr/translation.json"; import zh from "./locales/zh/translation.json"; -import it from "./locales/it/translation.json"; const locales = { en: { From 18bde24b3a21bd2c3df793a76fd072e476febf0f Mon Sep 17 00:00:00 2001 From: cloud <62519659+lem6ns@users.noreply.github.com> Date: Sun, 11 Jun 2023 11:29:55 -0600 Subject: [PATCH 04/27] feat(provider): Remote Stream --- src/backend/index.ts | 1 + src/backend/providers/remotestream.ts | 49 +++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 src/backend/providers/remotestream.ts diff --git a/src/backend/index.ts b/src/backend/index.ts index eb0ad897..d62f557b 100644 --- a/src/backend/index.ts +++ b/src/backend/index.ts @@ -9,6 +9,7 @@ import "./providers/m4ufree"; import "./providers/hdwatched"; import "./providers/2embed"; import "./providers/sflix"; +import "./providers/remotestream"; // embeds import "./embeds/streamm4u"; diff --git a/src/backend/providers/remotestream.ts b/src/backend/providers/remotestream.ts new file mode 100644 index 00000000..cf19f826 --- /dev/null +++ b/src/backend/providers/remotestream.ts @@ -0,0 +1,49 @@ +import { mwFetch } from "@/backend/helpers/fetch"; +import { registerProvider } from "@/backend/helpers/register"; +import { MWStreamQuality, MWStreamType } from "@/backend/helpers/streams"; +import { MWMediaType } from "@/backend/metadata/types"; + +const remotestreamBase = `https://fsa.remotestre.am`; + +registerProvider({ + id: "remotestream", + displayName: "Remote Stream", + disabled: false, + rank: 50, + type: [MWMediaType.MOVIE, MWMediaType.SERIES], + + async scrape({ media, episode, progress }) { + if (!this.type.includes(media.meta.type)) { + throw new Error("Unsupported type"); + } + + progress(30); + const type = media.meta.type === MWMediaType.MOVIE ? "Movies" : "Shows"; + let playlistLink = `${remotestreamBase}/${type}/${media.tmdbId}`; + + if (media.meta.type === MWMediaType.SERIES) { + const seasonNumber = media.meta.seasonData.number; + const episodeNumber = media.meta.seasonData.episodes.find( + (e) => e.id === episode + )?.number; + + playlistLink += `/${seasonNumber}/${episodeNumber}.m3u8`; + } else { + playlistLink += `/${media.tmdbId}.m3u8`; + } + + const streamRes = await mwFetch(playlistLink); + if (streamRes.type !== "application/x-mpegurl") + throw new Error("No watchable item found"); + progress(90); + return { + embeds: [], + stream: { + streamUrl: playlistLink, + quality: MWStreamQuality.QUNKNOWN, + type: MWStreamType.HLS, + captions: [], + }, + }; + }, +}); From 893a385f0043a989cbb39ab1772737294c49ca9b Mon Sep 17 00:00:00 2001 From: cloud <62519659+lem6ns@users.noreply.github.com> Date: Sun, 11 Jun 2023 11:34:57 -0600 Subject: [PATCH 05/27] fix(remotestream): additional path for tv --- src/backend/providers/remotestream.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backend/providers/remotestream.ts b/src/backend/providers/remotestream.ts index cf19f826..8439d2c1 100644 --- a/src/backend/providers/remotestream.ts +++ b/src/backend/providers/remotestream.ts @@ -27,7 +27,7 @@ registerProvider({ (e) => e.id === episode )?.number; - playlistLink += `/${seasonNumber}/${episodeNumber}.m3u8`; + playlistLink += `/${seasonNumber}/${episodeNumber}/${episodeNumber}.m3u8`; } else { playlistLink += `/${media.tmdbId}.m3u8`; } From ef782974fe20115e2993e482f1a7a48f8087b287 Mon Sep 17 00:00:00 2001 From: cloud <62519659+lem6ns@users.noreply.github.com> Date: Sun, 11 Jun 2023 11:36:05 -0600 Subject: [PATCH 06/27] fix(remotestream): Duplicate rank number --- src/backend/providers/remotestream.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backend/providers/remotestream.ts b/src/backend/providers/remotestream.ts index 8439d2c1..02c0f199 100644 --- a/src/backend/providers/remotestream.ts +++ b/src/backend/providers/remotestream.ts @@ -9,7 +9,7 @@ registerProvider({ id: "remotestream", displayName: "Remote Stream", disabled: false, - rank: 50, + rank: 55, type: [MWMediaType.MOVIE, MWMediaType.SERIES], async scrape({ media, episode, progress }) { From 1a613287f8453672c83d9adb0fdbbb77d0f7772b Mon Sep 17 00:00:00 2001 From: cloud <62519659+lem6ns@users.noreply.github.com> Date: Sun, 11 Jun 2023 10:44:51 -0600 Subject: [PATCH 07/27] feat(provider): streamflix --- src/backend/index.ts | 1 + src/backend/providers/streamflix.ts | 70 +++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+) create mode 100644 src/backend/providers/streamflix.ts diff --git a/src/backend/index.ts b/src/backend/index.ts index eb0ad897..447c2165 100644 --- a/src/backend/index.ts +++ b/src/backend/index.ts @@ -9,6 +9,7 @@ import "./providers/m4ufree"; import "./providers/hdwatched"; import "./providers/2embed"; import "./providers/sflix"; +import "./providers/streamflix"; // embeds import "./embeds/streamm4u"; diff --git a/src/backend/providers/streamflix.ts b/src/backend/providers/streamflix.ts new file mode 100644 index 00000000..90dd4975 --- /dev/null +++ b/src/backend/providers/streamflix.ts @@ -0,0 +1,70 @@ +import { proxiedFetch } from "@/backend/helpers/fetch"; +import { registerProvider } from "@/backend/helpers/register"; +import { + MWCaptionType, + MWStreamQuality, + MWStreamType, +} from "@/backend/helpers/streams"; +import { MWMediaType } from "@/backend/metadata/types"; + +const streamflixBase = "https://us-west2-compute-proxied.streamflix.one"; + +const qualityMap: Record = { + 360: MWStreamQuality.Q360P, + 540: MWStreamQuality.Q540P, + 480: MWStreamQuality.Q480P, + 720: MWStreamQuality.Q720P, + 1080: MWStreamQuality.Q1080P, +}; + +registerProvider({ + id: "streamflix", + displayName: "StreamFlix", + disabled: false, + rank: 69, + type: [MWMediaType.MOVIE, MWMediaType.SERIES], + + async scrape({ media, episode, progress }) { + if (!this.type.includes(media.meta.type)) { + throw new Error("Unsupported type"); + } + + progress(30); + const type = media.meta.type === MWMediaType.MOVIE ? "movies" : "tv"; + let seasonNumber: number | undefined; + let episodeNumber: number | undefined; + + if (media.meta.type === MWMediaType.SERIES) { + // can't do type === "tv" here :( + seasonNumber = media.meta.seasonData.number; + episodeNumber = media.meta.seasonData.episodes.find( + (e: any) => e.id === episode + )?.number; + } + + const streamRes = await proxiedFetch(`/api/player/${type}`, { + baseURL: streamflixBase, + params: { + id: media.tmdbId, + s: seasonNumber, + e: episodeNumber, + }, + }); + if (!streamRes.headers.Referer) throw new Error("No watchable item found"); + progress(90); + return { + embeds: [], + stream: { + streamUrl: streamRes.sources[0].url, + quality: qualityMap[streamRes.sources[0].quality], + type: MWStreamType.HLS, + captions: streamRes.subtitles.map((s: Record) => ({ + needsProxy: true, + url: s.url, + type: MWCaptionType.VTT, + langIso: s.lang, + })), + }, + }; + }, +}); From 424ee6fe7791b356ad43765d747dfd6420664f6e Mon Sep 17 00:00:00 2001 From: spinixster <135334442+spinixster@users.noreply.github.com> Date: Thu, 15 Jun 2023 08:55:40 +0700 Subject: [PATCH 08/27] Update i18n.ts --- src/setup/i18n.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/setup/i18n.ts b/src/setup/i18n.ts index 6116434a..a225c91e 100644 --- a/src/setup/i18n.ts +++ b/src/setup/i18n.ts @@ -11,6 +11,7 @@ import nl from "./locales/nl/translation.json"; import pirate from "./locales/pirate/translation.json"; import tr from "./locales/tr/translation.json"; import zh from "./locales/zh/translation.json"; +import vi from "./locales/vi/translation.json"; const locales = { en: { From f9d756e0efe4321e53e30c9b8ba7df88570d8d1a Mon Sep 17 00:00:00 2001 From: spinixster <135334442+spinixster@users.noreply.github.com> Date: Thu, 15 Jun 2023 09:06:19 +0700 Subject: [PATCH 09/27] Update i18n.ts --- src/setup/i18n.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/setup/i18n.ts b/src/setup/i18n.ts index a225c91e..21388b9f 100644 --- a/src/setup/i18n.ts +++ b/src/setup/i18n.ts @@ -10,8 +10,8 @@ import fr from "./locales/fr/translation.json"; import nl from "./locales/nl/translation.json"; import pirate from "./locales/pirate/translation.json"; import tr from "./locales/tr/translation.json"; -import zh from "./locales/zh/translation.json"; import vi from "./locales/vi/translation.json"; +import zh from "./locales/zh/translation.json"; const locales = { en: { From db75f2320d78c721d3754e1162ad49497d6a1a79 Mon Sep 17 00:00:00 2001 From: spinixster <135334442+spinixster@users.noreply.github.com> Date: Thu, 15 Jun 2023 10:46:05 +0700 Subject: [PATCH 10/27] Add files via upload add translation --- src/setup/locales/translation.json | 128 +++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/setup/locales/translation.json diff --git a/src/setup/locales/translation.json b/src/setup/locales/translation.json new file mode 100644 index 00000000..dae9c21d --- /dev/null +++ b/src/setup/locales/translation.json @@ -0,0 +1,128 @@ +{ + "global": { + "name": "movie-web" + }, + "search": { + "loading_series": "Đang tìm chương trình yêu thích của bạn...", + "loading_movie": "Đang tìm bộ phim yêu thích của bạn...", + "loading": "Đang tải...", + "allResults": "Đó là tất cả chúng tối có!", + "noResults": "Chúng tôi không thể tìm thấy gì!", + "allFailed": "Không thể tìm thấy nội dung, hãy thử lại!", + "headingTitle": "Kết quả tìm kiếm", + "bookmarks": "Đánh dấu", + "continueWatching": "Tiếp tục xem", + "title": "Bạn muốn xem gì?", + "placeholder": "Bạn muốn xem gì?" + }, + "media": { + "movie": "Phim", + "series": "Chương trình truyền hình", + "stopEditing": "Hãy dừng chỉnh sửa", + "errors": { + "genericTitle": "Rất tiếc, nó đã hỏng!", + "failedMeta": "Không thể tải meta", + "mediaFailed": "Chúng tôi không thể tìm thấy nội dung mà bạn yêu cầu, hãy kiểm tra kết nối internet của bạn và thử lại.", + "videoFailed": "Chúng tôi đã gặp lỗi khi phát nội dung mà bạn yêu cầu. Nếu điều này tiếp tục xảy ra, vui lòng báo cáo sự cố trên <0>máy chủ Discord hoặc trên <1>GitHub." + } + }, + "seasons": { + "seasonAndEpisode": "M{{season}} T{{episode}}" + }, + "notFound": { + "genericTitle": "Không tìm thấy", + "backArrow": "Quay lại trang chính", + "media": { + "title": "Không thể tìm thấy nội dung", + "description": "Chúng tôi không thể tìm thấy nội dung mà bạn yêu cầu. Hoặc là nó đã bị xóa, hoặc bạn đã xáo trộn URL" + }, + "provider": { + "title": "Nhà cung cấp này đã bị vô hiệu hóa", + "description": "Chúng tôi gặp vấn đề với nhà cung cấp hoặc nó quá bất ổn để sử dụng, cho nên chúng tôi đã phải vô hiệu hóa nó." + }, + "page": { + "title": "Không thể tìm thấy trang", + "description": "Chúng tôi đã tìm kiếm khắp nơi: dưới thùng rác, trong tủ quần áo, đằng sau máy chủ proxy nhưng vẫn không thể tìm thấy trang bạn đang tìm kiếm." + } + }, + "searchBar": { + "movie": "Phim", + "series": "Chương trình truyền hình", + "Search": "Tìm kiếm" + }, + "videoPlayer": { + "findingBestVideo": "Đang tìm nội dung tốt nhất cho bạn", + "noVideos": "Rất tiếc, không tìm thấy nội dung nào cho bạn", + "loading": "Đang tải...", + "backToHome": "Quay lại trang chính", + "backToHomeShort": "Quay lại", + "seasonAndEpisode": "M{{season}} T{{episode}}", + "timeLeft": "Còn {{timeLeft}}", + "finishAt": "Kết thúc vào {{timeFinished, datetime}}", + "buttons": { + "episodes": "Tập", + "source": "Source", + "captions": "Phụ đề", + "download": "Tải xuống", + "settings": "Cài đặt", + "pictureInPicture": "Hình trong hình", + "playbackSpeed": "Tốc độ phát" + }, + "popouts": { + "back": "Quay lại", + "sources": "Nguồn", + "seasons": "Mùa", + "captions": "Phụ đề", + "playbackSpeed": "Tốc độ phát", + "customPlaybackSpeed": "Tủy chỉnh tốc độ phát", + "captionPreferences": { + "title": "Tùy chỉnh", + "delay": "Trì hoãn", + "fontSize": "Kích cỡ", + "opacity": "Độ mờ", + "color": "Màu sắc" + }, + "episode": "T{{index}} - {{title}}", + "noCaptions": "Không phụ đề", + "linkedCaptions": "Phụ đề được liên kết", + "customCaption": "Phụ đề tùy chỉnh", + "uploadCustomCaption": "Tải phụ đề lên", + "noEmbeds": "Không tìm thấy nội dung nhúng nào cho nguồn này", + + "errors": { + "loadingWentWong": "Đã xảy ra lỗi khi tải các tập phim cho {{seasonTitle}}", + "embedsError": "Đã xảy ra lỗi khi tải nội dung nhúng cho nội dung bạn thích này" + }, + "descriptions": { + "sources": "Bạn muốn sử dụng nhà cung cấp nào?", + "embeds": "Chọn video để xem", + "seasons": "Chọn mùa bạn muốn xem", + "episode": "Chọn một tập", + "captions": "Chọn ngôn ngữ của phụ đề", + "captionPreferences": "Làm cho phụ đề trông như thế nào bạn muốn", + "playbackSpeed": "Thay đổi tốc độ phát" + } + }, + "errors": { + "fatalError": "Trình phát video đã gặp phải lỗi nghiêm trọng, vui lòng báo cáo sự cố trên <0>máy chủ Discord hoặc trên <1>GitHub." + } + }, + "settings": { + "title": "Cài đặt", + "language": "Ngôn ngữ", + "captionLanguage": "Ngôn ngữ phụ đề" + }, + "v3": { + "newSiteTitle": "Phiên bản mới đã được phát hành!", + "newDomain": "https://movie-web.app", + "newDomainText": "movie-web sẽ sớm chuyển sang trang mới: <0>https://movie-web.app. Hãy đảm bảo rằng các đánh dấu đã được cập nhật vì <1>trang web cũ sẽ dừng hoạt động vào {{date}}.", + "tireless": "Chúng tôi đã làm việc vất vả để tạo phiên bản mới này, chúng tôi hy vọng bạn sẽ thích những gì chúng tôi đã nung nấu trong những tháng qua.", + "leaveAnnouncement": "Hãy đưa tôi đến đó!" + }, + "casting": { + "casting": "Đang truyền tới thiết bị..." + }, + "errors": { + "offline": "Hãy kiểm tra kết nối Internet của bạn" + } +} From 80dd2158dffbc9d33e154971bd44d828b5a61b38 Mon Sep 17 00:00:00 2001 From: spinixster <135334442+spinixster@users.noreply.github.com> Date: Thu, 15 Jun 2023 10:48:26 +0700 Subject: [PATCH 11/27] Create translation.json --- src/setup/locales/vi/translation.json | 128 ++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/setup/locales/vi/translation.json diff --git a/src/setup/locales/vi/translation.json b/src/setup/locales/vi/translation.json new file mode 100644 index 00000000..45494388 --- /dev/null +++ b/src/setup/locales/vi/translation.json @@ -0,0 +1,128 @@ +{ + "global": { + "name": "movie-web" + }, + "search": { + "loading_series": "Đang tìm chương trình yêu thích của bạn...", + "loading_movie": "Đang tìm bộ phim yêu thích của bạn...", + "loading": "Đang tải...", + "allResults": "Đó là tất cả chúng tôi có!", + "noResults": "Chúng tôi không thể tìm thấy gì!", + "allFailed": "Không thể tìm thấy nội dung, hãy thử lại!", + "headingTitle": "Kết quả tìm kiếm", + "bookmarks": "Đánh dấu", + "continueWatching": "Tiếp tục xem", + "title": "Bạn muốn xem gì?", + "placeholder": "Bạn muốn xem gì?" + }, + "media": { + "movie": "Phim", + "series": "Chương trình truyền hình", + "stopEditing": "Hãy dừng chỉnh sửa", + "errors": { + "genericTitle": "Rất tiếc, nó đã hỏng!", + "failedMeta": "Không thể tải meta", + "mediaFailed": "Chúng tôi không thể tìm thấy nội dung mà bạn yêu cầu, hãy kiểm tra kết nối internet của bạn và thử lại.", + "videoFailed": "Chúng tôi đã gặp lỗi khi phát nội dung mà bạn yêu cầu. Nếu điều này tiếp tục xảy ra, vui lòng báo cáo sự cố trên <0>máy chủ Discord hoặc trên <1>GitHub." + } + }, + "seasons": { + "seasonAndEpisode": "M{{season}} T{{episode}}" + }, + "notFound": { + "genericTitle": "Không tìm thấy", + "backArrow": "Quay lại trang chính", + "media": { + "title": "Không thể tìm thấy nội dung", + "description": "Chúng tôi không thể tìm thấy nội dung mà bạn yêu cầu. Hoặc là nó đã bị xóa, hoặc bạn đã xáo trộn URL" + }, + "provider": { + "title": "Nhà cung cấp này đã bị vô hiệu hóa", + "description": "Chúng tôi đã gặp vấn đề với nhà cung cấp hoặc nó quá bất ổn để sử dụng, cho nên chúng tôi đã phải vô hiệu hóa nó." + }, + "page": { + "title": "Không thể tìm thấy trang", + "description": "Chúng tôi đã tìm kiếm khắp nơi: dưới thùng rác, trong tủ quần áo, đằng sau máy chủ proxy nhưng vẫn không thể tìm thấy trang bạn đang tìm kiếm." + } + }, + "searchBar": { + "movie": "Phim", + "series": "Chương trình truyền hình", + "Search": "Tìm kiếm" + }, + "videoPlayer": { + "findingBestVideo": "Đang tìm nội dung tốt nhất cho bạn", + "noVideos": "Rất tiếc, không tìm thấy nội dung nào cho bạn", + "loading": "Đang tải...", + "backToHome": "Quay lại trang chính", + "backToHomeShort": "Quay lại", + "seasonAndEpisode": "M{{season}} T{{episode}}", + "timeLeft": "Còn {{timeLeft}}", + "finishAt": "Kết thúc vào {{timeFinished, datetime}}", + "buttons": { + "episodes": "Tập", + "source": "Source", + "captions": "Phụ đề", + "download": "Tải xuống", + "settings": "Cài đặt", + "pictureInPicture": "Hình trong hình", + "playbackSpeed": "Tốc độ phát" + }, + "popouts": { + "back": "Quay lại", + "sources": "Nguồn", + "seasons": "Mùa", + "captions": "Phụ đề", + "playbackSpeed": "Tốc độ phát", + "customPlaybackSpeed": "Tủy chỉnh tốc độ phát", + "captionPreferences": { + "title": "Tùy chỉnh", + "delay": "Trì hoãn", + "fontSize": "Kích cỡ", + "opacity": "Độ mờ", + "color": "Màu sắc" + }, + "episode": "T{{index}} - {{title}}", + "noCaptions": "Không phụ đề", + "linkedCaptions": "Phụ đề được liên kết", + "customCaption": "Phụ đề tùy chỉnh", + "uploadCustomCaption": "Tải phụ đề lên", + "noEmbeds": "Không tìm thấy nội dung nhúng nào cho nguồn này", + + "errors": { + "loadingWentWong": "Đã xảy ra lỗi khi tải các tập phim cho {{seasonTitle}}", + "embedsError": "Đã xảy ra lỗi khi tải nội dung nhúng cho nội dung bạn thích này" + }, + "descriptions": { + "sources": "Bạn muốn sử dụng nhà cung cấp nào?", + "embeds": "Chọn video để xem", + "seasons": "Chọn mùa bạn muốn xem", + "episode": "Chọn một tập", + "captions": "Chọn ngôn ngữ của phụ đề", + "captionPreferences": "Làm cho phụ đề trông như thế nào bạn muốn", + "playbackSpeed": "Thay đổi tốc độ phát" + } + }, + "errors": { + "fatalError": "Trình phát video đã gặp phải lỗi nghiêm trọng, vui lòng báo cáo sự cố trên <0>máy chủ Discord hoặc trên <1>GitHub." + } + }, + "settings": { + "title": "Cài đặt", + "language": "Ngôn ngữ", + "captionLanguage": "Ngôn ngữ phụ đề" + }, + "v3": { + "newSiteTitle": "Phiên bản mới đã được phát hành!", + "newDomain": "https://movie-web.app", + "newDomainText": "movie-web sẽ sớm chuyển sang trang mới: <0>https://movie-web.app. Hãy đảm bảo rằng các đánh dấu đã được cập nhật vì <1>trang web cũ sẽ dừng hoạt động vào {{date}}.", + "tireless": "Chúng tôi đã làm việc vất vả để tạo phiên bản mới này, chúng tôi hy vọng bạn sẽ thích những gì chúng tôi đã nung nấu trong những tháng qua.", + "leaveAnnouncement": "Hãy đưa tôi đến đó!" + }, + "casting": { + "casting": "Đang truyền tới thiết bị..." + }, + "errors": { + "offline": "Hãy kiểm tra kết nối Internet của bạn" + } +} From 61c3ed076f778549a34f3ec92dfbe4dde65951dc Mon Sep 17 00:00:00 2001 From: spinixster <135334442+spinixster@users.noreply.github.com> Date: Thu, 15 Jun 2023 10:48:45 +0700 Subject: [PATCH 12/27] Delete translation.json --- src/setup/locales/translation.json | 128 ----------------------------- 1 file changed, 128 deletions(-) delete mode 100644 src/setup/locales/translation.json diff --git a/src/setup/locales/translation.json b/src/setup/locales/translation.json deleted file mode 100644 index dae9c21d..00000000 --- a/src/setup/locales/translation.json +++ /dev/null @@ -1,128 +0,0 @@ -{ - "global": { - "name": "movie-web" - }, - "search": { - "loading_series": "Đang tìm chương trình yêu thích của bạn...", - "loading_movie": "Đang tìm bộ phim yêu thích của bạn...", - "loading": "Đang tải...", - "allResults": "Đó là tất cả chúng tối có!", - "noResults": "Chúng tôi không thể tìm thấy gì!", - "allFailed": "Không thể tìm thấy nội dung, hãy thử lại!", - "headingTitle": "Kết quả tìm kiếm", - "bookmarks": "Đánh dấu", - "continueWatching": "Tiếp tục xem", - "title": "Bạn muốn xem gì?", - "placeholder": "Bạn muốn xem gì?" - }, - "media": { - "movie": "Phim", - "series": "Chương trình truyền hình", - "stopEditing": "Hãy dừng chỉnh sửa", - "errors": { - "genericTitle": "Rất tiếc, nó đã hỏng!", - "failedMeta": "Không thể tải meta", - "mediaFailed": "Chúng tôi không thể tìm thấy nội dung mà bạn yêu cầu, hãy kiểm tra kết nối internet của bạn và thử lại.", - "videoFailed": "Chúng tôi đã gặp lỗi khi phát nội dung mà bạn yêu cầu. Nếu điều này tiếp tục xảy ra, vui lòng báo cáo sự cố trên <0>máy chủ Discord hoặc trên <1>GitHub." - } - }, - "seasons": { - "seasonAndEpisode": "M{{season}} T{{episode}}" - }, - "notFound": { - "genericTitle": "Không tìm thấy", - "backArrow": "Quay lại trang chính", - "media": { - "title": "Không thể tìm thấy nội dung", - "description": "Chúng tôi không thể tìm thấy nội dung mà bạn yêu cầu. Hoặc là nó đã bị xóa, hoặc bạn đã xáo trộn URL" - }, - "provider": { - "title": "Nhà cung cấp này đã bị vô hiệu hóa", - "description": "Chúng tôi gặp vấn đề với nhà cung cấp hoặc nó quá bất ổn để sử dụng, cho nên chúng tôi đã phải vô hiệu hóa nó." - }, - "page": { - "title": "Không thể tìm thấy trang", - "description": "Chúng tôi đã tìm kiếm khắp nơi: dưới thùng rác, trong tủ quần áo, đằng sau máy chủ proxy nhưng vẫn không thể tìm thấy trang bạn đang tìm kiếm." - } - }, - "searchBar": { - "movie": "Phim", - "series": "Chương trình truyền hình", - "Search": "Tìm kiếm" - }, - "videoPlayer": { - "findingBestVideo": "Đang tìm nội dung tốt nhất cho bạn", - "noVideos": "Rất tiếc, không tìm thấy nội dung nào cho bạn", - "loading": "Đang tải...", - "backToHome": "Quay lại trang chính", - "backToHomeShort": "Quay lại", - "seasonAndEpisode": "M{{season}} T{{episode}}", - "timeLeft": "Còn {{timeLeft}}", - "finishAt": "Kết thúc vào {{timeFinished, datetime}}", - "buttons": { - "episodes": "Tập", - "source": "Source", - "captions": "Phụ đề", - "download": "Tải xuống", - "settings": "Cài đặt", - "pictureInPicture": "Hình trong hình", - "playbackSpeed": "Tốc độ phát" - }, - "popouts": { - "back": "Quay lại", - "sources": "Nguồn", - "seasons": "Mùa", - "captions": "Phụ đề", - "playbackSpeed": "Tốc độ phát", - "customPlaybackSpeed": "Tủy chỉnh tốc độ phát", - "captionPreferences": { - "title": "Tùy chỉnh", - "delay": "Trì hoãn", - "fontSize": "Kích cỡ", - "opacity": "Độ mờ", - "color": "Màu sắc" - }, - "episode": "T{{index}} - {{title}}", - "noCaptions": "Không phụ đề", - "linkedCaptions": "Phụ đề được liên kết", - "customCaption": "Phụ đề tùy chỉnh", - "uploadCustomCaption": "Tải phụ đề lên", - "noEmbeds": "Không tìm thấy nội dung nhúng nào cho nguồn này", - - "errors": { - "loadingWentWong": "Đã xảy ra lỗi khi tải các tập phim cho {{seasonTitle}}", - "embedsError": "Đã xảy ra lỗi khi tải nội dung nhúng cho nội dung bạn thích này" - }, - "descriptions": { - "sources": "Bạn muốn sử dụng nhà cung cấp nào?", - "embeds": "Chọn video để xem", - "seasons": "Chọn mùa bạn muốn xem", - "episode": "Chọn một tập", - "captions": "Chọn ngôn ngữ của phụ đề", - "captionPreferences": "Làm cho phụ đề trông như thế nào bạn muốn", - "playbackSpeed": "Thay đổi tốc độ phát" - } - }, - "errors": { - "fatalError": "Trình phát video đã gặp phải lỗi nghiêm trọng, vui lòng báo cáo sự cố trên <0>máy chủ Discord hoặc trên <1>GitHub." - } - }, - "settings": { - "title": "Cài đặt", - "language": "Ngôn ngữ", - "captionLanguage": "Ngôn ngữ phụ đề" - }, - "v3": { - "newSiteTitle": "Phiên bản mới đã được phát hành!", - "newDomain": "https://movie-web.app", - "newDomainText": "movie-web sẽ sớm chuyển sang trang mới: <0>https://movie-web.app. Hãy đảm bảo rằng các đánh dấu đã được cập nhật vì <1>trang web cũ sẽ dừng hoạt động vào {{date}}.", - "tireless": "Chúng tôi đã làm việc vất vả để tạo phiên bản mới này, chúng tôi hy vọng bạn sẽ thích những gì chúng tôi đã nung nấu trong những tháng qua.", - "leaveAnnouncement": "Hãy đưa tôi đến đó!" - }, - "casting": { - "casting": "Đang truyền tới thiết bị..." - }, - "errors": { - "offline": "Hãy kiểm tra kết nối Internet của bạn" - } -} From be90b020431d017c4fef23111ed7809a8dceda33 Mon Sep 17 00:00:00 2001 From: spinixster <135334442+spinixster@users.noreply.github.com> Date: Thu, 15 Jun 2023 10:53:08 +0700 Subject: [PATCH 13/27] Update translation.json --- src/setup/locales/vi/translation.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/setup/locales/vi/translation.json b/src/setup/locales/vi/translation.json index 45494388..d56b3595 100644 --- a/src/setup/locales/vi/translation.json +++ b/src/setup/locales/vi/translation.json @@ -20,7 +20,7 @@ "series": "Chương trình truyền hình", "stopEditing": "Hãy dừng chỉnh sửa", "errors": { - "genericTitle": "Rất tiếc, nó đã hỏng!", + "genericTitle": "Rất tiếc, đã hỏng!", "failedMeta": "Không thể tải meta", "mediaFailed": "Chúng tôi không thể tìm thấy nội dung mà bạn yêu cầu, hãy kiểm tra kết nối internet của bạn và thử lại.", "videoFailed": "Chúng tôi đã gặp lỗi khi phát nội dung mà bạn yêu cầu. Nếu điều này tiếp tục xảy ra, vui lòng báo cáo sự cố trên <0>máy chủ Discord hoặc trên <1>GitHub." From 07deb1897d1113460101e6464b61e37d5997ac13 Mon Sep 17 00:00:00 2001 From: spinixster <135334442+spinixster@users.noreply.github.com> Date: Thu, 15 Jun 2023 10:55:02 +0700 Subject: [PATCH 14/27] Update i18n.ts --- src/setup/i18n.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/setup/i18n.ts b/src/setup/i18n.ts index 21388b9f..d0ec7035 100644 --- a/src/setup/i18n.ts +++ b/src/setup/i18n.ts @@ -38,6 +38,9 @@ const locales = { pirate: { translation: pirate, }, + vi: { + translation: vi, + }, }; i18n // pass the i18n instance to react-i18next. From 4bd00eb47a23464611820c6a2cdb87e5bf06ce75 Mon Sep 17 00:00:00 2001 From: Jordaar <69628820+Jordaar@users.noreply.github.com> Date: Fri, 16 Jun 2023 14:37:07 +0530 Subject: [PATCH 15/27] feat(embed): add upcloud and streamsb embed scrapers --- src/backend/embeds/streamsb.ts | 212 +++++++++++++++++++++++++++++++++ src/backend/embeds/upcloud.ts | 93 +++++++++++++++ 2 files changed, 305 insertions(+) create mode 100644 src/backend/embeds/streamsb.ts create mode 100644 src/backend/embeds/upcloud.ts diff --git a/src/backend/embeds/streamsb.ts b/src/backend/embeds/streamsb.ts new file mode 100644 index 00000000..c755a0b0 --- /dev/null +++ b/src/backend/embeds/streamsb.ts @@ -0,0 +1,212 @@ +import Base64 from "crypto-js/enc-base64"; +import Utf8 from "crypto-js/enc-utf8"; + +import { MWEmbedType } from "@/backend/helpers/embed"; +import { proxiedFetch } from "@/backend/helpers/fetch"; +import { registerEmbedScraper } from "@/backend/helpers/register"; +import { + MWCaptionType, + MWStreamQuality, + MWStreamType, +} from "@/backend/helpers/streams"; + +const qualityOrder = [ + MWStreamQuality.Q1080P, + MWStreamQuality.Q720P, + MWStreamQuality.Q480P, + MWStreamQuality.Q360P, +]; + +async function fetchCaptchaToken(domain: string, recaptchaKey: string) { + const domainHash = Base64.stringify(Utf8.parse(domain)).replace(/=/g, "."); + + const recaptchaRender = await proxiedFetch( + `https://www.google.com/recaptcha/api.js?render=${recaptchaKey}` + ); + + const vToken = recaptchaRender.substring( + recaptchaRender.indexOf("/releases/") + 10, + recaptchaRender.indexOf("/recaptcha__en.js") + ); + + const recaptchaAnchor = await proxiedFetch( + `https://www.google.com/recaptcha/api2/anchor?ar=1&hl=en&size=invisible&cb=flicklax&k=${recaptchaKey}&co=${domainHash}&v=${vToken}` + ); + + const cToken = new DOMParser() + .parseFromString(recaptchaAnchor, "text/html") + .getElementById("recaptcha-token") + ?.getAttribute("value"); + + if (!cToken) throw new Error("Unable to find cToken"); + + const payload = { + v: vToken, + reason: "q", + k: recaptchaKey, + c: cToken, + sa: "", + co: domain, + }; + + const tokenData = await proxiedFetch( + `https://www.google.com/recaptcha/api2/reload?${new URLSearchParams( + payload + ).toString()}`, + { + headers: { referer: "https://www.google.com/recaptcha/api2/" }, + method: "POST", + } + ); + + const token = tokenData.match('rresp","(.+?)"'); + return token ? token[1] : null; +} + +registerEmbedScraper({ + id: "streamsb", + displayName: "StreamSB", + for: MWEmbedType.STREAMSB, + rank: 150, + async getStream({ url, progress }) { + /* Url variations + - domain.com/{id}?.html + - domain.com/{id} + - domain.com/embed-{id} + - domain.com/d/{id} + - domain.com/e/{id} + - domain.com/e/{id}-embed + */ + const streamsbUrl = url + .replace(".html", "") + .replace("embed-", "") + .replace("e/", "") + .replace("d/", ""); + + const parsedUrl = new URL(streamsbUrl); + const base = await proxiedFetch( + `${parsedUrl.origin}/d${parsedUrl.pathname}` + ); + + progress(20); + + // Parse captions from url + const captionUrl = parsedUrl.searchParams.get("caption_1"); + const captionLang = parsedUrl.searchParams.get("sub_1"); + + const basePage = new DOMParser().parseFromString(base, "text/html"); + + const downloadVideoFunctions = basePage.querySelectorAll( + "[onclick^=download_video]" + ); + + const dlDetails = []; + for (const func of downloadVideoFunctions) { + const funcContents = func.getAttribute("onclick"); + const regExpFunc = /download_video\('(.+?)','(.+?)','(.+?)'\)/; + const matchesFunc = regExpFunc.exec(funcContents ?? ""); + if (matchesFunc !== null) { + const quality = func.querySelector("span")?.textContent; + const regExpQuality = /(.+?) \((.+?)\)/; + const matchesQuality = regExpQuality.exec(quality ?? ""); + if (matchesQuality !== null) { + dlDetails.push({ + parameters: [matchesFunc[1], matchesFunc[2], matchesFunc[3]], + quality: { + label: matchesQuality[1].trim(), + size: matchesQuality[2], + }, + }); + } + } + } + + progress(40); + + let dls = await Promise.all( + dlDetails.map(async (dl) => { + const getDownload = await proxiedFetch( + `/dl?op=download_orig&id=${dl.parameters[0]}&mode=${dl.parameters[1]}&hash=${dl.parameters[2]}`, + { + baseURL: parsedUrl.origin, + } + ); + + const downloadPage = new DOMParser().parseFromString( + getDownload, + "text/html" + ); + + const recaptchaKey = downloadPage + .querySelector(".g-recaptcha") + ?.getAttribute("data-sitekey"); + if (!recaptchaKey) throw new Error("Unable to get captcha key"); + + const captchaToken = await fetchCaptchaToken( + parsedUrl.origin, + recaptchaKey + ); + if (!captchaToken) throw new Error("Unable to get captcha token"); + + const dlForm = new FormData(); + dlForm.append("op", "download_orig"); + dlForm.append("id", dl.parameters[0]); + dlForm.append("mode", dl.parameters[1]); + dlForm.append("hash", dl.parameters[2]); + dlForm.append("g-recaptcha-response", captchaToken); + + const download = await proxiedFetch( + `/dl?op=download_orig&id=${dl.parameters[0]}&mode=${dl.parameters[1]}&hash=${dl.parameters[2]}`, + { + baseURL: parsedUrl.origin, + method: "POST", + body: dlForm, + } + ); + + const dlLink = new DOMParser() + .parseFromString(download, "text/html") + .querySelector(".btn.btn-light.btn-lg") + ?.getAttribute("href"); + + console.log(dlLink); + + return { + quality: dl.quality.label as MWStreamQuality, + url: dlLink, + size: dl.quality.size, + captions: + captionUrl && captionLang + ? [ + { + url: captionUrl, + langIso: captionLang, + type: MWCaptionType.VTT, + }, + ] + : [], + }; + }) + ); + dls = dls.filter((d) => !!d.url); + dls = dls.sort((a, b) => { + const aQuality = qualityOrder.indexOf(a.quality); + const bQuality = qualityOrder.indexOf(b.quality); + return aQuality - bQuality; + }); + + progress(60); + + // TODO: Quality selection for embed scrapers + const dl = dls[0]; + if (!dl.url) throw new Error("No stream url found"); + + return { + embedId: MWEmbedType.STREAMSB, + streamUrl: dl.url, + quality: dl.quality, + captions: dl.captions, + type: MWStreamType.MP4, + }; + }, +}); diff --git a/src/backend/embeds/upcloud.ts b/src/backend/embeds/upcloud.ts new file mode 100644 index 00000000..b2877bb3 --- /dev/null +++ b/src/backend/embeds/upcloud.ts @@ -0,0 +1,93 @@ +import { AES, enc } from "crypto-js"; + +import { MWEmbedType } from "@/backend/helpers/embed"; +import { registerEmbedScraper } from "@/backend/helpers/register"; +import { + MWCaptionType, + MWStreamQuality, + MWStreamType, +} from "@/backend/helpers/streams"; + +import { proxiedFetch } from "../helpers/fetch"; + +interface StreamRes { + server: number; + sources: string; + tracks: { + file: string; + kind: "captions" | "thumbnails"; + label: string; + }[]; +} + +function isJSON(json: string) { + try { + JSON.parse(json); + return true; + } catch { + return false; + } +} + +registerEmbedScraper({ + id: "upcloud", + displayName: "UpCloud", + for: MWEmbedType.UPCLOUD, + rank: 200, + async getStream({ url }) { + // Example url: https://dokicloud.one/embed-4/{id}?z= + const parsedUrl = new URL(url.replace("embed-5", "embed-4")); + + const dataPath = parsedUrl.pathname.split("/"); + const dataId = dataPath[dataPath.length - 1]; + + const streamRes = await proxiedFetch( + `${parsedUrl.origin}/ajax/embed-4/getSources?id=${dataId}`, + { + headers: { + Referer: parsedUrl.origin, + "X-Requested-With": "XMLHttpRequest", + }, + } + ); + + let sources: + | { + file: string; + type: string; + } + | string = streamRes.sources; + + if (!isJSON(sources) || typeof sources === "string") { + const decryptionKey = await proxiedFetch( + `https://raw.githubusercontent.com/enimax-anime/key/e4/key.txt` + ); + + const decryptedStream = AES.decrypt(sources, decryptionKey).toString( + enc.Utf8 + ); + + const parsedStream = JSON.parse(decryptedStream)[0]; + if (!parsedStream) throw new Error("No stream found"); + sources = parsedStream as { file: string; type: string }; + } + + return { + embedId: MWEmbedType.UPCLOUD, + streamUrl: sources.file, + quality: MWStreamQuality.Q1080P, + type: MWStreamType.HLS, + captions: streamRes.tracks + .filter((sub) => sub.kind === "captions") + .map((sub) => { + return { + langIso: sub.label, + url: sub.file, + type: sub.file.endsWith("vtt") + ? MWCaptionType.VTT + : MWCaptionType.UNKNOWN, + }; + }), + }; + }, +}); From 7e696d5c2cad0e8c4f236cad4d7e1ca47f0f59ea Mon Sep 17 00:00:00 2001 From: Jordaar <69628820+Jordaar@users.noreply.github.com> Date: Fri, 16 Jun 2023 14:37:41 +0530 Subject: [PATCH 16/27] feat(provider): add gomovies provider --- src/backend/providers/gomovies.ts | 162 ++++++++++++++++++++++++++++++ 1 file changed, 162 insertions(+) create mode 100644 src/backend/providers/gomovies.ts diff --git a/src/backend/providers/gomovies.ts b/src/backend/providers/gomovies.ts new file mode 100644 index 00000000..9e22d095 --- /dev/null +++ b/src/backend/providers/gomovies.ts @@ -0,0 +1,162 @@ +import { MWEmbedType } from "../helpers/embed"; +import { proxiedFetch } from "../helpers/fetch"; +import { registerProvider } from "../helpers/register"; +import { MWMediaType } from "../metadata/types"; + +const gomoviesBase = "https://gomovies.sx"; + +registerProvider({ + id: "gomovies", + displayName: "GOmovies", + rank: 300, + type: [MWMediaType.MOVIE, MWMediaType.SERIES], + + async scrape({ media, episode }) { + const search = await proxiedFetch("/ajax/search", { + baseURL: gomoviesBase, + method: "POST", + body: JSON.stringify({ + keyword: media.meta.title, + }), + headers: { + "X-Requested-With": "XMLHttpRequest", + }, + }); + + const searchPage = new DOMParser().parseFromString(search, "text/html"); + const mediaElements = searchPage.querySelectorAll("a.nav-item"); + + const mediaData = Array.from(mediaElements).map((movieEl) => { + const name = movieEl?.querySelector("h3.film-name")?.textContent; + const year = movieEl?.querySelector( + "div.film-infor span:first-of-type" + )?.textContent; + const path = movieEl.getAttribute("href"); + return { name, year, path }; + }); + + const targetMedia = mediaData.find( + (m) => + m.name === media.meta.title && + (media.meta.type === MWMediaType.MOVIE + ? m.year === media.meta.year + : true) + ); + if (!targetMedia?.path) throw new Error("Media not found"); + + // Example movie path: /movie/watch-{slug}-{id} + // Example series path: /tv/watch-{slug}-{id} + let mediaId = targetMedia.path.split("-").pop()?.replace("/", ""); + + let sources = null; + if (media.meta.type === MWMediaType.SERIES) { + const seasons = await proxiedFetch( + `/ajax/v2/tv/seasons/${mediaId}`, + { + baseURL: gomoviesBase, + headers: { + "X-Requested-With": "XMLHttpRequest", + }, + } + ); + + const seasonsEl = new DOMParser() + .parseFromString(seasons, "text/html") + .querySelectorAll(".ss-item"); + + const seasonsData = [...seasonsEl].map((season) => ({ + number: season.innerHTML.replace("Season ", ""), + dataId: season.getAttribute("data-id"), + })); + + const seasonNumber = media.meta.seasonData.number; + const targetSeason = seasonsData.find( + (season) => +season.number === seasonNumber + ); + if (!targetSeason) throw new Error("Season not found"); + + const episodes = await proxiedFetch( + `/ajax/v2/season/episodes/${targetSeason.dataId}`, + { + baseURL: gomoviesBase, + headers: { + "X-Requested-With": "XMLHttpRequest", + }, + } + ); + + const episodesEl = new DOMParser() + .parseFromString(episodes, "text/html") + .querySelectorAll(".eps-item"); + + const episodesData = Array.from(episodesEl).map((ep) => ({ + dataId: ep.getAttribute("data-id"), + number: ep + .querySelector("strong") + ?.textContent?.replace("Eps", "") + .replace(":", "") + .trim(), + })); + + const episodeNumber = media.meta.seasonData.episodes.find( + (e) => e.id === episode + )?.number; + + const targetEpisode = episodesData.find((ep) => + ep.number ? +ep.number : ep.number === episodeNumber + ); + + if (!targetEpisode?.dataId) throw new Error("Episode not found"); + + mediaId = targetEpisode.dataId; + + sources = await proxiedFetch(`/ajax/v2/episode/servers/${mediaId}`, { + baseURL: gomoviesBase, + headers: { + "X-Requested-With": "XMLHttpRequest", + }, + }); + } else { + sources = await proxiedFetch(`/ajax/movie/episodes/${mediaId}`, { + baseURL: gomoviesBase, + headers: { + "X-Requested-With": "XMLHttpRequest", + }, + }); + } + + const upcloud = new DOMParser() + .parseFromString(sources, "text/html") + .querySelector('a[title*="upcloud" i]'); + + const upcloudDataId = + upcloud?.getAttribute("data-id") ?? upcloud?.getAttribute("data-linkid"); + + if (!upcloudDataId) throw new Error("Upcloud source not available"); + + const upcloudSource = await proxiedFetch<{ + type: "iframe" | string; + link: string; + sources: []; + title: string; + tracks: []; + }>(`/ajax/sources/${upcloudDataId}`, { + baseURL: gomoviesBase, + headers: { + "X-Requested-With": "XMLHttpRequest", + }, + }); + + if (!upcloudSource.link || upcloudSource.type !== "iframe") + throw new Error("No upcloud stream found"); + + return { + embeds: [ + { + type: MWEmbedType.UPCLOUD, + url: upcloudSource.link, + }, + ], + }; + }, +}); From d198760f9c0051c4cf91cab4ad2b84762e09c6ae Mon Sep 17 00:00:00 2001 From: Jordaar <69628820+Jordaar@users.noreply.github.com> Date: Fri, 16 Jun 2023 14:37:57 +0530 Subject: [PATCH 17/27] feat(provider): add kissasian provider --- src/backend/providers/kissasian.ts | 103 +++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 src/backend/providers/kissasian.ts diff --git a/src/backend/providers/kissasian.ts b/src/backend/providers/kissasian.ts new file mode 100644 index 00000000..01fa0a2e --- /dev/null +++ b/src/backend/providers/kissasian.ts @@ -0,0 +1,103 @@ +import { MWEmbedType } from "../helpers/embed"; +import { proxiedFetch } from "../helpers/fetch"; +import { registerProvider } from "../helpers/register"; +import { MWMediaType } from "../metadata/types"; + +const kissasianBase = "https://kissasian.li"; + +registerProvider({ + id: "kissasian", + displayName: "KissAsian", + rank: 10000, + type: [MWMediaType.MOVIE, MWMediaType.SERIES], + + async scrape({ media, episode, progress }) { + let seasonNumber = ""; + let episodeNumber = ""; + + if (media.meta.type === MWMediaType.SERIES) { + seasonNumber = + media.meta.seasonData.number === 1 + ? "" + : `${media.meta.seasonData.number}`; + episodeNumber = `${ + media.meta.seasonData.episodes.find((e) => e.id === episode)?.number ?? + "" + }`; + } + + const searchForm = new FormData(); + searchForm.append("keyword", `${media.meta.title} ${seasonNumber}`.trim()); + searchForm.append("type", "Drama"); + + const search = await proxiedFetch("/Search/SearchSuggest", { + baseURL: kissasianBase, + method: "POST", + body: searchForm, + }); + + const searchPage = new DOMParser().parseFromString(search, "text/html"); + + const dramas = Array.from(searchPage.querySelectorAll("a")).map((drama) => { + return { + name: drama.textContent, + url: drama.href, + }; + }); + + const targetDrama = + dramas.find( + (d) => d.name?.toLowerCase() === media.meta.title.toLowerCase() + ) ?? dramas[0]; + if (!targetDrama) throw new Error("Drama not found"); + + progress(30); + + const drama = await proxiedFetch(targetDrama.url); + + const dramaPage = new DOMParser().parseFromString(drama, "text/html"); + + const episodesEl = dramaPage.querySelectorAll("tbody tr:not(:first-child)"); + + const episodes = Array.from(episodesEl) + .map((ep) => { + const number = ep + ?.querySelector("td.episodeSub a") + ?.textContent?.split("Episode")[1] + ?.trim(); + const href = ep?.querySelector("td.episodeSub a")?.getAttribute("href"); + return { number, href }; + }) + .filter((e) => !!e.href); + + const targetEpisode = + media.meta.type === MWMediaType.MOVIE + ? episodes[0] + : episodes.find((e) => e.number === `${episodeNumber}`); + if (!targetEpisode?.href) throw new Error("Episode not found"); + + progress(70); + + const watch = await proxiedFetch(`${targetEpisode.href}&s=sb`, { + baseURL: kissasianBase, + }); + + const watchPage = new DOMParser().parseFromString(watch, "text/html"); + + const streamsbUrl = watchPage + .querySelector("iframe[id=my_video_1]") + ?.getAttribute("src"); + if (!streamsbUrl) throw new Error("Streamsb embed not found"); + + console.log(streamsbUrl); + + return { + embeds: [ + { + type: MWEmbedType.STREAMSB, + url: streamsbUrl, + }, + ], + }; + }, +}); From 2db7e0bef8ffbdf41f74481a21c2c11cc6d6dd2f Mon Sep 17 00:00:00 2001 From: Jordaar <69628820+Jordaar@users.noreply.github.com> Date: Fri, 16 Jun 2023 14:41:30 +0530 Subject: [PATCH 18/27] feat(enum): add upcloud and streamsb enum --- src/backend/helpers/embed.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/backend/helpers/embed.ts b/src/backend/helpers/embed.ts index 0dec6422..a584a0c7 100644 --- a/src/backend/helpers/embed.ts +++ b/src/backend/helpers/embed.ts @@ -4,6 +4,8 @@ export enum MWEmbedType { M4UFREE = "m4ufree", STREAMM4U = "streamm4u", PLAYM4U = "playm4u", + UPCLOUD = "upcloud", + STREAMSB = "streamsb", } export type MWEmbed = { From d4c6dac9f2edecf9c186d8fed18160945e4e7122 Mon Sep 17 00:00:00 2001 From: Jordaar <69628820+Jordaar@users.noreply.github.com> Date: Fri, 16 Jun 2023 14:43:36 +0530 Subject: [PATCH 19/27] disable 2embed --- src/backend/providers/2embed.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/backend/providers/2embed.ts b/src/backend/providers/2embed.ts index 48056020..7cc8938e 100644 --- a/src/backend/providers/2embed.ts +++ b/src/backend/providers/2embed.ts @@ -191,6 +191,7 @@ registerProvider({ displayName: "2Embed", rank: 125, type: [MWMediaType.MOVIE, MWMediaType.SERIES], + disabled: true, // Disabled, not working async scrape({ media, episode, progress }) { let embedUrl = `${twoEmbedBase}/embed/tmdb/movie?id=${media.tmdbId}`; From f6b830d06df314b442ccafdfd6cc9cc303db54ad Mon Sep 17 00:00:00 2001 From: Jordaar <69628820+Jordaar@users.noreply.github.com> Date: Fri, 16 Jun 2023 14:44:54 +0530 Subject: [PATCH 20/27] feat(register): new providers and embed scrapers --- src/backend/index.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/backend/index.ts b/src/backend/index.ts index eb0ad897..065f1c62 100644 --- a/src/backend/index.ts +++ b/src/backend/index.ts @@ -9,9 +9,13 @@ import "./providers/m4ufree"; import "./providers/hdwatched"; import "./providers/2embed"; import "./providers/sflix"; +import "./providers/gomovies"; +import "./providers/kissasian"; // embeds import "./embeds/streamm4u"; import "./embeds/playm4u"; +import "./embeds/upcloud"; +import "./embeds/streamsb"; initializeScraperStore(); From 58ca372a49ce1ae8009b1e5f33a4809ef9b4c6a4 Mon Sep 17 00:00:00 2001 From: Jordaar <69628820+Jordaar@users.noreply.github.com> Date: Fri, 16 Jun 2023 14:52:42 +0530 Subject: [PATCH 21/27] refactor(kissasian): change rank --- src/backend/providers/kissasian.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/backend/providers/kissasian.ts b/src/backend/providers/kissasian.ts index 01fa0a2e..8d4e50c4 100644 --- a/src/backend/providers/kissasian.ts +++ b/src/backend/providers/kissasian.ts @@ -8,7 +8,7 @@ const kissasianBase = "https://kissasian.li"; registerProvider({ id: "kissasian", displayName: "KissAsian", - rank: 10000, + rank: 130, type: [MWMediaType.MOVIE, MWMediaType.SERIES], async scrape({ media, episode, progress }) { @@ -89,8 +89,6 @@ registerProvider({ ?.getAttribute("src"); if (!streamsbUrl) throw new Error("Streamsb embed not found"); - console.log(streamsbUrl); - return { embeds: [ { From e912ea4715035b9305a22cbabad3db0f5a4795e3 Mon Sep 17 00:00:00 2001 From: Jordaar <69628820+Jordaar@users.noreply.github.com> Date: Fri, 16 Jun 2023 15:05:42 +0530 Subject: [PATCH 22/27] cleanup --- src/backend/embeds/streamsb.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/backend/embeds/streamsb.ts b/src/backend/embeds/streamsb.ts index c755a0b0..99ed563e 100644 --- a/src/backend/embeds/streamsb.ts +++ b/src/backend/embeds/streamsb.ts @@ -169,8 +169,6 @@ registerEmbedScraper({ .querySelector(".btn.btn-light.btn-lg") ?.getAttribute("href"); - console.log(dlLink); - return { quality: dl.quality.label as MWStreamQuality, url: dlLink, From 9003bf67887e4750e47a093db1a2b7a3ca8e4703 Mon Sep 17 00:00:00 2001 From: Jordaar <69628820+Jordaar@users.noreply.github.com> Date: Fri, 16 Jun 2023 16:12:07 +0530 Subject: [PATCH 23/27] feat(embed): add mp4upload embed scraper --- src/backend/embeds/mp4upload.ts | 32 ++++++++++++++++++++++++++++++++ src/backend/index.ts | 1 + 2 files changed, 33 insertions(+) create mode 100644 src/backend/embeds/mp4upload.ts diff --git a/src/backend/embeds/mp4upload.ts b/src/backend/embeds/mp4upload.ts new file mode 100644 index 00000000..3902e20b --- /dev/null +++ b/src/backend/embeds/mp4upload.ts @@ -0,0 +1,32 @@ +import { MWEmbedType } from "@/backend/helpers/embed"; +import { registerEmbedScraper } from "@/backend/helpers/register"; +import { MWStreamQuality, MWStreamType } from "@/backend/helpers/streams"; + +import { proxiedFetch } from "../helpers/fetch"; + +registerEmbedScraper({ + id: "mp4upload", + displayName: "mp4upload", + for: MWEmbedType.MP4UPLOAD, + rank: 170, + async getStream({ url }) { + const embed = await proxiedFetch(url); + + const playerSrcRegex = + /(?<=player\.src\()\s*{\s*type:\s*"[^"]+",\s*src:\s*"([^"]+)"\s*}\s*(?=\);)/s; + + const playerSrc = embed.match(playerSrcRegex); + + const streamUrl = playerSrc[1]; + + if (!streamUrl) throw new Error("Stream url not found"); + + return { + embedId: MWEmbedType.MP4UPLOAD, + streamUrl, + quality: MWStreamQuality.Q1080P, + captions: [], + type: MWStreamType.MP4, + }; + }, +}); diff --git a/src/backend/index.ts b/src/backend/index.ts index 065f1c62..a2beaa2a 100644 --- a/src/backend/index.ts +++ b/src/backend/index.ts @@ -17,5 +17,6 @@ import "./embeds/streamm4u"; import "./embeds/playm4u"; import "./embeds/upcloud"; import "./embeds/streamsb"; +import "./embeds/mp4upload"; initializeScraperStore(); From 7e948c60c1473dd4bda958add89c62dacdef74a3 Mon Sep 17 00:00:00 2001 From: Jordaar <69628820+Jordaar@users.noreply.github.com> Date: Fri, 16 Jun 2023 16:12:53 +0530 Subject: [PATCH 24/27] feat(enum): add mp4upload enum --- src/backend/helpers/embed.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/backend/helpers/embed.ts b/src/backend/helpers/embed.ts index a584a0c7..1ec3362c 100644 --- a/src/backend/helpers/embed.ts +++ b/src/backend/helpers/embed.ts @@ -6,6 +6,7 @@ export enum MWEmbedType { PLAYM4U = "playm4u", UPCLOUD = "upcloud", STREAMSB = "streamsb", + MP4UPLOAD = "mp4upload", } export type MWEmbed = { From a0bb03790a37fad2eed1ab15f4ada9814a84dc45 Mon Sep 17 00:00:00 2001 From: Jordaar <69628820+Jordaar@users.noreply.github.com> Date: Fri, 16 Jun 2023 16:14:05 +0530 Subject: [PATCH 25/27] refactor(streamsb): improve quality sorting --- src/backend/embeds/streamsb.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/backend/embeds/streamsb.ts b/src/backend/embeds/streamsb.ts index 99ed563e..e91b43c7 100644 --- a/src/backend/embeds/streamsb.ts +++ b/src/backend/embeds/streamsb.ts @@ -100,7 +100,7 @@ registerEmbedScraper({ "[onclick^=download_video]" ); - const dlDetails = []; + let dlDetails = []; for (const func of downloadVideoFunctions) { const funcContents = func.getAttribute("onclick"); const regExpFunc = /download_video\('(.+?)','(.+?)','(.+?)'\)/; @@ -121,6 +121,12 @@ registerEmbedScraper({ } } + dlDetails = dlDetails.sort((a, b) => { + const aQuality = qualityOrder.indexOf(a.quality.label as MWStreamQuality); + const bQuality = qualityOrder.indexOf(b.quality.label as MWStreamQuality); + return aQuality - bQuality; + }); + progress(40); let dls = await Promise.all( @@ -187,11 +193,6 @@ registerEmbedScraper({ }) ); dls = dls.filter((d) => !!d.url); - dls = dls.sort((a, b) => { - const aQuality = qualityOrder.indexOf(a.quality); - const bQuality = qualityOrder.indexOf(b.quality); - return aQuality - bQuality; - }); progress(60); From bc0f9a6abff9ab90b7106456f852de9fffbf8276 Mon Sep 17 00:00:00 2001 From: Jordaar <69628820+Jordaar@users.noreply.github.com> Date: Fri, 16 Jun 2023 16:15:41 +0530 Subject: [PATCH 26/27] feat(kissasian): additional mp4upload embed scraper --- src/backend/providers/kissasian.ts | 54 ++++++++++++++++++++---------- 1 file changed, 36 insertions(+), 18 deletions(-) diff --git a/src/backend/providers/kissasian.ts b/src/backend/providers/kissasian.ts index 8d4e50c4..90708970 100644 --- a/src/backend/providers/kissasian.ts +++ b/src/backend/providers/kissasian.ts @@ -5,6 +5,17 @@ import { MWMediaType } from "../metadata/types"; const kissasianBase = "https://kissasian.li"; +const embedProviders = [ + { + type: MWEmbedType.MP4UPLOAD, + id: "mp", + }, + { + type: MWEmbedType.STREAMSB, + id: "sb", + }, +]; + registerProvider({ id: "kissasian", displayName: "KissAsian", @@ -65,37 +76,44 @@ registerProvider({ ?.querySelector("td.episodeSub a") ?.textContent?.split("Episode")[1] ?.trim(); - const href = ep?.querySelector("td.episodeSub a")?.getAttribute("href"); - return { number, href }; + const url = ep?.querySelector("td.episodeSub a")?.getAttribute("href"); + return { number, url }; }) - .filter((e) => !!e.href); + .filter((e) => !!e.url); const targetEpisode = media.meta.type === MWMediaType.MOVIE ? episodes[0] : episodes.find((e) => e.number === `${episodeNumber}`); - if (!targetEpisode?.href) throw new Error("Episode not found"); + if (!targetEpisode?.url) throw new Error("Episode not found"); progress(70); - const watch = await proxiedFetch(`${targetEpisode.href}&s=sb`, { - baseURL: kissasianBase, - }); + let embeds = await Promise.all( + embedProviders.map(async (provider) => { + const watch = await proxiedFetch( + `${targetEpisode.url}&s=${provider.id}`, + { + baseURL: kissasianBase, + } + ); - const watchPage = new DOMParser().parseFromString(watch, "text/html"); + const watchPage = new DOMParser().parseFromString(watch, "text/html"); - const streamsbUrl = watchPage - .querySelector("iframe[id=my_video_1]") - ?.getAttribute("src"); - if (!streamsbUrl) throw new Error("Streamsb embed not found"); + const embedUrl = watchPage + .querySelector("iframe[id=my_video_1]") + ?.getAttribute("src"); + + return { + type: provider.type, + url: embedUrl ?? "", + }; + }) + ); + embeds = embeds.filter((e) => e.url !== ""); return { - embeds: [ - { - type: MWEmbedType.STREAMSB, - url: streamsbUrl, - }, - ], + embeds, }; }, }); From d47acada58f660cf4bc0788fd6b6a80d5e6c2729 Mon Sep 17 00:00:00 2001 From: mrjvs Date: Sat, 17 Jun 2023 20:20:38 +0200 Subject: [PATCH 27/27] Update i18n.ts --- src/setup/i18n.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/setup/i18n.ts b/src/setup/i18n.ts index 919754ae..ccb77d22 100644 --- a/src/setup/i18n.ts +++ b/src/setup/i18n.ts @@ -45,7 +45,7 @@ const locales = { }, vi: { translation: vi, - } + }, pl: { translation: pl, },