From c265d630ac81a05204f50cb29a2085bea7ed63f9 Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Sat, 11 Jul 2026 21:21:43 +0200 Subject: [PATCH 01/38] Translated using Weblate (Latvian) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently translated at 81.4% (594 of 729 strings) Translated using Weblate (Bengali) Currently translated at 48.1% (351 of 729 strings) Translated using Weblate (Lithuanian) Currently translated at 35.6% (260 of 729 strings) Translated using Weblate (Dutch) Currently translated at 91.0% (664 of 729 strings) Merge remote-tracking branch 'origin/master' Merge remote-tracking branch 'origin/master' Merge remote-tracking branch 'origin/master' Merge remote-tracking branch 'origin/master' Merge remote-tracking branch 'origin/master' Merge remote-tracking branch 'origin/master' Merge remote-tracking branch 'origin/master' Merge remote-tracking branch 'origin/master' Merge remote-tracking branch 'origin/master' Translated using Weblate (Arabic (Saudi Arabia)) Currently translated at 100.0% (4 of 4 strings) Translated using Weblate (Japanese) Currently translated at 100.0% (729 of 729 strings) Merge remote-tracking branch 'origin/master' Merge remote-tracking branch 'origin/master' Merge remote-tracking branch 'origin/master' Merge remote-tracking branch 'origin/master' Merge remote-tracking branch 'origin/master' Merge remote-tracking branch 'origin/master' Merge remote-tracking branch 'origin/master' Merge remote-tracking branch 'origin/master' Merge remote-tracking branch 'origin/master' Merge remote-tracking branch 'origin/master' Merge remote-tracking branch 'origin/master' Merge remote-tracking branch 'origin/master' Merge remote-tracking branch 'origin/master' Merge remote-tracking branch 'origin/master' Merge remote-tracking branch 'origin/master' Merge remote-tracking branch 'origin/master' Merge remote-tracking branch 'origin/master' Merge remote-tracking branch 'origin/master' Merge remote-tracking branch 'origin/master' Merge remote-tracking branch 'origin/master' Merge remote-tracking branch 'origin/master' Merge remote-tracking branch 'origin/master' Merge remote-tracking branch 'origin/master' Merge remote-tracking branch 'origin/master' Translated using Weblate (Vietnamese) Currently translated at 100.0% (729 of 729 strings) Translated using Weblate (Malayalam) Currently translated at 37.8% (276 of 729 strings) Translated using Weblate (Japanese) Currently translated at 99.5% (726 of 729 strings) Merge remote-tracking branch 'origin/master' Merge remote-tracking branch 'origin/master' Merge remote-tracking branch 'origin/master' Translated using Weblate (Korean) Currently translated at 100.0% (729 of 729 strings) Co-authored-by: Aishik Mukherjee Co-authored-by: Hosted Weblate Co-authored-by: Kie Takaba Co-authored-by: Man Co-authored-by: Nori Masomi (Mystical) Co-authored-by: Saleh ALHarbi Co-authored-by: caviaz Co-authored-by: gabix Co-authored-by: hou1234 Co-authored-by: trodiz Co-authored-by: ℂ𝕠𝕠𝕠𝕝 (𝕘𝕚𝕥𝕙𝕦𝕓.𝕔𝕠𝕞/ℂ𝕠𝕠𝕠𝕝) Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/bn/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ja/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ko/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/lt/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/lv/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ml/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/nl/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/vi/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/fastlane/ar_SA/ Translation: Cloudstream/App Translation: Cloudstream/Fastlane --- app/src/main/res/values-b+bn/strings.xml | 1 + app/src/main/res/values-b+ja/strings.xml | 3 + app/src/main/res/values-b+lt/strings.xml | 75 ++++++++++++++++++----- app/src/main/res/values-b+lv/strings.xml | 15 +++-- app/src/main/res/values-b+nl/strings.xml | 5 ++ fastlane/metadata/android/ar-SA/title.txt | 2 +- 6 files changed, 82 insertions(+), 19 deletions(-) diff --git a/app/src/main/res/values-b+bn/strings.xml b/app/src/main/res/values-b+bn/strings.xml index adc1b3f19..e90608793 100644 --- a/app/src/main/res/values-b+bn/strings.xml +++ b/app/src/main/res/values-b+bn/strings.xml @@ -353,4 +353,5 @@ শুরু থেকে চালু করুন স্পিচ রিকগনিশন উপলব্ধ নেই কথা বলা শুরু করুন… + ডাউনলোড তালিকা diff --git a/app/src/main/res/values-b+ja/strings.xml b/app/src/main/res/values-b+ja/strings.xml index 2b611ef69..c5e6272b2 100644 --- a/app/src/main/res/values-b+ja/strings.xml +++ b/app/src/main/res/values-b+ja/strings.xml @@ -730,4 +730,7 @@ ソースの優先順位 プレイヤーでのビデオソースの並び順を設定します プレイヤーメタデータオーバーレイを表示 + 動画 + プレビュー + ライブ diff --git a/app/src/main/res/values-b+lt/strings.xml b/app/src/main/res/values-b+lt/strings.xml index db7e994f0..bccc4cf59 100644 --- a/app/src/main/res/values-b+lt/strings.xml +++ b/app/src/main/res/values-b+lt/strings.xml @@ -25,18 +25,18 @@ Kopijuoti Duoti bananai Daugiau informacijos - Atsiuntimai + Atsisiuntimai Automatiškai pasirinkti kalbą Klaida kraunant nuorodas +30 - Atsiuntimas baigtas + Atsisiuntimas baigtas Tęsti žiūrėjimą - Rastas atnaujinimas! \n%1$s -> %2$s + Rastas atnaujinimas!\n%1$s -> %2$s Atsisiųsti kalbas Ieškoti naudojant tiekėjus Grįžti atgal Siunčiama - Daugiau pasirinkčiu + Daugiau parinkčių Paleisti seriją Grotuvo greitis %d Bananai duoti kūrėjams @@ -45,7 +45,7 @@ Žanrai -30 Serijos plakatas - Gali reikėti VPN šitam tiekėjui, kad veiktų teisingai + Gali reikėti VPN šiam tiekėjui, kad veiktų teisingai Ieškoti %s… Github Nėra duotu bananų @@ -66,7 +66,7 @@ Atšaukti Pradėti Filmukas - Atsiuntimas atšauktas + Atsisiuntimas atšauktas Išplėstinė paieška Tuščias sąrašas. Pabandykite pasirinkti kitą sąrašą. Chromecast subtitrai @@ -93,10 +93,10 @@ Užbaigta Naudoti sistemos ryškumą programos grotuve vietoj tamsumo Nepavyko atstatyti duomenis iš failo %s - Paleisti anonsa - Paleisti gyva transliacija + Paleisti anonsą + Paleisti gyvą transliaciją Nerasta serijų - Šis tiekėjas yra iš Torrentų, VPN rekomenduojama naudoti + Šis tiekėjas yra iš Torrent\'ų, rekomenduojamas VPN Nepavyko Plakatas Paleisti failą @@ -108,21 +108,21 @@ Perdaryti nustatymo procesą %1$d-%2$d Duoti bananą kūrėjams - Sugryšti + Sugrįšti Nuoroda nukopijuota į iškarpinę Paieška Informacija - Praleisti įkėlima + Praleisti įkėlimą Informacija Serija %d bus išleista Išsaugoti Perdaug teksto. Nepavyko išsaugoti i iškarpynę. - Atsiuntimas nepavyko + Atsisiuntimas nepavyko Pasidalinti Pagrindinis Plakatas Šaltiniai Nustatymai - Ieškoti + Paieška Kraunama… Pašalinti Daugiau informacijos @@ -138,7 +138,7 @@ Uždaryti Nustatyti žiūrėjimo statusą Paleisti su CloudStream - Subtitrų iškėlimas + Subtitrų lygis Serijos Skleisti: %s Sezonas @@ -248,4 +248,51 @@ Ar tikrai norite išeiti? Pašalinti iš žiūrimų Garso takelis + Užpildymas + min + Namai + Atsisiuntimų eilė + Balso atpažinimas nepasiekiamas + Pradėkite kalbėti… + Nežiūrimas + Paleisti visą seriją + Šis įrašas yra Torrent\'e, tai reiškia, kad tavo įrašo veikla gali būti sekama. Įsitikinkite, kad suprantate Torrenting prieš tęsiant. + Atkurti ryšį… + Pasirinkite elementus, kuriuos norite pašalinti + Šiuo metu atsisiuntimų nėra. + Šiuo metu atsisiuntimų eilėje nėra. + Pasiekiama žiūrėti neprisijungus + Pasirinkti viską + Panaikinti visus pasirinkimus + Tinklo srautas + Atidaryti vietinį video + Nuorodos perkrautos + Subtitrai + Repozitorijos pavadinimas ir adresas + nukopijuota! + Naujos serijos pranešimas + Ieškoti kituose plėtiniuose + Rodyti rekomendacijas + Apvado spalva + Rėmelio tipas + Ieškoti naudojant tipus + Laikyti ilgai, kad sugražinti pradinius nustatymus + Įkelti šriftus sudedant juos į %s + Metaduomenys nesuteikiamos svetainės, vaizdo įkėlimas nepavyks, jeigu jo nebus svetainėje. + Aprašymas + Nėra siužeto aprašymo + Aprašymas nerastas + Rodyti Logcat + Vaizdas vaizde + Tęsia atkūrimą mažame grotuve virš kitų programų + Grotuvo dydžio pakeitimo mygtukas + Pašalinti juodas paraštes + Subtitrai + Chromecast subtitrų nustatymai + Atkūrimo sparta + Prideda greičio pasirinkimą grotuve + Braukite, kas ieškotumėte + Braukite į šonus, kad kontroliuotumėte padėtį vaizdo įraše + Braukite, kad pakeistumėte nustatymus + Braukite aukštyn arba žemyn ekrano kairėje arba dešinėje, jei norite pakeisti ryškumą arba garsą diff --git a/app/src/main/res/values-b+lv/strings.xml b/app/src/main/res/values-b+lv/strings.xml index 9ab29cd43..531b5420b 100644 --- a/app/src/main/res/values-b+lv/strings.xml +++ b/app/src/main/res/values-b+lv/strings.xml @@ -287,12 +287,12 @@ Parasts Izskats Funkcijas - Ģenerāls + Vispārīgie Randomā poga Rādīt izlases pogu Sākums un Bibliotēka sadaļās Papildinājuma valodas Lietotnes izkārtojums - Izvēlētā media + Vēlamie multimediji Iespējot nepiedienīgu, izaicinošu saturu (NSFW) atbalstītajos papildinājumos Subtitru kodējums Devēji @@ -372,7 +372,7 @@ Kļūda Noņemt slēgtos parakstus no subtitriem Noņemt lieko no subtitriem (piemēram, reklāmu) - Filtrēt pēc vēlamās multivides valodas + Atlasīt pēc vēlamās multimediju valodas Ekstras Treileris https://piemērs.com/piemērs.mp4 @@ -382,7 +382,7 @@ Iepriekšējais Izlaist uzstādīšanu Mainiet lietotnes izskatu, lai tā atbilstu savai ierīcei - Ko tu vēlies redzēt + Ko jūs vēlētos skatīt Pabeigts Papildinājumi Pievienot repozitoriju @@ -607,4 +607,11 @@ Vai tiešām vēlaties neatgriezeniski dzēst visas šī seriāla, raidījuma epizodes?\n\n%s Pašlaik nav nevienas rindā ievietotas lejupielādes. Atvērt vietējo video + Multimedija + Multimediju informācija + Iespējojiet torrentu Iestatījumi/Pakalpojumu sniedzēji/Vēlamie multimediji sadaļā + Paziņojums par jaunu epizodi + Papildu spilgtums + Papildu spilgtums iespējots + Meklēšanas ieteikumi diff --git a/app/src/main/res/values-b+nl/strings.xml b/app/src/main/res/values-b+nl/strings.xml index 3164e6b4e..8a2424326 100644 --- a/app/src/main/res/values-b+nl/strings.xml +++ b/app/src/main/res/values-b+nl/strings.xml @@ -677,4 +677,9 @@ Bevestig voor afsluiten Toon dialoogvenster voordat de app wordt afgesloten Randgrote + Laat Speler Metadata Overlay zien + Kon de apparaat PIN niet vinden, probeer locale authenticatie + Zet torrent aan in Instellingen/Providers/Media voorkeur + Herstart de app en accepteer de Stream Torrent pop-up om verder te gaan. + Handmatige Update Knop diff --git a/fastlane/metadata/android/ar-SA/title.txt b/fastlane/metadata/android/ar-SA/title.txt index 7977b2906..dde89d58f 100644 --- a/fastlane/metadata/android/ar-SA/title.txt +++ b/fastlane/metadata/android/ar-SA/title.txt @@ -1 +1 @@ -كلاودستريم +CloudStream From abd73b4f1222361bd1d521daff097973eeea2e56 Mon Sep 17 00:00:00 2001 From: firelight <147925818+fire-light42@users.noreply.github.com> Date: Sun, 12 Jul 2026 12:02:09 +0000 Subject: [PATCH 02/38] Added migration guide for Compose --- COMPOSE.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 COMPOSE.md diff --git a/COMPOSE.md b/COMPOSE.md new file mode 100644 index 000000000..919789bb9 --- /dev/null +++ b/COMPOSE.md @@ -0,0 +1,19 @@ +# Migration guide to Compose + +1. MVI instead of MVVM The current design of CloudStream loosely uses the MVVM architecture. + +This means that the UI invokes the viewmodel with function calls, and it responds with LiveData fields that are observed. While this has worked, it generates a lot of boilerplate and has created some friction. + +To make it easier to work with Compose, the new architecture will be based on MVI. In short this means that the viewmodel exposes a singular immutable class that is observed, and receives all UI events with a singular event that is a sealed class. All the UI should be able to be recreated based on this singular state class, and all interactions should be able to be replayed using only the event callback. + +For a more detailed overview, see: https://www.youtube.com/watch?v=b2z1jvD4VMQ + +This is part of the effort to make CloudStream cross platform, as it allows us to decouple UI and logic. + +2. KMP-compatible libraries + +We plan to leverage Kotlin's KMP project to compile our code to different architectures. However, this requires us to only use KMP-compatible libraries, no Java. Therefore any pull requests must ensure that they use KMP-compatible libraries only. + +3. UI Changes + +While migrating to the new compose UI, you also have the opportunity to change the UI. However, this should only be to freshen up the UI, not completely redesign it. It is also important to stress that this process should not lose any features of the old UI, and be very conservative with adding new features. \ No newline at end of file From 137d6173b491dd9d26fe4a862fb0a94d8c7bcc5f Mon Sep 17 00:00:00 2001 From: firelight <147925818+fire-light42@users.noreply.github.com> Date: Sun, 12 Jul 2026 12:03:59 +0000 Subject: [PATCH 03/38] Minor markdown fixes --- COMPOSE.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/COMPOSE.md b/COMPOSE.md index 919789bb9..8d83a50ae 100644 --- a/COMPOSE.md +++ b/COMPOSE.md @@ -1,6 +1,8 @@ # Migration guide to Compose -1. MVI instead of MVVM The current design of CloudStream loosely uses the MVVM architecture. +### 1. MVI instead of MVVM + +The current design of CloudStream loosely uses the MVVM architecture. This means that the UI invokes the viewmodel with function calls, and it responds with LiveData fields that are observed. While this has worked, it generates a lot of boilerplate and has created some friction. @@ -10,10 +12,10 @@ For a more detailed overview, see: https://www.youtube.com/watch?v=b2z1jvD4VMQ This is part of the effort to make CloudStream cross platform, as it allows us to decouple UI and logic. -2. KMP-compatible libraries +### 2. KMP-compatible libraries We plan to leverage Kotlin's KMP project to compile our code to different architectures. However, this requires us to only use KMP-compatible libraries, no Java. Therefore any pull requests must ensure that they use KMP-compatible libraries only. -3. UI Changes +### 3. UI Changes While migrating to the new compose UI, you also have the opportunity to change the UI. However, this should only be to freshen up the UI, not completely redesign it. It is also important to stress that this process should not lose any features of the old UI, and be very conservative with adding new features. \ No newline at end of file From 70469f55dbc6a8535ff2b5a57b0f83e79825aa23 Mon Sep 17 00:00:00 2001 From: firelight <147925818+fire-light42@users.noreply.github.com> Date: Sun, 12 Jul 2026 21:43:59 +0000 Subject: [PATCH 04/38] [skip ci] Migration guide (#3058) --- COMPOSE.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 COMPOSE.md diff --git a/COMPOSE.md b/COMPOSE.md new file mode 100644 index 000000000..8d83a50ae --- /dev/null +++ b/COMPOSE.md @@ -0,0 +1,21 @@ +# Migration guide to Compose + +### 1. MVI instead of MVVM + +The current design of CloudStream loosely uses the MVVM architecture. + +This means that the UI invokes the viewmodel with function calls, and it responds with LiveData fields that are observed. While this has worked, it generates a lot of boilerplate and has created some friction. + +To make it easier to work with Compose, the new architecture will be based on MVI. In short this means that the viewmodel exposes a singular immutable class that is observed, and receives all UI events with a singular event that is a sealed class. All the UI should be able to be recreated based on this singular state class, and all interactions should be able to be replayed using only the event callback. + +For a more detailed overview, see: https://www.youtube.com/watch?v=b2z1jvD4VMQ + +This is part of the effort to make CloudStream cross platform, as it allows us to decouple UI and logic. + +### 2. KMP-compatible libraries + +We plan to leverage Kotlin's KMP project to compile our code to different architectures. However, this requires us to only use KMP-compatible libraries, no Java. Therefore any pull requests must ensure that they use KMP-compatible libraries only. + +### 3. UI Changes + +While migrating to the new compose UI, you also have the opportunity to change the UI. However, this should only be to freshen up the UI, not completely redesign it. It is also important to stress that this process should not lose any features of the old UI, and be very conservative with adding new features. \ No newline at end of file From 505722c1b5c1a79d8295e984e55d3c72457b2e44 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 12 Jul 2026 16:09:36 -0600 Subject: [PATCH 05/38] [skip ci] Torrent: migrate to kotlinx serialization (#2882) --- .../cloudstream3/ui/player/Torrent.kt | 163 +++++++----------- 1 file changed, 61 insertions(+), 102 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/Torrent.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/Torrent.kt index 2e554f75e..7e5d04eb0 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/Torrent.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/Torrent.kt @@ -9,6 +9,8 @@ import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.ExtractorLinkType import com.lagradost.cloudstream3.utils.newExtractorLink +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable import torrServer.TorrServer import java.io.File import java.net.ConnectException @@ -32,14 +34,14 @@ object Torrent { /** Returns true if the server is up */ private suspend fun echo(): Boolean { - if(TORRENT_SERVER_URL.isEmpty()) { + if (TORRENT_SERVER_URL.isEmpty()) { return false } return try { app.get( "$TORRENT_SERVER_URL/echo", ).text.isNotEmpty() - } catch (e: ConnectException) { + } catch (_: ConnectException) { // `Failed to connect to /127.0.0.1:8090` if the server is down false } catch (t: Throwable) { @@ -52,7 +54,7 @@ object Torrent { /** Gracefully shutdown the server. * should not be used because I am unable to start it again, and the stopTorrentServer() crashes the app */ suspend fun shutdown(): Boolean { - if(TORRENT_SERVER_URL.isEmpty()) { + if (TORRENT_SERVER_URL.isEmpty()) { return false } return try { @@ -68,7 +70,7 @@ object Torrent { /** Lists all torrents by the server */ @Throws private suspend fun list(): Array { - if(TORRENT_SERVER_URL.isEmpty()) { + if (TORRENT_SERVER_URL.isEmpty()) { throw ErrorLoadingException("Not initialized") } return app.post( @@ -83,7 +85,7 @@ object Torrent { /** Drops a single torrent, (I think) this means closing the stream. Returns returns if it is successful */ private suspend fun drop(hash: String): Boolean { - if(TORRENT_SERVER_URL.isEmpty()) { + if (TORRENT_SERVER_URL.isEmpty()) { return false } return try { @@ -104,7 +106,7 @@ object Torrent { /** Removes a single torrent from the server registry */ private suspend fun rem(hash: String): Boolean { - if(TORRENT_SERVER_URL.isEmpty()) { + if (TORRENT_SERVER_URL.isEmpty()) { return false } return try { @@ -126,7 +128,7 @@ object Torrent { /** Removes all torrents from the server, and returns if it is successful */ suspend fun clearAll(): Boolean { - if(TORRENT_SERVER_URL.isEmpty()) { + if (TORRENT_SERVER_URL.isEmpty()) { return true } return try { @@ -164,10 +166,8 @@ object Torrent { /** Gets all the metadata of a torrent, will throw if that hash does not exists * https://github.com/Diegopyl1209/torrentserver-aniyomi/blob/c18f58e51b6738f053261bc863177078aa9c1c98/web/api/torrents.go#L126 */ @Throws - suspend fun get( - hash: String, - ): TorrentStatus { - if(TORRENT_SERVER_URL.isEmpty()) { + suspend fun get(hash: String): TorrentStatus { + if (TORRENT_SERVER_URL.isEmpty()) { throw ErrorLoadingException("Not initialized") } return app.post( @@ -184,7 +184,7 @@ object Torrent { /** Adds a torrent to the server, this is needed for us to get the hash for further modification, as well as start streaming it*/ @Throws private suspend fun add(url: String): TorrentStatus { - if(TORRENT_SERVER_URL.isEmpty()) { + if (TORRENT_SERVER_URL.isEmpty()) { throw ErrorLoadingException("Not initialized") } return app.post( @@ -204,7 +204,7 @@ object Torrent { return true } val port = TorrServer.startTorrentServer(dir, 0) - if(port < 0) { + if (port < 0) { return false } TORRENT_SERVER_URL = "http://127.0.0.1:$port" @@ -278,94 +278,55 @@ object Torrent { // https://github.com/Diegopyl1209/torrentserver-aniyomi/blob/c18f58e51b6738f053261bc863177078aa9c1c98/web/api/torrents.go#L18 // https://github.com/Diegopyl1209/torrentserver-aniyomi/blob/main/web/api/route.go#L7 + @Serializable data class TorrentRequest( - @JsonProperty("action") - val action: String, - @JsonProperty("hash") - val hash: String = "", - @JsonProperty("link") - val link: String = "", - @JsonProperty("title") - val title: String = "", - @JsonProperty("poster") - val poster: String = "", - @JsonProperty("data") - val data: String = "", - @JsonProperty("save_to_db") - val saveToDB: Boolean = false, + @JsonProperty("action") @SerialName("action") val action: String, + @JsonProperty("hash") @SerialName("hash") val hash: String = "", + @JsonProperty("link") @SerialName("link") val link: String = "", + @JsonProperty("title") @SerialName("title") val title: String = "", + @JsonProperty("poster") @SerialName("poster") val poster: String = "", + @JsonProperty("data") @SerialName("data") val data: String = "", + @JsonProperty("save_to_db") @SerialName("save_to_db") val saveToDB: Boolean = false, ) // https://github.com/Diegopyl1209/torrentserver-aniyomi/blob/c18f58e51b6738f053261bc863177078aa9c1c98/torr/state/state.go#L33 // omitempty = nullable + @Serializable data class TorrentStatus( - @JsonProperty("title") - var title: String, - @JsonProperty("poster") - var poster: String, - @JsonProperty("data") - var data: String?, - @JsonProperty("timestamp") - var timestamp: Long, - @JsonProperty("name") - var name: String?, - @JsonProperty("hash") - var hash: String?, - @JsonProperty("stat") - var stat: Int, - @JsonProperty("stat_string") - var statString: String, - @JsonProperty("loaded_size") - var loadedSize: Long?, - @JsonProperty("torrent_size") - var torrentSize: Long?, - @JsonProperty("preloaded_bytes") - var preloadedBytes: Long?, - @JsonProperty("preload_size") - var preloadSize: Long?, - @JsonProperty("download_speed") - var downloadSpeed: Double?, - @JsonProperty("upload_speed") - var uploadSpeed: Double?, - @JsonProperty("total_peers") - var totalPeers: Int?, - @JsonProperty("pending_peers") - var pendingPeers: Int?, - @JsonProperty("active_peers") - var activePeers: Int?, - @JsonProperty("connected_seeders") - var connectedSeeders: Int?, - @JsonProperty("half_open_peers") - var halfOpenPeers: Int?, - @JsonProperty("bytes_written") - var bytesWritten: Long?, - @JsonProperty("bytes_written_data") - var bytesWrittenData: Long?, - @JsonProperty("bytes_read") - var bytesRead: Long?, - @JsonProperty("bytes_read_data") - var bytesReadData: Long?, - @JsonProperty("bytes_read_useful_data") - var bytesReadUsefulData: Long?, - @JsonProperty("chunks_written") - var chunksWritten: Long?, - @JsonProperty("chunks_read") - var chunksRead: Long?, - @JsonProperty("chunks_read_useful") - var chunksReadUseful: Long?, - @JsonProperty("chunks_read_wasted") - var chunksReadWasted: Long?, - @JsonProperty("pieces_dirtied_good") - var piecesDirtiedGood: Long?, - @JsonProperty("pieces_dirtied_bad") - var piecesDirtiedBad: Long?, - @JsonProperty("duration_seconds") - var durationSeconds: Double?, - @JsonProperty("bit_rate") - var bitRate: String?, - @JsonProperty("file_stats") - var fileStats: List?, - @JsonProperty("trackers") - var trackers: List?, + @JsonProperty("title") @SerialName("title") var title: String, + @JsonProperty("poster") @SerialName("poster") var poster: String, + @JsonProperty("data") @SerialName("data") var data: String?, + @JsonProperty("timestamp") @SerialName("timestamp") var timestamp: Long, + @JsonProperty("name") @SerialName("name") var name: String?, + @JsonProperty("hash") @SerialName("hash") var hash: String?, + @JsonProperty("stat") @SerialName("stat") var stat: Int, + @JsonProperty("stat_string") @SerialName("stat_string") var statString: String, + @JsonProperty("loaded_size") @SerialName("loaded_size") var loadedSize: Long?, + @JsonProperty("torrent_size") @SerialName("torrent_size") var torrentSize: Long?, + @JsonProperty("preloaded_bytes") @SerialName("preloaded_bytes") var preloadedBytes: Long?, + @JsonProperty("preload_size") @SerialName("preload_size") var preloadSize: Long?, + @JsonProperty("download_speed") @SerialName("download_speed") var downloadSpeed: Double?, + @JsonProperty("upload_speed") @SerialName("upload_speed") var uploadSpeed: Double?, + @JsonProperty("total_peers") @SerialName("total_peers") var totalPeers: Int?, + @JsonProperty("pending_peers") @SerialName("pending_peers") var pendingPeers: Int?, + @JsonProperty("active_peers") @SerialName("active_peers") var activePeers: Int?, + @JsonProperty("connected_seeders") @SerialName("connected_seeders") var connectedSeeders: Int?, + @JsonProperty("half_open_peers") @SerialName("half_open_peers") var halfOpenPeers: Int?, + @JsonProperty("bytes_written") @SerialName("bytes_written") var bytesWritten: Long?, + @JsonProperty("bytes_written_data") @SerialName("bytes_written_data") var bytesWrittenData: Long?, + @JsonProperty("bytes_read") @SerialName("bytes_read") var bytesRead: Long?, + @JsonProperty("bytes_read_data") @SerialName("bytes_read_data") var bytesReadData: Long?, + @JsonProperty("bytes_read_useful_data") @SerialName("bytes_read_useful_data") var bytesReadUsefulData: Long?, + @JsonProperty("chunks_written") @SerialName("chunks_written") var chunksWritten: Long?, + @JsonProperty("chunks_read") @SerialName("chunks_read") var chunksRead: Long?, + @JsonProperty("chunks_read_useful") @SerialName("chunks_read_useful") var chunksReadUseful: Long?, + @JsonProperty("chunks_read_wasted") @SerialName("chunks_read_wasted") var chunksReadWasted: Long?, + @JsonProperty("pieces_dirtied_good") @SerialName("pieces_dirtied_good") var piecesDirtiedGood: Long?, + @JsonProperty("pieces_dirtied_bad") @SerialName("pieces_dirtied_bad") var piecesDirtiedBad: Long?, + @JsonProperty("duration_seconds") @SerialName("duration_seconds") var durationSeconds: Double?, + @JsonProperty("bit_rate") @SerialName("bit_rate") var bitRate: String?, + @JsonProperty("file_stats") @SerialName("file_stats") var fileStats: List?, + @JsonProperty("trackers") @SerialName("trackers") var trackers: List?, ) { fun streamUrl(url: String): String { val fileName = @@ -381,12 +342,10 @@ object Torrent { } } + @Serializable data class TorrentFileStat( - @JsonProperty("id") - val id: Int?, - @JsonProperty("path") - val path: String?, - @JsonProperty("length") - val length: Long?, + @JsonProperty("id") @SerialName("id") val id: Int?, + @JsonProperty("path") @SerialName("path") val path: String?, + @JsonProperty("length") @SerialName("length") val length: Long?, ) -} \ No newline at end of file +} From 15632b864ac60a1eac66be4cd23e810945437c58 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 12 Jul 2026 16:18:22 -0600 Subject: [PATCH 06/38] [skip ci] TraktProvider: migrate to kotlinx serialization (#2872) --- library/api/jvm/library.api | 225 +++++++++++++- .../metaproviders/TraktProvider.kt | 276 ++++++++++-------- 2 files changed, 366 insertions(+), 135 deletions(-) diff --git a/library/api/jvm/library.api b/library/api/jvm/library.api index 025ebc391..cd2aaeea1 100644 --- a/library/api/jvm/library.api +++ b/library/api/jvm/library.api @@ -5831,6 +5831,7 @@ public class com/lagradost/cloudstream3/metaproviders/TraktProvider : com/lagrad } public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$Airs { + public static final field Companion Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Airs$Companion; public fun ()V public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V @@ -5847,7 +5848,23 @@ public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$Airs { public fun toString ()Ljava/lang/String; } +public final synthetic class com/lagradost/cloudstream3/metaproviders/TraktProvider$Airs$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Airs$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Airs; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Airs;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$Airs$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$Cast { + public static final field Companion Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Cast$Companion; public fun ()V public fun (Ljava/lang/String;Ljava/util/List;Ljava/lang/Long;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Person;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Images;)V public synthetic fun (Ljava/lang/String;Ljava/util/List;Ljava/lang/Long;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Person;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Images;ILkotlin/jvm/internal/DefaultConstructorMarker;)V @@ -5868,7 +5885,23 @@ public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$Cast { public fun toString ()Ljava/lang/String; } +public final synthetic class com/lagradost/cloudstream3/metaproviders/TraktProvider$Cast$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Cast$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Cast; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Cast;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$Cast$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$Data { + public static final field Companion Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Data$Companion; public fun ()V public fun (Lcom/lagradost/cloudstream3/TvType;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$MediaDetails;)V public synthetic fun (Lcom/lagradost/cloudstream3/TvType;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$MediaDetails;ILkotlin/jvm/internal/DefaultConstructorMarker;)V @@ -5883,7 +5916,23 @@ public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$Data { public fun toString ()Ljava/lang/String; } +public final synthetic class com/lagradost/cloudstream3/metaproviders/TraktProvider$Data$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Data$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Data; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Data;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$Data$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$Ids { + public static final field Companion Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Ids$Companion; public fun ()V public fun (Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;)V public synthetic fun (Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V @@ -5906,7 +5955,23 @@ public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$Ids { public fun toString ()Ljava/lang/String; } +public final synthetic class com/lagradost/cloudstream3/metaproviders/TraktProvider$Ids$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Ids$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Ids; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Ids;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$Ids$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$Images { + public static final field Companion Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Images$Companion; public fun ()V public fun (Ljava/util/List;Ljava/util/List;Ljava/util/List;Ljava/util/List;Ljava/util/List;Ljava/util/List;Ljava/util/List;Ljava/util/List;)V public synthetic fun (Ljava/util/List;Ljava/util/List;Ljava/util/List;Ljava/util/List;Ljava/util/List;Ljava/util/List;Ljava/util/List;Ljava/util/List;ILkotlin/jvm/internal/DefaultConstructorMarker;)V @@ -5933,7 +5998,23 @@ public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$Images public fun toString ()Ljava/lang/String; } +public final synthetic class com/lagradost/cloudstream3/metaproviders/TraktProvider$Images$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Images$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Images; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Images;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$Images$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$LinkData { + public static final field Companion Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$LinkData$Companion; public fun ()V public fun (Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;ZLjava/lang/Integer;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZZZ)V public synthetic fun (Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;ZLjava/lang/Integer;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZZZILkotlin/jvm/internal/DefaultConstructorMarker;)V @@ -5994,10 +6075,26 @@ public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$LinkDa public fun toString ()Ljava/lang/String; } +public final synthetic class com/lagradost/cloudstream3/metaproviders/TraktProvider$LinkData$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$LinkData$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$LinkData; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$LinkData;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$LinkData$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$MediaDetails { + public static final field Companion Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$MediaDetails$Companion; public fun ()V - public fun (Ljava/lang/String;Ljava/lang/Integer;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Ids;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Double;Ljava/lang/Long;Ljava/lang/Long;Ljava/lang/String;Ljava/util/List;Ljava/util/List;Ljava/util/List;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Airs;Ljava/lang/String;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Images;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$MediaDetails;)V - public synthetic fun (Ljava/lang/String;Ljava/lang/Integer;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Ids;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Double;Ljava/lang/Long;Ljava/lang/Long;Ljava/lang/String;Ljava/util/List;Ljava/util/List;Ljava/util/List;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Airs;Ljava/lang/String;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Images;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$MediaDetails;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Ljava/lang/String;Ljava/lang/Integer;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Ids;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Double;Ljava/lang/Long;Ljava/lang/Long;Ljava/lang/String;Ljava/util/List;Ljava/util/List;Ljava/util/List;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Airs;Ljava/lang/String;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Images;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$MediaSummary;)V + public synthetic fun (Ljava/lang/String;Ljava/lang/Integer;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Ids;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Double;Ljava/lang/Long;Ljava/lang/Long;Ljava/lang/String;Ljava/util/List;Ljava/util/List;Ljava/util/List;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Airs;Ljava/lang/String;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Images;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$MediaSummary;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun component1 ()Ljava/lang/String; public final fun component10 ()Ljava/lang/String; public final fun component11 ()Ljava/lang/String; @@ -6016,7 +6113,7 @@ public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$MediaD public final fun component23 ()Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Airs; public final fun component24 ()Ljava/lang/String; public final fun component25 ()Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Images; - public final fun component26 ()Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$MediaDetails; + public final fun component26 ()Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$MediaSummary; public final fun component3 ()Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Ids; public final fun component4 ()Ljava/lang/String; public final fun component5 ()Ljava/lang/String; @@ -6024,8 +6121,8 @@ public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$MediaD public final fun component7 ()Ljava/lang/Integer; public final fun component8 ()Ljava/lang/String; public final fun component9 ()Ljava/lang/String; - public final fun copy (Ljava/lang/String;Ljava/lang/Integer;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Ids;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Double;Ljava/lang/Long;Ljava/lang/Long;Ljava/lang/String;Ljava/util/List;Ljava/util/List;Ljava/util/List;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Airs;Ljava/lang/String;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Images;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$MediaDetails;)Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$MediaDetails; - public static synthetic fun copy$default (Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$MediaDetails;Ljava/lang/String;Ljava/lang/Integer;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Ids;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Double;Ljava/lang/Long;Ljava/lang/Long;Ljava/lang/String;Ljava/util/List;Ljava/util/List;Ljava/util/List;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Airs;Ljava/lang/String;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Images;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$MediaDetails;ILjava/lang/Object;)Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$MediaDetails; + public final fun copy (Ljava/lang/String;Ljava/lang/Integer;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Ids;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Double;Ljava/lang/Long;Ljava/lang/Long;Ljava/lang/String;Ljava/util/List;Ljava/util/List;Ljava/util/List;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Airs;Ljava/lang/String;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Images;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$MediaSummary;)Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$MediaDetails; + public static synthetic fun copy$default (Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$MediaDetails;Ljava/lang/String;Ljava/lang/Integer;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Ids;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Double;Ljava/lang/Long;Ljava/lang/Long;Ljava/lang/String;Ljava/util/List;Ljava/util/List;Ljava/util/List;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Airs;Ljava/lang/String;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Images;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$MediaSummary;ILjava/lang/Object;)Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$MediaDetails; public fun equals (Ljava/lang/Object;)Z public final fun getAiredEpisodes ()Ljava/lang/Integer; public final fun getAirs ()Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Airs; @@ -6040,7 +6137,7 @@ public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$MediaD public final fun getImages ()Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Images; public final fun getLanguage ()Ljava/lang/String; public final fun getLanguages ()Ljava/util/List; - public final fun getMedia ()Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$MediaDetails; + public final fun getMedia ()Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$MediaSummary; public final fun getNetwork ()Ljava/lang/String; public final fun getOverview ()Ljava/lang/String; public final fun getRating ()Ljava/lang/Double; @@ -6057,7 +6154,60 @@ public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$MediaD public fun toString ()Ljava/lang/String; } +public final synthetic class com/lagradost/cloudstream3/metaproviders/TraktProvider$MediaDetails$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$MediaDetails$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$MediaDetails; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$MediaDetails;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$MediaDetails$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$MediaSummary { + public static final field Companion Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$MediaSummary$Companion; + public fun ()V + public fun (Ljava/lang/String;Ljava/lang/Integer;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Ids;Ljava/lang/Double;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Images;)V + public synthetic fun (Ljava/lang/String;Ljava/lang/Integer;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Ids;Ljava/lang/Double;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Images;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Ljava/lang/String; + public final fun component2 ()Ljava/lang/Integer; + public final fun component3 ()Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Ids; + public final fun component4 ()Ljava/lang/Double; + public final fun component5 ()Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Images; + public final fun copy (Ljava/lang/String;Ljava/lang/Integer;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Ids;Ljava/lang/Double;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Images;)Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$MediaSummary; + public static synthetic fun copy$default (Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$MediaSummary;Ljava/lang/String;Ljava/lang/Integer;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Ids;Ljava/lang/Double;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Images;ILjava/lang/Object;)Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$MediaSummary; + public fun equals (Ljava/lang/Object;)Z + public final fun getIds ()Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Ids; + public final fun getImages ()Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Images; + public final fun getRating ()Ljava/lang/Double; + public final fun getTitle ()Ljava/lang/String; + public final fun getYear ()Ljava/lang/Integer; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final synthetic class com/lagradost/cloudstream3/metaproviders/TraktProvider$MediaSummary$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$MediaSummary$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$MediaSummary; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$MediaSummary;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$MediaSummary$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$People { + public static final field Companion Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$People$Companion; public fun ()V public fun (Ljava/util/List;)V public synthetic fun (Ljava/util/List;ILkotlin/jvm/internal/DefaultConstructorMarker;)V @@ -6070,7 +6220,23 @@ public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$People public fun toString ()Ljava/lang/String; } +public final synthetic class com/lagradost/cloudstream3/metaproviders/TraktProvider$People$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$People$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$People; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$People;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$People$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$Person { + public static final field Companion Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Person$Companion; public fun ()V public fun (Ljava/lang/String;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Ids;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Images;)V public synthetic fun (Ljava/lang/String;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Ids;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Images;ILkotlin/jvm/internal/DefaultConstructorMarker;)V @@ -6087,7 +6253,23 @@ public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$Person public fun toString ()Ljava/lang/String; } +public final synthetic class com/lagradost/cloudstream3/metaproviders/TraktProvider$Person$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Person$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Person; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Person;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$Person$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$Seasons { + public static final field Companion Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Seasons$Companion; public fun ()V public fun (Ljava/lang/Integer;Ljava/lang/Integer;Ljava/util/List;Ljava/lang/String;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Ids;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Images;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/Double;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;)V public synthetic fun (Ljava/lang/Integer;Ljava/lang/Integer;Ljava/util/List;Ljava/lang/String;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Ids;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Images;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/Double;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;ILkotlin/jvm/internal/DefaultConstructorMarker;)V @@ -6124,7 +6306,23 @@ public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$Season public fun toString ()Ljava/lang/String; } +public final synthetic class com/lagradost/cloudstream3/metaproviders/TraktProvider$Seasons$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Seasons$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Seasons; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Seasons;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$Seasons$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$TraktEpisode { + public static final field Companion Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$TraktEpisode$Companion; public fun ()V public fun (Ljava/util/List;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Ids;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Images;Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/Double;Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;)V public synthetic fun (Ljava/util/List;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Ids;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Images;Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/Double;Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;ILkotlin/jvm/internal/DefaultConstructorMarker;)V @@ -6165,6 +6363,21 @@ public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$TraktE public fun toString ()Ljava/lang/String; } +public final synthetic class com/lagradost/cloudstream3/metaproviders/TraktProvider$TraktEpisode$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$TraktEpisode$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$TraktEpisode; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$TraktEpisode;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$TraktEpisode$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public final class com/lagradost/cloudstream3/mvvm/ArchComponentExtKt { public static final field DEBUG_EXCEPTION Ljava/lang/String; public static final field DEBUG_PRINT Ljava/lang/String; diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/metaproviders/TraktProvider.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/metaproviders/TraktProvider.kt index dafec4d97..6011e14ea 100644 --- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/metaproviders/TraktProvider.kt +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/metaproviders/TraktProvider.kt @@ -34,6 +34,10 @@ import com.lagradost.cloudstream3.newTvSeriesLoadResponse import com.lagradost.cloudstream3.newTvSeriesSearchResponse import com.lagradost.cloudstream3.utils.AppUtils.parseJson import com.lagradost.cloudstream3.utils.AppUtils.toJson +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import kotlinx.serialization.json.JsonNames open class TraktProvider : MainAPI() { override var name = "Trakt" @@ -46,7 +50,6 @@ open class TraktProvider : MainAPI() { ) private val traktApiUrl = "https://api.trakt.tv" - private val traktClientId: String = BuildConfig.TRAKT_CLIENT_ID override val mainPage = mainPageOf( @@ -58,16 +61,21 @@ open class TraktProvider : MainAPI() { override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse { val apiResponse = getApi("${request.data}?extended=full,images&page=$page") - val results = parseJson>(apiResponse).map { element -> element.toSearchResponse() } + return newHomePageResponse(request.name, results) } private fun MediaDetails.toSearchResponse(): SearchResponse { - - val media = this.media ?: this + val media = this.media ?: MediaSummary( + title = this.title, + year = this.year, + ids = this.ids, + rating = this.rating, + images = this.images, + ) val mediaType = if (media.ids?.tvdb == null) TvType.Movie else TvType.TvSeries val poster = media.images?.poster?.firstOrNull() return if (mediaType == TvType.Movie) { @@ -75,7 +83,7 @@ open class TraktProvider : MainAPI() { name = media.title ?: "", url = Data( type = mediaType, - mediaDetails = media, + mediaDetails = this, ).toJson(), type = TvType.Movie, ) { @@ -87,7 +95,7 @@ open class TraktProvider : MainAPI() { name = media.title ?: "", url = Data( type = mediaType, - mediaDetails = media, + mediaDetails = this, ).toJson(), type = TvType.TvSeries, ) { @@ -98,9 +106,7 @@ open class TraktProvider : MainAPI() { } override suspend fun search(query: String, page: Int): SearchResponseList? { - val apiResponse = - getApi("$traktApiUrl/search/movie,show?extended=full,images&limit=20&page=$page&query=$query") - + val apiResponse = getApi("$traktApiUrl/search/movie,show?extended=full,images&limit=20&page=$page&query=$query") return newSearchResponseList(parseJson>(apiResponse).map { element -> element.toSearchResponse() }) @@ -115,9 +121,7 @@ open class TraktProvider : MainAPI() { val backDropUrl = fixPath(mediaDetails?.images?.fanart?.firstOrNull()) val logoUrl = fixPath(mediaDetails?.images?.logo?.firstOrNull()) - val resActor = - getApi("$traktApiUrl/$moviesOrShows/${mediaDetails?.ids?.trakt}/people?extended=full,images") - + val resActor = getApi("$traktApiUrl/$moviesOrShows/${mediaDetails?.ids?.trakt}/people?extended=full,images") val actors = parseJson(resActor).cast?.map { ActorData( Actor( @@ -128,9 +132,7 @@ open class TraktProvider : MainAPI() { ) } - val resRelated = - getApi("$traktApiUrl/$moviesOrShows/${mediaDetails?.ids?.trakt}/related?extended=full,images&limit=20") - + val resRelated = getApi("$traktApiUrl/$moviesOrShows/${mediaDetails?.ids?.trakt}/related?extended=full,images&limit=20") val relatedMedia = parseJson>(resRelated).map { it.toSearchResponse() } val isCartoon = @@ -142,7 +144,6 @@ open class TraktProvider : MainAPI() { val uniqueUrl = data.mediaDetails?.ids?.trakt?.toJson() ?: data.toJson() if (data.type == TvType.Movie) { - val linkData = LinkData( id = mediaDetails?.ids?.tmdb, traktId = mediaDetails?.ids?.trakt, @@ -156,7 +157,7 @@ open class TraktProvider : MainAPI() { year = mediaDetails?.year, orgTitle = mediaDetails?.title, isAnime = isAnime, - //jpTitle = later if needed as it requires another network request, + // jpTitle = later if needed as it requires another network request, airedDate = mediaDetails?.released ?: mediaDetails?.firstAired, isAsian = isAsian, @@ -190,7 +191,6 @@ open class TraktProvider : MainAPI() { addTMDbId(mediaDetails.ids?.tmdb.toString()) } } else { - val resSeasons = getApi("$traktApiUrl/shows/${mediaDetails?.ids?.trakt.toString()}/seasons?extended=full,images,episodes") val episodes = mutableListOf() @@ -198,9 +198,7 @@ open class TraktProvider : MainAPI() { var nextAir: NextAiring? = null seasons.forEach { season -> - season.episodes?.map { episode -> - val linkData = LinkData( id = mediaDetails?.ids?.tmdb, traktId = mediaDetails?.ids?.trakt, @@ -234,8 +232,7 @@ open class TraktProvider : MainAPI() { this.episode = episode.number this.description = episode.overview this.runTime = episode.runtime - this.posterUrl = fixPath( episode.images?.screenshot?.firstOrNull()) - //this.rating = episode.rating?.times(10)?.roundToInt() + this.posterUrl = fixPath(episode.images?.screenshot?.firstOrNull()) this.score = Score.from10(episode.rating) this.addDate(episode.firstAired, "yyyy-MM-dd'T'HH:mm:ss.SSSXXX") @@ -307,143 +304,164 @@ open class TraktProvider : MainAPI() { return "https://$url" } + @Serializable data class Data( - val type: TvType? = null, - val mediaDetails: MediaDetails? = null, + @JsonProperty("type") @SerialName("type") val type: TvType? = null, + @JsonProperty("mediaDetails") @SerialName("mediaDetails") val mediaDetails: MediaDetails? = null, ) + @Serializable + data class MediaSummary( + @JsonProperty("title") @SerialName("title") val title: String? = null, + @JsonProperty("year") @SerialName("year") val year: Int? = null, + @JsonProperty("ids") @SerialName("ids") val ids: Ids? = null, + @JsonProperty("rating") @SerialName("rating") val rating: Double? = null, + @JsonProperty("images") @SerialName("images") val images: Images? = null, + ) + + @OptIn(ExperimentalSerializationApi::class) // JsonNames is an experimental annotation for now + @Serializable data class MediaDetails( - @JsonProperty("title") val title: String? = null, - @JsonProperty("year") val year: Int? = null, - @JsonProperty("ids") val ids: Ids? = null, - @JsonProperty("tagline") val tagline: String? = null, - @JsonProperty("overview") val overview: String? = null, - @JsonProperty("released") val released: String? = null, - @JsonProperty("runtime") val runtime: Int? = null, - @JsonProperty("country") val country: String? = null, - @JsonProperty("updatedAt") val updatedAt: String? = null, - @JsonProperty("trailer") val trailer: String? = null, - @JsonProperty("homepage") val homepage: String? = null, - @JsonProperty("status") val status: String? = null, - @JsonProperty("rating") val rating: Double? = null, - @JsonProperty("votes") val votes: Long? = null, - @JsonProperty("comment_count") val commentCount: Long? = null, - @JsonProperty("language") val language: String? = null, - @JsonProperty("languages") val languages: List? = null, - @JsonProperty("available_translations") val availableTranslations: List? = null, - @JsonProperty("genres") val genres: List? = null, - @JsonProperty("certification") val certification: String? = null, - @JsonProperty("aired_episodes") val airedEpisodes: Int? = null, - @JsonProperty("first_aired") val firstAired: String? = null, - @JsonProperty("airs") val airs: Airs? = null, - @JsonProperty("network") val network: String? = null, - @JsonProperty("images") val images: Images? = null, - @JsonProperty("movie") @JsonAlias("show") val media: MediaDetails? = null + @JsonProperty("title") @SerialName("title") val title: String? = null, + @JsonProperty("year") @SerialName("year") val year: Int? = null, + @JsonProperty("ids") @SerialName("ids") val ids: Ids? = null, + @JsonProperty("tagline") @SerialName("tagline") val tagline: String? = null, + @JsonProperty("overview") @SerialName("overview") val overview: String? = null, + @JsonProperty("released") @SerialName("released") val released: String? = null, + @JsonProperty("runtime") @SerialName("runtime") val runtime: Int? = null, + @JsonProperty("country") @SerialName("country") val country: String? = null, + @JsonProperty("updatedAt") @SerialName("updatedAt") val updatedAt: String? = null, + @JsonProperty("trailer") @SerialName("trailer") val trailer: String? = null, + @JsonProperty("homepage") @SerialName("homepage") val homepage: String? = null, + @JsonProperty("status") @SerialName("status") val status: String? = null, + @JsonProperty("rating") @SerialName("rating") val rating: Double? = null, + @JsonProperty("votes") @SerialName("votes") val votes: Long? = null, + @JsonProperty("comment_count") @SerialName("comment_count") val commentCount: Long? = null, + @JsonProperty("language") @SerialName("language") val language: String? = null, + @JsonProperty("languages") @SerialName("languages") val languages: List? = null, + @JsonProperty("available_translations") @SerialName("available_translations") val availableTranslations: List? = null, + @JsonProperty("genres") @SerialName("genres") val genres: List? = null, + @JsonProperty("certification") @SerialName("certification") val certification: String? = null, + @JsonProperty("aired_episodes") @SerialName("aired_episodes") val airedEpisodes: Int? = null, + @JsonProperty("first_aired") @SerialName("first_aired") val firstAired: String? = null, + @JsonProperty("airs") @SerialName("airs") val airs: Airs? = null, + @JsonProperty("network") @SerialName("network") val network: String? = null, + @JsonProperty("images") @SerialName("images") val images: Images? = null, + @JsonProperty("media") @JsonAlias("movie", "show") @SerialName("media") @JsonNames("movie", "show") val media: MediaSummary? = null, ) + @Serializable data class Airs( - @JsonProperty("day") val day: String? = null, - @JsonProperty("time") val time: String? = null, - @JsonProperty("timezone") val timezone: String? = null, + @JsonProperty("day") @SerialName("day") val day: String? = null, + @JsonProperty("time") @SerialName("time") val time: String? = null, + @JsonProperty("timezone") @SerialName("timezone") val timezone: String? = null, ) + @Serializable data class Ids( - @JsonProperty("trakt") val trakt: Int? = null, - @JsonProperty("slug") val slug: String? = null, - @JsonProperty("tvdb") val tvdb: Int? = null, - @JsonProperty("imdb") val imdb: String? = null, - @JsonProperty("tmdb") val tmdb: Int? = null, - @JsonProperty("tvrage") val tvrage: String? = null, + @JsonProperty("trakt") @SerialName("trakt") val trakt: Int? = null, + @JsonProperty("slug") @SerialName("slug") val slug: String? = null, + @JsonProperty("tvdb") @SerialName("tvdb") val tvdb: Int? = null, + @JsonProperty("imdb") @SerialName("imdb") val imdb: String? = null, + @JsonProperty("tmdb") @SerialName("tmdb") val tmdb: Int? = null, + @JsonProperty("tvrage") @SerialName("tvrage") val tvrage: String? = null, ) + @Serializable data class Images( - @JsonProperty("poster") val poster: List? = null, - @JsonProperty("fanart") val fanart: List? = null, - @JsonProperty("logo") val logo: List? = null, - @JsonProperty("clearart") val clearArt: List? = null, - @JsonProperty("banner") val banner: List? = null, - @JsonProperty("thumb") val thumb: List? = null, - @JsonProperty("screenshot") val screenshot: List? = null, - @JsonProperty("headshot") val headshot: List? = null, + @JsonProperty("poster") @SerialName("poster") val poster: List? = null, + @JsonProperty("fanart") @SerialName("fanart") val fanart: List? = null, + @JsonProperty("logo") @SerialName("logo") val logo: List? = null, + @JsonProperty("clearart") @SerialName("clearart") val clearArt: List? = null, + @JsonProperty("banner") @SerialName("banner") val banner: List? = null, + @JsonProperty("thumb") @SerialName("thumb") val thumb: List? = null, + @JsonProperty("screenshot") @SerialName("screenshot") val screenshot: List? = null, + @JsonProperty("headshot") @SerialName("headshot") val headshot: List? = null, ) + @Serializable data class People( - @JsonProperty("cast") val cast: List? = null, + @JsonProperty("cast") @SerialName("cast") val cast: List? = null, ) + @Serializable data class Cast( - @JsonProperty("character") val character: String? = null, - @JsonProperty("characters") val characters: List? = null, - @JsonProperty("episode_count") val episodeCount: Long? = null, - @JsonProperty("person") val person: Person? = null, - @JsonProperty("images") val images: Images? = null, + @JsonProperty("character") @SerialName("character") val character: String? = null, + @JsonProperty("characters") @SerialName("characters") val characters: List? = null, + @JsonProperty("episode_count") @SerialName("episode_count") val episodeCount: Long? = null, + @JsonProperty("person") @SerialName("person") val person: Person? = null, + @JsonProperty("images") @SerialName("images") val images: Images? = null, ) + @Serializable data class Person( - @JsonProperty("name") val name: String? = null, - @JsonProperty("ids") val ids: Ids? = null, - @JsonProperty("images") val images: Images? = null, + @JsonProperty("name") @SerialName("name") val name: String? = null, + @JsonProperty("ids") @SerialName("ids") val ids: Ids? = null, + @JsonProperty("images") @SerialName("images") val images: Images? = null, ) + @Serializable data class Seasons( - @JsonProperty("aired_episodes") val airedEpisodes: Int? = null, - @JsonProperty("episode_count") val episodeCount: Int? = null, - @JsonProperty("episodes") val episodes: List? = null, - @JsonProperty("first_aired") val firstAired: String? = null, - @JsonProperty("ids") val ids: Ids? = null, - @JsonProperty("images") val images: Images? = null, - @JsonProperty("network") val network: String? = null, - @JsonProperty("number") val number: Int? = null, - @JsonProperty("overview") val overview: String? = null, - @JsonProperty("rating") val rating: Double? = null, - @JsonProperty("title") val title: String? = null, - @JsonProperty("updated_at") val updatedAt: String? = null, - @JsonProperty("votes") val votes: Int? = null, + @JsonProperty("aired_episodes") @SerialName("aired_episodes") val airedEpisodes: Int? = null, + @JsonProperty("episode_count") @SerialName("episode_count") val episodeCount: Int? = null, + @JsonProperty("episodes") @SerialName("episodes") val episodes: List? = null, + @JsonProperty("first_aired") @SerialName("first_aired") val firstAired: String? = null, + @JsonProperty("ids") @SerialName("ids") val ids: Ids? = null, + @JsonProperty("images") @SerialName("images") val images: Images? = null, + @JsonProperty("network") @SerialName("network") val network: String? = null, + @JsonProperty("number") @SerialName("number") val number: Int? = null, + @JsonProperty("overview") @SerialName("overview") val overview: String? = null, + @JsonProperty("rating") @SerialName("rating") val rating: Double? = null, + @JsonProperty("title") @SerialName("title") val title: String? = null, + @JsonProperty("updated_at") @SerialName("updated_at") val updatedAt: String? = null, + @JsonProperty("votes") @SerialName("votes") val votes: Int? = null, ) + @Serializable data class TraktEpisode( - @JsonProperty("available_translations") val availableTranslations: List? = null, - @JsonProperty("comment_count") val commentCount: Int? = null, - @JsonProperty("episode_type") val episodeType: String? = null, - @JsonProperty("first_aired") val firstAired: String? = null, - @JsonProperty("ids") val ids: Ids? = null, - @JsonProperty("images") val images: Images? = null, - @JsonProperty("number") val number: Int? = null, - @JsonProperty("number_abs") val numberAbs: Int? = null, - @JsonProperty("overview") val overview: String? = null, - @JsonProperty("rating") val rating: Double? = null, - @JsonProperty("runtime") val runtime: Int? = null, - @JsonProperty("season") val season: Int? = null, - @JsonProperty("title") val title: String? = null, - @JsonProperty("updated_at") val updatedAt: String? = null, - @JsonProperty("votes") val votes: Int? = null, + @JsonProperty("available_translations") @SerialName("available_translations") val availableTranslations: List? = null, + @JsonProperty("comment_count") @SerialName("comment_count") val commentCount: Int? = null, + @JsonProperty("episode_type") @SerialName("episode_type") val episodeType: String? = null, + @JsonProperty("first_aired") @SerialName("first_aired") val firstAired: String? = null, + @JsonProperty("ids") @SerialName("ids") val ids: Ids? = null, + @JsonProperty("images") @SerialName("images") val images: Images? = null, + @JsonProperty("number") @SerialName("number") val number: Int? = null, + @JsonProperty("number_abs") @SerialName("number_abs") val numberAbs: Int? = null, + @JsonProperty("overview") @SerialName("overview") val overview: String? = null, + @JsonProperty("rating") @SerialName("rating") val rating: Double? = null, + @JsonProperty("runtime") @SerialName("runtime") val runtime: Int? = null, + @JsonProperty("season") @SerialName("season") val season: Int? = null, + @JsonProperty("title") @SerialName("title") val title: String? = null, + @JsonProperty("updated_at") @SerialName("updated_at") val updatedAt: String? = null, + @JsonProperty("votes") @SerialName("votes") val votes: Int? = null, ) + @Serializable data class LinkData( - @JsonProperty("id") val id: Int? = null, - @JsonProperty("trakt_id") val traktId: Int? = null, - @JsonProperty("trakt_slug") val traktSlug: String? = null, - @JsonProperty("tmdb_id") val tmdbId: Int? = null, - @JsonProperty("imdb_id") val imdbId: String? = null, - @JsonProperty("tvdb_id") val tvdbId: Int? = null, - @JsonProperty("tvrage_id") val tvrageId: String? = null, - @JsonProperty("type") val type: String? = null, - @JsonProperty("season") val season: Int? = null, - @JsonProperty("episode") val episode: Int? = null, - @JsonProperty("ani_id") val aniId: String? = null, - @JsonProperty("anime_id") val animeId: String? = null, - @JsonProperty("title") val title: String? = null, - @JsonProperty("year") val year: Int? = null, - @JsonProperty("org_title") val orgTitle: String? = null, - @JsonProperty("is_anime") val isAnime: Boolean = false, - @JsonProperty("aired_year") val airedYear: Int? = null, - @JsonProperty("last_season") val lastSeason: Int? = null, - @JsonProperty("eps_title") val epsTitle: String? = null, - @JsonProperty("jp_title") val jpTitle: String? = null, - @JsonProperty("date") val date: String? = null, - @JsonProperty("aired_date") val airedDate: String? = null, - @JsonProperty("is_asian") val isAsian: Boolean = false, - @JsonProperty("is_bollywood") val isBollywood: Boolean = false, - @JsonProperty("is_cartoon") val isCartoon: Boolean = false, + @JsonProperty("id") @SerialName("id") val id: Int? = null, + @JsonProperty("trakt_id") @SerialName("trakt_id") val traktId: Int? = null, + @JsonProperty("trakt_slug") @SerialName("trakt_slug") val traktSlug: String? = null, + @JsonProperty("tmdb_id") @SerialName("tmdb_id") val tmdbId: Int? = null, + @JsonProperty("imdb_id") @SerialName("imdb_id") val imdbId: String? = null, + @JsonProperty("tvdb_id") @SerialName("tvdb_id") val tvdbId: Int? = null, + @JsonProperty("tvrage_id") @SerialName("tvrage_id") val tvrageId: String? = null, + @JsonProperty("type") @SerialName("type") val type: String? = null, + @JsonProperty("season") @SerialName("season") val season: Int? = null, + @JsonProperty("episode") @SerialName("episode") val episode: Int? = null, + @JsonProperty("ani_id") @SerialName("ani_id") val aniId: String? = null, + @JsonProperty("anime_id") @SerialName("anime_id") val animeId: String? = null, + @JsonProperty("title") @SerialName("title") val title: String? = null, + @JsonProperty("year") @SerialName("year") val year: Int? = null, + @JsonProperty("org_title") @SerialName("org_title") val orgTitle: String? = null, + @JsonProperty("is_anime") @SerialName("is_anime") val isAnime: Boolean = false, + @JsonProperty("aired_year") @SerialName("aired_year") val airedYear: Int? = null, + @JsonProperty("last_season") @SerialName("last_season") val lastSeason: Int? = null, + @JsonProperty("eps_title") @SerialName("eps_title") val epsTitle: String? = null, + @JsonProperty("jp_title") @SerialName("jp_title") val jpTitle: String? = null, + @JsonProperty("date") @SerialName("date") val date: String? = null, + @JsonProperty("aired_date") @SerialName("aired_date") val airedDate: String? = null, + @JsonProperty("is_asian") @SerialName("is_asian") val isAsian: Boolean = false, + @JsonProperty("is_bollywood") @SerialName("is_bollywood") val isBollywood: Boolean = false, + @JsonProperty("is_cartoon") @SerialName("is_cartoon") val isCartoon: Boolean = false, ) } From 7b3c938ba72f11303ec38bdf3b12e5560285bca2 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 12 Jul 2026 16:38:08 -0600 Subject: [PATCH 07/38] Add more custom serializers for kotlinx serialization (#2978) This adds numerous other serializers to replicate Jackson mapper configuration behavior, that some extensions will need. --- .../utils/serializers/SerializerTest.kt | 157 ----- .../utils/serializers/UriSerializerTest.kt | 41 ++ .../utils/serializers/FloatAsIntSerializer.kt | 42 ++ .../serializers/FloatAsLongSerializer.kt | 42 ++ .../serializers/JsonTransformSerializer.kt | 75 +++ .../utils/serializers/NonEmptySerializer.kt | 9 +- .../serializers/NullableStringSerializer.kt | 49 ++ .../utils/serializers/WriteOnlySerializer.kt | 6 +- .../utils/serializers/SerializersTest.kt | 602 ++++++++++++++++++ 9 files changed, 858 insertions(+), 165 deletions(-) delete mode 100644 app/src/androidTest/java/com/lagradost/cloudstream3/utils/serializers/SerializerTest.kt create mode 100644 app/src/androidTest/java/com/lagradost/cloudstream3/utils/serializers/UriSerializerTest.kt create mode 100644 library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/serializers/FloatAsIntSerializer.kt create mode 100644 library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/serializers/FloatAsLongSerializer.kt create mode 100644 library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/serializers/JsonTransformSerializer.kt create mode 100644 library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/serializers/NullableStringSerializer.kt create mode 100644 library/src/commonTest/kotlin/com/lagradost/cloudstream3/utils/serializers/SerializersTest.kt diff --git a/app/src/androidTest/java/com/lagradost/cloudstream3/utils/serializers/SerializerTest.kt b/app/src/androidTest/java/com/lagradost/cloudstream3/utils/serializers/SerializerTest.kt deleted file mode 100644 index 15ad532f8..000000000 --- a/app/src/androidTest/java/com/lagradost/cloudstream3/utils/serializers/SerializerTest.kt +++ /dev/null @@ -1,157 +0,0 @@ -package com.lagradost.cloudstream3.utils.serializers - -import android.net.Uri -import com.lagradost.cloudstream3.utils.AppUtils.parseJson -import com.lagradost.cloudstream3.utils.AppUtils.toJson -import kotlinx.serialization.ExperimentalSerializationApi -import kotlinx.serialization.KeepGeneratedSerializer -import kotlinx.serialization.Serializable -import org.junit.Assert.assertEquals -import org.junit.Assert.assertFalse -import org.junit.Assert.assertNull -import org.junit.Assert.assertTrue -import org.junit.Test - -@OptIn(ExperimentalSerializationApi::class) -@KeepGeneratedSerializer -@Serializable(with = NonEmptyData.Serializer::class) -data class NonEmptyData( - val title: String = "", - val tags: List = emptyList(), - val meta: Map = emptyMap(), - val name: String = "hello", -) { - object Serializer : NonEmptySerializer(NonEmptyData.generatedSerializer()) -} - -@OptIn(ExperimentalSerializationApi::class) -@KeepGeneratedSerializer -@Serializable(with = WriteOnlyData.Serializer::class) -data class WriteOnlyData( - val fieldA: String = "", - val fieldB: String = "", -) { - object Serializer : WriteOnlySerializer( - WriteOnlyData.generatedSerializer(), - setOf("fieldB"), - ) -} - -@OptIn(ExperimentalSerializationApi::class) -@KeepGeneratedSerializer -@Serializable(with = MultiWriteOnly.Serializer::class) -data class MultiWriteOnly( - val fieldA: String = "", - val fieldB: String = "", - val fieldC: String = "", -) { - object Serializer : WriteOnlySerializer( - MultiWriteOnly.generatedSerializer(), - setOf("fieldB", "fieldC"), - ) -} - -@Serializable -data class UriData( - @Serializable(with = UriSerializer::class) - val uri: Uri = Uri.EMPTY, -) - -class SerializerTest { - - @Test - fun nonEmptySerializerOmitsEmptyStrings() { - val data = NonEmptyData(title = "", name = "hello") - val result = data.toJson() - assertFalse(result.contains("title")) - assertTrue(result.contains("name")) - } - - @Test - fun nonEmptySerializerOmitsEmptyLists() { - val data = NonEmptyData(tags = emptyList(), name = "hello") - val result = data.toJson() - assertFalse(result.contains("tags")) - } - - @Test - fun nonEmptySerializerOmitsEmptyMaps() { - val data = NonEmptyData(meta = emptyMap(), name = "hello") - val result = data.toJson() - assertFalse(result.contains("meta")) - } - - @Test - fun nonEmptySerializerKeepsNonEmptyFields() { - val data = NonEmptyData(title = "hello", tags = listOf("a"), meta = mapOf("k" to "v")) - val result = data.toJson() - assertTrue(result.contains("title")) - assertTrue(result.contains("tags")) - assertTrue(result.contains("meta")) - } - - @Test - fun nonEmptySerializerDoesNotAffectDeserialization() { - val input = """{"title":"hello","tags":["a"],"meta":{"k":"v"},"name":"world"}""" - val result = parseJson(input) - assertEquals("hello", result.title) - assertEquals(listOf("a"), result.tags) - assertEquals(mapOf("k" to "v"), result.meta) - assertEquals("world", result.name) - } - - @Test - fun writeOnlySerializerOmitsFieldOnSerialize() { - val data = WriteOnlyData(fieldA = "hello", fieldB = "secret") - val result = data.toJson() - assertTrue(result.contains("fieldA")) - assertFalse(result.contains("fieldB")) - } - - @Test - fun writeOnlySerializerDeserializesNormally() { - val input = """{"fieldA":"hello","fieldB":"secret"}""" - val result = parseJson(input) - assertEquals("hello", result.fieldA) - assertEquals("secret", result.fieldB) - } - - @Test - fun writeOnlySerializerDeserializesMissingAsDefault() { - val input = """{"fieldA":"hello"}""" - val result = parseJson(input) - assertEquals("hello", result.fieldA) - assertEquals("", result.fieldB) - } - - @Test - fun writeOnlySerializerHandlesMultipleKeys() { - val data = MultiWriteOnly(fieldA = "hello", fieldB = "secret1", fieldC = "secret2") - val result = data.toJson() - assertTrue(result.contains("fieldA")) - assertFalse(result.contains("fieldB")) - assertFalse(result.contains("fieldC")) - } - - @Test - fun uriSerializerSerializesUriToString() { - val data = UriData(uri = Uri.parse("https://example.com/path?query=1")) - val result = data.toJson() - assertTrue(result.contains("https://example.com/path?query=1")) - } - - @Test - fun uriSerializerDeserializesStringToUri() { - val input = """{"uri":"https://example.com/path?query=1"}""" - val result = parseJson(input) - assertEquals(Uri.parse("https://example.com/path?query=1"), result.uri) - } - - @Test - fun uriSerializerRoundtripsCorrectly() { - val data = UriData(uri = Uri.parse("https://example.com/path?query=1")) - val encoded = data.toJson() - val decoded = parseJson(encoded) - assertEquals(data.uri, decoded.uri) - } -} diff --git a/app/src/androidTest/java/com/lagradost/cloudstream3/utils/serializers/UriSerializerTest.kt b/app/src/androidTest/java/com/lagradost/cloudstream3/utils/serializers/UriSerializerTest.kt new file mode 100644 index 000000000..3ffd37124 --- /dev/null +++ b/app/src/androidTest/java/com/lagradost/cloudstream3/utils/serializers/UriSerializerTest.kt @@ -0,0 +1,41 @@ +package com.lagradost.cloudstream3.utils.serializers + +import android.net.Uri +import com.lagradost.cloudstream3.utils.AppUtils.parseJson +import com.lagradost.cloudstream3.utils.AppUtils.toJson +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue +import org.junit.Test + +@Serializable +data class UriData( + @Serializable(with = UriSerializer::class) + @SerialName("uri") val uri: Uri = Uri.EMPTY, +) + +class UriSerializerTest { + + @Test + fun uriSerializerSerializesUriToString() { + val data = UriData(uri = Uri.parse("https://example.com/path?query=1")) + val result = data.toJson() + assertTrue(result.contains("https://example.com/path?query=1")) + } + + @Test + fun uriSerializerDeserializesStringToUri() { + val input = """{"uri":"https://example.com/path?query=1"}""" + val result = parseJson(input) + assertEquals(Uri.parse("https://example.com/path?query=1"), result.uri) + } + + @Test + fun uriSerializerRoundtripsCorrectly() { + val data = UriData(uri = Uri.parse("https://example.com/path?query=1")) + val encoded = data.toJson() + val decoded = parseJson(encoded) + assertEquals(data.uri, decoded.uri) + } +} diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/serializers/FloatAsIntSerializer.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/serializers/FloatAsIntSerializer.kt new file mode 100644 index 000000000..12c8f6ed0 --- /dev/null +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/serializers/FloatAsIntSerializer.kt @@ -0,0 +1,42 @@ +package com.lagradost.cloudstream3.utils.serializers + +import com.lagradost.cloudstream3.Prerelease +import kotlinx.serialization.KSerializer +import kotlinx.serialization.descriptors.PrimitiveKind +import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +import kotlinx.serialization.json.JsonDecoder +import kotlinx.serialization.json.JsonPrimitive +import kotlinx.serialization.json.doubleOrNull +import kotlinx.serialization.json.intOrNull + +/** + * Replicates Jackson's ACCEPT_FLOAT_AS_INT behaviour for Int fields. + * A floating-point JSON number is truncated to Int by dropping the + * fractional part, exactly like Jackson's default truncation behaviour. + * + * Usage: + * + * @Serializable + * data class MyData( + * @Serializable(with = FloatAsIntSerializer::class) + * @SerialName("count") val count: Int = 0, + * ) + */ +@Prerelease +object FloatAsIntSerializer : KSerializer { + + override val descriptor: SerialDescriptor = + PrimitiveSerialDescriptor("FloatAsInt", PrimitiveKind.INT) + + override fun deserialize(decoder: Decoder): Int { + val jsonDecoder = decoder as? JsonDecoder ?: return decoder.decodeInt() + val element = jsonDecoder.decodeJsonElement() + if (element !is JsonPrimitive) return 0 + return element.intOrNull ?: element.doubleOrNull?.toInt() ?: 0 + } + + override fun serialize(encoder: Encoder, value: Int) = encoder.encodeInt(value) +} diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/serializers/FloatAsLongSerializer.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/serializers/FloatAsLongSerializer.kt new file mode 100644 index 000000000..7fc394107 --- /dev/null +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/serializers/FloatAsLongSerializer.kt @@ -0,0 +1,42 @@ +package com.lagradost.cloudstream3.utils.serializers + +import com.lagradost.cloudstream3.Prerelease +import kotlinx.serialization.KSerializer +import kotlinx.serialization.descriptors.PrimitiveKind +import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +import kotlinx.serialization.json.JsonDecoder +import kotlinx.serialization.json.JsonPrimitive +import kotlinx.serialization.json.doubleOrNull +import kotlinx.serialization.json.longOrNull + +/** + * Replicates Jackson's ACCEPT_FLOAT_AS_INT behaviour for Long fields. + * A floating-point JSON number is truncated to Long by dropping the + * fractional part, exactly like Jackson's default truncation behaviour. + * + * Usage: + * + * @Serializable + * data class MyData( + * @Serializable(with = FloatAsLongSerializer::class) + * @SerialName("timestamp") val timestamp: Long = 0L, + * ) + */ +@Prerelease +object FloatAsLongSerializer : KSerializer { + + override val descriptor: SerialDescriptor = + PrimitiveSerialDescriptor("FloatAsLong", PrimitiveKind.LONG) + + override fun deserialize(decoder: Decoder): Long { + val jsonDecoder = decoder as? JsonDecoder ?: return decoder.decodeLong() + val element = jsonDecoder.decodeJsonElement() + if (element !is JsonPrimitive) return 0L + return element.longOrNull ?: element.doubleOrNull?.toLong() ?: 0L + } + + override fun serialize(encoder: Encoder, value: Long) = encoder.encodeLong(value) +} diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/serializers/JsonTransformSerializer.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/serializers/JsonTransformSerializer.kt new file mode 100644 index 000000000..6b9c25b59 --- /dev/null +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/serializers/JsonTransformSerializer.kt @@ -0,0 +1,75 @@ +package com.lagradost.cloudstream3.utils.serializers + +import com.lagradost.cloudstream3.Prerelease +import kotlinx.serialization.KSerializer +import kotlinx.serialization.json.JsonArray +import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.json.JsonNull +import kotlinx.serialization.json.JsonObject +import kotlinx.serialization.json.JsonPrimitive +import kotlinx.serialization.json.JsonTransformingSerializer + +/** + * Composable deserialize transformer that applies multiple Jackson-equivalent + * behaviours to specific fields by key. Pass only the sets you need; all + * default to empty (no-op). + * + * Behaviours (applied in order per field): + * singleValueAsListKeys ACCEPT_SINGLE_VALUE_AS_ARRAY + * emptyStringAsNullKeys ACCEPT_EMPTY_STRING_AS_NULL_OBJECT + * emptyArrayAsNullKeys ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT + * + * Usage: + * + * @OptIn(ExperimentalSerializationApi::class) // KeepGeneratedSerializer is an experimental annotation for now + * @KeepGeneratedSerializer + * @Serializable(with = MyData.Serializer::class) + * data class MyData( + * @SerialName("title") val title: String? = null, + * @SerialName("tags") val tags: List? = null, + * @SerialName("meta") val meta: MyMeta? = null, + * ) { + * object Serializer : JsonTransformSerializer( + * MyData.generatedSerializer(), + * singleValueAsListKeys = setOf("tags"), + * emptyStringAsNullKeys = setOf("title", "tags"), + * emptyArrayAsNullKeys = setOf("tags", "meta"), + * ) + * } + */ +@Prerelease +abstract class JsonTransformSerializer( + tSerializer: KSerializer, + private val singleValueAsListKeys: Set = emptySet(), + private val emptyArrayAsNullKeys: Set = emptySet(), + private val emptyStringAsNullKeys: Set = emptySet(), +) : JsonTransformingSerializer(tSerializer) { + + override fun transformDeserialize(element: JsonElement): JsonElement { + if (element !is JsonObject) return element + return JsonObject(element.mapValues { (key, value) -> + var result = value + if (key in singleValueAsListKeys) { + result = when (result) { + is JsonArray -> result + JsonNull -> JsonArray(emptyList()) + else -> JsonArray(listOf(result)) + } + } + + if (key in emptyStringAsNullKeys) { + if (result is JsonPrimitive && result.isString && result.content.isEmpty()) { + result = JsonNull + } + } + + if (key in emptyArrayAsNullKeys) { + if (result is JsonArray && result.isEmpty()) { + result = JsonNull + } + } + + result + }) + } +} diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/serializers/NonEmptySerializer.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/serializers/NonEmptySerializer.kt index 62b306e1f..8bc8f1734 100644 --- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/serializers/NonEmptySerializer.kt +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/serializers/NonEmptySerializer.kt @@ -17,13 +17,13 @@ import kotlinx.serialization.json.JsonTransformingSerializer * * Usage: * - * @OptIn(ExperimentalSerializationApi::class) + * @OptIn(ExperimentalSerializationApi::class) // KeepGeneratedSerializer is an experimental annotation for now * @KeepGeneratedSerializer * @Serializable(with = MyData.Serializer::class) * data class MyData( - * val tags: List = emptyList(), - * val title: String = "", - * val meta: Map = emptyMap(), + * @SerialName("tags") val tags: List = emptyList(), + * @SerialName("title") val title: String = "", + * @SerialName("meta") val meta: Map = emptyMap(), * ) { * object Serializer : NonEmptySerializer(MyData.generatedSerializer()) * } @@ -33,7 +33,6 @@ abstract class NonEmptySerializer(tSerializer: KSerializer) : override fun transformSerialize(element: JsonElement): JsonElement { if (element !is JsonObject) return element - return JsonObject(element.filterValues { value -> when (value) { is JsonPrimitive -> value.content.isNotEmpty() diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/serializers/NullableStringSerializer.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/serializers/NullableStringSerializer.kt new file mode 100644 index 000000000..6755304a5 --- /dev/null +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/serializers/NullableStringSerializer.kt @@ -0,0 +1,49 @@ +package com.lagradost.cloudstream3.utils.serializers + +import com.lagradost.cloudstream3.Prerelease +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.KSerializer +import kotlinx.serialization.builtins.serializer +import kotlinx.serialization.descriptors.PrimitiveKind +import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +import kotlinx.serialization.json.JsonDecoder +import kotlinx.serialization.json.JsonNull +import kotlinx.serialization.json.JsonPrimitive +import kotlinx.serialization.json.decodeFromJsonElement + +/** + * Convenience serializer for nullable String fields that combines + * ACCEPT_EMPTY_STRING_AS_NULL_OBJECT with null passthrough. + * An empty JSON string (`""`) or JSON null is decoded as null. + * + * Usage: + * + * @Serializable + * data class MyData( + * @Serializable(with = NullableStringSerializer::class) + * @SerialName("title") val title: String? = null, + * ) + */ +@Prerelease +object NullableStringSerializer : KSerializer { + + override val descriptor: SerialDescriptor = + PrimitiveSerialDescriptor("NullableString", PrimitiveKind.STRING) + + override fun deserialize(decoder: Decoder): String? { + val jsonDecoder = decoder as? JsonDecoder ?: return decoder.decodeString() + return when (val element = jsonDecoder.decodeJsonElement()) { + is JsonPrimitive -> element.content.ifEmpty { null } + JsonNull -> null + else -> jsonDecoder.json.decodeFromJsonElement(String.serializer(), element) + } + } + + override fun serialize(encoder: Encoder, value: String?) { + @OptIn(ExperimentalSerializationApi::class) // encodeNull is experimental for now + if (value == null) encoder.encodeNull() else encoder.encodeString(value) + } +} diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/serializers/WriteOnlySerializer.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/serializers/WriteOnlySerializer.kt index 1d06b7fca..1b6ca9aee 100644 --- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/serializers/WriteOnlySerializer.kt +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/serializers/WriteOnlySerializer.kt @@ -12,12 +12,12 @@ import kotlinx.serialization.json.JsonTransformingSerializer * * Usage: * - * @OptIn(ExperimentalSerializationApi::class) + * @OptIn(ExperimentalSerializationApi::class) // KeepGeneratedSerializer is an experimental annotation for now * @KeepGeneratedSerializer * @Serializable(with = MyData.Serializer::class) * data class MyData( - * val fieldA: String = "", - * val fieldB: String = "", + * @SerialName("fieldA") val fieldA: String = "", + * @SerialName("fieldB") val fieldB: String = "", * ) { * object Serializer : WriteOnlySerializer( * MyData.generatedSerializer(), diff --git a/library/src/commonTest/kotlin/com/lagradost/cloudstream3/utils/serializers/SerializersTest.kt b/library/src/commonTest/kotlin/com/lagradost/cloudstream3/utils/serializers/SerializersTest.kt new file mode 100644 index 000000000..89c0fea81 --- /dev/null +++ b/library/src/commonTest/kotlin/com/lagradost/cloudstream3/utils/serializers/SerializersTest.kt @@ -0,0 +1,602 @@ +package com.lagradost.cloudstream3.utils.serializers + +import com.lagradost.cloudstream3.utils.AppUtils.parseJson +import com.lagradost.cloudstream3.utils.AppUtils.toJson +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.KeepGeneratedSerializer +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFalse +import kotlin.test.assertNull +import kotlin.test.assertTrue + +@OptIn(ExperimentalSerializationApi::class) // KeepGeneratedSerializer is an experimental annotation for now +@KeepGeneratedSerializer +@Serializable(with = NonEmptyData.Serializer::class) +data class NonEmptyData( + @SerialName("title") val title: String = "", + @SerialName("tags") val tags: List = emptyList(), + @SerialName("meta") val meta: Map = emptyMap(), + @SerialName("name") val name: String = "hello", +) { + object Serializer : NonEmptySerializer(NonEmptyData.generatedSerializer()) +} + +@OptIn(ExperimentalSerializationApi::class) // KeepGeneratedSerializer is an experimental annotation for now +@KeepGeneratedSerializer +@Serializable(with = WriteOnlyData.Serializer::class) +data class WriteOnlyData( + @SerialName("fieldA") val fieldA: String = "", + @SerialName("fieldB") val fieldB: String = "", +) { + object Serializer : WriteOnlySerializer( + WriteOnlyData.generatedSerializer(), + setOf("fieldB"), + ) +} + +@OptIn(ExperimentalSerializationApi::class) // KeepGeneratedSerializer is an experimental annotation for now +@KeepGeneratedSerializer +@Serializable(with = MultiWriteOnly.Serializer::class) +data class MultiWriteOnly( + @SerialName("fieldA") val fieldA: String = "", + @SerialName("fieldB") val fieldB: String = "", + @SerialName("fieldC") val fieldC: String = "", +) { + object Serializer : WriteOnlySerializer( + MultiWriteOnly.generatedSerializer(), + setOf("fieldB", "fieldC"), + ) +} + +@OptIn(ExperimentalSerializationApi::class) // KeepGeneratedSerializer is an experimental annotation for now +@KeepGeneratedSerializer +@Serializable(with = SingleValueData.Serializer::class) +data class SingleValueData( + @SerialName("tags") val tags: List = emptyList(), + @SerialName("nums") val nums: List = emptyList(), +) { + object Serializer : JsonTransformSerializer( + SingleValueData.generatedSerializer(), + singleValueAsListKeys = setOf("tags", "nums"), + ) +} + +@Serializable +data class NestedMeta( + @SerialName("key") val key: String = "", +) + +@OptIn(ExperimentalSerializationApi::class) // KeepGeneratedSerializer is an experimental annotation for now +@KeepGeneratedSerializer +@Serializable(with = EmptyArrayData.Serializer::class) +data class EmptyArrayData( + @SerialName("meta") val meta: NestedMeta? = null, + @SerialName("other") val other: String = "hello", +) { + object Serializer : JsonTransformSerializer( + EmptyArrayData.generatedSerializer(), + emptyArrayAsNullKeys = setOf("meta"), + ) +} + +@OptIn(ExperimentalSerializationApi::class) // KeepGeneratedSerializer is an experimental annotation for now +@KeepGeneratedSerializer +@Serializable(with = EmptyStringData.Serializer::class) +data class EmptyStringData( + @SerialName("title") val title: String? = null, + @SerialName("episode") val episode: NestedMeta? = null, + @SerialName("keep") val keep: String? = "hello", +) { + object Serializer : JsonTransformSerializer( + EmptyStringData.generatedSerializer(), + emptyStringAsNullKeys = setOf("title", "episode", "keep"), + ) +} + +@OptIn(ExperimentalSerializationApi::class) // KeepGeneratedSerializer is an experimental annotation for now +@KeepGeneratedSerializer +@Serializable(with = SingleValueOrNullData.Serializer::class) +data class SingleValueOrNullData( + @SerialName("tags") val tags: List? = null, + @SerialName("nums") val nums: List? = null, + @SerialName("other") val other: String = "hello", +) { + object Serializer : JsonTransformSerializer( + SingleValueOrNullData.generatedSerializer(), + singleValueAsListKeys = setOf("tags", "nums"), + emptyArrayAsNullKeys = setOf("tags", "nums"), + ) +} + +@OptIn(ExperimentalSerializationApi::class) // KeepGeneratedSerializer is an experimental annotation for now +@KeepGeneratedSerializer +@Serializable(with = NullableFieldsData.Serializer::class) +data class NullableFieldsData( + @SerialName("title") val title: String? = null, + @SerialName("meta") val meta: NestedMeta? = null, + @SerialName("other") val other: String = "hello", +) { + object Serializer : JsonTransformSerializer( + NullableFieldsData.generatedSerializer(), + emptyStringAsNullKeys = setOf("title"), + emptyArrayAsNullKeys = setOf("meta"), + ) +} + +@OptIn(ExperimentalSerializationApi::class) // KeepGeneratedSerializer is an experimental annotation for now +@KeepGeneratedSerializer +@Serializable(with = SingleValueOrEmptyStringData.Serializer::class) +data class SingleValueOrEmptyStringData( + @SerialName("tags") val tags: List = emptyList(), + @SerialName("title") val title: String? = null, +) { + object Serializer : JsonTransformSerializer( + SingleValueOrEmptyStringData.generatedSerializer(), + singleValueAsListKeys = setOf("tags"), + emptyStringAsNullKeys = setOf("title"), + ) +} + +@OptIn(ExperimentalSerializationApi::class) // KeepGeneratedSerializer is an experimental annotation for now +@KeepGeneratedSerializer +@Serializable(with = AllTransformsData.Serializer::class) +data class AllTransformsData( + @SerialName("tags") val tags: List? = null, + @SerialName("title") val title: String? = null, + @SerialName("meta") val meta: NestedMeta? = null, + @SerialName("other") val other: String = "hello", +) { + object Serializer : JsonTransformSerializer( + AllTransformsData.generatedSerializer(), + singleValueAsListKeys = setOf("tags"), + emptyArrayAsNullKeys = setOf("tags", "meta"), + emptyStringAsNullKeys = setOf("title", "tags"), + ) +} + +@Serializable +data class FloatIntData( + @Serializable(with = FloatAsIntSerializer::class) + @SerialName("count") val count: Int = 0, +) + +@Serializable +data class FloatLongData( + @Serializable(with = FloatAsLongSerializer::class) + @SerialName("timestamp") val timestamp: Long = 0L, +) + +@Serializable +data class NullableStringData( + @Serializable(with = NullableStringSerializer::class) + @SerialName("title") val title: String? = null, +) + +class SerializersTest { + + @Test + fun nonEmptySerializerOmitsEmptyStrings() { + val data = NonEmptyData(title = "", name = "hello") + val result = data.toJson() + assertFalse(result.contains("title")) + assertTrue(result.contains("name")) + } + + @Test + fun nonEmptySerializerOmitsEmptyLists() { + val data = NonEmptyData(tags = emptyList(), name = "hello") + val result = data.toJson() + assertFalse(result.contains("tags")) + } + + @Test + fun nonEmptySerializerOmitsEmptyMaps() { + val data = NonEmptyData(meta = emptyMap(), name = "hello") + val result = data.toJson() + assertFalse(result.contains("meta")) + } + + @Test + fun nonEmptySerializerKeepsNonEmptyFields() { + val data = NonEmptyData(title = "hello", tags = listOf("a"), meta = mapOf("k" to "v")) + val result = data.toJson() + assertTrue(result.contains("title")) + assertTrue(result.contains("tags")) + assertTrue(result.contains("meta")) + } + + @Test + fun nonEmptySerializerDoesNotAffectDeserialization() { + val input = """{"title":"hello","tags":["a"],"meta":{"k":"v"},"name":"world"}""" + val result = parseJson(input) + assertEquals("hello", result.title) + assertEquals(listOf("a"), result.tags) + assertEquals(mapOf("k" to "v"), result.meta) + assertEquals("world", result.name) + } + + @Test + fun writeOnlySerializerOmitsFieldOnSerialize() { + val data = WriteOnlyData(fieldA = "hello", fieldB = "secret") + val result = data.toJson() + assertTrue(result.contains("fieldA")) + assertFalse(result.contains("fieldB")) + } + + @Test + fun writeOnlySerializerDeserializesNormally() { + val input = """{"fieldA":"hello","fieldB":"secret"}""" + val result = parseJson(input) + assertEquals("hello", result.fieldA) + assertEquals("secret", result.fieldB) + } + + @Test + fun writeOnlySerializerDeserializesMissingAsDefault() { + val input = """{"fieldA":"hello"}""" + val result = parseJson(input) + assertEquals("hello", result.fieldA) + assertEquals("", result.fieldB) + } + + @Test + fun writeOnlySerializerHandlesMultipleKeys() { + val data = MultiWriteOnly(fieldA = "hello", fieldB = "secret1", fieldC = "secret2") + val result = data.toJson() + assertTrue(result.contains("fieldA")) + assertFalse(result.contains("fieldB")) + assertFalse(result.contains("fieldC")) + } + + @Test + fun singleValueAsListDecodesArrayNormally() { + val input = """{"tags":["a","b"],"nums":[1,2]}""" + val result = parseJson(input) + assertEquals(listOf("a", "b"), result.tags) + assertEquals(listOf(1, 2), result.nums) + } + + @Test + fun singleValueAsListWrapsBareStringInList() { + val input = """{"tags":"a","nums":[1]}""" + val result = parseJson(input) + assertEquals(listOf("a"), result.tags) + } + + @Test + fun singleValueAsListWrapsBareIntInList() { + val input = """{"tags":[],"nums":42}""" + val result = parseJson(input) + assertEquals(listOf(42), result.nums) + } + + @Test + fun singleValueAsListDecodesNullAsEmptyList() { + val input = """{"tags":null,"nums":[]}""" + val result = parseJson(input) + assertEquals(emptyList(), result.tags) + } + + @Test + fun singleValueAsListRoundtripsCorrectly() { + val data = SingleValueData(tags = listOf("x", "y"), nums = listOf(1, 2)) + val encoded = data.toJson() + val decoded = parseJson(encoded) + assertEquals(data, decoded) + } + + @Test + fun emptyArrayAsNullDecodesEmptyArrayAsNull() { + val input = """{"meta":[],"other":"hello"}""" + val result = parseJson(input) + assertNull(result.meta) + } + + @Test + fun emptyArrayAsNullDecodesNullAsNull() { + val input = """{"meta":null,"other":"hello"}""" + val result = parseJson(input) + assertNull(result.meta) + } + + @Test + fun emptyArrayAsNullDecodesObjectNormally() { + val input = """{"meta":{"key":"value"},"other":"hello"}""" + val result = parseJson(input) + assertEquals(NestedMeta("value"), result.meta) + } + + @Test + fun emptyArrayAsNullDoesNotAffectOtherFields() { + val input = """{"meta":[],"other":"world"}""" + val result = parseJson(input) + assertEquals("world", result.other) + } + + @Test + fun emptyArrayAsNullRoundtripsCorrectly() { + val data = EmptyArrayData(meta = NestedMeta("test"), other = "hello") + val encoded = data.toJson() + val decoded = parseJson(encoded) + assertEquals(data, decoded) + } + + @Test + fun emptyStringAsNullDecodesEmptyStringAsNull() { + val input = """{"title":"","episode":null,"keep":"hello"}""" + val result = parseJson(input) + assertNull(result.title) + } + + @Test + fun emptyStringAsNullDecodesNullAsNull() { + val input = """{"title":null,"episode":null,"keep":"hello"}""" + val result = parseJson(input) + assertNull(result.title) + } + + @Test + fun emptyStringAsNullKeepsNonEmptyString() { + val input = """{"title":"hello","episode":null,"keep":"world"}""" + val result = parseJson(input) + assertEquals("hello", result.title) + } + + @Test + fun emptyStringAsNullDecodesObjectNormally() { + val input = """{"title":null,"episode":{"key":"value"},"keep":"hello"}""" + val result = parseJson(input) + assertEquals(NestedMeta("value"), result.episode) + } + + @Test + fun emptyStringAsNullDoesNotAffectNonEmptyFields() { + val input = """{"title":"","episode":null,"keep":"world"}""" + val result = parseJson(input) + assertEquals("world", result.keep) + } + + @Test + fun emptyStringAsNullRoundtripsCorrectly() { + val data = EmptyStringData(title = "hello", episode = NestedMeta("x"), keep = "world") + val encoded = data.toJson() + val decoded = parseJson(encoded) + assertEquals(data, decoded) + } + + @Test + fun singleValueOrNullWrapsBareValueInList() { + val input = """{"tags":"a","nums":1,"other":"hello"}""" + val result = parseJson(input) + assertEquals(listOf("a"), result.tags) + assertEquals(listOf(1), result.nums) + } + + @Test + fun singleValueOrNullTreatsEmptyArrayAsNull() { + val input = """{"tags":[],"nums":[],"other":"hello"}""" + val result = parseJson(input) + assertNull(result.tags) + assertNull(result.nums) + } + + @Test + fun singleValueOrNullDecodesArrayNormally() { + val input = """{"tags":["a","b"],"nums":[1,2],"other":"hello"}""" + val result = parseJson(input) + assertEquals(listOf("a", "b"), result.tags) + assertEquals(listOf(1, 2), result.nums) + } + + @Test + fun singleValueOrNullDoesNotAffectOtherFields() { + val input = """{"tags":[],"nums":[],"other":"world"}""" + val result = parseJson(input) + assertEquals("world", result.other) + } + + @Test + fun nullableFieldsEmptyStringBecomesNull() { + val input = """{"title":"","meta":{"key":"value"},"other":"hello"}""" + val result = parseJson(input) + assertNull(result.title) + assertEquals(NestedMeta("value"), result.meta) + } + + @Test + fun nullableFieldsEmptyArrayBecomesNull() { + val input = """{"title":"hello","meta":[],"other":"hello"}""" + val result = parseJson(input) + assertEquals("hello", result.title) + assertNull(result.meta) + } + + @Test + fun nullableFieldsBothNullAtOnce() { + val input = """{"title":"","meta":[],"other":"hello"}""" + val result = parseJson(input) + assertNull(result.title) + assertNull(result.meta) + } + + @Test + fun nullableFieldsDoesNotAffectOtherFields() { + val input = """{"title":"","meta":[],"other":"world"}""" + val result = parseJson(input) + assertEquals("world", result.other) + } + + @Test + fun nullableFieldsRoundtripsCorrectly() { + val data = NullableFieldsData(title = "hello", meta = NestedMeta("x"), other = "world") + val encoded = data.toJson() + val decoded = parseJson(encoded) + assertEquals(data, decoded) + } + + @Test + fun singleValueOrEmptyStringWrapsBareString() { + val input = """{"tags":"a","title":"hello"}""" + val result = parseJson(input) + assertEquals(listOf("a"), result.tags) + assertEquals("hello", result.title) + } + + @Test + fun singleValueOrEmptyStringTurnsEmptyTitleToNull() { + val input = """{"tags":["a"],"title":""}""" + val result = parseJson(input) + assertEquals(listOf("a"), result.tags) + assertNull(result.title) + } + + @Test + fun singleValueOrEmptyStringRoundtripsCorrectly() { + val data = SingleValueOrEmptyStringData(tags = listOf("x"), title = "hello") + val encoded = data.toJson() + val decoded = parseJson(encoded) + assertEquals(data, decoded) + } + + @Test + fun allTransformsWrapsBareStringInList() { + val input = """{"tags":"a","title":"hello","meta":{"key":"value"},"other":"world"}""" + val result = parseJson(input) + assertEquals(listOf("a"), result.tags) + } + + @Test + fun allTransformsEmptyArrayTagsBecomesNull() { + val input = """{"tags":[],"title":"hello","meta":{"key":"value"},"other":"world"}""" + val result = parseJson(input) + assertNull(result.tags) + } + + @Test + fun allTransformsEmptyMetaBecomesNull() { + val input = """{"tags":["a"],"title":"hello","meta":[],"other":"world"}""" + val result = parseJson(input) + assertNull(result.meta) + } + + @Test + fun allTransformsEmptyTitleBecomesNull() { + val input = """{"tags":["a"],"title":"","meta":{"key":"value"},"other":"world"}""" + val result = parseJson(input) + assertNull(result.title) + } + + @Test + fun allTransformsAllNullAtOnce() { + val input = """{"tags":[],"title":"","meta":[],"other":"world"}""" + val result = parseJson(input) + assertNull(result.tags) + assertNull(result.title) + assertNull(result.meta) + } + + @Test + fun allTransformsDoesNotAffectOtherFields() { + val input = """{"tags":[],"title":"","meta":[],"other":"world"}""" + val result = parseJson(input) + assertEquals("world", result.other) + } + + @Test + fun allTransformsRoundtripsCorrectly() { + val data = AllTransformsData(tags = listOf("a"), title = "hello", meta = NestedMeta("x"), other = "world") + val encoded = data.toJson() + val decoded = parseJson(encoded) + assertEquals(data, decoded) + } + + @Test + fun floatAsIntTruncatesFloat() { + val input = """{"count":3.9}""" + val result = parseJson(input) + assertEquals(3, result.count) + } + + @Test + fun floatAsIntDecodesIntNormally() { + val input = """{"count":42}""" + val result = parseJson(input) + assertEquals(42, result.count) + } + + @Test + fun floatAsIntHandlesNegativeFloat() { + val input = """{"count":-2.7}""" + val result = parseJson(input) + assertEquals(-2, result.count) + } + + @Test + fun floatAsIntRoundtripsCorrectly() { + val data = FloatIntData(count = 7) + val encoded = data.toJson() + val decoded = parseJson(encoded) + assertEquals(data, decoded) + } + + @Test + fun floatAsLongTruncatesFloat() { + val input = """{"timestamp":1234567890.9}""" + val result = parseJson(input) + assertEquals(1234567890L, result.timestamp) + } + + @Test + fun floatAsLongDecodesLongNormally() { + val input = """{"timestamp":9999999999}""" + val result = parseJson(input) + assertEquals(9999999999L, result.timestamp) + } + + @Test + fun floatAsLongHandlesNegativeFloat() { + val input = """{"timestamp":-100.6}""" + val result = parseJson(input) + assertEquals(-100L, result.timestamp) + } + + @Test + fun floatAsLongRoundtripsCorrectly() { + val data = FloatLongData(timestamp = 1700000000L) + val encoded = data.toJson() + val decoded = parseJson(encoded) + assertEquals(data, decoded) + } + + @Test + fun nullableStringDecodesEmptyStringAsNull() { + val input = """{"title":""}""" + val result = parseJson(input) + assertNull(result.title) + } + + @Test + fun nullableStringDecodesNullAsNull() { + val input = """{"title":null}""" + val result = parseJson(input) + assertNull(result.title) + } + + @Test + fun nullableStringKeepsNonEmptyString() { + val input = """{"title":"hello"}""" + val result = parseJson(input) + assertEquals("hello", result.title) + } + + @Test + fun nullableStringRoundtripsCorrectly() { + val data = NullableStringData(title = "world") + val encoded = data.toJson() + val decoded = parseJson(encoded) + assertEquals(data, decoded) + } +} From 2a75b8cb08aa5b384633dd08b76d546ed0173acd Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Mon, 13 Jul 2026 06:40:47 -0600 Subject: [PATCH 08/38] [skip ci] Mark UriSerializer InternalAPI (#3062) We shouldn't really be encouraging extensions to use it and use ktor Url instead which has a serializer for it built into ktor. Eventually I think we should factor out the two usages of an actual Uri in serialization and remove this serializer. --- .../lagradost/cloudstream3/utils/serializers/UriSerializer.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/serializers/UriSerializer.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/serializers/UriSerializer.kt index 7c73a6889..adceede42 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/serializers/UriSerializer.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/serializers/UriSerializer.kt @@ -1,6 +1,7 @@ package com.lagradost.cloudstream3.utils.serializers import android.net.Uri +import com.lagradost.cloudstream3.InternalAPI import kotlinx.serialization.KSerializer import kotlinx.serialization.descriptors.PrimitiveKind import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor @@ -26,6 +27,7 @@ import kotlinx.serialization.encoding.Encoder * val uri: Uri, * ) */ +@InternalAPI object UriSerializer : KSerializer { override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Uri", PrimitiveKind.STRING) From 105f168b2d6f31cd5a18dacfb6190de370a08eed Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Mon, 13 Jul 2026 06:42:15 -0600 Subject: [PATCH 09/38] [skip ci] Add new splitUrlParameters API accepting Ktor Url or string (#2931) --- .../cloudstream3/syncproviders/AuthAPI.kt | 9 +-- .../cloudstream3/utils/AppContextUtils.kt | 26 +++---- .../com/lagradost/cloudstream3/MainAPI.kt | 54 ++++++++++++- .../cloudstream3/SplitUrlParametersTest.kt | 76 +++++++++++++++++++ 4 files changed, 141 insertions(+), 24 deletions(-) create mode 100644 library/src/commonTest/kotlin/com/lagradost/cloudstream3/SplitUrlParametersTest.kt diff --git a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/AuthAPI.kt b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/AuthAPI.kt index 60e41e8b3..c0f0e4a03 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/AuthAPI.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/AuthAPI.kt @@ -6,13 +6,12 @@ import com.lagradost.cloudstream3.APIHolder import com.lagradost.cloudstream3.APIHolder.unixTime import com.lagradost.cloudstream3.APIHolder.unixTimeMS import com.lagradost.cloudstream3.base64Encode +import com.lagradost.cloudstream3.splitUrlParameters import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.APP_STRING -import com.lagradost.cloudstream3.utils.AppContextUtils.splitQuery import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import kotlinx.serialization.json.JsonNames -import java.net.URI import java.security.SecureRandom data class AuthLoginPage( @@ -172,10 +171,8 @@ abstract class AuthAPI { get() = unixTimeMS fun splitRedirectUrl(redirectUrl: String): Map { - return splitQuery( - URI( - redirectUrl.replace(APP_STRING, "https").replace("/#", "?") - ).toURL() + return splitUrlParameters( + redirectUrl.replace(APP_STRING, "https").replace("/#", "?") ) } diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/AppContextUtils.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/AppContextUtils.kt index c0be85a3f..8d322fd04 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/AppContextUtils.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/AppContextUtils.kt @@ -90,12 +90,9 @@ import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import okhttp3.Cache import java.io.File -import java.net.URL -import java.net.URLDecoder import java.util.concurrent.Executor import java.util.concurrent.Executors - object AppContextUtils { fun RecyclerView.isRecyclerScrollable(): Boolean { val layoutManager = @@ -635,16 +632,17 @@ object AppContextUtils { } } - fun splitQuery(url: URL): Map { - val queryPairs: MutableMap = LinkedHashMap() - val query: String = url.query - val pairs = query.split("&").toTypedArray() - for (pair in pairs) { - val idx = pair.indexOf("=") - queryPairs[URLDecoder.decode(pair.substring(0, idx), "UTF-8")] = - URLDecoder.decode(pair.substring(idx + 1), "UTF-8") - } - return queryPairs + // Deprecate after next stable + /* @Deprecated( + message = "Use splitUrlParameters instead.", + replaceWith = ReplaceWith( + expression = "splitUrlParameters(url.toString())", + imports = ["com.lagradost.cloudstream3.splitUrlParameters"], + ), + level = DeprecationLevel.WARNING, + ) */ + fun splitQuery(url: java.net.URL): Map { + return com.lagradost.cloudstream3.splitUrlParameters(url.toString()) } /**| S1:E2 Hello World @@ -902,4 +900,4 @@ object AppContextUtils { } else null return currentAudioFocusRequest } -} \ No newline at end of file +} diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/MainAPI.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/MainAPI.kt index 289e96998..3e8774db2 100644 --- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/MainAPI.kt +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/MainAPI.kt @@ -761,18 +761,64 @@ fun MainAPI.fixUrl(url: String): String { } } -/** Sort the urls based on quality +/** + * Sort the urls based on quality + * * @param urls Set of [ExtractorLink] - * */ + */ fun sortUrls(urls: Set): List { return urls.sortedBy { t -> -t.quality } } -/** Capitalize the first letter of string. +/** + * Splits the parameters of a [Url] into a map of key-value pairs. + * + * Unlike a manual `split("&")` / `split("=")` implementation, this relies on Ktor's + * built-in parameters parser ([Url.parameters]), which already handles URL-decoding, + * malformed pairs, and parameters without a value. + * + * Note: if a parameter key appears multiple times (e.g. `?a=1&a=2`), only the **first** + * value is kept, since the return type is `Map`. Use [Url.parameters] + * directly if you need all values for repeated keys. + * + * @param url the [Url] whose parameters should be extracted. + * @return a map of decoded parameter names to their first decoded value. + * + * @sample + * splitUrlParameters(Url("https://example.com/path?foo=bar&baz=qux")) + * // returns {"foo": "bar", "baz": "qux"} + */ +@Prerelease +fun splitUrlParameters(url: Url): Map { + return url.parameters.entries().associate { (key, values) -> key to values.firstOrNull().orEmpty() } +} + +/** + * Splits the parameters of a raw URL [String] into a map of key-value pairs. + * + * Convenience overload for callers that have a URL as plain text rather than a parsed + * [Url] instance. Internally parses [url] with Ktor's [Url] constructor and delegates + * to [splitUrlParameters]. + * + * @param url the URL string whose parameters should be extracted. + * @return a map of decoded parameter names to their first decoded value. + * + * @sample + * splitUrlParameters("https://example.com/path?foo=bar&baz=qux") + * // returns {"foo": "bar", "baz": "qux"} + */ +@Prerelease +fun splitUrlParameters(url: String): Map { + return splitUrlParameters(Url(url)) +} + +/** + * Capitalize the first letter of string. + * * @param str String to be capitalized * @return non-nullable String * @see capitalizeStringNullable - * */ + */ fun capitalizeString(str: String): String { return capitalizeStringNullable(str) ?: str } diff --git a/library/src/commonTest/kotlin/com/lagradost/cloudstream3/SplitUrlParametersTest.kt b/library/src/commonTest/kotlin/com/lagradost/cloudstream3/SplitUrlParametersTest.kt new file mode 100644 index 000000000..7e58c70be --- /dev/null +++ b/library/src/commonTest/kotlin/com/lagradost/cloudstream3/SplitUrlParametersTest.kt @@ -0,0 +1,76 @@ +package com.lagradost.cloudstream3 + +import io.ktor.http.Url +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue + +class SplitUrlParametersTest { + + @Test + fun splitsBasicParameters() { + val url = Url("https://example.com/path?foo=bar&baz=qux") + val result = splitUrlParameters(url) + assertEquals(mapOf("foo" to "bar", "baz" to "qux"), result) + } + + @Test + fun decodesUrlEncodedKeysAndValues() { + val url = Url("https://example.com/path?na%20me=hello%20world&sp%26ec=a%2Bb") + val result = splitUrlParameters(url) + assertEquals(mapOf("na me" to "hello world", "sp&ec" to "a+b"), result) + } + + @Test + fun returnsEmptyMapWhenThereAreNoParameters() { + val url = Url("https://example.com/path") + val result = splitUrlParameters(url) + assertTrue(result.isEmpty()) + } + + @Test + fun keepsOnlyFirstValueForRepeatedKeys() { + val url = Url("https://example.com/path?a=1&a=2&a=3") + val result = splitUrlParameters(url) + assertEquals(mapOf("a" to "1"), result) + } + + @Test + fun handlesParameterWithNoValue() { + val url = Url("https://example.com/path?flag&foo=bar") + val result = splitUrlParameters(url) + assertEquals("bar", result["foo"]) + assertEquals("", result["flag"]) + } + + @Test + fun stringOverloadSplitsBasicParameters() { + val result = splitUrlParameters("https://example.com/path?foo=bar&baz=qux") + assertEquals(mapOf("foo" to "bar", "baz" to "qux"), result) + } + + @Test + fun stringOverloadDecodesUrlEncodedKeysAndValues() { + val result = splitUrlParameters("https://example.com/path?na%20me=hello%20world&sp%26ec=a%2Bb") + assertEquals(mapOf("na me" to "hello world", "sp&ec" to "a+b"), result) + } + + @Test + fun stringOverloadReturnsEmptyMapWhenThereAreNoParameters() { + val result = splitUrlParameters("https://example.com/path") + assertTrue(result.isEmpty()) + } + + @Test + fun stringOverloadKeepsOnlyFirstValueForRepeatedKeys() { + val result = splitUrlParameters("https://example.com/path?a=1&a=2&a=3") + assertEquals(mapOf("a" to "1"), result) + } + + @Test + fun stringOverloadHandlesParameterWithNoValue() { + val result = splitUrlParameters("https://example.com/path?flag&foo=bar") + assertEquals("bar", result["foo"]) + assertEquals("", result["flag"]) + } +} From 86d713c33ce29e38f9b2af5f696419af24783ce3 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Mon, 13 Jul 2026 06:57:43 -0600 Subject: [PATCH 10/38] [skip ci] Improve ContextHelper API for KMP (#2834) --- library/api/jvm/library.api | 2 +- .../com/lagradost/api/ContextHelper.android.kt | 15 +++++---------- .../kotlin/com/lagradost/api/ContextHelper.kt | 11 +++++------ .../kotlin/com/lagradost/api/ContextHelper.jvm.kt | 10 ++-------- 4 files changed, 13 insertions(+), 25 deletions(-) diff --git a/library/api/jvm/library.api b/library/api/jvm/library.api index cd2aaeea1..f680e6086 100644 --- a/library/api/jvm/library.api +++ b/library/api/jvm/library.api @@ -6,7 +6,7 @@ public final class com/lagradost/api/BuildConfig { public final class com/lagradost/api/ContextHelper_jvmKt { public static final fun getContext ()Ljava/lang/Object; - public static final fun setContext (Ljava/lang/ref/WeakReference;)V + public static final fun setContext (Ljava/lang/Object;)V } public final class com/lagradost/api/Log { diff --git a/library/src/androidMain/kotlin/com/lagradost/api/ContextHelper.android.kt b/library/src/androidMain/kotlin/com/lagradost/api/ContextHelper.android.kt index 5e5897deb..6ace780ec 100644 --- a/library/src/androidMain/kotlin/com/lagradost/api/ContextHelper.android.kt +++ b/library/src/androidMain/kotlin/com/lagradost/api/ContextHelper.android.kt @@ -3,19 +3,14 @@ package com.lagradost.api import android.content.Context import java.lang.ref.WeakReference -var ctx: WeakReference? = null +private var contextRef: WeakReference? = null /** * Helper function for Android specific context. Not usable in JVM. * Do not use this unless absolutely necessary. */ -actual fun getContext(): Any? { - return ctx?.get() -} +actual fun getContext(): Any? = contextRef?.get() -actual fun setContext(context: WeakReference) { - val actualContext = context.get() as? Context - if (actualContext != null) { - ctx = WeakReference(actualContext) - } -} \ No newline at end of file +actual fun setContext(context: Any?) { + contextRef = (context as? Context)?.let { WeakReference(it) } +} diff --git a/library/src/commonMain/kotlin/com/lagradost/api/ContextHelper.kt b/library/src/commonMain/kotlin/com/lagradost/api/ContextHelper.kt index fb54e3ca9..451ec80e8 100644 --- a/library/src/commonMain/kotlin/com/lagradost/api/ContextHelper.kt +++ b/library/src/commonMain/kotlin/com/lagradost/api/ContextHelper.kt @@ -1,16 +1,15 @@ package com.lagradost.api -import java.lang.ref.WeakReference - /** - * Set context for android specific code such as webview. - * Does nothing on JVM. + * Set context for Android-specific code such as webview. + * Does nothing on non-Android platforms. */ -expect fun setContext(context: WeakReference) +expect fun setContext(context: Any?) + /** * Helper function for Android specific context. * Do not use this unless absolutely necessary. * setContext() must be called before this is called. - * @return Context if on android, null if not. + * @return Context if on Android, null if not. */ expect fun getContext(): Any? diff --git a/library/src/jvmMain/kotlin/com/lagradost/api/ContextHelper.jvm.kt b/library/src/jvmMain/kotlin/com/lagradost/api/ContextHelper.jvm.kt index a30810b89..83cae11a5 100644 --- a/library/src/jvmMain/kotlin/com/lagradost/api/ContextHelper.jvm.kt +++ b/library/src/jvmMain/kotlin/com/lagradost/api/ContextHelper.jvm.kt @@ -1,10 +1,4 @@ package com.lagradost.api -import java.lang.ref.WeakReference - -actual fun getContext(): Any? { - return null -} - -actual fun setContext(context: WeakReference) { -} \ No newline at end of file +actual fun getContext(): Any? = null +actual fun setContext(context: Any?) = Unit From eef67fa69994e7cce5d3f8e7c4f3aedb6f5ae577 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Mon, 13 Jul 2026 07:14:51 -0600 Subject: [PATCH 11/38] Replace usage of gson in extractors (#2983) --- library/api/jvm/library.api | 78 ++++++++++++--- .../cloudstream3/extractors/Dailymotion.kt | 56 ++++++----- .../cloudstream3/extractors/GDMirrorbot.kt | 99 +++++++++++-------- .../lagradost/cloudstream3/extractors/Voe.kt | 51 ++++++---- 4 files changed, 185 insertions(+), 99 deletions(-) diff --git a/library/api/jvm/library.api b/library/api/jvm/library.api index f680e6086..d0d80346a 100644 --- a/library/api/jvm/library.api +++ b/library/api/jvm/library.api @@ -1968,12 +1968,13 @@ public class com/lagradost/cloudstream3/extractors/Dailymotion : com/lagradost/c public fun getUrl (Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; } -public final class com/lagradost/cloudstream3/extractors/Dailymotion$MetaData { +public final class com/lagradost/cloudstream3/extractors/Dailymotion$Metadata { + public static final field Companion Lcom/lagradost/cloudstream3/extractors/Dailymotion$Metadata$Companion; public fun (Ljava/util/Map;Lcom/lagradost/cloudstream3/extractors/Dailymotion$SubtitlesWrapper;)V public final fun component1 ()Ljava/util/Map; public final fun component2 ()Lcom/lagradost/cloudstream3/extractors/Dailymotion$SubtitlesWrapper; - public final fun copy (Ljava/util/Map;Lcom/lagradost/cloudstream3/extractors/Dailymotion$SubtitlesWrapper;)Lcom/lagradost/cloudstream3/extractors/Dailymotion$MetaData; - public static synthetic fun copy$default (Lcom/lagradost/cloudstream3/extractors/Dailymotion$MetaData;Ljava/util/Map;Lcom/lagradost/cloudstream3/extractors/Dailymotion$SubtitlesWrapper;ILjava/lang/Object;)Lcom/lagradost/cloudstream3/extractors/Dailymotion$MetaData; + public final fun copy (Ljava/util/Map;Lcom/lagradost/cloudstream3/extractors/Dailymotion$SubtitlesWrapper;)Lcom/lagradost/cloudstream3/extractors/Dailymotion$Metadata; + public static synthetic fun copy$default (Lcom/lagradost/cloudstream3/extractors/Dailymotion$Metadata;Ljava/util/Map;Lcom/lagradost/cloudstream3/extractors/Dailymotion$SubtitlesWrapper;ILjava/lang/Object;)Lcom/lagradost/cloudstream3/extractors/Dailymotion$Metadata; public fun equals (Ljava/lang/Object;)Z public final fun getQualities ()Ljava/util/Map; public final fun getSubtitles ()Lcom/lagradost/cloudstream3/extractors/Dailymotion$SubtitlesWrapper; @@ -1981,7 +1982,23 @@ public final class com/lagradost/cloudstream3/extractors/Dailymotion$MetaData { public fun toString ()Ljava/lang/String; } +public final synthetic class com/lagradost/cloudstream3/extractors/Dailymotion$Metadata$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/extractors/Dailymotion$Metadata$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/extractors/Dailymotion$Metadata; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/extractors/Dailymotion$Metadata;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/extractors/Dailymotion$Metadata$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public final class com/lagradost/cloudstream3/extractors/Dailymotion$Quality { + public static final field Companion Lcom/lagradost/cloudstream3/extractors/Dailymotion$Quality$Companion; public fun (Ljava/lang/String;Ljava/lang/String;)V public final fun component1 ()Ljava/lang/String; public final fun component2 ()Ljava/lang/String; @@ -1994,7 +2011,23 @@ public final class com/lagradost/cloudstream3/extractors/Dailymotion$Quality { public fun toString ()Ljava/lang/String; } +public final synthetic class com/lagradost/cloudstream3/extractors/Dailymotion$Quality$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/extractors/Dailymotion$Quality$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/extractors/Dailymotion$Quality; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/extractors/Dailymotion$Quality;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/extractors/Dailymotion$Quality$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public final class com/lagradost/cloudstream3/extractors/Dailymotion$SubtitleData { + public static final field Companion Lcom/lagradost/cloudstream3/extractors/Dailymotion$SubtitleData$Companion; public fun (Ljava/lang/String;Ljava/util/List;)V public final fun component1 ()Ljava/lang/String; public final fun component2 ()Ljava/util/List; @@ -2007,7 +2040,23 @@ public final class com/lagradost/cloudstream3/extractors/Dailymotion$SubtitleDat public fun toString ()Ljava/lang/String; } +public final synthetic class com/lagradost/cloudstream3/extractors/Dailymotion$SubtitleData$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/extractors/Dailymotion$SubtitleData$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/extractors/Dailymotion$SubtitleData; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/extractors/Dailymotion$SubtitleData;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/extractors/Dailymotion$SubtitleData$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public final class com/lagradost/cloudstream3/extractors/Dailymotion$SubtitlesWrapper { + public static final field Companion Lcom/lagradost/cloudstream3/extractors/Dailymotion$SubtitlesWrapper$Companion; public fun (ZLjava/util/Map;)V public final fun component1 ()Z public final fun component2 ()Ljava/util/Map; @@ -2020,6 +2069,21 @@ public final class com/lagradost/cloudstream3/extractors/Dailymotion$SubtitlesWr public fun toString ()Ljava/lang/String; } +public final synthetic class com/lagradost/cloudstream3/extractors/Dailymotion$SubtitlesWrapper$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/extractors/Dailymotion$SubtitlesWrapper$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/extractors/Dailymotion$SubtitlesWrapper; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/extractors/Dailymotion$SubtitlesWrapper;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/extractors/Dailymotion$SubtitlesWrapper$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public final class com/lagradost/cloudstream3/extractors/DatabaseGdrive : com/lagradost/cloudstream3/extractors/Gdriveplayer { public fun ()V public fun getMainUrl ()Ljava/lang/String; @@ -2435,8 +2499,6 @@ public class com/lagradost/cloudstream3/extractors/GDMirrorbot : com/lagradost/c public fun getName ()Ljava/lang/String; public fun getRequiresReferer ()Z public fun getUrl (Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public fun setMainUrl (Ljava/lang/String;)V - public fun setName (Ljava/lang/String;)V } public class com/lagradost/cloudstream3/extractors/GUpload : com/lagradost/cloudstream3/utils/ExtractorApi { @@ -3701,7 +3763,6 @@ public final class com/lagradost/cloudstream3/extractors/Simpulumlamerop : com/l public fun ()V public fun getMainUrl ()Ljava/lang/String; public fun getName ()Ljava/lang/String; - public fun setMainUrl (Ljava/lang/String;)V } public class com/lagradost/cloudstream3/extractors/Slmaxed : com/lagradost/cloudstream3/utils/ExtractorApi { @@ -4203,9 +4264,6 @@ public final class com/lagradost/cloudstream3/extractors/Techinmind : com/lagrad public fun getMainUrl ()Ljava/lang/String; public fun getName ()Ljava/lang/String; public fun getRequiresReferer ()Z - public fun setMainUrl (Ljava/lang/String;)V - public fun setName (Ljava/lang/String;)V - public fun setRequiresReferer (Z)V } public final class com/lagradost/cloudstream3/extractors/Tubeless : com/lagradost/cloudstream3/extractors/Voe { @@ -4296,7 +4354,6 @@ public final class com/lagradost/cloudstream3/extractors/Urochsunloath : com/lag public fun ()V public fun getMainUrl ()Ljava/lang/String; public fun getName ()Ljava/lang/String; - public fun setMainUrl (Ljava/lang/String;)V } public class com/lagradost/cloudstream3/extractors/Userload : com/lagradost/cloudstream3/utils/ExtractorApi { @@ -4743,7 +4800,6 @@ public final class com/lagradost/cloudstream3/extractors/Yipsu : com/lagradost/c public fun ()V public fun getMainUrl ()Ljava/lang/String; public fun getName ()Ljava/lang/String; - public fun setMainUrl (Ljava/lang/String;)V } public class com/lagradost/cloudstream3/extractors/YourUpload : com/lagradost/cloudstream3/utils/ExtractorApi { diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Dailymotion.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Dailymotion.kt index 2b2a820fe..88b407f3b 100644 --- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Dailymotion.kt +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Dailymotion.kt @@ -1,15 +1,18 @@ package com.lagradost.cloudstream3.extractors -import com.google.gson.Gson +import com.fasterxml.jackson.annotation.JsonProperty import com.lagradost.cloudstream3.SubtitleFile import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.newSubtitleFile +import com.lagradost.cloudstream3.utils.AppUtils.parseJson import com.lagradost.cloudstream3.utils.ExtractorApi import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.newExtractorLink import com.lagradost.cloudstream3.utils.ExtractorLinkType import io.ktor.http.Url import io.ktor.http.decodeURLPart +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable class Geodailymotion : Dailymotion() { override val name = "GeoDailymotion" @@ -28,25 +31,25 @@ open class Dailymotion : ExtractorApi() { url: String, referer: String?, subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit + callback: (ExtractorLink) -> Unit, ) { val embedUrl = getEmbedUrl(url) ?: return val id = getVideoId(embedUrl) ?: return - val metaDataUrl = "$baseUrl/player/metadata/video/$id" - - val response = app.get(metaDataUrl, referer = embedUrl).text - val gson = Gson() - val meta = gson.fromJson(response, MetaData::class.java) + val metadataUrl = "$baseUrl/player/metadata/video/$id" + val response = app.get(metadataUrl, referer = embedUrl).text + val meta = parseJson(response) meta.qualities?.get("auto")?.forEach { quality -> val videoUrl = quality.url if (!videoUrl.isNullOrEmpty() && videoUrl.contains(".m3u8")) { - callback.invoke(newExtractorLink( - name, - name, - videoUrl, - ExtractorLinkType.M3U8 - )) + callback.invoke( + newExtractorLink( + name, + name, + videoUrl, + ExtractorLinkType.M3U8, + ) + ) } } @@ -55,7 +58,7 @@ open class Dailymotion : ExtractorApi() { subtitleCallback( newSubtitleFile( subData.label, - subUrl + subUrl, ) ) } @@ -68,6 +71,7 @@ open class Dailymotion : ExtractorApi() { val videoId = url.substringAfter("video=") return "$baseUrl/embed/video/$videoId" } + return null } @@ -77,23 +81,27 @@ open class Dailymotion : ExtractorApi() { return if (id.matches(videoIdRegex)) id else null } - data class MetaData( - val qualities: Map>?, - val subtitles: SubtitlesWrapper? + @Serializable + data class Metadata( + @JsonProperty("qualities") @SerialName("qualities") val qualities: Map>?, + @JsonProperty("subtitles") @SerialName("subtitles") val subtitles: SubtitlesWrapper?, ) + @Serializable data class Quality( - val type: String?, - val url: String? + @JsonProperty("type") @SerialName("type") val type: String?, + @JsonProperty("url") @SerialName("url") val url: String?, ) + @Serializable data class SubtitlesWrapper( - val enable: Boolean, - val data: Map? + @JsonProperty("enable") @SerialName("enable") val enable: Boolean, + @JsonProperty("data") @SerialName("data") val data: Map?, ) + @Serializable data class SubtitleData( - val label: String, - val urls: List + @JsonProperty("label") @SerialName("label") val label: String, + @JsonProperty("urls") @SerialName("urls") val urls: List, ) -} \ No newline at end of file +} diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/GDMirrorbot.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/GDMirrorbot.kt index ba297067e..b6563fd63 100644 --- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/GDMirrorbot.kt +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/GDMirrorbot.kt @@ -1,31 +1,34 @@ package com.lagradost.cloudstream3.extractors -import com.google.gson.JsonParser +import com.fasterxml.jackson.annotation.JsonProperty import com.lagradost.api.Log import com.lagradost.cloudstream3.SubtitleFile import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.base64Decode +import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson import com.lagradost.cloudstream3.utils.ExtractorApi import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.loadExtractor import io.ktor.http.Url +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable -class Techinmind: GDMirrorbot() { - override var name = "Techinmind Cloud AIO" - override var mainUrl = "https://stream.techinmind.space" - override var requiresReferer = true +class Techinmind : GDMirrorbot() { + override val name = "Techinmind Cloud AIO" + override val mainUrl = "https://stream.techinmind.space" + override val requiresReferer = true } open class GDMirrorbot : ExtractorApi() { - override var name = "GDMirrorbot" - override var mainUrl = "https://gdmirrorbot.nl" + override val name = "GDMirrorbot" + override val mainUrl = "https://gdmirrorbot.nl" override val requiresReferer = true override suspend fun getUrl( url: String, referer: String?, subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit + callback: (ExtractorLink) -> Unit, ) { val (sid, host) = if (!url.contains("key=")) { Pair(url.substringAfterLast("embed/"), getBaseUrl(app.get(url).url)) @@ -36,63 +39,58 @@ open class GDMirrorbot : ExtractorApi() { val idType = Regex("""idType\s*=\s*"([^"]+)"""").find(pageText)?.groupValues?.get(1) ?: "imdbid" val baseUrl = Regex("""let\s+baseUrl\s*=\s*"([^"]+)"""").find(pageText)?.groupValues?.get(1) val hostUrl = baseUrl?.let { getBaseUrl(it) } - if (finalId != null && myKey != null) { val apiUrl = if (url.contains("/tv/")) { val season = Regex("""/tv/\d+/(\d+)/""").find(url)?.groupValues?.get(1) ?: "1" val episode = Regex("""/tv/\d+/\d+/(\d+)""").find(url)?.groupValues?.get(1) ?: "1" "$mainUrl/myseriesapi?tmdbid=$finalId&season=$season&epname=$episode&key=$myKey" - } else { - "$mainUrl/mymovieapi?$idType=$finalId&key=$myKey" - } + } else "$mainUrl/mymovieapi?$idType=$finalId&key=$myKey" pageText = app.get(apiUrl).text } - val jsonElement = JsonParser.parseString(pageText) - if (!jsonElement.isJsonObject) return - val jsonObject = jsonElement.asJsonObject - + val embedData = tryParseJson(pageText) val embedId = url.substringAfterLast("/") - val sidValue = jsonObject["data"]?.asJsonArray - ?.takeIf { it.size() > 0 } - ?.get(0)?.asJsonObject - ?.get("fileslug")?.asString + val sidValue = embedData?.data?.firstOrNull()?.fileSlug ?.takeIf { it.isNotBlank() } ?: embedId - Pair(sidValue, hostUrl) } val postData = mapOf("sid" to sid) val responseText = app.post("$host/embedhelper.php", data = postData).text - val rootElement = JsonParser.parseString(responseText) - if (!rootElement.isJsonObject) return - val root = rootElement.asJsonObject + val root = tryParseJson(responseText) ?: return + val siteUrls = root.siteUrls ?: return + val siteFriendlyNames = root.siteFriendlyNames - val siteUrls = root["siteUrls"]?.asJsonObject ?: return - val siteFriendlyNames = root["siteFriendlyNames"]?.asJsonObject - - val decodedMresult = when { - root["mresult"]?.isJsonObject == true -> root["mresult"]!!.asJsonObject - root["mresult"]?.isJsonPrimitive == true -> try { - base64Decode(root["mresult"]!!.asString) - .let { JsonParser.parseString(it).asJsonObject } - } catch (e: Exception) { - Log.e("GDMirrorbot", "Failed to decode mresult: $e") - return + // mresult can arrive as a JSON object or a base64-encoded string + val mresult: Map? = run { + val raw = responseText + .substringAfter("\"mresult\":") + .trimStart() + when { + raw.startsWith("\"") -> { + // base64-encoded string + tryParseJson>( + try { base64Decode(raw.trim('"')) } catch (_: Exception) { return } + ) + } + raw.startsWith("{") -> tryParseJson>( + raw.substringBefore("\n}").substringBefore(",\n\"").let { "$it" } + .let { responseText.substringAfter("\"mresult\":").trimStart() } + ) + else -> null } - else -> return } - siteUrls.keySet().intersect(decodedMresult.keySet()).forEach { key -> - val base = siteUrls[key]?.asString?.trimEnd('/') ?: return@forEach - val path = decodedMresult[key]?.asString?.trimStart('/') ?: return@forEach + if (mresult == null) return + siteUrls.keys.intersect(mresult.keys).forEach { key -> + val base = siteUrls[key]?.trimEnd('/') ?: return@forEach + val path = mresult[key]?.trimStart('/') ?: return@forEach val fullUrl = "$base/$path" - val friendlyName = siteFriendlyNames?.get(key)?.asString ?: key - + val friendlyName = siteFriendlyNames?.get(key) ?: key try { when (friendlyName) { - "StreamHG","EarnVids" -> VidHidePro().getUrl(fullUrl, referer, subtitleCallback, callback) + "StreamHG", "EarnVids" -> VidHidePro().getUrl(fullUrl, referer, subtitleCallback, callback) "RpmShare", "UpnShare", "StreamP2p" -> VidStack().getUrl(fullUrl, referer, subtitleCallback, callback) else -> loadExtractor(fullUrl, referer ?: mainUrl, subtitleCallback, callback) } @@ -105,5 +103,20 @@ open class GDMirrorbot : ExtractorApi() { private fun getBaseUrl(url: String): String { return Url(url).let { "${it.protocol.name}://${it.host}" } } -} + @Serializable + private data class EmbedData( + @JsonProperty("data") @SerialName("data") val data: List? = null, + ) + + @Serializable + private data class FileSlug( + @JsonProperty("fileslug") @SerialName("fileslug") val fileSlug: String? = null, + ) + + @Serializable + private data class EmbedHelper( + @JsonProperty("siteUrls") @SerialName("siteUrls") val siteUrls: Map? = null, + @JsonProperty("siteFriendlyNames") @SerialName("siteFriendlyNames") val siteFriendlyNames: Map? = null, + ) +} diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Voe.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Voe.kt index 67eb49c9a..b3ea4df1b 100644 --- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Voe.kt +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Voe.kt @@ -1,16 +1,18 @@ package com.lagradost.cloudstream3.extractors -import com.google.gson.JsonObject -import com.google.gson.JsonParser +import com.fasterxml.jackson.annotation.JsonProperty import com.lagradost.cloudstream3.SubtitleFile import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.base64Decode +import com.lagradost.cloudstream3.utils.AppUtils.parseJson import com.lagradost.cloudstream3.utils.ExtractorApi import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.INFER_TYPE import com.lagradost.cloudstream3.utils.M3u8Helper import com.lagradost.cloudstream3.utils.Qualities import com.lagradost.cloudstream3.utils.newExtractorLink +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable class Tubeless : Voe() { override val name = "Tubeless" @@ -19,12 +21,12 @@ class Tubeless : Voe() { class Simpulumlamerop : Voe() { override val name = "Simplum" - override var mainUrl = "https://simpulumlamerop.com" + override val mainUrl = "https://simpulumlamerop.com" } class Urochsunloath : Voe() { override val name = "Uroch" - override var mainUrl = "https://urochsunloath.com" + override val mainUrl = "https://urochsunloath.com" } class NathanFromSubject : Voe() { @@ -33,7 +35,7 @@ class NathanFromSubject : Voe() { class Yipsu : Voe() { override val name = "Yipsu" - override var mainUrl = "https://yip.su" + override val mainUrl = "https://yip.su" } class MetaGnathTuggers : Voe() { @@ -59,38 +61,43 @@ open class Voe : ExtractorApi() { url: String, referer: String?, subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit + callback: (ExtractorLink) -> Unit, ) { var res = app.get(url, referer = referer) val redirectUrl = redirectRegex.find(res.document.data())?.groupValues?.get(1) if (redirectUrl != null) { res = app.get(redirectUrl, referer = referer) } - val encodedString = res.document.selectFirst("script[type=application/json]")?.data()?.trim()?.substringAfter("[\"")?.substringBeforeLast("\"]") + + val encodedString = res.document.selectFirst("script[type=application/json]") + ?.data()?.trim() + ?.substringAfter("[\"") + ?.substringBeforeLast("\"]") + if (encodedString == null) { println("encoded string not found.") return } - val decryptedJson = decryptF7(encodedString) - val m3u8 = decryptedJson.get("source")?.asString - val mp4 = decryptedJson.get("direct_access_url")?.asString + val decryptedJson = decryptF7(encodedString) + val m3u8 = decryptedJson?.source + val mp4 = decryptedJson?.directAccessUrl if (m3u8 != null) { M3u8Helper.generateM3u8( name, m3u8, "$mainUrl/", - headers = mapOf("Origin" to "$mainUrl/") + headers = mapOf("Origin" to "$mainUrl/"), ).forEach(callback) } - if (mp4!=null) - { + + if (mp4 != null) { callback.invoke( newExtractorLink( source = "$name MP4", name = "$name MP4", url = mp4, - INFER_TYPE + INFER_TYPE, ) { this.referer = url this.quality = Qualities.Unknown.value @@ -99,20 +106,19 @@ open class Voe : ExtractorApi() { } } - private fun decryptF7(p8: String): JsonObject { + private fun decryptF7(p8: String): VoeDecrypted? { return try { val vF = rot13(p8) val vF2 = replacePatterns(vF) val vF3 = removeUnderscores(vF2) val vF4 = base64Decode(vF3) val vF5 = charShift(vF4, 3) - val vF6 = reverse(vF5) + val vF6 = vF5.reversed() val vAtob = base64Decode(vF6) - - JsonParser.parseString(vAtob).asJsonObject + parseJson(vAtob) } catch (e: Exception) { println("Decryption error: ${e.message}") - JsonObject() + null } } @@ -139,6 +145,9 @@ open class Voe : ExtractorApi() { return input.map { (it.code - shift).toChar() }.joinToString("") } - private fun reverse(input: String): String = input.reversed() - + @Serializable + private data class VoeDecrypted( + @JsonProperty("source") @SerialName("source") val source: String? = null, + @JsonProperty("direct_access_url") @SerialName("direct_access_url") val directAccessUrl: String? = null, + ) } From b1ce67dbf385d510549063a13bd4e35a6e07bb20 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Mon, 13 Jul 2026 07:20:02 -0600 Subject: [PATCH 12/38] ControllerActivity: migrate to kotlinx serialization (#2958) --- .../cloudstream3/ui/ControllerActivity.kt | 149 ++++++------------ 1 file changed, 47 insertions(+), 102 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/ControllerActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/ControllerActivity.kt index 2aadfb13c..f00fd8031 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/ControllerActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/ControllerActivity.kt @@ -12,6 +12,7 @@ import android.widget.ImageView import android.widget.LinearLayout import android.widget.ListView import androidx.appcompat.app.AlertDialog +import com.fasterxml.jackson.annotation.JsonProperty import com.google.android.gms.cast.MediaLoadOptions import com.google.android.gms.cast.MediaQueueItem import com.google.android.gms.cast.MediaSeekOptions @@ -34,35 +35,24 @@ import com.lagradost.cloudstream3.ui.player.SubtitleData import com.lagradost.cloudstream3.ui.result.ResultEpisode import com.lagradost.cloudstream3.ui.subtitles.ChromecastSubtitlesFragment import com.lagradost.cloudstream3.utils.AppContextUtils.sortSubs +import com.lagradost.cloudstream3.utils.AppUtils.parseJson import com.lagradost.cloudstream3.utils.AppUtils.toJson import com.lagradost.cloudstream3.utils.CastHelper.awaitLinks import com.lagradost.cloudstream3.utils.CastHelper.getMediaInfo import com.lagradost.cloudstream3.utils.Coroutines.ioSafe -import com.lagradost.cloudstream3.utils.DataStore.toKotlinObject import com.lagradost.cloudstream3.utils.DataStoreHelper import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.Qualities import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable import org.json.JSONObject -/*class SkipOpController(val view: ImageView) : UIController() { - init { - view.setImageResource(R.drawable.exo_controls_fastforward) - view.setOnClickListener { - remoteMediaClient?.let { - val options = MediaSeekOptions.Builder() - .setPosition(it.approximateStreamPosition + 85000) - it.seek(options.build()) - } - } - } -}*/ - private fun RemoteMediaClient.getItemIndex(): Int? { return try { val index = this.mediaQueue.itemIds.indexOf(this.currentItem?.itemId ?: 0) if (index < 0) null else index - } catch (e: Exception) { + } catch (_: Exception) { null } } @@ -89,48 +79,41 @@ class SkipNextEpisodeController(val view: ImageView) : UIController() { } } +@Serializable data class MetadataHolder( - val apiName: String, - val isMovie: Boolean, - val title: String?, - val poster: String?, - val currentEpisodeIndex: Int, - val episodes: List, - val currentLinks: List, - val currentSubtitles: List + @JsonProperty("apiName") @SerialName("apiName") val apiName: String, + @JsonProperty("isMovie") @SerialName("isMovie") val isMovie: Boolean, + @JsonProperty("title") @SerialName("title") val title: String?, + @JsonProperty("poster") @SerialName("poster") val poster: String?, + @JsonProperty("currentEpisodeIndex") @SerialName("currentEpisodeIndex") val currentEpisodeIndex: Int, + @JsonProperty("episodes") @SerialName("episodes") val episodes: List, + @JsonProperty("currentLinks") @SerialName("currentLinks") val currentLinks: List, + @JsonProperty("currentSubtitles") @SerialName("currentSubtitles") val currentSubtitles: List, ) -class SelectSourceController(val view: ImageView, val activity: ControllerActivity) : - UIController() { +class SelectSourceController(val view: ImageView, val activity: ControllerActivity) : UIController() { init { view.setImageResource(R.drawable.ic_baseline_playlist_play_24) view.setOnClickListener { - // lateinit var dialog: AlertDialog val holder = getCurrentMetaData() - if (holder != null) { val items = holder.currentLinks if (items.isNotEmpty() && remoteMediaClient?.currentItem != null) { - val subTracks = - remoteMediaClient?.mediaInfo?.mediaTracks?.filter { it.type == MediaTrack.TYPE_TEXT } - ?: ArrayList() + val subTracks = remoteMediaClient?.mediaInfo?.mediaTracks?.filter { it.type == MediaTrack.TYPE_TEXT } + ?: ArrayList() - val bottomSheetDialogBuilder = - AlertDialog.Builder(view.context, R.style.AlertDialogCustomBlack) + val bottomSheetDialogBuilder = AlertDialog.Builder(view.context, R.style.AlertDialogCustomBlack) bottomSheetDialogBuilder.setView(R.layout.sort_bottom_sheet) + val bottomSheetDialog = bottomSheetDialogBuilder.create() bottomSheetDialog.show() - // bottomSheetDialog.setContentView(R.layout.sort_bottom_sheet) - val providerList = - bottomSheetDialog.findViewById(R.id.sort_providers)!! - val subtitleList = - bottomSheetDialog.findViewById(R.id.sort_subtitles)!! + + val providerList = bottomSheetDialog.findViewById(R.id.sort_providers)!! + val subtitleList = bottomSheetDialog.findViewById(R.id.sort_subtitles)!! if (subTracks.isEmpty()) { - bottomSheetDialog.findViewById(R.id.sort_subtitles_holder)?.visibility = - GONE + bottomSheetDialog.findViewById(R.id.sort_subtitles_holder)?.visibility = GONE } else { - val arrayAdapter = - ArrayAdapter(view.context, R.layout.sort_bottom_single_choice) + val arrayAdapter = ArrayAdapter(view.context, R.layout.sort_bottom_single_choice) arrayAdapter.add(view.context.getString(R.string.no_subtitles)) arrayAdapter.addAll(subTracks.mapNotNull { it.name }) @@ -138,10 +121,8 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi subtitleList.adapter = arrayAdapter val currentTracks = remoteMediaClient?.mediaStatus?.activeTrackIds - - val subtitleIndex = - if (currentTracks == null) 0 else subTracks.map { it.id } - .indexOfFirst { currentTracks.contains(it) } + 1 + val subtitleIndex = if (currentTracks == null) 0 else subTracks.map { it.id } + .indexOfFirst { currentTracks.contains(it) } + 1 subtitleList.setSelection(subtitleIndex) subtitleList.setItemChecked(subtitleIndex, true) @@ -153,9 +134,7 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi ChromecastSubtitlesFragment.getCurrentSavedStyle().apply { val font = TextTrackStyle() font.setFontFamily(fontFamily ?: "Google Sans") - fontGenericFamily?.let { - font.fontGenericFamily = it - } + fontGenericFamily?.let { font.fontGenericFamily = it } font.windowColor = windowColor font.backgroundColor = backgroundColor @@ -172,7 +151,7 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi if (!it.status.isSuccess) { Log.e( "CHROMECAST", "Failed with status code:" + - it.status.statusCode + " > " + it.status.statusMessage + it.status.statusCode + " > " + it.status.statusMessage ) } } @@ -181,17 +160,15 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi } } - //https://developers.google.com/cast/docs/reference/web_receiver/cast.framework.messages.MediaInformation + // https://developers.google.com/cast/docs/reference/web_receiver/cast.framework.messages.MediaInformation val contentUrl = (remoteMediaClient?.currentItem?.media?.contentUrl ?: remoteMediaClient?.currentItem?.media?.contentId) - val sortingMethods = - items.map { "${it.name} ${Qualities.getStringByInt(it.quality)}" } - .toTypedArray() + val sortingMethods = items.map { "${it.name} ${Qualities.getStringByInt(it.quality)}" } + .toTypedArray() val sotringIndex = items.indexOfFirst { it.url == contentUrl } - val arrayAdapter = - ArrayAdapter(view.context, R.layout.sort_bottom_single_choice) + val arrayAdapter = ArrayAdapter(view.context, R.layout.sort_bottom_single_choice) arrayAdapter.addAll(sortingMethods.toMutableList()) providerList.choiceMode = AbsListView.CHOICE_MODE_SINGLE @@ -201,10 +178,8 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi providerList.setOnItemClickListener { _, _, which, _ -> val epData = holder.episodes[holder.currentEpisodeIndex] - fun loadMirror(index: Int) { if (holder.currentLinks.size <= index) return - val mediaItem = getMediaInfo( epData, holder, @@ -214,25 +189,21 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi ) val startAt = remoteMediaClient?.approximateStreamPosition ?: 0 - - //remoteMediaClient.load(mediaItem, true, startAt) try { // THIS IS VERY IMPORTANT BECAUSE WE NEVER WANT TO AUTOLOAD THE NEXT EPISODE val currentIdIndex = remoteMediaClient?.getItemIndex() - val nextId = remoteMediaClient?.mediaQueue?.itemIds?.get( currentIdIndex?.plus(1) ?: 0 ) + if (currentIdIndex == null && nextId != null) { awaitLinks( remoteMediaClient?.queueInsertAndPlayItem( MediaQueueItem.Builder(mediaItem).build(), nextId, startAt, - JSONObject() + JSONObject(), ) - ) { - loadMirror(index + 1) - } + ) { loadMirror(index + 1) } } else { val mediaLoadOptions = MediaLoadOptions.Builder() @@ -244,11 +215,9 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi mediaItem, mediaLoadOptions ) - ) { - loadMirror(index + 1) - } + ) { loadMirror(index + 1) } } - } catch (e: Exception) { + } catch (_: Exception) { val mediaLoadOptions = MediaLoadOptions.Builder() .setPlayPosition(startAt) @@ -259,8 +228,8 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi } } } - loadMirror(which) + loadMirror(which) bottomSheetDialog.dismissSafe(activity) } } @@ -270,23 +239,19 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi private fun getCurrentMetaData(): MetadataHolder? { return try { - val data = remoteMediaClient?.mediaInfo?.customData?.toString() - data?.toKotlinObject() - } catch (e: Exception) { + val data = remoteMediaClient?.mediaInfo?.customData?.toString() ?: return null + parseJson(data) + } catch (_: Exception) { null } } var isLoadingMore = false - - override fun onMediaStatusUpdated() { super.onMediaStatusUpdated() val meta = getCurrentMetaData() + view.visibility = if ((meta?.currentLinks?.size ?: 0) > 1) VISIBLE else INVISIBLE - view.visibility = if ((meta?.currentLinks?.size - ?: 0) > 1 - ) VISIBLE else INVISIBLE try { if (meta != null && meta.episodes.size > meta.currentEpisodeIndex + 1) { val currentIdIndex = remoteMediaClient?.getItemIndex() ?: return @@ -303,7 +268,7 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi currentPosition, currentDuration, epData, - meta.episodes.getOrNull(index + 1) + meta.episodes.getOrNull(index + 1), ) } catch (t: Throwable) { logError(t) @@ -314,9 +279,7 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi ioSafe { val currentLinks = mutableSetOf() val currentSubs = mutableSetOf() - val generator = RepoLinkGenerator(listOf(epData)) - val isSuccessful = safeApiCall { generator.generateLinks( clearCache = false, @@ -329,7 +292,7 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi currentSubs.add(it) }, offset = 0, - isCasting = true + isCasting = true, ) } @@ -340,32 +303,18 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi val jsonCopy = meta.copy( currentLinks = sortedLinks, currentSubtitles = sortedSubs, - currentEpisodeIndex = index + currentEpisodeIndex = index, ) - val done = - JSONObject(jsonCopy.toJson()) - + val done = JSONObject(jsonCopy.toJson()) val mediaInfo = getMediaInfo( epData, jsonCopy, 0, done, - sortedSubs + sortedSubs, ) - /*fun loadIndex(index: Int) { - println("LOAD INDEX::::: $index") - if (meta.currentLinks.size <= index) return - val info = getMediaInfo( - epData, - meta, - index, - done) - awaitLinks(remoteMediaClient?.load(info, true, 0)) { - loadIndex(index + 1) - } - }*/ activity.runOnUiThread { awaitLinks( remoteMediaClient?.queueAppendItem( @@ -374,7 +323,6 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi ) ) { println("FAILED TO LOAD NEXT ITEM") - // loadIndex(1) } isLoadingMore = false } @@ -397,10 +345,7 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi class SkipTimeController(val view: ImageView, forwards: Boolean) : UIController() { init { - //val settingsManager = PreferenceManager.getDefaultSharedPreferences() - //val time = settingsManager?.getInt("chromecast_tap_time", 30) ?: 30 val time = 30 - //view.setImageResource(if (forwards) R.drawable.netflix_skip_forward else R.drawable.netflix_skip_back) view.setImageResource(if (forwards) R.drawable.go_forward_30 else R.drawable.go_back_30) view.setOnClickListener { remoteMediaClient?.let { From b245a2d9d75049f41796d919fe9bf1c0179d140a Mon Sep 17 00:00:00 2001 From: Phisher98 <153359846+phisher98@users.noreply.github.com> Date: Mon, 13 Jul 2026 19:58:26 +0530 Subject: [PATCH 13/38] Fix VidHidePro M3U8 type detection in JWPlayerHelper (#3064) --- .../extractors/helper/JWPlayerHelper.kt | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/helper/JWPlayerHelper.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/helper/JWPlayerHelper.kt index b026fa237..2b388d4e0 100644 --- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/helper/JWPlayerHelper.kt +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/helper/JWPlayerHelper.kt @@ -9,6 +9,8 @@ import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.M3u8Helper import com.lagradost.cloudstream3.utils.newExtractorLink +import com.lagradost.cloudstream3.utils.ExtractorLinkType +import com.lagradost.cloudstream3.utils.INFER_TYPE import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import kotlin.collections.orEmpty @@ -52,11 +54,12 @@ object JwPlayerHelper { }.toList() var extractedLinks = sourceMatches.flatMap { link -> - if (link.file.contains(".m3u8")) { + val cleanUrl = link.file.replace("\\/", "/") + if (cleanUrl.contains(".m3u8") || cleanUrl.contains(".txt")) { try { M3u8Helper.generateM3u8( source = sourceName, - streamUrl = link.file, + streamUrl = cleanUrl, referer = mainUrl, headers = headers, ) @@ -69,7 +72,7 @@ object JwPlayerHelper { newExtractorLink( source = sourceName, name = sourceName, - url = fixUrl(link.file, mainUrl), + url = fixUrl(cleanUrl, mainUrl), ) { this.referer = url this.headers = headers @@ -98,11 +101,13 @@ object JwPlayerHelper { */ if (extractedLinks.isEmpty()) { extractedLinks = m3u8Regex.findAll(script).toList().map { match -> - val link = match.groupValues[1] + val cleanUrl = match.groupValues[1].replace("\\/", "/") + val isM3u8 = cleanUrl.contains(".m3u8") || cleanUrl.contains(".txt") newExtractorLink( source = sourceName, name = sourceName, - url = fixUrl(link, mainUrl), + url = fixUrl(cleanUrl, mainUrl), + type = if (isM3u8) ExtractorLinkType.M3U8 else INFER_TYPE, ) { this.referer = url this.headers = headers From ecf5650bbdb89e5c9331e5a574792f94ff2765c3 Mon Sep 17 00:00:00 2001 From: firelight <147925818+fire-light42@users.noreply.github.com> Date: Mon, 13 Jul 2026 14:46:09 +0000 Subject: [PATCH 14/38] fix build --- app/src/main/res/values-b+lt/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values-b+lt/strings.xml b/app/src/main/res/values-b+lt/strings.xml index bccc4cf59..ff92ac093 100644 --- a/app/src/main/res/values-b+lt/strings.xml +++ b/app/src/main/res/values-b+lt/strings.xml @@ -249,7 +249,7 @@ Pašalinti iš žiūrimų Garso takelis Užpildymas - min + %d min Namai Atsisiuntimų eilė Balso atpažinimas nepasiekiamas From 076e7a51168b0d615ba0d7fde955517b917cedec Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Mon, 13 Jul 2026 12:00:32 -0600 Subject: [PATCH 15/38] [skip ci] test: add an IgnoreOnWeb annotation (#3034) --- .../kotlin/com/lagradost/cloudstream3/utils/Ignore.kt | 6 ++++++ .../com/lagradost/cloudstream3/utils/JsInterpreterTest.kt | 3 +++ 2 files changed, 9 insertions(+) create mode 100644 library/src/commonTest/kotlin/com/lagradost/cloudstream3/utils/Ignore.kt diff --git a/library/src/commonTest/kotlin/com/lagradost/cloudstream3/utils/Ignore.kt b/library/src/commonTest/kotlin/com/lagradost/cloudstream3/utils/Ignore.kt new file mode 100644 index 000000000..f815258a3 --- /dev/null +++ b/library/src/commonTest/kotlin/com/lagradost/cloudstream3/utils/Ignore.kt @@ -0,0 +1,6 @@ +package com.lagradost.cloudstream3.utils + +@OptIn(ExperimentalMultiplatform::class) // OptionalExpectation is an experimental annotation for now +@OptionalExpectation +@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION) +expect annotation class IgnoreOnWeb() diff --git a/library/src/commonTest/kotlin/com/lagradost/cloudstream3/utils/JsInterpreterTest.kt b/library/src/commonTest/kotlin/com/lagradost/cloudstream3/utils/JsInterpreterTest.kt index ded236f4e..588b8d268 100644 --- a/library/src/commonTest/kotlin/com/lagradost/cloudstream3/utils/JsInterpreterTest.kt +++ b/library/src/commonTest/kotlin/com/lagradost/cloudstream3/utils/JsInterpreterTest.kt @@ -2133,6 +2133,7 @@ class JsInterpreterTest { } @Test + @IgnoreOnWeb fun suspendEvalJsWithTimeoutCancelsInfiniteLoop() = runTest { /** * activeScope() provides a real CoroutineScope with a real Job, so withTimeout @@ -2326,6 +2327,7 @@ class JsInterpreterTest { } @Test + @IgnoreOnWeb fun jsContextEvalCancelledWhenEnclosingCoroutineCancelledViaWithTimeout() = runTest { val done = Channel() var elapsed = Duration.ZERO @@ -2360,6 +2362,7 @@ class JsInterpreterTest { } @Test + @IgnoreOnWeb fun jsContextJsTryCatchCannotSwallowEnclosingCancellation() = runTest { val done = Channel() var elapsed = Duration.ZERO From d778befe19751e0de3f815d1afb78d158c08eee5 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Mon, 13 Jul 2026 12:26:21 -0600 Subject: [PATCH 16/38] [skip ci] Add KMP locale API for SubtitleHelper (#2833) --- library/api/jvm/library.api | 3 +- .../utils/SubtitleHelperPlatform.android.kt | 24 ++++++++++++++ .../cloudstream3/utils/SubtitleHelper.kt | 27 ++-------------- .../utils/SubtitleHelperPlatform.kt | 13 ++++++++ .../utils/SubtitleHelperPlatform.jvm.kt | 21 +++++++++++++ .../utils/SubtitleHelperPlatform.web.kt | 31 +++++++++++++++++++ 6 files changed, 94 insertions(+), 25 deletions(-) create mode 100644 library/src/androidMain/kotlin/com/lagradost/cloudstream3/utils/SubtitleHelperPlatform.android.kt create mode 100644 library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/SubtitleHelperPlatform.kt create mode 100644 library/src/jvmMain/kotlin/com/lagradost/cloudstream3/utils/SubtitleHelperPlatform.jvm.kt create mode 100644 library/src/webMain/kotlin/com/lagradost/cloudstream3/utils/SubtitleHelperPlatform.web.kt diff --git a/library/api/jvm/library.api b/library/api/jvm/library.api index d0d80346a..49638f132 100644 --- a/library/api/jvm/library.api +++ b/library/api/jvm/library.api @@ -7524,8 +7524,9 @@ public final class com/lagradost/cloudstream3/utils/SubtitleHelper$LanguageMetad public fun toString ()Ljava/lang/String; } -public final class com/lagradost/cloudstream3/utils/SubtitleHelperKt { +public final class com/lagradost/cloudstream3/utils/SubtitleHelperPlatform_jvmKt { public static final fun getCurrentLocale ()Ljava/lang/String; + public static final fun localizedLanguageName (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; } public abstract class com/lagradost/cloudstream3/utils/serializers/NonEmptySerializer : kotlinx/serialization/json/JsonTransformingSerializer { diff --git a/library/src/androidMain/kotlin/com/lagradost/cloudstream3/utils/SubtitleHelperPlatform.android.kt b/library/src/androidMain/kotlin/com/lagradost/cloudstream3/utils/SubtitleHelperPlatform.android.kt new file mode 100644 index 000000000..1a87b512d --- /dev/null +++ b/library/src/androidMain/kotlin/com/lagradost/cloudstream3/utils/SubtitleHelperPlatform.android.kt @@ -0,0 +1,24 @@ +package com.lagradost.cloudstream3.utils + +import java.util.Locale + +// TODO: add androidMain actual using LocaleListCompat.getAdjustedDefault() to respect +// per-app language preferences set via AppCompatDelegate.setApplicationLocales() + +actual fun getCurrentLocale(): String = + Locale.getDefault().toLanguageTag() + +actual fun localizedLanguageName(ietfTag: String, localizedTo: String): String? { + val localeOfLangCode = Locale.forLanguageTag(ietfTag) + val localeOfLocalizeTo = Locale.forLanguageTag(localizedTo) + val displayName = localeOfLangCode.getDisplayName(localeOfLocalizeTo) + + // Locale.getDisplayName() falls back to the raw tag or "language (country)" form + // when it doesn't know how to render the name. + val langCodeWithCountry = "${localeOfLangCode.language} (" + val failed = + displayName.equals(ietfTag, ignoreCase = true) || + displayName.contains(langCodeWithCountry, ignoreCase = true) + + return if (failed) null else displayName +} \ No newline at end of file diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/SubtitleHelper.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/SubtitleHelper.kt index a6362efd0..497e129a0 100644 --- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/SubtitleHelper.kt +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/SubtitleHelper.kt @@ -1,13 +1,5 @@ package com.lagradost.cloudstream3.utils -import java.util.Locale - -// If you find a way to use SettingsGeneral getCurrentLocale() -// instead of this function do it. -fun getCurrentLocale(): String { - return Locale.getDefault().toLanguageTag() -} - @Suppress( "unused", "MemberVisibilityCanBePrivate" @@ -47,23 +39,10 @@ object SubtitleHelper { val ISO_639_3: String, // ISO 639-6 missing as it's intended to differentiate specific dialects and variants val openSubtitles: String, // inconsistent codes that do not conform ISO 639 ) { - fun localizedName(localizedTo: String? = null): String { - // Use system locale to localize language name - val localeOfLangCode = Locale.forLanguageTag(this.IETF_tag) - val localeOfLocalizeTo = Locale.forLanguageTag(localizedTo ?: getCurrentLocale()) - val sysLocalizedName = localeOfLangCode.getDisplayName(localeOfLocalizeTo) - - val langCodeWithCountry = "${localeOfLangCode.language} (" // ${localeOfLangCode.country})" - val failedToLocalize = - sysLocalizedName.equals(this.IETF_tag, ignoreCase = true) || - sysLocalizedName.contains(langCodeWithCountry, ignoreCase = true) - - return if (failedToLocalize) + fun localizedName(localizedTo: String? = null): String = + localizedLanguageName(this.IETF_tag, localizedTo ?: getCurrentLocale()) // fallback to native language name - this.nativeName - else - sysLocalizedName - } + ?: this.nativeName fun nameNextToFlagEmoji(localizedTo: String? = null): String { // fallback to [A][A] -> [?] question mak flag diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/SubtitleHelperPlatform.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/SubtitleHelperPlatform.kt new file mode 100644 index 000000000..38a254da8 --- /dev/null +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/SubtitleHelperPlatform.kt @@ -0,0 +1,13 @@ +package com.lagradost.cloudstream3.utils + +/** + * Returns the current locale as an IETF BCP 47 language tag. + */ +expect fun getCurrentLocale(): String + +/** + * Returns the display name of [ietfTag] localized into [localizedTo]. + * Returns null if the platform couldn't produce a meaningful name + * (i.e. it just echoed back the tag or contained a bare language code with parentheses). + */ +expect fun localizedLanguageName(ietfTag: String, localizedTo: String): String? \ No newline at end of file diff --git a/library/src/jvmMain/kotlin/com/lagradost/cloudstream3/utils/SubtitleHelperPlatform.jvm.kt b/library/src/jvmMain/kotlin/com/lagradost/cloudstream3/utils/SubtitleHelperPlatform.jvm.kt new file mode 100644 index 000000000..a43358ccb --- /dev/null +++ b/library/src/jvmMain/kotlin/com/lagradost/cloudstream3/utils/SubtitleHelperPlatform.jvm.kt @@ -0,0 +1,21 @@ +package com.lagradost.cloudstream3.utils + +import java.util.Locale + +actual fun getCurrentLocale(): String = + Locale.getDefault().toLanguageTag() + +actual fun localizedLanguageName(ietfTag: String, localizedTo: String): String? { + val localeOfLangCode = Locale.forLanguageTag(ietfTag) + val localeOfLocalizeTo = Locale.forLanguageTag(localizedTo) + val displayName = localeOfLangCode.getDisplayName(localeOfLocalizeTo) + + // Locale.getDisplayName() falls back to the raw tag or "language (country)" form + // when it doesn't know how to render the name. + val langCodeWithCountry = "${localeOfLangCode.language} (" + val failed = + displayName.equals(ietfTag, ignoreCase = true) || + displayName.contains(langCodeWithCountry, ignoreCase = true) + + return if (failed) null else displayName +} \ No newline at end of file diff --git a/library/src/webMain/kotlin/com/lagradost/cloudstream3/utils/SubtitleHelperPlatform.web.kt b/library/src/webMain/kotlin/com/lagradost/cloudstream3/utils/SubtitleHelperPlatform.web.kt new file mode 100644 index 000000000..33e293f31 --- /dev/null +++ b/library/src/webMain/kotlin/com/lagradost/cloudstream3/utils/SubtitleHelperPlatform.web.kt @@ -0,0 +1,31 @@ +package com.lagradost.cloudstream3.utils + +@OptIn(ExperimentalWasmJsInterop::class) +external class IntlDisplayNames(locale: String, options: JsAny) : JsAny { + fun of(code: String): String? +} + +@OptIn(ExperimentalWasmJsInterop::class) +private fun navigatorLanguage(): String = + js("navigator.language") + +actual fun getCurrentLocale(): String = + try { navigatorLanguage() } catch (_: Throwable) { "en" } + +@OptIn(ExperimentalWasmJsInterop::class) +private fun buildOptions(): JsAny = + js("({ type: 'language' })") + +@OptIn(ExperimentalWasmJsInterop::class) +private fun makeDisplayNames(locale: String, options: JsAny): IntlDisplayNames = + js("new Intl.DisplayNames([locale], options)") + +@OptIn(ExperimentalWasmJsInterop::class) +actual fun localizedLanguageName(ietfTag: String, localizedTo: String): String? = + try { + val dn = makeDisplayNames(localizedTo, buildOptions()) + val name = dn.of(ietfTag) + if (name.isNullOrBlank() || name.equals(ietfTag, ignoreCase = true)) null else name + } catch (_: Throwable) { + null + } From 15fea0c7caf50c457a745d06e2e74695d66308fa Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Mon, 13 Jul 2026 12:37:33 -0600 Subject: [PATCH 17/38] Bump compilesdk to 37 (#2670) --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f20c19832..f45bd6778 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -59,7 +59,7 @@ zipline = "1.27.0" jvmTarget = "1.8" jdkToolchain = "17" minSdk = "23" -compileSdk = "36" +compileSdk = "37" targetSdk = "36" versionCode = "68" From feddcccf0d4186fc6b6fe6520181c02f709b33af Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Tue, 14 Jul 2026 05:17:47 -0700 Subject: [PATCH 18/38] [skip ci] Update Kotlin to 2.4.0 (#2868) --- app/build.gradle.kts | 2 -- .../com/lagradost/cloudstream3/ui/home/HomeFragment.kt | 1 - gradle/libs.versions.toml | 2 +- library/api/jvm/library.api | 1 - library/build.gradle.kts | 8 ++------ .../com/lagradost/cloudstream3/utils/ExtractorApi.kt | 3 --- 6 files changed, 3 insertions(+), 14 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 75a9698c8..0feb866be 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -323,11 +323,9 @@ tasks.withType { compilerOptions { jvmTarget.set(javaTarget) jvmDefault.set(JvmDefaultMode.ENABLE) - freeCompilerArgs.add("-Xannotation-default-target=param-property") optIn.addAll( "com.lagradost.cloudstream3.InternalAPI", "com.lagradost.cloudstream3.Prerelease", - "kotlin.uuid.ExperimentalUuidApi", ) } } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt index b68ef5962..ab0715c6d 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt @@ -665,7 +665,6 @@ class HomeFragment : BaseFragment( fromUI = true ) showToast(R.string.action_reload, Toast.LENGTH_SHORT) - true } homePreviewSearchButton.setOnClickListener { _ -> diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f45bd6778..fdfaee7b2 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -26,7 +26,7 @@ junit = "4.13.2" junitKtx = "1.3.0" junitVersion = "1.3.0" juniversalchardet = "2.5.0" -kotlinGradlePlugin = "2.3.20" +kotlinGradlePlugin = "2.4.0" kotlinxAtomicfu = "0.33.0" kotlinxCollectionsImmutable = "0.4.0" kotlinxCoroutines = "1.11.0" diff --git a/library/api/jvm/library.api b/library/api/jvm/library.api index 49638f132..794d7d520 100644 --- a/library/api/jvm/library.api +++ b/library/api/jvm/library.api @@ -1185,7 +1185,6 @@ public final class com/lagradost/cloudstream3/Score { public static final field MAX I public static final field MAX_ZEROS I public static final field MIN I - public synthetic fun (ILkotlin/jvm/internal/DefaultConstructorMarker;)V public fun equals (Ljava/lang/Object;)Z public fun hashCode ()I public final fun toByte (I)B diff --git a/library/build.gradle.kts b/library/build.gradle.kts index aa460164e..18579eb0c 100644 --- a/library/build.gradle.kts +++ b/library/build.gradle.kts @@ -41,10 +41,7 @@ kotlin { jvm() compilerOptions { - freeCompilerArgs.addAll( - "-Xexpect-actual-classes", - "-Xannotation-default-target=param-property" - ) + freeCompilerArgs.add("-Xexpect-actual-classes") } sourceSets { @@ -91,8 +88,7 @@ kotlin { @OptIn(org.jetbrains.kotlin.gradle.dsl.abi.ExperimentalAbiValidation::class) // https://kotlinlang.org/docs/gradle-binary-compatibility-validation.html abiValidation { - enabled.set(true) - this.filters { + filters { exclude { annotatedWith.add("com.lagradost.cloudstream3.Prerelease") annotatedWith.add("com.lagradost.cloudstream3.InternalAPI") diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/ExtractorApi.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/ExtractorApi.kt index 554170df5..163059a97 100644 --- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/ExtractorApi.kt +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/ExtractorApi.kt @@ -1,5 +1,3 @@ -@file:OptIn(ExperimentalUuidApi::class) - package com.lagradost.cloudstream3.utils import com.fasterxml.jackson.annotation.JsonIgnore @@ -332,7 +330,6 @@ import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import kotlinx.serialization.Transient import kotlin.coroutines.cancellation.CancellationException -import kotlin.uuid.ExperimentalUuidApi import kotlin.uuid.Uuid import kotlin.uuid.toJavaUuid import kotlin.uuid.toKotlinUuid From 804b3c279c2752501d0a5d8711c68dffb77f8164 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Tue, 14 Jul 2026 06:04:51 -0700 Subject: [PATCH 19/38] extractors: migrate to kotlinx serialization (#2963) --- library/api/jvm/library.api | 836 +++++++++++++++++- .../cloudstream3/extractors/Acefile.kt | 13 +- .../cloudstream3/extractors/Blogger.kt | 14 +- .../cloudstream3/extractors/ByseSX.kt | 98 +- .../lagradost/cloudstream3/extractors/Cda.kt | 21 +- .../cloudstream3/extractors/Firestream.kt | 22 +- .../cloudstream3/extractors/Flyfile.kt | 18 +- .../cloudstream3/extractors/GUpload.kt | 28 +- .../cloudstream3/extractors/Gdriveplayer.kt | 22 +- .../cloudstream3/extractors/Gofile.kt | 32 +- .../extractors/HDMomPlayerExtractor.kt | 49 +- .../extractors/HDPlayerSystemExtractor.kt | 52 +- .../cloudstream3/extractors/Jeniusplay.kt | 17 +- .../cloudstream3/extractors/Linkbox.kt | 23 +- .../extractors/MailRuExtractor.kt | 48 +- .../extractors/OdnoklassnikiExtractor.kt | 55 +- .../extractors/PeaceMakerstExtractor.kt | 89 +- .../cloudstream3/extractors/PlayLtXyz.kt | 53 +- .../cloudstream3/extractors/Rabbitstream.kt | 54 +- .../extractors/SobreatsesuypExtractor.kt | 48 +- .../cloudstream3/extractors/StreamEmbed.kt | 26 +- .../cloudstream3/extractors/StreamSB.kt | 49 +- .../cloudstream3/extractors/Streamlare.kt | 50 +- .../cloudstream3/extractors/Streamplay.kt | 42 +- .../cloudstream3/extractors/TRsTXExtractor.kt | 62 +- .../cloudstream3/extractors/Tantifilm.kt | 42 +- .../extractors/TauVideoExtractor.kt | 36 +- .../cloudstream3/extractors/Uservideo.kt | 30 +- .../cloudstream3/extractors/Vicloud.kt | 26 +- .../cloudstream3/extractors/Vidara.kt | 34 +- .../extractors/VideoSeyredExtractor.kt | 64 +- .../cloudstream3/extractors/Vidoza.kt | 44 +- .../cloudstream3/extractors/Vinovo.kt | 33 +- .../cloudstream3/extractors/XStreamCdn.kt | 138 +-- .../cloudstream3/extractors/YourUpload.kt | 19 +- 35 files changed, 1570 insertions(+), 717 deletions(-) diff --git a/library/api/jvm/library.api b/library/api/jvm/library.api index 794d7d520..1b39b3906 100644 --- a/library/api/jvm/library.api +++ b/library/api/jvm/library.api @@ -1735,6 +1735,7 @@ public class com/lagradost/cloudstream3/extractors/Acefile : com/lagradost/cloud } public final class com/lagradost/cloudstream3/extractors/Acefile$Source { + public static final field Companion Lcom/lagradost/cloudstream3/extractors/Acefile$Source$Companion; public fun ()V public fun (Ljava/lang/String;)V public synthetic fun (Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V @@ -1747,6 +1748,21 @@ public final class com/lagradost/cloudstream3/extractors/Acefile$Source { public fun toString ()Ljava/lang/String; } +public final synthetic class com/lagradost/cloudstream3/extractors/Acefile$Source$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/extractors/Acefile$Source$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/extractors/Acefile$Source; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/extractors/Acefile$Source;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/extractors/Acefile$Source$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public final class com/lagradost/cloudstream3/extractors/AesHelper { public static final field INSTANCE Lcom/lagradost/cloudstream3/extractors/AesHelper; public final fun decryptAES (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; @@ -1815,16 +1831,12 @@ public final class com/lagradost/cloudstream3/extractors/ByseBuho : com/lagrados public fun ()V public fun getMainUrl ()Ljava/lang/String; public fun getName ()Ljava/lang/String; - public fun setMainUrl (Ljava/lang/String;)V - public fun setName (Ljava/lang/String;)V } public final class com/lagradost/cloudstream3/extractors/ByseQekaho : com/lagradost/cloudstream3/extractors/ByseSX { public fun ()V public fun getMainUrl ()Ljava/lang/String; public fun getName ()Ljava/lang/String; - public fun setMainUrl (Ljava/lang/String;)V - public fun setName (Ljava/lang/String;)V } public class com/lagradost/cloudstream3/extractors/ByseSX : com/lagradost/cloudstream3/utils/ExtractorApi { @@ -1833,24 +1845,18 @@ public class com/lagradost/cloudstream3/extractors/ByseSX : com/lagradost/clouds public fun getName ()Ljava/lang/String; public fun getRequiresReferer ()Z public fun getUrl (Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public fun setMainUrl (Ljava/lang/String;)V - public fun setName (Ljava/lang/String;)V } public final class com/lagradost/cloudstream3/extractors/ByseVepoin : com/lagradost/cloudstream3/extractors/ByseSX { public fun ()V public fun getMainUrl ()Ljava/lang/String; public fun getName ()Ljava/lang/String; - public fun setMainUrl (Ljava/lang/String;)V - public fun setName (Ljava/lang/String;)V } public final class com/lagradost/cloudstream3/extractors/Bysezejataos : com/lagradost/cloudstream3/extractors/ByseSX { public fun ()V public fun getMainUrl ()Ljava/lang/String; public fun getName ()Ljava/lang/String; - public fun setMainUrl (Ljava/lang/String;)V - public fun setName (Ljava/lang/String;)V } public final class com/lagradost/cloudstream3/extractors/Cavanhabg : com/lagradost/cloudstream3/extractors/VidHidePro { @@ -1870,6 +1876,7 @@ public class com/lagradost/cloudstream3/extractors/Cda : com/lagradost/cloudstre } public final class com/lagradost/cloudstream3/extractors/Cda$PlayerData { + public static final field Companion Lcom/lagradost/cloudstream3/extractors/Cda$PlayerData$Companion; public fun (Lcom/lagradost/cloudstream3/extractors/Cda$VideoPlayerData;)V public final fun component1 ()Lcom/lagradost/cloudstream3/extractors/Cda$VideoPlayerData; public final fun copy (Lcom/lagradost/cloudstream3/extractors/Cda$VideoPlayerData;)Lcom/lagradost/cloudstream3/extractors/Cda$PlayerData; @@ -1880,7 +1887,23 @@ public final class com/lagradost/cloudstream3/extractors/Cda$PlayerData { public fun toString ()Ljava/lang/String; } +public final synthetic class com/lagradost/cloudstream3/extractors/Cda$PlayerData$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/extractors/Cda$PlayerData$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/extractors/Cda$PlayerData; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/extractors/Cda$PlayerData;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/extractors/Cda$PlayerData$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public final class com/lagradost/cloudstream3/extractors/Cda$VideoPlayerData { + public static final field Companion Lcom/lagradost/cloudstream3/extractors/Cda$VideoPlayerData$Companion; public fun (Ljava/lang/String;Ljava/util/Map;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;)V public synthetic fun (Ljava/lang/String;Ljava/util/Map;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun component1 ()Ljava/lang/String; @@ -1900,6 +1923,21 @@ public final class com/lagradost/cloudstream3/extractors/Cda$VideoPlayerData { public fun toString ()Ljava/lang/String; } +public final synthetic class com/lagradost/cloudstream3/extractors/Cda$VideoPlayerData$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/extractors/Cda$VideoPlayerData$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/extractors/Cda$VideoPlayerData; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/extractors/Cda$VideoPlayerData;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/extractors/Cda$VideoPlayerData$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public final class com/lagradost/cloudstream3/extractors/Cdnplayer : com/lagradost/cloudstream3/extractors/XStreamCdn { public fun ()V public fun getMainUrl ()Ljava/lang/String; @@ -2086,16 +2124,15 @@ public final class com/lagradost/cloudstream3/extractors/Dailymotion$SubtitlesWr public final class com/lagradost/cloudstream3/extractors/DatabaseGdrive : com/lagradost/cloudstream3/extractors/Gdriveplayer { public fun ()V public fun getMainUrl ()Ljava/lang/String; - public fun setMainUrl (Ljava/lang/String;)V } public final class com/lagradost/cloudstream3/extractors/DatabaseGdrive2 : com/lagradost/cloudstream3/extractors/Gdriveplayer { public fun ()V public fun getMainUrl ()Ljava/lang/String; - public fun setMainUrl (Ljava/lang/String;)V } public final class com/lagradost/cloudstream3/extractors/DecryptKeys { + public static final field Companion Lcom/lagradost/cloudstream3/extractors/DecryptKeys$Companion; public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V public final fun component1 ()Ljava/lang/String; public final fun component2 ()Ljava/lang/String; @@ -2110,6 +2147,21 @@ public final class com/lagradost/cloudstream3/extractors/DecryptKeys { public fun toString ()Ljava/lang/String; } +public final synthetic class com/lagradost/cloudstream3/extractors/DecryptKeys$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/extractors/DecryptKeys$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/extractors/DecryptKeys; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/extractors/DecryptKeys;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/extractors/DecryptKeys$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public final class com/lagradost/cloudstream3/extractors/DesuArcg : com/lagradost/cloudstream3/extractors/JWPlayer { public fun ()V public fun getMainUrl ()Ljava/lang/String; @@ -2135,6 +2187,7 @@ public final class com/lagradost/cloudstream3/extractors/DesuOdvip : com/lagrado } public final class com/lagradost/cloudstream3/extractors/DetailsRoot { + public static final field Companion Lcom/lagradost/cloudstream3/extractors/DetailsRoot$Companion; public fun (JLjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLjava/lang/String;)V public final fun component1 ()J public final fun component2 ()Ljava/lang/String; @@ -2159,6 +2212,21 @@ public final class com/lagradost/cloudstream3/extractors/DetailsRoot { public fun toString ()Ljava/lang/String; } +public final synthetic class com/lagradost/cloudstream3/extractors/DetailsRoot$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/extractors/DetailsRoot$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/extractors/DetailsRoot; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/extractors/DetailsRoot;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/extractors/DetailsRoot$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public final class com/lagradost/cloudstream3/extractors/Dhcplay : com/lagradost/cloudstream3/extractors/CineMMRedirect { public fun ()V public fun getMainUrl ()Ljava/lang/String; @@ -2371,15 +2439,12 @@ public final class com/lagradost/cloudstream3/extractors/FeHD : com/lagradost/cl public fun getDomainUrl ()Ljava/lang/String; public fun getMainUrl ()Ljava/lang/String; public fun getName ()Ljava/lang/String; - public fun setDomainUrl (Ljava/lang/String;)V } public final class com/lagradost/cloudstream3/extractors/Fembed9hd : com/lagradost/cloudstream3/extractors/XStreamCdn { public fun ()V public fun getMainUrl ()Ljava/lang/String; public fun getName ()Ljava/lang/String; - public fun setMainUrl (Ljava/lang/String;)V - public fun setName (Ljava/lang/String;)V } public final class com/lagradost/cloudstream3/extractors/FileMoon : com/lagradost/cloudstream3/extractors/FilemoonV2 { @@ -2525,6 +2590,7 @@ public class com/lagradost/cloudstream3/extractors/Gdriveplayer : com/lagradost/ } public final class com/lagradost/cloudstream3/extractors/Gdriveplayer$Tracks { + public static final field Companion Lcom/lagradost/cloudstream3/extractors/Gdriveplayer$Tracks$Companion; public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V public final fun component1 ()Ljava/lang/String; public final fun component2 ()Ljava/lang/String; @@ -2539,6 +2605,21 @@ public final class com/lagradost/cloudstream3/extractors/Gdriveplayer$Tracks { public fun toString ()Ljava/lang/String; } +public final synthetic class com/lagradost/cloudstream3/extractors/Gdriveplayer$Tracks$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/extractors/Gdriveplayer$Tracks$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/extractors/Gdriveplayer$Tracks; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/extractors/Gdriveplayer$Tracks;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/extractors/Gdriveplayer$Tracks$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public final class com/lagradost/cloudstream3/extractors/Gdriveplayerapi : com/lagradost/cloudstream3/extractors/Gdriveplayer { public fun ()V public fun getMainUrl ()Ljava/lang/String; @@ -2609,6 +2690,7 @@ public class com/lagradost/cloudstream3/extractors/Gofile : com/lagradost/clouds } public final class com/lagradost/cloudstream3/extractors/Gofile$AccountData { + public static final field Companion Lcom/lagradost/cloudstream3/extractors/Gofile$AccountData$Companion; public fun ()V public fun (Ljava/lang/String;)V public synthetic fun (Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V @@ -2621,7 +2703,23 @@ public final class com/lagradost/cloudstream3/extractors/Gofile$AccountData { public fun toString ()Ljava/lang/String; } +public final synthetic class com/lagradost/cloudstream3/extractors/Gofile$AccountData$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/extractors/Gofile$AccountData$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/extractors/Gofile$AccountData; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/extractors/Gofile$AccountData;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/extractors/Gofile$AccountData$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public final class com/lagradost/cloudstream3/extractors/Gofile$AccountResponse { + public static final field Companion Lcom/lagradost/cloudstream3/extractors/Gofile$AccountResponse$Companion; public fun ()V public fun (Lcom/lagradost/cloudstream3/extractors/Gofile$AccountData;)V public synthetic fun (Lcom/lagradost/cloudstream3/extractors/Gofile$AccountData;ILkotlin/jvm/internal/DefaultConstructorMarker;)V @@ -2634,7 +2732,23 @@ public final class com/lagradost/cloudstream3/extractors/Gofile$AccountResponse public fun toString ()Ljava/lang/String; } +public final synthetic class com/lagradost/cloudstream3/extractors/Gofile$AccountResponse$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/extractors/Gofile$AccountResponse$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/extractors/Gofile$AccountResponse; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/extractors/Gofile$AccountResponse;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/extractors/Gofile$AccountResponse$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public final class com/lagradost/cloudstream3/extractors/Gofile$GofileData { + public static final field Companion Lcom/lagradost/cloudstream3/extractors/Gofile$GofileData$Companion; public fun ()V public fun (Ljava/util/Map;)V public synthetic fun (Ljava/util/Map;ILkotlin/jvm/internal/DefaultConstructorMarker;)V @@ -2647,7 +2761,23 @@ public final class com/lagradost/cloudstream3/extractors/Gofile$GofileData { public fun toString ()Ljava/lang/String; } +public final synthetic class com/lagradost/cloudstream3/extractors/Gofile$GofileData$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/extractors/Gofile$GofileData$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/extractors/Gofile$GofileData; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/extractors/Gofile$GofileData;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/extractors/Gofile$GofileData$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public final class com/lagradost/cloudstream3/extractors/Gofile$GofileFile { + public static final field Companion Lcom/lagradost/cloudstream3/extractors/Gofile$GofileFile$Companion; public fun ()V public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;)V public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;ILkotlin/jvm/internal/DefaultConstructorMarker;)V @@ -2666,7 +2796,23 @@ public final class com/lagradost/cloudstream3/extractors/Gofile$GofileFile { public fun toString ()Ljava/lang/String; } +public final synthetic class com/lagradost/cloudstream3/extractors/Gofile$GofileFile$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/extractors/Gofile$GofileFile$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/extractors/Gofile$GofileFile; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/extractors/Gofile$GofileFile;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/extractors/Gofile$GofileFile$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public final class com/lagradost/cloudstream3/extractors/Gofile$GofileResponse { + public static final field Companion Lcom/lagradost/cloudstream3/extractors/Gofile$GofileResponse$Companion; public fun ()V public fun (Lcom/lagradost/cloudstream3/extractors/Gofile$GofileData;)V public synthetic fun (Lcom/lagradost/cloudstream3/extractors/Gofile$GofileData;ILkotlin/jvm/internal/DefaultConstructorMarker;)V @@ -2679,6 +2825,21 @@ public final class com/lagradost/cloudstream3/extractors/Gofile$GofileResponse { public fun toString ()Ljava/lang/String; } +public final synthetic class com/lagradost/cloudstream3/extractors/Gofile$GofileResponse$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/extractors/Gofile$GofileResponse$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/extractors/Gofile$GofileResponse; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/extractors/Gofile$GofileResponse;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/extractors/Gofile$GofileResponse$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public final class com/lagradost/cloudstream3/extractors/GoodstreamExtractor : com/lagradost/cloudstream3/utils/ExtractorApi { public fun ()V public fun getMainUrl ()Ljava/lang/String; @@ -2710,6 +2871,7 @@ public class com/lagradost/cloudstream3/extractors/HDMomPlayer : com/lagradost/c } public final class com/lagradost/cloudstream3/extractors/HDMomPlayer$Track { + public static final field Companion Lcom/lagradost/cloudstream3/extractors/HDMomPlayer$Track$Companion; public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V public final fun component1 ()Ljava/lang/String; public final fun component2 ()Ljava/lang/String; @@ -2728,6 +2890,21 @@ public final class com/lagradost/cloudstream3/extractors/HDMomPlayer$Track { public fun toString ()Ljava/lang/String; } +public final synthetic class com/lagradost/cloudstream3/extractors/HDMomPlayer$Track$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/extractors/HDMomPlayer$Track$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/extractors/HDMomPlayer$Track; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/extractors/HDMomPlayer$Track;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/extractors/HDMomPlayer$Track$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public class com/lagradost/cloudstream3/extractors/HDPlayerSystem : com/lagradost/cloudstream3/utils/ExtractorApi { public fun ()V public fun getMainUrl ()Ljava/lang/String; @@ -2737,6 +2914,7 @@ public class com/lagradost/cloudstream3/extractors/HDPlayerSystem : com/lagrados } public final class com/lagradost/cloudstream3/extractors/HDPlayerSystem$SystemResponse { + public static final field Companion Lcom/lagradost/cloudstream3/extractors/HDPlayerSystem$SystemResponse$Companion; public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun component1 ()Ljava/lang/String; @@ -2754,6 +2932,21 @@ public final class com/lagradost/cloudstream3/extractors/HDPlayerSystem$SystemRe public fun toString ()Ljava/lang/String; } +public final synthetic class com/lagradost/cloudstream3/extractors/HDPlayerSystem$SystemResponse$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/extractors/HDPlayerSystem$SystemResponse$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/extractors/HDPlayerSystem$SystemResponse; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/extractors/HDPlayerSystem$SystemResponse;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/extractors/HDPlayerSystem$SystemResponse$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public final class com/lagradost/cloudstream3/extractors/HDStreamAble : com/lagradost/cloudstream3/extractors/PeaceMakerst { public fun ()V public fun getMainUrl ()Ljava/lang/String; @@ -2853,6 +3046,7 @@ public class com/lagradost/cloudstream3/extractors/Jeniusplay : com/lagradost/cl } public final class com/lagradost/cloudstream3/extractors/Jeniusplay$ResponseSource { + public static final field Companion Lcom/lagradost/cloudstream3/extractors/Jeniusplay$ResponseSource$Companion; public fun (ZLjava/lang/String;Ljava/lang/String;)V public final fun component1 ()Z public final fun component2 ()Ljava/lang/String; @@ -2867,6 +3061,21 @@ public final class com/lagradost/cloudstream3/extractors/Jeniusplay$ResponseSour public fun toString ()Ljava/lang/String; } +public final synthetic class com/lagradost/cloudstream3/extractors/Jeniusplay$ResponseSource$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/extractors/Jeniusplay$ResponseSource$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/extractors/Jeniusplay$ResponseSource; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/extractors/Jeniusplay$ResponseSource;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/extractors/Jeniusplay$ResponseSource$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public final class com/lagradost/cloudstream3/extractors/Jodwish : com/lagradost/cloudstream3/extractors/StreamWishExtractor { public fun ()V public fun getMainUrl ()Ljava/lang/String; @@ -2923,6 +3132,7 @@ public class com/lagradost/cloudstream3/extractors/Linkbox : com/lagradost/cloud } public final class com/lagradost/cloudstream3/extractors/Linkbox$Data { + public static final field Companion Lcom/lagradost/cloudstream3/extractors/Linkbox$Data$Companion; public fun ()V public fun (Lcom/lagradost/cloudstream3/extractors/Linkbox$ItemInfo;Ljava/lang/String;)V public synthetic fun (Lcom/lagradost/cloudstream3/extractors/Linkbox$ItemInfo;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V @@ -2937,7 +3147,23 @@ public final class com/lagradost/cloudstream3/extractors/Linkbox$Data { public fun toString ()Ljava/lang/String; } +public final synthetic class com/lagradost/cloudstream3/extractors/Linkbox$Data$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/extractors/Linkbox$Data$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/extractors/Linkbox$Data; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/extractors/Linkbox$Data;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/extractors/Linkbox$Data$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public final class com/lagradost/cloudstream3/extractors/Linkbox$ItemInfo { + public static final field Companion Lcom/lagradost/cloudstream3/extractors/Linkbox$ItemInfo$Companion; public fun ()V public fun (Ljava/util/ArrayList;)V public synthetic fun (Ljava/util/ArrayList;ILkotlin/jvm/internal/DefaultConstructorMarker;)V @@ -2950,7 +3176,23 @@ public final class com/lagradost/cloudstream3/extractors/Linkbox$ItemInfo { public fun toString ()Ljava/lang/String; } +public final synthetic class com/lagradost/cloudstream3/extractors/Linkbox$ItemInfo$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/extractors/Linkbox$ItemInfo$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/extractors/Linkbox$ItemInfo; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/extractors/Linkbox$ItemInfo;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/extractors/Linkbox$ItemInfo$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public final class com/lagradost/cloudstream3/extractors/Linkbox$Resolutions { + public static final field Companion Lcom/lagradost/cloudstream3/extractors/Linkbox$Resolutions$Companion; public fun ()V public fun (Ljava/lang/String;Ljava/lang/String;)V public synthetic fun (Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V @@ -2965,7 +3207,23 @@ public final class com/lagradost/cloudstream3/extractors/Linkbox$Resolutions { public fun toString ()Ljava/lang/String; } +public final synthetic class com/lagradost/cloudstream3/extractors/Linkbox$Resolutions$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/extractors/Linkbox$Resolutions$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/extractors/Linkbox$Resolutions; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/extractors/Linkbox$Resolutions;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/extractors/Linkbox$Resolutions$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public final class com/lagradost/cloudstream3/extractors/Linkbox$Responses { + public static final field Companion Lcom/lagradost/cloudstream3/extractors/Linkbox$Responses$Companion; public fun ()V public fun (Lcom/lagradost/cloudstream3/extractors/Linkbox$Data;)V public synthetic fun (Lcom/lagradost/cloudstream3/extractors/Linkbox$Data;ILkotlin/jvm/internal/DefaultConstructorMarker;)V @@ -2978,6 +3236,21 @@ public final class com/lagradost/cloudstream3/extractors/Linkbox$Responses { public fun toString ()Ljava/lang/String; } +public final synthetic class com/lagradost/cloudstream3/extractors/Linkbox$Responses$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/extractors/Linkbox$Responses$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/extractors/Linkbox$Responses; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/extractors/Linkbox$Responses;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/extractors/Linkbox$Responses$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public class com/lagradost/cloudstream3/extractors/LuluStream : com/lagradost/cloudstream3/utils/ExtractorApi { public fun ()V public fun getMainUrl ()Ljava/lang/String; @@ -3032,6 +3305,7 @@ public class com/lagradost/cloudstream3/extractors/MailRu : com/lagradost/clouds } public final class com/lagradost/cloudstream3/extractors/MailRu$MailRuData { + public static final field Companion Lcom/lagradost/cloudstream3/extractors/MailRu$MailRuData$Companion; public fun (Ljava/lang/String;Ljava/util/List;)V public final fun component1 ()Ljava/lang/String; public final fun component2 ()Ljava/util/List; @@ -3044,7 +3318,23 @@ public final class com/lagradost/cloudstream3/extractors/MailRu$MailRuData { public fun toString ()Ljava/lang/String; } +public final synthetic class com/lagradost/cloudstream3/extractors/MailRu$MailRuData$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/extractors/MailRu$MailRuData$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/extractors/MailRu$MailRuData; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/extractors/MailRu$MailRuData;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/extractors/MailRu$MailRuData$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public final class com/lagradost/cloudstream3/extractors/MailRu$MailRuVideoData { + public static final field Companion Lcom/lagradost/cloudstream3/extractors/MailRu$MailRuVideoData$Companion; public fun (Ljava/lang/String;Ljava/lang/String;)V public final fun component1 ()Ljava/lang/String; public final fun component2 ()Ljava/lang/String; @@ -3057,6 +3347,21 @@ public final class com/lagradost/cloudstream3/extractors/MailRu$MailRuVideoData public fun toString ()Ljava/lang/String; } +public final synthetic class com/lagradost/cloudstream3/extractors/MailRu$MailRuVideoData$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/extractors/MailRu$MailRuVideoData$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/extractors/MailRu$MailRuVideoData; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/extractors/MailRu$MailRuVideoData;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/extractors/MailRu$MailRuVideoData$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public class com/lagradost/cloudstream3/extractors/Maxstream : com/lagradost/cloudstream3/utils/ExtractorApi { public fun ()V public fun getMainUrl ()Ljava/lang/String; @@ -3272,6 +3577,7 @@ public class com/lagradost/cloudstream3/extractors/Odnoklassniki : com/lagradost } public final class com/lagradost/cloudstream3/extractors/Odnoklassniki$OkRuVideo { + public static final field Companion Lcom/lagradost/cloudstream3/extractors/Odnoklassniki$OkRuVideo$Companion; public fun (Ljava/lang/String;Ljava/lang/String;)V public final fun component1 ()Ljava/lang/String; public final fun component2 ()Ljava/lang/String; @@ -3284,6 +3590,21 @@ public final class com/lagradost/cloudstream3/extractors/Odnoklassniki$OkRuVideo public fun toString ()Ljava/lang/String; } +public final synthetic class com/lagradost/cloudstream3/extractors/Odnoklassniki$OkRuVideo$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/extractors/Odnoklassniki$OkRuVideo$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/extractors/Odnoklassniki$OkRuVideo; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/extractors/Odnoklassniki$OkRuVideo;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/extractors/Odnoklassniki$OkRuVideo$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public class com/lagradost/cloudstream3/extractors/OkRuHTTP : com/lagradost/cloudstream3/extractors/Odnoklassniki { public fun ()V public fun getMainUrl ()Ljava/lang/String; @@ -3321,6 +3642,7 @@ public class com/lagradost/cloudstream3/extractors/PeaceMakerst : com/lagradost/ } public final class com/lagradost/cloudstream3/extractors/PeaceMakerst$PeaceResponse { + public static final field Companion Lcom/lagradost/cloudstream3/extractors/PeaceMakerst$PeaceResponse$Companion; public fun (Ljava/lang/String;Ljava/util/List;Ljava/lang/String;Ljava/util/Map;)V public final fun component1 ()Ljava/lang/String; public final fun component2 ()Ljava/util/List; @@ -3329,7 +3651,7 @@ public final class com/lagradost/cloudstream3/extractors/PeaceMakerst$PeaceRespo public final fun copy (Ljava/lang/String;Ljava/util/List;Ljava/lang/String;Ljava/util/Map;)Lcom/lagradost/cloudstream3/extractors/PeaceMakerst$PeaceResponse; public static synthetic fun copy$default (Lcom/lagradost/cloudstream3/extractors/PeaceMakerst$PeaceResponse;Ljava/lang/String;Ljava/util/List;Ljava/lang/String;Ljava/util/Map;ILjava/lang/Object;)Lcom/lagradost/cloudstream3/extractors/PeaceMakerst$PeaceResponse; public fun equals (Ljava/lang/Object;)Z - public final fun getSIndex ()Ljava/lang/String; + public final fun getSourceIndex ()Ljava/lang/String; public final fun getSourceList ()Ljava/util/Map; public final fun getVideoImage ()Ljava/lang/String; public final fun getVideoSources ()Ljava/util/List; @@ -3337,7 +3659,23 @@ public final class com/lagradost/cloudstream3/extractors/PeaceMakerst$PeaceRespo public fun toString ()Ljava/lang/String; } +public final synthetic class com/lagradost/cloudstream3/extractors/PeaceMakerst$PeaceResponse$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/extractors/PeaceMakerst$PeaceResponse$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/extractors/PeaceMakerst$PeaceResponse; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/extractors/PeaceMakerst$PeaceResponse;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/extractors/PeaceMakerst$PeaceResponse$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public final class com/lagradost/cloudstream3/extractors/PeaceMakerst$Teve2ApiResponse { + public static final field Companion Lcom/lagradost/cloudstream3/extractors/PeaceMakerst$Teve2ApiResponse$Companion; public fun (Lcom/lagradost/cloudstream3/extractors/PeaceMakerst$Teve2Media;)V public final fun component1 ()Lcom/lagradost/cloudstream3/extractors/PeaceMakerst$Teve2Media; public final fun copy (Lcom/lagradost/cloudstream3/extractors/PeaceMakerst$Teve2Media;)Lcom/lagradost/cloudstream3/extractors/PeaceMakerst$Teve2ApiResponse; @@ -3348,7 +3686,23 @@ public final class com/lagradost/cloudstream3/extractors/PeaceMakerst$Teve2ApiRe public fun toString ()Ljava/lang/String; } +public final synthetic class com/lagradost/cloudstream3/extractors/PeaceMakerst$Teve2ApiResponse$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/extractors/PeaceMakerst$Teve2ApiResponse$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/extractors/PeaceMakerst$Teve2ApiResponse; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/extractors/PeaceMakerst$Teve2ApiResponse;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/extractors/PeaceMakerst$Teve2ApiResponse$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public final class com/lagradost/cloudstream3/extractors/PeaceMakerst$Teve2Link { + public static final field Companion Lcom/lagradost/cloudstream3/extractors/PeaceMakerst$Teve2Link$Companion; public fun (Ljava/lang/String;Ljava/lang/String;)V public final fun component1 ()Ljava/lang/String; public final fun component2 ()Ljava/lang/String; @@ -3361,7 +3715,23 @@ public final class com/lagradost/cloudstream3/extractors/PeaceMakerst$Teve2Link public fun toString ()Ljava/lang/String; } +public final synthetic class com/lagradost/cloudstream3/extractors/PeaceMakerst$Teve2Link$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/extractors/PeaceMakerst$Teve2Link$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/extractors/PeaceMakerst$Teve2Link; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/extractors/PeaceMakerst$Teve2Link;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/extractors/PeaceMakerst$Teve2Link$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public final class com/lagradost/cloudstream3/extractors/PeaceMakerst$Teve2Media { + public static final field Companion Lcom/lagradost/cloudstream3/extractors/PeaceMakerst$Teve2Media$Companion; public fun (Lcom/lagradost/cloudstream3/extractors/PeaceMakerst$Teve2Link;)V public final fun component1 ()Lcom/lagradost/cloudstream3/extractors/PeaceMakerst$Teve2Link; public final fun copy (Lcom/lagradost/cloudstream3/extractors/PeaceMakerst$Teve2Link;)Lcom/lagradost/cloudstream3/extractors/PeaceMakerst$Teve2Media; @@ -3372,7 +3742,23 @@ public final class com/lagradost/cloudstream3/extractors/PeaceMakerst$Teve2Media public fun toString ()Ljava/lang/String; } +public final synthetic class com/lagradost/cloudstream3/extractors/PeaceMakerst$Teve2Media$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/extractors/PeaceMakerst$Teve2Media$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/extractors/PeaceMakerst$Teve2Media; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/extractors/PeaceMakerst$Teve2Media;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/extractors/PeaceMakerst$Teve2Media$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public final class com/lagradost/cloudstream3/extractors/PeaceMakerst$VideoSource { + public static final field Companion Lcom/lagradost/cloudstream3/extractors/PeaceMakerst$VideoSource$Companion; public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V public final fun component1 ()Ljava/lang/String; public final fun component2 ()Ljava/lang/String; @@ -3387,6 +3773,21 @@ public final class com/lagradost/cloudstream3/extractors/PeaceMakerst$VideoSourc public fun toString ()Ljava/lang/String; } +public final synthetic class com/lagradost/cloudstream3/extractors/PeaceMakerst$VideoSource$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/extractors/PeaceMakerst$VideoSource$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/extractors/PeaceMakerst$VideoSource; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/extractors/PeaceMakerst$VideoSource;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/extractors/PeaceMakerst$VideoSource$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public final class com/lagradost/cloudstream3/extractors/Peytonepre : com/lagradost/cloudstream3/extractors/VidHidePro { public fun ()V public fun getMainUrl ()Ljava/lang/String; @@ -3434,6 +3835,7 @@ public final class com/lagradost/cloudstream3/extractors/PlayRu : com/lagradost/ } public final class com/lagradost/cloudstream3/extractors/Playback { + public static final field Companion Lcom/lagradost/cloudstream3/extractors/Playback$Companion; public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Ljava/lang/String;Lcom/lagradost/cloudstream3/extractors/DecryptKeys;Ljava/lang/String;Ljava/lang/String;)V public final fun component1 ()Ljava/lang/String; public final fun component2 ()Ljava/lang/String; @@ -3458,7 +3860,23 @@ public final class com/lagradost/cloudstream3/extractors/Playback { public fun toString ()Ljava/lang/String; } +public final synthetic class com/lagradost/cloudstream3/extractors/Playback$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/extractors/Playback$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/extractors/Playback; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/extractors/Playback;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/extractors/Playback$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public final class com/lagradost/cloudstream3/extractors/PlaybackDecrypt { + public static final field Companion Lcom/lagradost/cloudstream3/extractors/PlaybackDecrypt$Companion; public fun (Ljava/util/List;)V public final fun component1 ()Ljava/util/List; public final fun copy (Ljava/util/List;)Lcom/lagradost/cloudstream3/extractors/PlaybackDecrypt; @@ -3469,19 +3887,35 @@ public final class com/lagradost/cloudstream3/extractors/PlaybackDecrypt { public fun toString ()Ljava/lang/String; } +public final synthetic class com/lagradost/cloudstream3/extractors/PlaybackDecrypt$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/extractors/PlaybackDecrypt$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/extractors/PlaybackDecrypt; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/extractors/PlaybackDecrypt;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/extractors/PlaybackDecrypt$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public final class com/lagradost/cloudstream3/extractors/PlaybackDecryptSource { - public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;JLjava/lang/Object;)V + public static final field Companion Lcom/lagradost/cloudstream3/extractors/PlaybackDecryptSource$Companion; + public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;JLjava/lang/Integer;)V public final fun component1 ()Ljava/lang/String; public final fun component2 ()Ljava/lang/String; public final fun component3 ()Ljava/lang/String; public final fun component4 ()Ljava/lang/String; public final fun component5 ()J - public final fun component6 ()Ljava/lang/Object; - public final fun copy (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;JLjava/lang/Object;)Lcom/lagradost/cloudstream3/extractors/PlaybackDecryptSource; - public static synthetic fun copy$default (Lcom/lagradost/cloudstream3/extractors/PlaybackDecryptSource;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;JLjava/lang/Object;ILjava/lang/Object;)Lcom/lagradost/cloudstream3/extractors/PlaybackDecryptSource; + public final fun component6 ()Ljava/lang/Integer; + public final fun copy (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;JLjava/lang/Integer;)Lcom/lagradost/cloudstream3/extractors/PlaybackDecryptSource; + public static synthetic fun copy$default (Lcom/lagradost/cloudstream3/extractors/PlaybackDecryptSource;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;JLjava/lang/Integer;ILjava/lang/Object;)Lcom/lagradost/cloudstream3/extractors/PlaybackDecryptSource; public fun equals (Ljava/lang/Object;)Z public final fun getBitrateKbps ()J - public final fun getHeight ()Ljava/lang/Object; + public final fun getHeight ()Ljava/lang/Integer; public final fun getLabel ()Ljava/lang/String; public final fun getMimeType ()Ljava/lang/String; public final fun getQuality ()Ljava/lang/String; @@ -3490,7 +3924,23 @@ public final class com/lagradost/cloudstream3/extractors/PlaybackDecryptSource { public fun toString ()Ljava/lang/String; } +public final synthetic class com/lagradost/cloudstream3/extractors/PlaybackDecryptSource$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/extractors/PlaybackDecryptSource$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/extractors/PlaybackDecryptSource; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/extractors/PlaybackDecryptSource;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/extractors/PlaybackDecryptSource$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public final class com/lagradost/cloudstream3/extractors/PlaybackRoot { + public static final field Companion Lcom/lagradost/cloudstream3/extractors/PlaybackRoot$Companion; public fun (Lcom/lagradost/cloudstream3/extractors/Playback;)V public final fun component1 ()Lcom/lagradost/cloudstream3/extractors/Playback; public final fun copy (Lcom/lagradost/cloudstream3/extractors/Playback;)Lcom/lagradost/cloudstream3/extractors/PlaybackRoot; @@ -3501,6 +3951,21 @@ public final class com/lagradost/cloudstream3/extractors/PlaybackRoot { public fun toString ()Ljava/lang/String; } +public final synthetic class com/lagradost/cloudstream3/extractors/PlaybackRoot$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/extractors/PlaybackRoot$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/extractors/PlaybackRoot; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/extractors/PlaybackRoot;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/extractors/PlaybackRoot$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public class com/lagradost/cloudstream3/extractors/PlayerVoxzer : com/lagradost/cloudstream3/utils/ExtractorApi { public fun ()V public fun getMainUrl ()Ljava/lang/String; @@ -3535,6 +4000,7 @@ public class com/lagradost/cloudstream3/extractors/Rabbitstream : com/lagradost/ } public final class com/lagradost/cloudstream3/extractors/Rabbitstream$Sources { + public static final field Companion Lcom/lagradost/cloudstream3/extractors/Rabbitstream$Sources$Companion; public fun ()V public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V @@ -3551,7 +4017,23 @@ public final class com/lagradost/cloudstream3/extractors/Rabbitstream$Sources { public fun toString ()Ljava/lang/String; } +public final synthetic class com/lagradost/cloudstream3/extractors/Rabbitstream$Sources$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/extractors/Rabbitstream$Sources$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/extractors/Rabbitstream$Sources; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/extractors/Rabbitstream$Sources;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/extractors/Rabbitstream$Sources$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public final class com/lagradost/cloudstream3/extractors/Rabbitstream$SourcesEncrypted { + public static final field Companion Lcom/lagradost/cloudstream3/extractors/Rabbitstream$SourcesEncrypted$Companion; public fun ()V public fun (Ljava/lang/String;Ljava/lang/Boolean;Ljava/util/List;)V public synthetic fun (Ljava/lang/String;Ljava/lang/Boolean;Ljava/util/List;ILkotlin/jvm/internal/DefaultConstructorMarker;)V @@ -3568,7 +4050,23 @@ public final class com/lagradost/cloudstream3/extractors/Rabbitstream$SourcesEnc public fun toString ()Ljava/lang/String; } +public final synthetic class com/lagradost/cloudstream3/extractors/Rabbitstream$SourcesEncrypted$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/extractors/Rabbitstream$SourcesEncrypted$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/extractors/Rabbitstream$SourcesEncrypted; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/extractors/Rabbitstream$SourcesEncrypted;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/extractors/Rabbitstream$SourcesEncrypted$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public final class com/lagradost/cloudstream3/extractors/Rabbitstream$SourcesResponses { + public static final field Companion Lcom/lagradost/cloudstream3/extractors/Rabbitstream$SourcesResponses$Companion; public fun ()V public fun (Ljava/util/List;Ljava/util/List;)V public synthetic fun (Ljava/util/List;Ljava/util/List;ILkotlin/jvm/internal/DefaultConstructorMarker;)V @@ -3583,7 +4081,23 @@ public final class com/lagradost/cloudstream3/extractors/Rabbitstream$SourcesRes public fun toString ()Ljava/lang/String; } +public final synthetic class com/lagradost/cloudstream3/extractors/Rabbitstream$SourcesResponses$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/extractors/Rabbitstream$SourcesResponses$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/extractors/Rabbitstream$SourcesResponses; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/extractors/Rabbitstream$SourcesResponses;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/extractors/Rabbitstream$SourcesResponses$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public final class com/lagradost/cloudstream3/extractors/Rabbitstream$Tracks { + public static final field Companion Lcom/lagradost/cloudstream3/extractors/Rabbitstream$Tracks$Companion; public fun ()V public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V @@ -3600,6 +4114,21 @@ public final class com/lagradost/cloudstream3/extractors/Rabbitstream$Tracks { public fun toString ()Ljava/lang/String; } +public final synthetic class com/lagradost/cloudstream3/extractors/Rabbitstream$Tracks$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/extractors/Rabbitstream$Tracks$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/extractors/Rabbitstream$Tracks; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/extractors/Rabbitstream$Tracks;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/extractors/Rabbitstream$Tracks$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public class com/lagradost/cloudstream3/extractors/RapidVid : com/lagradost/cloudstream3/utils/ExtractorApi { public fun ()V public fun getMainUrl ()Ljava/lang/String; @@ -3774,6 +4303,7 @@ public class com/lagradost/cloudstream3/extractors/Slmaxed : com/lagradost/cloud } public final class com/lagradost/cloudstream3/extractors/Slmaxed$JsonResponse { + public static final field Companion Lcom/lagradost/cloudstream3/extractors/Slmaxed$JsonResponse$Companion; public fun ()V public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;)V public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;ILkotlin/jvm/internal/DefaultConstructorMarker;)V @@ -3794,7 +4324,23 @@ public final class com/lagradost/cloudstream3/extractors/Slmaxed$JsonResponse { public fun toString ()Ljava/lang/String; } +public final synthetic class com/lagradost/cloudstream3/extractors/Slmaxed$JsonResponse$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/extractors/Slmaxed$JsonResponse$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/extractors/Slmaxed$JsonResponse; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/extractors/Slmaxed$JsonResponse;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/extractors/Slmaxed$JsonResponse$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public final class com/lagradost/cloudstream3/extractors/Slmaxed$Result { + public static final field Companion Lcom/lagradost/cloudstream3/extractors/Slmaxed$Result$Companion; public fun ()V public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V @@ -3811,6 +4357,21 @@ public final class com/lagradost/cloudstream3/extractors/Slmaxed$Result { public fun toString ()Ljava/lang/String; } +public final synthetic class com/lagradost/cloudstream3/extractors/Slmaxed$Result$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/extractors/Slmaxed$Result$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/extractors/Slmaxed$Result; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/extractors/Slmaxed$Result;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/extractors/Slmaxed$Result$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public final class com/lagradost/cloudstream3/extractors/Smoothpre : com/lagradost/cloudstream3/extractors/VidHidePro { public fun ()V public fun getMainUrl ()Ljava/lang/String; @@ -3828,6 +4389,7 @@ public class com/lagradost/cloudstream3/extractors/Sobreatsesuyp : com/lagradost } public final class com/lagradost/cloudstream3/extractors/Sobreatsesuyp$SobreatsesuypVideoData { + public static final field Companion Lcom/lagradost/cloudstream3/extractors/Sobreatsesuyp$SobreatsesuypVideoData$Companion; public fun ()V public fun (Ljava/lang/String;Ljava/lang/String;)V public synthetic fun (Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V @@ -3842,6 +4404,21 @@ public final class com/lagradost/cloudstream3/extractors/Sobreatsesuyp$Sobreatse public fun toString ()Ljava/lang/String; } +public final synthetic class com/lagradost/cloudstream3/extractors/Sobreatsesuyp$SobreatsesuypVideoData$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/extractors/Sobreatsesuyp$SobreatsesuypVideoData$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/extractors/Sobreatsesuyp$SobreatsesuypVideoData; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/extractors/Sobreatsesuyp$SobreatsesuypVideoData;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/extractors/Sobreatsesuyp$SobreatsesuypVideoData$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public final class com/lagradost/cloudstream3/extractors/Ssbstream : com/lagradost/cloudstream3/extractors/StreamSB { public fun ()V public fun getMainUrl ()Ljava/lang/String; @@ -3854,8 +4431,6 @@ public class com/lagradost/cloudstream3/extractors/StreamEmbed : com/lagradost/c public fun getName ()Ljava/lang/String; public fun getRequiresReferer ()Z public fun getUrl (Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public fun setMainUrl (Ljava/lang/String;)V - public fun setName (Ljava/lang/String;)V } public final class com/lagradost/cloudstream3/extractors/StreamHLS : com/lagradost/cloudstream3/extractors/StreamWishExtractor { @@ -3881,6 +4456,7 @@ public class com/lagradost/cloudstream3/extractors/StreamSB : com/lagradost/clou } public final class com/lagradost/cloudstream3/extractors/StreamSB$Main { + public static final field Companion Lcom/lagradost/cloudstream3/extractors/StreamSB$Main$Companion; public fun (Lcom/lagradost/cloudstream3/extractors/StreamSB$StreamData;I)V public final fun component1 ()Lcom/lagradost/cloudstream3/extractors/StreamSB$StreamData; public final fun component2 ()I @@ -3893,7 +4469,23 @@ public final class com/lagradost/cloudstream3/extractors/StreamSB$Main { public fun toString ()Ljava/lang/String; } +public final synthetic class com/lagradost/cloudstream3/extractors/StreamSB$Main$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/extractors/StreamSB$Main$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/extractors/StreamSB$Main; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/extractors/StreamSB$Main;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/extractors/StreamSB$Main$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public final class com/lagradost/cloudstream3/extractors/StreamSB$StreamData { + public static final field Companion Lcom/lagradost/cloudstream3/extractors/StreamSB$StreamData$Companion; public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/ArrayList;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/ArrayList;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun component1 ()Ljava/lang/String; @@ -3919,7 +4511,23 @@ public final class com/lagradost/cloudstream3/extractors/StreamSB$StreamData { public fun toString ()Ljava/lang/String; } +public final synthetic class com/lagradost/cloudstream3/extractors/StreamSB$StreamData$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/extractors/StreamSB$StreamData$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/extractors/StreamSB$StreamData; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/extractors/StreamSB$StreamData;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/extractors/StreamSB$StreamData$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public final class com/lagradost/cloudstream3/extractors/StreamSB$Subs { + public static final field Companion Lcom/lagradost/cloudstream3/extractors/StreamSB$Subs$Companion; public fun ()V public fun (Ljava/lang/String;Ljava/lang/String;)V public synthetic fun (Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V @@ -3934,6 +4542,21 @@ public final class com/lagradost/cloudstream3/extractors/StreamSB$Subs { public fun toString ()Ljava/lang/String; } +public final synthetic class com/lagradost/cloudstream3/extractors/StreamSB$Subs$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/extractors/StreamSB$Subs$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/extractors/StreamSB$Subs; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/extractors/StreamSB$Subs;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/extractors/StreamSB$Subs$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public final class com/lagradost/cloudstream3/extractors/StreamSB1 : com/lagradost/cloudstream3/extractors/StreamSB { public fun ()V public fun getMainUrl ()Ljava/lang/String; @@ -4102,6 +4725,7 @@ public class com/lagradost/cloudstream3/extractors/Streamplay : com/lagradost/cl } public final class com/lagradost/cloudstream3/extractors/Streamplay$Source { + public static final field Companion Lcom/lagradost/cloudstream3/extractors/Streamplay$Source$Companion; public fun ()V public fun (Ljava/lang/String;Ljava/lang/String;)V public synthetic fun (Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V @@ -4116,6 +4740,21 @@ public final class com/lagradost/cloudstream3/extractors/Streamplay$Source { public fun toString ()Ljava/lang/String; } +public final synthetic class com/lagradost/cloudstream3/extractors/Streamplay$Source$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/extractors/Streamplay$Source$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/extractors/Streamplay$Source; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/extractors/Streamplay$Source;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/extractors/Streamplay$Source$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public final class com/lagradost/cloudstream3/extractors/Streamsss : com/lagradost/cloudstream3/extractors/StreamSB { public fun ()V public fun getMainUrl ()Ljava/lang/String; @@ -4170,6 +4809,7 @@ public class com/lagradost/cloudstream3/extractors/TRsTX : com/lagradost/cloudst } public final class com/lagradost/cloudstream3/extractors/TRsTX$TrstxVideoData { + public static final field Companion Lcom/lagradost/cloudstream3/extractors/TRsTX$TrstxVideoData$Companion; public fun ()V public fun (Ljava/lang/String;Ljava/lang/String;)V public synthetic fun (Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V @@ -4184,17 +4824,31 @@ public final class com/lagradost/cloudstream3/extractors/TRsTX$TrstxVideoData { public fun toString ()Ljava/lang/String; } +public final synthetic class com/lagradost/cloudstream3/extractors/TRsTX$TrstxVideoData$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/extractors/TRsTX$TrstxVideoData$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/extractors/TRsTX$TrstxVideoData; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/extractors/TRsTX$TrstxVideoData;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/extractors/TRsTX$TrstxVideoData$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public class com/lagradost/cloudstream3/extractors/Tantifilm : com/lagradost/cloudstream3/utils/ExtractorApi { public fun ()V public fun getMainUrl ()Ljava/lang/String; public fun getName ()Ljava/lang/String; public fun getRequiresReferer ()Z public fun getUrl (Ljava/lang/String;Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public fun setMainUrl (Ljava/lang/String;)V - public fun setName (Ljava/lang/String;)V } public final class com/lagradost/cloudstream3/extractors/Tantifilm$TantifilmData { + public static final field Companion Lcom/lagradost/cloudstream3/extractors/Tantifilm$TantifilmData$Companion; public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V public final fun component1 ()Ljava/lang/String; public final fun component2 ()Ljava/lang/String; @@ -4209,7 +4863,23 @@ public final class com/lagradost/cloudstream3/extractors/Tantifilm$TantifilmData public fun toString ()Ljava/lang/String; } +public final synthetic class com/lagradost/cloudstream3/extractors/Tantifilm$TantifilmData$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/extractors/Tantifilm$TantifilmData$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/extractors/Tantifilm$TantifilmData; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/extractors/Tantifilm$TantifilmData;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/extractors/Tantifilm$TantifilmData$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public final class com/lagradost/cloudstream3/extractors/Tantifilm$TantifilmJsonData { + public static final field Companion Lcom/lagradost/cloudstream3/extractors/Tantifilm$TantifilmJsonData$Companion; public fun (ZLjava/util/List;Ljava/util/List;Z)V public final fun component1 ()Z public final fun component2 ()Ljava/util/List; @@ -4222,10 +4892,25 @@ public final class com/lagradost/cloudstream3/extractors/Tantifilm$TantifilmJson public final fun getData ()Ljava/util/List; public final fun getSuccess ()Z public fun hashCode ()I - public final fun is_vr ()Z + public final fun isVr ()Z public fun toString ()Ljava/lang/String; } +public final synthetic class com/lagradost/cloudstream3/extractors/Tantifilm$TantifilmJsonData$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/extractors/Tantifilm$TantifilmJsonData$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/extractors/Tantifilm$TantifilmJsonData; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/extractors/Tantifilm$TantifilmJsonData;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/extractors/Tantifilm$TantifilmJsonData$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public class com/lagradost/cloudstream3/extractors/TauVideo : com/lagradost/cloudstream3/utils/ExtractorApi { public fun ()V public fun getMainUrl ()Ljava/lang/String; @@ -4235,6 +4920,7 @@ public class com/lagradost/cloudstream3/extractors/TauVideo : com/lagradost/clou } public final class com/lagradost/cloudstream3/extractors/TauVideo$TauVideoData { + public static final field Companion Lcom/lagradost/cloudstream3/extractors/TauVideo$TauVideoData$Companion; public fun (Ljava/lang/String;Ljava/lang/String;)V public final fun component1 ()Ljava/lang/String; public final fun component2 ()Ljava/lang/String; @@ -4247,7 +4933,23 @@ public final class com/lagradost/cloudstream3/extractors/TauVideo$TauVideoData { public fun toString ()Ljava/lang/String; } +public final synthetic class com/lagradost/cloudstream3/extractors/TauVideo$TauVideoData$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/extractors/TauVideo$TauVideoData$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/extractors/TauVideo$TauVideoData; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/extractors/TauVideo$TauVideoData;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/extractors/TauVideo$TauVideoData$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public final class com/lagradost/cloudstream3/extractors/TauVideo$TauVideoUrls { + public static final field Companion Lcom/lagradost/cloudstream3/extractors/TauVideo$TauVideoUrls$Companion; public fun (Ljava/util/List;)V public final fun component1 ()Ljava/util/List; public final fun copy (Ljava/util/List;)Lcom/lagradost/cloudstream3/extractors/TauVideo$TauVideoUrls; @@ -4258,6 +4960,21 @@ public final class com/lagradost/cloudstream3/extractors/TauVideo$TauVideoUrls { public fun toString ()Ljava/lang/String; } +public final synthetic class com/lagradost/cloudstream3/extractors/TauVideo$TauVideoUrls$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/extractors/TauVideo$TauVideoUrls$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/extractors/TauVideo$TauVideoUrls; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/extractors/TauVideo$TauVideoUrls;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/extractors/TauVideo$TauVideoUrls$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public final class com/lagradost/cloudstream3/extractors/Techinmind : com/lagradost/cloudstream3/extractors/GDMirrorbot { public fun ()V public fun getMainUrl ()Ljava/lang/String; @@ -4382,6 +5099,7 @@ public class com/lagradost/cloudstream3/extractors/Uservideo : com/lagradost/clo } public final class com/lagradost/cloudstream3/extractors/Uservideo$Sources { + public static final field Companion Lcom/lagradost/cloudstream3/extractors/Uservideo$Sources$Companion; public fun ()V public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V @@ -4398,6 +5116,21 @@ public final class com/lagradost/cloudstream3/extractors/Uservideo$Sources { public fun toString ()Ljava/lang/String; } +public final synthetic class com/lagradost/cloudstream3/extractors/Uservideo$Sources$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/extractors/Uservideo$Sources$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/extractors/Uservideo$Sources; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/extractors/Uservideo$Sources;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/extractors/Uservideo$Sources$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public class com/lagradost/cloudstream3/extractors/Vicloud : com/lagradost/cloudstream3/utils/ExtractorApi { public fun ()V public fun getMainUrl ()Ljava/lang/String; @@ -4550,6 +5283,7 @@ public class com/lagradost/cloudstream3/extractors/VideoSeyred : com/lagradost/c } public final class com/lagradost/cloudstream3/extractors/VideoSeyred$VSSource { + public static final field Companion Lcom/lagradost/cloudstream3/extractors/VideoSeyred$VSSource$Companion; public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V public final fun component1 ()Ljava/lang/String; public final fun component2 ()Ljava/lang/String; @@ -4564,7 +5298,23 @@ public final class com/lagradost/cloudstream3/extractors/VideoSeyred$VSSource { public fun toString ()Ljava/lang/String; } +public final synthetic class com/lagradost/cloudstream3/extractors/VideoSeyred$VSSource$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/extractors/VideoSeyred$VSSource$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/extractors/VideoSeyred$VSSource; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/extractors/VideoSeyred$VSSource;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/extractors/VideoSeyred$VSSource$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public final class com/lagradost/cloudstream3/extractors/VideoSeyred$VSTrack { + public static final field Companion Lcom/lagradost/cloudstream3/extractors/VideoSeyred$VSTrack$Companion; public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun component1 ()Ljava/lang/String; @@ -4584,7 +5334,23 @@ public final class com/lagradost/cloudstream3/extractors/VideoSeyred$VSTrack { public fun toString ()Ljava/lang/String; } +public final synthetic class com/lagradost/cloudstream3/extractors/VideoSeyred$VSTrack$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/extractors/VideoSeyred$VSTrack$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/extractors/VideoSeyred$VSTrack; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/extractors/VideoSeyred$VSTrack;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/extractors/VideoSeyred$VSTrack$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public final class com/lagradost/cloudstream3/extractors/VideoSeyred$VideoSeyredSource { + public static final field Companion Lcom/lagradost/cloudstream3/extractors/VideoSeyred$VideoSeyredSource$Companion; public fun (Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Ljava/util/List;)V public final fun component1 ()Ljava/lang/String; public final fun component2 ()Ljava/lang/String; @@ -4601,6 +5367,21 @@ public final class com/lagradost/cloudstream3/extractors/VideoSeyred$VideoSeyred public fun toString ()Ljava/lang/String; } +public final synthetic class com/lagradost/cloudstream3/extractors/VideoSeyred$VideoSeyredSource$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/extractors/VideoSeyred$VideoSeyredSource$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/extractors/VideoSeyred$VideoSeyredSource; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/extractors/VideoSeyred$VideoSeyredSource;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/extractors/VideoSeyred$VideoSeyredSource$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public final class com/lagradost/cloudstream3/extractors/Videzz : com/lagradost/cloudstream3/extractors/Vidoza { public fun ()V public fun getMainUrl ()Ljava/lang/String; @@ -4688,8 +5469,6 @@ public final class com/lagradost/cloudstream3/extractors/VinovoSi : com/lagrados public fun ()V public fun getMainUrl ()Ljava/lang/String; public fun getName ()Ljava/lang/String; - public fun setMainUrl (Ljava/lang/String;)V - public fun setName (Ljava/lang/String;)V } public class com/lagradost/cloudstream3/extractors/VinovoTo : com/lagradost/cloudstream3/utils/ExtractorApi { @@ -4786,7 +5565,6 @@ public class com/lagradost/cloudstream3/extractors/XStreamCdn : com/lagradost/cl public fun getName ()Ljava/lang/String; public fun getRequiresReferer ()Z public fun getUrl (Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public fun setDomainUrl (Ljava/lang/String;)V } public final class com/lagradost/cloudstream3/extractors/Xenolyzb : com/lagradost/cloudstream3/extractors/VidHidePro { diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Acefile.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Acefile.kt index a86c5f01a..5b214c5dd 100644 --- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Acefile.kt +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Acefile.kt @@ -1,8 +1,11 @@ package com.lagradost.cloudstream3.extractors +import com.fasterxml.jackson.annotation.JsonProperty import com.lagradost.cloudstream3.SubtitleFile import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.utils.* +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable open class Acefile : ExtractorApi() { override val name = "Acefile" @@ -13,7 +16,7 @@ open class Acefile : ExtractorApi() { url: String, referer: String?, subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit + callback: (ExtractorLink) -> Unit, ) { val id = "/(?:d|download|player|f|file)/(\\w+)".toRegex().find(url)?.groupValues?.get(1) val script = getAndUnpack(app.get("$mainUrl/player/${id ?: return}").text) @@ -27,14 +30,14 @@ open class Acefile : ExtractorApi() { newExtractorLink( this.name, this.name, - video ?: return + video ?: return, ) ) } + @Serializable data class Source( - val data: String? = null, + @JsonProperty("data") @SerialName("data") val data: String? = null, ) - -} \ No newline at end of file +} diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Blogger.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Blogger.kt index a9f4a3eb9..e00ffcb57 100644 --- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Blogger.kt +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Blogger.kt @@ -4,6 +4,8 @@ import com.fasterxml.jackson.annotation.JsonProperty import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.utils.* import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable open class Blogger : ExtractorApi() { override val name = "Blogger" @@ -22,10 +24,10 @@ open class Blogger : ExtractorApi() { newExtractorLink( name, name, - it.play_url, + it.playUrl, ) { this.referer = "https://www.youtube.com/" - this.quality = when (it.format_id) { + this.quality = when (it.formatId) { 18 -> 360 22 -> 720 else -> Qualities.Unknown.value @@ -36,11 +38,13 @@ open class Blogger : ExtractorApi() { } } } + return sources } + @Serializable private data class ResponseSource( - @JsonProperty("play_url") val play_url: String, - @JsonProperty("format_id") val format_id: Int + @JsonProperty("play_url") @SerialName("play_url") val playUrl: String, + @JsonProperty("format_id") @SerialName("format_id") val formatId: Int, ) -} \ No newline at end of file +} diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/ByseSX.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/ByseSX.kt index 5248a8861..4781474d6 100644 --- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/ByseSX.kt +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/ByseSX.kt @@ -13,30 +13,32 @@ import dev.whyoleg.cryptography.DelicateCryptographyApi import dev.whyoleg.cryptography.algorithms.AES import io.ktor.http.Url import io.ktor.http.decodeURLPart +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable class Bysezejataos : ByseSX() { - override var name = "Bysezejataos" - override var mainUrl = "https://bysezejataos.com" + override val name = "Bysezejataos" + override val mainUrl = "https://bysezejataos.com" } class ByseBuho : ByseSX() { - override var name = "ByseBuho" - override var mainUrl = "https://bysebuho.com" + override val name = "ByseBuho" + override val mainUrl = "https://bysebuho.com" } class ByseVepoin : ByseSX() { - override var name = "ByseVepoin" - override var mainUrl = "https://bysevepoin.com" + override val name = "ByseVepoin" + override val mainUrl = "https://bysevepoin.com" } class ByseQekaho : ByseSX() { - override var name = "ByseQekaho" - override var mainUrl = "https://byseqekaho.com" + override val name = "ByseQekaho" + override val mainUrl = "https://byseqekaho.com" } open class ByseSX : ExtractorApi() { - override var name = "Byse" - override var mainUrl = "https://byse.sx" + override val name = "Byse" + override val mainUrl = "https://byse.sx" override val requiresReferer = true private val aesGcm = CryptographyProvider.Default.get(AES.GCM) @@ -112,7 +114,7 @@ open class ByseSX : ExtractorApi() { url: String, referer: String?, subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit + callback: (ExtractorLink) -> Unit, ) { val refererUrl = getBaseUrl(url) val playbackRoot = getPlayback(url) ?: return @@ -123,64 +125,58 @@ open class ByseSX : ExtractorApi() { name, streamUrl, mainUrl, - headers = headers + headers = headers, ).forEach(callback) } } +@Serializable data class DetailsRoot( - val id: Long, - val code: String, - val title: String, - @JsonProperty("poster_url") - val posterUrl: String, - val description: String, - @JsonProperty("created_at") - val createdAt: String, - @JsonProperty("owner_private") - val ownerPrivate: Boolean, - @JsonProperty("embed_frame_url") - val embedFrameUrl: String, + @JsonProperty("id") @SerialName("id") val id: Long, + @JsonProperty("code") @SerialName("code") val code: String, + @JsonProperty("title") @SerialName("title") val title: String, + @JsonProperty("poster_url") @SerialName("poster_url") val posterUrl: String, + @JsonProperty("description") @SerialName("description") val description: String, + @JsonProperty("created_at") @SerialName("created_at") val createdAt: String, + @JsonProperty("owner_private") @SerialName("owner_private") val ownerPrivate: Boolean, + @JsonProperty("embed_frame_url") @SerialName("embed_frame_url") val embedFrameUrl: String, ) +@Serializable data class PlaybackRoot( - val playback: Playback, + @JsonProperty("playback") @SerialName("playback") val playback: Playback, ) +@Serializable data class Playback( - val algorithm: String, - val iv: String, - val payload: String, - @JsonProperty("key_parts") - val keyParts: List, - @JsonProperty("expires_at") - val expiresAt: String, - @JsonProperty("decrypt_keys") - val decryptKeys: DecryptKeys, - val iv2: String, - val payload2: String, + @JsonProperty("algorithm") @SerialName("algorithm") val algorithm: String, + @JsonProperty("iv") @SerialName("iv") val iv: String, + @JsonProperty("payload") @SerialName("payload") val payload: String, + @JsonProperty("key_parts") @SerialName("key_parts") val keyParts: List, + @JsonProperty("expires_at") @SerialName("expires_at") val expiresAt: String, + @JsonProperty("decrypt_keys") @SerialName("decrypt_keys") val decryptKeys: DecryptKeys, + @JsonProperty("iv2") @SerialName("iv2") val iv2: String, + @JsonProperty("payload2") @SerialName("payload2") val payload2: String, ) +@Serializable data class DecryptKeys( - @JsonProperty("edge_1") - val edge1: String, - @JsonProperty("edge_2") - val edge2: String, - @JsonProperty("legacy_fallback") - val legacyFallback: String, + @JsonProperty("edge_1") @SerialName("edge_1") val edge1: String, + @JsonProperty("edge_2") @SerialName("edge_2") val edge2: String, + @JsonProperty("legacy_fallback") @SerialName("legacy_fallback") val legacyFallback: String, ) +@Serializable data class PlaybackDecrypt( - val sources: List, + @JsonProperty("sources") @SerialName("sources") val sources: List, ) +@Serializable data class PlaybackDecryptSource( - val quality: String, - val label: String, - @JsonProperty("mime_type") - val mimeType: String, - val url: String, - @JsonProperty("bitrate_kbps") - val bitrateKbps: Long, - val height: Any?, + @JsonProperty("quality") @SerialName("quality") val quality: String, + @JsonProperty("label") @SerialName("label") val label: String, + @JsonProperty("mime_type") @SerialName("mime_type") val mimeType: String, + @JsonProperty("url") @SerialName("url") val url: String, + @JsonProperty("bitrate_kbps") @SerialName("bitrate_kbps") val bitrateKbps: Long, + @JsonProperty("height") @SerialName("height") val height: Int?, ) diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Cda.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Cda.kt index 5c9f58efc..3b5189f0e 100644 --- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Cda.kt +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Cda.kt @@ -1,5 +1,6 @@ package com.lagradost.cloudstream3.extractors +import com.fasterxml.jackson.annotation.JsonProperty import com.lagradost.cloudstream3.USER_AGENT import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson @@ -8,6 +9,8 @@ import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.Qualities import com.lagradost.cloudstream3.utils.StringUtils.decodeUrl import com.lagradost.cloudstream3.utils.newExtractorLink +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable open class Cda : ExtractorApi() { override var mainUrl = "https://ebd.cda.pl" @@ -22,7 +25,7 @@ open class Cda : ExtractorApi() { "https://ebd.cda.pl/647x500/$mediaId", headers = mapOf( "Referer" to "https://ebd.cda.pl/647x500/$mediaId", "User-Agent" to USER_AGENT, - "Cookie" to "cda.player=html5" + "Cookie" to "cda.player=html5", ) ).document val dataRaw = doc.selectFirst("[player_data]")?.attr("player_data") ?: return null @@ -86,15 +89,17 @@ open class Cda : ExtractorApi() { else -> a } + @Serializable data class VideoPlayerData( - val file: String, - val qualities: Map = mapOf(), - val quality: String?, - val ts: Int?, - val hash2: String? + @JsonProperty("file") @SerialName("file") val file: String, + @JsonProperty("qualities") @SerialName("qualities") val qualities: Map = mapOf(), + @JsonProperty("quality") @SerialName("quality") val quality: String?, + @JsonProperty("ts") @SerialName("ts") val ts: Int?, + @JsonProperty("hash2") @SerialName("hash2") val hash2: String?, ) + @Serializable data class PlayerData( - val video: VideoPlayerData + @JsonProperty("video") @SerialName("video") val video: VideoPlayerData, ) -} \ No newline at end of file +} diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Firestream.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Firestream.kt index c2fbaeee2..f1d33e853 100644 --- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Firestream.kt +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Firestream.kt @@ -1,5 +1,6 @@ package com.lagradost.cloudstream3.extractors +import com.fasterxml.jackson.annotation.JsonProperty import com.lagradost.cloudstream3.Prerelease import com.lagradost.cloudstream3.SubtitleFile import com.lagradost.cloudstream3.app @@ -10,31 +11,29 @@ import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable class Firestream : ExtractorApi() { - override val name: String = "Firestream" - override val mainUrl: String = "https://firestream.to" - override val requiresReferer: Boolean = false + override val name = "Firestream" + override val mainUrl = "https://firestream.to" + override val requiresReferer = false override suspend fun getUrl( url: String, referer: String?, subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit + callback: (ExtractorLink) -> Unit, ) { val id = url.removeSuffix("/").substringAfterLast("/") val url = getExtractorUrl(id) val doc = app.get(url).document val token = doc.selectFirst("script[id=token-blob]")!!.data() - - val videoResponse = - app.post("$mainUrl/api/videos/$id/resolve", json = mapOf("blob" to token)) - .parsed() + val videoResponse = app.post("$mainUrl/api/videos/$id/resolve", json = mapOf("blob" to token)) + .parsed() callback.invoke( newExtractorLink( source = name, name = name, - url = videoResponse.signedVideoUrl + url = videoResponse.signedVideoUrl, ) ) } @@ -45,7 +44,6 @@ class Firestream : ExtractorApi() { @Serializable private data class VideoResponse( - @SerialName("signedVideoUrl") - val signedVideoUrl: String, + @JsonProperty("signedVideoUrl") @SerialName("signedVideoUrl") val signedVideoUrl: String, ) -} \ No newline at end of file +} diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Flyfile.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Flyfile.kt index 50042bd19..b58827652 100644 --- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Flyfile.kt +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Flyfile.kt @@ -11,16 +11,16 @@ import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable open class Flyfile : ExtractorApi() { - override val name: String = "FlyFile" - override val mainUrl: String = "https://flyfile.app" + override val name = "FlyFile" + override val mainUrl = "https://flyfile.app" + override val requiresReferer = false open val apiUrl: String = "https://api.flyfile.app" - override val requiresReferer: Boolean = false override suspend fun getUrl( url: String, referer: String?, subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit + callback: (ExtractorLink) -> Unit, ) { val videoId = url.substringAfterLast("/") val videoInfo = app.get("$apiUrl/api/streaming/assign/$videoId") @@ -32,16 +32,14 @@ open class Flyfile : ExtractorApi() { source = name, name = name, url = streamUrl, - type = ExtractorLinkType.M3U8 + type = ExtractorLinkType.M3U8, ) ) } @Serializable private data class StreamInfo( - @SerialName("url") - val url: String, - @SerialName("token") - val token: String + @SerialName("url") val url: String, + @SerialName("token") val token: String, ) -} \ No newline at end of file +} diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/GUpload.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/GUpload.kt index 1203b2167..8fb0ae046 100644 --- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/GUpload.kt +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/GUpload.kt @@ -4,11 +4,14 @@ import com.fasterxml.jackson.annotation.JsonProperty import com.lagradost.cloudstream3.SubtitleFile import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.base64Decode -import com.lagradost.cloudstream3.utils.AppUtils +import com.lagradost.cloudstream3.utils.AppUtils.parseJson import com.lagradost.cloudstream3.utils.ExtractorApi import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.getQualityFromName import com.lagradost.cloudstream3.utils.newExtractorLink +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import kotlinx.serialization.json.JsonElement open class GUpload: ExtractorApi() { override val name: String = "GUpload" @@ -19,12 +22,12 @@ open class GUpload: ExtractorApi() { url: String, referer: String?, subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit + callback: (ExtractorLink) -> Unit, ) { val response = app.get(url, referer = referer).text val playerConfigString = response.substringAfter("const config = ").substringBefore(";") - val playerConfig = AppUtils.parseJson(playerConfigString) + val playerConfig = parseJson(playerConfigString) callback.invoke( newExtractorLink( @@ -39,14 +42,15 @@ open class GUpload: ExtractorApi() { ) } + @Serializable private data class VideoInfo( - @JsonProperty("videoUrl") val videoUrl: String, - @JsonProperty("posterUrl") val posterUrl: String? = null, - @JsonProperty("videoId") val videoId: String? = null, - @JsonProperty("primaryColor") val primaryColor: String? = null, - @JsonProperty("audioTracks") val audioTracks: List = emptyList(), - @JsonProperty("subtitleTracks") val subtitleTracks: List = emptyList(), - @JsonProperty("vastFallbackList") val vastFallbackList: List = emptyList(), - @JsonProperty("videoOwnerId") val videoOwnerId: Long = 0, + @JsonProperty("videoUrl") @SerialName("videoUrl") val videoUrl: String, + @JsonProperty("posterUrl") @SerialName("posterUrl") val posterUrl: String? = null, + @JsonProperty("videoId") @SerialName("videoId") val videoId: String? = null, + @JsonProperty("primaryColor") @SerialName("primaryColor") val primaryColor: String? = null, + @JsonProperty("audioTracks") @SerialName("audioTracks") val audioTracks: List = emptyList(), + @JsonProperty("subtitleTracks") @SerialName("subtitleTracks") val subtitleTracks: List = emptyList(), + @JsonProperty("vastFallbackList") @SerialName("vastFallbackList") val vastFallbackList: List = emptyList(), + @JsonProperty("videoOwnerId") @SerialName("videoOwnerId") val videoOwnerId: Long = 0, ) -} \ No newline at end of file +} diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Gdriveplayer.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Gdriveplayer.kt index f484d766c..10abcce31 100644 --- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Gdriveplayer.kt +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Gdriveplayer.kt @@ -5,14 +5,16 @@ import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.extractors.helper.AesHelper.cryptoAESHandler import com.lagradost.cloudstream3.utils.* import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable import org.jsoup.nodes.Element class DatabaseGdrive2 : Gdriveplayer() { - override var mainUrl = "https://databasegdriveplayer.co" + override val mainUrl = "https://databasegdriveplayer.co" } class DatabaseGdrive : Gdriveplayer() { - override var mainUrl = "https://series.databasegdriveplayer.co" + override val mainUrl = "https://series.databasegdriveplayer.co" } class Gdriveplayerapi : Gdriveplayer() { @@ -73,10 +75,9 @@ open class Gdriveplayer : ExtractorApi() { url: String, referer: String?, subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit + callback: (ExtractorLink) -> Unit, ) { val document = app.get(url).document - val eval = unpackJs(document)?.replace("\\", "") ?: return val data = Regex("data='(\\S+?)'").first(eval) ?: return val password = Regex("null,['|\"](\\w+)['|\"]").first(eval) @@ -84,9 +85,9 @@ open class Gdriveplayer : ExtractorApi() { ?.joinToString("") { it.toInt().toChar().toString() }.let { Regex("var pass = \"(\\S+?)\"").first(it ?: return)?.encodeToByteArray() } - ?: throw ErrorLoadingException("can't find password") - val decryptedData = cryptoAESHandler(data, password, false, false)?.let { getAndUnpack(it) }?.replace("\\", "") + ?: throw ErrorLoadingException("can't find password") + val decryptedData = cryptoAESHandler(data, password, false, false)?.let { getAndUnpack(it) }?.replace("\\", "") val sourceData = decryptedData?.substringAfter("sources:[")?.substringBefore("],") val subData = decryptedData?.substringAfter("tracks:[")?.substringBefore("],") @@ -111,16 +112,17 @@ open class Gdriveplayer : ExtractorApi() { subtitleCallback.invoke( newSubtitleFile( sub.label, - httpsify(sub.file) + httpsify(sub.file), ) ) } } } + @Serializable data class Tracks( - @JsonProperty("file") val file: String, - @JsonProperty("kind") val kind: String, - @JsonProperty("label") val label: String, + @JsonProperty("file") @SerialName("file") val file: String, + @JsonProperty("kind") @SerialName("kind") val kind: String, + @JsonProperty("label") @SerialName("label") val label: String, ) } diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Gofile.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Gofile.kt index 2b42e42c7..9edc54a41 100644 --- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Gofile.kt +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Gofile.kt @@ -8,6 +8,8 @@ import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.ExtractorLinkType import com.lagradost.cloudstream3.utils.Qualities import com.lagradost.cloudstream3.utils.newExtractorLink +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable import kotlin.math.round open class Gofile : ExtractorApi() { @@ -20,20 +22,18 @@ open class Gofile : ExtractorApi() { url: String, referer: String?, subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit + callback: (ExtractorLink) -> Unit, ) { val id = Regex("/(?:\\?c=|d/)([\\da-zA-Z-]+)").find(url)?.groupValues?.get(1) ?: return - val token = app.post( "$mainApi/accounts", ).parsedSafe()?.data?.token ?: return val globalRes = app.get("$mainUrl/dist/js/config.js").text val wt = Regex("""appdata\.wt\s*=\s*[\"']([^\"']+)[\"']""").find(globalRes)?.groupValues?.get(1) ?: return - val headers = mapOf( "Authorization" to "Bearer $token", - "X-Website-Token" to wt + "X-Website-Token" to wt, ) val parsedResponse = app.get( @@ -42,7 +42,6 @@ open class Gofile : ExtractorApi() { ).parsedSafe() val childrenMap = parsedResponse?.data?.children ?: return - for ((_, file) in childrenMap) { if (file.link.isNullOrEmpty() || file.type != "file") continue val fileName = file.name ?: "" @@ -54,7 +53,7 @@ open class Gofile : ExtractorApi() { "Gofile", "[Gofile] $fileName [$formattedSize]", file.link, - ExtractorLinkType.VIDEO + ExtractorLinkType.VIDEO, ) { this.quality = getQuality(fileName) this.headers = mapOf("Cookie" to "accountToken=$token") @@ -84,26 +83,31 @@ open class Gofile : ExtractorApi() { } } + @Serializable data class AccountResponse( - @JsonProperty("data") val data: AccountData? = null + @JsonProperty("data") @SerialName("data") val data: AccountData? = null, ) + @Serializable data class AccountData( - @JsonProperty("token") val token: String? = null + @JsonProperty("token") @SerialName("token") val token: String? = null, ) + @Serializable data class GofileResponse( - @JsonProperty("data") val data: GofileData? = null + @JsonProperty("data") @SerialName("data") val data: GofileData? = null, ) + @Serializable data class GofileData( - @JsonProperty("children") val children: Map? = null + @JsonProperty("children") @SerialName("children") val children: Map? = null, ) + @Serializable data class GofileFile( - @JsonProperty("type") val type: String? = null, - @JsonProperty("name") val name: String? = null, - @JsonProperty("link") val link: String? = null, - @JsonProperty("size") val size: Long? = 0L + @JsonProperty("type") @SerialName("type") val type: String? = null, + @JsonProperty("name") @SerialName("name") val name: String? = null, + @JsonProperty("link") @SerialName("link") val link: String? = null, + @JsonProperty("size") @SerialName("size") val size: Long? = 0L, ) } diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/HDMomPlayerExtractor.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/HDMomPlayerExtractor.kt index 1ccd3e4d5..f7fa2bef1 100644 --- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/HDMomPlayerExtractor.kt +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/HDMomPlayerExtractor.kt @@ -8,39 +8,41 @@ import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.extractors.helper.AesHelper import com.lagradost.cloudstream3.utils.* import com.lagradost.cloudstream3.utils.AppUtils.parseJson +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable open class HDMomPlayer : ExtractorApi() { - override val name = "HDMomPlayer" - override val mainUrl = "https://hdmomplayer.com" + override val name = "HDMomPlayer" + override val mainUrl = "https://hdmomplayer.com" override val requiresReferer = true - override suspend fun getUrl(url: String, referer: String?, subtitleCallback: (SubtitleFile) -> Unit, callback: (ExtractorLink) -> Unit) { - val m3uLink:String? - val extRef = referer ?: "" - val iSource = app.get(url, referer=extRef).text - + override suspend fun getUrl( + url: String, + referer: String?, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit, + ) { + val m3uLink: String? + val extRef = referer ?: "" + val iSource = app.get(url, referer = extRef).text val bePlayer = Regex("""bePlayer\('([^']+)',\s*'(\{[^\}]+\})'\);""").find(iSource)?.groupValues if (bePlayer != null) { val bePlayerPass = bePlayer.get(1) val bePlayerData = bePlayer.get(2) - val encrypted = AesHelper.cryptoAESHandler(bePlayerData, bePlayerPass.encodeToByteArray(), false)?.replace("\\", "") ?: throw ErrorLoadingException("failed to decrypt") - + val encrypted = AesHelper.cryptoAESHandler(bePlayerData, bePlayerPass.encodeToByteArray(), false)?.replace("\\", "") ?: throw ErrorLoadingException("failed to decrypt") m3uLink = Regex("""video_location\":\"([^\"]+)""").find(encrypted)?.groupValues?.get(1) } else { m3uLink = Regex("""file:\"([^\"]+)""").find(iSource)?.groupValues?.get(1) - val trackStr = Regex("""tracks:\[([^\]]+)""").find(iSource)?.groupValues?.get(1) if (trackStr != null) { - val tracks:List = parseJson>("[${trackStr}]") - + val tracks = parseJson>("[${trackStr}]") for (track in tracks) { if (track.file == null || track.label == null) continue if (track.label.contains("Forced")) continue - subtitleCallback.invoke( newSubtitleFile( lang = track.label, - url = fixUrl(mainUrl + track.file) + url = fixUrl(mainUrl + track.file), ) ) } @@ -49,10 +51,10 @@ open class HDMomPlayer : ExtractorApi() { callback.invoke( newExtractorLink( - source = this.name, - name = this.name, - url = m3uLink ?: throw ErrorLoadingException("m3u link not found"), - type = ExtractorLinkType.M3U8 + source = this.name, + name = this.name, + url = m3uLink ?: throw ErrorLoadingException("m3u link not found"), + type = ExtractorLinkType.M3U8, ) { this.referer = url this.quality = Qualities.Unknown.value @@ -60,11 +62,12 @@ open class HDMomPlayer : ExtractorApi() { ) } + @Serializable data class Track( - @JsonProperty("file") val file: String?, - @JsonProperty("label") val label: String?, - @JsonProperty("kind") val kind: String?, - @JsonProperty("language") val language: String?, - @JsonProperty("default") val default: String? + @JsonProperty("file") @SerialName("file") val file: String?, + @JsonProperty("label") @SerialName("label") val label: String?, + @JsonProperty("kind") @SerialName("kind") val kind: String?, + @JsonProperty("language") @SerialName("language") val language: String?, + @JsonProperty("default") @SerialName("default") val default: String?, ) } diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/HDPlayerSystemExtractor.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/HDPlayerSystemExtractor.kt index 9cff2049f..a991528da 100644 --- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/HDPlayerSystemExtractor.kt +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/HDPlayerSystemExtractor.kt @@ -2,56 +2,60 @@ package com.lagradost.cloudstream3.extractors +import com.fasterxml.jackson.annotation.JsonProperty import com.lagradost.api.Log import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.utils.* -import com.fasterxml.jackson.annotation.JsonProperty +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable open class HDPlayerSystem : ExtractorApi() { - override val name = "HDPlayerSystem" - override val mainUrl = "https://hdplayersystem.live" + override val name = "HDPlayerSystem" + override val mainUrl = "https://hdplayersystem.live" override val requiresReferer = true - override suspend fun getUrl(url: String, referer: String?, subtitleCallback: (SubtitleFile) -> Unit, callback: (ExtractorLink) -> Unit) { - val extRef = referer ?: "" - val vidId = if (url.contains("video/")) { + override suspend fun getUrl( + url: String, + referer: String?, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit, + ) { + val extRef = referer ?: "" + val vidId = if (url.contains("video/")) { url.substringAfter("video/") } else { url.substringAfter("?data=") } - val postUrl = "${mainUrl}/player/index.php?data=${vidId}&do=getVideo" - + val postUrl = "$mainUrl/player/index.php?data=$vidId&do=getVideo" val response = app.post( postUrl, data = mapOf( "hash" to vidId, - "r" to extRef + "r" to extRef, ), referer = extRef, headers = mapOf( - "Content-Type" to "application/x-www-form-urlencoded; charset=UTF-8", - "X-Requested-With" to "XMLHttpRequest" + "Content-Type" to "application/x-www-form-urlencoded; charset=UTF-8", + "X-Requested-With" to "XMLHttpRequest", ) ) val videoResponse = response.parsedSafe() ?: throw ErrorLoadingException("failed to parse response") - val m3uLink = videoResponse.securedLink - + val m3uLink = videoResponse.securedLink callback.invoke( newExtractorLink( - source = this.name, - name = this.name, - url = m3uLink - ) { - this.referer = extRef - } + source = this.name, + name = this.name, + url = m3uLink, + ) { this.referer = extRef } ) } + @Serializable data class SystemResponse( - @JsonProperty("hls") val hls: String, - @JsonProperty("videoImage") val videoImage: String? = null, - @JsonProperty("videoSource") val videoSource: String, - @JsonProperty("securedLink") val securedLink: String + @JsonProperty("hls") @SerialName("hls") val hls: String, + @JsonProperty("videoImage") @SerialName("videoImage") val videoImage: String? = null, + @JsonProperty("videoSource") @SerialName("videoSource") val videoSource: String, + @JsonProperty("securedLink") @SerialName("securedLink") val securedLink: String, ) -} \ No newline at end of file +} diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Jeniusplay.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Jeniusplay.kt index 896228b51..9f66867f6 100644 --- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Jeniusplay.kt +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Jeniusplay.kt @@ -9,6 +9,8 @@ import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.M3u8Helper import com.lagradost.cloudstream3.utils.getAndUnpack import com.lagradost.cloudstream3.utils.getPacked +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable open class Jeniusplay : ExtractorApi() { override val name = "Jeniusplay" @@ -19,16 +21,15 @@ open class Jeniusplay : ExtractorApi() { url: String, referer: String?, subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit + callback: (ExtractorLink) -> Unit, ) { val document = app.get(url, referer = "$mainUrl/").document val hash = url.split("/").last().substringAfter("data=") - val m3uLink = app.post( url = "$mainUrl/player/index.php?data=$hash&do=getVideo", data = mapOf("hash" to hash, "r" to "$referer"), referer = url, - headers = mapOf("X-Requested-With" to "XMLHttpRequest") + headers = mapOf("X-Requested-With" to "XMLHttpRequest"), ).parsed().videoSource M3u8Helper.generateM3u8( @@ -36,7 +37,6 @@ open class Jeniusplay : ExtractorApi() { m3uLink, url, ).forEach(callback) - document.select("script").map { script -> if (getPacked(script.data()) != null) { val unpacked = getAndUnpack(script.data()) @@ -45,9 +45,10 @@ open class Jeniusplay : ExtractorApi() { } } + @Serializable data class ResponseSource( - @JsonProperty("hls") val hls: Boolean, - @JsonProperty("videoSource") val videoSource: String, - @JsonProperty("securedLink") val securedLink: String?, + @JsonProperty("hls") @SerialName("hls") val hls: Boolean, + @JsonProperty("videoSource") @SerialName("videoSource") val videoSource: String, + @JsonProperty("securedLink") @SerialName("securedLink") val securedLink: String?, ) -} \ No newline at end of file +} diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Linkbox.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Linkbox.kt index bfa94326a..91b34ac2c 100644 --- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Linkbox.kt +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Linkbox.kt @@ -7,6 +7,8 @@ import com.lagradost.cloudstream3.utils.ExtractorApi import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.getQualityFromName import com.lagradost.cloudstream3.utils.newExtractorLink +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable open class Linkbox : ExtractorApi() { override val name = "Linkbox" @@ -17,7 +19,7 @@ open class Linkbox : ExtractorApi() { url: String, referer: String?, subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit + callback: (ExtractorLink) -> Unit, ) { val token = Regex("""(?:/f/|/file/|\?id=)(\w+)""").find(url)?.groupValues?.get(1) val id = app.get("$mainUrl/api/file/share_out_list/?sortField=utime&sortAsc=0&pageNo=1&pageSize=50&shareToken=$token").parsedSafe()?.data?.itemId @@ -36,22 +38,25 @@ open class Linkbox : ExtractorApi() { } } + @Serializable data class Resolutions( - @JsonProperty("url") val url: String? = null, - @JsonProperty("resolution") val resolution: String? = null, + @JsonProperty("url") @SerialName("url") val url: String? = null, + @JsonProperty("resolution") @SerialName("resolution") val resolution: String? = null, ) + @Serializable data class ItemInfo( - @JsonProperty("resolutionList") val resolutionList: ArrayList? = arrayListOf(), + @JsonProperty("resolutionList") @SerialName("resolutionList") val resolutionList: ArrayList? = arrayListOf(), ) + @Serializable data class Data( - @JsonProperty("itemInfo") val itemInfo: ItemInfo? = null, - @JsonProperty("itemId") val itemId: String? = null, + @JsonProperty("itemInfo") @SerialName("itemInfo") val itemInfo: ItemInfo? = null, + @JsonProperty("itemId") @SerialName("itemId") val itemId: String? = null, ) + @Serializable data class Responses( - @JsonProperty("data") val data: Data? = null, + @JsonProperty("data") @SerialName("data") val data: Data? = null, ) - -} \ No newline at end of file +} diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/MailRuExtractor.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/MailRuExtractor.kt index f1fd1288c..899c6ced5 100644 --- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/MailRuExtractor.kt +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/MailRuExtractor.kt @@ -2,51 +2,57 @@ package com.lagradost.cloudstream3.extractors +import com.fasterxml.jackson.annotation.JsonProperty import com.lagradost.api.Log import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.utils.* -import com.fasterxml.jackson.annotation.JsonProperty +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable open class MailRu : ExtractorApi() { - override val name = "MailRu" - override val mainUrl = "https://my.mail.ru" + override val name = "MailRu" + override val mainUrl = "https://my.mail.ru" override val requiresReferer = false - override suspend fun getUrl(url: String, referer: String?, subtitleCallback: (SubtitleFile) -> Unit, callback: (ExtractorLink) -> Unit) { + override suspend fun getUrl( + url: String, + referer: String?, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit, + ) { val extRef = referer ?: "" - - val vidId = url.substringAfter("video/embed/").trim() - val videoReq = app.get("${mainUrl}/+/video/meta/${vidId}", referer=url) - val videoKey = videoReq.cookies["video_key"].toString() - - val videoData = AppUtils.tryParseJson(videoReq.text) ?: throw ErrorLoadingException("Video not found") + val vidId = url.substringAfter("video/embed/").trim() + val videoReq = app.get("$mainUrl/+/video/meta/$vidId", referer = url) + val videoKey = videoReq.cookies["video_key"].toString() + val videoData = AppUtils.tryParseJson(videoReq.text) ?: + throw ErrorLoadingException("Video not found") for (video in videoData.videos) { - val videoUrl = if (video.url.startsWith("//")) "https:${video.url}" else video.url - callback.invoke( newExtractorLink( - source = this.name, - name = this.name, - url = videoUrl, - type = ExtractorLinkType.M3U8 + source = this.name, + name = this.name, + url = videoUrl, + type = ExtractorLinkType.M3U8, ) { this.referer = url - this.headers = mapOf("Cookie" to "video_key=${videoKey}") + this.headers = mapOf("Cookie" to "video_key=$videoKey") this.quality = getQualityFromName(video.key) } ) } } + @Serializable data class MailRuData( - @JsonProperty("provider") val provider: String, - @JsonProperty("videos") val videos: List + @JsonProperty("provider") @SerialName("provider") val provider: String, + @JsonProperty("videos") @SerialName("videos") val videos: List, ) + @Serializable data class MailRuVideoData( - @JsonProperty("url") val url: String, - @JsonProperty("key") val key: String + @JsonProperty("url") @SerialName("url") val url: String, + @JsonProperty("key") @SerialName("key") val key: String, ) } diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/OdnoklassnikiExtractor.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/OdnoklassnikiExtractor.kt index 69c3b7759..fd845e1a0 100644 --- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/OdnoklassnikiExtractor.kt +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/OdnoklassnikiExtractor.kt @@ -7,19 +7,26 @@ import com.lagradost.cloudstream3.ErrorLoadingException import com.lagradost.cloudstream3.SubtitleFile import com.lagradost.cloudstream3.USER_AGENT import com.lagradost.cloudstream3.app -import com.lagradost.cloudstream3.utils.AppUtils +import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson import com.lagradost.cloudstream3.utils.ExtractorApi import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.INFER_TYPE import com.lagradost.cloudstream3.utils.getQualityFromName import com.lagradost.cloudstream3.utils.newExtractorLink +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable open class Odnoklassniki : ExtractorApi() { - override val name = "Odnoklassniki" - override val mainUrl = "https://odnoklassniki.ru" + override val name = "Odnoklassniki" + override val mainUrl = "https://odnoklassniki.ru" override val requiresReferer = false - override suspend fun getUrl(url: String, referer: String?, subtitleCallback: (SubtitleFile) -> Unit, callback: (ExtractorLink) -> Unit) { + override suspend fun getUrl( + url: String, + referer: String?, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit, + ) { val headers = mapOf( "Accept" to "*/*", "Connection" to "keep-alive", @@ -29,34 +36,33 @@ open class Odnoklassniki : ExtractorApi() { "Origin" to mainUrl, "User-Agent" to USER_AGENT, ) - val embedUrl = url.replace("/video/","/videoembed/") - val videoReq = app.get(embedUrl, headers=headers).text.replace("\\"", "\"").replace("\\\\", "\\") + + val embedUrl = url.replace("/video/", "/videoembed/") + val videoReq = app.get(embedUrl, headers = headers).text.replace("\\"", "\"").replace("\\\\", "\\") .replace(Regex("\\\\u([0-9A-Fa-f]{4})")) { matchResult -> matchResult.groupValues[1].toInt(16).toChar().toString() } + val videosStr = Regex(""""videos":(\[[^]]*])""").find(videoReq)?.groupValues?.get(1) ?: throw ErrorLoadingException("Video not found") - val videos = AppUtils.tryParseJson>(videosStr) ?: throw ErrorLoadingException("Video not found") - + val videos = tryParseJson>(videosStr) ?: throw ErrorLoadingException("Video not found") for (video in videos) { - - val videoUrl = if (video.url.startsWith("//")) "https:${video.url}" else video.url - - val quality = video.name.uppercase() + val videoUrl = if (video.url.startsWith("//")) "https:${video.url}" else video.url + val quality = video.name.uppercase() .replace("MOBILE", "144p") .replace("LOWEST", "240p") - .replace("LOW", "360p") - .replace("SD", "480p") - .replace("HD", "720p") - .replace("FULL", "1080p") - .replace("QUAD", "1440p") - .replace("ULTRA", "4k") + .replace("LOW", "360p") + .replace("SD", "480p") + .replace("HD", "720p") + .replace("FULL", "1080p") + .replace("QUAD", "1440p") + .replace("ULTRA", "4k") callback.invoke( newExtractorLink( - source = this.name, - name = this.name, - url = videoUrl, - type = INFER_TYPE + source = this.name, + name = this.name, + url = videoUrl, + type = INFER_TYPE, ) { this.referer = "$mainUrl/" this.quality = getQualityFromName(quality) @@ -66,8 +72,9 @@ open class Odnoklassniki : ExtractorApi() { } } + @Serializable data class OkRuVideo( - @JsonProperty("name") val name: String, - @JsonProperty("url") val url: String, + @JsonProperty("name") @SerialName("name") val name: String, + @JsonProperty("url") @SerialName("url") val url: String, ) } diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/PeaceMakerstExtractor.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/PeaceMakerstExtractor.kt index f96387d75..7dd12d690 100644 --- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/PeaceMakerstExtractor.kt +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/PeaceMakerstExtractor.kt @@ -2,57 +2,63 @@ package com.lagradost.cloudstream3.extractors +import com.fasterxml.jackson.annotation.JsonProperty import com.lagradost.api.Log import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.utils.* -import com.fasterxml.jackson.annotation.JsonProperty +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable open class PeaceMakerst : ExtractorApi() { - override val name = "PeaceMakerst" - override val mainUrl = "https://peacemakerst.com" + override val name = "PeaceMakerst" + override val mainUrl = "https://peacemakerst.com" override val requiresReferer = true - override suspend fun getUrl(url: String, referer: String?, subtitleCallback: (SubtitleFile) -> Unit, callback: (ExtractorLink) -> Unit) { - val m3uLink:String? - val extRef = referer ?: "" - val postUrl = "${url}?do=getVideo" - + override suspend fun getUrl( + url: String, + referer: String?, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit, + ) { + val m3uLink: String? + val extRef = referer ?: "" + val postUrl = "$url?do=getVideo" val response = app.post( postUrl, data = mapOf( "hash" to url.substringAfter("video/"), - "r" to extRef, - "s" to "" + "r" to extRef, + "s" to "", ), referer = extRef, headers = mapOf( - "Content-Type" to "application/x-www-form-urlencoded; charset=UTF-8", - "X-Requested-With" to "XMLHttpRequest" - ) + "Content-Type" to "application/x-www-form-urlencoded; charset=UTF-8", + "X-Requested-With" to "XMLHttpRequest", + ), ) - if (response.text.contains("teve2.com.tr\\/embed\\/")) { - val teve2Id = response.text.substringAfter("teve2.com.tr\\/embed\\/").substringBefore("\"") - val teve2Response = app.get( - "https://www.teve2.com.tr/action/media/${teve2Id}", - referer = "https://www.teve2.com.tr/embed/${teve2Id}" - ).parsedSafe() ?: throw ErrorLoadingException("teve2 response is null") - m3uLink = teve2Response.media.link.serviceUrl + "//" + teve2Response.media.link.securePath + if (response.text.contains("teve2.com.tr\\/embed\\/")) { + val teve2Id = response.text.substringAfter("teve2.com.tr\\/embed\\/").substringBefore("\"") + val teve2Response = app.get( + "https://www.teve2.com.tr/action/media/$teve2Id", + referer = "https://www.teve2.com.tr/embed/$teve2Id", + ).parsedSafe() ?: throw ErrorLoadingException("teve2 response is null") + m3uLink = teve2Response.media.link.serviceUrl + "//" + teve2Response.media.link.securePath } else { val videoResponse = response.parsedSafe() ?: throw ErrorLoadingException("peace response is null") - val videoSources = videoResponse.videoSources - if (videoSources.isNotEmpty()) { - m3uLink = videoSources.lastOrNull()?.file + val videoSources = videoResponse.videoSources + m3uLink = if (videoSources.isNotEmpty()) { + videoSources.lastOrNull()?.file } else { - m3uLink = null + null } } callback.invoke( newExtractorLink( - source = this.name, - name = this.name, - url = m3uLink ?: throw ErrorLoadingException("m3u link not found"), + source = this.name, + name = this.name, + url = m3uLink ?: throw ErrorLoadingException("m3u link not found"), ) { this.referer = extRef this.quality = Qualities.Unknown.value @@ -60,29 +66,34 @@ open class PeaceMakerst : ExtractorApi() { ) } + @Serializable data class PeaceResponse( - @JsonProperty("videoImage") val videoImage: String?, - @JsonProperty("videoSources") val videoSources: List, - @JsonProperty("sIndex") val sIndex: String, - @JsonProperty("sourceList") val sourceList: Map + @JsonProperty("videoImage") @SerialName("videoImage") val videoImage: String?, + @JsonProperty("videoSources") @SerialName("videoSources") val videoSources: List, + @JsonProperty("sIndex") @SerialName("sIndex") val sourceIndex: String, + @JsonProperty("sourceList") @SerialName("sourceList") val sourceList: Map, ) + @Serializable data class VideoSource( - @JsonProperty("file") val file: String, - @JsonProperty("label") val label: String, - @JsonProperty("type") val type: String + @JsonProperty("file") @SerialName("file") val file: String, + @JsonProperty("label") @SerialName("label") val label: String, + @JsonProperty("type") @SerialName("type") val type: String, ) + @Serializable data class Teve2ApiResponse( - @JsonProperty("Media") val media: Teve2Media + @JsonProperty("Media") @SerialName("Media") val media: Teve2Media, ) + @Serializable data class Teve2Media( - @JsonProperty("Link") val link: Teve2Link + @JsonProperty("Link") @SerialName("Link") val link: Teve2Link, ) + @Serializable data class Teve2Link( - @JsonProperty("ServiceUrl") val serviceUrl: String, - @JsonProperty("SecurePath") val securePath: String + @JsonProperty("ServiceUrl") @SerialName("ServiceUrl") val serviceUrl: String, + @JsonProperty("SecurePath") @SerialName("SecurePath") val securePath: String, ) -} \ No newline at end of file +} diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/PlayLtXyz.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/PlayLtXyz.kt index c59022abb..a5e71b619 100644 --- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/PlayLtXyz.kt +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/PlayLtXyz.kt @@ -1,33 +1,28 @@ package com.lagradost.cloudstream3.extractors -import com.lagradost.api.Log import com.fasterxml.jackson.annotation.JsonProperty +import com.lagradost.api.Log import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.utils.* import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable -open class PlayLtXyz: ExtractorApi() { - override val name: String = "PlayLt" - override val mainUrl: String = "https://play.playlt.xyz" +open class PlayLtXyz : ExtractorApi() { + override val name = "PlayLt" + override val mainUrl = "https://play.playlt.xyz" override val requiresReferer = true - private data class ResponseData( - @JsonProperty("data") val data: String? = null - ) - override suspend fun getUrl(url: String, referer: String?): List { val extractedLinksList = mutableListOf() - //Log.i(this.name, "Result => (url) $url") var idUser = "" var idFile = "" var bodyText = "" val doc = app.get(url, referer = referer).document - //Log.i(this.name, "Result => (url, script) $url / ${doc.select("script")}") bodyText = doc.select("script").firstOrNull { val text = it.toString() text.contains("var idUser") }?.toString() ?: "" - //Log.i(this.name, "Result => (bodyText) $bodyText") if (bodyText.isNotBlank()) { idUser = "(?<=var idUser = \")(.*)(?=\";)".toRegex().find(bodyText) ?.groupValues?.get(0) ?: "" @@ -35,29 +30,27 @@ open class PlayLtXyz: ExtractorApi() { idFile = "(?<=var idfile = \")(.*)(?=\";)".toRegex().find(bodyText) ?.groupValues?.get(0) ?: "" } - //Log.i(this.name, "Result => (idUser, idFile) $idUser / $idFile") + if (idUser.isNotBlank() && idFile.isNotBlank()) { - //val sess = HttpSession() val ajaxHead = mapOf( Pair("Origin", mainUrl), Pair("Referer", mainUrl), Pair("Sec-Fetch-Site", "same-site"), Pair("Sec-Fetch-Mode", "cors"), - Pair("Sec-Fetch-Dest", "empty") - ) - val ajaxData = mapOf( - Pair("referrer", referer ?: mainUrl), - Pair("typeend", "html") + Pair("Sec-Fetch-Dest", "empty"), ) - //idUser = 608f7c85cf0743547f1f1b4e - val posturl = "https://api-plhq.playlt.xyz/apiv5/$idUser/$idFile" - val data = app.post(posturl, headers = ajaxHead, data = ajaxData) - //Log.i(this.name, "Result => (posturl) $posturl") + val ajaxData = mapOf( + Pair("referrer", referer ?: mainUrl), + Pair("typeend", "html"), + ) + + val postUrl = "https://api-plhq.playlt.xyz/apiv5/$idUser/$idFile" + val data = app.post(postUrl, headers = ajaxHead, data = ajaxData) if (data.isSuccessful) { - val itemstr = data.text - Log.i(this.name, "Result => (data) $itemstr") - tryParseJson(itemstr)?.let { item -> + val itemStr = data.text + Log.i(this.name, "Result => (data) $itemStr") + tryParseJson(itemStr)?.let { item -> val linkUrl = item.data ?: "" if (linkUrl.isNotBlank()) { extractedLinksList.add( @@ -65,7 +58,7 @@ open class PlayLtXyz: ExtractorApi() { source = name, name = name, url = linkUrl, - type = ExtractorLinkType.M3U8 + type = ExtractorLinkType.M3U8, ) { this.referer = url this.quality = Qualities.Unknown.value @@ -75,6 +68,12 @@ open class PlayLtXyz: ExtractorApi() { } } } + return extractedLinksList } -} \ No newline at end of file + + @Serializable + private data class ResponseData( + @JsonProperty("data") @SerialName("data") val data: String? = null, + ) +} diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Rabbitstream.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Rabbitstream.kt index fdd559258..46b83f10a 100644 --- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Rabbitstream.kt +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Rabbitstream.kt @@ -7,8 +7,8 @@ import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.base64DecodeArray import com.lagradost.cloudstream3.base64Encode import com.lagradost.cloudstream3.newSubtitleFile -import com.lagradost.cloudstream3.utils.AppUtils import com.lagradost.cloudstream3.utils.AppUtils.parseJson +import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson import com.lagradost.cloudstream3.utils.ExtractorApi import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.M3u8Helper @@ -16,6 +16,8 @@ import dev.whyoleg.cryptography.CryptographyProvider import dev.whyoleg.cryptography.DelicateCryptographyApi import dev.whyoleg.cryptography.algorithms.AES import dev.whyoleg.cryptography.algorithms.MD5 +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable class Megacloud : Rabbitstream() { override val name = "Megacloud" @@ -36,6 +38,7 @@ class Megacloud : Rabbitstream() { extractedKey += sourcesArray[i].toString() sourcesArray[i] = ' ' } + currentIndex += index[1] } @@ -55,7 +58,7 @@ class Megacloud : Rabbitstream() { val matchKey2 = matchingKey(match.groupValues[2]) try { listOf(matchKey1.toInt(16), matchKey2.toInt(16)) - } catch (e: NumberFormatException) { + } catch (_: NumberFormatException) { emptyList() } }.filter { it.isNotEmpty() } @@ -86,26 +89,25 @@ open class Rabbitstream : ExtractorApi() { url: String, referer: String?, subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit + callback: (ExtractorLink) -> Unit, ) { val id = url.substringAfterLast("/").substringBefore("?") - val response = app.get( "$mainUrl/$embed/getSources?id=$id", referer = mainUrl, - headers = mapOf("X-Requested-With" to "XMLHttpRequest") + headers = mapOf("X-Requested-With" to "XMLHttpRequest"), ) val encryptedMap = response.parsedSafe() val sources = encryptedMap?.sources val decryptedSources = if (sources == null || encryptedMap.encrypted == false) { - response.parsedSafe() + response.parsedSafe() } else { val (key, encData) = extractRealKey(sources) - val decrypted = decryptMapped>(encData, key) + val decrypted = tryParseJson>(decrypt(encData, key)) SourcesResponses( sources = decrypted, - tracks = encryptedMap.tracks + tracks = encryptedMap.tracks, ) } @@ -121,7 +123,7 @@ open class Rabbitstream : ExtractorApi() { subtitleCallback.invoke( newSubtitleFile( track?.label ?: return@map, - track.file ?: return@map + track.file ?: return@map, ) ) } @@ -133,16 +135,11 @@ open class Rabbitstream : ExtractorApi() { return extractedKey to sources } - private inline suspend fun decryptMapped(input: String, key: String): T? { - val decrypt = decrypt(input, key) - return AppUtils.tryParseJson(decrypt) - } - private suspend fun decrypt(input: String, key: String): String { return decryptSourceUrl( generateKey( salt = base64DecodeArray(input).copyOfRange(8, 16), - secret = key.encodeToByteArray() + secret = key.encodeToByteArray(), ), input ) } @@ -154,6 +151,7 @@ open class Rabbitstream : ExtractorApi() { key = md5(key + secret + salt) currentKey += key } + return currentKey } @@ -172,26 +170,30 @@ open class Rabbitstream : ExtractorApi() { return decryptedData.decodeToString() } + @Serializable data class Tracks( - @JsonProperty("file") val file: String? = null, - @JsonProperty("label") val label: String? = null, - @JsonProperty("kind") val kind: String? = null, + @JsonProperty("file") @SerialName("file") val file: String? = null, + @JsonProperty("label") @SerialName("label") val label: String? = null, + @JsonProperty("kind") @SerialName("kind") val kind: String? = null, ) + @Serializable data class Sources( - @JsonProperty("file") val file: String? = null, - @JsonProperty("type") val type: String? = null, - @JsonProperty("label") val label: String? = null, + @JsonProperty("file") @SerialName("file") val file: String? = null, + @JsonProperty("type") @SerialName("type") val type: String? = null, + @JsonProperty("label") @SerialName("label") val label: String? = null, ) + @Serializable data class SourcesResponses( - @JsonProperty("sources") val sources: List? = emptyList(), - @JsonProperty("tracks") val tracks: List? = emptyList(), + @JsonProperty("sources") @SerialName("sources") val sources: List? = emptyList(), + @JsonProperty("tracks") @SerialName("tracks") val tracks: List? = emptyList(), ) + @Serializable data class SourcesEncrypted( - @JsonProperty("sources") val sources: String? = null, - @JsonProperty("encrypted") val encrypted: Boolean? = null, - @JsonProperty("tracks") val tracks: List? = emptyList(), + @JsonProperty("sources") @SerialName("sources") val sources: String? = null, + @JsonProperty("encrypted") @SerialName("encrypted") val encrypted: Boolean? = null, + @JsonProperty("tracks") @SerialName("tracks") val tracks: List? = emptyList(), ) } diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/SobreatsesuypExtractor.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/SobreatsesuypExtractor.kt index be95940e0..0c5d7c974 100644 --- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/SobreatsesuypExtractor.kt +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/SobreatsesuypExtractor.kt @@ -2,51 +2,53 @@ package com.lagradost.cloudstream3.extractors +import com.fasterxml.jackson.annotation.JsonProperty import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.utils.* -import com.fasterxml.jackson.annotation.JsonProperty +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import kotlinx.serialization.json.JsonElement open class Sobreatsesuyp : ExtractorApi() { - override val name = "Sobreatsesuyp" - override val mainUrl = "https://sobreatsesuyp.com" + override val name = "Sobreatsesuyp" + override val mainUrl = "https://sobreatsesuyp.com" override val requiresReferer = true - override suspend fun getUrl(url: String, referer: String?, subtitleCallback: (SubtitleFile) -> Unit, callback: (ExtractorLink) -> Unit) { + override suspend fun getUrl( + url: String, + referer: String?, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit, + ) { val extRef = referer ?: "" - val videoReq = app.get(url, referer = extRef).text - - val file = Regex("""file\":\"([^\"]+)""").find(videoReq)?.groupValues?.get(1) ?: throw ErrorLoadingException("File not found") - val postLink = "${mainUrl}/" + file.replace("\\", "") - val rawList = app.post(postLink, referer = extRef).parsedSafe>() ?: throw ErrorLoadingException("Post link not found") - + val file = Regex("""file\":\"([^\"]+)""").find(videoReq)?.groupValues?.get(1) ?: throw ErrorLoadingException("File not found") + val postLink = "$mainUrl/" + file.replace("\\", "") + val rawList = app.post(postLink, referer = extRef).parsedSafe>() ?: throw ErrorLoadingException("Post link not found") val postJson: List = rawList.drop(1).map { item -> val mapItem = item as Map<*, *> SobreatsesuypVideoData( title = mapItem["title"] as? String, - file = mapItem["file"] as? String + file = mapItem["file"] as? String, ) } for (item in postJson) { if (item.file == null || item.title == null) continue - - val videoData = app.post("${mainUrl}/playlist/${item.file.substring(1)}.txt", referer = extRef).text - + val videoData = app.post("$mainUrl/playlist/${item.file.substring(1)}.txt", referer = extRef).text callback.invoke( newExtractorLink( - source = this.name, - name = "${this.name} - ${item.title}", - url = videoData, - ) { - this.referer = extRef - } + source = this.name, + name = "${this.name} - ${item.title}", + url = videoData, + ) { this.referer = extRef } ) } } + @Serializable data class SobreatsesuypVideoData( - @JsonProperty("title") val title: String? = null, - @JsonProperty("file") val file: String? = null + @JsonProperty("title") @SerialName("title") val title: String? = null, + @JsonProperty("file") @SerialName("file") val file: String? = null, ) -} \ No newline at end of file +} diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/StreamEmbed.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/StreamEmbed.kt index c89f4d226..8c5eb5d6c 100644 --- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/StreamEmbed.kt +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/StreamEmbed.kt @@ -7,22 +7,23 @@ import com.lagradost.cloudstream3.utils.AppUtils.parseJson import com.lagradost.cloudstream3.utils.ExtractorApi import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.M3u8Helper +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable open class StreamEmbed : ExtractorApi() { - override var name = "StreamEmbed" - override var mainUrl = "https://watch.gxplayer.xyz" + override val name = "StreamEmbed" + override val mainUrl = "https://watch.gxplayer.xyz" override val requiresReferer = true override suspend fun getUrl( url: String, referer: String?, subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit + callback: (ExtractorLink) -> Unit, ) { val jsonString = app.get(url, referer = mainUrl).text .substringAfter("var video = ").substringBefore(";") val video = parseJson
(jsonString) - M3u8Helper.generateM3u8( this.name, "$mainUrl/m3u8/${video.uid}/${video.md5}/master.txt?s=1&id=${video.id}&cache=${video.status}", @@ -30,14 +31,15 @@ open class StreamEmbed : ExtractorApi() { ).forEach(callback) } + @Serializable private data class Details( - @JsonProperty("id") val id: String, - @JsonProperty("uid") val uid: String, - @JsonProperty("slug") val slug: String, - @JsonProperty("title") val title: String, - @JsonProperty("quality") val quality: String, - @JsonProperty("type") val type: String, - @JsonProperty("status") val status: String, - @JsonProperty("md5") val md5: String, + @JsonProperty("id") @SerialName("id") val id: String, + @JsonProperty("uid") @SerialName("uid") val uid: String, + @JsonProperty("slug") @SerialName("slug") val slug: String, + @JsonProperty("title") @SerialName("title") val title: String, + @JsonProperty("quality") @SerialName("quality") val quality: String, + @JsonProperty("type") @SerialName("type") val type: String, + @JsonProperty("status") @SerialName("status") val status: String, + @JsonProperty("md5") @SerialName("md5") val md5: String, ) } diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/StreamSB.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/StreamSB.kt index 67cf1f8da..a7584d708 100644 --- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/StreamSB.kt +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/StreamSB.kt @@ -7,6 +7,8 @@ import com.lagradost.cloudstream3.newSubtitleFile import com.lagradost.cloudstream3.utils.ExtractorApi import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.M3u8Helper +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable import kotlin.random.Random class Sblona : StreamSB() { @@ -141,17 +143,15 @@ open class StreamSB : ExtractorApi() { url: String, referer: String?, subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit + callback: (ExtractorLink) -> Unit, ) { - val regexID = + val regexId = Regex("(embed-[a-zA-Z\\d]{0,8}[a-zA-Z\\d_-]+|/e/[a-zA-Z\\d]{0,8}[a-zA-Z\\d_-]+)") - val id = regexID.findAll(url).map { + val id = regexId.findAll(url).map { it.value.replace(Regex("(embed-|/e/)"), "") }.first() val master = "$mainUrl/375664356a494546326c4b797c7c6e756577776778623171737/${encodeId(id)}" - val headers = mapOf( - "watchsb" to "sbstream", - ) + val headers = mapOf("watchsb" to "sbstream") val mapped = app.get( master.lowercase(), headers = headers, @@ -161,9 +161,8 @@ open class StreamSB : ExtractorApi() { name, mapped?.streamData?.file ?: return, url, - headers = headers + headers = headers, ).forEach(callback) - mapped.streamData.subs?.map {sub -> subtitleCallback.invoke( newSubtitleFile( @@ -189,25 +188,27 @@ open class StreamSB : ExtractorApi() { } } - data class Subs ( - @JsonProperty("file") val file: String? = null, - @JsonProperty("label") val label: String? = null, + @Serializable + data class Subs( + @JsonProperty("file") @SerialName("file") val file: String? = null, + @JsonProperty("label") @SerialName("label") val label: String? = null, ) - data class StreamData ( - @JsonProperty("file") val file: String, - @JsonProperty("cdn_img") val cdnImg: String, - @JsonProperty("hash") val hash: String, - @JsonProperty("subs") val subs: ArrayList? = arrayListOf(), - @JsonProperty("length") val length: String, - @JsonProperty("id") val id: String, - @JsonProperty("title") val title: String, - @JsonProperty("backup") val backup: String, + @Serializable + data class StreamData( + @JsonProperty("file") @SerialName("file") val file: String, + @JsonProperty("cdn_img") @SerialName("cdn_img") val cdnImg: String, + @JsonProperty("hash") @SerialName("hash") val hash: String, + @JsonProperty("subs") @SerialName("subs") val subs: ArrayList? = arrayListOf(), + @JsonProperty("length") @SerialName("length") val length: String, + @JsonProperty("id") @SerialName("id") val id: String, + @JsonProperty("title") @SerialName("title") val title: String, + @JsonProperty("backup") @SerialName("backup") val backup: String, ) - data class Main ( - @JsonProperty("stream_data") val streamData: StreamData, - @JsonProperty("status_code") val statusCode: Int, + @Serializable + data class Main( + @JsonProperty("stream_data") @SerialName("stream_data") val streamData: StreamData, + @JsonProperty("status_code") @SerialName("status_code") val statusCode: Int, ) - } diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Streamlare.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Streamlare.kt index da2dd62be..d17e59c82 100644 --- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Streamlare.kt +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Streamlare.kt @@ -9,10 +9,11 @@ import com.lagradost.cloudstream3.utils.INFER_TYPE import com.lagradost.cloudstream3.utils.newExtractorLink import com.lagradost.cloudstream3.utils.Qualities import com.lagradost.nicehttp.RequestBodyTypes +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.RequestBody.Companion.toRequestBody - class Streamlare : Slmaxed() { override val mainUrl = "https://streamlare.com/" } @@ -24,27 +25,11 @@ open class Slmaxed : ExtractorApi() { // https://slmaxed.com/e/oLvgezw3LjPzbp8E -> oLvgezw3LjPzbp8E val embedRegex = Regex("""/e/([^/]*)""") - - - data class JsonResponse( - @JsonProperty val status: String? = null, - @JsonProperty val message: String? = null, - @JsonProperty val type: String? = null, - @JsonProperty val token: String? = null, - @JsonProperty val result: Map? = null - ) - - data class Result( - @JsonProperty val label: String? = null, - @JsonProperty val file: String? = null, - @JsonProperty val type: String? = null - ) - override suspend fun getUrl(url: String, referer: String?): List? { val id = embedRegex.find(url)!!.groupValues[1] val json = app.post( "${mainUrl}api/video/stream/get", - requestBody = """{"id":"$id"}""".toRequestBody(RequestBodyTypes.JSON.toMediaTypeOrNull()) + requestBody = """{"id":"$id"}""".toRequestBody(RequestBodyTypes.JSON.toMediaTypeOrNull()), ).parsed() return json.result?.mapNotNull { it.value.let { result -> @@ -52,18 +37,29 @@ open class Slmaxed : ExtractorApi() { this.name, this.name, result.file ?: return@mapNotNull null, - type = if (result.type?.contains( - "hls", - ignoreCase = true - ) == true - ) ExtractorLinkType.M3U8 else INFER_TYPE + type = if (result.type?.contains("hls", ignoreCase = true) == true) ExtractorLinkType.M3U8 else INFER_TYPE, ) { this.referer = url - this.quality = - result.label?.replace("p", "", ignoreCase = true)?.trim()?.toIntOrNull() - ?: Qualities.Unknown.value + this.quality = result.label?.replace("p", "", ignoreCase = true)?.trim()?.toIntOrNull() + ?: Qualities.Unknown.value } } } } -} \ No newline at end of file + + @Serializable + data class JsonResponse( + @JsonProperty("status") @SerialName("status") val status: String? = null, + @JsonProperty("message") @SerialName("message") val message: String? = null, + @JsonProperty("type") @SerialName("type") val type: String? = null, + @JsonProperty("token") @SerialName("token") val token: String? = null, + @JsonProperty("result") @SerialName("result") val result: Map? = null, + ) + + @Serializable + data class Result( + @JsonProperty("label") @SerialName("label") val label: String? = null, + @JsonProperty("file") @SerialName("file") val file: String? = null, + @JsonProperty("type") @SerialName("type") val type: String? = null, + ) +} diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Streamplay.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Streamplay.kt index 9886300aa..1bcf9966b 100644 --- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Streamplay.kt +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Streamplay.kt @@ -8,6 +8,8 @@ import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.utils.* import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson import io.ktor.http.Url +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable open class Streamplay : ExtractorApi() { override val name = "Streamplay" @@ -18,37 +20,36 @@ open class Streamplay : ExtractorApi() { url: String, referer: String?, subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit + callback: (ExtractorLink) -> Unit, ) { val request = app.get(url, referer = referer) val redirectUrl = request.url val mainServer = Url(redirectUrl).let { "${it.protocol.name}://${it.host}" } val key = redirectUrl.substringAfter("embed-").substringBefore(".html") - val token = - request.document.select("script").find { it.data().contains("sitekey:") }?.data() - ?.substringAfterLast("sitekey: '")?.substringBefore("',")?.let { captchaKey -> - getCaptchaToken( - redirectUrl, - captchaKey, - referer = "$mainServer/" - ) - } ?: throw ErrorLoadingException("can't bypass captcha") + val token = request.document.select("script").find { it.data().contains("sitekey:") }?.data() + ?.substringAfterLast("sitekey: '")?.substringBefore("',")?.let { captchaKey -> + getCaptchaToken( + redirectUrl, + captchaKey, + referer = "$mainServer/", + ) + } ?: throw ErrorLoadingException("can't bypass captcha") + app.post( "$mainServer/player-$key-488x286.html", data = mapOf( "op" to "embed", - "token" to token + "token" to token, ), referer = redirectUrl, headers = mapOf( "Accept" to "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8", - "Content-Type" to "application/x-www-form-urlencoded" - ) + "Content-Type" to "application/x-www-form-urlencoded", + ), ).document.select("script").find { script -> script.data().contains("eval(function(p,a,c,k,e,d)") }?.let { val data = getAndUnpack(it.data()).substringAfter("sources=[").substringBefore(",desc") - .replace("file", "\"file\"") - .replace("label", "\"label\"") + .replace("file", "\"file\"").replace("label", "\"label\"") tryParseJson>("[$data}]")?.map { res -> callback.invoke( newExtractorLink( @@ -57,24 +58,21 @@ open class Streamplay : ExtractorApi() { res.file ?: return@map null, ) { this.referer = "$mainServer/" + this.headers = mapOf("Range" to "bytes=0-") this.quality = when (res.label) { "HD" -> Qualities.P720.value "SD" -> Qualities.P480.value else -> Qualities.Unknown.value } - this.headers = mapOf( - "Range" to "bytes=0-" - ) } ) } } - } + @Serializable data class Source( - @JsonProperty("file") val file: String? = null, - @JsonProperty("label") val label: String? = null, + @JsonProperty("file") @SerialName("file") val file: String? = null, + @JsonProperty("label") @SerialName("label") val label: String? = null, ) - } diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/TRsTXExtractor.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/TRsTXExtractor.kt index 1348f74d5..6014105d8 100644 --- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/TRsTXExtractor.kt +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/TRsTXExtractor.kt @@ -2,68 +2,68 @@ package com.lagradost.cloudstream3.extractors +import com.fasterxml.jackson.annotation.JsonProperty import com.lagradost.api.Log import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.utils.* -import com.fasterxml.jackson.annotation.JsonProperty +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import kotlinx.serialization.json.JsonElement open class TRsTX : ExtractorApi() { - override val name = "TRsTX" - override val mainUrl = "https://trstx.org" + override val name = "TRsTX" + override val mainUrl = "https://trstx.org" override val requiresReferer = true - override suspend fun getUrl(url: String, referer: String?, subtitleCallback: (SubtitleFile) -> Unit, callback: (ExtractorLink) -> Unit) { + override suspend fun getUrl( + url: String, + referer: String?, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit, + ) { val extRef = referer ?: "" - - val videoReq = app.get(url, referer=extRef).text - - val file = Regex("""file\":\"([^\"]+)""").find(videoReq)?.groupValues?.get(1) ?: throw ErrorLoadingException("File not found") - val postLink = "${mainUrl}/" + file.replace("\\", "") - val rawList = app.post(postLink, referer=extRef).parsedSafe>() ?: throw ErrorLoadingException("Post link not found") - + val videoReq = app.get(url, referer = extRef).text + val file = Regex("""file\":\"([^\"]+)""").find(videoReq)?.groupValues?.get(1) ?: throw ErrorLoadingException("File not found") + val postLink = "$mainUrl/" + file.replace("\\", "") + val rawList = app.post(postLink, referer = extRef).parsedSafe>() ?: throw ErrorLoadingException("Post link not found") val postJson: List = rawList.drop(1).map { item -> val mapItem = item as Map<*, *> TrstxVideoData( title = mapItem["title"] as? String, - file = mapItem["file"] as? String + file = mapItem["file"] as? String, ) } val vidLinks = mutableSetOf() - val vidMap = mutableListOf>() + val vidMap = mutableListOf>() for (item in postJson) { if (item.file == null || item.title == null) continue - - val fileUrl = "${mainUrl}/playlist/" + item.file.substring(1) + ".txt" - val videoData = app.post(fileUrl, referer=extRef).text - - if (videoData in vidLinks) { continue } + val fileUrl = "$mainUrl/playlist/" + item.file.substring(1) + ".txt" + val videoData = app.post(fileUrl, referer = extRef).text + if (videoData in vidLinks) continue vidLinks.add(videoData) - vidMap.add(mapOf( - "title" to item.title, - "videoData" to videoData + "title" to item.title, + "videoData" to videoData, )) } for (mapEntry in vidMap) { - val title = mapEntry["title"] ?: continue + val title = mapEntry["title"] ?: continue val m3uLink = mapEntry["videoData"] ?: continue - callback.invoke( newExtractorLink( - source = this.name, - name = "${this.name} - ${title}", - url = m3uLink, - ) { - this.referer = extRef - } + source = this.name, + name = "${this.name} - $title", + url = m3uLink, + ) { this.referer = extRef } ) } } + @Serializable data class TrstxVideoData( - @JsonProperty("title") val title: String? = null, - @JsonProperty("file") val file: String? = null + @JsonProperty("title") @SerialName("title") val title: String? = null, + @JsonProperty("file") @SerialName("file") val file: String? = null, ) } diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Tantifilm.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Tantifilm.kt index 4e330d1a7..e8022b323 100644 --- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Tantifilm.kt +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Tantifilm.kt @@ -6,38 +6,42 @@ import com.lagradost.cloudstream3.utils.ExtractorApi import com.lagradost.cloudstream3.utils.AppUtils.parseJson import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.newExtractorLink +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable open class Tantifilm : ExtractorApi() { - override var name = "Tantifilm" - override var mainUrl = "https://cercafilm.net" + override val name = "Tantifilm" + override val mainUrl = "https://cercafilm.net" override val requiresReferer = false - data class TantifilmJsonData ( - @JsonProperty("success") val success : Boolean, - @JsonProperty("data") val data : List, - @JsonProperty("captions")val captions : List, - @JsonProperty("is_vr") val is_vr : Boolean - ) - - data class TantifilmData ( - @JsonProperty("file") val file : String, - @JsonProperty("label") val label : String, - @JsonProperty("type") val type : String - ) - override suspend fun getUrl(url: String, referer: String?): List? { val link = "$mainUrl/api/source/${url.substringAfterLast("/")}" val response = app.post(link).text.replace("""\""","") - val jsonvideodata = parseJson(response) - return jsonvideodata.data.map { + val jsonVideoData = parseJson(response) + return jsonVideoData.data.map { newExtractorLink( this.name, this.name, - it.file+".${it.type}" + it.file + ".${it.type}", ) { this.referer = mainUrl this.quality = it.label.filter{ it.isDigit() }.toInt() } } } -} \ No newline at end of file + + @Serializable + data class TantifilmJsonData( + @JsonProperty("success") @SerialName("success") val success: Boolean, + @JsonProperty("data") @SerialName("data") val data: List, + @JsonProperty("captions") @SerialName("captions") val captions: List, + @JsonProperty("is_vr") @SerialName("is_vr") val isVr: Boolean, + ) + + @Serializable + data class TantifilmData( + @JsonProperty("file") @SerialName("file") val file: String, + @JsonProperty("label") @SerialName("label") val label: String, + @JsonProperty("type") @SerialName("type") val type: String, + ) +} diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/TauVideoExtractor.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/TauVideoExtractor.kt index 1c9cce2a8..9133ca99c 100644 --- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/TauVideoExtractor.kt +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/TauVideoExtractor.kt @@ -2,29 +2,35 @@ package com.lagradost.cloudstream3.extractors +import com.fasterxml.jackson.annotation.JsonProperty import com.lagradost.api.Log import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.utils.* -import com.fasterxml.jackson.annotation.JsonProperty +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable open class TauVideo : ExtractorApi() { - override val name = "TauVideo" - override val mainUrl = "https://tau-video.xyz" + override val name = "TauVideo" + override val mainUrl = "https://tau-video.xyz" override val requiresReferer = true - override suspend fun getUrl(url: String, referer: String?, subtitleCallback: (SubtitleFile) -> Unit, callback: (ExtractorLink) -> Unit) { - val extRef = referer ?: "" + override suspend fun getUrl( + url: String, + referer: String?, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit, + ) { + val extRef = referer ?: "" val videoKey = url.split("/").last() - val videoUrl = "${mainUrl}/api/video/${videoKey}" - + val videoUrl = "$mainUrl/api/video/$videoKey" val api = app.get(videoUrl).parsedSafe() ?: throw ErrorLoadingException("TauVideo") for (video in api.urls) { callback.invoke( newExtractorLink( - source = this.name, - name = this.name, - url = video.url, + source = this.name, + name = this.name, + url = video.url, ) { this.referer = extRef this.quality = getQualityFromName(video.label) @@ -33,12 +39,14 @@ open class TauVideo : ExtractorApi() { } } + @Serializable data class TauVideoUrls( - @JsonProperty("urls") val urls: List + @JsonProperty("urls") @SerialName("urls") val urls: List, ) + @Serializable data class TauVideoData( - @JsonProperty("url") val url: String, - @JsonProperty("label") val label: String, + @JsonProperty("url") @SerialName("url") val url: String, + @JsonProperty("label") @SerialName("label") val label: String, ) -} \ No newline at end of file +} diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Uservideo.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Uservideo.kt index 307825998..97922127d 100644 --- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Uservideo.kt +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Uservideo.kt @@ -3,51 +3,51 @@ package com.lagradost.cloudstream3.extractors import com.fasterxml.jackson.annotation.JsonProperty import com.lagradost.cloudstream3.SubtitleFile import com.lagradost.cloudstream3.app -import com.lagradost.cloudstream3.utils.AppUtils +import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson import com.lagradost.cloudstream3.utils.ExtractorApi import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.Qualities import com.lagradost.cloudstream3.utils.newExtractorLink +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable open class Uservideo : ExtractorApi() { - override val name: String = "Uservideo" - override val mainUrl: String = "https://uservideo.xyz" + override val name = "Uservideo" + override val mainUrl = "https://uservideo.xyz" override val requiresReferer = false override suspend fun getUrl( url: String, referer: String?, subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit + callback: (ExtractorLink) -> Unit, ) { val script = app.get(url).document.selectFirst("script:containsData(hosts =)")?.data() val host = script?.substringAfter("hosts = [\"")?.substringBefore("\"];") val servers = script?.substringAfter("servers = \"")?.substringBefore("\";") - - val sources = app.get("$host/s/$servers").text.substringAfter("\"sources\":[").substringBefore("],").let { - AppUtils.tryParseJson>("[$it]") - } val quality = Regex("(\\d{3,4})[Pp]").find(url)?.groupValues?.getOrNull(1)?.toIntOrNull() + val sources = app.get("$host/s/$servers").text.substringAfter("\"sources\":[").substringBefore("],").let { + tryParseJson>("[$it]") + } sources?.map { source -> callback.invoke( newExtractorLink( name, name, - source.src ?: return@map null + source.src ?: return@map null, ) { this.referer = url this.quality = quality ?: Qualities.Unknown.value } ) } - } + @Serializable data class Sources( - @JsonProperty("src") val src: String? = null, - @JsonProperty("type") val type: String? = null, - @JsonProperty("label") val label: String? = null, + @JsonProperty("src") @SerialName("src") val src: String? = null, + @JsonProperty("type") @SerialName("type") val type: String? = null, + @JsonProperty("label") @SerialName("label") val label: String? = null, ) - -} \ No newline at end of file +} diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Vicloud.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Vicloud.kt index 6b82ee454..b3082cea6 100644 --- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Vicloud.kt +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Vicloud.kt @@ -8,25 +8,25 @@ import com.lagradost.cloudstream3.utils.ExtractorApi import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.getQualityFromName import com.lagradost.cloudstream3.utils.newExtractorLink +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable open class Vicloud : ExtractorApi() { - override val name: String = "Vicloud" - override val mainUrl: String = "https://vicloud.sbs" + override val name = "Vicloud" + override val mainUrl = "https://vicloud.sbs" override val requiresReferer = false override suspend fun getUrl( url: String, referer: String?, subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit + callback: (ExtractorLink) -> Unit, ) { val id = Regex("\"apiQuery\":\"(.*?)\"").find(app.get(url).text)?.groupValues?.getOrNull(1) app.get( "$mainUrl/api/?$id=&_=$unixTimeMS", - headers = mapOf( - "X-Requested-With" to "XMLHttpRequest" - ), - referer = url + headers = mapOf("X-Requested-With" to "XMLHttpRequest"), + referer = url, ).parsedSafe()?.sources?.map { source -> callback.invoke( newExtractorLink( @@ -39,16 +39,16 @@ open class Vicloud : ExtractorApi() { } ) } - } + @Serializable private data class Sources( - @JsonProperty("file") val file: String? = null, - @JsonProperty("label") val label: String? = null, + @JsonProperty("file") @SerialName("file") val file: String? = null, + @JsonProperty("label") @SerialName("label") val label: String? = null, ) + @Serializable private data class Responses( - @JsonProperty("sources") val sources: List? = arrayListOf(), + @JsonProperty("sources") @SerialName("sources") val sources: List? = arrayListOf(), ) - -} \ No newline at end of file +} diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Vidara.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Vidara.kt index d74a1ccd6..04c60675e 100644 --- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Vidara.kt +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Vidara.kt @@ -44,27 +44,29 @@ class VidaraSo : Vidara() { } open class Vidara : ExtractorApi() { - override val name: String = "Vidara" + override val name = "Vidara" override val mainUrl = "https://vidara.to" - override val requiresReferer: Boolean = false + override val requiresReferer = false override suspend fun getUrl( url: String, referer: String?, subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit + callback: (ExtractorLink) -> Unit, ) { val fileCode = url.substringAfterLast("/") - val fileInfo = - app.post("$mainUrl/api/stream", json = mapOf("filecode" to fileCode, "device" to "web")) - .parsed() - + val fileInfo = app.post( + "$mainUrl/api/stream", json = mapOf( + "filecode" to fileCode, + "device" to "web" + ) + ).parsed() callback.invoke( newExtractorLink( source = name, name = name, url = fileInfo.streamingUrl, - type = ExtractorLinkType.M3U8 + type = ExtractorLinkType.M3U8, ) ) @@ -77,19 +79,15 @@ open class Vidara : ExtractorApi() { @Serializable private data class StreamUpFileInfo( - val title: String, - val thumbnail: String, - @SerialName("streaming_url") - @JsonProperty("streaming_url") - val streamingUrl: String, - val subtitles: List? + @JsonProperty("title") @SerialName("title") val title: String, + @JsonProperty("thumbnail") @SerialName("thumbnail") val thumbnail: String, + @JsonProperty("streaming_url") @SerialName("streaming_url") val streamingUrl: String, + @JsonProperty("subtitles") @SerialName("subtitles") val subtitles: List?, ) @Serializable private data class StreamUpSubtitle( - @SerialName("file_path") - @JsonProperty("file_path") - val filePath: String, - val language: String, + @JsonProperty("file_path") @SerialName("file_path") val filePath: String, + @JsonProperty("language") @SerialName("language") val language: String, ) } diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/VideoSeyredExtractor.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/VideoSeyredExtractor.kt index bc94ae0cc..0c8e41ab7 100644 --- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/VideoSeyredExtractor.kt +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/VideoSeyredExtractor.kt @@ -7,27 +7,32 @@ import com.lagradost.api.Log import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.utils.* import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable open class VideoSeyred : ExtractorApi() { - override val name = "VideoSeyred" - override val mainUrl = "https://videoseyred.in" + override val name = "VideoSeyred" + override val mainUrl = "https://videoseyred.in" override val requiresReferer = true - override suspend fun getUrl(url: String, referer: String?, subtitleCallback: (SubtitleFile) -> Unit, callback: (ExtractorLink) -> Unit) { - val extRef = referer ?: "" - val videoId = url.substringAfter("embed/").substringBefore("?") - val videoUrl = "${mainUrl}/playlist/${videoId}.json" - - val responseRaw = app.get(videoUrl) - val responseList: List = tryParseJson>(responseRaw.text) ?: throw ErrorLoadingException("VideoSeyred") - val response = responseList[0] - + override suspend fun getUrl( + url: String, + referer: String?, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit, + ) { + val extRef = referer ?: "" + val videoId = url.substringAfter("embed/").substringBefore("?") + val videoUrl = "$mainUrl/playlist/$videoId.json" + val responseRaw = app.get(videoUrl) + val responseList = tryParseJson>(responseRaw.text) ?: throw ErrorLoadingException("VideoSeyred") + val response = responseList[0] for (track in response.tracks) { if (track.label != null && track.kind == "captions") { subtitleCallback.invoke( newSubtitleFile( lang = track.label, - url = fixUrl(track.file) + url = fixUrl(track.file), ) ) } @@ -36,35 +41,38 @@ open class VideoSeyred : ExtractorApi() { for (source in response.sources) { callback.invoke( newExtractorLink( - source = this.name, - name = this.name, - url = source.file, + source = this.name, + name = this.name, + url = source.file, ) { - this.referer = "${mainUrl}/" + this.referer = "$mainUrl/" this.quality = Qualities.Unknown.value } ) } } + @Serializable data class VideoSeyredSource( - @JsonProperty("image") val image: String, - @JsonProperty("title") val title: String, - @JsonProperty("sources") val sources: List, - @JsonProperty("tracks") val tracks: List + @JsonProperty("image") @SerialName("image") val image: String, + @JsonProperty("title") @SerialName("title") val title: String, + @JsonProperty("sources") @SerialName("sources") val sources: List, + @JsonProperty("tracks") @SerialName("tracks") val tracks: List, ) + @Serializable data class VSSource( - @JsonProperty("file") val file: String, - @JsonProperty("type") val type: String, - @JsonProperty("default") val default: String + @JsonProperty("file") @SerialName("file") val file: String, + @JsonProperty("type") @SerialName("type") val type: String, + @JsonProperty("default") @SerialName("default") val default: String, ) + @Serializable data class VSTrack( - @JsonProperty("file") val file: String, - @JsonProperty("kind") val kind: String, - @JsonProperty("language") val language: String? = null, - @JsonProperty("label") val label: String? = null, - @JsonProperty("default") val default: String? = null + @JsonProperty("file") @SerialName("file") val file: String, + @JsonProperty("kind") @SerialName("kind") val kind: String, + @JsonProperty("language") @SerialName("language") val language: String? = null, + @JsonProperty("label") @SerialName("label") val label: String? = null, + @JsonProperty("default") @SerialName("default") val default: String? = null, ) } diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Vidoza.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Vidoza.kt index df663f97f..5cb1706cd 100644 --- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Vidoza.kt +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Vidoza.kt @@ -4,26 +4,28 @@ import com.fasterxml.jackson.annotation.JsonProperty import com.lagradost.api.Log import com.lagradost.cloudstream3.SubtitleFile import com.lagradost.cloudstream3.app -import com.lagradost.cloudstream3.utils.AppUtils +import com.lagradost.cloudstream3.utils.AppUtils.parseJson import com.lagradost.cloudstream3.utils.ExtractorApi import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.getQualityFromName import com.lagradost.cloudstream3.utils.newExtractorLink +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable -class Videzz: Vidoza() { - override val mainUrl: String = "https://videzz.net" +class Videzz : Vidoza() { + override val mainUrl = "https://videzz.net" } -open class Vidoza: ExtractorApi() { - override val name: String = "Vidoza" - override val mainUrl: String = "https://vidoza.net" - override val requiresReferer: Boolean = false +open class Vidoza : ExtractorApi() { + override val name = "Vidoza" + override val mainUrl = "https://vidoza.net" + override val requiresReferer = false override suspend fun getUrl( url: String, referer: String?, subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit + callback: (ExtractorLink) -> Unit, ) { val response = app.get(url).document val script = response.selectFirst("script:containsData(sourcesCode)")?.data() @@ -32,31 +34,27 @@ open class Vidoza: ExtractorApi() { // e.g. sourcesCode: [{ src: "https://str38.vidoza.net/vod/v2/.../v.mp4", type: "video/mp4", label:"SD", res:"720"}], var sourcesArray = script.substringAfter("sourcesCode:").substringBefore("\n") arrayOf("src", "type", "label", "res").forEach { - // add missing quotation marks, e.g. src: "https..." -> "src": "https..." - sourcesArray = sourcesArray - .replace(Regex(""""?$it"?:"""), """"$it":""") + // Add missing quotation marks, e.g. src: "https..." -> "src": "https..." + sourcesArray = sourcesArray.replace(Regex(""""?$it"?:"""), """"$it":""") } - val videoData = AppUtils.parseJson(sourcesArray) + val videoData = parseJson>(sourcesArray) for (stream in videoData) { callback.invoke( newExtractorLink( source = name, name = name, - url = stream.source - ) { - quality = getQualityFromName(stream.resolution) - } + url = stream.source, + ) { quality = getQualityFromName(stream.resolution) } ) } } - private class VinovoDataList: ArrayList() - + @Serializable private data class VinovoVideoData( - @JsonProperty("src") val source: String, - @JsonProperty("type") val type: String?, - @JsonProperty("label") val label: String?, - @JsonProperty("res") val resolution: String?, + @JsonProperty("src") @SerialName("src") val source: String, + @JsonProperty("type") @SerialName("type") val type: String?, + @JsonProperty("label") @SerialName("label") val label: String?, + @JsonProperty("res") @SerialName("res") val resolution: String?, ) -} \ No newline at end of file +} diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Vinovo.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Vinovo.kt index e905543a8..8fc42d65a 100644 --- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Vinovo.kt +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Vinovo.kt @@ -1,32 +1,33 @@ package com.lagradost.cloudstream3.extractors import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.cloudstream3.APIHolder +import com.lagradost.cloudstream3.APIHolder.getCaptchaToken import com.lagradost.cloudstream3.SubtitleFile import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.utils.ExtractorApi import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.Qualities import com.lagradost.cloudstream3.utils.newExtractorLink +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable class VinovoSi : VinovoTo() { - override var name = "VinovoSi" - override var mainUrl = "https://vinovo.si" + override val name = "VinovoSi" + override val mainUrl = "https://vinovo.si" } open class VinovoTo : ExtractorApi() { - override val mainUrl: String = "https://vinovo.to" - override val name: String = "VinovoTo" - override val requiresReferer: Boolean = false + override val mainUrl = "https://vinovo.to" + override val name = "VinovoTo" + override val requiresReferer = false override suspend fun getUrl( url: String, referer: String?, subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit + callback: (ExtractorLink) -> Unit, ) { val fixedUrl = url.replace("/d/", "/e/") - val resp = app.get(fixedUrl, referer = referer) val doc = resp.document @@ -35,21 +36,20 @@ open class VinovoTo : ExtractorApi() { val fileCode = doc.selectFirst("meta[name=\"file_code\"]")?.attr("content") ?: return val captchaToken = doc.selectFirst("meta[name=recaptcha]")?.attr("content") ?: return - val captchaSolution = - APIHolder.getCaptchaToken(fixedUrl, captchaToken, "$mainUrl/") ?: return + val captchaSolution = getCaptchaToken(fixedUrl, captchaToken, "$mainUrl/") ?: return val streamInfo = app.post( url = "$mainUrl/api/file/url/$fileCode", data = mapOf("token" to videoToken, "recaptcha" to captchaSolution), headers = mapOf( "Origin" to mainUrl, - "X-Requested-With" to "XMLHttpRequest" + "X-Requested-With" to "XMLHttpRequest", ), cookies = resp.cookies, - referer = fixedUrl + referer = fixedUrl, ).parsed() - val fileUrl = "$videoBaseUrl/stream/${streamInfo.token}" + val fileUrl = "$videoBaseUrl/stream/${streamInfo.token}" callback.invoke( newExtractorLink(source = name, name = name, url = fileUrl) { val dataTitle = doc.selectFirst("video")?.attr("data-title").orEmpty() @@ -59,8 +59,9 @@ open class VinovoTo : ExtractorApi() { ) } + @Serializable private data class VinovoFileResp( - @JsonProperty("status") val status: String, - @JsonProperty("token") val token: String, + @JsonProperty("status") @SerialName("status") val status: String, + @JsonProperty("token") @SerialName("token") val token: String, ) -} \ No newline at end of file +} diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/XStreamCdn.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/XStreamCdn.kt index 6e3a8126d..4392e64ed 100644 --- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/XStreamCdn.kt +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/XStreamCdn.kt @@ -9,96 +9,74 @@ import com.lagradost.cloudstream3.utils.ExtractorApi import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.getQualityFromName import com.lagradost.cloudstream3.utils.newExtractorLink +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable class StreamM4u : XStreamCdn() { - override val name: String = "StreamM4u" - override val mainUrl: String = "https://streamm4u.club" + override val name = "StreamM4u" + override val mainUrl = "https://streamm4u.club" } class Fembed9hd : XStreamCdn() { - override var mainUrl = "https://fembed9hd.com" - override var name = "Fembed9hd" + override val name = "Fembed9hd" + override val mainUrl = "https://fembed9hd.com" } -class Cdnplayer: XStreamCdn() { - override val name: String = "Cdnplayer" - override val mainUrl: String = "https://cdnplayer.online" +class Cdnplayer : XStreamCdn() { + override val name = "Cdnplayer" + override val mainUrl = "https://cdnplayer.online" } -class Kotakajair: XStreamCdn() { - override val name: String = "Kotakajair" - override val mainUrl: String = "https://kotakajair.xyz" +class Kotakajair : XStreamCdn() { + override val name = "Kotakajair" + override val mainUrl = "https://kotakajair.xyz" } -class FEnet: XStreamCdn() { - override val name: String = "FEnet" - override val mainUrl: String = "https://fembed.net" +class FEnet : XStreamCdn() { + override val name = "FEnet" + override val mainUrl = "https://fembed.net" } -class Rasacintaku: XStreamCdn() { - override val mainUrl: String = "https://rasa-cintaku-semakin-berantai.xyz" +class Rasacintaku : XStreamCdn() { + override val mainUrl = "https://rasa-cintaku-semakin-berantai.xyz" } -class LayarKaca: XStreamCdn() { - override val name: String = "LayarKaca-xxi" - override val mainUrl: String = "https://layarkacaxxi.icu" +class LayarKaca : XStreamCdn() { + override val name = "LayarKaca-xxi" + override val mainUrl = "https://layarkacaxxi.icu" } -class DBfilm: XStreamCdn() { - override val name: String = "DBfilm" - override val mainUrl: String = "https://dbfilm.bar" +class DBfilm : XStreamCdn() { + override val name = "DBfilm" + override val mainUrl = "https://dbfilm.bar" } -class Luxubu : XStreamCdn(){ - override val name: String = "FE" - override val mainUrl: String = "https://www.luxubu.review" +class Luxubu : XStreamCdn() { + override val name = "FE" + override val mainUrl = "https://www.luxubu.review" } -class FEmbed: XStreamCdn() { - override val name: String = "FEmbed" - override val mainUrl: String = "https://www.fembed.com" +class FEmbed : XStreamCdn() { + override val name = "FEmbed" + override val mainUrl = "https://www.fembed.com" } -class Fplayer: XStreamCdn() { - override val name: String = "Fplayer" - override val mainUrl: String = "https://fplayer.info" +class Fplayer : XStreamCdn() { + override val name = "Fplayer" + override val mainUrl = "https://fplayer.info" } -class FeHD: XStreamCdn() { - override val name: String = "FeHD" - override val mainUrl: String = "https://fembed-hd.com" - override var domainUrl: String = "fembed-hd.com" +class FeHD : XStreamCdn() { + override val name = "FeHD" + override val mainUrl = "https://fembed-hd.com" + override val domainUrl = "fembed-hd.com" } open class XStreamCdn : ExtractorApi() { - override val name: String = "XStreamCdn" - override val mainUrl: String = "https://embedsito.com" + override val name = "XStreamCdn" + override val mainUrl = "https://embedsito.com" override val requiresReferer = false - open var domainUrl: String = "embedsito.com" - - private data class ResponseData( - @JsonProperty("file") val file: String, - @JsonProperty("label") val label: String, - //val type: String // Mp4 - ) - - private data class Player( - @JsonProperty("poster_file") val poster_file: String? = null, - ) - - private data class ResponseJson( - @JsonProperty("success") val success: Boolean, - @JsonProperty("player") val player: Player? = null, - @JsonProperty("data") val data: List?, - @JsonProperty("captions") val captions: List?, - ) - - private data class Captions( - @JsonProperty("id") val id: String, - @JsonProperty("hash") val hash: String, - @JsonProperty("language") val language: String, - @JsonProperty("extension") val extension: String - ) + open val domainUrl = "embedsito.com" override fun getExtractorUrl(id: String): String { return "$domainUrl/api/source/$id" @@ -108,16 +86,17 @@ open class XStreamCdn : ExtractorApi() { url: String, referer: String?, subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit + callback: (ExtractorLink) -> Unit, ) { val headers = mapOf( "Referer" to url, "User-Agent" to "Mozilla/5.0 (Windows NT 10.0; rv:78.0) Gecko/20100101 Firefox/78.0", ) + val id = url.trimEnd('/').split("/").last() - val newUrl = "https://${domainUrl}/api/source/${id}" + val newUrl = "https://$domainUrl/api/source/$id" app.post(newUrl, headers = headers).let { res -> - val sources = tryParseJson(res.text) + val sources = tryParseJson(res.text) sources?.let { if (it.success && it.data != null) { it.data.map { source -> @@ -135,7 +114,7 @@ open class XStreamCdn : ExtractorApi() { } } - val userData = sources?.player?.poster_file?.split("/")?.get(2) + val userData = sources?.player?.posterFile?.split("/")?.get(2) sources?.captions?.map { subtitleCallback.invoke( newSubtitleFile( @@ -146,4 +125,31 @@ open class XStreamCdn : ExtractorApi() { } } } -} \ No newline at end of file + + @Serializable + private data class ResponseData( + @JsonProperty("file") @SerialName("file") val file: String, + @JsonProperty("label") @SerialName("label") val label: String, + ) + + @Serializable + private data class Player( + @JsonProperty("poster_file") @SerialName("poster_file") val posterFile: String? = null, + ) + + @Serializable + private data class ResponseJson( + @JsonProperty("success") @SerialName("success") val success: Boolean, + @JsonProperty("player") @SerialName("player") val player: Player? = null, + @JsonProperty("data") @SerialName("data") val data: List?, + @JsonProperty("captions") @SerialName("captions") val captions: List?, + ) + + @Serializable + private data class Captions( + @JsonProperty("id") @SerialName("id") val id: String, + @JsonProperty("hash") @SerialName("hash") val hash: String, + @JsonProperty("language") @SerialName("language") val language: String, + @JsonProperty("extension") @SerialName("extension") val extension: String, + ) +} diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/YourUpload.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/YourUpload.kt index 9f2d6bde0..88eb58333 100644 --- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/YourUpload.kt +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/YourUpload.kt @@ -7,8 +7,10 @@ import com.lagradost.cloudstream3.utils.ExtractorApi import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.getQualityFromName import com.lagradost.cloudstream3.utils.newExtractorLink +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable -open class YourUpload: ExtractorApi() { +open class YourUpload : ExtractorApi() { override val name = "Yourupload" override val mainUrl = "https://www.yourupload.com" override val requiresReferer = false @@ -19,13 +21,11 @@ open class YourUpload: ExtractorApi() { val quality = Regex("\\d{3,4}p").find(this.select("title").text())?.groupValues?.get(0) this.select("script").map { script -> if (script.data().contains("var jwplayerOptions = {")) { - val data = - script.data().substringAfter("var jwplayerOptions = {").substringBefore(",\n") + val data = script.data().substringAfter("var jwplayerOptions = {").substringBefore(",\n") val link = tryParseJson( - "{${ - data.replace("file", "\"file\"").replace("'", "\"") - }}" + "{${data.replace("file", "\"file\"").replace("'", "\"")}}" ) + sources.add( newExtractorLink( source = name, @@ -39,11 +39,12 @@ open class YourUpload: ExtractorApi() { } } } + return sources } + @Serializable private data class ResponseSource( - @JsonProperty("file") val file: String, + @JsonProperty("file") @SerialName("file") val file: String, ) - -} \ No newline at end of file +} From c66b0eadd62f5a781ae1c07c6780eb4028cbfd5b Mon Sep 17 00:00:00 2001 From: firelight <147925818+fire-light42@users.noreply.github.com> Date: Tue, 14 Jul 2026 13:29:29 +0000 Subject: [PATCH 20/38] Fix failed cast in ContextHelper --- app/src/main/java/com/lagradost/cloudstream3/CloudStreamApp.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/CloudStreamApp.kt b/app/src/main/java/com/lagradost/cloudstream3/CloudStreamApp.kt index a9cd9c01e..466ef5a00 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/CloudStreamApp.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/CloudStreamApp.kt @@ -113,7 +113,7 @@ class CloudStreamApp : Application(), SingletonImageLoader.Factory { get() = _context?.get() private set(value) { _context = WeakReference(value) - setContext(WeakReference(value)) + setContext(value) } fun getKeyClass(path: String, valueType: Class): T? { From 36502213342b98ab297f1ab7c68736a4b1b5bb86 Mon Sep 17 00:00:00 2001 From: Kraptor123 <89366989+Kraptor123@users.noreply.github.com> Date: Tue, 14 Jul 2026 18:05:15 +0300 Subject: [PATCH 21/38] [skip ci] ui: Hide pinned NSFW-only extensions from non-NSFW categories (#2923) --- .../com/lagradost/cloudstream3/ui/home/HomeFragment.kt | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt index ab0715c6d..96c5f9f56 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt @@ -451,7 +451,14 @@ class HomeFragment : BaseFragment( arrayAdapter.clear() val sortedApis = validAPIs .filter { - it.hasMainPage && (pinnedphashset.contains(it.name) || it.supportedTypes.any( + val isPinned = pinnedphashset.contains(it.name) + + // Hide pinned NSFW when NSFW not selected. NSFW is distracting when not chosen. + if (isPinned && !preSelectedTypes.contains(TvType.NSFW)) { + if (it.supportedTypes.all { type -> type == TvType.NSFW }) return@filter false + } + + it.hasMainPage && (isPinned || it.supportedTypes.any( preSelectedTypes::contains )) } From c09c06820dc3b47d237893c22202f5687434ca0c Mon Sep 17 00:00:00 2001 From: Leandre Chamberland-Dozois Date: Tue, 14 Jul 2026 11:19:39 -0400 Subject: [PATCH 22/38] fix(download): remove deleted/cancelled items from the Downloads list in real time (#1227) (#2598) --- .../ui/download/DownloadViewModel.kt | 27 ++++++++++++++++++- .../ui/download/button/BaseFetchButton.kt | 12 +++++---- 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadViewModel.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadViewModel.kt index 0d35d5670..19c006b28 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadViewModel.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadViewModel.kt @@ -10,6 +10,7 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.lagradost.api.Log +import com.lagradost.cloudstream3.CloudStreamApp import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.isEpisodeBased import com.lagradost.cloudstream3.mvvm.Resource @@ -36,6 +37,7 @@ import com.lagradost.cloudstream3.utils.ResourceLiveData import com.lagradost.cloudstream3.utils.downloader.DownloadObjects import com.lagradost.cloudstream3.utils.downloader.DownloadQueueManager import com.lagradost.cloudstream3.utils.downloader.VideoDownloadManager.deleteFilesAndUpdateSettings +import com.lagradost.cloudstream3.utils.downloader.VideoDownloadManager.downloadDeleteEvent import com.lagradost.cloudstream3.utils.downloader.VideoDownloadManager.getDownloadFileInfo import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext @@ -67,6 +69,17 @@ class DownloadViewModel : ViewModel() { private val _selectedItemIds = ConsistentLiveData?>(null) val selectedItemIds: LiveData?> = _selectedItemIds + init { + // Keep the Downloads list in sync when a download is deleted/cancelled from + // anywhere in the app (result page button, queue, notification, etc.). See + // onDownloadDeleted for the rationale (issue #1227). + downloadDeleteEvent += ::onDownloadDeleted + } + + override fun onCleared() { + downloadDeleteEvent -= ::onDownloadDeleted + super.onCleared() + } fun cancelSelection() { updateSelectedItems { null } @@ -389,6 +402,18 @@ class DownloadViewModel : ViewModel() { postChildren(_childCards.success?.filter { it.data.id !in idsToRemove }) } + /** + * Refreshes the Downloads screen in real time when a download is deleted/cancelled. + */ + private fun onDownloadDeleted(id: Int) { + // Keep multi-select state consistent: forget the removed id if it was selected. + updateSelectedItems { it?.minus(id) } + + val context = CloudStreamApp.context ?: return + updateHeaderList(context) + postChildren(_childCards.success?.filterNot { it.data.id == id }) + } + private fun updateStorageStats(visual: List) { try { val stat = StatFs(Environment.getExternalStorageDirectory().path) @@ -584,4 +609,4 @@ class DownloadViewModel : ViewModel() { val names: List, val parentName: String? ) -} \ No newline at end of file +} diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/download/button/BaseFetchButton.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/download/button/BaseFetchButton.kt index 382a770cd..450dc9a61 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/download/button/BaseFetchButton.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/download/button/BaseFetchButton.kt @@ -164,9 +164,11 @@ abstract class BaseFetchButton(context: Context, attributeSet: AttributeSet) : } } - /*fun downloadDeleteEvent(data: Int) { - - }*/ + fun downloadDeleteEvent(data: Int) { + if (data == persistentId) { + resetView() + } + } /*fun downloadEvent(data: Pair) { val (id, action) = data @@ -185,7 +187,7 @@ abstract class BaseFetchButton(context: Context, attributeSet: AttributeSet) : override fun onAttachedToWindow() { VideoDownloadManager.downloadStatusEvent += ::downloadStatusEvent - // VideoDownloadManager.downloadDeleteEvent += ::downloadDeleteEvent + VideoDownloadManager.downloadDeleteEvent += ::downloadDeleteEvent // VideoDownloadManager.downloadEvent += ::downloadEvent VideoDownloadManager.downloadProgressEvent += ::downloadProgressEvent @@ -200,7 +202,7 @@ abstract class BaseFetchButton(context: Context, attributeSet: AttributeSet) : override fun onDetachedFromWindow() { VideoDownloadManager.downloadStatusEvent -= ::downloadStatusEvent - // VideoDownloadManager.downloadDeleteEvent -= ::downloadDeleteEvent + VideoDownloadManager.downloadDeleteEvent -= ::downloadDeleteEvent // VideoDownloadManager.downloadEvent -= ::downloadEvent VideoDownloadManager.downloadProgressEvent -= ::downloadProgressEvent From fb8bcb4ac70e4277ce29c540c6a2ea386de5268b Mon Sep 17 00:00:00 2001 From: firelight <147925818+fire-light42@users.noreply.github.com> Date: Tue, 14 Jul 2026 17:23:52 +0000 Subject: [PATCH 23/38] more robust date parsing (#3068) --- .../com/lagradost/cloudstream3/MainAPI.kt | 12 +++++++++-- .../lagradost/cloudstream3/EpisodeDateTest.kt | 20 +++++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/MainAPI.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/MainAPI.kt index 3e8774db2..ca024b463 100644 --- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/MainAPI.kt +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/MainAPI.kt @@ -2576,13 +2576,21 @@ constructor( @OptIn(FormatStringsInDatetimeFormats::class) fun Episode.addDate(date: String?, format: String = "yyyy-MM-dd") { - if (date == null) return + if (date.isNullOrBlank()) return this.date = runCatching { // First try standard ISO 8601 (e.g. "2026-01-01T12:30:00.000Z", "2026-05-17T14:35+02:00") runCatching { Instant.parse(date).toEpochMilliseconds() } .getOrElse { val fmt = DateTimeComponents.Format { byUnicodePattern(format) } - val components = DateTimeComponents.parse(date, fmt) + + // Try parsing the full date first then only parse the beginning of the string + // May lose time, but better than failing. + val components = runCatching { + DateTimeComponents.parse(date, fmt) + }.recoverCatching { + DateTimeComponents.parse(date.trimStart().take(format.length), fmt) + }.getOrThrow() + /** * Try multiple conversions in order of precision for non-ISO-8601 formats, * since the date string may or may not include time and/or timezone offset: diff --git a/library/src/commonTest/kotlin/com/lagradost/cloudstream3/EpisodeDateTest.kt b/library/src/commonTest/kotlin/com/lagradost/cloudstream3/EpisodeDateTest.kt index 4dc56978e..422b4cd93 100644 --- a/library/src/commonTest/kotlin/com/lagradost/cloudstream3/EpisodeDateTest.kt +++ b/library/src/commonTest/kotlin/com/lagradost/cloudstream3/EpisodeDateTest.kt @@ -31,6 +31,26 @@ class EpisodeDateTest { assertEquals(expected, ep.date) } + @Test + fun addDateDefaultFormatParsesExtraTime() { + val ep = episode() + ep.addDate("2026-05-17 12:30:45") + val expected = LocalDate(2026, 5, 17) + .atStartOfDayIn(TimeZone.currentSystemDefault()) + .toEpochMilliseconds() + assertEquals(expected, ep.date) + } + + @Test + fun addDateDefaultFormatParsesExtraJunk() { + val ep = episode() + ep.addDate(" 2026-05-17 random data") + val expected = LocalDate(2026, 5, 17) + .atStartOfDayIn(TimeZone.currentSystemDefault()) + .toEpochMilliseconds() + assertEquals(expected, ep.date) + } + @Test fun addDateNullDoesNotSetDate() { val ep = episode() From fef17079a8263e9afd20124ddbd13334f5c4835c Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Wed, 15 Jul 2026 06:05:44 -0700 Subject: [PATCH 24/38] [skip ci] Coroutines KMP API (#2836) --- library/api/jvm/library.api | 8 ++ .../cloudstream3/mvvm/ArchComponentExt.kt | 87 ++++++------------- .../cloudstream3/utils/Coroutines.kt | 7 +- .../cloudstream3/mvvm/ArchComponentExtTest.kt | 31 +++++++ .../mvvm/ArchComponentExt.jvmCommon.kt | 37 ++++++++ .../utils/Coroutines.jvmCommon.kt | 4 + 6 files changed, 112 insertions(+), 62 deletions(-) create mode 100644 library/src/commonTest/kotlin/com/lagradost/cloudstream3/mvvm/ArchComponentExtTest.kt create mode 100644 library/src/jvmCommonMain/kotlin/com/lagradost/cloudstream3/mvvm/ArchComponentExt.jvmCommon.kt diff --git a/library/api/jvm/library.api b/library/api/jvm/library.api index 1b39b3906..0b0996f94 100644 --- a/library/api/jvm/library.api +++ b/library/api/jvm/library.api @@ -7235,6 +7235,10 @@ public final class com/lagradost/cloudstream3/mvvm/ArchComponentExtKt { public static final fun throwAbleToResource (Ljava/lang/Throwable;)Lcom/lagradost/cloudstream3/mvvm/Resource; } +public final class com/lagradost/cloudstream3/mvvm/ArchComponentExt_jvmCommonKt { + public static final fun platformThrowAbleToResource (Ljava/lang/Throwable;)Lcom/lagradost/cloudstream3/mvvm/Resource; +} + public final class com/lagradost/cloudstream3/mvvm/DebugException : java/lang/Exception { public fun (Ljava/lang/String;)V } @@ -7438,6 +7442,10 @@ public final class com/lagradost/cloudstream3/utils/Coroutines { public final fun threadSafeListOf ([Ljava/lang/Object;)Ljava/util/List; } +public final class com/lagradost/cloudstream3/utils/Coroutines_jvmCommonKt { + public static final fun getWorkerDispatcher ()Lkotlinx/coroutines/CoroutineDispatcher; +} + public final class com/lagradost/cloudstream3/utils/Coroutines_jvmKt { public static final fun runOnMainThreadNative (Lkotlin/jvm/functions/Function0;)V } diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/mvvm/ArchComponentExt.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/mvvm/ArchComponentExt.kt index 4cd8eefa6..73de880db 100644 --- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/mvvm/ArchComponentExt.kt +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/mvvm/ArchComponentExt.kt @@ -4,15 +4,11 @@ import androidx.annotation.AnyThread import com.lagradost.api.Log import com.lagradost.cloudstream3.ErrorLoadingException import com.lagradost.cloudstream3.utils.AppDebug +import com.lagradost.cloudstream3.utils.Coroutines.ioWork import com.lagradost.cloudstream3.utils.WorkerThread import kotlinx.coroutines.* -import java.io.InterruptedIOException -import java.net.SocketTimeoutException -import java.net.UnknownHostException -import javax.net.ssl.SSLHandshakeException import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext -import kotlin.reflect.full.NoSuchPropertyException const val DEBUG_EXCEPTION = "THIS IS A DEBUG EXCEPTION!" const val DEBUG_PRINT = "DEBUG PRINT" @@ -61,7 +57,7 @@ sealed class Resource { companion object { fun fromResult(result: Result) : Resource { val value = result.getOrNull() - return if(value != null) { + return if (value != null) { Success(value) } else { throwAbleToResource(result.exceptionOrNull() ?: Exception("this should not be possible")) @@ -72,7 +68,6 @@ sealed class Resource { fun logError(throwable: Throwable) { Log.d("ApiError", "-------------------------------------------------------------------") - Log.d("ApiError", "safeApiCall: " + throwable.localizedMessage) Log.d("ApiError", "safeApiCall: " + throwable.message) throwable.printStackTrace() Log.d("ApiError", "-------------------------------------------------------------------") @@ -129,16 +124,21 @@ suspend fun suspendSafeApiCall(apiCall: suspend () -> T): T? { } fun Throwable.getAllMessages(): String { - return (this.localizedMessage ?: "") + (this.cause?.getAllMessages()?.let { "\n$it" } ?: "") + return (this.message ?: "") + (this.cause?.getAllMessages()?.let { "\n$it" } ?: "") } fun Throwable.getStackTracePretty(showMessage: Boolean = true): String { - val prefix = if (showMessage) this.localizedMessage?.let { "\n$it" } ?: "" else "" - return prefix + this.stackTrace.joinToString( - separator = "\n" - ) { - "${it.fileName} ${it.lineNumber}" - } + val prefix = if (showMessage) this.message?.let { "\n$it" } ?: "" else "" + return prefix + this.stackTraceToString() + .lines() + .mapNotNull { line -> + val trimmed = line.trim() + if (!trimmed.startsWith("at ")) return@mapNotNull null + // pull "File.kt:42" out of "at com.pkg.Class.method(File.kt:42)" + Regex("""\(([^)]+)\)$""").find(trimmed)?.groupValues?.get(1) + ?.replace(":", " ") + } + .joinToString("\n") } fun safeFail(throwable: Throwable): Resource { @@ -162,50 +162,26 @@ fun CoroutineScope.launchSafe( return this.launch(context, start, obj) } +expect fun platformThrowAbleToResource(throwable: Throwable): Resource + fun throwAbleToResource( throwable: Throwable ): Resource { return when (throwable) { - is NoSuchMethodException, is NoSuchFieldException, is NoSuchMethodError, is NoSuchFieldError, is NoSuchPropertyException -> { - Resource.Failure( - false, - "App or extension is outdated, update the app or try pre-release.\n${throwable.message}" // todo add exact version? - ) - } - is NullPointerException -> { - for (line in throwable.stackTrace) { - if (line?.fileName?.endsWith("provider.kt", ignoreCase = true) == true) { - return Resource.Failure( - false, - "NullPointerException at ${line.fileName} ${line.lineNumber}\nSite might have updated or added Cloudflare/DDOS protection" - ) - } + val traceLine = throwable.stackTraceToString() + .lines() + .firstOrNull { it.contains("provider.kt", ignoreCase = true) } + ?.let { Regex("""\(([^)]+)\)$""").find(it)?.groupValues?.get(1) } + if (traceLine != null) { + return Resource.Failure( + false, + "NullPointerException at $traceLine\nSite might have updated or added Cloudflare/DDOS protection" + ) } safeFail(throwable) } - is SocketTimeoutException, is InterruptedIOException -> { - Resource.Failure( - true, - "Connection Timeout\nPlease try again later." - ) - } -// is HttpException -> { -// Resource.Failure( -// false, -// throwable.statusCode, -// null, -// throwable.message ?: "HttpException" -// ) -// } - is UnknownHostException -> { - Resource.Failure( - true, - "Cannot connect to server, try again later.\n${throwable.message}" - ) - } - is ErrorLoadingException -> { Resource.Failure( true, @@ -217,20 +193,13 @@ fun throwAbleToResource( Resource.Failure(false, "This operation is not implemented.") } - is SSLHandshakeException -> { - Resource.Failure( - true, - (throwable.message ?: "SSLHandshakeException") + "\nTry a VPN or DNS." - ) - } - is CancellationException -> { throwable.cause?.let { throwAbleToResource(it) } ?: safeFail(throwable) } - else -> safeFail(throwable) + else -> platformThrowAbleToResource(throwable) } } @@ -238,9 +207,9 @@ fun throwAbleToResource( suspend fun safeApiCall( @WorkerThread apiCall: suspend () -> T, ): Resource { - return withContext(Dispatchers.IO) { + return apiCall.ioWork { work -> try { - Resource.Success(apiCall.invoke()) + Resource.Success(work.invoke()) } catch (throwable: Throwable) { logError(throwable) throwAbleToResource(throwable) diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/Coroutines.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/Coroutines.kt index 4536eb88b..4a269c525 100644 --- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/Coroutines.kt +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/Coroutines.kt @@ -10,6 +10,7 @@ import kotlinx.coroutines.* @AnyThread expect fun runOnMainThreadNative(@MainThread work: (() -> Unit)) +expect val workerDispatcher: CoroutineDispatcher internal expect annotation class WorkerThread() object Coroutines { @@ -24,7 +25,7 @@ object Coroutines { @AnyThread fun T.ioSafe(@WorkerThread work: suspend (CoroutineScope.(T) -> Unit)): Job { val value = this - return CoroutineScope(Dispatchers.IO).launchSafe { + return CoroutineScope(workerDispatcher).launchSafe { work(value) } } @@ -32,7 +33,7 @@ object Coroutines { @AnyThread suspend fun V.ioWorkSafe(@WorkerThread work: suspend (CoroutineScope.(V) -> T)): T? { val value = this - return withContext(Dispatchers.IO) { + return withContext(workerDispatcher) { try { work(value) } catch (e: Exception) { @@ -45,7 +46,7 @@ object Coroutines { @AnyThread suspend fun V.ioWork(@WorkerThread work: suspend (CoroutineScope.(V) -> T)): T { val value = this - return withContext(Dispatchers.IO) { + return withContext(workerDispatcher) { work(value) } } diff --git a/library/src/commonTest/kotlin/com/lagradost/cloudstream3/mvvm/ArchComponentExtTest.kt b/library/src/commonTest/kotlin/com/lagradost/cloudstream3/mvvm/ArchComponentExtTest.kt new file mode 100644 index 000000000..970fe728d --- /dev/null +++ b/library/src/commonTest/kotlin/com/lagradost/cloudstream3/mvvm/ArchComponentExtTest.kt @@ -0,0 +1,31 @@ +package com.lagradost.cloudstream3.mvvm + +import kotlin.test.Test +import kotlin.test.assertFalse +import kotlin.test.assertTrue + +class ArchComponentExtTest { + + @Test + fun getStackTracePrettyStripsFullyQualifiedClassNames() { + val throwable = try { + throw IllegalStateException("Exception") + } catch (t: Throwable) { + t + } + + val pretty = throwable.getStackTracePretty() + + // Should not contain the fully qualified package/class prefix + assertFalse( + pretty.contains("com.lagradost.cloudstream3.mvvm.ArchComponentExtTest"), + "Expected no fully qualified class names, got:\n$pretty" + ) + + // Should still reference the source file itself + assertTrue( + pretty.contains("ArchComponentExtTest.kt"), + "Expected pretty stack trace to reference a .kt file, got:\n$pretty" + ) + } +} diff --git a/library/src/jvmCommonMain/kotlin/com/lagradost/cloudstream3/mvvm/ArchComponentExt.jvmCommon.kt b/library/src/jvmCommonMain/kotlin/com/lagradost/cloudstream3/mvvm/ArchComponentExt.jvmCommon.kt new file mode 100644 index 000000000..f2ff5e3ac --- /dev/null +++ b/library/src/jvmCommonMain/kotlin/com/lagradost/cloudstream3/mvvm/ArchComponentExt.jvmCommon.kt @@ -0,0 +1,37 @@ +package com.lagradost.cloudstream3.mvvm + +import java.io.InterruptedIOException +import java.net.SocketTimeoutException +import java.net.UnknownHostException +import javax.net.ssl.SSLHandshakeException +import kotlin.reflect.full.NoSuchPropertyException + +actual fun platformThrowAbleToResource(throwable: Throwable): Resource { + return when (throwable) { + is NoSuchMethodException, is NoSuchFieldException, is NoSuchMethodError, is NoSuchFieldError, is NoSuchPropertyException -> { + Resource.Failure( + false, + "App or extension is outdated, update the app or try pre-release.\n${throwable.message}" // todo add exact version? + ) + } + is SocketTimeoutException, is InterruptedIOException -> { + Resource.Failure( + true, + "Connection Timeout\nPlease try again later." + ) + } + is UnknownHostException -> { + Resource.Failure( + true, + "Cannot connect to server, try again later.\n${throwable.message}" + ) + } + is SSLHandshakeException -> { + Resource.Failure( + true, + (throwable.message ?: "SSLHandshakeException") + "\nTry a VPN or DNS." + ) + } + else -> safeFail(throwable) + } +} diff --git a/library/src/jvmCommonMain/kotlin/com/lagradost/cloudstream3/utils/Coroutines.jvmCommon.kt b/library/src/jvmCommonMain/kotlin/com/lagradost/cloudstream3/utils/Coroutines.jvmCommon.kt index 2ae886f59..03a5be6c2 100644 --- a/library/src/jvmCommonMain/kotlin/com/lagradost/cloudstream3/utils/Coroutines.jvmCommon.kt +++ b/library/src/jvmCommonMain/kotlin/com/lagradost/cloudstream3/utils/Coroutines.jvmCommon.kt @@ -1,3 +1,7 @@ package com.lagradost.cloudstream3.utils +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Dispatchers + +actual val workerDispatcher: CoroutineDispatcher = Dispatchers.IO internal actual typealias WorkerThread = androidx.annotation.WorkerThread From 91e533686eddbecc1d0a895f25ee5d592a6528b3 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Wed, 15 Jul 2026 06:31:41 -0700 Subject: [PATCH 25/38] [skip ci] Rewrite TmdbProvider to remove external lib dependency (#2800) --- app/build.gradle.kts | 2 + gradle/libs.versions.toml | 2 - library/api/jvm/library.api | 694 ++++++++++++++++++ library/build.gradle.kts | 1 - .../metaproviders/TmdbProvider.kt | 661 ++++++++++------- 5 files changed, 1076 insertions(+), 284 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 0feb866be..2ad8451a3 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -276,6 +276,8 @@ dependencies { implementation(libs.jackson.module.kotlin) // JSON Parser implementation(libs.zipline) + // Temp/deprecated; will be removed once extensions have time to migrate from using it + implementation("com.google.code.gson:gson:2.11.0") // Deprecated; will be removed once extensions have time to migrate from using it implementation("me.xdrop:fuzzywuzzy:1.4.0") diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index fdfaee7b2..898529d9a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -49,7 +49,6 @@ qrcodeKotlin = "4.5.0" rhino = { strictly = "1.8.1" } # Requires minSdk 26 or later beginning at version 1.9.0 safefile = "0.0.8" shimmer = "0.5.0" -tmdbJava = "2.13.0" torrentserver = "7861970" tvprovider = "1.1.0" video = "1.0.0" @@ -129,7 +128,6 @@ qrcode-kotlin = { module = "io.github.g0dkar:qrcode-kotlin", version.ref = "qrco rhino = { module = "org.mozilla:rhino", version.ref = "rhino" } safefile = { module = "com.github.LagradOst:SafeFile", version.ref = "safefile" } shimmer = { module = "com.facebook.shimmer:shimmer", version.ref = "shimmer" } -tmdb-java = { module = "com.uwetrottmann.tmdb2:tmdb-java", version.ref = "tmdbJava" } torrentserver = { module = "com.github.recloudstream:torrentserver", version.ref = "torrentserver" } tvprovider = { module = "androidx.tvprovider:tvprovider", version.ref = "tvprovider" } video = { module = "com.google.android.mediahome:video", version.ref = "video" } diff --git a/library/api/jvm/library.api b/library/api/jvm/library.api index 0b0996f94..0d9b8f76e 100644 --- a/library/api/jvm/library.api +++ b/library/api/jvm/library.api @@ -6613,6 +6613,7 @@ public final class com/lagradost/cloudstream3/metaproviders/SyncRedirector { } public final class com/lagradost/cloudstream3/metaproviders/TmdbLink { + public static final field Companion Lcom/lagradost/cloudstream3/metaproviders/TmdbLink$Companion; public fun (Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/String;)V public synthetic fun (Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun component1 ()Ljava/lang/String; @@ -6632,6 +6633,21 @@ public final class com/lagradost/cloudstream3/metaproviders/TmdbLink { public fun toString ()Ljava/lang/String; } +public final synthetic class com/lagradost/cloudstream3/metaproviders/TmdbLink$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/metaproviders/TmdbLink$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/metaproviders/TmdbLink; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/metaproviders/TmdbLink;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/metaproviders/TmdbLink$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public class com/lagradost/cloudstream3/metaproviders/TmdbProvider : com/lagradost/cloudstream3/MainAPI { public fun ()V public fun fetchContentRating (Ljava/lang/Integer;Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; @@ -6650,6 +6666,684 @@ public class com/lagradost/cloudstream3/metaproviders/TmdbProvider : com/lagrado public fun search (Ljava/lang/String;ILkotlin/coroutines/Continuation;)Ljava/lang/Object; } +public final class com/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbCastMember { + public static final field Companion Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbCastMember$Companion; + public fun ()V + public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V + public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Ljava/lang/String; + public final fun component2 ()Ljava/lang/String; + public final fun component3 ()Ljava/lang/String; + public final fun copy (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbCastMember; + public static synthetic fun copy$default (Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbCastMember;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbCastMember; + public fun equals (Ljava/lang/Object;)Z + public final fun getCharacter ()Ljava/lang/String; + public final fun getName ()Ljava/lang/String; + public final fun getProfilePath ()Ljava/lang/String; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final synthetic class com/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbCastMember$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbCastMember$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbCastMember; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbCastMember;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbCastMember$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbContentRating { + public static final field Companion Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbContentRating$Companion; + public fun ()V + public fun (Ljava/lang/String;Ljava/lang/String;)V + public synthetic fun (Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Ljava/lang/String; + public final fun component2 ()Ljava/lang/String; + public final fun copy (Ljava/lang/String;Ljava/lang/String;)Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbContentRating; + public static synthetic fun copy$default (Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbContentRating;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbContentRating; + public fun equals (Ljava/lang/Object;)Z + public final fun getCountry ()Ljava/lang/String; + public final fun getRating ()Ljava/lang/String; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final synthetic class com/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbContentRating$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbContentRating$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbContentRating; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbContentRating;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbContentRating$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbContentRatings { + public static final field Companion Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbContentRatings$Companion; + public fun ()V + public fun (Ljava/util/List;)V + public synthetic fun (Ljava/util/List;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Ljava/util/List; + public final fun copy (Ljava/util/List;)Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbContentRatings; + public static synthetic fun copy$default (Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbContentRatings;Ljava/util/List;ILjava/lang/Object;)Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbContentRatings; + public fun equals (Ljava/lang/Object;)Z + public final fun getResults ()Ljava/util/List; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final synthetic class com/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbContentRatings$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbContentRatings$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbContentRatings; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbContentRatings;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbContentRatings$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbCredits { + public static final field Companion Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbCredits$Companion; + public fun ()V + public fun (Ljava/util/List;)V + public synthetic fun (Ljava/util/List;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Ljava/util/List; + public final fun copy (Ljava/util/List;)Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbCredits; + public static synthetic fun copy$default (Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbCredits;Ljava/util/List;ILjava/lang/Object;)Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbCredits; + public fun equals (Ljava/lang/Object;)Z + public final fun getCast ()Ljava/util/List; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final synthetic class com/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbCredits$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbCredits$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbCredits; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbCredits;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbCredits$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbEpisode { + public static final field Companion Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbEpisode$Companion; + public fun ()V + public fun (Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Double;Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbIds;)V + public synthetic fun (Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Double;Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbIds;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Ljava/lang/Integer; + public final fun component2 ()Ljava/lang/String; + public final fun component3 ()Ljava/lang/String; + public final fun component4 ()Ljava/lang/Integer; + public final fun component5 ()Ljava/lang/Integer; + public final fun component6 ()Ljava/lang/String; + public final fun component7 ()Ljava/lang/String; + public final fun component8 ()Ljava/lang/Double; + public final fun component9 ()Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbIds; + public final fun copy (Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Double;Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbIds;)Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbEpisode; + public static synthetic fun copy$default (Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbEpisode;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Double;Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbIds;ILjava/lang/Object;)Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbEpisode; + public fun equals (Ljava/lang/Object;)Z + public final fun getAirDate ()Ljava/lang/String; + public final fun getEpisodeNumber ()Ljava/lang/Integer; + public final fun getExternalIds ()Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbIds; + public final fun getId ()Ljava/lang/Integer; + public final fun getName ()Ljava/lang/String; + public final fun getOverview ()Ljava/lang/String; + public final fun getSeasonNumber ()Ljava/lang/Integer; + public final fun getStillPath ()Ljava/lang/String; + public final fun getVoteAverage ()Ljava/lang/Double; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final synthetic class com/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbEpisode$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbEpisode$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbEpisode; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbEpisode;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbEpisode$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbGenre { + public static final field Companion Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbGenre$Companion; + public fun ()V + public fun (Ljava/lang/Integer;Ljava/lang/String;)V + public synthetic fun (Ljava/lang/Integer;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Ljava/lang/Integer; + public final fun component2 ()Ljava/lang/String; + public final fun copy (Ljava/lang/Integer;Ljava/lang/String;)Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbGenre; + public static synthetic fun copy$default (Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbGenre;Ljava/lang/Integer;Ljava/lang/String;ILjava/lang/Object;)Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbGenre; + public fun equals (Ljava/lang/Object;)Z + public final fun getId ()Ljava/lang/Integer; + public final fun getName ()Ljava/lang/String; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final synthetic class com/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbGenre$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbGenre$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbGenre; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbGenre;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbGenre$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbIds { + public static final field Companion Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbIds$Companion; + public fun ()V + public fun (Ljava/lang/String;Ljava/lang/Integer;)V + public synthetic fun (Ljava/lang/String;Ljava/lang/Integer;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Ljava/lang/String; + public final fun component2 ()Ljava/lang/Integer; + public final fun copy (Ljava/lang/String;Ljava/lang/Integer;)Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbIds; + public static synthetic fun copy$default (Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbIds;Ljava/lang/String;Ljava/lang/Integer;ILjava/lang/Object;)Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbIds; + public fun equals (Ljava/lang/Object;)Z + public final fun getImdbId ()Ljava/lang/String; + public final fun getTvdbId ()Ljava/lang/Integer; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final synthetic class com/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbIds$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbIds$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbIds; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbIds;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbIds$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbMovieDetail { + public static final field Companion Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbMovieDetail$Companion; + public fun ()V + public fun (Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Double;Ljava/util/List;Ljava/lang/Integer;Ljava/lang/String;Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbIds;Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbVideos;Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbCredits;Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbPageResult;Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbPageResult;Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbReleaseDates;)V + public synthetic fun (Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Double;Ljava/util/List;Ljava/lang/Integer;Ljava/lang/String;Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbIds;Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbVideos;Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbCredits;Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbPageResult;Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbPageResult;Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbReleaseDates;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Ljava/lang/Integer; + public final fun component10 ()Ljava/lang/String; + public final fun component11 ()Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbIds; + public final fun component12 ()Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbVideos; + public final fun component13 ()Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbCredits; + public final fun component14 ()Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbPageResult; + public final fun component15 ()Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbPageResult; + public final fun component16 ()Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbReleaseDates; + public final fun component2 ()Ljava/lang/String; + public final fun component3 ()Ljava/lang/String; + public final fun component4 ()Ljava/lang/String; + public final fun component5 ()Ljava/lang/String; + public final fun component6 ()Ljava/lang/String; + public final fun component7 ()Ljava/lang/Double; + public final fun component8 ()Ljava/util/List; + public final fun component9 ()Ljava/lang/Integer; + public final fun copy (Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Double;Ljava/util/List;Ljava/lang/Integer;Ljava/lang/String;Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbIds;Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbVideos;Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbCredits;Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbPageResult;Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbPageResult;Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbReleaseDates;)Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbMovieDetail; + public static synthetic fun copy$default (Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbMovieDetail;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Double;Ljava/util/List;Ljava/lang/Integer;Ljava/lang/String;Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbIds;Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbVideos;Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbCredits;Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbPageResult;Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbPageResult;Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbReleaseDates;ILjava/lang/Object;)Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbMovieDetail; + public fun equals (Ljava/lang/Object;)Z + public final fun getCredits ()Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbCredits; + public final fun getDisplayTitle ()Ljava/lang/String; + public final fun getExternalIds ()Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbIds; + public final fun getGenres ()Ljava/util/List; + public final fun getId ()Ljava/lang/Integer; + public final fun getImdbId ()Ljava/lang/String; + public final fun getOriginalTitle ()Ljava/lang/String; + public final fun getOverview ()Ljava/lang/String; + public final fun getPosterPath ()Ljava/lang/String; + public final fun getRecommendations ()Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbPageResult; + public final fun getReleaseDate ()Ljava/lang/String; + public final fun getReleaseDates ()Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbReleaseDates; + public final fun getRuntime ()Ljava/lang/Integer; + public final fun getSimilar ()Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbPageResult; + public final fun getTitle ()Ljava/lang/String; + public final fun getVideos ()Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbVideos; + public final fun getVoteAverage ()Ljava/lang/Double; + public final fun getYear ()Ljava/lang/Integer; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final synthetic class com/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbMovieDetail$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbMovieDetail$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbMovieDetail; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbMovieDetail;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbMovieDetail$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbMultiResult { + public static final field Companion Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbMultiResult$Companion; + public fun ()V + public fun (Ljava/util/List;)V + public synthetic fun (Ljava/util/List;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Ljava/util/List; + public final fun copy (Ljava/util/List;)Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbMultiResult; + public static synthetic fun copy$default (Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbMultiResult;Ljava/util/List;ILjava/lang/Object;)Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbMultiResult; + public fun equals (Ljava/lang/Object;)Z + public final fun getResults ()Ljava/util/List; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final synthetic class com/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbMultiResult$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbMultiResult$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbMultiResult; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbMultiResult;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbMultiResult$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbPageResult { + public static final field Companion Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbPageResult$Companion; + public fun ()V + public fun (Ljava/util/List;Ljava/lang/Integer;Ljava/lang/Integer;)V + public synthetic fun (Ljava/util/List;Ljava/lang/Integer;Ljava/lang/Integer;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Ljava/util/List; + public final fun component2 ()Ljava/lang/Integer; + public final fun component3 ()Ljava/lang/Integer; + public final fun copy (Ljava/util/List;Ljava/lang/Integer;Ljava/lang/Integer;)Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbPageResult; + public static synthetic fun copy$default (Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbPageResult;Ljava/util/List;Ljava/lang/Integer;Ljava/lang/Integer;ILjava/lang/Object;)Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbPageResult; + public fun equals (Ljava/lang/Object;)Z + public final fun getResults ()Ljava/util/List; + public final fun getTotalPages ()Ljava/lang/Integer; + public final fun getTotalResults ()Ljava/lang/Integer; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final synthetic class com/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbPageResult$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbPageResult$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbPageResult; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbPageResult;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbPageResult$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbReleaseDateEntry { + public static final field Companion Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbReleaseDateEntry$Companion; + public fun ()V + public fun (Ljava/lang/String;Ljava/lang/Integer;)V + public synthetic fun (Ljava/lang/String;Ljava/lang/Integer;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Ljava/lang/String; + public final fun component2 ()Ljava/lang/Integer; + public final fun copy (Ljava/lang/String;Ljava/lang/Integer;)Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbReleaseDateEntry; + public static synthetic fun copy$default (Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbReleaseDateEntry;Ljava/lang/String;Ljava/lang/Integer;ILjava/lang/Object;)Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbReleaseDateEntry; + public fun equals (Ljava/lang/Object;)Z + public final fun getCertification ()Ljava/lang/String; + public final fun getType ()Ljava/lang/Integer; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final synthetic class com/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbReleaseDateEntry$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbReleaseDateEntry$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbReleaseDateEntry; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbReleaseDateEntry;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbReleaseDateEntry$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbReleaseDateResult { + public static final field Companion Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbReleaseDateResult$Companion; + public fun ()V + public fun (Ljava/lang/String;Ljava/util/List;)V + public synthetic fun (Ljava/lang/String;Ljava/util/List;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Ljava/lang/String; + public final fun component2 ()Ljava/util/List; + public final fun copy (Ljava/lang/String;Ljava/util/List;)Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbReleaseDateResult; + public static synthetic fun copy$default (Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbReleaseDateResult;Ljava/lang/String;Ljava/util/List;ILjava/lang/Object;)Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbReleaseDateResult; + public fun equals (Ljava/lang/Object;)Z + public final fun getCountry ()Ljava/lang/String; + public final fun getReleaseDates ()Ljava/util/List; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final synthetic class com/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbReleaseDateResult$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbReleaseDateResult$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbReleaseDateResult; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbReleaseDateResult;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbReleaseDateResult$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbReleaseDates { + public static final field Companion Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbReleaseDates$Companion; + public fun ()V + public fun (Ljava/util/List;)V + public synthetic fun (Ljava/util/List;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Ljava/util/List; + public final fun copy (Ljava/util/List;)Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbReleaseDates; + public static synthetic fun copy$default (Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbReleaseDates;Ljava/util/List;ILjava/lang/Object;)Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbReleaseDates; + public fun equals (Ljava/lang/Object;)Z + public final fun getResults ()Ljava/util/List; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final synthetic class com/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbReleaseDates$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbReleaseDates$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbReleaseDates; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbReleaseDates;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbReleaseDates$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbSearchResult { + public static final field Companion Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbSearchResult$Companion; + public fun ()V + public fun (Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Double;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V + public synthetic fun (Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Double;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Ljava/lang/Integer; + public final fun component10 ()Ljava/lang/String; + public final fun component2 ()Ljava/lang/String; + public final fun component3 ()Ljava/lang/String; + public final fun component4 ()Ljava/lang/String; + public final fun component5 ()Ljava/lang/String; + public final fun component6 ()Ljava/lang/String; + public final fun component7 ()Ljava/lang/Double; + public final fun component8 ()Ljava/lang/String; + public final fun component9 ()Ljava/lang/String; + public final fun copy (Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Double;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbSearchResult; + public static synthetic fun copy$default (Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbSearchResult;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Double;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbSearchResult; + public fun equals (Ljava/lang/Object;)Z + public final fun getDisplayTitle ()Ljava/lang/String; + public final fun getFirstAirDate ()Ljava/lang/String; + public final fun getId ()Ljava/lang/Integer; + public final fun getMediaType ()Ljava/lang/String; + public final fun getName ()Ljava/lang/String; + public final fun getOriginalName ()Ljava/lang/String; + public final fun getOriginalTitle ()Ljava/lang/String; + public final fun getPosterPath ()Ljava/lang/String; + public final fun getReleaseDate ()Ljava/lang/String; + public final fun getTitle ()Ljava/lang/String; + public final fun getVoteAverage ()Ljava/lang/Double; + public final fun getYear ()Ljava/lang/Integer; + public fun hashCode ()I + public final fun isTv ()Z + public fun toString ()Ljava/lang/String; +} + +public final synthetic class com/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbSearchResult$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbSearchResult$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbSearchResult; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbSearchResult;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbSearchResult$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbSeasonDetail { + public static final field Companion Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbSeasonDetail$Companion; + public fun ()V + public fun (Ljava/lang/Integer;Ljava/util/List;)V + public synthetic fun (Ljava/lang/Integer;Ljava/util/List;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Ljava/lang/Integer; + public final fun component2 ()Ljava/util/List; + public final fun copy (Ljava/lang/Integer;Ljava/util/List;)Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbSeasonDetail; + public static synthetic fun copy$default (Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbSeasonDetail;Ljava/lang/Integer;Ljava/util/List;ILjava/lang/Object;)Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbSeasonDetail; + public fun equals (Ljava/lang/Object;)Z + public final fun getEpisodes ()Ljava/util/List; + public final fun getSeasonNumber ()Ljava/lang/Integer; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final synthetic class com/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbSeasonDetail$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbSeasonDetail$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbSeasonDetail; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbSeasonDetail;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbSeasonDetail$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbSeasonSummary { + public static final field Companion Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbSeasonSummary$Companion; + public fun ()V + public fun (Ljava/lang/Integer;Ljava/lang/Integer;)V + public synthetic fun (Ljava/lang/Integer;Ljava/lang/Integer;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Ljava/lang/Integer; + public final fun component2 ()Ljava/lang/Integer; + public final fun copy (Ljava/lang/Integer;Ljava/lang/Integer;)Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbSeasonSummary; + public static synthetic fun copy$default (Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbSeasonSummary;Ljava/lang/Integer;Ljava/lang/Integer;ILjava/lang/Object;)Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbSeasonSummary; + public fun equals (Ljava/lang/Object;)Z + public final fun getEpisodeCount ()Ljava/lang/Integer; + public final fun getSeasonNumber ()Ljava/lang/Integer; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final synthetic class com/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbSeasonSummary$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbSeasonSummary$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbSeasonSummary; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbSeasonSummary;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbSeasonSummary$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbTvDetail { + public static final field Companion Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbTvDetail$Companion; + public fun ()V + public fun (Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Double;Ljava/util/List;Ljava/util/List;Ljava/util/List;Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbIds;Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbVideos;Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbCredits;Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbPageResult;Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbPageResult;Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbContentRatings;)V + public synthetic fun (Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Double;Ljava/util/List;Ljava/util/List;Ljava/util/List;Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbIds;Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbVideos;Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbCredits;Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbPageResult;Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbPageResult;Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbContentRatings;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Ljava/lang/Integer; + public final fun component10 ()Ljava/util/List; + public final fun component11 ()Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbIds; + public final fun component12 ()Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbVideos; + public final fun component13 ()Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbCredits; + public final fun component14 ()Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbPageResult; + public final fun component15 ()Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbPageResult; + public final fun component16 ()Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbContentRatings; + public final fun component2 ()Ljava/lang/String; + public final fun component3 ()Ljava/lang/String; + public final fun component4 ()Ljava/lang/String; + public final fun component5 ()Ljava/lang/String; + public final fun component6 ()Ljava/lang/String; + public final fun component7 ()Ljava/lang/Double; + public final fun component8 ()Ljava/util/List; + public final fun component9 ()Ljava/util/List; + public final fun copy (Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Double;Ljava/util/List;Ljava/util/List;Ljava/util/List;Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbIds;Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbVideos;Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbCredits;Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbPageResult;Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbPageResult;Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbContentRatings;)Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbTvDetail; + public static synthetic fun copy$default (Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbTvDetail;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Double;Ljava/util/List;Ljava/util/List;Ljava/util/List;Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbIds;Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbVideos;Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbCredits;Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbPageResult;Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbPageResult;Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbContentRatings;ILjava/lang/Object;)Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbTvDetail; + public fun equals (Ljava/lang/Object;)Z + public final fun getContentRatings ()Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbContentRatings; + public final fun getCredits ()Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbCredits; + public final fun getDisplayTitle ()Ljava/lang/String; + public final fun getEpisodeRunTime ()Ljava/util/List; + public final fun getExternalIds ()Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbIds; + public final fun getFirstAirDate ()Ljava/lang/String; + public final fun getGenres ()Ljava/util/List; + public final fun getId ()Ljava/lang/Integer; + public final fun getName ()Ljava/lang/String; + public final fun getOriginalName ()Ljava/lang/String; + public final fun getOverview ()Ljava/lang/String; + public final fun getPosterPath ()Ljava/lang/String; + public final fun getRecommendations ()Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbPageResult; + public final fun getSeasons ()Ljava/util/List; + public final fun getSimilar ()Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbPageResult; + public final fun getVideos ()Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbVideos; + public final fun getVoteAverage ()Ljava/lang/Double; + public final fun getYear ()Ljava/lang/Integer; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final synthetic class com/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbTvDetail$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbTvDetail$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbTvDetail; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbTvDetail;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbTvDetail$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbVideo { + public static final field Companion Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbVideo$Companion; + public fun ()V + public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V + public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Ljava/lang/String; + public final fun component2 ()Ljava/lang/String; + public final fun component3 ()Ljava/lang/String; + public final fun copy (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbVideo; + public static synthetic fun copy$default (Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbVideo;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbVideo; + public fun equals (Ljava/lang/Object;)Z + public final fun getKey ()Ljava/lang/String; + public final fun getSite ()Ljava/lang/String; + public final fun getType ()Ljava/lang/String; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final synthetic class com/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbVideo$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbVideo$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbVideo; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbVideo;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbVideo$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbVideos { + public static final field Companion Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbVideos$Companion; + public fun ()V + public fun (Ljava/util/List;)V + public synthetic fun (Ljava/util/List;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Ljava/util/List; + public final fun copy (Ljava/util/List;)Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbVideos; + public static synthetic fun copy$default (Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbVideos;Ljava/util/List;ILjava/lang/Object;)Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbVideos; + public fun equals (Ljava/lang/Object;)Z + public final fun getResults ()Ljava/util/List; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final synthetic class com/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbVideos$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbVideos$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbVideos; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbVideos;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class com/lagradost/cloudstream3/metaproviders/TmdbProvider$TmdbVideos$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public class com/lagradost/cloudstream3/metaproviders/TraktProvider : com/lagradost/cloudstream3/MainAPI { public fun ()V public fun getHasMainPage ()Z diff --git a/library/build.gradle.kts b/library/build.gradle.kts index 18579eb0c..92969baa6 100644 --- a/library/build.gradle.kts +++ b/library/build.gradle.kts @@ -64,7 +64,6 @@ kotlin { implementation(libs.ktor.http) implementation(libs.nicehttp) // HTTP Library implementation(libs.rhino) // Run JavaScript - implementation(libs.tmdb.java) // TMDB API v3 Wrapper Made with RetroFit implementation(libs.bundles.cryptography) // Cryptography } diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/metaproviders/TmdbProvider.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/metaproviders/TmdbProvider.kt index 89f935da3..88dda631a 100644 --- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/metaproviders/TmdbProvider.kt +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/metaproviders/TmdbProvider.kt @@ -1,7 +1,9 @@ package com.lagradost.cloudstream3.metaproviders +import com.fasterxml.jackson.annotation.JsonIgnore import com.fasterxml.jackson.annotation.JsonProperty import com.lagradost.cloudstream3.Actor +import com.lagradost.cloudstream3.ActorData import com.lagradost.cloudstream3.Episode import com.lagradost.cloudstream3.ErrorLoadingException import com.lagradost.cloudstream3.HomePageList @@ -20,6 +22,8 @@ import com.lagradost.cloudstream3.SearchResponseList import com.lagradost.cloudstream3.TvSeriesLoadResponse import com.lagradost.cloudstream3.TvSeriesSearchResponse import com.lagradost.cloudstream3.TvType +import com.lagradost.cloudstream3.addDate +import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.newEpisode import com.lagradost.cloudstream3.newHomePageResponse import com.lagradost.cloudstream3.newMovieLoadResponse @@ -28,35 +32,22 @@ import com.lagradost.cloudstream3.newTvSeriesLoadResponse import com.lagradost.cloudstream3.newTvSeriesSearchResponse import com.lagradost.cloudstream3.runAllAsync import com.lagradost.cloudstream3.toNewSearchResponseList +import com.lagradost.cloudstream3.utils.AppUtils.parseJson import com.lagradost.cloudstream3.utils.AppUtils.toJson -import com.uwetrottmann.tmdb2.Tmdb -import com.uwetrottmann.tmdb2.entities.AppendToResponse -import com.uwetrottmann.tmdb2.entities.BaseMovie -import com.uwetrottmann.tmdb2.entities.BaseTvShow -import com.uwetrottmann.tmdb2.entities.CastMember -import com.uwetrottmann.tmdb2.entities.ContentRating -import com.uwetrottmann.tmdb2.entities.Movie -import com.uwetrottmann.tmdb2.entities.ReleaseDate -import com.uwetrottmann.tmdb2.entities.ReleaseDatesResult -import com.uwetrottmann.tmdb2.entities.TvSeason -import com.uwetrottmann.tmdb2.entities.TvShow -import com.uwetrottmann.tmdb2.entities.Videos -import com.uwetrottmann.tmdb2.enumerations.AppendToResponseItem -import com.uwetrottmann.tmdb2.enumerations.VideoType -import retrofit2.awaitResponse -import retrofit2.Response -import java.util.Calendar +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable /** * episode and season starting from 1 * they are null if movie - * */ + */ +@Serializable data class TmdbLink( - @JsonProperty("imdbID") val imdbID: String?, - @JsonProperty("tmdbID") val tmdbID: Int?, - @JsonProperty("episode") val episode: Int?, - @JsonProperty("season") val season: Int?, - @JsonProperty("movieName") val movieName: String? = null, + @JsonProperty("imdbID") @SerialName("imdbID") val imdbID: String?, + @JsonProperty("tmdbID") @SerialName("tmdbID") val tmdbID: Int?, + @JsonProperty("episode") @SerialName("episode") val episode: Int?, + @JsonProperty("season") @SerialName("season") val season: Int?, + @JsonProperty("movieName") @SerialName("movieName") val movieName: String? = null, ) open class TmdbProvider : MainAPI() { @@ -67,19 +58,184 @@ open class TmdbProvider : MainAPI() { open val useMetaLoadResponse = false open val apiName = "TMDB" - // As some sites doesn't support s0 + // As some sites don't support s0 open val disableSeasonZero = true override val hasMainPage = true override val providerType = ProviderType.MetaProvider - // Fuck it, public private api key because github actions won't co-operate. - // Please no stealy. - private val tmdb = Tmdb("e6333b32409e02a4a6eba6fb7ff866bb") + private val tmdbApiKey = "e6333b32409e02a4a6eba6fb7ff866bb" + private val tmdbApiUrl = "https://api.themoviedb.org/3" + + @Serializable + data class TmdbIds( + @JsonProperty("imdb_id") @SerialName("imdb_id") val imdbId: String? = null, + @JsonProperty("tvdb_id") @SerialName("tvdb_id") val tvdbId: Int? = null, + ) + + @Serializable + data class TmdbGenre( + @JsonProperty("id") @SerialName("id") val id: Int? = null, + @JsonProperty("name") @SerialName("name") val name: String? = null, + ) + + @Serializable + data class TmdbCastMember( + @JsonProperty("name") @SerialName("name") val name: String? = null, + @JsonProperty("character") @SerialName("character") val character: String? = null, + @JsonProperty("profile_path") @SerialName("profile_path") val profilePath: String? = null, + ) + + @Serializable + data class TmdbCredits( + @JsonProperty("cast") @SerialName("cast") val cast: List? = null, + ) + + @Serializable + data class TmdbVideo( + @JsonProperty("key") @SerialName("key") val key: String? = null, + @JsonProperty("site") @SerialName("site") val site: String? = null, + @JsonProperty("type") @SerialName("type") val type: String? = null, + ) + + @Serializable + data class TmdbVideos( + @JsonProperty("results") @SerialName("results") val results: List? = null, + ) + + // Shared between movie and tv search results + @Serializable + data class TmdbSearchResult( + @JsonProperty("id") @SerialName("id") val id: Int? = null, + @JsonProperty("title") @SerialName("title") val title: String? = null, // movies + @JsonProperty("original_title") @SerialName("original_title") val originalTitle: String? = null, + @JsonProperty("name") @SerialName("name") val name: String? = null, // tv + @JsonProperty("original_name") @SerialName("original_name") val originalName: String? = null, + @JsonProperty("poster_path") @SerialName("poster_path") val posterPath: String? = null, + @JsonProperty("vote_average") @SerialName("vote_average") val voteAverage: Double? = null, + @JsonProperty("release_date") @SerialName("release_date") val releaseDate: String? = null, + @JsonProperty("first_air_date") @SerialName("first_air_date") val firstAirDate: String? = null, + @JsonProperty("media_type") @SerialName("media_type") val mediaType: String? = null, // for multi-search + ) { + @get:JsonIgnore val isTv get() = name != null || mediaType == "tv" + @get:JsonIgnore val displayTitle get() = title ?: originalTitle ?: name ?: originalName ?: "" + @get:JsonIgnore val year get() = (releaseDate ?: firstAirDate)?.take(4)?.toIntOrNull() + } + + @Serializable + data class TmdbPageResult( + @JsonProperty("results") @SerialName("results") val results: List? = null, + @JsonProperty("total_pages") @SerialName("total_pages") val totalPages: Int? = null, + @JsonProperty("total_results") @SerialName("total_results") val totalResults: Int? = null, + ) + + @Serializable + data class TmdbMultiResult( + @JsonProperty("results") @SerialName("results") val results: List? = null, + ) + + @Serializable + data class TmdbEpisode( + @JsonProperty("id") @SerialName("id") val id: Int? = null, + @JsonProperty("name") @SerialName("name") val name: String? = null, + @JsonProperty("overview") @SerialName("overview") val overview: String? = null, + @JsonProperty("episode_number") @SerialName("episode_number") val episodeNumber: Int? = null, + @JsonProperty("season_number") @SerialName("season_number") val seasonNumber: Int? = null, + @JsonProperty("still_path") @SerialName("still_path") val stillPath: String? = null, + @JsonProperty("air_date") @SerialName("air_date") val airDate: String? = null, + @JsonProperty("vote_average") @SerialName("vote_average") val voteAverage: Double? = null, + @JsonProperty("external_ids") @SerialName("external_ids") val externalIds: TmdbIds? = null, + ) + + @Serializable + data class TmdbSeasonDetail( + @JsonProperty("season_number") @SerialName("season_number") val seasonNumber: Int? = null, + @JsonProperty("episodes") @SerialName("episodes") val episodes: List? = null, + ) + + @Serializable + data class TmdbSeasonSummary( + @JsonProperty("season_number") @SerialName("season_number") val seasonNumber: Int? = null, + @JsonProperty("episode_count") @SerialName("episode_count") val episodeCount: Int? = null, + ) + + @Serializable + data class TmdbContentRating( + @JsonProperty("iso_3166_1") @SerialName("iso_3166_1") val country: String? = null, + @JsonProperty("rating") @SerialName("rating") val rating: String? = null, + ) + + @Serializable + data class TmdbContentRatings( + @JsonProperty("results") @SerialName("results") val results: List? = null, + ) + + @Serializable + data class TmdbReleaseDateEntry( + @JsonProperty("certification") @SerialName("certification") val certification: String? = null, + @JsonProperty("type") @SerialName("type") val type: Int? = null, + ) + + @Serializable + data class TmdbReleaseDateResult( + @JsonProperty("iso_3166_1") @SerialName("iso_3166_1") val country: String? = null, + @JsonProperty("release_dates") @SerialName("release_dates") val releaseDates: List? = null, + ) + + @Serializable + data class TmdbReleaseDates( + @JsonProperty("results") @SerialName("results") val results: List? = null, + ) + + @Serializable + data class TmdbTvDetail( + @JsonProperty("id") @SerialName("id") val id: Int? = null, + @JsonProperty("name") @SerialName("name") val name: String? = null, + @JsonProperty("original_name") @SerialName("original_name") val originalName: String? = null, + @JsonProperty("overview") @SerialName("overview") val overview: String? = null, + @JsonProperty("poster_path") @SerialName("poster_path") val posterPath: String? = null, + @JsonProperty("first_air_date") @SerialName("first_air_date") val firstAirDate: String? = null, + @JsonProperty("vote_average") @SerialName("vote_average") val voteAverage: Double? = null, + @JsonProperty("genres") @SerialName("genres") val genres: List? = null, + @JsonProperty("episode_run_time") @SerialName("episode_run_time") val episodeRunTime: List? = null, + @JsonProperty("seasons") @SerialName("seasons") val seasons: List? = null, + @JsonProperty("external_ids") @SerialName("external_ids") val externalIds: TmdbIds? = null, + @JsonProperty("videos") @SerialName("videos") val videos: TmdbVideos? = null, + @JsonProperty("credits") @SerialName("credits") val credits: TmdbCredits? = null, + @JsonProperty("recommendations") @SerialName("recommendations") val recommendations: TmdbPageResult? = null, + @JsonProperty("similar") @SerialName("similar") val similar: TmdbPageResult? = null, + @JsonProperty("content_ratings") @SerialName("content_ratings") val contentRatings: TmdbContentRatings? = null, + ) { + @get:JsonIgnore val displayTitle get() = name ?: originalName ?: "" + @get:JsonIgnore val year get() = firstAirDate?.take(4)?.toIntOrNull() + } + + @Serializable + data class TmdbMovieDetail( + @JsonProperty("id") @SerialName("id") val id: Int? = null, + @JsonProperty("title") @SerialName("title") val title: String? = null, + @JsonProperty("original_title") @SerialName("original_title") val originalTitle: String? = null, + @JsonProperty("overview") @SerialName("overview") val overview: String? = null, + @JsonProperty("poster_path") @SerialName("poster_path") val posterPath: String? = null, + @JsonProperty("release_date") @SerialName("release_date") val releaseDate: String? = null, + @JsonProperty("vote_average") @SerialName("vote_average") val voteAverage: Double? = null, + @JsonProperty("genres") @SerialName("genres") val genres: List? = null, + @JsonProperty("runtime") @SerialName("runtime") val runtime: Int? = null, + @JsonProperty("imdb_id") @SerialName("imdb_id") val imdbId: String? = null, + @JsonProperty("external_ids") @SerialName("external_ids") val externalIds: TmdbIds? = null, + @JsonProperty("videos") @SerialName("videos") val videos: TmdbVideos? = null, + @JsonProperty("credits") @SerialName("credits") val credits: TmdbCredits? = null, + @JsonProperty("recommendations") @SerialName("recommendations") val recommendations: TmdbPageResult? = null, + @JsonProperty("similar") @SerialName("similar") val similar: TmdbPageResult? = null, + @JsonProperty("release_dates") @SerialName("release_dates") val releaseDates: TmdbReleaseDates? = null, + ) { + @get:JsonIgnore val displayTitle get() = title ?: originalTitle ?: "" + @get:JsonIgnore val year get() = releaseDate?.take(4)?.toIntOrNull() + } private fun getImageUrl(link: String?): String? { - if (link == null) return null - return if (link.startsWith("/")) "https://image.tmdb.org/t/p/w500/$link" else link + link ?: return null + return if (link.startsWith("/")) "https://image.tmdb.org/t/p/w500$link" else link } private fun getUrl(id: Int?, tvShow: Boolean): String { @@ -87,199 +243,197 @@ open class TmdbProvider : MainAPI() { else "https://www.themoviedb.org/movie/${id ?: -1}" } - private fun BaseTvShow.toSearchResponse(): TvSeriesSearchResponse { - return newTvSeriesSearchResponse( - name = this.name ?: this.original_name, + private suspend fun getApi(path: String, extraParams: Map = emptyMap()): String { + val params = buildMap { + put("api_key", tmdbApiKey) + putAll(extraParams) + } + return app.get( + url = "$tmdbApiUrl$path", + params = params, + ).text + } + + private fun TmdbSearchResult.toSearchResponse() = if (isTv) { + newTvSeriesSearchResponse( + name = displayTitle, url = getUrl(id, true), type = TvType.TvSeries, - fix = false + fix = false, ) { this.id = this@toSearchResponse.id - this.posterUrl = getImageUrl(poster_path) - this.score = Score.from10(vote_average) - this.year = first_air_date?.let { - Calendar.getInstance().apply { - time = it - }.get(Calendar.YEAR) - } + this.posterUrl = getImageUrl(posterPath) + this.score = Score.from10(voteAverage) + this.year = this@toSearchResponse.year } - } - - private fun BaseMovie.toSearchResponse(): MovieSearchResponse { - return newMovieSearchResponse( - name = this.title ?: this.original_title, + } else { + newMovieSearchResponse( + name = displayTitle, url = getUrl(id, false), type = TvType.Movie, - fix = false + fix = false, ) { this.id = this@toSearchResponse.id - this.posterUrl = getImageUrl(poster_path) - this.score = Score.from10(vote_average) - this.year = release_date?.let { - Calendar.getInstance().apply { - time = it - }.get(Calendar.YEAR) - } + this.posterUrl = getImageUrl(posterPath) + this.score = Score.from10(voteAverage) + this.year = this@toSearchResponse.year } } - private fun List?.toActors(): List>? { + private fun List?.toActors(): List>? { return this?.mapNotNull { + it ?: return@mapNotNull null Pair( - Actor(it?.name ?: return@mapNotNull null, getImageUrl(it.profile_path)), - it.character + Actor(it.name ?: return@mapNotNull null, getImageUrl(it.profilePath)), + it.character, ) } } - private suspend fun TvShow.toLoadResponse(): TvSeriesLoadResponse { - val tvSeasonsService = tmdb.tvSeasonsService() - val episodes = mutableListOf() - - val validSeasons = this.seasons?.filter { !disableSeasonZero || (it.season_number ?: 0) != 0 } ?: emptyList() - for (season in validSeasons) { - val seasonNumber = season.season_number ?: continue - - val response: Response = tmdb.tvSeasonsService() - .season(this.id, seasonNumber, "external_ids,images,episodes") - .awaitResponse() - - val fullSeason = response.body() ?: continue - - fullSeason.episodes?.forEach { episode -> - episodes += newEpisode( - TmdbLink( - episode.external_ids?.imdb_id ?: this.external_ids?.imdb_id, - this.id, - episode.episode_number, - episode.season_number, - this.name ?: this.original_name - ).toJson() - ) { - this.name = episode.name - this.season = episode.season_number - this.episode = episode.episode_number - this.score = Score.from10(episode.vote_average) - this.description = episode.overview - this.date = episode.air_date?.time - this.posterUrl = getImageUrl(episode.still_path) - } - } - } - - return newTvSeriesLoadResponse( - this.name ?: this.original_name, - getUrl(id, true), - TvType.TvSeries, - episodes - ) { - posterUrl = getImageUrl(poster_path) - year = first_air_date?.let { - Calendar.getInstance().apply { - time = it - }.get(Calendar.YEAR) - } - plot = overview - addImdbId(external_ids?.imdb_id) - tags = genres?.mapNotNull { it.name } - duration = episode_run_time?.average()?.toInt() - score = Score.from10(vote_average) - addTrailer(videos.toTrailers()) - recommendations = (this@toLoadResponse.recommendations - ?: this@toLoadResponse.similar)?.results?.map { it.toSearchResponse() } - addActors(credits?.cast?.toList().toActors()) - contentRating = fetchContentRating(id, "US") - } - } - - private fun Videos?.toTrailers(): List? { - return this?.results?.filter { it.type != VideoType.OPENING_CREDITS && it.type != VideoType.FEATURETTE } - ?.sortedBy { it.type?.ordinal ?: 10000 } + private fun TmdbVideos?.toTrailers(): List? { + val skipTypes = setOf("Opening Credits", "Featurette") + return this?.results + ?.filter { it.type !in skipTypes } + ?.sortedBy { it.type } ?.mapNotNull { when (it.site?.trim()?.lowercase()) { - "youtube" -> { // TODO FILL SITES - "https://www.youtube.com/watch?v=${it.key}" - } + "youtube" -> "https://www.youtube.com/watch?v=${it.key}" else -> null } } } - private suspend fun Movie.toLoadResponse(): MovieLoadResponse { - return newMovieLoadResponse( - this.title ?: this.original_title, getUrl(id, false), TvType.Movie, TmdbLink( - this.imdb_id, - this.id, - null, - null, - this.title ?: this.original_title, - ).toJson() - ) { - posterUrl = getImageUrl(poster_path) - year = release_date?.let { - Calendar.getInstance().apply { - time = it - }.get(Calendar.YEAR) - } - plot = overview - addImdbId(external_ids?.imdb_id) - tags = genres?.mapNotNull { it.name } - duration = runtime - score = Score.from10(vote_average) - addTrailer(videos.toTrailers()) + open suspend fun fetchContentRating(id: Int?, country: String): String? { + id ?: return null + // Try TV content ratings first + val tvRating = parseJson( + getApi("/tv/$id/content_ratings") + ).results?.firstOrNull { it.country == country }?.rating + if (tvRating != null) return tvRating + // Fall back to movie release dates + return parseJson( + getApi("/movie/$id/release_dates") + ).results?.firstOrNull { it.country == country } + ?.releaseDates?.firstOrNull { !it.certification.isNullOrBlank() } + ?.certification + } + + private suspend fun TmdbTvDetail.toLoadResponse(): TvSeriesLoadResponse { + val episodes = mutableListOf() + val validSeasons = seasons?.filter { !disableSeasonZero || (it.seasonNumber ?: 0) != 0 } + ?: emptyList() + + for (season in validSeasons) { + val seasonNum = season.seasonNumber ?: continue + val fullSeason = parseJson( + getApi("/tv/$id/season/$seasonNum", mapOf("append_to_response" to "external_ids")) + ) + + fullSeason.episodes?.forEach { episode -> + episodes += newEpisode( + TmdbLink( + episode.externalIds?.imdbId ?: externalIds?.imdbId, + id, + episode.episodeNumber, + episode.seasonNumber, + displayTitle, + ).toJson() + ) { + this.name = episode.name + this.season = episode.seasonNumber + this.episode = episode.episodeNumber + this.score = Score.from10(episode.voteAverage) + this.description = episode.overview + this.posterUrl = getImageUrl(episode.stillPath) + this.addDate(episode.airDate) + } + } + } + + return newTvSeriesLoadResponse( + displayTitle, + getUrl(id, true), + TvType.TvSeries, + episodes, + ) { + posterUrl = getImageUrl(posterPath) + this.year = this@toLoadResponse.year + plot = overview + addImdbId(externalIds?.imdbId) + tags = genres?.mapNotNull { it.name } + duration = episodeRunTime?.average()?.toInt() + score = Score.from10(voteAverage) + addTrailer(videos.toTrailers()) recommendations = (this@toLoadResponse.recommendations ?: this@toLoadResponse.similar)?.results?.map { it.toSearchResponse() } addActors(credits?.cast?.toList().toActors()) + contentRating = contentRatings?.results?.firstOrNull { it.country == "US" }?.rating + ?: fetchContentRating(id, "US") + } + } - contentRating = fetchContentRating(id, "US") + private suspend fun TmdbMovieDetail.toLoadResponse(): MovieLoadResponse { + return newMovieLoadResponse( + displayTitle, + getUrl(id, false), + TvType.Movie, + TmdbLink( + imdbId ?: externalIds?.imdbId, + id, + null, + null, + displayTitle, + ).toJson() + ) { + posterUrl = getImageUrl(posterPath) + this.year = this@toLoadResponse.year + plot = overview + addImdbId(imdbId ?: externalIds?.imdbId) + tags = genres?.mapNotNull { it.name } + duration = runtime + score = Score.from10(voteAverage) + addTrailer(videos.toTrailers()) + recommendations = (this@toLoadResponse.recommendations + ?: this@toLoadResponse.similar)?.results?.map { it.toSearchResponse() } + addActors(credits?.cast?.toList().toActors()) + contentRating = releaseDates?.results + ?.firstOrNull { it.country == "US" } + ?.releaseDates?.firstOrNull { !it.certification.isNullOrBlank() } + ?.certification } } override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse { - - // SAME AS DISCOVER IT SEEMS -// val popularSeries = tmdb.tvService().popular(page, "en-US").execute().body()?.results?.map { -// it.toSearchResponse() -// } ?: listOf() -// -// val popularMovies = -// tmdb.moviesService().popular(page, "en-US", "840").execute().body()?.results?.map { -// it.toSearchResponse() -// } ?: listOf() - var discoverMovies: List = listOf() var discoverSeries: List = listOf() var topMovies: List = listOf() var topSeries: List = listOf() runAllAsync( { - discoverMovies = tmdb.discoverMovie().page(page).build().awaitResponse().body()?.results?.map { - it.toSearchResponse() - } ?: listOf() - }, { - discoverSeries = tmdb.discoverTv().page(page).build().awaitResponse().body()?.results?.map { - it.toSearchResponse() - } ?: listOf() - }, { - // https://en.wikipedia.org/wiki/ISO_3166-1 - topMovies = - tmdb.moviesService().topRated(page, "en-US", "US").awaitResponse() - .body()?.results?.map { - it.toSearchResponse() - } ?: listOf() - }, { - topSeries = - tmdb.tvService().topRated(page, "en-US").awaitResponse().body()?.results?.map { - it.toSearchResponse() - } ?: listOf() - } + discoverMovies = parseJson( + getApi("/discover/movie", mapOf("page" to "$page")) + ).results?.map { it.toSearchResponse() as MovieSearchResponse } ?: listOf() + }, + { + discoverSeries = parseJson( + getApi("/discover/tv", mapOf("page" to "$page")) + ).results?.map { it.toSearchResponse() as TvSeriesSearchResponse } ?: listOf() + }, + { + topMovies = parseJson( + getApi("/movie/top_rated", mapOf("page" to "$page", "language" to "en-US", "region" to "US")) + ).results?.map { it.toSearchResponse() as MovieSearchResponse } ?: listOf() + }, + { + topSeries = parseJson( + getApi("/tv/top_rated", mapOf("page" to "$page", "language" to "en-US")) + ).results?.map { it.toSearchResponse() as TvSeriesSearchResponse } ?: listOf() + }, ) return newHomePageResponse( listOf( -// HomePageList("Popular Series", popularSeries), -// HomePageList("Popular Movies", popularMovies), HomePageList("Popular Movies", discoverMovies), HomePageList("Popular Series", discoverSeries), HomePageList("Top Movies", topMovies), @@ -288,47 +442,14 @@ open class TmdbProvider : MainAPI() { ) } - open fun loadFromImdb(imdb: String, seasons: List): LoadResponse? { - return null - } + open fun loadFromImdb(imdb: String, seasons: List): LoadResponse? = null + open fun loadFromTmdb(tmdbId: Int, seasons: List): LoadResponse? = null + open fun loadFromImdb(imdb: String): LoadResponse? = null + open fun loadFromTmdb(tmdbId: Int): LoadResponse? = null - open fun loadFromTmdb(tmdb: Int, seasons: List): LoadResponse? { - return null - } - - open fun loadFromImdb(imdb: String): LoadResponse? { - return null - } - - open fun loadFromTmdb(tmdb: Int): LoadResponse? { - return null - } - - open suspend fun fetchContentRating(id: Int?, country: String): String? { - id ?: return null - - val contentRatings = tmdb.tvService().content_ratings(id).awaitResponse().body()?.results - return if (!contentRatings.isNullOrEmpty()) { - contentRatings.firstOrNull { it: ContentRating -> - it.iso_3166_1 == country - }?.rating - } else { - val releaseDates = tmdb.moviesService().releaseDates(id).awaitResponse().body()?.results - val certification = releaseDates?.firstOrNull { it: ReleaseDatesResult -> - it.iso_3166_1 == country - }?.release_dates?.firstOrNull { it: ReleaseDate -> - !it.certification.isNullOrBlank() - }?.certification - - certification - } - } - - // Possible to add recommendations and such here. override suspend fun load(url: String): LoadResponse? { // https://www.themoviedb.org/movie/7445-brothers // https://www.themoviedb.org/tv/71914-the-wheel-of-time - val idRegex = Regex("""themoviedb\.org/(.*)/(\d+)""") val found = idRegex.find(url) @@ -337,86 +458,64 @@ open class TmdbProvider : MainAPI() { ?: throw ErrorLoadingException("No id found") return if (useMetaLoadResponse) { - return if (isTvSeries) { - val body = tmdb.tvService() - .tv( - id, - "en-US", - AppendToResponse( - AppendToResponseItem.EXTERNAL_IDS, - AppendToResponseItem.VIDEOS + if (isTvSeries) { + val detail = parseJson( + getApi( + "/tv/$id", + mapOf( + "language" to "en-US", + "append_to_response" to "external_ids,videos,credits,recommendations,similar,content_ratings", ) ) - .awaitResponse().body() - val response = body?.toLoadResponse() - if (response != null) { - if (response.recommendations.isNullOrEmpty()) - tmdb.tvService().recommendations(id, 1, "en-US").awaitResponse().body() - ?.let { - it.results?.map { res -> res.toSearchResponse() } - }?.let { list -> - response.recommendations = list - } - - if (response.actors.isNullOrEmpty()) - tmdb.tvService().credits(id, "en-US").awaitResponse().body()?.let { - response.addActors(it.cast?.toActors()) - } - } - - response + ) + detail.toLoadResponse() } else { - val body = tmdb.moviesService() - .summary( - id, - "en-US", - AppendToResponse( - AppendToResponseItem.EXTERNAL_IDS, - AppendToResponseItem.VIDEOS + val detail = parseJson( + getApi( + "/movie/$id", + mapOf( + "language" to "en-US", + "append_to_response" to "external_ids,videos,credits,recommendations,similar,release_dates", ) ) - .awaitResponse().body() - val response = body?.toLoadResponse() - if (response != null) { - if (response.recommendations.isNullOrEmpty()) - tmdb.moviesService().recommendations(id, 1, "en-US").awaitResponse().body() - ?.let { - it.results?.map { res -> res.toSearchResponse() } - }?.let { list -> - response.recommendations = list - } - - if (response.actors.isNullOrEmpty()) - tmdb.moviesService().credits(id).awaitResponse().body()?.let { - response.addActors(it.cast?.toActors()) - } - } - response + ) + detail.toLoadResponse() } } else { loadFromTmdb(id)?.let { return it } if (isTvSeries) { - tmdb.tvService().externalIds(id).awaitResponse().body()?.imdb_id?.let { - val fromImdb = loadFromImdb(it) - val result = if (fromImdb == null) { - val details = tmdb.tvService().tv(id, "en-US").awaitResponse().body() - loadFromImdb(it, details?.seasons ?: listOf()) - ?: loadFromTmdb(id, details?.seasons ?: listOf()) - } else fromImdb - - result + val externalIds = parseJson(getApi("/tv/$id/external_ids")) + val imdbId = externalIds.imdbId + if (imdbId != null) { + val fromImdb = loadFromImdb(imdbId) + if (fromImdb != null) return fromImdb + } + val seasons = parseJson(getApi("/tv/$id")).seasons ?: listOf() + if (imdbId != null) { + loadFromImdb(imdbId, seasons) ?: loadFromTmdb(id, seasons) + } else { + loadFromTmdb(id, seasons) } } else { - tmdb.moviesService().externalIds(id).awaitResponse() - .body()?.imdb_id?.let { loadFromImdb(it) } + val imdbId = parseJson(getApi("/movie/$id")).imdbId + if (imdbId != null) loadFromImdb(imdbId) else null } } } override suspend fun search(query: String, page: Int): SearchResponseList? { - return tmdb.searchService().multi(query, page, "en-US", "US", includeAdult).awaitResponse() - .body()?.results?.mapNotNull { - it.movie?.toSearchResponse() ?: it.tvShow?.toSearchResponse() - }?.toNewSearchResponseList() + return parseJson( + getApi( + "/search/multi", + mapOf( + "query" to query, + "page" to "$page", + "language" to "en-US", + "include_adult" to "$includeAdult", + ) + ) + ).results?.mapNotNull { + if (it.mediaType == "person") null else it.toSearchResponse() + }?.toNewSearchResponseList() } -} \ No newline at end of file +} From 6ca7507146748bb378a9155cbc5a7e70f3919650 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Wed, 15 Jul 2026 06:34:43 -0700 Subject: [PATCH 26/38] HlsPlaylistParser: replace usage of Java APIs with kotlin equivalents (#2828) This does add kotlinx-io as a dependency, it is more light weight then okio, and when we migrate to ktor for example it will become an implicit dependency anyway so it should be fine to just add. Allows for more reliable and readable usage then doing some of this manually. --- gradle/libs.versions.toml | 2 + library/api/jvm/library.api | 52 +++------------ library/build.gradle.kts | 1 + .../cloudstream3/utils/HlsPlaylistParser.kt | 63 ++++++++++--------- 4 files changed, 44 insertions(+), 74 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 898529d9a..e38895451 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -31,6 +31,7 @@ kotlinxAtomicfu = "0.33.0" kotlinxCollectionsImmutable = "0.4.0" kotlinxCoroutines = "1.11.0" kotlinxDatetime = "0.8.0" +kotlinxIOCore = "0.9.1" kotlinxSerializationJson = "1.11.0" ksoup = "0.2.6" ktor = "3.5.0" @@ -98,6 +99,7 @@ kotlinx-collections-immutable = { module = "org.jetbrains.kotlinx:kotlinx-collec kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinxCoroutines" } kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "kotlinxCoroutines" } kotlinx-datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.ref = "kotlinxDatetime" } +kotlinx-io-core = { module = "org.jetbrains.kotlinx:kotlinx-io-core", version.ref = "kotlinxIOCore" } kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" } ksoup = { module = "com.fleeksoft.ksoup:ksoup", version.ref = "ksoup" } ktor-http = { module = "io.ktor:ktor-http", version.ref = "ktor" } diff --git a/library/api/jvm/library.api b/library/api/jvm/library.api index 0d9b8f76e..195cd49fe 100644 --- a/library/api/jvm/library.api +++ b/library/api/jvm/library.api @@ -8327,44 +8327,6 @@ public final class com/lagradost/cloudstream3/utils/HlsPlaylistParser { public final fun parse (Ljava/lang/String;Ljava/lang/String;)Lcom/lagradost/cloudstream3/utils/HlsPlaylistParser$HlsMultivariantPlaylist; } -public final class com/lagradost/cloudstream3/utils/HlsPlaylistParser$C { - public static final field CENC_TYPE_cbc1 Ljava/lang/String; - public static final field CENC_TYPE_cbcs Ljava/lang/String; - public static final field CENC_TYPE_cenc Ljava/lang/String; - public static final field CENC_TYPE_cens Ljava/lang/String; - public static final field INSTANCE Lcom/lagradost/cloudstream3/utils/HlsPlaylistParser$C; - public static final field ROLE_FLAG_ALTERNATE I - public static final field ROLE_FLAG_CAPTION I - public static final field ROLE_FLAG_COMMENTARY I - public static final field ROLE_FLAG_DESCRIBES_MUSIC_AND_SOUND I - public static final field ROLE_FLAG_DESCRIBES_VIDEO I - public static final field ROLE_FLAG_DUB I - public static final field ROLE_FLAG_EASY_TO_READ I - public static final field ROLE_FLAG_EMERGENCY I - public static final field ROLE_FLAG_ENHANCED_DIALOG_INTELLIGIBILITY I - public static final field ROLE_FLAG_MAIN I - public static final field ROLE_FLAG_SIGN I - public static final field ROLE_FLAG_SUBTITLE I - public static final field ROLE_FLAG_SUPPLEMENTARY I - public static final field ROLE_FLAG_TRANSCRIBES_DIALOG I - public static final field ROLE_FLAG_TRICK_PLAY I - public static final field SELECTION_FLAG_AUTOSELECT I - public static final field SELECTION_FLAG_DEFAULT I - public static final field SELECTION_FLAG_FORCED I - public static final field TRACK_TYPE_AUDIO I - public static final field TRACK_TYPE_CAMERA_MOTION I - public static final field TRACK_TYPE_DEFAULT I - public static final field TRACK_TYPE_IMAGE I - public static final field TRACK_TYPE_METADATA I - public static final field TRACK_TYPE_NONE I - public static final field TRACK_TYPE_TEXT I - public static final field TRACK_TYPE_UNKNOWN I - public static final field TRACK_TYPE_VIDEO I - public final fun getCLEARKEY_UUID ()Ljava/util/UUID; - public final fun getPLAYREADY_UUID ()Ljava/util/UUID; - public final fun getWIDEVINE_UUID ()Ljava/util/UUID; -} - public final class com/lagradost/cloudstream3/utils/HlsPlaylistParser$DrmInitData { public fun (Ljava/lang/String;[Lcom/lagradost/cloudstream3/utils/HlsPlaylistParser$SchemeData;)V public final fun component1 ()Ljava/lang/String; @@ -8630,7 +8592,7 @@ public final class com/lagradost/cloudstream3/utils/HlsPlaylistParser$ParserExce public final class com/lagradost/cloudstream3/utils/HlsPlaylistParser$PsshAtomUtil { public static final field INSTANCE Lcom/lagradost/cloudstream3/utils/HlsPlaylistParser$PsshAtomUtil; - public final fun buildPsshAtom (Ljava/util/UUID;[Ljava/util/UUID;[B)[B + public final fun buildPsshAtom (Lkotlin/uuid/Uuid;[Lkotlin/uuid/Uuid;[B)[B } public final class com/lagradost/cloudstream3/utils/HlsPlaylistParser$Rendition { @@ -8651,19 +8613,19 @@ public final class com/lagradost/cloudstream3/utils/HlsPlaylistParser$Rendition } public final class com/lagradost/cloudstream3/utils/HlsPlaylistParser$SchemeData { - public fun (Ljava/util/UUID;Ljava/lang/String;Ljava/lang/String;[B)V - public synthetic fun (Ljava/util/UUID;Ljava/lang/String;Ljava/lang/String;[BILkotlin/jvm/internal/DefaultConstructorMarker;)V - public final fun component1 ()Ljava/util/UUID; + public fun (Lkotlin/uuid/Uuid;Ljava/lang/String;Ljava/lang/String;[B)V + public synthetic fun (Lkotlin/uuid/Uuid;Ljava/lang/String;Ljava/lang/String;[BILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Lkotlin/uuid/Uuid; public final fun component2 ()Ljava/lang/String; public final fun component3 ()Ljava/lang/String; public final fun component4 ()[B - public final fun copy (Ljava/util/UUID;Ljava/lang/String;Ljava/lang/String;[B)Lcom/lagradost/cloudstream3/utils/HlsPlaylistParser$SchemeData; - public static synthetic fun copy$default (Lcom/lagradost/cloudstream3/utils/HlsPlaylistParser$SchemeData;Ljava/util/UUID;Ljava/lang/String;Ljava/lang/String;[BILjava/lang/Object;)Lcom/lagradost/cloudstream3/utils/HlsPlaylistParser$SchemeData; + public final fun copy (Lkotlin/uuid/Uuid;Ljava/lang/String;Ljava/lang/String;[B)Lcom/lagradost/cloudstream3/utils/HlsPlaylistParser$SchemeData; + public static synthetic fun copy$default (Lcom/lagradost/cloudstream3/utils/HlsPlaylistParser$SchemeData;Lkotlin/uuid/Uuid;Ljava/lang/String;Ljava/lang/String;[BILjava/lang/Object;)Lcom/lagradost/cloudstream3/utils/HlsPlaylistParser$SchemeData; public fun equals (Ljava/lang/Object;)Z public final fun getData ()[B public final fun getLicenseServerUrl ()Ljava/lang/String; public final fun getMimeType ()Ljava/lang/String; - public final fun getUuid ()Ljava/util/UUID; + public final fun getUuid ()Lkotlin/uuid/Uuid; public fun hashCode ()I public fun toString ()Ljava/lang/String; } diff --git a/library/build.gradle.kts b/library/build.gradle.kts index 92969baa6..59a36ce7f 100644 --- a/library/build.gradle.kts +++ b/library/build.gradle.kts @@ -59,6 +59,7 @@ kotlin { implementation(libs.kotlinx.atomicfu) implementation(libs.kotlinx.coroutines.core) implementation(libs.kotlinx.datetime) + implementation(libs.kotlinx.io.core) implementation(libs.kotlinx.serialization.json) // JSON Parser implementation(libs.ksoup) // HTML Parser implementation(libs.ktor.http) diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/HlsPlaylistParser.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/HlsPlaylistParser.kt index cd5d752b3..3edd0f410 100644 --- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/HlsPlaylistParser.kt +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/HlsPlaylistParser.kt @@ -21,9 +21,10 @@ package com.lagradost.cloudstream3.utils import com.lagradost.cloudstream3.base64DecodeArray import io.ktor.http.Url -import java.io.IOException -import java.nio.ByteBuffer -import java.util.UUID +import kotlinx.io.Buffer +import kotlinx.io.IOException +import kotlinx.io.readByteArray +import kotlin.uuid.Uuid @Suppress("unused") object HlsPlaylistParser { @@ -186,13 +187,13 @@ object HlsPlaylistParser { data class SchemeData( /** - * The {@link UUID} of the DRM scheme, or {@link C#UUID_NIL} if the data is universal (i.e. + * The [Uuid] of the DRM scheme, or [C.UUID_NIL] if the data is universal (i.e. * applies to all schemes). */ - val uuid: UUID, + val uuid: Uuid, /** The URL of the server to which license requests should be made. May be null if unknown. */ val licenseServerUrl: String? = null, - /** The mimeType of {@link #data}. */ + /** The mimeType of [data]. */ val mimeType: String, /** The initialization data. May be null for scheme support checks only. */ val data: ByteArray @@ -454,14 +455,14 @@ object HlsPlaylistParser { // "." or "..", remove the appropriate segments of the path. if (i == segmentStart + 1 && url[segmentStart] == '.') { // Given "abc/def/./ghi", remove "./" to get "abc/def/ghi". - url.delete(segmentStart, nextSegmentStart) + url.deleteRange(segmentStart, nextSegmentStart) limit -= nextSegmentStart - segmentStart i = segmentStart } else if (i == segmentStart + 2 && url[segmentStart] == '.' && url[segmentStart + 1] == '.') { // Given "abc/def/../ghi", remove "def/../" to get "abc/ghi". val prevSegmentStart = url.lastIndexOf("/", segmentStart - 2) + 1 val removeFrom = if (prevSegmentStart > offset) prevSegmentStart else offset - url.delete(removeFrom, nextSegmentStart) + url.deleteRange(removeFrom, nextSegmentStart) limit -= nextSegmentStart - removeFrom segmentStart = prevSegmentStart i = prevSegmentStart @@ -532,31 +533,31 @@ object HlsPlaylistParser { } } - object C { + private object C { /** - * UUID for the ClearKey DRM scheme. + * [Uuid] for the ClearKey DRM scheme. * * * ClearKey is supported on Android devices running Android 5.0 (API Level 21) and up. */ - val CLEARKEY_UUID = UUID(-0x1d8e62a7567a4c37L, 0x781AB030AF78D30EL) + val CLEARKEY_UUID = Uuid.fromLongs(-0x1d8e62a7567a4c37L, 0x781AB030AF78D30EL) /** - * UUID for the Widevine DRM scheme. + * [Uuid] for the Widevine DRM scheme. * * * Widevine is supported on Android devices running Android 4.3 (API Level 18) and up. */ - val WIDEVINE_UUID = UUID(-0x121074568629b532L, -0x5c37d8232ae2de13L) + val WIDEVINE_UUID = Uuid.fromLongs(-0x121074568629b532L, -0x5c37d8232ae2de13L) /** - * UUID for the PlayReady DRM scheme. + * [Uuid] for the PlayReady DRM scheme. * * * PlayReady is supported on all AndroidTV devices. Note that most other Android devices do not * provide PlayReady support. */ - val PLAYREADY_UUID = UUID(-0x65fb0f8667bfbd7aL, -0x546d19a41f77a06bL) + val PLAYREADY_UUID = Uuid.fromLongs(-0x65fb0f8667bfbd7aL, -0x546d19a41f77a06bL) /** "cenc" scheme type name as defined in ISO/IEC 23001-7:2016. */ @@ -1066,7 +1067,7 @@ object HlsPlaylistParser { object PsshAtomUtil { fun buildPsshAtom( - systemId: UUID, keyIds: Array?, data: ByteArray? + systemId: Uuid, keyIds: Array?, data: ByteArray? ): ByteArray { val dataLength = data?.size ?: 0 var psshBoxLength: Int = @@ -1074,26 +1075,30 @@ object HlsPlaylistParser { if (keyIds != null) { psshBoxLength += 4 /* KID_count */ + (keyIds.size * 16) /* KIDs */ } - val psshBox: ByteBuffer = ByteBuffer.allocate(psshBoxLength) - psshBox.putInt(psshBoxLength) - psshBox.putInt(Mp4Box.TYPE_pssh) - psshBox.putInt(if (keyIds != null) 0x01000000 else 0 /* version=(buildV1Atom ? 1 : 0), flags=0 */) - psshBox.putLong(systemId.mostSignificantBits) - psshBox.putLong(systemId.leastSignificantBits) + val buffer = Buffer() + buffer.writeInt(psshBoxLength) + buffer.writeInt(Mp4Box.TYPE_pssh) + buffer.writeInt(if (keyIds != null) 0x01000000 else 0 /* version=(buildV1Atom ? 1 : 0), flags=0 */) + systemId.toLongs { mostSignificantBits, leastSignificantBits -> + buffer.writeLong(mostSignificantBits) + buffer.writeLong(leastSignificantBits) + } if (keyIds != null) { - psshBox.putInt(keyIds.size) + buffer.writeInt(keyIds.size) for (keyId in keyIds) { - psshBox.putLong(keyId.mostSignificantBits) - psshBox.putLong(keyId.leastSignificantBits) + keyId.toLongs { mostSignificantBits, leastSignificantBits -> + buffer.writeLong(mostSignificantBits) + buffer.writeLong(leastSignificantBits) + } } } if (data != null && data.size != 0) { - psshBox.putInt(data.size) - psshBox.put(data) + buffer.writeInt(data.size) + buffer.write(data) } else { - psshBox.putInt(0) + buffer.writeInt(0) } - return psshBox.array() + return buffer.readByteArray() } } From e48c97363e626ee20e74673910e7f8a771ab351a Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Wed, 15 Jul 2026 15:27:22 -0700 Subject: [PATCH 27/38] [skip ci] Add actual declarations for webMain/webTest (#3072) --- .../com/lagradost/api/ContextHelper.web.kt | 4 +++ .../webMain/kotlin/com/lagradost/api/Log.kt | 21 ++++++++++++ .../extractors/YoutubeExtractor.web.kt | 32 +++++++++++++++++++ .../cloudstream3/mvvm/ArchComponentExt.web.kt | 32 +++++++++++++++++++ .../cloudstream3/utils/Coroutines.web.kt | 14 ++++++++ .../cloudstream3/utils/Ignore.web.kt | 5 +++ 6 files changed, 108 insertions(+) create mode 100644 library/src/webMain/kotlin/com/lagradost/api/ContextHelper.web.kt create mode 100644 library/src/webMain/kotlin/com/lagradost/api/Log.kt create mode 100644 library/src/webMain/kotlin/com/lagradost/cloudstream3/extractors/YoutubeExtractor.web.kt create mode 100644 library/src/webMain/kotlin/com/lagradost/cloudstream3/mvvm/ArchComponentExt.web.kt create mode 100644 library/src/webMain/kotlin/com/lagradost/cloudstream3/utils/Coroutines.web.kt create mode 100644 library/src/webTest/kotlin/com/lagradost/cloudstream3/utils/Ignore.web.kt diff --git a/library/src/webMain/kotlin/com/lagradost/api/ContextHelper.web.kt b/library/src/webMain/kotlin/com/lagradost/api/ContextHelper.web.kt new file mode 100644 index 000000000..83cae11a5 --- /dev/null +++ b/library/src/webMain/kotlin/com/lagradost/api/ContextHelper.web.kt @@ -0,0 +1,4 @@ +package com.lagradost.api + +actual fun getContext(): Any? = null +actual fun setContext(context: Any?) = Unit diff --git a/library/src/webMain/kotlin/com/lagradost/api/Log.kt b/library/src/webMain/kotlin/com/lagradost/api/Log.kt new file mode 100644 index 000000000..be185f0ca --- /dev/null +++ b/library/src/webMain/kotlin/com/lagradost/api/Log.kt @@ -0,0 +1,21 @@ +package com.lagradost.api + +actual object Log { + actual fun d(tag: String, message: String) { + // There is no console.debug in Kotlin/JS (Yet) + // We will switch this when it is added. + console.log("[$tag] $message") + } + + actual fun i(tag: String, message: String) { + console.info("[$tag] $message") + } + + actual fun w(tag: String, message: String) { + console.warn("[$tag] $message") + } + + actual fun e(tag: String, message: String) { + console.error("[$tag] $message") + } +} diff --git a/library/src/webMain/kotlin/com/lagradost/cloudstream3/extractors/YoutubeExtractor.web.kt b/library/src/webMain/kotlin/com/lagradost/cloudstream3/extractors/YoutubeExtractor.web.kt new file mode 100644 index 000000000..07463bd56 --- /dev/null +++ b/library/src/webMain/kotlin/com/lagradost/cloudstream3/extractors/YoutubeExtractor.web.kt @@ -0,0 +1,32 @@ +package com.lagradost.cloudstream3.extractors + +import com.lagradost.cloudstream3.SubtitleFile +import com.lagradost.cloudstream3.utils.ExtractorApi +import com.lagradost.cloudstream3.utils.ExtractorLink + +/** + * Web/JS target shim. + * + * NewPipeExtractor is JVM-only, so the full extraction logic used on + * Android/desktop can't run here. This stub keeps the multiplatform + * source set compiling; it performs no extraction and simply reports + * that no links were found on this target. + * + * If/when a JS-compatible YouTube extraction path exists, replace the + * body of getUrl() with that implementation. + */ +actual open class YoutubeExtractor actual constructor() : ExtractorApi() { + + actual override val mainUrl = "https://www.youtube.com" + actual override val name = "YouTube" + actual override val requiresReferer = false + + actual override suspend fun getUrl( + url: String, + referer: String?, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit, + ) { + // No-op on web target: NewPipeExtractor is unavailable here. + } +} diff --git a/library/src/webMain/kotlin/com/lagradost/cloudstream3/mvvm/ArchComponentExt.web.kt b/library/src/webMain/kotlin/com/lagradost/cloudstream3/mvvm/ArchComponentExt.web.kt new file mode 100644 index 000000000..f108bf7fb --- /dev/null +++ b/library/src/webMain/kotlin/com/lagradost/cloudstream3/mvvm/ArchComponentExt.web.kt @@ -0,0 +1,32 @@ +package com.lagradost.cloudstream3.mvvm + +actual fun platformThrowAbleToResource(throwable: Throwable): Resource { + val message = throwable.message.orEmpty() + return when { + message.contains("timeout", ignoreCase = true) -> { + Resource.Failure( + true, + "Connection Timeout\nPlease try again later." + ) + } + + message.contains("NetworkError", ignoreCase = true) || + message.contains("Failed to fetch", ignoreCase = true) || + message.contains("ERR_NAME_NOT_RESOLVED", ignoreCase = true) -> { + Resource.Failure( + true, + "Cannot connect to server, try again later.\n${throwable.message}" + ) + } + + message.contains("SSL", ignoreCase = true) || + message.contains("certificate", ignoreCase = true) -> { + Resource.Failure( + true, + (throwable.message ?: "SSL error") + "\nTry a VPN or DNS." + ) + } + + else -> safeFail(throwable) + } +} diff --git a/library/src/webMain/kotlin/com/lagradost/cloudstream3/utils/Coroutines.web.kt b/library/src/webMain/kotlin/com/lagradost/cloudstream3/utils/Coroutines.web.kt new file mode 100644 index 000000000..3ea59f6cc --- /dev/null +++ b/library/src/webMain/kotlin/com/lagradost/cloudstream3/utils/Coroutines.web.kt @@ -0,0 +1,14 @@ +package com.lagradost.cloudstream3.utils + +import androidx.annotation.AnyThread +import androidx.annotation.MainThread +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Dispatchers + +actual val workerDispatcher: CoroutineDispatcher = Dispatchers.Default +internal actual annotation class WorkerThread() + +@AnyThread +actual fun runOnMainThreadNative(@MainThread work: () -> Unit) { + work.invoke() +} diff --git a/library/src/webTest/kotlin/com/lagradost/cloudstream3/utils/Ignore.web.kt b/library/src/webTest/kotlin/com/lagradost/cloudstream3/utils/Ignore.web.kt new file mode 100644 index 000000000..0381a1944 --- /dev/null +++ b/library/src/webTest/kotlin/com/lagradost/cloudstream3/utils/Ignore.web.kt @@ -0,0 +1,5 @@ +package com.lagradost.cloudstream3.utils + +import kotlin.test.Ignore + +actual typealias IgnoreOnWeb = Ignore From 115be075d4b156b6795ff60bceb96986a629bd99 Mon Sep 17 00:00:00 2001 From: danzybae Date: Mon, 20 Jul 2026 01:30:04 +0700 Subject: [PATCH 28/38] Fix posterHeaders not passed in home preview carousel (#3085) --- .../com/lagradost/cloudstream3/ui/home/HomeScrollAdapter.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeScrollAdapter.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeScrollAdapter.kt index e42e774b5..1020205ac 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeScrollAdapter.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeScrollAdapter.kt @@ -58,7 +58,7 @@ class HomeScrollAdapter( when (binding) { is HomeScrollViewBinding -> { - binding.homeScrollPreview.loadImage(posterUrl) + binding.homeScrollPreview.loadImage(posterUrl, item.posterHeaders) binding.homeScrollPreviewTags.apply { text = item.tags?.joinToString(" • ") ?: "" isGone = item.tags.isNullOrEmpty() @@ -79,8 +79,8 @@ class HomeScrollAdapter( binding.homeScrollPreview.setOnClickListener { view -> callback.invoke(view ?: return@setOnClickListener, position, item) } - binding.homeScrollPreview.loadImage(posterUrl) + binding.homeScrollPreview.loadImage(posterUrl, item.posterHeaders) } } } -} \ No newline at end of file +} From 2cd65aad4f7c2676beeed676d925bef314fc8880 Mon Sep 17 00:00:00 2001 From: danzybae Date: Sun, 26 Jul 2026 02:53:45 +0700 Subject: [PATCH 29/38] Fix posterHeaders not passed in TV detail menu (#3088) --- .../com/lagradost/cloudstream3/ui/result/ResultFragmentTv.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentTv.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentTv.kt index cfbacc5d1..ce67763d0 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentTv.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentTv.kt @@ -877,7 +877,7 @@ class ResultFragmentTv : BaseFragment( resultCastText.setText(d.actorsText) resultNextAiring.setText(d.nextAiringEpisode) resultNextAiringTime.setText(d.nextAiringDate) - resultPoster.loadImage(d.posterImage) + resultPoster.loadImage(d.posterImage, headers = d.posterHeaders) var isExpanded = false resultDescription.apply { @@ -910,7 +910,7 @@ class ResultFragmentTv : BaseFragment( R.drawable.profile_bg_teal ).random() - backgroundPoster.loadImage(d.posterBackgroundImage) { + backgroundPoster.loadImage(d.posterBackgroundImage, headers = d.posterHeaders) { error { getImageFromDrawable(context ?: return@error null, error) } } From 93d3b36575568bceeaf9e77fef1afb888d88a0dd Mon Sep 17 00:00:00 2001 From: Kraptor123 <89366989+Kraptor123@users.noreply.github.com> Date: Sun, 26 Jul 2026 01:43:11 +0300 Subject: [PATCH 30/38] feat: add settings button to provider selection in home (#3060) --- .../cloudstream3/ui/home/HomeFragment.kt | 24 ++++++++++++- .../sort_bottom_single_provider_choice.xml | 34 ++++++++++++++----- 2 files changed, 49 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt index 96c5f9f56..604695859 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt @@ -24,6 +24,8 @@ import androidx.core.view.isVisible import androidx.fragment.app.activityViewModels import androidx.preference.PreferenceManager import androidx.recyclerview.widget.RecyclerView +import com.lagradost.cloudstream3.plugins.PluginManager +import com.lagradost.cloudstream3.CloudStreamApp.Companion.getActivity import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.chip.Chip @@ -43,6 +45,7 @@ import com.lagradost.cloudstream3.mvvm.Resource import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.mvvm.observe import com.lagradost.cloudstream3.mvvm.observeNullable +import com.lagradost.cloudstream3.plugins.Plugin import com.lagradost.cloudstream3.ui.APIRepository.Companion.noneApi import com.lagradost.cloudstream3.ui.APIRepository.Companion.randomApi import com.lagradost.cloudstream3.ui.BaseFragment @@ -424,11 +427,30 @@ class HomeFragment : BaseFragment( .inflate(R.layout.sort_bottom_single_provider_choice, parent, false) val titleText = view.findViewById(R.id.text1) val pinIcon = view.findViewById(R.id.pinicon) + val settingsIcon = view.findViewById(R.id.action_settings) + val name = getItem(position) titleText?.text = name + val providerApi = currentValidApis[position] val isPinned = - pinnedphashset.contains(currentValidApis[position].name) + pinnedphashset.contains(providerApi.name) pinIcon.visibility = if (isPinned) View.VISIBLE else View.GONE + + val pluginInstance = providerApi.sourcePlugin?.let { PluginManager.plugins[it] } as? Plugin + val isDownloadedPluginWithSettings = pluginInstance?.openSettings != null && !isLayout(TV) + + settingsIcon.visibility = if (isDownloadedPluginWithSettings) View.VISIBLE else View.GONE + if (isDownloadedPluginWithSettings) { + settingsIcon.setOnClickListener { + try { + val activityContext = it.context.getActivity() ?: it.context + pluginInstance.openSettings?.invoke(activityContext) + } catch (e: Throwable) { + logError(e) + } + } + } + return view } } diff --git a/app/src/main/res/layout/sort_bottom_single_provider_choice.xml b/app/src/main/res/layout/sort_bottom_single_provider_choice.xml index 657e2f2b8..ea778c488 100644 --- a/app/src/main/res/layout/sort_bottom_single_provider_choice.xml +++ b/app/src/main/res/layout/sort_bottom_single_provider_choice.xml @@ -10,13 +10,31 @@ style="@style/CheckLabel" android:layout_gravity="center" tools:text="hello" /> - + android:orientation="horizontal"> + + + + + \ No newline at end of file From 1e33e4227700c90c972b1a3d9071002ad6e8e8ac Mon Sep 17 00:00:00 2001 From: firelight <147925818+fire-light42@users.noreply.github.com> Date: Fri, 31 Jul 2026 14:59:31 +0000 Subject: [PATCH 31/38] fix subtitle clipping (#3099) --- .../lagradost/cloudstream3/ui/subtitles/SubtitlesFragment.kt | 3 +++ app/src/main/res/layout/player_custom_layout.xml | 2 ++ app/src/main/res/layout/player_custom_layout_tv.xml | 2 ++ app/src/main/res/layout/trailer_custom_layout.xml | 2 ++ 4 files changed, 9 insertions(+) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/subtitles/SubtitlesFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/subtitles/SubtitlesFragment.kt index cf892424f..bf77ef247 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/subtitles/SubtitlesFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/subtitles/SubtitlesFragment.kt @@ -115,6 +115,9 @@ class SubtitlesFragment : BaseDialogFragment( ) } + view.clipToPadding = false + view.clipChildren = false + // we default to 25sp, this is needed as RoundedBackgroundColorSpan breaks on override sizes val size = data.fixedTextSize ?: 25.0f view.setFixedTextSize(TypedValue.COMPLEX_UNIT_SP, size) diff --git a/app/src/main/res/layout/player_custom_layout.xml b/app/src/main/res/layout/player_custom_layout.xml index 04a2a1f1f..c4aa4c473 100644 --- a/app/src/main/res/layout/player_custom_layout.xml +++ b/app/src/main/res/layout/player_custom_layout.xml @@ -1076,6 +1076,8 @@ diff --git a/app/src/main/res/layout/player_custom_layout_tv.xml b/app/src/main/res/layout/player_custom_layout_tv.xml index 077929d87..88e4cd984 100644 --- a/app/src/main/res/layout/player_custom_layout_tv.xml +++ b/app/src/main/res/layout/player_custom_layout_tv.xml @@ -1116,6 +1116,8 @@ diff --git a/app/src/main/res/layout/trailer_custom_layout.xml b/app/src/main/res/layout/trailer_custom_layout.xml index 88a318874..ddaa53464 100644 --- a/app/src/main/res/layout/trailer_custom_layout.xml +++ b/app/src/main/res/layout/trailer_custom_layout.xml @@ -102,6 +102,8 @@ From 2eb05e8f354ffdd4f0833dbc365250ac7a99eeb2 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Fri, 31 Jul 2026 09:01:45 -0600 Subject: [PATCH 32/38] DataStore: deprecate toKotlinObject (#2964) 1. It can sometimes cause type erasure 2. It is less clear what it is doing 3. It is easy to replace 4. It is very rarely used, almost all app usage has already been replaced, and I haven't found any extensions actually using it but still following normal deprecation cycle as some still might be using it. --- .../lagradost/cloudstream3/utils/DataStore.kt | 32 ++++++++++++++----- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/DataStore.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/DataStore.kt index 02ee69791..68f262d8d 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/DataStore.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/DataStore.kt @@ -181,11 +181,11 @@ object DataStore { } fun Context.getKey(path: String, valueType: Class): T? { - try { + return try { val json: String = getSharedPrefs().getString(path, null) ?: return null - return parseJson(json, valueType.kotlin) - } catch (e: Exception) { - return null + parseJson(json, valueType.kotlin) + } catch (_: Exception) { + null } } @@ -193,21 +193,37 @@ object DataStore { setKey(getFolderName(folder, path), value) } + @Deprecated( + message = "Use parseJson(this) directly instead.", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith( + expression = "parseJson(this)", + imports = ["com.lagradost.cloudstream3.utils.AppUtils.parseJson"], + ), + ) inline fun String.toKotlinObject(): T { return parseJson(this) } + @Deprecated( + message = "Use parseJson(this) directly instead.", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith( + expression = "parseJson(this)", + imports = ["com.lagradost.cloudstream3.utils.AppUtils.parseJson"], + ), + ) fun String.toKotlinObject(valueType: Class): T { return parseJson(this, valueType.kotlin) } // GET KEY GIVEN PATH AND DEFAULT VALUE, NULL IF ERROR inline fun Context.getKey(path: String, defVal: T?): T? { - try { + return try { val json: String = getSharedPrefs().getString(path, null) ?: return defVal - return json.toKotlinObject() - } catch (e: Exception) { - return null + parseJson(json) + } catch (_: Exception) { + null } } From f09435dc183ba3e6c37579f10d8d8ba6aa5ba307 Mon Sep 17 00:00:00 2001 From: CranberrySoup <142951702+CranberrySoup@users.noreply.github.com> Date: Fri, 31 Jul 2026 15:25:00 +0000 Subject: [PATCH 33/38] Add source hiding (#2570) --- .../cloudstream3/ui/player/GeneratorPlayer.kt | 110 ++++++++++++---- .../ui/player/PlayerGeneratorViewModel.kt | 62 ++++++++- .../source_priority/QualityDataHelper.kt | 48 ++++++- .../source_priority/SourcePriorityDialog.kt | 121 +++++++++--------- .../SourceProfileSettingsDialog.kt | 48 +++++++ .../player_select_source_priority.xml | 34 +++-- .../layout/player_select_source_priority.xml | 7 + .../layout/source_profile_settings_dialog.xml | 99 ++++++++++++++ app/src/main/res/values/strings.xml | 8 ++ 9 files changed, 426 insertions(+), 111 deletions(-) create mode 100644 app/src/main/java/com/lagradost/cloudstream3/ui/player/source_priority/SourceProfileSettingsDialog.kt create mode 100644 app/src/main/res/layout/source_profile_settings_dialog.xml diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt index 91e3ff970..683a322c4 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt @@ -8,6 +8,7 @@ import android.content.Context import android.content.Intent import android.content.res.ColorStateList import android.graphics.Bitmap +import android.graphics.Typeface import android.os.Build import android.os.Bundle import android.text.Spanned @@ -79,6 +80,7 @@ import com.lagradost.cloudstream3.ui.player.CS3IPlayer.Companion.preferredAudioT import com.lagradost.cloudstream3.ui.player.CustomDecoder.Companion.updateForcedEncoding import com.lagradost.cloudstream3.ui.player.PlayerSubtitleHelper.Companion.toSubtitleMimeType import com.lagradost.cloudstream3.ui.player.source_priority.LinkSource +import com.lagradost.cloudstream3.ui.player.source_priority.ProfileSettings import com.lagradost.cloudstream3.ui.player.source_priority.QualityDataHelper import com.lagradost.cloudstream3.ui.player.source_priority.QualityDataHelper.getLinkPriority import com.lagradost.cloudstream3.ui.player.source_priority.QualityProfileDialog @@ -132,6 +134,7 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.isActive import kotlinx.coroutines.launch import java.io.Serializable +import java.lang.ref.WeakReference import java.util.Calendar import java.util.UUID import java.util.concurrent.ConcurrentHashMap @@ -510,7 +513,7 @@ class GeneratorPlayer : FullScreenPlayer() { showDownloadProgress(DownloadEvent(0, 0, 0, null)) // uiReset() // Removed due to UX - + currentSelectedLink = link // setEpisodes(viewModel.getAllMeta() ?: emptyList()) setPlayerDimen(null) @@ -1112,21 +1115,29 @@ class GeneratorPlayer : FullScreenPlayer() { var sourceIndex = 0 var startSource = 0 - var sortedUrls = emptyList>() + // Filtered and sorted links + var currentHiddenFooter: View? = null + var filteredLinks: List = emptyList() fun refreshLinks(qualityProfile: Int) { - sortedUrls = viewModel.state.sortLinks(qualityProfile) - if (sortedUrls.isEmpty()) { + val currentLinkUsed = currentSelectedLink + // Always display current linkFooter + val sortedLinks = viewModel.state.sortLinks(qualityProfile) + + filteredLinks = sortedLinks.filter { it.shouldUseLink || it.link == currentLinkUsed } + + if (sortedLinks.isEmpty()) { sourceDialog.findViewById(R.id.sort_sources_holder)?.isGone = true } else { - startSource = sortedUrls.indexOf(currentSelectedLink) + startSource = filteredLinks.indexOfFirst { it.link == currentLinkUsed } sourceIndex = startSource val sourcesArrayAdapter = ArrayAdapter(ctx, R.layout.sort_bottom_single_choice) - sourcesArrayAdapter.addAll(sortedUrls.map { (link, uri) -> + sourcesArrayAdapter.addAll(filteredLinks.map { displayLink -> + val (link, uri) = displayLink.link val name = link?.name ?: uri?.name ?: "NULL" "$name ${Qualities.getStringByInt(link?.quality)}" }) @@ -1142,7 +1153,7 @@ class GeneratorPlayer : FullScreenPlayer() { } providerList.setOnItemLongClickListener { _, _, position, _ -> - sortedUrls.getOrNull(position)?.first?.url?.let { + sortedLinks.getOrNull(position)?.link?.first?.url?.let { clipboardHelper( txt(R.string.video_source), it @@ -1150,6 +1161,25 @@ class GeneratorPlayer : FullScreenPlayer() { } true } + + val hiddenLinks = sortedLinks.size - filteredLinks.size + providerList.removeFooterView(currentHiddenFooter) + + if (hiddenLinks > 0) { + val hiddenLinksFooter: TextView = layoutInflater.inflate( + R.layout.sort_bottom_footer_add_choice, null + ) as TextView + + providerList.addFooterView(hiddenLinksFooter, null, false) + currentHiddenFooter = hiddenLinksFooter + + val hiddenLinksText = + ctx.resources.getQuantityString(R.plurals.links_hidden, hiddenLinks) + .format(hiddenLinks) + hiddenLinksFooter.text = hiddenLinksText + hiddenLinksFooter.setCompoundDrawables(null, null, null, null) + hiddenLinksFooter.setTypeface(null, Typeface.ITALIC) + } } } @@ -1363,8 +1393,8 @@ class GeneratorPlayer : FullScreenPlayer() { } } if (init) { - sortedUrls.getOrNull(sourceIndex)?.let { - loadLink(it, true) + filteredLinks.getOrNull(sourceIndex)?.let { + loadLink(it.link, true) } } sourceDialog.dismissSafe(activity) @@ -1532,6 +1562,10 @@ class GeneratorPlayer : FullScreenPlayer() { } override fun playerError(exception: Throwable) { + currentSelectedLink?.let { link -> + viewModel.modifyState { this.addError(link) } + } + val currentUrl = currentSelectedLink?.let { it.first?.url ?: it.second?.uri?.toString() } ?: "unknown" val headers = currentSelectedLink?.first?.headers?.toString() ?: "none" @@ -1554,8 +1588,22 @@ class GeneratorPlayer : FullScreenPlayer() { private fun noLinksFound() { viewModel.forceClearCache = true + val hiddenLinks = viewModel.state.sortLinks(currentQualityProfile).count { !it.shouldUseLink } + + context?.let { ctx -> + // Display that there are hidden links to the user. + if (hiddenLinks > 0) { + val noLinksString = ctx.getString(R.string.no_links_found_toast) + val hiddenString = + ctx.resources.getQuantityString(R.plurals.links_hidden, hiddenLinks) + .format(hiddenLinks) + val toastText = "$noLinksString\n($hiddenString)" + showToast(toastText, Toast.LENGTH_SHORT) + } else { + showToast(R.string.no_links_found_toast, Toast.LENGTH_SHORT) + } + } - showToast(R.string.no_links_found_toast, Toast.LENGTH_SHORT) activity?.popCurrentPage() } @@ -1566,7 +1614,9 @@ class GeneratorPlayer : FullScreenPlayer() { } val links = viewModel.state.sortLinks(currentQualityProfile) - if (links.isEmpty()) { + + val firstAvailableLink = links.firstOrNull { it.shouldUseLink }?.link + if (firstAvailableLink == null) { noLinksFound() return } @@ -1574,7 +1624,7 @@ class GeneratorPlayer : FullScreenPlayer() { if (!isPlayerActive.compareAndSet(false, true)) { return } - loadLink(links.first(), false) + loadLink(firstAvailableLink, false) showPlayerMetadata() } @@ -1641,25 +1691,26 @@ class GeneratorPlayer : FullScreenPlayer() { } } - override fun hasNextMirror(): Boolean { + private fun getNextLink(): DisplayLink? { val links = viewModel.state.sortLinks(currentQualityProfile) - return links.isNotEmpty() && links.indexOf(currentSelectedLink) + 1 < links.size + val currentIndex = links.indexOfFirst { it.link == currentSelectedLink } + val nextPotentialLink = + links.withIndex().firstOrNull { it.index > currentIndex && it.value.shouldUseLink } + return nextPotentialLink?.value + } + + override fun hasNextMirror(): Boolean { + return getNextLink() != null } override fun nextMirror() { - val links = viewModel.state.sortLinks(currentQualityProfile) - if (links.isEmpty()) { + val nextLink = getNextLink() + if (nextLink == null) { noLinksFound() return } - val newIndex = links.indexOf(currentSelectedLink) + 1 - if (newIndex >= links.size) { - noLinksFound() - return - } - - loadLink(links[newIndex], true) + loadLink(nextLink.link, true) } override fun onDestroy() { @@ -2166,6 +2217,7 @@ class GeneratorPlayer : FullScreenPlayer() { isPlayerActive.set(false) binding?.overlayLoadingSkipButton?.isVisible = false binding?.playerLoadingOverlay?.isVisible = true + viewModel.modifyState { setError(emptyList()) } uiReset() } @@ -2297,19 +2349,23 @@ class GeneratorPlayer : FullScreenPlayer() { } } - observe(viewModel.currentLinks) { (links, instance) -> + observe(viewModel.currentLinks) { (_, instance) -> if (instance != viewModel.state.instance) return@observe // Outdated observe - val turnVisible = links.isNotEmpty() && viewModel.generator?.canSkipLoading == true + val sortedLinks = viewModel.state.sortLinks(currentQualityProfile) + val usableLinks = sortedLinks.count { link -> link.shouldUseLink } + + val turnVisible = usableLinks > 0 && viewModel.generator?.canSkipLoading == true val wasGone = binding.overlayLoadingSkipButton.isGone binding.overlayLoadingSkipButton.apply { isVisible = turnVisible - if (links.isEmpty()) { + + if (usableLinks == 0) { setText(R.string.skip_loading) } else { @SuppressLint("SetTextI18n") - text = "${context.getString(R.string.skip_loading)} (${links.size})" + text = "${context.getString(R.string.skip_loading)} (${usableLinks})" } } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/PlayerGeneratorViewModel.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/PlayerGeneratorViewModel.kt index e3c390d50..cb8cf8bff 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/PlayerGeneratorViewModel.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/PlayerGeneratorViewModel.kt @@ -10,6 +10,8 @@ import com.lagradost.cloudstream3.mvvm.Resource import com.lagradost.cloudstream3.mvvm.launchSafe import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.mvvm.safeApiCall +import com.lagradost.cloudstream3.ui.player.source_priority.ProfileSettings +import com.lagradost.cloudstream3.ui.player.source_priority.QualityDataHelper import com.lagradost.cloudstream3.ui.player.source_priority.QualityDataHelper.getLinkPriority import com.lagradost.cloudstream3.ui.result.ResultEpisode import com.lagradost.cloudstream3.utils.Coroutines.ioSafe @@ -40,12 +42,20 @@ data class GeneratorState( val id: Int?, ) +data class DisplayLink( + val link: VideoLink, + // If the link should be displayed and used by the player + val shouldUseLink: Boolean, + val priority: Int +) + /** Immutable state of all current links relevant to displaying the video */ // @MustUseReturnValues // @Immutable data class VideoState( val subtitles: PersistentSet = persistentSetOf(), val links: PersistentSet = persistentSetOf(), + val erroredLinks: PersistentSet = persistentSetOf(), val stamps: PersistentList = persistentListOf(), val loading: Resource = Resource.Loading(), val generatorState: GeneratorState? = null, @@ -56,19 +66,52 @@ data class VideoState( * * sortedBy is not exactly expensive, but each hasNextMirror does it again, so this alleviates unnecessary recomputation * */ - private val sortedLinks: ConcurrentHashMap> = ConcurrentHashMap() + private val sortedLinks: ConcurrentHashMap> = ConcurrentHashMap() + /** + * The cache is guaranteed to be up to date link-wise due to the immutable links. + * However, hideNegativeSources and hideErrorSources could be updated, which requires clearing the cache. + */ fun clearSortedLinksCache() = sortedLinks.clear() + private fun hasLinkErrored(link: VideoLink): Boolean { + return erroredLinks.any { it == link } + } + + private fun VideoLink.toDisplayLink( + qualityProfile: Int, + hideNegativeSources: Boolean, + hideErrorSources: Boolean + ): DisplayLink { + val priority = getLinkPriority(qualityProfile, this.first) + val shouldHideLink = + (hideNegativeSources && priority < 0) || (hideErrorSources && hasLinkErrored(this)) + val displayLink = DisplayLink(this, !shouldHideLink, priority) + + return displayLink + } + // Modifying sortedLinks is not considered a "visible" side effect, and rerunning it does not change the result // It is by all standards, idempotent and by extension also pure as it has no "visible" side effect /** Returns .links in the sorted order according to the qualityProfile. * Use .links if order is not needed */ @Contract(pure = true) - fun sortLinks(qualityProfile: Int): List { - return sortedLinks[qualityProfile] ?: links.sortedBy { link -> + fun sortLinks(qualityProfile: Int): List { + sortedLinks[qualityProfile]?.let { + return it + } + + val hideNegativeSources = + QualityDataHelper.getProfileSetting(qualityProfile, ProfileSettings.HideNegativeSources) + val hideErrorSources = + QualityDataHelper.getProfileSetting(qualityProfile, ProfileSettings.HideErrorSources) + + return links.map { link -> // negative because we want to sort highest quality first - -getLinkPriority(qualityProfile, link.first) + link.toDisplayLink(qualityProfile, hideNegativeSources, hideErrorSources) + }.sortedBy { + // negative because we want to sort highest quality first + -it.priority }.also { value -> sortedLinks[qualityProfile] = value } } @@ -113,6 +156,12 @@ data class VideoState( @JvmName("setVideoSkipStamp") @Contract(pure = true) fun set(items: Collection): VideoState = copy(stamps = items.toPersistentList()) + + @Contract(pure = true) + fun addError(item: VideoLink): VideoState = copy(erroredLinks = erroredLinks.add(item)) + + @Contract(pure = true) + fun setError(items: Collection): VideoState = copy(erroredLinks = items.toPersistentSet()) } data class VideoLive( @@ -141,9 +190,8 @@ class PlayerGeneratorViewModel : ViewModel() { var state = VideoState(instance = 0) private set - private val _currentLinks = - MutableLiveData>>>(null) - val currentLinks: LiveData>>> = _currentLinks + private val _currentLinks = MutableLiveData>>(null) + val currentLinks: LiveData>> = _currentLinks private val _currentSubtitles = MutableLiveData>>(null) val currentSubtitles: LiveData>> = _currentSubtitles diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/source_priority/QualityDataHelper.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/source_priority/QualityDataHelper.kt index 15d25148c..d8d47bd10 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/source_priority/QualityDataHelper.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/source_priority/QualityDataHelper.kt @@ -12,12 +12,16 @@ import com.lagradost.cloudstream3.utils.txt import com.lagradost.cloudstream3.utils.DataStoreHelper.currentAccount import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.Qualities +import java.util.EnumMap +import java.util.concurrent.ConcurrentHashMap +import kotlin.also import kotlin.math.abs object QualityDataHelper { private const val VIDEO_SOURCE_PRIORITY = "video_source_priority" private const val VIDEO_PROFILE_NAME = "video_profile_name" private const val VIDEO_QUALITY_PRIORITY = "video_quality_priority" + const val VIDEO_PROFILE_SETTINGS = "video_profile_settings" // Old key only supporting one type per profile @Deprecated("Changed to support multiple types per profile") @@ -53,13 +57,21 @@ object QualityDataHelper { val types: Set ) + + // Map profile and name to priority + val sourcePriorityCache: ConcurrentHashMap> = ConcurrentHashMap() + fun getSourcePriority(profile: Int, name: String?): Int { if (name == null) return DEFAULT_SOURCE_PRIORITY - return getKey( + + return sourcePriorityCache[profile]?.get(name) ?: (getKey( "$currentAccount/$VIDEO_SOURCE_PRIORITY/$profile", name, DEFAULT_SOURCE_PRIORITY - ) ?: DEFAULT_SOURCE_PRIORITY + ) ?: DEFAULT_SOURCE_PRIORITY).also { + sourcePriorityCache.getOrPut(profile) { hashMapOf() } + sourcePriorityCache[profile]?.set(name, it) + } } fun getAllSourcePriorityNames(profile: Int): List { @@ -77,6 +89,8 @@ object QualityDataHelper { } else { setKey(folder, name, priority) } + + sourcePriorityCache[profile]?.set(name, priority) } fun setProfileName(profile: Int, name: String?) { @@ -93,12 +107,17 @@ object QualityDataHelper { ?: txt(R.string.profile_number, profile) } + // Map profile and quality to priority + val qualityPriorityCache: ConcurrentHashMap> = ConcurrentHashMap() fun getQualityPriority(profile: Int, quality: Qualities): Int { - return getKey( + return qualityPriorityCache[profile]?.get(quality) ?: (getKey( "$currentAccount/$VIDEO_QUALITY_PRIORITY/$profile", quality.value.toString(), quality.defaultPriority - ) ?: quality.defaultPriority + )?.also { + qualityPriorityCache.getOrPut(profile) { EnumMap(Qualities::class.java) } + qualityPriorityCache[profile]?.set(quality, it) + }) ?: quality.defaultPriority } fun setQualityPriority(profile: Int, quality: Qualities, priority: Int) { @@ -107,8 +126,24 @@ object QualityDataHelper { quality.value.toString(), priority ) + qualityPriorityCache[profile]?.set(quality, priority) } + fun setProfileSetting(profile: Int, setting: ProfileSettings, value: T) { + val folder = "$currentAccount/$VIDEO_PROFILE_SETTINGS/$profile" + // Prevent unnecessary keys + if (value == setting.defaultValue) { + removeKey(folder, setting.key) + } else { + setKey(folder, setting.key, value) + } + } + + inline fun getProfileSetting(profile: Int, setting: ProfileSettings): T { + val folder = "$currentAccount/$VIDEO_PROFILE_SETTINGS/$profile" + val value = getKey(folder, setting.key) + return value ?: setting.defaultValue + } @Suppress("DEPRECATION") fun getQualityProfileTypes(profile: Int): Set { @@ -224,3 +259,8 @@ object QualityDataHelper { return Qualities.entries.minBy { abs(it.value - target) } } } + +sealed class ProfileSettings(val key: String, val defaultValue: T) { + object HideErrorSources : ProfileSettings("hide_error_sources", false) + object HideNegativeSources : ProfileSettings("hide_negative_sources", false) +} diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/source_priority/SourcePriorityDialog.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/source_priority/SourcePriorityDialog.kt index c8ac96ebb..604ec3149 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/source_priority/SourcePriorityDialog.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/source_priority/SourcePriorityDialog.kt @@ -14,7 +14,7 @@ import com.lagradost.cloudstream3.utils.UIHelper.fixSystemBarsPadding class SourcePriorityDialog( val ctx: Context, - @StyleRes themeRes: Int, + @StyleRes val themeRes: Int, val links: List, private val profile: QualityDataHelper.QualityProfile, /** @@ -28,76 +28,79 @@ class SourcePriorityDialog( PlayerSelectSourcePriorityBinding.inflate(LayoutInflater.from(ctx), null, false) setContentView(binding.root) fixSystemBarsPadding(binding.root) - val sourcesRecyclerView = binding.sortSources - val qualitiesRecyclerView = binding.sortQualities - val profileText = binding.profileTextEditable - val saveBtt = binding.saveBtt - val exitBtt = binding.closeBtt - val helpBtt = binding.helpBtt - profileText.setText(QualityDataHelper.getProfileName(profile.id).asString(context)) - profileText.hint = txt(R.string.profile_number, profile.id).asString(context) + binding.apply { + profileTextEditable.setText( + QualityDataHelper.getProfileName(profile.id).asString(context) + ) + profileTextEditable.hint = txt(R.string.profile_number, profile.id).asString(context) - sourcesRecyclerView.adapter = PriorityAdapter( - ).apply { - submitList(links.map { link -> - SourcePriority( - null, - link.source, - QualityDataHelper.getSourcePriority(profile.id, link.source) - ) - }.distinctBy { it.name }.sortedBy { -it.priority }) - } + sortSources.adapter = PriorityAdapter( + ).apply { + val sortedLinks = links.map { link -> + SourcePriority( + null, + link.source, + QualityDataHelper.getSourcePriority(profile.id, link.source) + ) + }.distinctBy { it.name }.sortedBy { -it.priority } - qualitiesRecyclerView.adapter = PriorityAdapter( - ).apply { - submitList(Qualities.entries.mapNotNull { - SourcePriority( - it, - Qualities.getStringByIntFull(it.value).ifBlank { return@mapNotNull null }, - QualityDataHelper.getQualityPriority(profile.id, it) - ) - }.sortedBy { -it.priority }) - } - - @Suppress("UNCHECKED_CAST") // We know the types - saveBtt.setOnClickListener { - val qualityAdapter = qualitiesRecyclerView.adapter as? PriorityAdapter - val sourcesAdapter = sourcesRecyclerView.adapter as? PriorityAdapter - - val qualities = qualityAdapter?.immutableCurrentList ?: emptyList() - val sources = sourcesAdapter?.immutableCurrentList ?: emptyList() - - qualities.forEach { - QualityDataHelper.setQualityPriority(profile.id, it.data, it.priority) + submitList(sortedLinks) } - sources.forEach { - QualityDataHelper.setSourcePriority(profile.id, it.name, it.priority) + sortQualities.adapter = PriorityAdapter( + ).apply { + submitList(Qualities.entries.mapNotNull { + SourcePriority( + it, + Qualities.getStringByIntFull(it.value).ifBlank { return@mapNotNull null }, + QualityDataHelper.getQualityPriority(profile.id, it) + ) + }.sortedBy { -it.priority }) } - qualityAdapter?.submitList(qualities.sortedBy { -it.priority }) - sourcesAdapter?.submitList(sources.sortedBy { -it.priority }) + @Suppress("UNCHECKED_CAST") // We know the types + saveBtt.setOnClickListener { + val qualityAdapter = sortQualities.adapter as? PriorityAdapter + val sourcesAdapter = sortSources.adapter as? PriorityAdapter - val savedProfileName = profileText.text.toString() - if (savedProfileName.isBlank()) { - QualityDataHelper.setProfileName(profile.id, null) - } else { - QualityDataHelper.setProfileName(profile.id, savedProfileName) + val qualities = qualityAdapter?.immutableCurrentList ?: emptyList() + val sources = sourcesAdapter?.immutableCurrentList ?: emptyList() + + qualities.forEach { + QualityDataHelper.setQualityPriority(profile.id, it.data, it.priority) + } + + sources.forEach { + QualityDataHelper.setSourcePriority(profile.id, it.name, it.priority) + } + + qualityAdapter?.submitList(qualities.sortedBy { -it.priority }) + sourcesAdapter?.submitList(sources.sortedBy { -it.priority }) + + val savedProfileName = profileTextEditable.text.toString() + if (savedProfileName.isBlank()) { + QualityDataHelper.setProfileName(profile.id, null) + } else { + QualityDataHelper.setProfileName(profile.id, savedProfileName) + } + updatedCallback.invoke() } - updatedCallback.invoke() - } - exitBtt.setOnClickListener { - this.dismissSafe() - } + closeBtt.setOnClickListener { + dismissSafe() + } - helpBtt.setOnClickListener { - AlertDialog.Builder(context, R.style.AlertDialogCustom).apply { - setMessage(R.string.quality_profile_help) - }.show() - } + helpBtt.setOnClickListener { + AlertDialog.Builder(context, R.style.AlertDialogCustom).apply { + setMessage(R.string.quality_profile_help) + }.show() + } + settingsBtt.setOnClickListener { + SourceProfileSettingsDialog(ctx, themeRes, profile.id).show() + } + } super.show() } } \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/source_priority/SourceProfileSettingsDialog.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/source_priority/SourceProfileSettingsDialog.kt new file mode 100644 index 000000000..c26955b5e --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/source_priority/SourceProfileSettingsDialog.kt @@ -0,0 +1,48 @@ +package com.lagradost.cloudstream3.ui.player.source_priority + +import android.app.Dialog +import android.content.Context +import android.view.LayoutInflater +import androidx.annotation.StyleRes +import com.lagradost.cloudstream3.databinding.SourceProfileSettingsDialogBinding +import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe +import com.lagradost.cloudstream3.utils.UIHelper.fixSystemBarsPadding + +class SourceProfileSettingsDialog( + val ctx: Context, + @StyleRes themeRes: Int, + val profile: Int +) : Dialog(ctx, themeRes) { + override fun show() { + val binding = + SourceProfileSettingsDialogBinding.inflate(LayoutInflater.from(ctx), null, false) + setContentView(binding.root) + fixSystemBarsPadding(binding.root) + + binding.apply { + var hideErrorSources = QualityDataHelper.getProfileSetting(profile, ProfileSettings.HideErrorSources) + var hideNegativeSources = QualityDataHelper.getProfileSetting(profile, ProfileSettings.HideNegativeSources) + + profileHideErrorSources.isChecked = hideErrorSources + profileHideErrorSources.setOnCheckedChangeListener { _, bool -> + hideErrorSources = bool + } + + profileHideNegativeSources.isChecked = hideNegativeSources + profileHideNegativeSources.setOnCheckedChangeListener { _, bool -> + hideNegativeSources = bool + } + + applyBtt.setOnClickListener { + QualityDataHelper.setProfileSetting(profile, ProfileSettings.HideErrorSources, hideErrorSources) + QualityDataHelper.setProfileSetting(profile, ProfileSettings.HideNegativeSources, hideNegativeSources) + dismissSafe() + } + + cancelBtt.setOnClickListener { + dismissSafe() + } + } + super.show() + } +} \ No newline at end of file diff --git a/app/src/main/res/layout-port/player_select_source_priority.xml b/app/src/main/res/layout-port/player_select_source_priority.xml index 2cba9c869..926227c58 100644 --- a/app/src/main/res/layout-port/player_select_source_priority.xml +++ b/app/src/main/res/layout-port/player_select_source_priority.xml @@ -1,6 +1,5 @@ - - + @@ -33,14 +32,21 @@ android:textSize="20sp" android:textStyle="bold" /> + + + android:src="@drawable/baseline_help_outline_24" /> - + + tools:ignore="LabelFor" + tools:text="@string/profile_number" /> + android:text="@string/sort_save" /> + android:text="@string/sort_close" /> diff --git a/app/src/main/res/layout/player_select_source_priority.xml b/app/src/main/res/layout/player_select_source_priority.xml index 182cd1861..0d825de77 100644 --- a/app/src/main/res/layout/player_select_source_priority.xml +++ b/app/src/main/res/layout/player_select_source_priority.xml @@ -160,6 +160,13 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 31cf951cf..14780da6b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -622,6 +622,14 @@ Subscribe Unsubscribe Profile %d + Profile settings + Hide sources with a negative priority + Hide sources with errors + + %d hidden link + %d hidden links + + Wi-Fi Mobile data Set default From a2adb6caaad38465b7b1e75a2d0de35c7e95ccf2 Mon Sep 17 00:00:00 2001 From: firelight <147925818+fire-light42@users.noreply.github.com> Date: Fri, 31 Jul 2026 15:35:02 +0000 Subject: [PATCH 34/38] [skip ci] Swallow exception when device does not support channels --- .../com/lagradost/cloudstream3/utils/TvChannelUtils.kt | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/TvChannelUtils.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/TvChannelUtils.kt index 4c0717b3f..b5b367262 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/TvChannelUtils.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/TvChannelUtils.kt @@ -149,10 +149,12 @@ object TvChannelUtils { .setInputId(inputId) .build() - val channelUri = context.contentResolver.insert( - TvContractCompat.Channels.CONTENT_URI, - channel.toContentValues() - ) + val channelUri = runCatching { + context.contentResolver.insert( + TvContractCompat.Channels.CONTENT_URI, + channel.toContentValues() + ) + }.getOrNull() channelUri?.let { val channelId = ContentUris.parseId(it) From ff701cb220897af32b02623db333adf263b443aa Mon Sep 17 00:00:00 2001 From: firelight <147925818+fire-light42@users.noreply.github.com> Date: Fri, 31 Jul 2026 16:59:46 +0000 Subject: [PATCH 35/38] Add Belarusian, Catalan and Albanian --- .../com/lagradost/cloudstream3/ui/settings/SettingsGeneral.kt | 3 +++ app/src/main/res/{values-be => values-b+be}/strings.xml | 0 app/src/main/res/{values-ca => values-b+ca}/strings.xml | 0 app/src/main/res/{values-sq => values-b+sq}/strings.xml | 0 4 files changed, 3 insertions(+) rename app/src/main/res/{values-be => values-b+be}/strings.xml (100%) rename app/src/main/res/{values-ca => values-b+ca}/strings.xml (100%) rename app/src/main/res/{values-sq => values-b+sq}/strings.xml (100%) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsGeneral.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsGeneral.kt index 354424978..631b12990 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsGeneral.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsGeneral.kt @@ -132,6 +132,9 @@ val appLanguages = arrayListOf( Pair("日本語 (にほんご)", "ja"), Pair("正體中文(臺灣)", "zh-TW"), Pair("한국어", "ko"), + Pair("беларуская мова", "be"), + Pair("català", "ca"), + Pair("Shqip мова", "sq"), /* end language list */ ).sortedBy { it.first.lowercase(Locale.ROOT) } // ye, we go alphabetical, so ppl don't put their lang on top diff --git a/app/src/main/res/values-be/strings.xml b/app/src/main/res/values-b+be/strings.xml similarity index 100% rename from app/src/main/res/values-be/strings.xml rename to app/src/main/res/values-b+be/strings.xml diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-b+ca/strings.xml similarity index 100% rename from app/src/main/res/values-ca/strings.xml rename to app/src/main/res/values-b+ca/strings.xml diff --git a/app/src/main/res/values-sq/strings.xml b/app/src/main/res/values-b+sq/strings.xml similarity index 100% rename from app/src/main/res/values-sq/strings.xml rename to app/src/main/res/values-b+sq/strings.xml From 7f750964f3929679a6674f1d13d62d082110af56 Mon Sep 17 00:00:00 2001 From: "recloudstream[bot]" <111277985+recloudstream[bot]@users.noreply.github.com> Date: Fri, 31 Jul 2026 17:00:05 +0000 Subject: [PATCH 36/38] chore(locales): fix locale issues --- .../lagradost/cloudstream3/ui/settings/SettingsGeneral.kt | 6 +++--- app/src/main/res/values-b+be/strings.xml | 2 +- app/src/main/res/values-b+ca/strings.xml | 2 +- app/src/main/res/values-b+sq/strings.xml | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsGeneral.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsGeneral.kt index 631b12990..b04f9a4a2 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsGeneral.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsGeneral.kt @@ -77,6 +77,7 @@ val appLanguages = arrayListOf( Pair("Azərbaycan dili", "az"), Pair("Bahasa Indonesia", "in"), Pair("Bahasa Melayu", "ms"), + Pair("català", "ca"), Pair("Deutsch", "de"), Pair("English", "en"), Pair("Español", "es"), @@ -97,6 +98,7 @@ val appLanguages = arrayListOf( Pair("Português", "pt"), Pair("Português (Brasil)", "pt-BR"), Pair("Română", "ro"), + Pair("Shqip мова", "sq"), Pair("Slovenčina", "sk"), Pair("Soomaaliga", "so"), Pair("Svenska", "sv"), @@ -106,6 +108,7 @@ val appLanguages = arrayListOf( Pair("Wikang Filipino", "fil"), Pair("Čeština", "cs"), Pair("Ελληνικά", "el"), + Pair("беларуская мова", "be"), Pair("български", "bg"), Pair("македонски", "mk"), Pair("русский", "ru"), @@ -132,9 +135,6 @@ val appLanguages = arrayListOf( Pair("日本語 (にほんご)", "ja"), Pair("正體中文(臺灣)", "zh-TW"), Pair("한국어", "ko"), - Pair("беларуская мова", "be"), - Pair("català", "ca"), - Pair("Shqip мова", "sq"), /* end language list */ ).sortedBy { it.first.lowercase(Locale.ROOT) } // ye, we go alphabetical, so ppl don't put their lang on top diff --git a/app/src/main/res/values-b+be/strings.xml b/app/src/main/res/values-b+be/strings.xml index 3d9200faf..9fbf8a8d5 100644 --- a/app/src/main/res/values-b+be/strings.xml +++ b/app/src/main/res/values-b+be/strings.xml @@ -125,7 +125,7 @@ Працягнуць прагляд Выдаліць Больш інфармацыі - \@string/home_play + @string/home_play Для карэктнай працы гэтага пастаўшчыка можа спатрэбіцца VPN Гэты пастаўшчык — Torrent, рэкамендуецца VPN Вэб-сайт не пастаўляе метададзеных, загрузіць відэа не ўдасца, калі на сайце яго няма. diff --git a/app/src/main/res/values-b+ca/strings.xml b/app/src/main/res/values-b+ca/strings.xml index 495eb5e3c..621243c78 100644 --- a/app/src/main/res/values-b+ca/strings.xml +++ b/app/src/main/res/values-b+ca/strings.xml @@ -123,7 +123,7 @@ Continua mirant Eliminar Més info - \@string/home_play + @string/home_play Potser necessiteu una VPN per que aquest proveïdor funcioni correctament Aquest proveïdor és un Torrent, es recomana fer servir una VPN El lloc no proveeix metadades, la càrrega del video fallarà si no existeix al lloc. diff --git a/app/src/main/res/values-b+sq/strings.xml b/app/src/main/res/values-b+sq/strings.xml index a13f87bfa..f45c8d87a 100644 --- a/app/src/main/res/values-b+sq/strings.xml +++ b/app/src/main/res/values-b+sq/strings.xml @@ -127,7 +127,7 @@ Vazhdo shikimin Hiq Më shumë informacion - \@string/home_play + @string/home_play Një VPN mund të nevojitet që ky ofrues të funksionojë në rregull Ky ofrues është Torrent, rekomandohet një VPN Metadata nuk ofrohet nga kjo faqe, ngarkimi i videos do të dështojë nëse nuk ekziston në këtë faqe. From e80fa1b30dc56b6cfb39ace68c8c284b255df785 Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Fri, 31 Jul 2026 19:00:10 +0200 Subject: [PATCH 37/38] Translated using Weblate (Albanian) Currently translated at 100.0% (733 of 733 strings) Translated using Weblate (Polish) Currently translated at 100.0% (733 of 733 strings) Translated using Weblate (Bengali) Currently translated at 52.6% (384 of 729 strings) Translated using Weblate (English) Currently translated at 100.0% (729 of 729 strings) Merge remote-tracking branch 'origin/master' Merge remote-tracking branch 'origin/master' Update translation files Updated by "Remove blank strings" hook in Weblate. Translated using Weblate (Bengali) Currently translated at 51.0% (372 of 729 strings) Merge remote-tracking branch 'origin/master' Merge remote-tracking branch 'origin/master' Merge remote-tracking branch 'origin/master' Merge remote-tracking branch 'origin/master' Translated using Weblate (Dutch) Currently translated at 92.4% (674 of 729 strings) Translated using Weblate (Vietnamese) Currently translated at 100.0% (729 of 729 strings) Translated using Weblate (Vietnamese) Currently translated at 100.0% (729 of 729 strings) Translated using Weblate (Bengali) Currently translated at 75.0% (3 of 4 strings) Translated using Weblate (Bengali) Currently translated at 49.9% (364 of 729 strings) Co-authored-by: 007 Co-authored-by: Aishik Mukherjee Co-authored-by: Hosted Weblate Co-authored-by: Man Co-authored-by: Matthaiks Co-authored-by: caviaz Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/bn/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/en/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/nl/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/pl/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/sq/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/vi/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/fastlane/bn/ Translation: Cloudstream/App Translation: Cloudstream/Fastlane --- app/src/main/res/values-b+bn/strings.xml | 49 ++++++++++++++++--- app/src/main/res/values-b+nl/strings.xml | 13 +++++ app/src/main/res/values-b+pl/strings.xml | 9 ++++ app/src/main/res/values-b+sq/strings.xml | 7 +++ app/src/main/res/values-b+vi/strings.xml | 10 ++-- app/src/main/res/values/strings.xml | 1 - fastlane/metadata/android/bn/changelogs/2.txt | 1 + .../metadata/android/bn/full_description.txt | 7 +++ .../metadata/android/bn/short_description.txt | 1 + fastlane/metadata/android/bn/title.txt | 1 + 10 files changed, 85 insertions(+), 14 deletions(-) create mode 100644 fastlane/metadata/android/bn/changelogs/2.txt create mode 100644 fastlane/metadata/android/bn/full_description.txt create mode 100644 fastlane/metadata/android/bn/short_description.txt create mode 100644 fastlane/metadata/android/bn/title.txt diff --git a/app/src/main/res/values-b+bn/strings.xml b/app/src/main/res/values-b+bn/strings.xml index e90608793..b6edbd1cd 100644 --- a/app/src/main/res/values-b+bn/strings.xml +++ b/app/src/main/res/values-b+bn/strings.xml @@ -3,7 +3,7 @@ পোস্টার ক্লাউডস্ট্রিম দিয়ে চালান হোম - অভিনয়েঃ %s + অভিনয়ে: %s %1$dদিন %2$dঘন্টা %3$dমিনিট %1$dঘন্টা %2$dমিনিট %d মিনিট @@ -22,7 +22,7 @@ খুঁজুন ডাউনলোডসমূহ সেটিংস - %1$s এপি %2$d + %1$s পর্ব %2$d পর্ব %d মুক্তির তারিখ শেয়ার ব্রাউজারে খুলুন @@ -131,8 +131,8 @@ স্টোরেজ এর অনুমতি অনুপস্থিত। দয়া করে আবার চেষ্টা করুন। তথ্য ট্রেইলার প্রদর্শন করুন - কিটসু হতে পোস্টারসমূহ প্রদর্শন করুন - স্বয়ংক্রিয়ভাবে প্লাগিন এর হালনাগাদ + Kitsu থেকে পোস্টারসমূহ প্রদর্শন করুন + স্বয়ংক্রিয় প্লাগইন আপডেট স্বয়ংক্রিয়ভাবে প্লাগিনসমুহের ডাউনলোড হালনাগাদ ও ব্যাকআপ অ্যাপ এর হালনাগাদ দেখান @@ -148,9 +148,9 @@ সামনে বা পিছনের দিকে যেতে ডান বা বাম দিকে দুবার আলতো চাপুন ফাইল ডিলিট মান ডিফল্ট এ রিসেট করুন - স্টার্টআপে নতুন আপডেটের জন্য স্বয়ংক্রিয়ভাবে অনুসন্ধান করুন + স্টার্টআপে নতুন আপডেটের জন্য স্বয়ংক্রিয়ভাবে অনুসন্ধান করুন যোগ। সিনেমা - এনিমে এর ফিলার পর্ব দেখায় + অ্যানিমে ফিলার পর্ব প্রদর্শন টিভি সিরিজ লিংক পাওয়া যায়নি চা খাওয়ানো হয়েছে @@ -174,7 +174,7 @@ ডিলিট শুরু কার্টুন - কিছু ফোন নতুন প্যাকেজ ইনস্টলার সাপোর্ট করে না। যদি আপডেটগুলি ইনস্টল না হয় তবে পুরোনো পদ্ধতি ব্যবহার করে দেখুন। + Some devices do not support the new package installer. Try the legacy option if updates do not install। সাবটাইটেল নেই এই প্রোভাইডার ক্রোমকাস্ট সাপোর্ট করে না উন্নত অনুসন্ধান @@ -204,7 +204,7 @@ আমাদের তৈরি Anime দেখার অ্যাপ্লিকেশন 18+ এনিমে - অনুসন্ধান ফলাফলে নির্বাচিত ভিডিও কুয়ালিটি লুকান + অনুসন্ধানের ফলে নির্বাচিত ভিডিওর মান লুকান অ্যাপ লাইভস্ট্রিম APK ইনস্টলার @@ -354,4 +354,37 @@ স্পিচ রিকগনিশন উপলব্ধ নেই কথা বলা শুরু করুন… ডাউনলোড তালিকা + সম্পূর্ণ সিরিজ প্লে করুন + এই ভিডিওটি একটি টরেন্ট, অর্থাৎ আপনার ভিডিও অ্যাক্টিভিটি ট্র্যাক করা সম্ভব।\nএগোনোর আগে টরেন্টিং সম্পর্কে ভালোভাবে বুঝে নিন। + ডিলিট করার জন্য আইটেম সিলেক্ট করুন + বর্তমানে কোনো ডাউনলোড নেই। + বর্তমানে কোনো ডাউনলোড কিউতে নেই। + অফলাইনে দেখার জন্য উপলব্ধ + সব সিলেক্ট করুন + সব ডিসিলেক্ট করুন + লোকাল ভিডিও খুলুন + অতিরিক্ত ব্রাইটনেস + ডিসপ্লে ব্রাইটনেস ১০০% ছাড়িয়ে গেলে ব্রাইটনেস ফিল্টার চালু করুন + সার্চ সাজেশন + টাইপ করার সময় সার্চ সাজেশন দেখান + সাজেশন মুছে ফেলুন + প্লেয়ার মেটাডেটা ওভারলে প্রদর্শন + কাস্ট প্যানেল প্রদর্শন + প্রাক-মুক্তি সংস্করণ ইনস্টলেশন + প্রাক-মুক্তি সংস্করণ ইতোমধ্যে ইনস্টল রয়েছে। + প্রাক-মুক্তি সংস্করণ ইনস্টল করতে সমস্যা হয়েছে। + (%1$d | %2$s) মুছুন + সাবধান + আপনি কি নিম্নলিখিত আইটেমগুলো স্থায়ীভাবে মুছতে চান?\n\n%s + ফাইল মুছুন + আপনি কি নিশ্চিত যে আপনি %1$s-এ নিচের এপিসোডগুলো স্থায়ীভাবে মুছে ফেলতে চান?\n\n%2$s + আপনি নিচের সিরিজগুলোর সব এপিসোডও স্থায়ীভাবে মুছে ফেলবেন:\n\n%s + আপনি কি নিশ্চিত যে আপনি নিচের সিরিজটির সব এপিসোড স্থায়ীভাবে মুছে ফেলতে চান?\n\n%s + সঙ্গীত + অডিও বই + মিডিয়া + অডিও + পডকাস্ট + ভিডিও + এনকোডিং ত্রুটি diff --git a/app/src/main/res/values-b+nl/strings.xml b/app/src/main/res/values-b+nl/strings.xml index 8a2424326..feec84263 100644 --- a/app/src/main/res/values-b+nl/strings.xml +++ b/app/src/main/res/values-b+nl/strings.xml @@ -682,4 +682,17 @@ Zet torrent aan in Instellingen/Providers/Media voorkeur Herstart de app en accepteer de Stream Torrent pop-up om verder te gaan. Handmatige Update Knop + Open de app met Vingerafdruk, Face ID, PIN, Pattern en Wachtwoord. + Seekbar preview + Zet preview thumbnail aan op seekbar + Software decodering + Software decodering staat de mogelijkheid toe om de speler videobestanden af te laten spelen op jouw apparaat, maar het kan een haperige of onstabiele kijkervaring zorgen op hoge resolutie. + Volume heeft de 100% overschreden + Update Plugins + Geen plugins zijn geupdate. + Speler meldingen + De speler melding om de kijkervaring van de achtergrond de besturen + Online + Hoeveel verschillende items kunnen in parallel gedownload worden + Parallel downloads diff --git a/app/src/main/res/values-b+pl/strings.xml b/app/src/main/res/values-b+pl/strings.xml index 35367a1b2..b69146c92 100644 --- a/app/src/main/res/values-b+pl/strings.xml +++ b/app/src/main/res/values-b+pl/strings.xml @@ -742,4 +742,13 @@ Wideo Zapowiedź Na żywo + Ustawienia profilu + Ukryj źródła z negatywnym priorytetem + Ukryj źródła z błędami + + %d ukryty odnośnik + %d ukryte odnośniki + %d ukrytych odnośników + %d ukrytych odnośników + diff --git a/app/src/main/res/values-b+sq/strings.xml b/app/src/main/res/values-b+sq/strings.xml index a13f87bfa..4716d757f 100644 --- a/app/src/main/res/values-b+sq/strings.xml +++ b/app/src/main/res/values-b+sq/strings.xml @@ -735,4 +735,11 @@ %d shkarkime në radhë Live + Cilësimet e profil-it + Fshih burimet me prioritet negativ + Fshih burimet me error-e + + %d link i fshehur + %d linqe të fshehura + diff --git a/app/src/main/res/values-b+vi/strings.xml b/app/src/main/res/values-b+vi/strings.xml index 3b2b3876f..53679c05c 100644 --- a/app/src/main/res/values-b+vi/strings.xml +++ b/app/src/main/res/values-b+vi/strings.xml @@ -36,7 +36,7 @@ Tập tiếp theo Thể loại Chia sẻ - Mở bằng trình duyệt + Phát bằng Trình duyệt Bỏ tải Đang tải… Đang xem @@ -255,13 +255,13 @@ Chất lượng xem ưu tiên (WiFi) Số ký tự tối đa tiêu đề trình phát Hiển thị thông tin trình phát - Kích thước bộ nhớ đệm video - Thời lượng bộ nhớ đệm + Kích thước bộ đệm video + Thời lượng bộ đệm video Bộ nhớ đệm video trên thiết bị Xoá bộ nhớ đệm hình ảnh và video Sẽ gây lỗi nếu đặt quá cao trên thiết bị có bộ nhớ thấp như Android TV. - Sẽ gây lỗi nếu đặt quá cao trên máy có dung lượng lưu trữ thấp như Android TV. - DNS over HTTPS + Sẽ gây lỗi nếu đặt quá cao trên thiết bị có dung lượng lưu trữ thấp như Android TV. + DNS qua HTTPS Rất hữu ích để bỏ chặn ISP Bản sao trang web Xoá trang web diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 14780da6b..4a918231f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -791,5 +791,4 @@ %d downloads queued Live - diff --git a/fastlane/metadata/android/bn/changelogs/2.txt b/fastlane/metadata/android/bn/changelogs/2.txt new file mode 100644 index 000000000..91032cb8a --- /dev/null +++ b/fastlane/metadata/android/bn/changelogs/2.txt @@ -0,0 +1 @@ +- চেঞ্জলগ যোগ করা হয়েছে! diff --git a/fastlane/metadata/android/bn/full_description.txt b/fastlane/metadata/android/bn/full_description.txt new file mode 100644 index 000000000..e1866f54f --- /dev/null +++ b/fastlane/metadata/android/bn/full_description.txt @@ -0,0 +1,7 @@ +CloudStream-3 দিয়ে সিনেমা, টিভি-সিরিজ ও অ্যানিমে স্ট্রিম ও ডাউনলোড করা যায়। + +অ্যাপে কোনো বিজ্ঞাপন ও অ্যানালিটিক্স নেই এবং +এটি একাধিক ট্রেলার ও মুভি সাইট সাপোর্ট করে, আরও আছে যেমন— +বুকমার্ক +সাবটাইটেল ডাউনলোড +ক্রোমকাস্ট সাপোর্ট diff --git a/fastlane/metadata/android/bn/short_description.txt b/fastlane/metadata/android/bn/short_description.txt new file mode 100644 index 000000000..3d62da485 --- /dev/null +++ b/fastlane/metadata/android/bn/short_description.txt @@ -0,0 +1 @@ +সিনেমা, টিভি সিরিজ ও অ্যানিমে স্ট্রিম ও ডাউনলোড করুন। diff --git a/fastlane/metadata/android/bn/title.txt b/fastlane/metadata/android/bn/title.txt new file mode 100644 index 000000000..dde89d58f --- /dev/null +++ b/fastlane/metadata/android/bn/title.txt @@ -0,0 +1 @@ +CloudStream From caf0db3dc13bd75a496d4a94152bb8f22f8fdb1e Mon Sep 17 00:00:00 2001 From: Jelle Thijs Date: Sat, 1 Aug 2026 03:14:02 +0200 Subject: [PATCH 38/38] Feat: Add optional clock to TV player --- .../cloudstream3/ui/home/HomeFragment.kt | 28 +++++++++++++++++++ .../ui/player/FullScreenPlayer.kt | 3 +- .../cloudstream3/ui/player/GeneratorPlayer.kt | 8 ++++++ .../cloudstream3/ui/settings/SettingsUI.kt | 2 ++ .../res/drawable/ic_baseline_clock_24.xml | 9 ++++++ app/src/main/res/layout/fragment_home.xml | 6 ++++ app/src/main/res/layout/fragment_home_tv.xml | 17 +++++++++++ .../main/res/layout/player_custom_layout.xml | 9 ++++++ .../res/layout/player_custom_layout_tv.xml | 16 +++++++++++ .../main/res/layout/trailer_custom_layout.xml | 6 ++++ .../res/values/donottranslate-strings.xml | 1 + app/src/main/res/values/strings.xml | 2 ++ app/src/main/res/xml/settings_ui.xml | 6 ++++ 13 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 app/src/main/res/drawable/ic_baseline_clock_24.xml diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt index 604695859..0e52baff6 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt @@ -701,6 +701,23 @@ class HomeFragment : BaseFragment( homeViewModel.queryTextSubmit("") } + // Load value for toggling Tv layout real time clock. Hide by default at startup + // set visibility first, to apply a scroll effect later + context?.let { + if (isLayout(TV)) { + val settingsManager = PreferenceManager.getDefaultSharedPreferences(it) + val toggleClock = + settingsManager.getBoolean( + getString(R.string.tv_layout_clock_key), + false + ) + binding.homeClock.isVisible = toggleClock + } else { + binding.homeClock.isVisible = false + } + } + + homeMasterRecycler.addOnScrollListener(object : RecyclerView.OnScrollListener() { override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { if (isLayout(PHONE)) { @@ -740,6 +757,17 @@ class HomeFragment : BaseFragment( view.getLocationInWindow(rect) scrollParent.isVisible = true scrollParent.translationY = rect[1].toFloat() - 60.toPx + + // Move the TV layout real time clock out of the way too + // We check if we have the correct layout and if the clock is enabled + if(isLayout(TV) && binding.homeClock.isVisible) { + val scrollParent = binding.homeClock + + val rect = IntArray(2) + view.getLocationInWindow(rect) + scrollParent.isVisible = true + scrollParent.translationY = rect[1].toFloat() - 60.toPx + } } } super.onScrolled(recyclerView, dx, dy) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/FullScreenPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/FullScreenPlayer.kt index 20c37dcf4..d90b6043f 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/FullScreenPlayer.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/FullScreenPlayer.kt @@ -307,6 +307,7 @@ open class FullScreenPlayer : AbstractPlayerFragment( playerVideoTitleRez, playerVideoInfo, playerGoBackHolder, + playerVideoClock, ).forEach { it.animateY(titleMove) } @@ -772,7 +773,7 @@ open class FullScreenPlayer : AbstractPlayerFragment( val showPlayerEpisodes = !isGone && isThereEpisodes() playerEpisodesButtonRoot.isVisible = showPlayerEpisodes playerEpisodesButton.isVisible = showPlayerEpisodes - playerVideoTitleHolder.isGone = togglePlayerTitleGone + playerVideoTitleHolder.isGone = togglePlayerTitleGone || playerVideoTitle.text.isBlank() playerVideoTitleRez.isGone = isGone || playerVideoTitleRez.text.isBlank() playerEpisodeFiller.isGone = isGone playerCenterMenu.isGone = isGone diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt index 683a322c4..4495a5602 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt @@ -2271,6 +2271,14 @@ class GeneratorPlayer : FullScreenPlayer() { fromTagToEnglishLanguageName(it)?.lowercase() ?: return@mapNotNull null } ?: listOf() } + + // Set up TV clock visibility + if (isLayout(TV)) { + val showTvClock = settingsManager.getBoolean(ctx.getString(R.string.tv_layout_clock_key), false) + playerBinding?.playerVideoClock?.isVisible = showTvClock + } else { + playerBinding?.playerVideoClock?.isVisible = false + } } unwrapBundle(savedInstanceState) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUI.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUI.kt index f4c522bf9..5f86a3a5a 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUI.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUI.kt @@ -228,6 +228,8 @@ class SettingsUI : BasePreferenceFragmentCompat() { return@setOnPreferenceClickListener true } + getPref(R.string.tv_layout_clock_key)?.hideOn(PHONE or EMULATOR) + getPref(R.string.confirm_exit_key)?.setOnPreferenceClickListener { val prefNames = resources.getStringArray(R.array.confirm_exit) val prefValues = resources.getIntArray(R.array.confirm_exit_values) diff --git a/app/src/main/res/drawable/ic_baseline_clock_24.xml b/app/src/main/res/drawable/ic_baseline_clock_24.xml new file mode 100644 index 000000000..3436c6208 --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_clock_24.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml index 99a764dee..8d78c01b3 100644 --- a/app/src/main/res/layout/fragment_home.xml +++ b/app/src/main/res/layout/fragment_home.xml @@ -227,6 +227,12 @@ tools:listitem="@layout/homepage_parent" tools:visibility="gone" /> + + + + + + + diff --git a/app/src/main/res/layout/player_custom_layout_tv.xml b/app/src/main/res/layout/player_custom_layout_tv.xml index 88e4cd984..c6445d80f 100644 --- a/app/src/main/res/layout/player_custom_layout_tv.xml +++ b/app/src/main/res/layout/player_custom_layout_tv.xml @@ -352,6 +352,22 @@ android:layout_marginEnd="32dp" android:orientation="vertical"> + + + + opensubtitles_key subdl_key animeskip_key + tv_layout_clock_key pref_category_security_key pref_category_gestures_key diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4a918231f..eed41ce81 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -374,6 +374,8 @@ Looks Features General + Show real time clock + Show a real time clock at the top of the screen. Applies to the homepage and player Random Button Show random button on Homepage and Library Extension languages diff --git a/app/src/main/res/xml/settings_ui.xml b/app/src/main/res/xml/settings_ui.xml index 1b516ffa3..774724f99 100644 --- a/app/src/main/res/xml/settings_ui.xml +++ b/app/src/main/res/xml/settings_ui.xml @@ -89,6 +89,12 @@ android:icon="@drawable/metadata_overlay_icon" android:key="@string/show_player_metadata_key" android:title="@string/show_player_metadata_overlay" /> +