From 54f74509a8d25f3e9a96aeb57f63d32c777cbbb0 Mon Sep 17 00:00:00 2001 From: KingLucius Date: Thu, 6 Jul 2023 14:50:25 +0300 Subject: [PATCH] Just Keep SoraStream --- .vscode/settings.json | 3 - Anichi/build.gradle.kts | 42 -- Anichi/src/main/AndroidManifest.xml | 2 - Anichi/src/main/kotlin/com/hexated/Anichi.kt | 609 ------------------ .../main/kotlin/com/hexated/AnichiPlugin.kt | 14 - Anifreakz/build.gradle.kts | 27 - Anifreakz/src/main/AndroidManifest.xml | 2 - .../src/main/kotlin/com/hexated/Anifreakz.kt | 193 ------ .../kotlin/com/hexated/AnifreakzPlugin.kt | 14 - Anilibria/build.gradle.kts | 27 - Anilibria/icon.png | Bin 48946 -> 0 bytes Anilibria/src/main/AndroidManifest.xml | 2 - .../src/main/kotlin/com/hexated/Anilibria.kt | 197 ------ .../kotlin/com/hexated/AnilibriaPlugin.kt | 14 - AnimeIndoProvider/build.gradle.kts | 27 - .../src/main/AndroidManifest.xml | 2 - .../kotlin/com/hexated/AnimeIndoProvider.kt | 169 ----- .../com/hexated/AnimeIndoProviderPlugin.kt | 14 - AnimeSailProvider/build.gradle.kts | 27 - .../src/main/AndroidManifest.xml | 2 - .../kotlin/com/hexated/AnimeSailProvider.kt | 196 ------ .../com/hexated/AnimeSailProviderPlugin.kt | 14 - Animixplay/build.gradle.kts | 27 - Animixplay/src/main/AndroidManifest.xml | 2 - .../src/main/kotlin/com/hexated/Animixplay.kt | 446 ------------- .../kotlin/com/hexated/AnimixplayPlugin.kt | 14 - .../main/kotlin/com/hexated/GogoExtractor.kt | 166 ----- Aniworld/build.gradle.kts | 27 - Aniworld/src/main/AndroidManifest.xml | 2 - .../src/main/kotlin/com/hexated/Aniworld.kt | 233 ------- .../main/kotlin/com/hexated/AniworldPlugin.kt | 17 - Anizm/build.gradle.kts | 27 - Anizm/src/main/AndroidManifest.xml | 2 - Anizm/src/main/kotlin/com/hexated/Anizm.kt | 195 ------ .../main/kotlin/com/hexated/AnizmPlugin.kt | 14 - Anroll/build.gradle.kts | 26 - Anroll/src/main/AndroidManifest.xml | 2 - Anroll/src/main/kotlin/com/hexated/Anroll.kt | 241 ------- .../main/kotlin/com/hexated/AnrollPlugin.kt | 14 - Dizikorea/build.gradle.kts | 23 - Dizikorea/src/main/AndroidManifest.xml | 2 - .../src/main/kotlin/com/hexated/Dizikorea.kt | 203 ------ .../kotlin/com/hexated/DizikoreaPlugin.kt | 14 - DramaSerial/build.gradle.kts | 25 - DramaSerial/src/main/AndroidManifest.xml | 2 - .../main/kotlin/com/hexated/DramaSerial.kt | 124 ---- .../kotlin/com/hexated/DramaSerialPlugin.kt | 15 - .../src/main/kotlin/com/hexated/Lkctwoone.kt | 8 - DramaidProvider/build.gradle.kts | 26 - DramaidProvider/src/main/AndroidManifest.xml | 2 - .../kotlin/com/hexated/DramaidProvider.kt | 225 ------- .../com/hexated/DramaidProviderPlugin.kt | 15 - Dubbindo/build.gradle.kts | 27 - Dubbindo/src/main/AndroidManifest.xml | 2 - .../src/main/kotlin/com/hexated/Dubbindo.kt | 136 ---- .../main/kotlin/com/hexated/DubbindoPlugin.kt | 14 - DubokuProvider/build.gradle.kts | 27 - DubokuProvider/src/main/AndroidManifest.xml | 2 - .../main/kotlin/com/hexated/DubokuProvider.kt | 133 ---- .../com/hexated/DubokuProviderPlugin.kt | 14 - GomunimeProvider/build.gradle.kts | 27 - GomunimeProvider/src/main/AndroidManifest.xml | 2 - .../kotlin/com/hexated/GomunimeProvider.kt | 238 ------- .../com/hexated/GomunimeProviderPlugin.kt | 14 - Gomunimeis/build.gradle.kts | 27 - Gomunimeis/src/main/AndroidManifest.xml | 2 - .../src/main/kotlin/com/hexated/Gomunimeis.kt | 163 ----- .../kotlin/com/hexated/GomunimeisPlugin.kt | 14 - GoodPorn/build.gradle.kts | 25 - GoodPorn/src/main/AndroidManifest.xml | 2 - .../src/main/kotlin/com/hexated/GoodPorn.kt | 126 ---- .../main/kotlin/com/hexated/GoodPornPlugin.kt | 13 - HDrezkaProvider/build.gradle.kts | 28 - HDrezkaProvider/src/main/AndroidManifest.xml | 2 - .../kotlin/com/hexated/HDrezkaProvider.kt | 395 ------------ .../com/hexated/HDrezkaProviderPlugin.kt | 14 - Hdfilmcehennemi/build.gradle.kts | 26 - Hdfilmcehennemi/src/main/AndroidManifest.xml | 2 - .../kotlin/com/hexated/Hdfilmcehennemi.kt | 240 ------- .../com/hexated/HdfilmcehennemiPlugin.kt | 14 - Hentaiheaven/build.gradle.kts | 25 - Hentaiheaven/src/main/AndroidManifest.xml | 2 - .../main/kotlin/com/hexated/Hentaiheaven.kt | 166 ----- .../kotlin/com/hexated/HentaiheavenPlugin.kt | 13 - IMoviehd/build.gradle.kts | 26 - IMoviehd/src/main/AndroidManifest.xml | 2 - .../src/main/kotlin/com/hexated/IMoviehd.kt | 169 ----- .../main/kotlin/com/hexated/IMoviehdPlugin.kt | 14 - IdlixProvider/build.gradle.kts | 28 - IdlixProvider/src/main/AndroidManifest.xml | 2 - .../main/kotlin/com/hexated/IdlixProvider.kt | 228 ------- .../kotlin/com/hexated/IdlixProviderPlugin.kt | 14 - Kickassanime/build.gradle.kts | 27 - Kickassanime/src/main/AndroidManifest.xml | 2 - .../main/kotlin/com/hexated/GogoExtractor.kt | 166 ----- .../main/kotlin/com/hexated/Kickassanime.kt | 382 ----------- .../com/hexated/KickassanimeExtractor.kt | 201 ------ .../kotlin/com/hexated/KickassanimePlugin.kt | 14 - .../kotlin/com/hexated/KickassanimeUtils.kt | 90 --- Kissasian/build.gradle.kts | 23 - Kissasian/src/main/AndroidManifest.xml | 2 - .../src/main/kotlin/com/hexated/Kissasian.kt | 140 ---- .../kotlin/com/hexated/KissasianPlugin.kt | 14 - KisskhProvider/build.gradle.kts | 28 - KisskhProvider/src/main/AndroidManifest.xml | 2 - .../main/kotlin/com/hexated/KisskhProvider.kt | 217 ------- .../com/hexated/KisskhProviderPlugin.kt | 14 - KuramanimeProvider/build.gradle.kts | 27 - .../src/main/AndroidManifest.xml | 2 - .../src/main/kotlin/com/hexated/Extractors.kt | 14 - .../kotlin/com/hexated/KuramanimeProvider.kt | 211 ------ .../com/hexated/KuramanimeProviderPlugin.kt | 16 - KuronimeProvider/build.gradle.kts | 27 - KuronimeProvider/src/main/AndroidManifest.xml | 2 - .../kotlin/com/hexated/KuronimeProvider.kt | 238 ------- .../com/hexated/KuronimeProviderPlugin.kt | 14 - LayarKacaProvider/build.gradle.kts | 27 - .../src/main/AndroidManifest.xml | 2 - .../kotlin/com/hexated/LayarKacaProvider.kt | 208 ------ .../com/hexated/LayarKacaProviderPlugin.kt | 14 - Loklok/build.gradle.kts | 29 - Loklok/src/main/AndroidManifest.xml | 2 - Loklok/src/main/kotlin/com/hexated/Loklok.kt | 428 ------------ .../main/kotlin/com/hexated/LoklokPlugin.kt | 14 - Minioppai/build.gradle.kts | 25 - Minioppai/src/main/AndroidManifest.xml | 2 - .../src/main/kotlin/com/hexated/Minioppai.kt | 243 ------- .../kotlin/com/hexated/MinioppaiPlugin.kt | 13 - Moviehab/build.gradle.kts | 26 - Moviehab/src/main/AndroidManifest.xml | 2 - .../src/main/kotlin/com/hexated/Moviehab.kt | 205 ------ .../main/kotlin/com/hexated/MoviehabPlugin.kt | 14 - Movierulzhd/build.gradle.kts | 26 - Movierulzhd/src/main/AndroidManifest.xml | 2 - .../src/main/kotlin/com/hexated/Extractors.kt | 148 ----- .../main/kotlin/com/hexated/Movierulzhd.kt | 273 -------- .../kotlin/com/hexated/MovierulzhdPlugin.kt | 17 - .../main/kotlin/com/hexated/RabbitStream.kt | 302 --------- MultiplexProvider/build.gradle.kts | 27 - .../src/main/AndroidManifest.xml | 2 - .../kotlin/com/hexated/MultiplexProvider.kt | 188 ------ .../com/hexated/MultiplexProviderPlugin.kt | 14 - NeonimeProvider/build.gradle.kts | 28 - NeonimeProvider/src/main/AndroidManifest.xml | 2 - .../kotlin/com/hexated/NeonimeProvider.kt | 186 ------ .../com/hexated/NeonimeProviderPlugin.kt | 14 - Ngefilm/build.gradle.kts | 27 - Ngefilm/src/main/AndroidManifest.xml | 2 - .../src/main/kotlin/com/hexated/Extractors.kt | 149 ----- .../src/main/kotlin/com/hexated/Ngefilm.kt | 176 ----- .../main/kotlin/com/hexated/NgefilmPlugin.kt | 21 - NontonAnimeIDProvider/build.gradle.kts | 27 - .../src/main/AndroidManifest.xml | 2 - .../com/hexated/NontonAnimeIDProvider.kt | 252 -------- .../hexated/NontonAnimeIDProviderPlugin.kt | 15 - OnetwothreeTv/build.gradle.kts | 25 - OnetwothreeTv/src/main/AndroidManifest.xml | 2 - .../main/kotlin/com/hexated/OnetwothreeTv.kt | 195 ------ .../kotlin/com/hexated/OnetwothreeTvPlugin.kt | 14 - OploverzProvider/build.gradle.kts | 27 - OploverzProvider/src/main/AndroidManifest.xml | 2 - .../kotlin/com/hexated/OploverzProvider.kt | 251 -------- .../com/hexated/OploverzProviderPlugin.kt | 16 - OtakudesuProvider/build.gradle.kts | 27 - .../src/main/AndroidManifest.xml | 2 - .../kotlin/com/hexated/OtakudesuProvider.kt | 222 ------- .../com/hexated/OtakudesuProviderPlugin.kt | 15 - Paradisehill/build.gradle.kts | 25 - Paradisehill/src/main/AndroidManifest.xml | 2 - .../main/kotlin/com/hexated/Paradisehill.kt | 124 ---- .../kotlin/com/hexated/ParadisehillPlugin.kt | 13 - PhimmoichillProvider/build.gradle.kts | 28 - .../src/main/AndroidManifest.xml | 2 - .../com/hexated/PhimmoichillProvider.kt | 192 ------ .../com/hexated/PhimmoichillProviderPlugin.kt | 14 - README.md | 43 -- RebahinProvider/build.gradle.kts | 29 - RebahinProvider/src/main/AndroidManifest.xml | 2 - .../src/main/kotlin/com/hexated/Kitanonton.kt | 32 - .../kotlin/com/hexated/RebahinProvider.kt | 315 --------- .../com/hexated/RebahinProviderPlugin.kt | 15 - Samehadaku/build.gradle.kts | 27 - Samehadaku/src/main/AndroidManifest.xml | 2 - .../src/main/kotlin/com/hexated/Extractors.kt | 43 -- .../src/main/kotlin/com/hexated/Samehadaku.kt | 234 ------- .../kotlin/com/hexated/SamehadakuPlugin.kt | 16 - StremioX/build.gradle.kts | 26 - StremioX/icon.png | Bin 52736 -> 0 bytes StremioX/src/main/AndroidManifest.xml | 2 - .../src/main/kotlin/com/hexated/StremioC.kt | 361 ----------- .../src/main/kotlin/com/hexated/StremioX.kt | 456 ------------- .../main/kotlin/com/hexated/StremioXPlugin.kt | 15 - .../main/kotlin/com/hexated/SubsExtractors.kt | 115 ---- StremioX/src/main/kotlin/com/hexated/Utils.kt | 74 --- TimefourTv/build.gradle.kts | 25 - TimefourTv/src/main/AndroidManifest.xml | 2 - .../src/main/kotlin/com/hexated/TimefourTv.kt | 214 ------ .../kotlin/com/hexated/TimefourTvExtractor.kt | 129 ---- .../kotlin/com/hexated/TimefourTvPlugin.kt | 14 - TocanimeProvider/build.gradle.kts | 27 - TocanimeProvider/src/main/AndroidManifest.xml | 2 - .../kotlin/com/hexated/TocanimeProvider.kt | 178 ----- .../com/hexated/TocanimeProviderPlugin.kt | 14 - Topdocumentaryfilms/build.gradle.kts | 23 - .../src/main/AndroidManifest.xml | 2 - .../kotlin/com/hexated/Topdocumentaryfilms.kt | 101 --- .../com/hexated/TopdocumentaryfilmsPlugin.kt | 14 - Turkish/build.gradle.kts | 26 - Turkish/src/main/AndroidManifest.xml | 2 - .../src/main/kotlin/com/hexated/Lajkema.kt | 8 - .../src/main/kotlin/com/hexated/Turkish.kt | 139 ---- .../main/kotlin/com/hexated/TurkishPlugin.kt | 15 - Tvtwofourseven/build.gradle.kts | 22 - Tvtwofourseven/src/main/AndroidManifest.xml | 2 - .../main/kotlin/com/hexated/Tvtwofourseven.kt | 114 ---- .../com/hexated/TvtwofoursevenPlugin.kt | 14 - UakinoProvider/build.gradle.kts | 27 - UakinoProvider/src/main/AndroidManifest.xml | 2 - .../main/kotlin/com/hexated/UakinoProvider.kt | 181 ------ .../com/hexated/UakinoProviderPlugin.kt | 14 - UseeTv/build.gradle.kts | 25 - UseeTv/src/main/AndroidManifest.xml | 2 - UseeTv/src/main/kotlin/com/hexated/UseeTv.kt | 93 --- .../main/kotlin/com/hexated/UseeTvPlugin.kt | 14 - WcofunProvider/build.gradle.kts | 27 - WcofunProvider/src/main/AndroidManifest.xml | 2 - .../main/kotlin/com/hexated/WcofunProvider.kt | 172 ----- .../com/hexated/WcofunProviderPlugin.kt | 14 - Xcineio/build.gradle.kts | 26 - Xcineio/src/main/AndroidManifest.xml | 2 - Xcineio/src/main/kotlin/com/hexated/XCine.kt | 211 ------ .../kotlin/com/hexated/XCineExtractors.kt | 63 -- .../main/kotlin/com/hexated/XCinePlugin.kt | 20 - Xcinetop/build.gradle.kts | 26 - Xcinetop/icon.png | Bin 112374 -> 0 bytes Xcinetop/src/main/AndroidManifest.xml | 2 - .../src/main/kotlin/com/hexated/Xcinetop.kt | 191 ------ .../main/kotlin/com/hexated/XcinetopPlugin.kt | 14 - YomoviesProvider/build.gradle.kts | 26 - YomoviesProvider/src/main/AndroidManifest.xml | 2 - .../kotlin/com/hexated/YomoviesProvider.kt | 175 ----- .../com/hexated/YomoviesProviderPlugin.kt | 14 - YugenAnime/build.gradle.kts | 27 - YugenAnime/src/main/AndroidManifest.xml | 2 - .../src/main/kotlin/com/hexated/YugenAnime.kt | 234 ------- .../kotlin/com/hexated/YugenAnimePlugin.kt | 14 - 246 files changed, 16895 deletions(-) delete mode 100644 .vscode/settings.json delete mode 100644 Anichi/build.gradle.kts delete mode 100644 Anichi/src/main/AndroidManifest.xml delete mode 100644 Anichi/src/main/kotlin/com/hexated/Anichi.kt delete mode 100644 Anichi/src/main/kotlin/com/hexated/AnichiPlugin.kt delete mode 100644 Anifreakz/build.gradle.kts delete mode 100644 Anifreakz/src/main/AndroidManifest.xml delete mode 100644 Anifreakz/src/main/kotlin/com/hexated/Anifreakz.kt delete mode 100644 Anifreakz/src/main/kotlin/com/hexated/AnifreakzPlugin.kt delete mode 100644 Anilibria/build.gradle.kts delete mode 100644 Anilibria/icon.png delete mode 100644 Anilibria/src/main/AndroidManifest.xml delete mode 100644 Anilibria/src/main/kotlin/com/hexated/Anilibria.kt delete mode 100644 Anilibria/src/main/kotlin/com/hexated/AnilibriaPlugin.kt delete mode 100644 AnimeIndoProvider/build.gradle.kts delete mode 100644 AnimeIndoProvider/src/main/AndroidManifest.xml delete mode 100644 AnimeIndoProvider/src/main/kotlin/com/hexated/AnimeIndoProvider.kt delete mode 100644 AnimeIndoProvider/src/main/kotlin/com/hexated/AnimeIndoProviderPlugin.kt delete mode 100644 AnimeSailProvider/build.gradle.kts delete mode 100644 AnimeSailProvider/src/main/AndroidManifest.xml delete mode 100644 AnimeSailProvider/src/main/kotlin/com/hexated/AnimeSailProvider.kt delete mode 100644 AnimeSailProvider/src/main/kotlin/com/hexated/AnimeSailProviderPlugin.kt delete mode 100644 Animixplay/build.gradle.kts delete mode 100644 Animixplay/src/main/AndroidManifest.xml delete mode 100644 Animixplay/src/main/kotlin/com/hexated/Animixplay.kt delete mode 100644 Animixplay/src/main/kotlin/com/hexated/AnimixplayPlugin.kt delete mode 100644 Animixplay/src/main/kotlin/com/hexated/GogoExtractor.kt delete mode 100644 Aniworld/build.gradle.kts delete mode 100644 Aniworld/src/main/AndroidManifest.xml delete mode 100644 Aniworld/src/main/kotlin/com/hexated/Aniworld.kt delete mode 100644 Aniworld/src/main/kotlin/com/hexated/AniworldPlugin.kt delete mode 100644 Anizm/build.gradle.kts delete mode 100644 Anizm/src/main/AndroidManifest.xml delete mode 100644 Anizm/src/main/kotlin/com/hexated/Anizm.kt delete mode 100644 Anizm/src/main/kotlin/com/hexated/AnizmPlugin.kt delete mode 100644 Anroll/build.gradle.kts delete mode 100644 Anroll/src/main/AndroidManifest.xml delete mode 100644 Anroll/src/main/kotlin/com/hexated/Anroll.kt delete mode 100644 Anroll/src/main/kotlin/com/hexated/AnrollPlugin.kt delete mode 100644 Dizikorea/build.gradle.kts delete mode 100644 Dizikorea/src/main/AndroidManifest.xml delete mode 100644 Dizikorea/src/main/kotlin/com/hexated/Dizikorea.kt delete mode 100644 Dizikorea/src/main/kotlin/com/hexated/DizikoreaPlugin.kt delete mode 100644 DramaSerial/build.gradle.kts delete mode 100644 DramaSerial/src/main/AndroidManifest.xml delete mode 100644 DramaSerial/src/main/kotlin/com/hexated/DramaSerial.kt delete mode 100644 DramaSerial/src/main/kotlin/com/hexated/DramaSerialPlugin.kt delete mode 100644 DramaSerial/src/main/kotlin/com/hexated/Lkctwoone.kt delete mode 100644 DramaidProvider/build.gradle.kts delete mode 100644 DramaidProvider/src/main/AndroidManifest.xml delete mode 100644 DramaidProvider/src/main/kotlin/com/hexated/DramaidProvider.kt delete mode 100644 DramaidProvider/src/main/kotlin/com/hexated/DramaidProviderPlugin.kt delete mode 100644 Dubbindo/build.gradle.kts delete mode 100644 Dubbindo/src/main/AndroidManifest.xml delete mode 100644 Dubbindo/src/main/kotlin/com/hexated/Dubbindo.kt delete mode 100644 Dubbindo/src/main/kotlin/com/hexated/DubbindoPlugin.kt delete mode 100644 DubokuProvider/build.gradle.kts delete mode 100644 DubokuProvider/src/main/AndroidManifest.xml delete mode 100644 DubokuProvider/src/main/kotlin/com/hexated/DubokuProvider.kt delete mode 100644 DubokuProvider/src/main/kotlin/com/hexated/DubokuProviderPlugin.kt delete mode 100644 GomunimeProvider/build.gradle.kts delete mode 100644 GomunimeProvider/src/main/AndroidManifest.xml delete mode 100644 GomunimeProvider/src/main/kotlin/com/hexated/GomunimeProvider.kt delete mode 100644 GomunimeProvider/src/main/kotlin/com/hexated/GomunimeProviderPlugin.kt delete mode 100644 Gomunimeis/build.gradle.kts delete mode 100644 Gomunimeis/src/main/AndroidManifest.xml delete mode 100644 Gomunimeis/src/main/kotlin/com/hexated/Gomunimeis.kt delete mode 100644 Gomunimeis/src/main/kotlin/com/hexated/GomunimeisPlugin.kt delete mode 100644 GoodPorn/build.gradle.kts delete mode 100644 GoodPorn/src/main/AndroidManifest.xml delete mode 100644 GoodPorn/src/main/kotlin/com/hexated/GoodPorn.kt delete mode 100644 GoodPorn/src/main/kotlin/com/hexated/GoodPornPlugin.kt delete mode 100644 HDrezkaProvider/build.gradle.kts delete mode 100644 HDrezkaProvider/src/main/AndroidManifest.xml delete mode 100644 HDrezkaProvider/src/main/kotlin/com/hexated/HDrezkaProvider.kt delete mode 100644 HDrezkaProvider/src/main/kotlin/com/hexated/HDrezkaProviderPlugin.kt delete mode 100644 Hdfilmcehennemi/build.gradle.kts delete mode 100644 Hdfilmcehennemi/src/main/AndroidManifest.xml delete mode 100644 Hdfilmcehennemi/src/main/kotlin/com/hexated/Hdfilmcehennemi.kt delete mode 100644 Hdfilmcehennemi/src/main/kotlin/com/hexated/HdfilmcehennemiPlugin.kt delete mode 100644 Hentaiheaven/build.gradle.kts delete mode 100644 Hentaiheaven/src/main/AndroidManifest.xml delete mode 100644 Hentaiheaven/src/main/kotlin/com/hexated/Hentaiheaven.kt delete mode 100644 Hentaiheaven/src/main/kotlin/com/hexated/HentaiheavenPlugin.kt delete mode 100644 IMoviehd/build.gradle.kts delete mode 100644 IMoviehd/src/main/AndroidManifest.xml delete mode 100644 IMoviehd/src/main/kotlin/com/hexated/IMoviehd.kt delete mode 100644 IMoviehd/src/main/kotlin/com/hexated/IMoviehdPlugin.kt delete mode 100644 IdlixProvider/build.gradle.kts delete mode 100644 IdlixProvider/src/main/AndroidManifest.xml delete mode 100644 IdlixProvider/src/main/kotlin/com/hexated/IdlixProvider.kt delete mode 100644 IdlixProvider/src/main/kotlin/com/hexated/IdlixProviderPlugin.kt delete mode 100644 Kickassanime/build.gradle.kts delete mode 100644 Kickassanime/src/main/AndroidManifest.xml delete mode 100644 Kickassanime/src/main/kotlin/com/hexated/GogoExtractor.kt delete mode 100644 Kickassanime/src/main/kotlin/com/hexated/Kickassanime.kt delete mode 100644 Kickassanime/src/main/kotlin/com/hexated/KickassanimeExtractor.kt delete mode 100644 Kickassanime/src/main/kotlin/com/hexated/KickassanimePlugin.kt delete mode 100644 Kickassanime/src/main/kotlin/com/hexated/KickassanimeUtils.kt delete mode 100644 Kissasian/build.gradle.kts delete mode 100644 Kissasian/src/main/AndroidManifest.xml delete mode 100644 Kissasian/src/main/kotlin/com/hexated/Kissasian.kt delete mode 100644 Kissasian/src/main/kotlin/com/hexated/KissasianPlugin.kt delete mode 100644 KisskhProvider/build.gradle.kts delete mode 100644 KisskhProvider/src/main/AndroidManifest.xml delete mode 100644 KisskhProvider/src/main/kotlin/com/hexated/KisskhProvider.kt delete mode 100644 KisskhProvider/src/main/kotlin/com/hexated/KisskhProviderPlugin.kt delete mode 100644 KuramanimeProvider/build.gradle.kts delete mode 100644 KuramanimeProvider/src/main/AndroidManifest.xml delete mode 100644 KuramanimeProvider/src/main/kotlin/com/hexated/Extractors.kt delete mode 100644 KuramanimeProvider/src/main/kotlin/com/hexated/KuramanimeProvider.kt delete mode 100644 KuramanimeProvider/src/main/kotlin/com/hexated/KuramanimeProviderPlugin.kt delete mode 100644 KuronimeProvider/build.gradle.kts delete mode 100644 KuronimeProvider/src/main/AndroidManifest.xml delete mode 100644 KuronimeProvider/src/main/kotlin/com/hexated/KuronimeProvider.kt delete mode 100644 KuronimeProvider/src/main/kotlin/com/hexated/KuronimeProviderPlugin.kt delete mode 100644 LayarKacaProvider/build.gradle.kts delete mode 100644 LayarKacaProvider/src/main/AndroidManifest.xml delete mode 100644 LayarKacaProvider/src/main/kotlin/com/hexated/LayarKacaProvider.kt delete mode 100644 LayarKacaProvider/src/main/kotlin/com/hexated/LayarKacaProviderPlugin.kt delete mode 100644 Loklok/build.gradle.kts delete mode 100644 Loklok/src/main/AndroidManifest.xml delete mode 100644 Loklok/src/main/kotlin/com/hexated/Loklok.kt delete mode 100644 Loklok/src/main/kotlin/com/hexated/LoklokPlugin.kt delete mode 100644 Minioppai/build.gradle.kts delete mode 100644 Minioppai/src/main/AndroidManifest.xml delete mode 100644 Minioppai/src/main/kotlin/com/hexated/Minioppai.kt delete mode 100644 Minioppai/src/main/kotlin/com/hexated/MinioppaiPlugin.kt delete mode 100644 Moviehab/build.gradle.kts delete mode 100644 Moviehab/src/main/AndroidManifest.xml delete mode 100644 Moviehab/src/main/kotlin/com/hexated/Moviehab.kt delete mode 100644 Moviehab/src/main/kotlin/com/hexated/MoviehabPlugin.kt delete mode 100644 Movierulzhd/build.gradle.kts delete mode 100644 Movierulzhd/src/main/AndroidManifest.xml delete mode 100644 Movierulzhd/src/main/kotlin/com/hexated/Extractors.kt delete mode 100644 Movierulzhd/src/main/kotlin/com/hexated/Movierulzhd.kt delete mode 100644 Movierulzhd/src/main/kotlin/com/hexated/MovierulzhdPlugin.kt delete mode 100644 Movierulzhd/src/main/kotlin/com/hexated/RabbitStream.kt delete mode 100644 MultiplexProvider/build.gradle.kts delete mode 100644 MultiplexProvider/src/main/AndroidManifest.xml delete mode 100644 MultiplexProvider/src/main/kotlin/com/hexated/MultiplexProvider.kt delete mode 100644 MultiplexProvider/src/main/kotlin/com/hexated/MultiplexProviderPlugin.kt delete mode 100644 NeonimeProvider/build.gradle.kts delete mode 100644 NeonimeProvider/src/main/AndroidManifest.xml delete mode 100644 NeonimeProvider/src/main/kotlin/com/hexated/NeonimeProvider.kt delete mode 100644 NeonimeProvider/src/main/kotlin/com/hexated/NeonimeProviderPlugin.kt delete mode 100644 Ngefilm/build.gradle.kts delete mode 100644 Ngefilm/src/main/AndroidManifest.xml delete mode 100644 Ngefilm/src/main/kotlin/com/hexated/Extractors.kt delete mode 100644 Ngefilm/src/main/kotlin/com/hexated/Ngefilm.kt delete mode 100644 Ngefilm/src/main/kotlin/com/hexated/NgefilmPlugin.kt delete mode 100644 NontonAnimeIDProvider/build.gradle.kts delete mode 100644 NontonAnimeIDProvider/src/main/AndroidManifest.xml delete mode 100644 NontonAnimeIDProvider/src/main/kotlin/com/hexated/NontonAnimeIDProvider.kt delete mode 100644 NontonAnimeIDProvider/src/main/kotlin/com/hexated/NontonAnimeIDProviderPlugin.kt delete mode 100644 OnetwothreeTv/build.gradle.kts delete mode 100644 OnetwothreeTv/src/main/AndroidManifest.xml delete mode 100644 OnetwothreeTv/src/main/kotlin/com/hexated/OnetwothreeTv.kt delete mode 100644 OnetwothreeTv/src/main/kotlin/com/hexated/OnetwothreeTvPlugin.kt delete mode 100644 OploverzProvider/build.gradle.kts delete mode 100644 OploverzProvider/src/main/AndroidManifest.xml delete mode 100644 OploverzProvider/src/main/kotlin/com/hexated/OploverzProvider.kt delete mode 100644 OploverzProvider/src/main/kotlin/com/hexated/OploverzProviderPlugin.kt delete mode 100644 OtakudesuProvider/build.gradle.kts delete mode 100644 OtakudesuProvider/src/main/AndroidManifest.xml delete mode 100644 OtakudesuProvider/src/main/kotlin/com/hexated/OtakudesuProvider.kt delete mode 100644 OtakudesuProvider/src/main/kotlin/com/hexated/OtakudesuProviderPlugin.kt delete mode 100644 Paradisehill/build.gradle.kts delete mode 100644 Paradisehill/src/main/AndroidManifest.xml delete mode 100644 Paradisehill/src/main/kotlin/com/hexated/Paradisehill.kt delete mode 100644 Paradisehill/src/main/kotlin/com/hexated/ParadisehillPlugin.kt delete mode 100644 PhimmoichillProvider/build.gradle.kts delete mode 100644 PhimmoichillProvider/src/main/AndroidManifest.xml delete mode 100644 PhimmoichillProvider/src/main/kotlin/com/hexated/PhimmoichillProvider.kt delete mode 100644 PhimmoichillProvider/src/main/kotlin/com/hexated/PhimmoichillProviderPlugin.kt delete mode 100644 README.md delete mode 100644 RebahinProvider/build.gradle.kts delete mode 100644 RebahinProvider/src/main/AndroidManifest.xml delete mode 100644 RebahinProvider/src/main/kotlin/com/hexated/Kitanonton.kt delete mode 100644 RebahinProvider/src/main/kotlin/com/hexated/RebahinProvider.kt delete mode 100644 RebahinProvider/src/main/kotlin/com/hexated/RebahinProviderPlugin.kt delete mode 100644 Samehadaku/build.gradle.kts delete mode 100644 Samehadaku/src/main/AndroidManifest.xml delete mode 100644 Samehadaku/src/main/kotlin/com/hexated/Extractors.kt delete mode 100644 Samehadaku/src/main/kotlin/com/hexated/Samehadaku.kt delete mode 100644 Samehadaku/src/main/kotlin/com/hexated/SamehadakuPlugin.kt delete mode 100644 StremioX/build.gradle.kts delete mode 100644 StremioX/icon.png delete mode 100644 StremioX/src/main/AndroidManifest.xml delete mode 100644 StremioX/src/main/kotlin/com/hexated/StremioC.kt delete mode 100644 StremioX/src/main/kotlin/com/hexated/StremioX.kt delete mode 100644 StremioX/src/main/kotlin/com/hexated/StremioXPlugin.kt delete mode 100644 StremioX/src/main/kotlin/com/hexated/SubsExtractors.kt delete mode 100644 StremioX/src/main/kotlin/com/hexated/Utils.kt delete mode 100644 TimefourTv/build.gradle.kts delete mode 100644 TimefourTv/src/main/AndroidManifest.xml delete mode 100644 TimefourTv/src/main/kotlin/com/hexated/TimefourTv.kt delete mode 100644 TimefourTv/src/main/kotlin/com/hexated/TimefourTvExtractor.kt delete mode 100644 TimefourTv/src/main/kotlin/com/hexated/TimefourTvPlugin.kt delete mode 100644 TocanimeProvider/build.gradle.kts delete mode 100644 TocanimeProvider/src/main/AndroidManifest.xml delete mode 100644 TocanimeProvider/src/main/kotlin/com/hexated/TocanimeProvider.kt delete mode 100644 TocanimeProvider/src/main/kotlin/com/hexated/TocanimeProviderPlugin.kt delete mode 100644 Topdocumentaryfilms/build.gradle.kts delete mode 100644 Topdocumentaryfilms/src/main/AndroidManifest.xml delete mode 100644 Topdocumentaryfilms/src/main/kotlin/com/hexated/Topdocumentaryfilms.kt delete mode 100644 Topdocumentaryfilms/src/main/kotlin/com/hexated/TopdocumentaryfilmsPlugin.kt delete mode 100644 Turkish/build.gradle.kts delete mode 100644 Turkish/src/main/AndroidManifest.xml delete mode 100644 Turkish/src/main/kotlin/com/hexated/Lajkema.kt delete mode 100644 Turkish/src/main/kotlin/com/hexated/Turkish.kt delete mode 100644 Turkish/src/main/kotlin/com/hexated/TurkishPlugin.kt delete mode 100644 Tvtwofourseven/build.gradle.kts delete mode 100644 Tvtwofourseven/src/main/AndroidManifest.xml delete mode 100644 Tvtwofourseven/src/main/kotlin/com/hexated/Tvtwofourseven.kt delete mode 100644 Tvtwofourseven/src/main/kotlin/com/hexated/TvtwofoursevenPlugin.kt delete mode 100644 UakinoProvider/build.gradle.kts delete mode 100644 UakinoProvider/src/main/AndroidManifest.xml delete mode 100644 UakinoProvider/src/main/kotlin/com/hexated/UakinoProvider.kt delete mode 100644 UakinoProvider/src/main/kotlin/com/hexated/UakinoProviderPlugin.kt delete mode 100644 UseeTv/build.gradle.kts delete mode 100644 UseeTv/src/main/AndroidManifest.xml delete mode 100644 UseeTv/src/main/kotlin/com/hexated/UseeTv.kt delete mode 100644 UseeTv/src/main/kotlin/com/hexated/UseeTvPlugin.kt delete mode 100644 WcofunProvider/build.gradle.kts delete mode 100644 WcofunProvider/src/main/AndroidManifest.xml delete mode 100644 WcofunProvider/src/main/kotlin/com/hexated/WcofunProvider.kt delete mode 100644 WcofunProvider/src/main/kotlin/com/hexated/WcofunProviderPlugin.kt delete mode 100644 Xcineio/build.gradle.kts delete mode 100644 Xcineio/src/main/AndroidManifest.xml delete mode 100644 Xcineio/src/main/kotlin/com/hexated/XCine.kt delete mode 100644 Xcineio/src/main/kotlin/com/hexated/XCineExtractors.kt delete mode 100644 Xcineio/src/main/kotlin/com/hexated/XCinePlugin.kt delete mode 100644 Xcinetop/build.gradle.kts delete mode 100644 Xcinetop/icon.png delete mode 100644 Xcinetop/src/main/AndroidManifest.xml delete mode 100644 Xcinetop/src/main/kotlin/com/hexated/Xcinetop.kt delete mode 100644 Xcinetop/src/main/kotlin/com/hexated/XcinetopPlugin.kt delete mode 100644 YomoviesProvider/build.gradle.kts delete mode 100644 YomoviesProvider/src/main/AndroidManifest.xml delete mode 100644 YomoviesProvider/src/main/kotlin/com/hexated/YomoviesProvider.kt delete mode 100644 YomoviesProvider/src/main/kotlin/com/hexated/YomoviesProviderPlugin.kt delete mode 100644 YugenAnime/build.gradle.kts delete mode 100644 YugenAnime/src/main/AndroidManifest.xml delete mode 100644 YugenAnime/src/main/kotlin/com/hexated/YugenAnime.kt delete mode 100644 YugenAnime/src/main/kotlin/com/hexated/YugenAnimePlugin.kt diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index c5f3f6b9..00000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "java.configuration.updateBuildConfiguration": "interactive" -} \ No newline at end of file diff --git a/Anichi/build.gradle.kts b/Anichi/build.gradle.kts deleted file mode 100644 index 24eea240..00000000 --- a/Anichi/build.gradle.kts +++ /dev/null @@ -1,42 +0,0 @@ -import org.jetbrains.kotlin.konan.properties.Properties - -// use an integer for version numbers -version = 4 - -android { - defaultConfig { - val properties = Properties() - properties.load(project.rootProject.file("local.properties").inputStream()) - - buildConfigField("String", "ANICHI_API", "\"${properties.getProperty("ANICHI_API")}\"") - buildConfigField("String", "ANICHI_SERVER", "\"${properties.getProperty("ANICHI_SERVER")}\"") - buildConfigField("String", "ANICHI_ENDPOINT", "\"${properties.getProperty("ANICHI_ENDPOINT")}\"") - buildConfigField("String", "ANICHI_APP", "\"${properties.getProperty("ANICHI_APP")}\"") - - - } -} - -cloudstream { - language = "en" - // All of these properties are optional, you can safely remove them - - // description = "Lorem Ipsum" - authors = listOf("Hexated") - - /** - * Status int as the following: - * 0: Down - * 1: Ok - * 2: Slow - * 3: Beta only - * */ - status = 1 // will be 3 if unspecified - tvTypes = listOf( - "AnimeMovie", - "Anime", - "OVA", - ) - - iconUrl = "https://media.discordapp.net/attachments/1059306855865782282/1123970193274712096/Anichi.png" -} \ No newline at end of file diff --git a/Anichi/src/main/AndroidManifest.xml b/Anichi/src/main/AndroidManifest.xml deleted file mode 100644 index 874740e3..00000000 --- a/Anichi/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/Anichi/src/main/kotlin/com/hexated/Anichi.kt b/Anichi/src/main/kotlin/com/hexated/Anichi.kt deleted file mode 100644 index 02d0fe98..00000000 --- a/Anichi/src/main/kotlin/com/hexated/Anichi.kt +++ /dev/null @@ -1,609 +0,0 @@ -package com.hexated - -import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.APIHolder.getTracker -import com.lagradost.cloudstream3.LoadResponse.Companion.addActors -import com.lagradost.cloudstream3.LoadResponse.Companion.addAniListId -import com.lagradost.cloudstream3.LoadResponse.Companion.addMalId -import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer -import com.lagradost.cloudstream3.extractors.helper.GogoHelper -import com.lagradost.cloudstream3.mvvm.safeApiCall -import com.lagradost.cloudstream3.utils.* -import com.lagradost.cloudstream3.utils.AppUtils.parseJson -import com.lagradost.cloudstream3.utils.AppUtils.toJson -import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson -import com.lagradost.nicehttp.RequestBodyTypes -import okhttp3.MediaType.Companion.toMediaTypeOrNull -import okhttp3.RequestBody.Companion.toRequestBody -import java.net.URI - -class Anichi : MainAPI() { - override var name = "Anichi" - override val instantLinkLoading = true - override val hasQuickSearch = false - override val hasMainPage = true - - private fun getStatus(t: String): ShowStatus { - return when (t) { - "Finished" -> ShowStatus.Completed - "Releasing" -> ShowStatus.Ongoing - else -> ShowStatus.Completed - } - } - - private fun getType(t: String?): TvType { - return when { - t.equals("OVA", true) || t.equals("Special") -> TvType.OVA - t.equals("Movie", true) -> TvType.AnimeMovie - else -> TvType.Anime - } - } - - override val supportedTypes = setOf(TvType.Anime, TvType.AnimeMovie) - - private val popularTitle = "Popular" - private val animeRecentTitle = "Latest Anime" - private val donghuaRecentTitle = "Latest Donghua" - private val movieTitle = "Movie" - - override val mainPage = mainPageOf( - """$apiUrl?variables={"search":{"sortBy":"Latest_Update","allowAdult":${settingsForProvider.enableAdult},"allowUnknown":false},"limit":26,"page":%d,"translationType":"sub","countryOrigin":"JP"}&extensions={"persistedQuery":{"version":1,"sha256Hash":"$mainHash"}}""" to animeRecentTitle, - """$apiUrl?variables={"search":{"sortBy":"Latest_Update","allowAdult":${settingsForProvider.enableAdult},"allowUnknown":false},"limit":26,"page":%d,"translationType":"sub","countryOrigin":"CN"}&extensions={"persistedQuery":{"version":1,"sha256Hash":"$mainHash"}}""" to donghuaRecentTitle, - """$apiUrl?variables={"type":"anime","size":30,"dateRange":1,"page":%d,"allowAdult":${settingsForProvider.enableAdult},"allowUnknown":false}&extensions={"persistedQuery":{"version":1,"sha256Hash":"$popularHash"}}""" to popularTitle, - """$apiUrl?variables={"search":{"slug":"movie-anime","format":"anime","tagType":"upcoming","name":"Trending Movies"}}&extensions={"persistedQuery":{"version":1,"sha256Hash":"$slugHash"}}""" to movieTitle - ) - - override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse { - - val url = request.data.format(page) - val res = app.get(url, headers = headers).parsedSafe()?.data - val query = res?.shows ?: res?.queryPopular ?: res?.queryListForTag - val card = if(request.name == popularTitle) query?.recommendations?.map { it.anyCard } else query?.edges - val home = card?.filter { - // filtering in case there is an anime with 0 episodes available on the site. - !(it?.availableEpisodes?.raw == 0 && it.availableEpisodes.sub == 0 && it.availableEpisodes.dub == 0) - }?.mapNotNull { media -> - media?.toSearchResponse() - } ?: emptyList() - return newHomePageResponse( - list = HomePageList( - name = request.name, - list = home, - ), - hasNext = request.name != movieTitle - ) - } - - private fun Edges.toSearchResponse(): AnimeSearchResponse? { - - return newAnimeSearchResponse( - name ?: englishName ?: nativeName ?: "", - Id ?: return null, - fix = false - ) { - this.posterUrl = thumbnail - this.year = airedStart?.year - this.otherName = englishName - addDub(availableEpisodes?.dub) - addSub(availableEpisodes?.sub) - } - } - - override suspend fun search(query: String): List? { - - val link = - """$apiUrl?variables={"search":{"allowAdult":false,"allowUnknown":false,"query":"$query"},"limit":26,"page":1,"translationType":"sub","countryOrigin":"ALL"}&extensions={"persistedQuery":{"version":1,"sha256Hash":"$mainHash"}}""" - val res = app.get( - link, - headers = headers - ).text.takeUnless { it.contains("PERSISTED_QUERY_NOT_FOUND") } - // Retries - ?: app.get( - link, - headers = headers - ).text.takeUnless { it.contains("PERSISTED_QUERY_NOT_FOUND") } - ?: return emptyList() - - val response = parseJson(res) - - val results = response.data?.shows?.edges?.filter { - // filtering in case there is an anime with 0 episodes available on the site. - !(it.availableEpisodes?.raw == 0 && it.availableEpisodes.sub == 0 && it.availableEpisodes.dub == 0) - } - - return results?.map { - newAnimeSearchResponse(it.name ?: "", "${it.Id}", fix = false) { - this.posterUrl = it.thumbnail - this.year = it.airedStart?.year - this.otherName = it.englishName - addDub(it.availableEpisodes?.dub) - addSub(it.availableEpisodes?.sub) - } - } - } - - override suspend fun load(url: String): LoadResponse? { - - val id = url.substringAfterLast("/") - // lazy to format - val body = """ - { - "query": " query(\n ${'$'}_id: String!\n ) {\n show(\n _id: ${'$'}_id\n ) {\n _id\n name\n description\n thumbnail\n thumbnails\n lastEpisodeInfo\n lastEpisodeDate \n type\n genres\n score\n status\n season\n altNames \n averageScore\n rating\n episodeCount\n episodeDuration\n broadcastInterval\n banner\n airedEnd\n airedStart \n studios\n characters\n availableEpisodesDetail\n availableEpisodes\n prevideos\n nameOnlyString\n relatedShows\n relatedMangas\n musics\n isAdult\n \n tags\n countryOfOrigin\n\n pageStatus{\n _id\n notes\n pageId\n showId\n \n # ranks:[Object]\n views\n likesCount\n commentCount\n dislikesCount\n reviewCount\n userScoreCount\n userScoreTotalValue\n userScoreAverValue\n viewers{\n firstViewers{\n viewCount\n lastWatchedDate\n user{\n _id\n displayName\n picture\n # description\n hideMe\n # createdAt\n # badges\n brief\n }\n \n }\n recViewers{\n viewCount\n lastWatchedDate\n user{\n _id\n displayName\n picture\n # description\n hideMe\n # createdAt\n # badges\n brief\n }\n \n }\n }\n\n }\n }\n }", - "extensions": "{\"persistedQuery\":{\"version\":1,\"sha256Hash\":\"$detailHash\"}}", - "variables": "{\"_id\":\"$id\"}" - } - """.trimIndent().trim().toRequestBody(RequestBodyTypes.JSON.toMediaTypeOrNull()) - - val res = app.post(apiUrl, requestBody = body, headers = headers) - val showData = res.parsedSafe()?.data?.show ?: return null - - val title = showData.name - val description = showData.description - val poster = showData.thumbnail - val type = getType(showData.type ?: "") - - val episodes = showData.availableEpisodesDetail.let { - if (it == null) return@let Pair(null, null) - if (showData.Id == null) return@let Pair(null, null) - Pair( - it.getEpisode("sub", showData.Id), - it.getEpisode("dub", showData.Id), - ) - } - - val characters = showData.characters?.map { - val role = when (it.role) { - "Main" -> ActorRole.Main - "Supporting" -> ActorRole.Supporting - "Background" -> ActorRole.Background - else -> null - } - val name = it.name?.full ?: it.name?.native ?: "" - val image = it.image?.large ?: it.image?.medium - Pair(Actor(name, image), role) - } - - val names = showData.altNames?.plus(title)?.filterNotNull() ?: emptyList() - val trackers = getTracker(names, TrackerType.getTypes(type), showData.airedStart?.year) - - return newAnimeLoadResponse(title ?: "", url, TvType.Anime) { - engName = showData.altNames?.firstOrNull() - posterUrl = trackers?.image ?: poster - backgroundPosterUrl = trackers?.cover ?: showData.banner - rating = showData.averageScore?.times(100) - tags = showData.genres - year = showData.airedStart?.year - duration = showData.episodeDuration?.div(60_000) - addTrailer(showData.prevideos.filter { it.isNotBlank() } - .map { "https://www.youtube.com/watch?v=$it" }) - - addEpisodes(DubStatus.Subbed, episodes.first) - addEpisodes(DubStatus.Dubbed, episodes.second) - addActors(characters) - //this.recommendations = recommendations - - showStatus = getStatus(showData.status.toString()) - addMalId(trackers?.malId) - addAniListId(trackers?.aniId?.toIntOrNull()) - plot = description?.replace(Regex("""<(.*?)>"""), "") - } - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - - val loadData = parseJson(data) - - val apiUrl = - """$apiUrl?variables={"showId":"${loadData.hash}","translationType":"${loadData.dubStatus}","episodeString":"${loadData.episode}"}&extensions={"persistedQuery":{"version":1,"sha256Hash":"$serverHash"}}""" - val apiResponse = app.get(apiUrl, headers = headers).parsed() - - apiResponse.data?.episode?.sourceUrls?.apmap { source -> - safeApiCall { - val link = fixSourceUrls(source.sourceUrl ?: return@safeApiCall, source.sourceName) ?: return@safeApiCall - if (URI(link).isAbsolute || link.startsWith("//")) { - val fixedLink = if (link.startsWith("//")) "https:$link" else link - val host = link.getHost() - - when { - fixedLink.contains(Regex("(?i)playtaku|gogo")) || source.sourceName == "Vid-mp4" -> { - invokeGogo(fixedLink, subtitleCallback, callback) - } - embedIsBlacklisted(fixedLink) -> { - loadExtractor(fixedLink, subtitleCallback, callback) - } - URI(fixedLink).path.contains(".m3u") -> { - getM3u8Qualities(fixedLink, serverUrl, host).forEach(callback) - } - else -> { - callback( - ExtractorLink( - name, - host, - fixedLink, - serverUrl, - Qualities.P1080.value, - false - ) - ) - } - } - } else { - val fixedLink = link.fixUrlPath() - val links = app.get(fixedLink).parsedSafe()?.links - ?: emptyList() - links.forEach { server -> - val host = server.link.getHost() - when { - source.sourceName == "Default" -> { - if (server.resolutionStr == "SUB" || server.resolutionStr == "Alt vo_SUB") { - getM3u8Qualities( - server.link, - "https://static.crunchyroll.com/", - host, - ).forEach(callback) - } - } - server.hls != null && server.hls -> { - getM3u8Qualities( - server.link, - "$apiEndPoint/player?uri=" + (if (URI(server.link).host.isNotEmpty()) server.link else apiEndPoint + URI( - server.link - ).path), - host - ).forEach(callback) - } - else -> { - callback( - ExtractorLink( - host, - host, - server.link, - "$apiEndPoint/player?uri=" + (if (URI(server.link).host.isNotEmpty()) server.link else apiEndPoint + URI( - server.link - ).path), - server.resolutionStr.removeSuffix("p").toIntOrNull() - ?: Qualities.P1080.value, - false, - isDash = server.resolutionStr == "Dash 1" - ) - ) - } - } - } - } - } - } - return true - } - - private val embedBlackList = listOf( - "https://mp4upload.com/", - "https://streamsb.net/", - "https://dood.to/", - "https://videobin.co/", - "https://ok.ru", - "https://streamlare.com", - "streaming.php", - ) - - private fun embedIsBlacklisted(url: String): Boolean { - embedBlackList.forEach { - if (it.javaClass.name == "kotlin.text.Regex") { - if ((it as Regex).matches(url)) { - return true - } - } else { - if (url.contains(it)) { - return true - } - } - } - return false - } - - private fun AvailableEpisodesDetail.getEpisode( - lang: String, - id: String - ): List { - val meta = if (lang == "sub") this.sub else this.dub - return meta.map { eps -> - Episode( - AnichiLoadData(id, lang, eps).toJson(), - "Ep $eps" - ) - }.reversed() - } - - private suspend fun getM3u8Qualities( - m3u8Link: String, - referer: String, - qualityName: String, - ): List { - return M3u8Helper.generateM3u8( - this.name, - m3u8Link, - referer, - name = qualityName - ) - } - - private suspend fun invokeGogo( - link: String, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ) { - val iframe = app.get(link) - val iframeDoc = iframe.document - argamap({ - iframeDoc.select(".list-server-items > .linkserver") - .forEach { element -> - val status = element.attr("data-status") ?: return@forEach - if (status != "1") return@forEach - val extractorData = element.attr("data-video") ?: return@forEach - loadExtractor(extractorData, iframe.url, subtitleCallback, callback) - } - }, { - val iv = "3134003223491201" - val secretKey = "37911490979715163134003223491201" - val secretDecryptKey = "54674138327930866480207815084989" - GogoHelper.extractVidstream( - iframe.url, - "Gogoanime", - callback, - iv, - secretKey, - secretDecryptKey, - isUsingAdaptiveKeys = false, - isUsingAdaptiveData = true, - iframeDocument = iframeDoc - ) - }) - } - - private fun String.getHost(): String { - return fixTitle(URI(this).host.substringBeforeLast(".").substringAfterLast(".")) - } - - private fun String.fixUrlPath() : String { - return if(this.contains(".json?")) apiEndPoint + this else apiEndPoint + URI(this).path + ".json?" + URI(this).query - } - - private fun fixSourceUrls(url: String, source: String?) : String? { - return if(source == "Ak" || url.contains("/player/vitemb")) { - tryParseJson(base64Decode(url.substringAfter("=")))?.idUrl - } else { - url.replace(" ", "%20") - } - } - - companion object { - private const val apiUrl = BuildConfig.ANICHI_API - private const val serverUrl = BuildConfig.ANICHI_SERVER - private const val apiEndPoint = BuildConfig.ANICHI_ENDPOINT - - private const val mainHash = "e42a4466d984b2c0a2cecae5dd13aa68867f634b16ee0f17b380047d14482406" - private const val popularHash = "31a117653812a2547fd981632e8c99fa8bf8a75c4ef1a77a1567ef1741a7ab9c" - private const val slugHash = "bf603205eb2533ca21d0324a11f623854d62ed838a27e1b3fcfb712ab98b03f4" - private const val detailHash = "bb263f91e5bdd048c1c978f324613aeccdfe2cbc694a419466a31edb58c0cc0b" - private const val serverHash = "5e7e17cdd0166af5a2d8f43133d9ce3ce9253d1fdb5160a0cfd515564f98d061" - - private val headers = mapOf( - "app-version" to "android_c-247", - "from-app" to BuildConfig.ANICHI_APP, - "platformstr" to "android_c", - ) - } - - data class AnichiLoadData( - val hash: String, - val dubStatus: String, - val episode: String - ) - - data class AkIframe( - @JsonProperty("idUrl") val idUrl: String? = null, - ) - - data class Stream( - @JsonProperty("format") val format: String? = null, - @JsonProperty("audio_lang") val audio_lang: String? = null, - @JsonProperty("hardsub_lang") val hardsub_lang: String? = null, - @JsonProperty("url") val url: String? = null, - ) - - data class PortData( - @JsonProperty("streams") val streams: ArrayList? = arrayListOf(), - ) - - data class Subtitles( - @JsonProperty("lang") val lang: String?, - @JsonProperty("label") val label: String?, - @JsonProperty("src") val src: String?, - ) - - data class Links( - @JsonProperty("link") val link: String, - @JsonProperty("hls") val hls: Boolean?, - @JsonProperty("resolutionStr") val resolutionStr: String, - @JsonProperty("src") val src: String?, - @JsonProperty("portData") val portData: PortData? = null, - @JsonProperty("subtitles") val subtitles: ArrayList? = arrayListOf(), - ) - - data class AnichiVideoApiResponse( - @JsonProperty("links") val links: List - ) - - data class Data( - @JsonProperty("shows") val shows: Shows? = null, - @JsonProperty("queryListForTag") val queryListForTag: Shows? = null, - @JsonProperty("queryPopular") val queryPopular: Shows? = null, - ) - - data class Shows( - @JsonProperty("edges") val edges: List? = arrayListOf(), - @JsonProperty("recommendations") val recommendations: List? = arrayListOf(), - ) - - data class EdgesCard( - @JsonProperty("anyCard") val anyCard: Edges? = null, - ) - - data class CharacterImage( - @JsonProperty("large") val large: String?, - @JsonProperty("medium") val medium: String? - ) - - data class CharacterName( - @JsonProperty("full") val full: String?, - @JsonProperty("native") val native: String? - ) - - data class Characters( - @JsonProperty("image") val image: CharacterImage?, - @JsonProperty("role") val role: String?, - @JsonProperty("name") val name: CharacterName?, - ) - - data class Edges( - @JsonProperty("_id") val Id: String?, - @JsonProperty("name") val name: String?, - @JsonProperty("englishName") val englishName: String?, - @JsonProperty("nativeName") val nativeName: String?, - @JsonProperty("thumbnail") val thumbnail: String?, - @JsonProperty("type") val type: String?, - @JsonProperty("season") val season: Season?, - @JsonProperty("score") val score: Double?, - @JsonProperty("airedStart") val airedStart: AiredStart?, - @JsonProperty("availableEpisodes") val availableEpisodes: AvailableEpisodes?, - @JsonProperty("availableEpisodesDetail") val availableEpisodesDetail: AvailableEpisodesDetail?, - @JsonProperty("studios") val studios: List?, - @JsonProperty("genres") val genres: List?, - @JsonProperty("averageScore") val averageScore: Int?, - @JsonProperty("characters") val characters: List?, - @JsonProperty("altNames") val altNames: List?, - @JsonProperty("description") val description: String?, - @JsonProperty("status") val status: String?, - @JsonProperty("banner") val banner: String?, - @JsonProperty("episodeDuration") val episodeDuration: Int?, - @JsonProperty("prevideos") val prevideos: List = emptyList(), - ) - - data class AvailableEpisodes( - @JsonProperty("sub") val sub: Int, - @JsonProperty("dub") val dub: Int, - @JsonProperty("raw") val raw: Int - ) - - data class AiredStart( - @JsonProperty("year") val year: Int, - @JsonProperty("month") val month: Int, - @JsonProperty("date") val date: Int - ) - - data class Season( - @JsonProperty("quarter") val quarter: String, - @JsonProperty("year") val year: Int - ) - - data class AnichiQuery( - @JsonProperty("data") val data: Data? = null - ) - - data class Detail( - @JsonProperty("data") val data: DetailShow - ) - - data class DetailShow( - @JsonProperty("show") val show: Edges - ) - - data class AvailableEpisodesDetail( - @JsonProperty("sub") val sub: List, - @JsonProperty("dub") val dub: List, - @JsonProperty("raw") val raw: List - ) - - data class LinksQuery( - @JsonProperty("data") val data: LinkData? = LinkData() - ) - - data class LinkData( - @JsonProperty("episode") val episode: Episode? = Episode() - ) - - data class SourceUrls( - @JsonProperty("sourceUrl") val sourceUrl: String? = null, - @JsonProperty("priority") val priority: Int? = null, - @JsonProperty("sourceName") val sourceName: String? = null, - @JsonProperty("type") val type: String? = null, - @JsonProperty("className") val className: String? = null, - @JsonProperty("streamerId") val streamerId: String? = null - ) - - data class Episode( - @JsonProperty("sourceUrls") val sourceUrls: ArrayList = arrayListOf(), - ) - - data class Sub( - @JsonProperty("hour") val hour: Int? = null, - @JsonProperty("minute") val minute: Int? = null, - @JsonProperty("year") val year: Int? = null, - @JsonProperty("month") val month: Int? = null, - @JsonProperty("date") val date: Int? = null - ) - - data class LastEpisodeDate( - @JsonProperty("dub") val dub: Sub? = Sub(), - @JsonProperty("sub") val sub: Sub? = Sub(), - @JsonProperty("raw") val raw: Sub? = Sub() - ) - - data class AnyCard( - @JsonProperty("_id") val Id: String? = null, - @JsonProperty("name") val name: String? = null, - @JsonProperty("englishName") val englishName: String? = null, - @JsonProperty("nativeName") val nativeName: String? = null, - @JsonProperty("availableEpisodes") val availableEpisodes: AvailableEpisodes? = null, - @JsonProperty("score") val score: Double? = null, - @JsonProperty("lastEpisodeDate") val lastEpisodeDate: LastEpisodeDate? = LastEpisodeDate(), - @JsonProperty("thumbnail") val thumbnail: String? = null, - @JsonProperty("lastChapterDate") val lastChapterDate: String? = null, - @JsonProperty("availableChapters") val availableChapters: String? = null, - @JsonProperty("__typename") val _typename: String? = null - ) - - data class PageStatus( - @JsonProperty("_id") val Id: String? = null, - @JsonProperty("views") val views: String? = null, - @JsonProperty("showId") val showId: String? = null, - @JsonProperty("rangeViews") val rangeViews: String? = null, - @JsonProperty("isManga") val isManga: Boolean? = null, - @JsonProperty("__typename") val _typename: String? = null - ) - - - data class Recommendations( - @JsonProperty("anyCard") val anyCard: AnyCard? = null, - @JsonProperty("pageStatus") val pageStatus: PageStatus? = PageStatus(), - @JsonProperty("__typename") val _typename: String? = null - ) - - data class QueryPopular( - @JsonProperty("total") val total: Int? = null, - @JsonProperty("recommendations") val recommendations: ArrayList = arrayListOf(), - @JsonProperty("__typename") val _typename: String? = null - ) - - data class DataPopular( - @JsonProperty("queryPopular") val queryPopular: QueryPopular? = QueryPopular() - ) - - -} \ No newline at end of file diff --git a/Anichi/src/main/kotlin/com/hexated/AnichiPlugin.kt b/Anichi/src/main/kotlin/com/hexated/AnichiPlugin.kt deleted file mode 100644 index 6fbec8d3..00000000 --- a/Anichi/src/main/kotlin/com/hexated/AnichiPlugin.kt +++ /dev/null @@ -1,14 +0,0 @@ - -package com.hexated - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class AnichiPlugin: Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(Anichi()) - } -} \ No newline at end of file diff --git a/Anifreakz/build.gradle.kts b/Anifreakz/build.gradle.kts deleted file mode 100644 index 83b7a132..00000000 --- a/Anifreakz/build.gradle.kts +++ /dev/null @@ -1,27 +0,0 @@ -// use an integer for version numbers -version = 1 - - -cloudstream { - language = "de" - // All of these properties are optional, you can safely remove them - - // description = "Lorem Ipsum" - authors = listOf("Hexated") - - /** - * Status int as the following: - * 0: Down - * 1: Ok - * 2: Slow - * 3: Beta only - * */ - status = 0 // will be 3 if unspecified - tvTypes = listOf( - "AnimeMovie", - "Anime", - "OVA", - ) - - iconUrl = "https://www.google.com/s2/favicons?domain=anifreakz.com&sz=%size%" -} \ No newline at end of file diff --git a/Anifreakz/src/main/AndroidManifest.xml b/Anifreakz/src/main/AndroidManifest.xml deleted file mode 100644 index c98063f8..00000000 --- a/Anifreakz/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/Anifreakz/src/main/kotlin/com/hexated/Anifreakz.kt b/Anifreakz/src/main/kotlin/com/hexated/Anifreakz.kt deleted file mode 100644 index 32d9b7e4..00000000 --- a/Anifreakz/src/main/kotlin/com/hexated/Anifreakz.kt +++ /dev/null @@ -1,193 +0,0 @@ -package com.hexated - -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.Qualities -import com.lagradost.cloudstream3.utils.getAndUnpack -import org.jsoup.nodes.Element -import java.net.URLDecoder - -class Anifreakz : MainAPI() { - override var mainUrl = "https://anifreakz.com" - override var name = "Anifreakz" - override val hasMainPage = true - override var lang = "de" - override val hasDownloadSupport = true - - override val supportedTypes = setOf( - TvType.Anime, - TvType.AnimeMovie, - TvType.OVA - ) - - override val mainPage = mainPageOf( - "$mainUrl/series?filter=null&page=" to "Anime Neuste", - "$mainUrl/series?filter={\"sorting\":\"popular\"}&page=" to "Anime Angesagt", - "$mainUrl/series?filter={\"sorting\":\"released\"}&page=" to "Anime Veröffentlicht", - "$mainUrl/kalender" to "Anime Kalender", - ) - - override suspend fun getMainPage( - page: Int, - request: MainPageRequest - ): HomePageResponse { - val items = mutableListOf() - val nonPaged = request.name == "Anime Kalender" && page <= 1 - - if (!nonPaged) { - val document = app.get(request.data + page).document - val home = document.select("div.app-section div.col").mapNotNull { - it.toSearchResult() - } - items.add(HomePageList(request.name, home)) - } - - if (nonPaged) { - val document = app.get(request.data).document - document.select("div.app-content div.app-section").forEach { block -> - val header = block.selectFirst("div.app-heading div.text")?.ownText() ?: "" - val home = block.select("div.col").mapNotNull { - it.toSearchResult() - } - items.add(HomePageList(header, home)) - } - } - - return newHomePageResponse(items) - - } - - private fun Element.toSearchResult(): AnimeSearchResponse? { - val href = fixUrl(this.selectFirst("a")?.attr("href") ?: return null) - val title = this.selectFirst("a.list-title")?.text()?.trim() ?: return null - val posterUrl = fixUrlNull(this.selectFirst("div.media-cover")?.attr("data-src")) - - return newAnimeSearchResponse(title, href, TvType.Anime) { - this.posterUrl = posterUrl - } - } - - override suspend fun search(query: String): List { - val document = app.get("$mainUrl/search/$query").document - return document.select("div.app-section div.col").mapNotNull { - it.toSearchResult() - } - } - - private fun decode(input: String): String? = URLDecoder.decode(input, "utf-8") - - override suspend fun load(url: String): LoadResponse? { - val document = app.get(url).document - - val title = - document.selectFirst("div.pl-md-4 h1, div.caption-content h1")?.text() ?: return null - val poster = fixUrlNull(document.selectFirst("div.media.media-cover")?.attr("data-src")) - val tags = document.select("div.categories a").map { it.text() } - val rating = document.selectFirst("div.featured-box div:contains(IMDB) div.text")?.text() - .toRatingInt() - val year = - document.selectFirst("div.featured-box div:contains(Veröffentlicht) div.text")?.text() - ?.trim()?.toIntOrNull() - val type = if (document.select("div.episodes.tab-content") - .isNullOrEmpty() - ) TvType.Movie else TvType.TvSeries - val description = - document.select("div.detail-attr div.text-content, div.caption div:contains(Übersicht) div.text") - .text().trim() - val trailer = document.selectFirst("button:contains(Trailer)")?.attr("data-remote") - ?.substringAfter("?trailer=")?.let { - decode(it) - } - - return if (type == TvType.Movie) { - newMovieLoadResponse(title, url, type, url) { - posterUrl = poster - this.year = year - this.rating = rating - plot = description - this.tags = tags - addTrailer(trailer) - } - } else { - val subEpisodes = mutableListOf() - document.select("div.episodes.tab-content div[id*=season-]").forEach { season -> - val seasonNum = season.attr("id").substringAfterLast("-").toIntOrNull() - season.select("a").map { eps -> - val href = eps.attr("href") - val name = eps.select("div.name").text() - val episode = - eps.select("div.episode").text().substringBefore(".").toIntOrNull() - val episodes = Episode( - href, - name, - seasonNum, - episode - ) - subEpisodes.add(episodes) - } - } - newAnimeLoadResponse(title, url, type) { - posterUrl = poster - this.year = year - addEpisodes(DubStatus.Subbed, subEpisodes) - this.rating = rating - plot = description - this.tags = tags - addTrailer(trailer) - } - } - - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - val document = app.get(data).document - - val servers = mutableListOf>() - - document.select("div[aria-labelledby=videoSource] button").map { - servers.add(it.attr("data-embed") to it.text()) - } - - document.select("div.nav-player-select a").map { - servers.add(it.attr("data-embed") to it.select("span").text()) - } - - servers.distinctBy { it.first }.apmap { (id, source) -> - val iframe = app.post( - "$mainUrl/ajax/embed", - data = mapOf("id" to id, "captcha" to ""), - headers = mapOf( - "X-Requested-With" to "XMLHttpRequest" - ), - referer = data - ).document.select("iframe").attr("src") - val doc = app.get(iframe, referer = "$mainUrl/").document - val link = - unpackJs(doc)?.substringAfter("file:\"")?.substringBefore("\"") ?: return@apmap null - callback.invoke( - ExtractorLink( - source, - source, - link, - "https://filemoon.sx/", - Qualities.Unknown.value, - isM3u8 = link.contains(".m3u8") - ) - ) - } - - return true - } - - private fun unpackJs(script: Element): String? { - return script.select("script").find { it.data().contains("eval(function(p,a,c,k,e,d)") } - ?.data()?.let { getAndUnpack(it) } - } - -} \ No newline at end of file diff --git a/Anifreakz/src/main/kotlin/com/hexated/AnifreakzPlugin.kt b/Anifreakz/src/main/kotlin/com/hexated/AnifreakzPlugin.kt deleted file mode 100644 index 5de45031..00000000 --- a/Anifreakz/src/main/kotlin/com/hexated/AnifreakzPlugin.kt +++ /dev/null @@ -1,14 +0,0 @@ - -package com.hexated - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class AnifreakzPlugin: Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(Anifreakz()) - } -} \ No newline at end of file diff --git a/Anilibria/build.gradle.kts b/Anilibria/build.gradle.kts deleted file mode 100644 index 061d5f39..00000000 --- a/Anilibria/build.gradle.kts +++ /dev/null @@ -1,27 +0,0 @@ -// use an integer for version numbers -version = 2 - - -cloudstream { - language = "ru" - // All of these properties are optional, you can safely remove them - - // description = "Lorem Ipsum" - authors = listOf("Hexated") - - /** - * Status int as the following: - * 0: Down - * 1: Ok - * 2: Slow - * 3: Beta only - * */ - status = 1 // will be 3 if unspecified - tvTypes = listOf( - "AnimeMovie", - "OVA", - "Anime", - ) - - iconUrl = "https://raw.githubusercontent.com/hexated/cloudstream-extensions-hexated/master/Anilibria/icon.png" -} \ No newline at end of file diff --git a/Anilibria/icon.png b/Anilibria/icon.png deleted file mode 100644 index 844b3684d6caac443a5e7bafdfd92488d2768855..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 48946 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 - \ No newline at end of file diff --git a/Anilibria/src/main/kotlin/com/hexated/Anilibria.kt b/Anilibria/src/main/kotlin/com/hexated/Anilibria.kt deleted file mode 100644 index c51afb3c..00000000 --- a/Anilibria/src/main/kotlin/com/hexated/Anilibria.kt +++ /dev/null @@ -1,197 +0,0 @@ -package com.hexated - -import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.LoadResponse.Companion.addAniListId -import com.lagradost.cloudstream3.LoadResponse.Companion.addMalId -import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.getQualityFromName -import org.jsoup.Jsoup -import org.jsoup.nodes.Element - -class Anilibria : MainAPI() { - override var mainUrl = "https://anilibria.tv" - override var name = "Anilibria" - override val hasMainPage = true - override var lang = "ru" - override val hasDownloadSupport = true - - override val supportedTypes = setOf( - TvType.Anime, - TvType.AnimeMovie, - TvType.OVA - ) - - companion object { - fun getType(t: String): TvType { - return when { - t.contains("Фильм", true) -> TvType.Movie - t.contains("ТВ", true) -> TvType.Anime - else -> TvType.OVA - } - } - } - - override val mainPage = mainPageOf( - "1" to "Новое", - "2" to "Популярное", - ) - - override suspend fun getMainPage( - page: Int, - request: MainPageRequest - ): HomePageResponse { - val document = app.post( - "$mainUrl/public/catalog.php", data = mapOf( - "page" to "$page", - "xpage" to "catalog", - "sort" to request.data, - "finish" to "1", - "search" to "{\"year\":\"\",\"genre\":\"\",\"season\":\"\"}" - ), headers = mapOf("X-Requested-With" to "XMLHttpRequest") - ).parsedSafe()?.table?.let { Jsoup.parse(it) } - val home = document?.select("a")?.mapNotNull { - it.toSearchResult() - } ?: throw ErrorLoadingException("Invalid json responses") - return newHomePageResponse(request.name, home) - } - - private fun Element.toSearchResult(): AnimeSearchResponse? { - val href = fixUrl(this.attr("href")) - val title = this.selectFirst("span")?.text() ?: return null - val posterUrl = fixUrlNull(this.selectFirst("img")?.attr("src")) - - return newAnimeSearchResponse(title, href, TvType.Anime) { - this.posterUrl = posterUrl - addDubStatus(isDub = true) - } - } - - override suspend fun search(query: String): List { - val document = app.post( - "$mainUrl/public/search.php", - data = mapOf("search" to query, "small" to "1"), - headers = mapOf("X-Requested-With" to "XMLHttpRequest") - ).parsedSafe()?.mes?.let { Jsoup.parse(it) } - - return document?.select("a")?.mapNotNull { - it.toSearchResult() - } ?: throw ErrorLoadingException("Invalid json responses") - } - - override suspend fun load(url: String): LoadResponse? { - val document = app.get(url).document - - val title = document.selectFirst("h1.release-title")?.text() ?: return null - val poster = fixUrlNull(document.selectFirst("img#adminPoster")?.attr("src")) - val trackTitle = (document.selectFirst("h1.release-title br")?.nextSibling() - ?: document.selectFirst("h1.release-title")?.text()?.substringAfter("/")?.trim()).toString() - val type = document.selectFirst("div#xreleaseInfo b:contains(Тип:)")?.nextSibling() - .toString().substringBefore(",").trim() - val trackType = type.let { - if(it.contains("Фильм", true)) "movie" else "tv" - } - val year = document.selectFirst("div#xreleaseInfo b:contains(Сезон:)")?.nextElementSibling() - ?.text()?.filter { it.isDigit() }?.toIntOrNull() - val (malId, anilistId, image, cover) = getTracker(trackTitle, trackType, year) - val episodes = document.select("script").find { it.data().contains("var player =") }?.data() - ?.substringAfter("file:[")?.substringBefore("],")?.let { data -> - tryParseJson>("[$data]")?.mapNotNull { eps -> - Episode( - eps.file ?: return@mapNotNull null, - name = eps.title ?: return@mapNotNull null, - posterUrl = fixUrlNull(eps.poster), - ) - } - } - return newAnimeLoadResponse(title, url, getType(type)) { - posterUrl = image ?: poster - backgroundPosterUrl = cover ?: image ?: poster - this.year = year - addEpisodes(DubStatus.Subbed, episodes) - plot = document.select("p.detail-description").text().trim() - this.tags = document.selectFirst("div#xreleaseInfo b:contains(Жанры:)")?.nextSibling() - .toString().split(",").map { it.trim() } - addMalId(malId) - addAniListId(anilistId?.toIntOrNull()) - } - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - - data.split(",").map { it.trim() }.map { m3u -> - val quality = Regex("\\[([0-9]+p)]").find(m3u)?.groupValues?.getOrNull(1) - val link = m3u.removePrefix("[$quality]").trim() - callback.invoke( - ExtractorLink( - this.name, - this.name, - link, - "$mainUrl/", - getQualityFromName(quality), - true - ) - ) - } - - return true - } - - private suspend fun getTracker(title: String?, type: String?, year: Int?): Tracker { - val res = app.get("https://api.consumet.org/meta/anilist/$title") - .parsedSafe()?.results?.find { media -> - (media.title?.english.equals(title, true) || media.title?.romaji.equals( - title, - true - )) || (media.type.equals(type, true) && media.releaseDate == year) - } - return Tracker(res?.malId, res?.aniId, res?.image, res?.cover) - } - - data class Tracker( - val malId: Int? = null, - val aniId: String? = null, - val image: String? = null, - val cover: String? = null, - ) - - data class Title( - @JsonProperty("romaji") val romaji: String? = null, - @JsonProperty("english") val english: String? = null, - ) - - data class Results( - @JsonProperty("id") val aniId: String? = null, - @JsonProperty("malId") val malId: Int? = null, - @JsonProperty("title") val title: Title? = null, - @JsonProperty("releaseDate") val releaseDate: Int? = null, - @JsonProperty("type") val type: String? = null, - @JsonProperty("image") val image: String? = null, - @JsonProperty("cover") val cover: String? = null, - ) - - data class AniSearch( - @JsonProperty("results") val results: ArrayList? = arrayListOf(), - ) - - private data class Episodes( - @JsonProperty("file") val file: String? = null, - @JsonProperty("title") val title: String? = null, - @JsonProperty("poster") val poster: String? = null, - ) - - private data class Home( - @JsonProperty("table") val table: String? = null, - ) - - private data class Search( - @JsonProperty("mes") val mes: String? = null, - ) - -} \ No newline at end of file diff --git a/Anilibria/src/main/kotlin/com/hexated/AnilibriaPlugin.kt b/Anilibria/src/main/kotlin/com/hexated/AnilibriaPlugin.kt deleted file mode 100644 index d813fb25..00000000 --- a/Anilibria/src/main/kotlin/com/hexated/AnilibriaPlugin.kt +++ /dev/null @@ -1,14 +0,0 @@ - -package com.hexated - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class AnilibriaPlugin: Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(Anilibria()) - } -} \ No newline at end of file diff --git a/AnimeIndoProvider/build.gradle.kts b/AnimeIndoProvider/build.gradle.kts deleted file mode 100644 index 1b1a9869..00000000 --- a/AnimeIndoProvider/build.gradle.kts +++ /dev/null @@ -1,27 +0,0 @@ -// use an integer for version numbers -version = 10 - - -cloudstream { - language = "id" - // All of these properties are optional, you can safely remove them - - // description = "Lorem Ipsum" - authors = listOf("Hexated") - - /** - * Status int as the following: - * 0: Down - * 1: Ok - * 2: Slow - * 3: Beta only - * */ - status = 1 // will be 3 if unspecified - tvTypes = listOf( - "AnimeMovie", - "OVA", - "Anime", - ) - - iconUrl = "https://www.google.com/s2/favicons?domain=animeindo.fun&sz=%size%" -} \ No newline at end of file diff --git a/AnimeIndoProvider/src/main/AndroidManifest.xml b/AnimeIndoProvider/src/main/AndroidManifest.xml deleted file mode 100644 index 874740e3..00000000 --- a/AnimeIndoProvider/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/AnimeIndoProvider/src/main/kotlin/com/hexated/AnimeIndoProvider.kt b/AnimeIndoProvider/src/main/kotlin/com/hexated/AnimeIndoProvider.kt deleted file mode 100644 index 4c40ad78..00000000 --- a/AnimeIndoProvider/src/main/kotlin/com/hexated/AnimeIndoProvider.kt +++ /dev/null @@ -1,169 +0,0 @@ -package com.hexated - -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.httpsify -import com.lagradost.cloudstream3.utils.loadExtractor -import org.jsoup.Jsoup -import org.jsoup.nodes.Element - -class AnimeIndoProvider : MainAPI() { - override var mainUrl = "https://animeindo.quest" - override var name = "AnimeIndo" - override val hasMainPage = true - override var lang = "id" - override val hasDownloadSupport = true - - override val supportedTypes = setOf( - TvType.Anime, - TvType.AnimeMovie, - TvType.OVA - ) - - companion object { - fun getType(t: String): TvType { - return if (t.contains("OVA", true) || t.contains("Special")) TvType.OVA - else if (t.contains("Movie", true)) TvType.AnimeMovie - else TvType.Anime - } - - fun getStatus(t: String): ShowStatus { - return when (t) { - "Finished Airing" -> ShowStatus.Completed - "Currently Airing" -> ShowStatus.Ongoing - else -> ShowStatus.Completed - } - } - - } - - override val mainPage = mainPageOf( - "$mainUrl/anime-terbaru/page/" to "Anime Terbaru", - "$mainUrl/ongoing/page/" to "Anime Ongoing", - "$mainUrl/populer/page/" to "Anime Populer", - "$mainUrl/donghua-terbaru/page/" to "Donghua Terbaru", - ) - - override suspend fun getMainPage( - page: Int, - request: MainPageRequest - ): HomePageResponse { - val document = app.get(request.data + page).document - val home = document.select("div.post-show > article, div.relat > article").mapNotNull { - it.toSearchResult() - } - return newHomePageResponse(request.name, home) - } - - private fun getProperAnimeLink(uri: String): String { - return if (uri.contains("/anime/")) { - uri - } else { - var title = uri.substringAfter("$mainUrl/") - title = when { - (title.contains("-episode")) && !(title.contains("-movie")) -> Regex("(.+)-episode").find( - title - )?.groupValues?.get(1).toString() - (title.contains("-movie")) -> Regex("(.+)-movie").find(title)?.groupValues?.get( - 1 - ).toString() - else -> title - } - "$mainUrl/anime/$title" - } - } - - private fun Element.toSearchResult(): AnimeSearchResponse? { - val title = this.selectFirst("div.title")?.text()?.trim() ?: return null - val href = getProperAnimeLink(this.selectFirst("a")!!.attr("href")) - val posterUrl = this.select("img[itemprop=image]").attr("src").toString() - val type = getType(this.select("div.type").text().trim()) - val epNum = - this.selectFirst("span.episode")?.ownText()?.replace(Regex("\\D"), "")?.trim() - ?.toIntOrNull() - return newAnimeSearchResponse(title, href, type) { - this.posterUrl = posterUrl - addSub(epNum) - } - - } - - override suspend fun search(query: String): List { - val anime = mutableListOf() - (1..2).forEach { page -> - val link = "$mainUrl/page/$page/?s=$query" - val document = app.get(link).document - val media = document.select(".site-main.relat > article").mapNotNull { - val title = it.selectFirst("div.title > h2")!!.ownText().trim() - val href = it.selectFirst("a")!!.attr("href") - val posterUrl = it.selectFirst("img")!!.attr("src").toString() - val type = getType(it.select("div.type").text().trim()) - newAnimeSearchResponse(title, href, type) { - this.posterUrl = posterUrl - } - } - if(media.isNotEmpty()) anime.addAll(media) - } - return anime - } - - override suspend fun load(url: String): LoadResponse? { - val document = app.get(url).document - - val title = document.selectFirst("h1.entry-title")?.text()?.replace("Subtitle Indonesia", "") - ?.trim() ?: return null - val poster = document.selectFirst("div.thumb > img[itemprop=image]")?.attr("src") - val tags = document.select("div.genxed > a").map { it.text() } - val type = document.selectFirst("div.info-content > div.spe > span:contains(Type:)")?.ownText() - ?.trim()?.lowercase() ?: "tv" - val year = document.selectFirst("div.info-content > div.spe > span:contains(Released:)")?.ownText()?.let { - Regex("\\d,\\s(\\d*)").find(it)?.groupValues?.get(1)?.toIntOrNull() - } - val status = getStatus(document.selectFirst("div.info-content > div.spe > span:nth-child(1)")!!.ownText().trim()) - val description = document.select("div[itemprop=description] > p").text() - - val trailer = document.selectFirst("div.player-embed iframe")?.attr("src") - val episodes = document.select("div.lstepsiode.listeps ul li").mapNotNull { - val header = it.selectFirst("span.lchx > a") ?: return@mapNotNull null - val episode = header.text().trim().replace("Episode", "").trim().toIntOrNull() - val link = fixUrl(header.attr("href")) - Episode(link, header.text(), episode = episode) - }.reversed() - - return newAnimeLoadResponse(title, url, getType(type)) { - engName = title - posterUrl = poster - this.year = year - addEpisodes(DubStatus.Subbed, episodes) - showStatus = status - plot = description - this.tags = tags - addTrailer(trailer) - } - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - - val document = app.get(data).document - document.select("div.itemleft > .mirror > option").mapNotNull { - fixUrl(Jsoup.parse(base64Decode(it.attr("value"))).select("iframe").attr("src")) - }.apmap { - if (it.startsWith(mainUrl)) { - app.get(it, referer = "$mainUrl/").document.select("iframe").attr("src") - } else { - it - } - }.apmap { - loadExtractor(httpsify(it), data, subtitleCallback, callback) - } - - return true - } - -} \ No newline at end of file diff --git a/AnimeIndoProvider/src/main/kotlin/com/hexated/AnimeIndoProviderPlugin.kt b/AnimeIndoProvider/src/main/kotlin/com/hexated/AnimeIndoProviderPlugin.kt deleted file mode 100644 index 7f4d0a0d..00000000 --- a/AnimeIndoProvider/src/main/kotlin/com/hexated/AnimeIndoProviderPlugin.kt +++ /dev/null @@ -1,14 +0,0 @@ - -package com.hexated - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class AnimeIndoProviderPlugin: Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(AnimeIndoProvider()) - } -} \ No newline at end of file diff --git a/AnimeSailProvider/build.gradle.kts b/AnimeSailProvider/build.gradle.kts deleted file mode 100644 index bbfed124..00000000 --- a/AnimeSailProvider/build.gradle.kts +++ /dev/null @@ -1,27 +0,0 @@ -// use an integer for version numbers -version = 8 - - -cloudstream { - language = "id" - // All of these properties are optional, you can safely remove them - - // description = "Lorem Ipsum" - authors = listOf("Hexated") - - /** - * Status int as the following: - * 0: Down - * 1: Ok - * 2: Slow - * 3: Beta only - * */ - status = 1 // will be 3 if unspecified - tvTypes = listOf( - "AnimeMovie", - "Anime", - "OVA", - ) - - iconUrl = "https://www.google.com/s2/favicons?domain=111.90.143.42&sz=%size%" -} \ No newline at end of file diff --git a/AnimeSailProvider/src/main/AndroidManifest.xml b/AnimeSailProvider/src/main/AndroidManifest.xml deleted file mode 100644 index 874740e3..00000000 --- a/AnimeSailProvider/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/AnimeSailProvider/src/main/kotlin/com/hexated/AnimeSailProvider.kt b/AnimeSailProvider/src/main/kotlin/com/hexated/AnimeSailProvider.kt deleted file mode 100644 index 45094af4..00000000 --- a/AnimeSailProvider/src/main/kotlin/com/hexated/AnimeSailProvider.kt +++ /dev/null @@ -1,196 +0,0 @@ -package com.hexated - -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.mvvm.safeApiCall -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.Qualities -import com.lagradost.cloudstream3.utils.loadExtractor -import com.lagradost.nicehttp.NiceResponse -import org.jsoup.Jsoup -import org.jsoup.nodes.Element - -class AnimeSailProvider : MainAPI() { - override var mainUrl = "https://154.26.137.28" - override var name = "AnimeSail" - override val hasMainPage = true - override var lang = "id" - override val hasDownloadSupport = true - - override val supportedTypes = setOf( - TvType.Anime, - TvType.AnimeMovie, - TvType.OVA - ) - - companion object { - fun getType(t: String): TvType { - return if (t.contains("OVA", true) || t.contains("Special")) TvType.OVA - else if (t.contains("Movie", true)) TvType.AnimeMovie - else TvType.Anime - } - - fun getStatus(t: String): ShowStatus { - return when (t) { - "Completed" -> ShowStatus.Completed - "Ongoing" -> ShowStatus.Ongoing - else -> ShowStatus.Completed - } - } - } - - private suspend fun request(url: String, ref: String? = null): NiceResponse { - return app.get( - url, - headers = mapOf("Accept" to "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8"), - cookies = mapOf("_as_ipin_ct" to "ID"), - referer = ref - ) - } - - override val mainPage = mainPageOf( - "$mainUrl/page/" to "Episode Terbaru", - "$mainUrl/movie-terbaru/page/" to "Movie Terbaru", - "$mainUrl/genres/donghua/page/" to "Donghua" - ) - - override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse { - val document = request(request.data + page).document - val home = document.select("article").map { - it.toSearchResult() - } - return newHomePageResponse(request.name, home) - } - - private fun getProperAnimeLink(uri: String): String { - return if (uri.contains("/anime/")) { - uri - } else { - var title = uri.substringAfter("$mainUrl/") - title = when { - (title.contains("-episode")) && !(title.contains("-movie")) -> title.substringBefore( - "-episode" - ) - (title.contains("-movie")) -> title.substringBefore("-movie") - else -> title - } - - "$mainUrl/anime/$title" - } - } - - private fun Element.toSearchResult(): AnimeSearchResponse { - val href = getProperAnimeLink(fixUrlNull(this.selectFirst("a")?.attr("href")).toString()) - val title = this.select(".tt > h2").text().trim() - val posterUrl = fixUrlNull(this.selectFirst("div.limit img")?.attr("src")) - val epNum = this.selectFirst(".tt > h2")?.text()?.let { - Regex("Episode\\s?(\\d+)").find(it)?.groupValues?.getOrNull(1)?.toIntOrNull() - } - return newAnimeSearchResponse(title, href, TvType.Anime) { - this.posterUrl = posterUrl - addSub(epNum) - } - - } - - override suspend fun search(query: String): List { - val link = "$mainUrl/?s=$query" - val document = request(link).document - - return document.select("div.listupd article").map { - it.toSearchResult() - } - } - - override suspend fun load(url: String): LoadResponse { - val document = request(url).document - - val title = document.selectFirst("h1.entry-title")?.text().toString() - .replace("Subtitle Indonesia", "").trim() - val poster = document.selectFirst("div.entry-content > img")?.attr("src") - val type = document.select("tbody th:contains(Tipe)").next().text().lowercase() - val year = document.select("tbody th:contains(Dirilis)").next().text().trim().toIntOrNull() - - val episodes = document.select("ul.daftar > li").map { - val link = fixUrl(it.select("a").attr("href")) - val name = it.select("a").text() - val episode = Regex("Episode\\s?(\\d+)").find(name)?.groupValues?.getOrNull(0)?.toIntOrNull() - Episode(link, name, episode = episode) - }.reversed() - - return newAnimeLoadResponse(title, url, getType(type)) { - posterUrl = poster - this.year = year - addEpisodes(DubStatus.Subbed, episodes) - showStatus = - getStatus(document.select("tbody th:contains(Status)").next().text().trim()) - plot = document.selectFirst("div.entry-content > p")?.text() - this.tags = - document.select("tbody th:contains(Genre)").next().select("a").map { it.text() } - } - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - - val document = request(data).document - - document.select(".mobius > .mirror > option").apmap { - safeApiCall { - val iframe = fixUrl( - Jsoup.parse(base64Decode(it.attr("data-em"))).select("iframe").attr("src") - ?: throw ErrorLoadingException("No iframe found") - ) - - when { - iframe.startsWith("$mainUrl/utils/player/arch/") || iframe.startsWith( - "$mainUrl/utils/player/race/" - ) -> request(iframe, ref = data).document.select("source").attr("src") - .let { link -> - val source = - when { - iframe.contains("/arch/") -> "Arch" - iframe.contains("/race/") -> "Race" - else -> this.name - } - val quality = - Regex("\\.(\\d{3,4})\\.").find(link)?.groupValues?.get(1) - callback.invoke( - ExtractorLink( - source = source, - name = source, - url = link, - referer = mainUrl, - quality = quality?.toIntOrNull() ?: Qualities.Unknown.value - ) - ) - } -// skip for now -// iframe.startsWith("$mainUrl/utils/player/fichan/") -> "" -// iframe.startsWith("$mainUrl/utils/player/blogger/") -> "" - iframe.startsWith("https://aghanim.xyz/tools/redirect/") -> { - val link = "https://rasa-cintaku-semakin-berantai.xyz/v/${ - iframe.substringAfter("id=").substringBefore("&token") - }" - loadExtractor(link, mainUrl, subtitleCallback, callback) - } - iframe.startsWith("$mainUrl/utils/player/framezilla/") || iframe.startsWith("https://uservideo.xyz") -> { - request(iframe, ref = data).document.select("iframe").attr("src") - .let { link -> - loadExtractor(fixUrl(link), mainUrl, subtitleCallback, callback) - } - } - else -> { - loadExtractor(iframe, mainUrl, subtitleCallback, callback) - } - } - } - } - - return true - } - -} \ No newline at end of file diff --git a/AnimeSailProvider/src/main/kotlin/com/hexated/AnimeSailProviderPlugin.kt b/AnimeSailProvider/src/main/kotlin/com/hexated/AnimeSailProviderPlugin.kt deleted file mode 100644 index dfdc4c0c..00000000 --- a/AnimeSailProvider/src/main/kotlin/com/hexated/AnimeSailProviderPlugin.kt +++ /dev/null @@ -1,14 +0,0 @@ - -package com.hexated - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class AnimeSailProviderPlugin: Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(AnimeSailProvider()) - } -} \ No newline at end of file diff --git a/Animixplay/build.gradle.kts b/Animixplay/build.gradle.kts deleted file mode 100644 index 4d5730fa..00000000 --- a/Animixplay/build.gradle.kts +++ /dev/null @@ -1,27 +0,0 @@ -// use an integer for version numbers -version = 7 - - -cloudstream { - language = "en" - // All of these properties are optional, you can safely remove them - - // description = "Lorem Ipsum" - authors = listOf("Hexated") - - /** - * Status int as the following: - * 0: Down - * 1: Ok - * 2: Slow - * 3: Beta only - * */ - status = 1 // will be 3 if unspecified - tvTypes = listOf( - "AnimeMovie", - "Anime", - "OVA", - ) - - iconUrl = "https://www.google.com/s2/favicons?domain=animixplay.to&sz=%size%" -} \ No newline at end of file diff --git a/Animixplay/src/main/AndroidManifest.xml b/Animixplay/src/main/AndroidManifest.xml deleted file mode 100644 index 874740e3..00000000 --- a/Animixplay/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/Animixplay/src/main/kotlin/com/hexated/Animixplay.kt b/Animixplay/src/main/kotlin/com/hexated/Animixplay.kt deleted file mode 100644 index 79be5f62..00000000 --- a/Animixplay/src/main/kotlin/com/hexated/Animixplay.kt +++ /dev/null @@ -1,446 +0,0 @@ -package com.hexated - -import com.fasterxml.jackson.annotation.JsonProperty -import com.hexated.GogoExtractor.extractVidstream -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.LoadResponse.Companion.addAniListId -import com.lagradost.cloudstream3.LoadResponse.Companion.addMalId -import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.loadExtractor -import org.jsoup.Jsoup -import java.net.URI - - -class Animixplay : MainAPI() { - override var mainUrl = "https://animixplay.red" - override var name = "Animixplay" - override val hasMainPage = true - override var lang = "en" - override val hasQuickSearch = true - override val hasDownloadSupport = true - - override val supportedTypes = setOf( - TvType.Anime, - TvType.AnimeMovie, - TvType.OVA - ) - - companion object { - fun getStatus(t: String?): ShowStatus { - return when (t) { - "Finished Airing" -> ShowStatus.Completed - "Currently Airing" -> ShowStatus.Ongoing - else -> ShowStatus.Completed - } - } - } - - override val mainPage = mainPageOf( - "$mainUrl/api/search" to "Sub", - "$mainUrl/api/search" to "Dub", - "$mainUrl/a/XsWgdGCnKJfNvDFAM28EV" to "All", - "$mainUrl/api/search" to "Movie", - ) - - private var newPagination: String? = null - override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse { - val items = mutableListOf() - val paged = page.toString() - val pagination = if (request.name == "Movie") { - paged.replace(paged, "99999999") - } else { - paged.replace(paged, "3020-05-06 00:00:00") - } - - if (page <= 1) { - val headers = when (request.name) { - "Sub" -> mapOf("seasonal" to pagination) - "Dub" -> mapOf("seasonaldub" to pagination) - "All" -> mapOf("recent" to pagination) - "Movie" -> mapOf("movie" to pagination) - else -> mapOf() - } - val res = app.post( - request.data, - data = headers, - referer = mainUrl, - headers = mapOf("X-Requested-With" to "XMLHttpRequest") - ).parsedSafe() - newPagination = res?.last.toString() - val home = res?.result?.mapNotNull { - it.toSearchResponse() - } ?: throw ErrorLoadingException("No media found") - items.add( - HomePageList( - name = request.name, - list = home, - ) - ) - } else { - val headers = when (request.name) { - "Sub" -> mapOf("seasonal" to "$newPagination") - "Dub" -> mapOf("seasonaldub" to "$newPagination") - "All" -> mapOf("recent" to "$newPagination") - "Movie" -> mapOf("movie" to "$newPagination") - else -> mapOf() - } - val res = app.post( - request.data, - data = headers, - referer = mainUrl, - headers = mapOf("X-Requested-With" to "XMLHttpRequest") - ).parsedSafe() - newPagination = res?.last.toString() - val home = res?.result?.mapNotNull { - it.toSearchResponse() - } ?: throw ErrorLoadingException("No media found") - items.add( - HomePageList( - name = request.name, - list = home, - ) - ) - } - - return newHomePageResponse(items) - } - - private fun Anime.toSearchResponse(): AnimeSearchResponse? { - return newAnimeSearchResponse( - title ?: return null, - fixUrl(url ?: return null), - TvType.TvSeries, - ) { - this.posterUrl = img ?: picture - addDubStatus( - isDub = title.contains("Dub"), - episodes = Regex("EP\\s([0-9]+)/").find( - infotext ?: "" - )?.groupValues?.getOrNull(1) - ?.toIntOrNull() - ) - } - } - - - override suspend fun search(query: String): List? { - return app.post( - url = "https://v1.ij7p9towl8uj4qafsopjtrjk.workers.dev", - referer = mainUrl, - data = mapOf( - "q2" to query, - "origin" to "1", - "root" to "animixplay.to", - "d" to "gogoanime.tel" - ) - ).parsedSafe()?.result?.let { - Jsoup.parse(it).select("div").map { elem -> - - val href = fixUrl(elem.select("a").attr("href")) - val title = elem.select("a").attr("title") - newAnimeSearchResponse(title, href, TvType.Anime) { - this.posterUrl = elem.select("img").attr("src") - addDubStatus(isDub = title.contains("Dub")) - } - } - } - } - - override suspend fun quickSearch(query: String): List? { - return app.post( - "https://cdn.animixplay.to/api/search", - data = mapOf("qfast" to query, "root" to URI(mainUrl).host) - ).parsedSafe()?.result?.let { - Jsoup.parse(it).select("a").map { elem -> - val href = elem.attr("href") - val title = elem.select("p.name").text() - newAnimeSearchResponse(title, href, TvType.Anime) { - this.posterUrl = elem.select("img").attr("src") - addDubStatus(isDub = title.contains("Dub")) - } - } - } - } - - private suspend fun loadMissingAnime(url: String): LoadResponse? { - val doc = app.get(url).document - - val title = doc.selectFirst("span.animetitle")?.text() - val image = fixUrlNull(doc.selectFirst("meta[property=og:image]")?.attr("content")) - val genres = doc.selectFirst("span#genredata")?.text()?.split(",")?.map { it.trim() } - - val subEpisodes = mutableListOf() - val dubEpisodes = mutableListOf() - val dataEps = doc.select("div#epslistplace") - .text().trim() - Regex("\"([0-9]+)\":\"(\\S+?)\"").findAll(dataEps).toList() - .map { it.groupValues[1] to it.groupValues[2] }.map { (ep, link) -> - val episode = Episode(fixUrl(link), episode = ep.toInt() + 1) - if (url.contains("-dub")) { - dubEpisodes.add(episode) - } else { - subEpisodes.add(episode) - } - } - - return newAnimeLoadResponse( - title ?: return null, - url, - TvType.Anime - ) { - this.posterUrl = image - this.tags = genres - if (subEpisodes.isNotEmpty()) addEpisodes(DubStatus.Subbed, subEpisodes) - if (dubEpisodes.isNotEmpty()) addEpisodes(DubStatus.Dubbed, dubEpisodes) - } - } - - override suspend fun load(url: String): LoadResponse? { - - val (fixUrl, malId) = if (url.contains("/anime/")) { - listOf(url, Regex("anime/([0-9]+)/?").find(url)?.groupValues?.get(1)) - } else { - val malId = app.get(url).text.substringAfterLast("malid = '").substringBefore("';") - listOf("$mainUrl/anime/$malId", malId) - } - - val anilistId = app.post( - "https://graphql.anilist.co/", data = mapOf( - "query" to "{Media(idMal:$malId,type:ANIME){id}}", - ) - ).parsedSafe()?.data?.media?.id - - val res = app.get("$mainUrl/assets/mal/$malId.json").parsedSafe() - ?: return loadMissingAnime(url) - - val subEpisodes = mutableListOf() - val dubEpisodes = mutableListOf() - - app.post("$mainUrl/api/search", data = mapOf("recomended" to "$malId")) - .parsedSafe()?.data?.map { server -> - server.items?.apmap { data -> - val jsonData = - app.get( - fixUrl( - data.url ?: return@apmap null - ) - ).document.select("div#epslistplace") - .text().trim() - val episodeData = when (server.type) { - "AL" -> Regex("\"([0-9]+)\":\\[(.*?)]").findAll(jsonData).toList() - .map { it.groupValues[1] to it.groupValues[2] }.map { (ep, link) -> - Episode(link, episode = ep.toInt() + 1) - } - "RUSH" -> Regex("\"([0-9]+)\":\\[(.*?)]").findAll(jsonData).toList() - .map { it.groupValues[1] to it.groupValues[2] }.map { (ep, link) -> - val linkData = - Regex("\"vid\":\"(\\S+?)\"").findAll(link) - .map { it.groupValues[1] } - .toList().joinToString("") - Episode(linkData, episode = ep.toInt() + 1) - } - else -> { - Regex("\"([0-9]+)\":\"(\\S+?)\"").findAll(jsonData).toList() - .map { it.groupValues[1] to it.groupValues[2] }.map { (ep, link) -> - Episode(fixUrl(link), episode = ep.toInt() + 1) - } - } - } - episodeData.map { - if (data.url.contains("-dub")) { - dubEpisodes.add(it) - } else { - subEpisodes.add(it) - } - } - } - } - - val recommendations = app.get("$mainUrl/assets/similar/$malId.json") - .parsedSafe()?.recommendations?.mapNotNull { rec -> - newAnimeSearchResponse( - rec.title ?: return@mapNotNull null, - "$mainUrl/anime/${rec.malId}/", - TvType.Anime - ) { - this.posterUrl = rec.imageUrl - addDubStatus(dubExist = false, subExist = true) - } - } - - return newAnimeLoadResponse( - res.title ?: return null, - url, - TvType.Anime - ) { - engName = res.title - posterUrl = res.imageUrl - this.year = res.aired?.from?.split("-")?.firstOrNull()?.toIntOrNull() - showStatus = getStatus(res.status) - plot = res.synopsis - this.tags = res.genres?.mapNotNull { it.name } - this.recommendations = recommendations - addMalId(malId?.toIntOrNull()) - addAniListId(anilistId?.toIntOrNull()) - addTrailer(res.trailerUrl) - if (subEpisodes.isNotEmpty()) addEpisodes(DubStatus.Subbed, groupEpisodes(subEpisodes)) - if (dubEpisodes.isNotEmpty()) addEpisodes(DubStatus.Dubbed, groupEpisodes(dubEpisodes)) - } - - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - if (!data.contains("\"")) { - invokeGogo(data, subtitleCallback, callback) - } else { - data.split("http").apmap { - val link = it.replace("\"", "").let { url -> "http$url".trim() } - if (link.startsWith("https://gogohd.net")) { - invokeGogo(link, subtitleCallback, callback) - } else { - loadExtractor(link, "$mainUrl/", subtitleCallback) { links -> - val name = - if (link.startsWith("https://streamsb.net")) "StreamNet" else links.name - callback.invoke( - ExtractorLink( - name, - name, - links.url, - links.referer, - links.quality, - links.isM3u8, - links.headers, - links.extractorData - ) - ) - } - } - } - } - return true - } - - private fun groupEpisodes(episodes: List): List { - return episodes.groupBy { it.episode }.map { eps -> - val epsNum = eps.key - val epsLink = eps.value.joinToString("") { it.data }.replace("\",\"", "") - Episode(epsLink, episode = epsNum) - } - } - - private suspend fun invokeGogo( - link: String, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ) { - val iframe = app.get(link) - val iframeDoc = iframe.document - argamap({ - iframeDoc.select(".list-server-items > .linkserver") - .forEach { element -> - val status = element.attr("data-status") ?: return@forEach - if (status != "1") return@forEach - val extractorData = element.attr("data-video") ?: return@forEach - loadExtractor(extractorData, iframe.url, subtitleCallback, callback) - } - }, { - val iv = "3134003223491201" - val secretKey = "37911490979715163134003223491201" - val secretDecryptKey = "54674138327930866480207815084989" - extractVidstream( - iframe.url, - this.name, - callback, - iv, - secretKey, - secretDecryptKey, - isUsingAdaptiveKeys = false, - isUsingAdaptiveData = true, - iframeDocument = iframeDoc - ) - }) - } - - private data class IdAni( - @JsonProperty("id") val id: String? = null, - ) - - private data class MediaAni( - @JsonProperty("Media") val media: IdAni? = null, - ) - - private data class DataAni( - @JsonProperty("data") val data: MediaAni? = null, - ) - - private data class Items( - @JsonProperty("url") val url: String? = null, - @JsonProperty("title") val title: String? = null, - ) - - private data class Episodes( - @JsonProperty("type") val type: String? = null, - @JsonProperty("items") val items: ArrayList? = arrayListOf(), - ) - - private data class Data( - @JsonProperty("data") val data: ArrayList? = arrayListOf(), - ) - - private data class Aired( - @JsonProperty("from") val from: String? = null, - ) - - private data class Genres( - @JsonProperty("name") val name: String? = null, - ) - - private data class RecResult( - @JsonProperty("recommendations") val recommendations: ArrayList? = arrayListOf(), - ) - - private data class Recommendations( - @JsonProperty("mal_id") val malId: String? = null, - @JsonProperty("image_url") val imageUrl: String? = null, - @JsonProperty("title") val title: String? = null, - ) - - private data class AnimeDetail( - @JsonProperty("title") val title: String? = null, - @JsonProperty("image_url") val imageUrl: String? = null, - @JsonProperty("type") val type: String? = null, - @JsonProperty("aired") val aired: Aired? = null, - @JsonProperty("status") val status: String? = null, - @JsonProperty("synopsis") val synopsis: String? = null, - @JsonProperty("trailer_url") val trailerUrl: String? = null, - @JsonProperty("genres") val genres: ArrayList? = arrayListOf(), - ) - - private data class Search( - @JsonProperty("result") val result: String? = null, - ) - - private data class Result( - @JsonProperty("result") val result: ArrayList = arrayListOf(), - @JsonProperty("last") val last: Any? = null, - ) - - private data class Anime( - @JsonProperty("title") val title: String? = null, - @JsonProperty("url") val url: String? = null, - @JsonProperty("img") val img: String? = null, - @JsonProperty("picture") val picture: String? = null, - @JsonProperty("infotext") val infotext: String? = null, - ) - - private data class FullSearch( - @JsonProperty("result") val result: String? = null, - ) - -} \ No newline at end of file diff --git a/Animixplay/src/main/kotlin/com/hexated/AnimixplayPlugin.kt b/Animixplay/src/main/kotlin/com/hexated/AnimixplayPlugin.kt deleted file mode 100644 index e759a515..00000000 --- a/Animixplay/src/main/kotlin/com/hexated/AnimixplayPlugin.kt +++ /dev/null @@ -1,14 +0,0 @@ - -package com.hexated - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class AnimixplayPlugin: Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(Animixplay()) - } -} \ No newline at end of file diff --git a/Animixplay/src/main/kotlin/com/hexated/GogoExtractor.kt b/Animixplay/src/main/kotlin/com/hexated/GogoExtractor.kt deleted file mode 100644 index ed19fd25..00000000 --- a/Animixplay/src/main/kotlin/com/hexated/GogoExtractor.kt +++ /dev/null @@ -1,166 +0,0 @@ -package com.hexated - -import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.cloudstream3.app -import com.lagradost.cloudstream3.base64Decode -import com.lagradost.cloudstream3.base64DecodeArray -import com.lagradost.cloudstream3.base64Encode -import com.lagradost.cloudstream3.mvvm.normalSafeApiCall -import com.lagradost.cloudstream3.mvvm.safeApiCall -import com.lagradost.cloudstream3.utils.AppUtils -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.M3u8Helper -import com.lagradost.cloudstream3.utils.getQualityFromName -import org.jsoup.nodes.Document -import java.net.URI -import javax.crypto.Cipher -import javax.crypto.spec.IvParameterSpec -import javax.crypto.spec.SecretKeySpec - -object GogoExtractor { - - /** - * @param id base64Decode(show_id) + IV - * @return the encryption key - * */ - private fun getKey(id: String): String? { - return normalSafeApiCall { - id.map { - it.code.toString(16) - }.joinToString("").substring(0, 32) - } - } - - // https://github.com/saikou-app/saikou/blob/3e756bd8e876ad7a9318b17110526880525a5cd3/app/src/main/java/ani/saikou/anime/source/extractors/GogoCDN.kt#L60 - // No Licence on the function - private fun cryptoHandler( - string: String, - iv: String, - secretKeyString: String, - encrypt: Boolean = true - ): String { - //println("IV: $iv, Key: $secretKeyString, encrypt: $encrypt, Message: $string") - val ivParameterSpec = IvParameterSpec(iv.toByteArray()) - val secretKey = SecretKeySpec(secretKeyString.toByteArray(), "AES") - val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding") - return if (!encrypt) { - cipher.init(Cipher.DECRYPT_MODE, secretKey, ivParameterSpec) - String(cipher.doFinal(base64DecodeArray(string))) - } else { - cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivParameterSpec) - base64Encode(cipher.doFinal(string.toByteArray())) - } - } - - /** - * @param iframeUrl something like https://gogoplay4.com/streaming.php?id=XXXXXX - * @param mainApiName used for ExtractorLink names and source - * @param iv secret iv from site, required non-null if isUsingAdaptiveKeys is off - * @param secretKey secret key for decryption from site, required non-null if isUsingAdaptiveKeys is off - * @param secretDecryptKey secret key to decrypt the response json, required non-null if isUsingAdaptiveKeys is off - * @param isUsingAdaptiveKeys generates keys from IV and ID, see getKey() - * @param isUsingAdaptiveData generate encrypt-ajax data based on $("script[data-name='episode']")[0].dataset.value - * */ - suspend fun extractVidstream( - iframeUrl: String, - mainApiName: String, - callback: (ExtractorLink) -> Unit, - iv: String?, - secretKey: String?, - secretDecryptKey: String?, - // This could be removed, but i prefer it verbose - isUsingAdaptiveKeys: Boolean, - isUsingAdaptiveData: Boolean, - // If you don't want to re-fetch the document - iframeDocument: Document? = null - ) = safeApiCall { - // https://github.com/saikou-app/saikou/blob/3e756bd8e876ad7a9318b17110526880525a5cd3/app/src/main/java/ani/saikou/anime/source/extractors/GogoCDN.kt - // No Licence on the following code - // Also modified of https://github.com/jmir1/aniyomi-extensions/blob/master/src/en/gogoanime/src/eu/kanade/tachiyomi/animeextension/en/gogoanime/extractors/GogoCdnExtractor.kt - // License on the code above https://github.com/jmir1/aniyomi-extensions/blob/master/LICENSE - - if ((iv == null || secretKey == null || secretDecryptKey == null) && !isUsingAdaptiveKeys) - return@safeApiCall - - val id = Regex("id=([^&]+)").find(iframeUrl)!!.value.removePrefix("id=") - - var document: Document? = iframeDocument - val foundIv = - iv ?: (document ?: app.get(iframeUrl).document.also { document = it }) - .select("""div.wrapper[class*=container]""") - .attr("class").split("-").lastOrNull() ?: return@safeApiCall - val foundKey = secretKey ?: getKey(base64Decode(id) + foundIv) ?: return@safeApiCall - val foundDecryptKey = secretDecryptKey ?: foundKey - - val uri = URI(iframeUrl) - val mainUrl = "https://" + uri.host - - val encryptedId = cryptoHandler(id, foundIv, foundKey) - val encryptRequestData = if (isUsingAdaptiveData) { - // Only fetch the document if necessary - val realDocument = document ?: app.get(iframeUrl).document - val dataEncrypted = - realDocument.select("script[data-name='episode']").attr("data-value") - val headers = cryptoHandler(dataEncrypted, foundIv, foundKey, false) - "id=$encryptedId&alias=$id&" + headers.substringAfter("&") - } else { - "id=$encryptedId&alias=$id" - } - - val jsonResponse = - app.get( - "$mainUrl/encrypt-ajax.php?$encryptRequestData", - headers = mapOf("X-Requested-With" to "XMLHttpRequest") - ) - val dataencrypted = - jsonResponse.text.substringAfter("{\"data\":\"").substringBefore("\"}") - val datadecrypted = cryptoHandler(dataencrypted, foundIv, foundDecryptKey, false) - val sources = AppUtils.parseJson(datadecrypted) - - suspend fun invokeGogoSource( - source: GogoSource, - sourceCallback: (ExtractorLink) -> Unit - ) { - if (source.file.contains(".m3u8")) { - M3u8Helper.generateM3u8( - mainApiName, - source.file, - mainUrl, - headers = mapOf("Origin" to "https://plyr.link") - ).forEach(sourceCallback) - } else { - sourceCallback.invoke( - ExtractorLink( - mainApiName, - mainApiName, - source.file, - mainUrl, - getQualityFromName(source.label), - ) - ) - } - } - - sources.source?.forEach { - invokeGogoSource(it, callback) - } - sources.sourceBk?.forEach { - invokeGogoSource(it, callback) - } - } - - data class GogoSources( - @JsonProperty("source") val source: List?, - @JsonProperty("sourceBk") val sourceBk: List?, - //val track: List, - //val advertising: List, - //val linkiframe: String - ) - - data class GogoSource( - @JsonProperty("file") val file: String, - @JsonProperty("label") val label: String?, - @JsonProperty("type") val type: String?, - @JsonProperty("default") val default: String? = null - ) -} diff --git a/Aniworld/build.gradle.kts b/Aniworld/build.gradle.kts deleted file mode 100644 index 004864a6..00000000 --- a/Aniworld/build.gradle.kts +++ /dev/null @@ -1,27 +0,0 @@ -// use an integer for version numbers -version = 4 - - -cloudstream { - language = "de" - // All of these properties are optional, you can safely remove them - - // description = "Lorem Ipsum" - authors = listOf("Hexated") - - /** - * Status int as the following: - * 0: Down - * 1: Ok - * 2: Slow - * 3: Beta only - * */ - status = 1 // will be 3 if unspecified - tvTypes = listOf( - "AnimeMovie", - "Anime", - "OVA", - ) - - iconUrl = "https://www.google.com/s2/favicons?domain=aniworld.to&sz=%size%" -} \ No newline at end of file diff --git a/Aniworld/src/main/AndroidManifest.xml b/Aniworld/src/main/AndroidManifest.xml deleted file mode 100644 index c98063f8..00000000 --- a/Aniworld/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/Aniworld/src/main/kotlin/com/hexated/Aniworld.kt b/Aniworld/src/main/kotlin/com/hexated/Aniworld.kt deleted file mode 100644 index f0765f52..00000000 --- a/Aniworld/src/main/kotlin/com/hexated/Aniworld.kt +++ /dev/null @@ -1,233 +0,0 @@ -package com.hexated - -import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.LoadResponse.Companion.addActors -import com.lagradost.cloudstream3.extractors.DoodLaExtractor -import com.lagradost.cloudstream3.extractors.Voe -import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.M3u8Helper -import com.lagradost.cloudstream3.utils.loadExtractor -import org.jsoup.nodes.Document -import org.jsoup.nodes.Element -import java.net.URI - -class Aniworld : MainAPI() { - override var mainUrl = "https://aniworld.to" - override var name = "Aniworld" - override val hasMainPage = true - override var lang = "de" - override val hasDownloadSupport = true - - override val supportedTypes = setOf( - TvType.Anime, - TvType.AnimeMovie, - TvType.OVA - ) - - companion object { - fun getType(t: String): TvType { - return when { - t.contains("Anime Ova") -> TvType.OVA - t.contains("Anime Movie") -> TvType.AnimeMovie - else -> TvType.Anime - } - } - - fun getStatus(t: String): ShowStatus { - return when { - t.contains("/complete", true) -> ShowStatus.Completed - t.contains("/running", true) -> ShowStatus.Ongoing - else -> ShowStatus.Completed - } - } - } - - override suspend fun getMainPage( - page: Int, - request: MainPageRequest - ): HomePageResponse { - - val document = app.get(mainUrl).document - val item = arrayListOf() - document.select("div.carousel").map { ele -> - val header = ele.selectFirst("h2")?.text() ?: return@map - val home = ele.select("div.coverListItem").mapNotNull { - it.toSearchResult() - } - if (home.isNotEmpty()) item.add(HomePageList(header, home)) - } - return HomePageResponse(item) - } - - override suspend fun search(query: String): List { - val json = app.post( - "$mainUrl/ajax/search", - data = mapOf("keyword" to query), - referer = "$mainUrl/search", - headers = mapOf( - "x-requested-with" to "XMLHttpRequest" - ) - ) - return tryParseJson>(json.text)?.filter { - !it.link.contains("episode-") && it.link.contains( - "/stream" - ) - }?.map { - newAnimeSearchResponse( - it.title?.replace(Regex(""), "") ?: "", - fixUrl(it.link), - TvType.Anime - ) { - } - } ?: throw ErrorLoadingException() - - } - - override suspend fun load(url: String): LoadResponse? { - val document = app.get(url).document - - val title = document.selectFirst("div.series-title span")?.text() ?: return null - val poster = fixUrlNull(document.selectFirst("div.seriesCoverBox img")?.attr("data-src")) - val tags = document.select("div.genres li a").map { it.text() } - val year = document.selectFirst("span[itemprop=startDate] a")?.text()?.toIntOrNull() - val description = document.select("p.seri_des").text() - val actor = - document.select("li:contains(Schauspieler:) ul li a").map { it.select("span").text() } - - val episodes = mutableListOf() - document.select("div#stream > ul:first-child li").map { ele -> - val page = ele.selectFirst("a") - val epsDocument = app.get(fixUrl(page?.attr("href") ?: return@map)).document - epsDocument.select("div#stream > ul:nth-child(4) li").mapNotNull { eps -> - episodes.add( - Episode( - fixUrl(eps.selectFirst("a")?.attr("href") ?: return@mapNotNull null), - episode = eps.selectFirst("a")?.text()?.toIntOrNull(), - season = page.text().toIntOrNull() - ) - ) - } - } - - return newAnimeLoadResponse( - title, - url, - TvType.Anime - ) { - engName = title - posterUrl = poster - this.year = year - addEpisodes( - DubStatus.Subbed, - episodes - ) - addActors(actor) - plot = description - this.tags = tags - } - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - val document = app.get(data).document - document.select("div.hosterSiteVideo ul li").map { - Triple( - it.attr("data-lang-key"), - it.attr("data-link-target"), - it.select("h4").text() - ) - }.filter { - it.third != "Vidoza" - }.apmap { - val redirectUrl = app.get(fixUrl(it.second)).url - val lang = it.first.getLanguage(document) - if (it.third == "VOE") { - invokeVoe(redirectUrl, lang, data, callback) - } else { - loadExtractor(redirectUrl, data, subtitleCallback) { link -> - val name = "${link.name} [${lang}]" - callback.invoke( - ExtractorLink( - name, - name, - link.url, - link.referer, - link.quality, - link.isM3u8, - link.headers, - link.extractorData - ) - ) - } - } - } - - return true - } - - private suspend fun invokeVoe( - url: String, - lang: String?, - referer: String, - callback: (ExtractorLink) -> Unit, - ) { - val name = "Voe [${lang}]" - val request = app.get(url, referer = referer) - val baseUrl = getBaseUrl(request.url) - val res = request.document - val script = res.select("script").find { it.data().contains("sources =") }?.data() - val link = - Regex("[\"']hls[\"']:\\s*[\"'](.*)[\"']").find(script ?: return)?.groupValues?.get(1) - - M3u8Helper.generateM3u8( - name, - link ?: return, - "$baseUrl/", - headers = mapOf("Origin" to "$baseUrl/") - ).forEach(callback) - } - - private fun Element.toSearchResult(): AnimeSearchResponse? { - val href = fixUrlNull(this.selectFirst("a")?.attr("href")) ?: return null - val title = this.selectFirst("h3")?.text() ?: return null - val posterUrl = fixUrlNull(this.selectFirst("img")?.attr("data-src")) - return newAnimeSearchResponse(title, href, TvType.Anime) { - this.posterUrl = posterUrl - } - } - - private fun String.getLanguage(document: Document): String? { - return document.selectFirst("div.changeLanguageBox img[data-lang-key=$this]")?.attr("title") - ?.removePrefix("mit")?.trim() - } - - private fun getBaseUrl(url: String): String { - return URI(url).let { - "${it.scheme}://${it.host}" - } - } - - private data class AnimeSearch( - @JsonProperty("link") val link: String, - @JsonProperty("title") val title: String? = null, - ) - -} - -class Dooood : DoodLaExtractor() { - override var mainUrl = "https://urochsunloath.com" -} - -class Simpulumlamerop : Voe() { - override var mainUrl = "https://simpulumlamerop.com" -} - -class Urochsunloath : Voe() { - override var mainUrl = "https://urochsunloath.com" -} \ No newline at end of file diff --git a/Aniworld/src/main/kotlin/com/hexated/AniworldPlugin.kt b/Aniworld/src/main/kotlin/com/hexated/AniworldPlugin.kt deleted file mode 100644 index b70bad8f..00000000 --- a/Aniworld/src/main/kotlin/com/hexated/AniworldPlugin.kt +++ /dev/null @@ -1,17 +0,0 @@ - -package com.hexated - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class AniworldPlugin: Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(Aniworld()) - registerExtractorAPI(Urochsunloath()) - registerExtractorAPI(Simpulumlamerop()) - registerExtractorAPI(Dooood()) - } -} \ No newline at end of file diff --git a/Anizm/build.gradle.kts b/Anizm/build.gradle.kts deleted file mode 100644 index 6c69805d..00000000 --- a/Anizm/build.gradle.kts +++ /dev/null @@ -1,27 +0,0 @@ -// use an integer for version numbers -version = 1 - - -cloudstream { - language = "tr" - // All of these properties are optional, you can safely remove them - - // description = "Lorem Ipsum" - authors = listOf("Hexated") - - /** - * Status int as the following: - * 0: Down - * 1: Ok - * 2: Slow - * 3: Beta only - * */ - status = 1 // will be 3 if unspecified - tvTypes = listOf( - "AnimeMovie", - "Anime", - "OVA", - ) - - iconUrl = "https://www.google.com/s2/favicons?domain=anizm.net&sz=%size%" -} \ No newline at end of file diff --git a/Anizm/src/main/AndroidManifest.xml b/Anizm/src/main/AndroidManifest.xml deleted file mode 100644 index 874740e3..00000000 --- a/Anizm/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/Anizm/src/main/kotlin/com/hexated/Anizm.kt b/Anizm/src/main/kotlin/com/hexated/Anizm.kt deleted file mode 100644 index 1d5fb8c0..00000000 --- a/Anizm/src/main/kotlin/com/hexated/Anizm.kt +++ /dev/null @@ -1,195 +0,0 @@ -package com.hexated - -import android.util.Log -import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer -import com.lagradost.cloudstream3.mvvm.safeApiCall -import com.lagradost.cloudstream3.utils.* -import org.jsoup.Jsoup -import org.jsoup.nodes.Element - - -class Anizm : MainAPI() { - override var mainUrl = "https://anizm.net" - override var name = "Anizm" - override val hasMainPage = true - override var lang = "tr" - override val hasDownloadSupport = true - - override val supportedTypes = setOf( - TvType.Anime, - TvType.AnimeMovie, - TvType.OVA - ) - - companion object { - private const val mainServer = "https://anizmplayer.com" - } - - override val mainPage = mainPageOf( - "$mainUrl/anime-izle?sayfa=" to "Son Eklenen Animeler", - ) - - override suspend fun getMainPage( - page: Int, - request: MainPageRequest - ): HomePageResponse { - val document = app.get(request.data + page).document - val home = document.select("div.restrictedWidth div#episodesMiddle").mapNotNull { - it.toSearchResult() - } - return newHomePageResponse(request.name, home) - } - - private fun getProperAnimeLink(uri: String): String { - return if (uri.contains("-bolum")) { - "$mainUrl/${uri.substringAfter("$mainUrl/").replace(Regex("-[0-9]+-bolum.*"), "")}" - } else { - uri - } - } - - private fun Element.toSearchResult(): AnimeSearchResponse? { - val href = getProperAnimeLink(this.selectFirst("a")!!.attr("href")) - val title = this.selectFirst("div.title, h5.animeTitle a")?.text() ?: return null - val posterUrl = fixUrlNull(this.selectFirst("img")?.attr("src")) - val episode = this.selectFirst("div.truncateText")?.text()?.let { - Regex("([0-9]+).\\s?Bölüm").find(it)?.groupValues?.getOrNull(1)?.toIntOrNull() - } - - return newAnimeSearchResponse(title, href, TvType.Anime) { - this.posterUrl = posterUrl - addSub(episode) - } - } - - override suspend fun search(query: String): List { - val document = app.get( - "$mainUrl/fullViewSearch?search=$query&skip=0", - headers = mapOf("X-Requested-With" to "XMLHttpRequest") - ).document - - return document.select("div.searchResultItem").mapNotNull { - it.toSearchResult() - } - } - - override suspend fun load(url: String): LoadResponse { - val document = app.get(url).document - - val title = document.selectFirst("h2.anizm_pageTitle a")!!.text().trim() - val type = - if (document.select("div.ui.grid div.four.wide").size == 1) TvType.Movie else TvType.Anime - val trailer = document.select("div.yt-hd-thumbnail-inner-container iframe").attr("src") - val episodes = document.select("div.ui.grid div.four.wide").map { - val name = it.select("div.episodeBlock").text() - val link = fixUrl(it.selectFirst("a")?.attr("href").toString()) - Episode(link, name) - } - return newAnimeLoadResponse(title, url, type) { - posterUrl = fixUrlNull(document.selectFirst("div.infoPosterImg > img")?.attr("src")) - this.year = document.select("div.infoSta ul li:first-child").text().trim().toIntOrNull() - addEpisodes(DubStatus.Subbed, episodes) - plot = document.select("div.infoDesc").text().trim() - this.tags = document.select("span.dataValue span.ui.label").map { it.text() } - addTrailer(trailer) - } - } - - private suspend fun invokeLokalSource( - url: String, - translator: String, - sourceCallback: (ExtractorLink) -> Unit - ) { - app.get(url, referer = "$mainUrl/").document.select("script").find { script -> - script.data().contains("eval(function(p,a,c,k,e,d)") - }?.let { - val key = getAndUnpack(it.data()).substringAfter("FirePlayer(\"").substringBefore("\",") - val referer = "$mainServer/video/$key" - val link = "$mainServer/player/index.php?data=$key&do=getVideo" - Log.i("hexated", link) - app.post( - link, - data = mapOf("hash" to key, "r" to "$mainUrl/"), - referer = referer, - headers = mapOf( - "Accept" to "*/*", - "Origin" to mainServer, - "Content-Type" to "application/x-www-form-urlencoded; charset=UTF-8", - "X-Requested-With" to "XMLHttpRequest" - ) - ).parsedSafe()?.videoSource?.let { m3uLink -> - M3u8Helper.generateM3u8( - "${this.name} ($translator)", - m3uLink, - referer - ).forEach(sourceCallback) - } - } - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - val document = app.get(data).document - document.select("div.episodeTranslators div#fansec").map { - Pair(it.select("a").attr("translator"), it.select("div.title").text()) - }.apmap { (url, translator) -> - safeApiCall { - app.get( - url, - referer = data, - headers = mapOf( - "Accept" to "application/json, text/javascript, */*; q=0.01", - "X-Requested-With" to "XMLHttpRequest" - ) - ).parsedSafe()?.data?.let { - Jsoup.parse(it).select("a").apmap { video -> - app.get( - video.attr("video"), - referer = data, - headers = mapOf( - "Accept" to "application/json, text/javascript, */*; q=0.01", - "X-Requested-With" to "XMLHttpRequest" - ) - ).parsedSafe()?.player?.let { iframe -> - Jsoup.parse(iframe).select("iframe").attr("src").let { link -> - when { - link.startsWith(mainServer) -> { - invokeLokalSource(link, translator, callback) - } - else -> { - loadExtractor( - fixUrl(link), - "$mainUrl/", - subtitleCallback, - callback - ) - } - } - } - } - } - } - } - } - return true - } - - data class Source( - @JsonProperty("videoSource") val videoSource: String?, - ) - - data class Videos( - @JsonProperty("player") val player: String?, - ) - - data class Translators( - @JsonProperty("data") val data: String?, - ) - -} \ No newline at end of file diff --git a/Anizm/src/main/kotlin/com/hexated/AnizmPlugin.kt b/Anizm/src/main/kotlin/com/hexated/AnizmPlugin.kt deleted file mode 100644 index c6741426..00000000 --- a/Anizm/src/main/kotlin/com/hexated/AnizmPlugin.kt +++ /dev/null @@ -1,14 +0,0 @@ - -package com.hexated - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class AnizmPlugin: Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(Anizm()) - } -} \ No newline at end of file diff --git a/Anroll/build.gradle.kts b/Anroll/build.gradle.kts deleted file mode 100644 index bbbb0839..00000000 --- a/Anroll/build.gradle.kts +++ /dev/null @@ -1,26 +0,0 @@ -// use an integer for version numbers -version = 1 - -cloudstream { - language = "pt-pt" - // All of these properties are optional, you can safely remove them - - // description = "Lorem Ipsum" - authors = listOf("Hexated") - - /** - * Status int as the following: - * 0: Down - * 1: Ok - * 2: Slow - * 3: Beta only - * */ - status = 1 // will be 3 if unspecified - tvTypes = listOf( - "Anime", - "AnimeMovie", - "OVA", - ) - - iconUrl = "https://www.google.com/s2/favicons?domain=www.anroll.net&sz=%size%" -} \ No newline at end of file diff --git a/Anroll/src/main/AndroidManifest.xml b/Anroll/src/main/AndroidManifest.xml deleted file mode 100644 index c98063f8..00000000 --- a/Anroll/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/Anroll/src/main/kotlin/com/hexated/Anroll.kt b/Anroll/src/main/kotlin/com/hexated/Anroll.kt deleted file mode 100644 index 178db598..00000000 --- a/Anroll/src/main/kotlin/com/hexated/Anroll.kt +++ /dev/null @@ -1,241 +0,0 @@ -package com.hexated - -import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.utils.AppUtils.toJson -import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.Qualities -import org.jsoup.nodes.Element - -class Anroll : MainAPI() { - override var mainUrl = "https://www.anroll.net" - override var name = "Anroll" - override val hasMainPage = true - override var lang = "pt" - override val hasDownloadSupport = true - override val hasQuickSearch = true - - override val supportedTypes = setOf( - TvType.Anime, - TvType.AnimeMovie, - TvType.OVA - ) - - companion object { - private const val searchUrl = "https://apiv2-prd.anroll.net" - private const val episodeUrl = "https://apiv3-prd.anroll.net" - private const val posterUrl = "https://static.anroll.net" - private const val videoUrl = "https://cdn-zenitsu.gamabunta.xyz" - } - - override suspend fun getMainPage( - page: Int, - request: MainPageRequest - ): HomePageResponse { - val document = app.get("$mainUrl/home").document - val home = mutableListOf() - document.select("div.sc-f5d5b250-1.iJHcsI").map { div -> - val header = div.selectFirst("h2")?.text() ?: return@map - val child = HomePageList( - header, - div.select("ul li").mapNotNull { - it.toSearchResult() - }, - header == "Últimos Laçamentos" - ) - home.add(child) - } - return HomePageResponse(home) - } - - private fun Element.toSearchResult(): AnimeSearchResponse? { - val title = this.selectFirst("h1")?.text()?.trim() ?: "" - val href = fixUrl(this.selectFirst("a")?.attr("href") ?: return null) - val posterUrl = fixUrlNull(this.select("img").attr("src")) - val epNum = this.selectFirst("span.sc-f5d5b250-3.fsTgnD b")?.text()?.toIntOrNull() - val isDub = this.selectFirst("div.sc-9dbd1f1d-5.efznig")?.text() == "DUB" - return newAnimeSearchResponse(title, href, TvType.Anime) { - this.posterUrl = posterUrl - addDubStatus(isDub, epNum) - } - } - - override suspend fun quickSearch(query: String): List = search(query) - - override suspend fun search(query: String): List { - val res = app.get("$searchUrl/search?q=$query").parsedSafe() - val collection = mutableListOf() - val anime = res?.data_anime?.mapNotNull { - addAnimeSearch( - it.titulo ?: return@mapNotNull null, - "a/${it.generate_id}", - it.slug_serie ?: return@mapNotNull null, - Image.Anime - ) - } - if (anime?.isNotEmpty() == true) collection.addAll(anime) - val filme = res?.data_filme?.mapNotNull { - addAnimeSearch( - it.nome_filme ?: return@mapNotNull null, - "f/${it.generate_id}", - it.slug_filme ?: return@mapNotNull null, - Image.Filme - ) - } - if (filme?.isNotEmpty() == true) collection.addAll(filme) - return collection - } - - override suspend fun load(url: String): LoadResponse? { - val fixUrl = getProperAnimeLink(url) ?: throw ErrorLoadingException() - val document = app.get(fixUrl).document - - val article = document.selectFirst("article.sc-f5d5b250-9") ?: return null - val title = article.selectFirst("h2")?.text() ?: return null - val poster = fixUrlNull(document.select("article.sc-f5d5b250-8 img").attr("src")) - val tags = article.select("div#generos a").map { it.text() } - val year = article.selectFirst("div.sc-f5d5b250-4")?.nextElementSibling()?.text() - ?.toIntOrNull() - val description = document.select("div.sinopse").text().trim() - val type = if (fixUrl.contains("/a/")) TvType.Anime else TvType.AnimeMovie - - val episodes = mutableListOf() - - if (type == TvType.Anime) { - for (i in 1..10) { - val dataEpisode = app.get("$episodeUrl/animes/${fixUrl.substringAfterLast("/")}/episodes?page=$i&order=desc") - .parsedSafe()?.data?.map { - Episode( - Load(it.anime?.get("slug_serie"), it.n_episodio, "animes").toJson(), - it.titulo_episodio, - episode = it.n_episodio?.toIntOrNull(), - posterUrl = it.anime?.get("slug_serie")?.fixImageUrl(Image.Episode), - description = it.sinopse_episodio - ) - }?.reversed() ?: emptyList() - if(dataEpisode.isEmpty()) break else episodes.addAll(dataEpisode) - } - } else { - val dataEpisode = listOf( - Episode( - Load( - document.selectFirst("script:containsData(slug_filme)")?.data()?.let { - Regex("[\"']slug_filme[\"']:[\"'](\\S+?)[\"']").find(it)?.groupValues?.get(1) - } ?: return null, "movie", "movies" - ).toJson() - - ) - ) - episodes.addAll(dataEpisode) - } - - return newAnimeLoadResponse(title, url, type) { - engName = title - posterUrl = poster - this.year = year - addEpisodes(DubStatus.Subbed, episodes) - plot = description - this.tags = tags - } - - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - val load = tryParseJson(data) - callback.invoke( - ExtractorLink( - this.name, - this.name, - if(load?.type == "movies") { - "$videoUrl/hls/${load.type}/${load.slug_serie}/${load.n_episodio}.mp4/media-1/stream.m3u8" - } else { - "$videoUrl/cf/hls/${load?.type}/${load?.slug_serie}/${load?.n_episodio}.mp4/media-1/stream.m3u8" - }, - "$mainUrl/", - Qualities.Unknown.value, - true - ) - ) - return true - } - - private suspend fun getProperAnimeLink(uri: String): String? { - return if (uri.contains("/e/")) { - app.get(uri).document.selectFirst("div.epcontrol2 a[href*=/a/]")?.attr("href")?.let { - fixUrl(it) - } - } else { - uri - } - } - - private fun addAnimeSearch(titulo: String, id: String, slug: String, type: Image): AnimeSearchResponse { - return newAnimeSearchResponse(titulo, "$mainUrl/$id", TvType.Anime) { - this.posterUrl = slug.fixImageUrl(type) - } - } - - private fun String.fixImageUrl(param: Image): String { - return when (param) { - Image.Episode -> { - "$posterUrl/images/animes/screens/$this/130x74/007.jpg" - } - Image.Anime -> { - "$mainUrl/_next/image?url=$posterUrl/images/animes/capas/130x209/$this.jpg&w=384&q=75" - } - Image.Filme -> { - "$mainUrl/_next/image?url=$posterUrl/images/filmes/capas/130x209/$this.jpg&w=384&q=75" - } - } - } - - enum class Image { - Episode, - Anime, - Filme, - } - - data class Load( - val slug_serie: String? = null, - val n_episodio: String? = null, - val type: String? = null, - ) - - data class DataEpisode( - @JsonProperty("id_series_episodios") val id_series_episodios: Int? = null, - @JsonProperty("n_episodio") val n_episodio: String? = null, - @JsonProperty("titulo_episodio") val titulo_episodio: String? = null, - @JsonProperty("sinopse_episodio") val sinopse_episodio: String? = null, - @JsonProperty("generate_id") val generate_id: String? = null, - @JsonProperty("anime") val anime: HashMap? = null, - ) - - data class LoadAnime( - @JsonProperty("data") val data: ArrayList? = arrayListOf() - ) - - data class DataAnime( - @JsonProperty("titulo") val titulo: String? = null, - @JsonProperty("generate_id") val generate_id: String? = null, - @JsonProperty("slug_serie") val slug_serie: String? = null, - @JsonProperty("total_eps_anime") val total_eps_anime: Int? = null, - ) - - data class DataFilme( - @JsonProperty("nome_filme") val nome_filme: String? = null, - @JsonProperty("generate_id") val generate_id: String? = null, - @JsonProperty("slug_filme") val slug_filme: String? = null, - ) - - data class SearchAnime( - @JsonProperty("data_anime") val data_anime: ArrayList? = arrayListOf(), - @JsonProperty("data_filme") val data_filme: ArrayList? = arrayListOf(), - ) - -} \ No newline at end of file diff --git a/Anroll/src/main/kotlin/com/hexated/AnrollPlugin.kt b/Anroll/src/main/kotlin/com/hexated/AnrollPlugin.kt deleted file mode 100644 index 431b5f31..00000000 --- a/Anroll/src/main/kotlin/com/hexated/AnrollPlugin.kt +++ /dev/null @@ -1,14 +0,0 @@ - -package com.hexated - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class AnrollPlugin: Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(Anroll()) - } -} \ No newline at end of file diff --git a/Dizikorea/build.gradle.kts b/Dizikorea/build.gradle.kts deleted file mode 100644 index f5df22d2..00000000 --- a/Dizikorea/build.gradle.kts +++ /dev/null @@ -1,23 +0,0 @@ -// use an integer for version numbers -version = 1 - - -cloudstream { - language = "tr" - // All of these properties are optional, you can safely remove them - - // description = "Lorem Ipsum" - authors = listOf("Hexated") - - /** - * Status int as the following: - * 0: Down - * 1: Ok - * 2: Slow - * 3: Beta only - * */ - status = 1 // will be 3 if unspecified - tvTypes = listOf("AsianDrama",) - - iconUrl = "https://www.google.com/s2/favicons?domain=dizikorea.com&sz=%size%" -} \ No newline at end of file diff --git a/Dizikorea/src/main/AndroidManifest.xml b/Dizikorea/src/main/AndroidManifest.xml deleted file mode 100644 index c98063f8..00000000 --- a/Dizikorea/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/Dizikorea/src/main/kotlin/com/hexated/Dizikorea.kt b/Dizikorea/src/main/kotlin/com/hexated/Dizikorea.kt deleted file mode 100644 index adb1d95f..00000000 --- a/Dizikorea/src/main/kotlin/com/hexated/Dizikorea.kt +++ /dev/null @@ -1,203 +0,0 @@ -package com.hexated - -import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.LoadResponse.Companion.addActors -import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer -import com.lagradost.cloudstream3.utils.* -import org.jsoup.nodes.Element -import java.net.URI - -class Dizikorea : MainAPI() { - override var mainUrl = "https://dizikorea.com" - override var name = "Dizikorea" - override val hasMainPage = true - override var lang = "tr" - override val hasDownloadSupport = true - override val hasQuickSearch = true - override val supportedTypes = setOf(TvType.AsianDrama) - - override val mainPage = mainPageOf( - "$mainUrl/diziler/page/" to "Kore Dizileri", - "$mainUrl/kore-filmleri-izle/page/" to "Son Eklenen Filmler", - ) - - override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse { - val document = app.get(request.data + page).document - val home = document.select("ul li.segment-poster").mapNotNull { - it.toSearchResult() - } - return newHomePageResponse(request.name, home) - } - - private fun Element.toSearchResult(): SearchResponse? { - val href = fixUrl(this.selectFirst("a")!!.attr("href")) - val title = this.selectFirst("h2.truncate")?.text()?.trim() ?: return null - val posterUrl = fixUrlNull(this.selectFirst("img")?.attr("data-src")) - - return newTvSeriesSearchResponse(title, href, TvType.AsianDrama) { - this.posterUrl = posterUrl - } - } - - override suspend fun quickSearch(query: String): List? = search(query) - - override suspend fun search(query: String): List? { - return app.post("$mainUrl/ajax.php?qr=$query") - .parsedSafe()?.data?.result?.mapNotNull { item -> - newTvSeriesSearchResponse( - item.s_name ?: return@mapNotNull null, - item.s_link ?: return@mapNotNull null, - TvType.AsianDrama - ) { - this.posterUrl = fixUrlNull(item.s_image) - } - } - } - - override suspend fun load(url: String): LoadResponse? { - val document = app.get(url).document - - val title = document.selectFirst("h1.page-title")?.ownText()?.trim() ?: return null - val poster = fixUrlNull(document.selectFirst("a.ui.image img")?.attr("data-src")) - val tags = document.select("div.nano-content div:contains(Tür:) a").map { it.text() } - - val year = document.selectFirst("table.ui.unstackable tr td:contains(Yapım Yılı) a")?.text() - ?.trim() - ?.toIntOrNull() - val description = document.selectFirst("p#tv-series-desc")?.text()?.trim() - val rating = - document.selectFirst("table.ui.unstackable tr td:contains(IMDb Puanı) .color-imdb") - ?.text()?.trim() - .toRatingInt() - val actors = document.select("div.global-box div.item").map { - Actor( - it.select("h5.truncate").text().trim(), - fixUrlNull(it.selectFirst("img")?.attr("src")) - ) - } - val type = if (document.select("div.all-seriespart") - .isNullOrEmpty() - ) TvType.Movie else TvType.TvSeries - val trailer = document.selectFirst("a.prettyPhoto")?.attr("href") - - return when (type) { - TvType.Movie -> { - newMovieLoadResponse(title, url, TvType.Movie, url) { - this.posterUrl = poster - this.year = year - this.plot = description - this.tags = tags - this.rating = rating - addActors(actors) - addTrailer(trailer) - } - } - else -> { - val episodes = document.select("div.all-seriespart div.el-item").map { ep -> - Episode( - fixUrl(ep.selectFirst("a")!!.attr("href")), - episode = ep.attr("data-epnumber").toIntOrNull(), - season = ep.selectFirst("span.season-name")?.text()?.filter { it.isDigit() } - ?.toIntOrNull() - ) - } - newTvSeriesLoadResponse(title, url, TvType.TvSeries, episodes) { - this.posterUrl = poster - this.year = year - this.plot = description - this.tags = tags - this.rating = rating - addActors(actors) - addTrailer(trailer) - } - } - } - } - - private fun getBaseUrl(url: String): String { - return URI(url).let { - "${it.scheme}://${it.host}" - } - } - - private suspend fun invokeLokalSource( - url: String, - source: String, - callback: (ExtractorLink) -> Unit - ) { - val script = app.get(url, referer = "$mainUrl/").document.select("script") - .find { it.data().contains("sources:") }?.data()?.substringAfter("sources: [") - ?.substringBefore("],")?.replace(Regex("\"?file\"?"), "\"file\"") - - AppUtils.tryParseJson(script)?.file?.let { link -> - if (link.contains(".m3u8")) { - M3u8Helper.generateM3u8( - source, - fixUrl(link), - getBaseUrl(url) - ).forEach(callback) - } else { - callback.invoke( - ExtractorLink( - source, - source, - fixUrl(link), - "$mainUrl/", - Qualities.Unknown.value, - ) - ) - } - } - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - - val document = app.get(data).document - val sources: MutableList = mutableListOf() - - val mainServer = document.select("div#singlePlay iframe").attr("src") - sources.add(mainServer) - - document.select("ul.linkler li").apmap { - val server = - app.get(it.select("a").attr("href")).document.select("div#singlePlay iframe") - .attr("src") - if (sources.isNotEmpty()) sources.add(fixUrl(server)) - } - - sources.distinct().apmap { link -> - when { - link.startsWith("https://playerkorea") -> invokeLokalSource(link, this.name, callback) - link.startsWith("https://vidmoly") -> invokeLokalSource(link, "Vidmoly", callback) - else -> loadExtractor(link, "$mainUrl/", subtitleCallback, callback) - } - } - - return true - } - - private data class SearchItem( - @JsonProperty("s_link") val s_link: String? = null, - @JsonProperty("s_image") val s_image: String? = null, - @JsonProperty("s_name") val s_name: String? = null, - ) - - private data class Result( - @JsonProperty("result") val result: ArrayList = arrayListOf(), - ) - - private data class Search( - @JsonProperty("data") val data: Result? = null, - ) - - private data class Source( - @JsonProperty("file") val file: String? = null, - ) - -} \ No newline at end of file diff --git a/Dizikorea/src/main/kotlin/com/hexated/DizikoreaPlugin.kt b/Dizikorea/src/main/kotlin/com/hexated/DizikoreaPlugin.kt deleted file mode 100644 index 06e5083f..00000000 --- a/Dizikorea/src/main/kotlin/com/hexated/DizikoreaPlugin.kt +++ /dev/null @@ -1,14 +0,0 @@ - -package com.hexated - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class DizikoreaPlugin: Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(Dizikorea()) - } -} \ No newline at end of file diff --git a/DramaSerial/build.gradle.kts b/DramaSerial/build.gradle.kts deleted file mode 100644 index 37e56657..00000000 --- a/DramaSerial/build.gradle.kts +++ /dev/null @@ -1,25 +0,0 @@ -// use an integer for version numbers -version = 3 - - -cloudstream { - language = "id" - // All of these properties are optional, you can safely remove them - - // description = "Lorem Ipsum" - authors = listOf("Hexated") - - /** - * Status int as the following: - * 0: Down - * 1: Ok - * 2: Slow - * 3: Beta only - * */ - status = 1 // will be 3 if unspecified - tvTypes = listOf( - "AsianDrama", - ) - - iconUrl = "https://www.google.com/s2/favicons?domain=149.3.170.35&sz=%size%" -} \ No newline at end of file diff --git a/DramaSerial/src/main/AndroidManifest.xml b/DramaSerial/src/main/AndroidManifest.xml deleted file mode 100644 index c98063f8..00000000 --- a/DramaSerial/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/DramaSerial/src/main/kotlin/com/hexated/DramaSerial.kt b/DramaSerial/src/main/kotlin/com/hexated/DramaSerial.kt deleted file mode 100644 index 881debc5..00000000 --- a/DramaSerial/src/main/kotlin/com/hexated/DramaSerial.kt +++ /dev/null @@ -1,124 +0,0 @@ -package com.hexated - -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.loadExtractor -import org.jsoup.nodes.Element - -class DramaSerial : MainAPI() { - override var mainUrl = "https://dramaserial.wiki" - override var name = "DramaSerial" - override val hasMainPage = true - override var lang = "id" - override val hasDownloadSupport = true - override val supportedTypes = setOf(TvType.AsianDrama) - - override val mainPage = mainPageOf( - "$mainUrl/page/" to "Latest Movie", - "$mainUrl/Genre/ongoing/page/" to "Ongoing", - "$mainUrl/Genre/drama-serial-korea/page/" to "Drama Serial Korea", - "$mainUrl/Genre/drama-serial-jepang/page/" to "Drama Serial Jepang", - "$mainUrl/Genre/drama-serial-mandarin/page/" to "Drama Serial Mandarin", - "$mainUrl/Genre/drama-serial-filipina/page/" to "Drama Serial Filipina", - "$mainUrl/Genre/drama-serial-india/page/" to "Drama Serial India", - ) - - override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse { - val document = app.get(request.data + page).document - val home = document.select("main#main article").mapNotNull { - it.toSearchResult() - } - return newHomePageResponse(request.name, home) - } - - private fun Element.toSearchResult(): SearchResponse? { - val href = fixUrl(this.selectFirst("a")!!.attr("href")) - val title = this.selectFirst("h2.entry-title a")?.text()?.trim() ?: return null - val posterUrl = fixUrlNull(this.selectFirst("img")?.attr("src")) - val episode = - this.selectFirst("div.gmr-episode-item")?.text()?.filter { it.isDigit() }?.toIntOrNull() - - return newAnimeSearchResponse(title, href, TvType.AsianDrama) { - this.posterUrl = posterUrl - addSub(episode) - } - } - - override suspend fun search(query: String): List { - val link = "$mainUrl/?s=$query&post_type[]=post&post_type[]=tv" - val document = app.get(link).document - - return document.select("main#main article").mapNotNull { - it.toSearchResult() - } - } - - override suspend fun load(url: String): LoadResponse { - val document = app.get(url).document - - val title = document.selectFirst("h1.entry-title")!!.text().trim() - val poster = fixUrlNull(document.selectFirst("figure.pull-left img")?.attr("src")) - val tags = - document.select("div.gmr-movie-innermeta span:contains(Genre:) a").map { it.text() } - val year = - document.selectFirst("div.gmr-movie-innermeta span:contains(Year:) a")!!.text().trim() - .toIntOrNull() - val duration = - document.selectFirst("div.gmr-movie-innermeta span:contains(Duration:)")?.text() - ?.filter { it.isDigit() }?.toIntOrNull() - val description = document.select("div.entry-content.entry-content-single div.entry-content.entry-content-single").text().trim() - val type = if(document.select("div.page-links").isNullOrEmpty()) TvType.Movie else TvType.AsianDrama - - if (type == TvType.Movie) { - return newMovieLoadResponse(title, url, TvType.Movie, url) { - posterUrl = poster - this.year = year - plot = description - this.tags = tags - this.duration = duration - } - } else { - val episodes = document.select("div.page-links span.page-link-number").mapNotNull { eps -> - val episode = eps.text().filter { it.isDigit() }.toIntOrNull() - val link = if(episode == 1) { - url - } else { - eps.parent()?.attr("href") - } - Episode( - link ?: return@mapNotNull null, - episode = episode, - ) - } - return newTvSeriesLoadResponse(title, url, TvType.AsianDrama, episodes = episodes) { - posterUrl = poster - this.year = year - this.duration = duration - plot = description - this.tags = tags - } - } - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - val document = app.get(data).document - - val iframe = document.select("div.gmr-server-wrap iframe").attr("src") - app.get(iframe, referer = "$mainUrl/").document.select("div#header-slider ul li").apmap { mLink -> - mLink.attr("onclick").substringAfter("frame('").substringBefore("')").let { iLink -> - val uLink = app.get(iLink, referer = iframe).document.select("script").find { it.data().contains("(document).ready") }?.data()?.substringAfter("replace(\"")?.substringBefore("\");") ?: return@apmap null - val link = app.get(uLink, referer = iLink).document.selectFirst("iframe")?.attr("src") ?: return@apmap null - loadExtractor(fixUrl(link), "$mainUrl/", subtitleCallback, callback) - } - } - - return true - - } - -} diff --git a/DramaSerial/src/main/kotlin/com/hexated/DramaSerialPlugin.kt b/DramaSerial/src/main/kotlin/com/hexated/DramaSerialPlugin.kt deleted file mode 100644 index 781dda9e..00000000 --- a/DramaSerial/src/main/kotlin/com/hexated/DramaSerialPlugin.kt +++ /dev/null @@ -1,15 +0,0 @@ - -package com.hexated - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class DramaSerialPlugin: Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(DramaSerial()) - registerExtractorAPI(Lkctwoone()) - } -} \ No newline at end of file diff --git a/DramaSerial/src/main/kotlin/com/hexated/Lkctwoone.kt b/DramaSerial/src/main/kotlin/com/hexated/Lkctwoone.kt deleted file mode 100644 index 1957e026..00000000 --- a/DramaSerial/src/main/kotlin/com/hexated/Lkctwoone.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.hexated - -import com.lagradost.cloudstream3.extractors.XStreamCdn - -class Lkctwoone: XStreamCdn() { - override val name: String = "LKC21" - override val mainUrl: String = "https://lkc21.net" -} \ No newline at end of file diff --git a/DramaidProvider/build.gradle.kts b/DramaidProvider/build.gradle.kts deleted file mode 100644 index 42fd4171..00000000 --- a/DramaidProvider/build.gradle.kts +++ /dev/null @@ -1,26 +0,0 @@ -// use an integer for version numbers -version = 6 - - -cloudstream { - language = "id" - // All of these properties are optional, you can safely remove them - - // description = "Lorem Ipsum" - authors = listOf("Hexated") - - /** - * Status int as the following: - * 0: Down - * 1: Ok - * 2: Slow - * 3: Beta only - * */ - status = 1 // will be 3 if unspecified - tvTypes = listOf( - "AsianDrama", - "Movie", - ) - - iconUrl = "https://www.google.com/s2/favicons?domain=dramaid.asia&sz=%size%" -} \ No newline at end of file diff --git a/DramaidProvider/src/main/AndroidManifest.xml b/DramaidProvider/src/main/AndroidManifest.xml deleted file mode 100644 index c98063f8..00000000 --- a/DramaidProvider/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/DramaidProvider/src/main/kotlin/com/hexated/DramaidProvider.kt b/DramaidProvider/src/main/kotlin/com/hexated/DramaidProvider.kt deleted file mode 100644 index d1e90376..00000000 --- a/DramaidProvider/src/main/kotlin/com/hexated/DramaidProvider.kt +++ /dev/null @@ -1,225 +0,0 @@ -package com.hexated - -import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.extractors.XStreamCdn -import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.getQualityFromName -import com.lagradost.cloudstream3.utils.loadExtractor -import org.jsoup.Jsoup -import org.jsoup.nodes.Element - -class DramaidProvider : MainAPI() { - override var mainUrl = "https://dramaid.best" - override var name = "DramaId" - override val hasQuickSearch = false - override val hasMainPage = true - override var lang = "id" - override val hasDownloadSupport = true - override val hasChromecastSupport = false - override val supportedTypes = setOf(TvType.AsianDrama) - - companion object { - fun getStatus(t: String): ShowStatus { - return when (t) { - "Completed" -> ShowStatus.Completed - "Ongoing" -> ShowStatus.Ongoing - else -> ShowStatus.Completed - } - } - } - - override val mainPage = mainPageOf( - "&status=&type=&order=update" to "Drama Terbaru", - "&order=latest" to "Baru Ditambahkan", - "&status=&type=&order=popular" to "Drama Popular", - ) - - override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse { - val document = app.get("$mainUrl/series/?page=$page${request.data}").document - val home = document.select("article[itemscope=itemscope]").mapNotNull { - it.toSearchResult() - } - return newHomePageResponse(request.name, home) - } - - private fun getProperDramaLink(uri: String): String { - return if (uri.contains("/series/")) { - uri - } else { - "$mainUrl/series/" + Regex("$mainUrl/(.+)-ep.+").find(uri)?.groupValues?.get(1) - .toString() - } - } - - private fun Element.toSearchResult(): SearchResponse? { - val href = getProperDramaLink(this.selectFirst("a.tip")!!.attr("href")) - val title = this.selectFirst("h2[itemprop=headline]")?.text()?.trim() ?: return null - val posterUrl = fixUrlNull(this.selectFirst(".limit > noscript > img")?.attr("src")) - - return newTvSeriesSearchResponse(title, href, TvType.AsianDrama) { - this.posterUrl = posterUrl - } - } - - override suspend fun search(query: String): List { - val link = "$mainUrl/?s=$query" - val document = app.get(link).document - - return document.select("article[itemscope=itemscope]").map { - val title = it.selectFirst("h2[itemprop=headline]")!!.text().trim() - val poster = it.selectFirst(".limit > noscript > img")!!.attr("src") - val href = it.selectFirst("a.tip")!!.attr("href") - - newTvSeriesSearchResponse(title, href, TvType.AsianDrama) { - this.posterUrl = poster - } - } - } - - override suspend fun load(url: String): LoadResponse { - val document = app.get(url).document - - val title = document.selectFirst("h1.entry-title")!!.text().trim() - val poster = document.select(".thumb > noscript > img").attr("src") - val tags = document.select(".genxed > a").map { it.text() } - - val year = Regex("\\d, ([0-9]*)").find( - document.selectFirst(".info-content > .spe > span > time")!!.text().trim() - )?.groupValues?.get(1).toString().toIntOrNull() - val status = getStatus( - document.select(".info-content > .spe > span:nth-child(1)") - .text().trim().replace("Status: ", "") - ) - val description = document.select(".entry-content > p").text().trim() - - val episodes = document.select(".eplister > ul > li").map { - val name = it.selectFirst("a > .epl-title")!!.text().trim() - val link = it.select("a").attr("href") - val epNum = it.selectFirst("a > .epl-num")!!.text().trim().toIntOrNull() - newEpisode(link) { - this.name = name - this.episode = epNum - } - }.reversed() - - val recommendations = - document.select(".listupd > article[itemscope=itemscope]").map { rec -> - val epTitle = rec.selectFirst("h2[itemprop=headline]")!!.text().trim() - val epPoster = rec.selectFirst(".limit > noscript > img")!!.attr("src") - val epHref = fixUrl(rec.selectFirst("a.tip")!!.attr("href")) - - newTvSeriesSearchResponse(epTitle, epHref, TvType.AsianDrama) { - this.posterUrl = epPoster - } - } - - if (episodes.size == 1) { - return newMovieLoadResponse(title, url, TvType.Movie, episodes[0].data) { - posterUrl = poster - this.year = year - plot = description - this.tags = tags - this.recommendations = recommendations - } - } else { - return newTvSeriesLoadResponse(title, url, TvType.AsianDrama, episodes = episodes) { - posterUrl = poster - this.year = year - showStatus = status - plot = description - this.tags = tags - this.recommendations = recommendations - } - } - - } - - private data class Sources( - @JsonProperty("file") val file: String, - @JsonProperty("label") val label: String, - @JsonProperty("type") val type: String, - @JsonProperty("default") val default: Boolean? - ) - - private data class Tracks( - @JsonProperty("file") val file: String, - @JsonProperty("label") val label: String, - @JsonProperty("kind") val type: String, - @JsonProperty("default") val default: Boolean? - ) - - private suspend fun invokeDriveSource( - url: String, - name: String, - subCallback: (SubtitleFile) -> Unit, - sourceCallback: (ExtractorLink) -> Unit - ) { - val server = app.get(url).document.selectFirst(".picasa")?.nextElementSibling()?.data() - - val source = "[${server!!.substringAfter("sources: [").substringBefore("],")}]".trimIndent() - val trackers = server.substringAfter("tracks:[").substringBefore("],") - .replace("//language", "") - .replace("file", "\"file\"") - .replace("label", "\"label\"") - .replace("kind", "\"kind\"").trimIndent() - - tryParseJson>(source)?.map { - sourceCallback( - ExtractorLink( - name, - "Drive", - fixUrl(it.file), - referer = "https://motonews.club/", - quality = getQualityFromName(it.label) - ) - ) - } - - tryParseJson(trackers)?.let { - subCallback.invoke( - SubtitleFile( - if (it.label.contains("Indonesia")) "${it.label}n" else it.label, - it.file - ) - ) - } - - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - val document = app.get(data).document - val sources = document.select(".mobius > .mirror > option").mapNotNull { - fixUrl(Jsoup.parse(base64Decode(it.attr("value"))).select("iframe").attr("src")) - } - - sources.map { - it.replace("https://ndrama.xyz", "https://www.fembed.com") - }.apmap { - when { - it.contains("motonews.club") -> invokeDriveSource( - it, - this.name, - subtitleCallback, - callback - ) - else -> loadExtractor(it, data, subtitleCallback, callback) - } - } - - return true - } - -} - -class Vanfem : XStreamCdn() { - override val name: String = "Vanfem" - override val mainUrl: String = "https://vanfem.com" -} - diff --git a/DramaidProvider/src/main/kotlin/com/hexated/DramaidProviderPlugin.kt b/DramaidProvider/src/main/kotlin/com/hexated/DramaidProviderPlugin.kt deleted file mode 100644 index 78c28ae2..00000000 --- a/DramaidProvider/src/main/kotlin/com/hexated/DramaidProviderPlugin.kt +++ /dev/null @@ -1,15 +0,0 @@ - -package com.hexated - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class DramaidProviderPlugin: Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(DramaidProvider()) - registerExtractorAPI(Vanfem()) - } -} \ No newline at end of file diff --git a/Dubbindo/build.gradle.kts b/Dubbindo/build.gradle.kts deleted file mode 100644 index d92b972b..00000000 --- a/Dubbindo/build.gradle.kts +++ /dev/null @@ -1,27 +0,0 @@ -// use an integer for version numbers -version = 1 - -cloudstream { - language = "id" - // All of these properties are optional, you can safely remove them - - // description = "Lorem Ipsum" - authors = listOf("Hexated") - - /** - * Status int as the following: - * 0: Down - * 1: Ok - * 2: Slow - * 3: Beta only - * */ - status = 1 // will be 3 if unspecified - tvTypes = listOf( - "TvSeries", - "Movie", - "Cartoon", - "Anime", - ) - - iconUrl = "https://www.google.com/s2/favicons?domain=www.dubbindo.xyz&sz=%size%" -} \ No newline at end of file diff --git a/Dubbindo/src/main/AndroidManifest.xml b/Dubbindo/src/main/AndroidManifest.xml deleted file mode 100644 index c98063f8..00000000 --- a/Dubbindo/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/Dubbindo/src/main/kotlin/com/hexated/Dubbindo.kt b/Dubbindo/src/main/kotlin/com/hexated/Dubbindo.kt deleted file mode 100644 index dc9fd653..00000000 --- a/Dubbindo/src/main/kotlin/com/hexated/Dubbindo.kt +++ /dev/null @@ -1,136 +0,0 @@ -package com.hexated - -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.utils.AppUtils.toJson -import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.Qualities -import com.lagradost.cloudstream3.utils.loadExtractor -import org.jsoup.nodes.Element - -class Dubbindo : MainAPI() { - override var mainUrl = "https://www.dubbindo.xyz" - override var name = "Dubbindo" - override val hasMainPage = true - override var lang = "id" - override val hasDownloadSupport = true - - override val supportedTypes = setOf( - TvType.TvSeries, - TvType.Movie, - TvType.Cartoon, - TvType.Anime, - ) - - override val mainPage = mainPageOf( - "$mainUrl/videos/category/1" to "Movie", - "$mainUrl/videos/category/3" to "TV Series", - "$mainUrl/videos/category/5" to "Anime Series", - "$mainUrl/videos/category/4" to "Anime Movie", - "$mainUrl/videos/category/other" to "Other", - ) - - override suspend fun getMainPage( - page: Int, - request: MainPageRequest - ): HomePageResponse { - val document = app.get("${request.data}?page_id=$page").document - val home = document.select("div.videos-latest-list.pt_timeline_vids div.video-wrapper") - .mapNotNull { - it.toSearchResult() - } - return newHomePageResponse( - list = HomePageList( - name = request.name, - list = home, - isHorizontalImages = true - ), - hasNext = true - ) - } - - private fun Element.toSearchResult(): TvSeriesSearchResponse? { - val title = this.selectFirst("h4,div.video-title")?.text()?.trim() ?: "" - val href = this.selectFirst("a")?.attr("href") ?: return null - val posterUrl = fixUrlNull(this.selectFirst("img")?.attr("src")) - return newTvSeriesSearchResponse(title, href, TvType.TvSeries) { - this.posterUrl = posterUrl - } - } - - override suspend fun search(query: String): List { - val searchResponse = mutableListOf() - for (i in 1..10) { - val document = - app.get( - "$mainUrl/search?keyword=$query&page_id=$i", - ).document - val results = document.select("div.videos-latest-list.row div.video-wrapper") - .mapNotNull { - it.toSearchResult() - } - searchResponse.addAll(results) - if (results.isEmpty()) break - } - return searchResponse - } - - override suspend fun load(url: String): LoadResponse? { - val document = app.get(url).document - - val title = document.selectFirst("div.video-big-title h1")?.text() ?: return null - val poster = document.selectFirst("meta[property=og:image]")?.attr("content") - val tags = document.select("div.pt_categories li a").map { it.text() } - val description = document.select("div.watch-video-description p").text() - val recommendations = document.select("div.related-video-wrapper").mapNotNull { - it.toSearchResult() - } - val video = document.select("video#my-video source").map { - Video( - it.attr("src"), - it.attr("size"), - it.attr("type"), - ) - } - - return newMovieLoadResponse(title, url, TvType.Movie, video.toJson()) { - posterUrl = poster - plot = description - this.tags = tags - this.recommendations = recommendations - } - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - - tryParseJson>(data)?.map { video -> - if(video.type == "video/mp4" || video.type == "video/x-msvideo" || video.type == "video/x-matroska") { - callback.invoke( - ExtractorLink( - this.name, - this.name, - video.src ?: return@map, - "", - video.res?.toIntOrNull() ?: Qualities.Unknown.value, - ) - ) - } else { - loadExtractor(video.src ?: return@map, "", subtitleCallback, callback) - } - } - - return true - } - - data class Video( - val src: String? = null, - val res: String? = null, - val type: String? = null, - ) - -} \ No newline at end of file diff --git a/Dubbindo/src/main/kotlin/com/hexated/DubbindoPlugin.kt b/Dubbindo/src/main/kotlin/com/hexated/DubbindoPlugin.kt deleted file mode 100644 index 22d3b82d..00000000 --- a/Dubbindo/src/main/kotlin/com/hexated/DubbindoPlugin.kt +++ /dev/null @@ -1,14 +0,0 @@ - -package com.hexated - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class DubbindoPlugin: Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(Dubbindo()) - } -} \ No newline at end of file diff --git a/DubokuProvider/build.gradle.kts b/DubokuProvider/build.gradle.kts deleted file mode 100644 index c5826742..00000000 --- a/DubokuProvider/build.gradle.kts +++ /dev/null @@ -1,27 +0,0 @@ -// use an integer for version numbers -version = 1 - - -cloudstream { - language = "zh" - // All of these properties are optional, you can safely remove them - - // description = "Lorem Ipsum" - authors = listOf("Hexated") - - /** - * Status int as the following: - * 0: Down - * 1: Ok - * 2: Slow - * 3: Beta only - * */ - status = 1 // will be 3 if unspecified - tvTypes = listOf( - "AsianDrama", - "TvSeries", - "Movie", - ) - - iconUrl = "https://www.google.com/s2/favicons?domain=www.duboku.tv&sz=%size%" -} \ No newline at end of file diff --git a/DubokuProvider/src/main/AndroidManifest.xml b/DubokuProvider/src/main/AndroidManifest.xml deleted file mode 100644 index c98063f8..00000000 --- a/DubokuProvider/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/DubokuProvider/src/main/kotlin/com/hexated/DubokuProvider.kt b/DubokuProvider/src/main/kotlin/com/hexated/DubokuProvider.kt deleted file mode 100644 index 1262fb5d..00000000 --- a/DubokuProvider/src/main/kotlin/com/hexated/DubokuProvider.kt +++ /dev/null @@ -1,133 +0,0 @@ -package com.hexated - -import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.LoadResponse.Companion.addActors -import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.M3u8Helper -import org.jsoup.nodes.Element - -class DubokuProvider : MainAPI() { - override var mainUrl = "https://www.duboku.tv" - override var name = "Duboku" - override val hasMainPage = true - override var lang = "zh" - override val hasDownloadSupport = true - override val supportedTypes = setOf( - TvType.Movie, - TvType.TvSeries, - TvType.AsianDrama, - ) - - override val mainPage = mainPageOf( - "$mainUrl/vodshow/2--time------" to "连续剧 时间", - "$mainUrl/vodshow/2--hits------" to "连续剧 人气", - "$mainUrl/vodshow/13--time------" to "陆剧 时间", - "$mainUrl/vodshow/13--hits------" to "陆剧 人气", - "$mainUrl/vodshow/15--time------" to "日韩剧 时间", - "$mainUrl/vodshow/15--hits------" to "日韩剧 人气", - "$mainUrl/vodshow/21--time------" to "短剧 时间", - "$mainUrl/vodshow/21--hits------" to "短剧 人气", - "$mainUrl/vodshow/16--time------" to "英美剧 时间", - "$mainUrl/vodshow/16--hits------" to "英美剧 人气", - "$mainUrl/vodshow/14--time------" to "台泰剧 时间", - "$mainUrl/vodshow/14--hits------" to "台泰剧 人气", - "$mainUrl/vodshow/20--time------" to "港剧 时间", - "$mainUrl/vodshow/20--hits------" to "港剧 人气", - ) - - override suspend fun getMainPage( - page: Int, - request: MainPageRequest - ): HomePageResponse { - val document = app.get("${request.data}$page---.html").document - val home = document.select("ul.myui-vodlist.clearfix li").mapNotNull { - it.toSearchResult() - } - return newHomePageResponse(request.name, home) - } - - private fun Element.toSearchResult(): SearchResponse? { - val title = this.selectFirst("h4.title a")?.text()?.trim() ?: return null - val href = fixUrl(this.selectFirst("a")?.attr("href").toString()) - val posterUrl = fixUrlNull(this.selectFirst("a")?.attr("data-original")) - val episode = this.selectFirst("span.pic-text.text-right")?.text()?.filter { it.isDigit() } - ?.toIntOrNull() - - return newAnimeSearchResponse(title, href, TvType.Movie) { - this.posterUrl = posterUrl - addSub(episode) - } - } - - override suspend fun search(query: String): List { - val document = app.get("$mainUrl/vodsearch/-------------.html?wd=$query&submit=").document - - return document.select("ul#searchList li").mapNotNull { - it.toSearchResult() - } - } - - override suspend fun load(url: String): LoadResponse? { - val document = app.get(url).document - - val title = document.selectFirst("h1.title")?.text()?.trim() ?: return null - val tvType = if (document.select("ul.myui-content__list li").size == 1 - ) TvType.Movie else TvType.TvSeries - val actors = document.select("p.data")[2].select("a").map { it.text() } - - val episodes = document.select("ul.myui-content__list li").map { - val href = fixUrl(it.select("a").attr("href")) - val name = it.select("a").text().trim() - Episode( - data = href, - name = name, - ) - } - return newTvSeriesLoadResponse(title, url, tvType, episodes) { - this.posterUrl = fixUrlNull( - document.selectFirst("a.myui-vodlist__thumb.picture img")?.attr("data-original") - ) - this.year = - document.select("p.data")[0].select("a").last()?.text()?.trim()?.toIntOrNull() - this.plot = document.selectFirst("span.sketch.content")?.text()?.trim() - this.tags = document.select("p.data")[0].select("a").map { it.text() } - this.rating = document.select("div#rating span.branch").text().toRatingInt() - addActors(actors) - } - - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - - app.get(data).document.select("script").map { script -> - if (script.data().contains("var player_data={")) { - val dataJson = - script.data().substringAfter("var player_data={").substringBefore("}") - tryParseJson("{$dataJson}")?.let { source -> - M3u8Helper.generateM3u8( - this.name, - source.url ?: return@map, - referer = "https://w.duboku.io/", - headers = mapOf("Origin" to "https://w.duboku.io") - ).forEach(callback) - } - } - } - - - return true - } - - data class Sources( - @JsonProperty("url") val url: String?, - ) - - -} \ No newline at end of file diff --git a/DubokuProvider/src/main/kotlin/com/hexated/DubokuProviderPlugin.kt b/DubokuProvider/src/main/kotlin/com/hexated/DubokuProviderPlugin.kt deleted file mode 100644 index b82c70b4..00000000 --- a/DubokuProvider/src/main/kotlin/com/hexated/DubokuProviderPlugin.kt +++ /dev/null @@ -1,14 +0,0 @@ - -package com.hexated - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class DubokuProviderPlugin: Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(DubokuProvider()) - } -} \ No newline at end of file diff --git a/GomunimeProvider/build.gradle.kts b/GomunimeProvider/build.gradle.kts deleted file mode 100644 index 96c05f73..00000000 --- a/GomunimeProvider/build.gradle.kts +++ /dev/null @@ -1,27 +0,0 @@ -// use an integer for version numbers -version = 2 - - -cloudstream { - language = "id" - // All of these properties are optional, you can safely remove them - -// description = "Lorem Ipsum" - authors = listOf("Hexated") - - /** - * Status int as the following: - * 0: Down - * 1: Ok - * 2: Slow - * 3: Beta only - * */ - status = 0 // will be 3 if unspecified - tvTypes = listOf( - "AnimeMovie", - "Anime", - "OVA", - ) - - iconUrl = "https://www.google.com/s2/favicons?domain=185.231.223.76&sz=%size%" -} \ No newline at end of file diff --git a/GomunimeProvider/src/main/AndroidManifest.xml b/GomunimeProvider/src/main/AndroidManifest.xml deleted file mode 100644 index c98063f8..00000000 --- a/GomunimeProvider/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/GomunimeProvider/src/main/kotlin/com/hexated/GomunimeProvider.kt b/GomunimeProvider/src/main/kotlin/com/hexated/GomunimeProvider.kt deleted file mode 100644 index d9fa137b..00000000 --- a/GomunimeProvider/src/main/kotlin/com/hexated/GomunimeProvider.kt +++ /dev/null @@ -1,238 +0,0 @@ -package com.hexated - -import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer -import com.lagradost.cloudstream3.mvvm.safeApiCall -import com.lagradost.cloudstream3.utils.AppUtils.parseJson -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.M3u8Helper -import com.lagradost.cloudstream3.utils.Qualities -import com.lagradost.cloudstream3.utils.loadExtractor -import org.jsoup.Jsoup - -class GomunimeProvider : MainAPI() { - override var mainUrl = "https://185.231.223.76" - override var name = "Gomunime" - override val hasMainPage = true - override var lang = "id" - override val hasDownloadSupport = true - - override val supportedTypes = setOf( - TvType.Anime, - TvType.AnimeMovie, - TvType.OVA - ) - - companion object { - - private const val mainServer = "https://path.onicdn.xyz/app/rapi.php" - - fun getType(t: String): TvType { - return if (t.contains("OVA") || t.contains("Special")) TvType.OVA - else if (t.contains("Movie")) TvType.AnimeMovie - else TvType.Anime - } - - fun getStatus(t: String): ShowStatus { - return when (t) { - "Completed" -> ShowStatus.Completed - "Ongoing" -> ShowStatus.Ongoing - else -> ShowStatus.Completed - } - } - } - - override val mainPage = mainPageOf( - "e" to "Episode Baru", - "c" to "Completed", - "la" to "Live Action", - "t" to "Trending" - ) - - override suspend fun getMainPage( - page: Int, - request: MainPageRequest - ): HomePageResponse { - val home = Jsoup.parse( - (app.post( - url = "$mainUrl/wp-admin/admin-ajax.php/wp-admin/admin-ajax.php", - headers = mapOf("Referer" to mainUrl), - data = mapOf( - "action" to "home_ajax", - "fungsi" to request.data, - "pag" to "$page" - ) - ).parsedSafe()?.html ?: throw ErrorLoadingException("Invalid Json reponse")) - ).select("li").mapNotNull { - val title = it.selectFirst("a.name")?.text()?.trim() ?: return@mapNotNull null - val href = getProperAnimeLink(it.selectFirst("a")!!.attr("href")) - val posterUrl = it.selectFirst("img")?.attr("src") - val type = getType(it.selectFirst(".taglist > span")!!.text().trim()) - val epNum = it.select(".tag.ep").text().replace(Regex("[^0-9]"), "").trim() - .toIntOrNull() - newAnimeSearchResponse(title, href, type) { - this.posterUrl = posterUrl - addSub(epNum) - } - } - - return newHomePageResponse(request.name, home) - } - - private fun getProperAnimeLink(uri: String): String { - return if (uri.contains("-episode")) { - val href = - "$mainUrl/anime/" + Regex("\\w\\d/(.*)-episode.*").find(uri)?.groupValues?.get(1) - .toString() - when { - href.contains("pokemon") -> href.replace(Regex("-[0-9]+"), "") - else -> href - } - } else { - uri - } - } - - override suspend fun search(query: String): List { - val link = "$mainUrl/?s=$query" - val document = app.get(link).document - - return document.select(".anime-list > li").map { - val title = it.selectFirst("a.name")!!.text() - val poster = it.selectFirst("img")!!.attr("src") - val tvType = getType(it.selectFirst(".taglist > span")?.text().toString()) - val href = fixUrl(it.selectFirst("a.name")!!.attr("href")) - - newAnimeSearchResponse(title, href, tvType) { - this.posterUrl = poster - addDubStatus(dubExist = false, subExist = true) - } - } - } - - override suspend fun load(url: String): LoadResponse { - val document = app.get(url).document - - val title = document.selectFirst(".entry-title")?.text().toString() - val poster = document.selectFirst(".thumbposter > img")?.attr("data-lazy-src") - val tags = document.select(".genxed > a").map { it.text() } - - val year = Regex("\\d, ([0-9]*)").find( - document.select("time[itemprop = datePublished]").text() - )?.groupValues?.get(1)?.toIntOrNull() - val status = getStatus(document.selectFirst(".spe > span")!!.ownText()) - val description = document.select("div[itemprop = description] > p").text() - val trailer = document.selectFirst("div.embed-responsive noscript iframe")?.attr("src") - val episodes = parseJson>( - Regex("var episodelist = (\\[.*])").find( - document.select(".bixbox.bxcl.epcheck > script").toString().trim() - )?.groupValues?.get(1).toString().replace(Regex("""\\"""), "").trim() - ).map { - val name = - Regex("(Episode\\s?[0-9]+)").find(it.epTitle.toString())?.groupValues?.getOrNull(0) - ?: it.epTitle - val link = it.epLink - Episode(link, name) - }.reversed() - - return newAnimeLoadResponse(title, url, TvType.Anime) { - engName = title - posterUrl = poster - this.year = year - addEpisodes(DubStatus.Subbed, episodes) - showStatus = status - plot = description - this.tags = tags - addTrailer(trailer) - } - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - val document = app.get(data).document - val scriptData = document.select("aside.sidebar > script").dataNodes().toString() - val key = scriptData.substringAfter("var a_ray = '").substringBefore("';") - val title = scriptData.substringAfter("var judul_postingan = \"").substringBefore("\";") - .replace(" ", "+") - val image = document.select("img#tempvid").last()?.attr("src").toString() - - val sources: List> = app.post( - url = mainServer, - data = mapOf("data" to key, "gambar" to image, "judul" to title, "func" to "mirror"), - referer = "$mainUrl/" - ).document.select("div.gomunime-server-mirror").map { - Pair( - it.attr("data-vhash"), - it.attr("data-type") - ) - } - - sources.apmap { - safeApiCall { - when { - it.second.contains("frame") -> { - loadExtractor(it.first, mainUrl, subtitleCallback, callback) - } - // Skip for now -// it.second.contains("hls") -> { -// app.post( -// url = mainServer, -// data = mapOf("fid" to it.first, "func" to "hls") -// ).text.let { link -> -// M3u8Helper.generateM3u8( -// this.name, -// link, -// "$mainUrl/", -// headers = mapOf("Origin" to mainUrl) -// ).forEach(callback) -// } -// } - it.second.contains("mp4") -> { - app.post( - url = mainServer, - data = mapOf("data" to it.first, "func" to "blogs") - ).parsed>().map { - callback.invoke( - ExtractorLink( - source = name, - name = "Mobi SD", - url = it.file, - referer = "$mainUrl/", - quality = Qualities.P360.value - ) - ) - } - } - else -> null - } - } - } - - return true - } - - private data class Response( - @JsonProperty("status") val status: Boolean, - @JsonProperty("html") val html: String - ) - - data class MobiSource( - @JsonProperty("file") val file: String, - @JsonProperty("label") val label: String, - @JsonProperty("type") val type: String - ) - - private data class EpisodeElement( - @JsonProperty("data-index") val dataIndex: Long?, - @JsonProperty("ep-num") val epNum: String?, - @JsonProperty("ep-title") val epTitle: String?, - @JsonProperty("ep-link") val epLink: String, - @JsonProperty("ep-date") val epDate: String? - ) - -} \ No newline at end of file diff --git a/GomunimeProvider/src/main/kotlin/com/hexated/GomunimeProviderPlugin.kt b/GomunimeProvider/src/main/kotlin/com/hexated/GomunimeProviderPlugin.kt deleted file mode 100644 index 06fa91e8..00000000 --- a/GomunimeProvider/src/main/kotlin/com/hexated/GomunimeProviderPlugin.kt +++ /dev/null @@ -1,14 +0,0 @@ - -package com.hexated - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class GomunimeProviderPlugin: Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(GomunimeProvider()) - } -} \ No newline at end of file diff --git a/Gomunimeis/build.gradle.kts b/Gomunimeis/build.gradle.kts deleted file mode 100644 index 5c6d720f..00000000 --- a/Gomunimeis/build.gradle.kts +++ /dev/null @@ -1,27 +0,0 @@ -// use an integer for version numbers -version = 5 - - -cloudstream { - language = "id" - // All of these properties are optional, you can safely remove them - - // description = "Lorem Ipsum" - authors = listOf("Hexated") - - /** - * Status int as the following: - * 0: Down - * 1: Ok - * 2: Slow - * 3: Beta only - * */ - status = 1 // will be 3 if unspecified - tvTypes = listOf( - "AnimeMovie", - "Anime", - "OVA", - ) - - iconUrl = "https://www.google.com/s2/favicons?domain=gomunime.is&sz=%size%" -} \ No newline at end of file diff --git a/Gomunimeis/src/main/AndroidManifest.xml b/Gomunimeis/src/main/AndroidManifest.xml deleted file mode 100644 index c98063f8..00000000 --- a/Gomunimeis/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/Gomunimeis/src/main/kotlin/com/hexated/Gomunimeis.kt b/Gomunimeis/src/main/kotlin/com/hexated/Gomunimeis.kt deleted file mode 100644 index 43e8616e..00000000 --- a/Gomunimeis/src/main/kotlin/com/hexated/Gomunimeis.kt +++ /dev/null @@ -1,163 +0,0 @@ -package com.hexated - -import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.utils.* -import java.util.ArrayList - - -class Gomunimeis : MainAPI() { - override var mainUrl = "https://anoboy.life" - override var name = "Gomunime.is" - override val hasMainPage = true - override var lang = "id" - override val hasQuickSearch = true - override val hasDownloadSupport = true - - override val supportedTypes = setOf( - TvType.Anime, - TvType.AnimeMovie, - TvType.OVA - ) - - companion object { - private const val mainImageUrl = "https://upload.anoboy.life" - - fun getType(t: String): TvType { - return if (t.contains("OVA", true) || t.contains("Special", true)) TvType.OVA - else if (t.contains("Movie", true)) TvType.AnimeMovie - else TvType.Anime - } - - fun getStatus(t: String): ShowStatus { - return when (t) { - "Completed" -> ShowStatus.Completed - "Ongoing" -> ShowStatus.Ongoing - else -> ShowStatus.Completed - } - } - } - - override val mainPage = mainPageOf( - "&limit=12&action=load_movie_last_update&status=Ongoing" to "Episode Baru", - "&limit=15&action=load_movie_last_update&status=Completed" to "Completed", - "&limit=15&action=load_movie_last_update&type=Live Action" to "Live Action", - "&limit=15&action=load_movie_trending" to "Trending" - ) - - override suspend fun getMainPage( - page: Int, - request: MainPageRequest - ): HomePageResponse { - val home = app.get( - "$mainUrl/my-ajax?page=$page${request.data}", - headers = mapOf("X-Requested-With" to "XMLHttpRequest") - ) - .parsedSafe()?.data - ?.mapNotNull { media -> - media.toSearchResponse() - } ?: throw ErrorLoadingException("Invalid Json reponse") - return newHomePageResponse(request.name, home) - } - - private fun Anime.toSearchResponse(): SearchResponse? { - - return newAnimeSearchResponse( - postTitle ?: return null, - "$mainUrl/anime/$postName", - TvType.TvSeries, - ) { - this.posterUrl = "$mainImageUrl/$image" - addSub(totalEpisode?.toIntOrNull()) - } - } - - override suspend fun quickSearch(query: String): List = search(query) - - override suspend fun search(query: String): List { - return app.get( - "$mainUrl/my-ajax?page=1&limit=10&action=load_search_movie&keyword=$query", - referer = "$mainUrl/search/?keyword=$query", - headers = mapOf("X-Requested-With" to "XMLHttpRequest") - ).parsedSafe()?.data - ?.mapNotNull { media -> - media.toSearchResponse() - } ?: throw ErrorLoadingException("Invalid Json reponse") - } - - override suspend fun load(url: String): LoadResponse { - val document = app.get(url).document - - val title = document.selectFirst(".entry-title")?.text().toString() - val poster = document.selectFirst(".thumbposter > img")?.attr("src") - val tags = document.select(".genxed > a").map { it.text() } - val type = document.selectFirst("div.info-content .spe span:last-child")?.ownText()?.lowercase() ?: "tv" - - val year = Regex("\\d, (\\d*)").find( - document.selectFirst("div.info-content .spe span.split")?.ownText().toString() - )?.groupValues?.get(1)?.toIntOrNull() - val status = getStatus(document.selectFirst(".spe > span")!!.ownText()) - val description = document.select("div[itemprop = description] > p").text() - val episodes = document.select(".eplister > ul > li").map { - val episode = Regex("Episode\\s?(\\d+)").find( - it.select(".epl-title").text() - )?.groupValues?.getOrNull(0) - val link = it.select("a").attr("href") - Episode(link, episode = episode?.toIntOrNull()) - }.reversed() - - return newAnimeLoadResponse(title, url, getType(type)) { - engName = title - posterUrl = poster - this.year = year - addEpisodes(DubStatus.Subbed, episodes) - showStatus = status - plot = description - this.tags = tags - } - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - - val document = app.get(data).document - document.select("div.player-container iframe").attr("src").substringAfter("html#") - .let { id -> - app.get("https://gomunimes.com/stream?id=$id") - .parsedSafe()?.server?.streamsb?.link?.let { link -> - loadExtractor(link, "https://vidgomunime.xyz/", subtitleCallback, callback) - } - } - - return true - } - - data class Streamsb( - @JsonProperty("link") val link: String?, - ) - - data class Server( - @JsonProperty("streamsb") val streamsb: Streamsb?, - ) - - data class Sources( - @JsonProperty("server") val server: Server?, - ) - - data class Responses( - @JsonProperty("data") val data: ArrayList? = arrayListOf(), - ) - - data class Anime( - @JsonProperty("post_title") val postTitle: String?, - @JsonProperty("post_name") val postName: String?, - @JsonProperty("image") val image: String?, - @JsonProperty("total_episode") val totalEpisode: String?, - @JsonProperty("salt") val salt: String?, - ) - -} \ No newline at end of file diff --git a/Gomunimeis/src/main/kotlin/com/hexated/GomunimeisPlugin.kt b/Gomunimeis/src/main/kotlin/com/hexated/GomunimeisPlugin.kt deleted file mode 100644 index 36a76324..00000000 --- a/Gomunimeis/src/main/kotlin/com/hexated/GomunimeisPlugin.kt +++ /dev/null @@ -1,14 +0,0 @@ - -package com.hexated - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class GomunimeisPlugin: Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(Gomunimeis()) - } -} \ No newline at end of file diff --git a/GoodPorn/build.gradle.kts b/GoodPorn/build.gradle.kts deleted file mode 100644 index a40d91b2..00000000 --- a/GoodPorn/build.gradle.kts +++ /dev/null @@ -1,25 +0,0 @@ -// use an integer for version numbers -version = 7 - - -cloudstream { - language = "en" - // All of these properties are optional, you can safely remove them - - description = "Premium porn with 4K support (use VPN if links not working)" - authors = listOf("Sora") - - /** - * Status int as the following: - * 0: Down - * 1: Ok - * 2: Slow - * 3: Beta only - * */ - status = 1 // will be 3 if unspecified - tvTypes = listOf( - "NSFW", - ) - - iconUrl = "https://www.google.com/s2/favicons?domain=goodporn.to&sz=%size%" -} \ No newline at end of file diff --git a/GoodPorn/src/main/AndroidManifest.xml b/GoodPorn/src/main/AndroidManifest.xml deleted file mode 100644 index c98063f8..00000000 --- a/GoodPorn/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/GoodPorn/src/main/kotlin/com/hexated/GoodPorn.kt b/GoodPorn/src/main/kotlin/com/hexated/GoodPorn.kt deleted file mode 100644 index 9ed8cb62..00000000 --- a/GoodPorn/src/main/kotlin/com/hexated/GoodPorn.kt +++ /dev/null @@ -1,126 +0,0 @@ -package com.hexated - -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.LoadResponse.Companion.addActors -import com.lagradost.cloudstream3.utils.* -import org.jsoup.nodes.Element -import java.util.* - -class GoodPorn : MainAPI() { - override var mainUrl = "https://goodporn.to" - override var name = "GoodPorn" - override val hasMainPage = true - override val hasDownloadSupport = true - override val vpnStatus = VPNStatus.MightBeNeeded - override val supportedTypes = setOf(TvType.NSFW) - - override val mainPage = mainPageOf( - "$mainUrl/?mode=async&function=get_block&block_id=list_videos_most_recent_videos&sort_by=post_date&from=" to "New Videos", - "$mainUrl/?mode=async&function=get_block&block_id=list_videos_most_recent_videos&sort_by=video_viewed&from=" to "Most Viewed Videos", - "$mainUrl/?mode=async&function=get_block&block_id=list_videos_most_recent_videos&sort_by=rating&from=" to "Top Rated Videos ", - "$mainUrl/?mode=async&function=get_block&block_id=list_videos_most_recent_videos&sort_by=most_commented&from=" to "Most Commented Videos", - "$mainUrl/?mode=async&function=get_block&block_id=list_videos_most_recent_videos&sort_by=duration&from=" to "Longest Videos", - "$mainUrl/channels/brazzers/?mode=async&function=get_block&block_id=list_videos_common_videos_list&sort_by=post_date&from=" to "Brazzers", - "$mainUrl/channels/digitalplayground/?mode=async&function=get_block&block_id=list_videos_common_videos_list&sort_by=post_date&from=" to "Digital Playground", - "$mainUrl/channels/realitykings/?mode=async&function=get_block&block_id=list_videos_common_videos_list&sort_by=post_date&from=" to "Realitykings", - "$mainUrl/channels/babes-network/?mode=async&function=get_block&block_id=list_videos_common_videos_list&sort_by=post_date&from=" to "Babes Network", - ) - - override suspend fun getMainPage( - page: Int, - request: MainPageRequest - ): HomePageResponse { - val document = app.get(request.data + page).document - val home = - document.select("div#list_videos_most_recent_videos_items div.item, div#list_videos_common_videos_list_items div.item") - .mapNotNull { - it.toSearchResult() - } - return newHomePageResponse( - list = HomePageList( - name = request.name, - list = home, - isHorizontalImages = true - ), - hasNext = true - ) - } - - private fun Element.toSearchResult(): SearchResponse? { - val title = this.selectFirst("strong.title")?.text() ?: return null - val href = fixUrl(this.selectFirst("a")!!.attr("href")) - val posterUrl = fixUrlNull(this.select("div.img > img").attr("data-original")) - return newMovieSearchResponse(title, href, TvType.Movie) { - this.posterUrl = posterUrl - } - - } - - override suspend fun search(query: String): List { - val searchResponse = mutableListOf() - for (i in 1..15) { - val document = - app.get( - "$mainUrl/search/nikki-benz/?mode=async&function=get_block&block_id=list_videos_videos_list_search_result&q=$query&category_ids=&sort_by=&from_videos=$i&from_albums=$i", - headers = mapOf("X-Requested-With" to "XMLHttpRequest") - ).document - val results = - document.select("div#list_videos_videos_list_search_result_items div.item") - .mapNotNull { - it.toSearchResult() - } - searchResponse.addAll(results) - if (results.isEmpty()) break - } - return searchResponse - } - - override suspend fun load(url: String): LoadResponse { - val document = app.get(url).document - - val title = document.selectFirst("div.headline > h1")?.text()?.trim().toString() - val poster = - fixUrlNull(document.selectFirst("meta[property=og:image]")?.attr("content").toString()) - val tags = document.select("div.info div:nth-child(5) > a").map { it.text() } - val description = document.select("div.info div:nth-child(2)").text().trim() - val actors = document.select("div.info div:nth-child(6) > a").map { it.text() } - val recommendations = - document.select("div#list_videos_related_videos_items div.item").mapNotNull { - it.toSearchResult() - } - - return newMovieLoadResponse(title, url, TvType.NSFW, url) { - this.posterUrl = poster - this.plot = description - this.tags = tags - addActors(actors) - this.recommendations = recommendations - } - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - val document = app.get(data).document - document.select("div.info div:last-child a").map { res -> - callback.invoke( - ExtractorLink( - this.name, - this.name, - res.attr("href") - .replace(Regex("\\?download\\S+.mp4&"), "?") + "&rnd=${Date().time}", - referer = data, - quality = Regex("([0-9]+p),").find(res.text())?.groupValues?.get(1) - .let { getQualityFromName(it) }, - headers = mapOf("Range" to "bytes=0-"), - ) - ) - } - - return true - } - -} \ No newline at end of file diff --git a/GoodPorn/src/main/kotlin/com/hexated/GoodPornPlugin.kt b/GoodPorn/src/main/kotlin/com/hexated/GoodPornPlugin.kt deleted file mode 100644 index cdb2d9fd..00000000 --- a/GoodPorn/src/main/kotlin/com/hexated/GoodPornPlugin.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.hexated - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class GoodPornPlugin: Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(GoodPorn()) - } -} \ No newline at end of file diff --git a/HDrezkaProvider/build.gradle.kts b/HDrezkaProvider/build.gradle.kts deleted file mode 100644 index 2fc9aa63..00000000 --- a/HDrezkaProvider/build.gradle.kts +++ /dev/null @@ -1,28 +0,0 @@ -// use an integer for version numbers -version = 3 - - -cloudstream { - language = "ru" - // All of these properties are optional, you can safely remove them - - // description = "Lorem Ipsum" - authors = listOf("Hexated") - - /** - * Status int as the following: - * 0: Down - * 1: Ok - * 2: Slow - * 3: Beta only - * */ - status = 1 // will be 3 if unspecified - tvTypes = listOf( - "AsianDrama", - "Anime", - "TvSeries", - "Movie", - ) - - iconUrl = "https://www.google.com/s2/favicons?domain=hdrezka19139.org&sz=%size%" -} \ No newline at end of file diff --git a/HDrezkaProvider/src/main/AndroidManifest.xml b/HDrezkaProvider/src/main/AndroidManifest.xml deleted file mode 100644 index c98063f8..00000000 --- a/HDrezkaProvider/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/HDrezkaProvider/src/main/kotlin/com/hexated/HDrezkaProvider.kt b/HDrezkaProvider/src/main/kotlin/com/hexated/HDrezkaProvider.kt deleted file mode 100644 index 9c119902..00000000 --- a/HDrezkaProvider/src/main/kotlin/com/hexated/HDrezkaProvider.kt +++ /dev/null @@ -1,395 +0,0 @@ -package com.hexated - -import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.LoadResponse.Companion.addActors -import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer -import com.lagradost.cloudstream3.utils.AppUtils.toJson -import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.Qualities -import com.lagradost.cloudstream3.utils.getQualityFromName -import org.jsoup.Jsoup -import org.jsoup.nodes.Element -import java.util.* - -class HDrezkaProvider : MainAPI() { - override var mainUrl = "https://rezka.ag" - override var name = "HDrezka" - override val hasMainPage = true - override var lang = "ru" - override val hasDownloadSupport = true - override val supportedTypes = setOf( - TvType.Movie, - TvType.TvSeries, - TvType.Anime, - TvType.AsianDrama - ) - - override val mainPage = mainPageOf( - "$mainUrl/films/?filter=watching" to "фильмы", - "$mainUrl/series/?filter=watching" to "сериалы", - "$mainUrl/cartoons/?filter=watching" to "мультфильмы", - "$mainUrl/animation/?filter=watching" to "аниме", - ) - - override suspend fun getMainPage( - page: Int, - request: MainPageRequest - ): HomePageResponse { - val url = request.data.split("?") - val home = app.get("${url.first()}page/$page/?${url.last()}").document.select( - "div.b-content__inline_items div.b-content__inline_item" - ).map { - it.toSearchResult() - } - - return newHomePageResponse(request.name, home) - } - - private fun Element.toSearchResult(): SearchResponse { - val title = - this.selectFirst("div.b-content__inline_item-link > a")?.text()?.trim().toString() - val href = this.selectFirst("a")?.attr("href").toString() - val posterUrl = this.select("img").attr("src") - val type = if (this.select("span.info").isNotEmpty()) TvType.TvSeries else TvType.Movie - return if (type == TvType.Movie) { - newMovieSearchResponse(title, href, TvType.Movie) { - this.posterUrl = posterUrl - } - } else { - val episode = - this.select("span.info").text().substringAfter(",").replace(Regex("[^0-9]"), "") - .toIntOrNull() - newAnimeSearchResponse(title, href, TvType.TvSeries) { - this.posterUrl = posterUrl - addDubStatus( - dubExist = true, - dubEpisodes = episode, - subExist = true, - subEpisodes = episode - ) - } - } - } - - override suspend fun search(query: String): List { - val link = "$mainUrl/search/?do=search&subaction=search&q=$query" - val document = app.get(link).document - - return document.select("div.b-content__inline_items div.b-content__inline_item").map { - it.toSearchResult() - } - } - - override suspend fun load(url: String): LoadResponse { - val document = app.get(url).document - - val id = url.split("/").last().split("-").first() - val title = (document.selectFirst("div.b-post__origtitle")?.text()?.trim() - ?: document.selectFirst("div.b-post__title h1")?.text()?.trim()).toString() - val poster = fixUrlNull(document.selectFirst("div.b-sidecover img")?.attr("src")) - val tags = - document.select("table.b-post__info > tbody > tr:contains(Жанр) span[itemprop=genre]") - .map { it.text() } - val year = document.select("div.film-info > div:nth-child(2) a").text().toIntOrNull() - val tvType = if (document.select("div#simple-episodes-tabs") - .isNullOrEmpty() - ) TvType.Movie else TvType.TvSeries - val description = document.selectFirst("div.b-post__description_text")?.text()?.trim() - val trailer = app.post( - "$mainUrl/engine/ajax/gettrailervideo.php", - data = mapOf("id" to id), - referer = url - ).parsedSafe()?.code.let { - Jsoup.parse(it.toString()).select("iframe").attr("src") - } - val rating = - document.selectFirst("table.b-post__info > tbody > tr:nth-child(1) span.bold")?.text() - .toRatingInt() - val actors = - document.select("table.b-post__info > tbody > tr:last-child span.item").mapNotNull { - Actor( - it.selectFirst("span[itemprop=name]")?.text() ?: return@mapNotNull null, - it.selectFirst("span[itemprop=actor]")?.attr("data-photo") - ) - } - - val recommendations = document.select("div.b-sidelist div.b-content__inline_item").map { - it.toSearchResult() - } - - val data = HashMap() - val server = ArrayList>() - - data["id"] = id - data["favs"] = document.selectFirst("input#ctrl_favs")?.attr("value").toString() - data["ref"] = url - - return if (tvType == TvType.TvSeries) { - document.select("ul#translators-list li").map { res -> - server.add( - mapOf( - "translator_name" to res.text(), - "translator_id" to res.attr("data-translator_id"), - ) - ) - } - val episodes = document.select("div#simple-episodes-tabs ul li").map { - val season = it.attr("data-season_id").toIntOrNull() - val episode = it.attr("data-episode_id").toIntOrNull() - val name = "Episode $episode" - - data["season"] = "$season" - data["episode"] = "$episode" - data["server"] = server - data["action"] = "get_stream" - - Episode( - data.toJson(), - name, - season, - episode, - ) - } - - newTvSeriesLoadResponse(title, url, TvType.TvSeries, episodes) { - this.posterUrl = poster - this.year = year - this.plot = description - this.tags = tags - this.rating = rating - addActors(actors) - this.recommendations = recommendations - addTrailer(trailer) - } - } else { - document.select("ul#translators-list li").map { res -> - server.add( - mapOf( - "translator_name" to res.text(), - "translator_id" to res.attr("data-translator_id"), - "camrip" to res.attr("data-camrip"), - "ads" to res.attr("data-ads"), - "director" to res.attr("data-director") - ) - ) - } - - data["server"] = server - data["action"] = "get_movie" - - newMovieLoadResponse(title, url, TvType.Movie, data.toJson()) { - this.posterUrl = poster - this.year = year - this.plot = description - this.tags = tags - this.rating = rating - addActors(actors) - this.recommendations = recommendations - addTrailer(trailer) - } - } - } - - private fun decryptStreamUrl(data: String): String { - - fun getTrash(arr: List, item: Int): List { - val trash = ArrayList>() - for (i in 1..item) { - trash.add(arr) - } - return trash.reduce { acc, list -> - val temp = ArrayList() - acc.forEach { ac -> - list.forEach { li -> - temp.add(ac.plus(li)) - } - } - return@reduce temp - } - } - - val trashList = listOf("@", "#", "!", "^", "$") - val trashSet = getTrash(trashList, 2) + getTrash(trashList, 3) - var trashString = data.replace("#h", "").split("//_//").joinToString("") - - trashSet.forEach { - val temp = base64Encode(it.toByteArray()) - trashString = trashString.replace(temp, "") - } - - return base64Decode(trashString) - - } - - private fun cleanCallback( - source: String, - url: String, - quality: String, - isM3u8: Boolean, - sourceCallback: (ExtractorLink) -> Unit - ) { - sourceCallback.invoke( - ExtractorLink( - source, - source, - url, - "$mainUrl/", - getQuality(quality), - isM3u8, - headers = mapOf( - "Origin" to mainUrl - ) - ) - ) - } - - private fun getLanguage(str: String): String { - return when (str) { - "Русский" -> "Russian" - "Українська" -> "Ukrainian" - else -> str - } - } - - private fun getQuality(str: String): Int { - return when (str) { - "360p" -> Qualities.P240.value - "480p" -> Qualities.P360.value - "720p" -> Qualities.P480.value - "1080p" -> Qualities.P720.value - "1080p Ultra" -> Qualities.P1080.value - else -> getQualityFromName(str) - } - } - - private fun invokeSources( - source: String, - url: String, - subtitle: String, - subCallback: (SubtitleFile) -> Unit, - sourceCallback: (ExtractorLink) -> Unit - ) { - decryptStreamUrl(url).split(",").map { links -> - val quality = - Regex("\\[([0-9]{3,4}p\\s?\\w*?)]").find(links)?.groupValues?.getOrNull(1) - ?.trim() ?: return@map null - links.replace("[$quality]", "").split(" or ") - .map { - val link = it.trim() - val type = if(link.contains(".m3u8")) "(Main)" else "(Backup)" - cleanCallback( - "$source $type", - link, - quality, - link.contains(".m3u8"), - sourceCallback, - ) - } - } - - subtitle.split(",").map { sub -> - val language = - Regex("\\[(.*)]").find(sub)?.groupValues?.getOrNull(1) ?: return@map null - val link = sub.replace("[$language]", "").trim() - subCallback.invoke( - SubtitleFile( - getLanguage(language), - link - ) - ) - } - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - - tryParseJson(data)?.let { res -> - if (res.server?.isEmpty() == true) { - val document = app.get(res.ref ?: return@let).document - document.select("script").map { script -> - if (script.data().contains("sof.tv.initCDNMoviesEvents(")) { - val dataJson = - script.data().substringAfter("false, {").substringBefore("});") - tryParseJson("{$dataJson}")?.let { source -> - invokeSources( - this.name, - source.streams, - source.subtitle.toString(), - subtitleCallback, - callback - ) - } - } - } - } else { - res.server?.apmap { server -> - app.post( - url = "$mainUrl/ajax/get_cdn_series/?t=${Date().time}", - data = mapOf( - "id" to res.id, - "translator_id" to server.translator_id, - "favs" to res.favs, - "is_camrip" to server.camrip, - "is_ads" to server.ads, - "is_director" to server.director, - "season" to res.season, - "episode" to res.episode, - "action" to res.action, - ).filterValues { it != null }.mapValues { it.value as String }, - referer = res.ref - ).parsedSafe()?.let { source -> - invokeSources( - server.translator_name.toString(), - source.url, - source.subtitle.toString(), - subtitleCallback, - callback - ) - } - } - } - } - - return true - } - - data class LocalSources( - @JsonProperty("streams") val streams: String, - @JsonProperty("subtitle") val subtitle: Any?, - ) - - data class Sources( - @JsonProperty("url") val url: String, - @JsonProperty("subtitle") val subtitle: Any?, - ) - - data class Server( - @JsonProperty("translator_name") val translator_name: String?, - @JsonProperty("translator_id") val translator_id: String?, - @JsonProperty("camrip") val camrip: String?, - @JsonProperty("ads") val ads: String?, - @JsonProperty("director") val director: String?, - ) - - data class Data( - @JsonProperty("id") val id: String?, - @JsonProperty("favs") val favs: String?, - @JsonProperty("server") val server: List?, - @JsonProperty("season") val season: String?, - @JsonProperty("episode") val episode: String?, - @JsonProperty("action") val action: String?, - @JsonProperty("ref") val ref: String?, - ) - - data class Trailer( - @JsonProperty("success") val success: Boolean?, - @JsonProperty("code") val code: String?, - ) - -} \ No newline at end of file diff --git a/HDrezkaProvider/src/main/kotlin/com/hexated/HDrezkaProviderPlugin.kt b/HDrezkaProvider/src/main/kotlin/com/hexated/HDrezkaProviderPlugin.kt deleted file mode 100644 index 7b7f122c..00000000 --- a/HDrezkaProvider/src/main/kotlin/com/hexated/HDrezkaProviderPlugin.kt +++ /dev/null @@ -1,14 +0,0 @@ - -package com.hexated - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class HDrezkaProviderPlugin: Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(HDrezkaProvider()) - } -} \ No newline at end of file diff --git a/Hdfilmcehennemi/build.gradle.kts b/Hdfilmcehennemi/build.gradle.kts deleted file mode 100644 index 008bccce..00000000 --- a/Hdfilmcehennemi/build.gradle.kts +++ /dev/null @@ -1,26 +0,0 @@ -// use an integer for version numbers -version = 9 - - -cloudstream { - language = "tr" - // All of these properties are optional, you can safely remove them - - // description = "Lorem Ipsum" - authors = listOf("Hexated") - - /** - * Status int as the following: - * 0: Down - * 1: Ok - * 2: Slow - * 3: Beta only - * */ - status = 1 // will be 3 if unspecified - tvTypes = listOf( - "TvSeries", - "Movie", - ) - - iconUrl = "https://www.google.com/s2/favicons?domain=hdfilmcehennemi.live&sz=%size%" -} \ No newline at end of file diff --git a/Hdfilmcehennemi/src/main/AndroidManifest.xml b/Hdfilmcehennemi/src/main/AndroidManifest.xml deleted file mode 100644 index c98063f8..00000000 --- a/Hdfilmcehennemi/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/Hdfilmcehennemi/src/main/kotlin/com/hexated/Hdfilmcehennemi.kt b/Hdfilmcehennemi/src/main/kotlin/com/hexated/Hdfilmcehennemi.kt deleted file mode 100644 index ff9b2b60..00000000 --- a/Hdfilmcehennemi/src/main/kotlin/com/hexated/Hdfilmcehennemi.kt +++ /dev/null @@ -1,240 +0,0 @@ -package com.hexated - -import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.LoadResponse.Companion.addActors -import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer -import com.lagradost.cloudstream3.mvvm.safeApiCall -import com.lagradost.cloudstream3.utils.* -import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson -import org.jsoup.nodes.Element - -class Hdfilmcehennemi : MainAPI() { - override var mainUrl = "https://www.hdfilmcehennemi.life" - override var name = "hdfilmcehennemi" - override val hasMainPage = true - override var lang = "tr" - override val hasQuickSearch = true - override val hasDownloadSupport = true - override val supportedTypes = setOf( - TvType.Movie, - TvType.TvSeries, - ) - - override val mainPage = mainPageOf( - "$mainUrl/category/tavsiye-filmler-izle2/page/" to "Tavsiye Filmler Kategorisi", - "$mainUrl/yabancidiziizle-1/page/" to "Son Eklenen Yabancı Diziler", - "$mainUrl/imdb-7-puan-uzeri-filmler/page/" to "Imdb 7+ Filmler", - "$mainUrl/en-cok-yorumlananlar/page/" to "En Çok Yorumlananlar", - "$mainUrl/en-cok-begenilen-filmleri-izle/page/" to "En Çok Beğenilenler", - ) - - override suspend fun getMainPage( - page: Int, - request: MainPageRequest - ): HomePageResponse { - val document = app.get(request.data + page).document - val home = document.select("div.card-body div.row div.col-6.col-sm-3.poster-container") - .mapNotNull { - it.toSearchResult() - } - return newHomePageResponse(request.name, home) - } - - private fun Element.toSearchResult(): SearchResponse? { - val title = this.selectFirst("h2.title")?.text() ?: return null - val href = fixUrlNull(this.selectFirst("a")?.attr("href")) ?: return null - val posterUrl = fixUrlNull(this.selectFirst("img")?.attr("data-src")) - return newMovieSearchResponse(title, href, TvType.Movie) { - this.posterUrl = posterUrl - } - - } - - private fun Media.toSearchResponse(): SearchResponse? { - return newMovieSearchResponse( - title ?: return null, - "$mainUrl/$slugPrefix$slug", - TvType.TvSeries, - ) { - this.posterUrl = "$mainUrl/uploads/poster/$poster" - } - } - - override suspend fun quickSearch(query: String): List = search(query) - - override suspend fun search(query: String): List { - return app.post( - "$mainUrl/search/", - data = mapOf("query" to query), - referer = "$mainUrl/", - headers = mapOf( - "Accept" to "application/json, text/javascript, */*; q=0.01", - "X-Requested-With" to "XMLHttpRequest" - ) - ).parsedSafe()?.result?.mapNotNull { media -> - media.toSearchResponse() - } ?: throw ErrorLoadingException("Invalid Json reponse") - } - - override suspend fun load(url: String): LoadResponse? { - val document = app.get(url).document - - val title = document.selectFirst("div.card-header > h1, div.card-header > h2")?.text() - ?.removeSuffix("Filminin Bilgileri")?.trim() - ?: return null - val poster = fixUrlNull(document.selectFirst("img.img-fluid")?.attr("src")) - val tags = document.select("div.mb-0.lh-lg div:nth-child(5) a").map { it.text() } - val year = - document.selectFirst("div.mb-0.lh-lg div:nth-child(4) a")?.text()?.trim()?.toIntOrNull() - val tvType = if (document.select("nav#seasonsTabs").isNullOrEmpty() - ) TvType.Movie else TvType.TvSeries - val description = document.selectFirst("article.text-white > p")?.text()?.trim() - val rating = document.selectFirst("div.rating-votes div.rate span")?.text()?.toRatingInt() - val actors = document.select("div.mb-0.lh-lg div:last-child a.chip").map { - Actor(it.text(), it.select("img").attr("src")) - } - val recommendations = - document.select("div.swiper-wrapper div.poster.poster-pop").mapNotNull { - val recName = it.selectFirst("h2.title")?.text() ?: return@mapNotNull null - val recHref = - fixUrlNull(it.selectFirst("a")?.attr("href")) ?: return@mapNotNull null - val recPosterUrl = fixUrlNull(it.selectFirst("img")?.attr("data-src")) - newTvSeriesSearchResponse(recName, recHref, TvType.TvSeries) { - this.posterUrl = recPosterUrl - } - } - - return if (tvType == TvType.TvSeries) { - val trailer = - document.selectFirst("button.btn.btn-fragman.btn-danger")?.attr("data-trailer") - ?.let { - "https://www.youtube.com/embed/$it" - } - val episodes = document.select("div#seasonsTabs-tabContent div.card-list-item").map { - val href = it.select("a").attr("href") - val name = it.select("h3").text().trim() - val episode = it.select("h3").text().let { num -> - Regex("Sezon\\s?([0-9]+).").find(num)?.groupValues?.getOrNull(1)?.toIntOrNull() - } - val season = it.parents()[1].attr("id").substringAfter("-").toIntOrNull() - Episode( - href, - name, - season, - episode, - ) - } - newTvSeriesLoadResponse(title, url, TvType.TvSeries, episodes) { - this.posterUrl = poster - this.year = year - this.plot = description - this.tags = tags - this.rating = rating - addActors(actors) - this.recommendations = recommendations - addTrailer(trailer) - } - } else { - val trailer = - document.selectFirst("nav.nav.card-nav.nav-slider a[data-bs-toggle=\"modal\"]") - ?.attr("data-trailer")?.let { - "https://www.youtube.com/embed/$it" - } - newMovieLoadResponse(title, url, TvType.Movie, url) { - this.posterUrl = poster - this.year = year - this.plot = description - this.tags = tags - this.rating = rating - addActors(actors) - this.recommendations = recommendations - addTrailer(trailer) - } - } - } - - private fun String.addMarks(str: String): String { - return this.replace(Regex("\"?$str\"?"), "\"$str\"") - } - - private suspend fun invokeLocalSource( - source: String, - url: String, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ) { - val script = app.get( - url, - referer = "${mainUrl}/" - ).document.select("script") - .find { it.data().contains("sources:") }?.data() ?: return - val videoData = getAndUnpack(script).substringAfter("file_link=\"").substringBefore("\";") - val subData = script.substringAfter("tracks: [").substringBefore("]") - - callback.invoke( - ExtractorLink( - source, - source, - base64Decode(videoData), - "$mainUrl/", - Qualities.Unknown.value, - true - ) - ) - - tryParseJson>("[${subData}]") - ?.filter { it.kind == "captions" }?.map { - subtitleCallback.invoke( - SubtitleFile( - it.label.toString(), - fixUrl(it.file.toString()) - ) - ) - } - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - app.get(data).document.select("nav.nav.card-nav.nav-slider a.nav-link").map { - Pair(it.attr("href"), it.text()) - }.apmap { (url, source) -> - safeApiCall { - app.get(url).document.select("div.card-video > iframe").attr("data-src") - .let { link -> - if (link.startsWith(mainUrl)) { - invokeLocalSource(source, link, subtitleCallback, callback) - } else { - loadExtractor(link, "$mainUrl/", subtitleCallback, callback) - } - } - } - } - return true - } - - private data class Source( - @JsonProperty("file") val file: String? = null, - ) - - private data class SubSource( - @JsonProperty("file") val file: String? = null, - @JsonProperty("label") val label: String? = null, - @JsonProperty("kind") val kind: String? = null, - ) - - data class Result( - @JsonProperty("result") val result: ArrayList? = arrayListOf(), - ) - - data class Media( - @JsonProperty("title") val title: String? = null, - @JsonProperty("poster") val poster: String? = null, - @JsonProperty("slug") val slug: String? = null, - @JsonProperty("slug_prefix") val slugPrefix: String? = null, - ) -} \ No newline at end of file diff --git a/Hdfilmcehennemi/src/main/kotlin/com/hexated/HdfilmcehennemiPlugin.kt b/Hdfilmcehennemi/src/main/kotlin/com/hexated/HdfilmcehennemiPlugin.kt deleted file mode 100644 index 257679b0..00000000 --- a/Hdfilmcehennemi/src/main/kotlin/com/hexated/HdfilmcehennemiPlugin.kt +++ /dev/null @@ -1,14 +0,0 @@ - -package com.hexated - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class HdfilmcehennemiPlugin: Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(Hdfilmcehennemi()) - } -} \ No newline at end of file diff --git a/Hentaiheaven/build.gradle.kts b/Hentaiheaven/build.gradle.kts deleted file mode 100644 index 017bc97a..00000000 --- a/Hentaiheaven/build.gradle.kts +++ /dev/null @@ -1,25 +0,0 @@ -// use an integer for version numbers -version = 3 - - -cloudstream { - language = "en" - // All of these properties are optional, you can safely remove them - - // description = "Lorem Ipsum" - authors = listOf("Sora") - - /** - * Status int as the following: - * 0: Down - * 1: Ok - * 2: Slow - * 3: Beta only - * */ - status = 1 // will be 3 if unspecified - tvTypes = listOf( - "NSFW", - ) - - iconUrl = "https://www.google.com/s2/favicons?domain=hentaihaven.xxx&sz=%size%" -} \ No newline at end of file diff --git a/Hentaiheaven/src/main/AndroidManifest.xml b/Hentaiheaven/src/main/AndroidManifest.xml deleted file mode 100644 index c98063f8..00000000 --- a/Hentaiheaven/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/Hentaiheaven/src/main/kotlin/com/hexated/Hentaiheaven.kt b/Hentaiheaven/src/main/kotlin/com/hexated/Hentaiheaven.kt deleted file mode 100644 index aea799a0..00000000 --- a/Hentaiheaven/src/main/kotlin/com/hexated/Hentaiheaven.kt +++ /dev/null @@ -1,166 +0,0 @@ -package com.hexated - -import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer -import com.lagradost.cloudstream3.utils.* -import okhttp3.FormBody -import org.jsoup.nodes.Element - -class Hentaiheaven : MainAPI() { - override var mainUrl = "https://hentaihaven.xxx" - override var name = "Hentaiheaven" - override val hasMainPage = true - override var lang = "en" - override val hasDownloadSupport = true - - override val supportedTypes = setOf( - TvType.NSFW) - - override val mainPage = mainPageOf( - "?m_orderby=new-manga" to "New", - "?m_orderby=views" to "Most Views", - "?m_orderby=rating" to "Rating", - "?m_orderby=alphabet" to "A-Z", - ) - - override suspend fun getMainPage( - page: Int, - request: MainPageRequest - ): HomePageResponse { - val document = app.get("$mainUrl/page/$page/${request.data}").document - val home = - document.select("div.page-listing-item div.col-6.col-md-zarat.badge-pos-1").mapNotNull { - it.toSearchResult() - } - return newHomePageResponse(request.name, home) - } - - private fun Element.toSearchResult(): AnimeSearchResponse? { - val href = fixUrl(this.selectFirst("a")!!.attr("href")) - val title = - this.selectFirst("h3 a, h5 a")?.text()?.trim() ?: this.selectFirst("a")?.attr("title") - ?: return null - val posterUrl = fixUrlNull(this.selectFirst("img")?.attr("src")) - val episode = this.selectFirst("span.chapter.font-meta a")?.text()?.filter { it.isDigit() } - ?.toIntOrNull() - - return newAnimeSearchResponse(title, href, TvType.Anime) { - this.posterUrl = posterUrl - addSub(episode) - } - } - - override suspend fun search(query: String): List { - val link = "$mainUrl/?s=$query&post_type=wp-manga" - val document = app.get(link).document - - return document.select("div.c-tabs-item div.row.c-tabs-item__content").mapNotNull { - it.toSearchResult() - } - } - - override suspend fun load(url: String): LoadResponse? { - val document = app.get(url).document - - val title = document.selectFirst("div.post-title h1")?.text()?.trim() ?: return null - val poster = document.select("div.summary_image img").attr("src") - val tags = document.select("div.genres-content > a").map { it.text() } - - val description = document.select("div.description-summary p").text().trim() - val trailer = document.selectFirst("a.trailerbutton")?.attr("href") - - val episodes = document.select("div.listing-chapters_wrap ul li").mapNotNull { - val name = it.selectFirst("a")?.text() ?: return@mapNotNull null - val image = fixUrlNull(it.selectFirst("a img")?.attr("src")) - val link = fixUrlNull(it.selectFirst("a")?.attr("href")) ?: return@mapNotNull null - Episode(link, name, posterUrl = image) - }.reversed() - - val recommendations = - document.select("div.row div.col-6.col-md-zarat").mapNotNull { - it.toSearchResult() - } - - return newAnimeLoadResponse(title, url, TvType.NSFW) { - engName = title - posterUrl = poster - addEpisodes(DubStatus.Subbed, episodes) - plot = description - this.tags = tags - this.recommendations = recommendations - addTrailer(trailer) - } - - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - - val doc = app.get(data).document - val meta = doc.selectFirst("meta[itemprop=thumbnailUrl]")?.attr("content")?.substringAfter("/hh/")?.substringBefore("/") ?: return false - doc.select("div.player_logic_item iframe").attr("src").let { iframe -> - val document = app.get(iframe, referer = data).text - val en = Regex("var\\sen\\s=\\s'(\\S+)';").find(document)?.groupValues?.getOrNull(1) - val iv = Regex("var\\siv\\s=\\s'(\\S+)';").find(document)?.groupValues?.getOrNull(1) - - val body = FormBody.Builder() - .addEncoded("action", "zarat_get_data_player_ajax") - .addEncoded("a", "$en") - .addEncoded("b", "$iv") - .build() - - app.post( - "$mainUrl/wp-content/plugins/player-logic/api.php", -// data = mapOf( -// "action" to "zarat_get_data_player_ajax", -// "a" to "$en", -// "b" to "$iv" -// ), - requestBody = body, -// headers = mapOf("Sec-Fetch-Mode" to "cors") - ).parsedSafe()?.data?.sources?.map { res -> -// M3u8Helper.generateM3u8( -// this.name, -// res.src ?: return@map null, -// referer = "$mainUrl/", -// headers = mapOf( -// "Origin" to mainUrl, -// ) -// ).forEach(callback) - callback.invoke( - ExtractorLink( - this.name, - this.name, - res.src?.replace("/hh//", "/hh/$meta/") ?: return@map null, - referer = "", - quality = Qualities.Unknown.value, - isM3u8 = true - ) - ) - } - } - - return true - } - - data class Response( - @JsonProperty("data") val data: Data? = null, - ) - - data class Data( - @JsonProperty("sources") val sources: ArrayList? = arrayListOf(), - ) - - data class Sources( - @JsonProperty("src") val src: String? = null, - @JsonProperty("type") val type: String? = null, - @JsonProperty("label") val label: String? = null, - ) - - -} \ No newline at end of file diff --git a/Hentaiheaven/src/main/kotlin/com/hexated/HentaiheavenPlugin.kt b/Hentaiheaven/src/main/kotlin/com/hexated/HentaiheavenPlugin.kt deleted file mode 100644 index d51b5849..00000000 --- a/Hentaiheaven/src/main/kotlin/com/hexated/HentaiheavenPlugin.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.hexated - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class HentaiheavenPlugin: Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(Hentaiheaven()) - } -} \ No newline at end of file diff --git a/IMoviehd/build.gradle.kts b/IMoviehd/build.gradle.kts deleted file mode 100644 index 54ae7e8d..00000000 --- a/IMoviehd/build.gradle.kts +++ /dev/null @@ -1,26 +0,0 @@ -// use an integer for version numbers -version = 1 - - -cloudstream { - language = "th" - // All of these properties are optional, you can safely remove them - - // description = "Lorem Ipsum" - authors = listOf("Hexated") - - /** - * Status int as the following: - * 0: Down - * 1: Ok - * 2: Slow - * 3: Beta only - * */ - status = 1 // will be 3 if unspecified - tvTypes = listOf( - "TvSeries", - "Movie", - ) - - iconUrl = "https://www.google.com/s2/favicons?domain=www.i-moviehd.com&sz=%size%" -} \ No newline at end of file diff --git a/IMoviehd/src/main/AndroidManifest.xml b/IMoviehd/src/main/AndroidManifest.xml deleted file mode 100644 index c98063f8..00000000 --- a/IMoviehd/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/IMoviehd/src/main/kotlin/com/hexated/IMoviehd.kt b/IMoviehd/src/main/kotlin/com/hexated/IMoviehd.kt deleted file mode 100644 index 314a4e33..00000000 --- a/IMoviehd/src/main/kotlin/com/hexated/IMoviehd.kt +++ /dev/null @@ -1,169 +0,0 @@ -package com.hexated - -import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.LoadResponse.Companion.addActors -import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer -import com.lagradost.cloudstream3.mvvm.safeApiCall -import com.lagradost.cloudstream3.utils.* -import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson -import org.jsoup.nodes.Element -import java.net.URI - -class IMoviehd : MainAPI() { - override var mainUrl = "https://www.i-moviehd.com" - override var name = "I-Moviehd" - override val hasMainPage = true - override var lang = "th" - override val hasDownloadSupport = true - override val supportedTypes = setOf( - TvType.Movie, - TvType.TvSeries, - ) - - override val mainPage = mainPageOf( - "$mainUrl/page/" to "RECOMMENDATION", - "$mainUrl/category/series-ซีรี่ส์/page/" to "NEW SERIES", - "$mainUrl/top-movie/page/" to "TOP MOVIES IMDB", - "$mainUrl/top-series/page/" to "TOP SERIES IMDB", - ) - - override suspend fun getMainPage( - page: Int, - request: MainPageRequest - ): HomePageResponse { - val document = app.get(request.data + page).document - val home = document.select("div.item-wrap.clearfix div.item").mapNotNull { - it.toSearchResult() - } - return newHomePageResponse(fixTitle(request.name), home) - } - - private fun getProperAnimeLink(uri: String): String { - return if (uri.substringAfter("$mainUrl/").contains("-ep-")) { - val title = uri.substringAfter("$mainUrl/").replace(Regex("-ep-[0-9]+"), "") - "$mainUrl/$title" - } else { - uri - } - } - - private fun Element.toSearchResult(): SearchResponse? { - val title = this.selectFirst("a")?.attr("title") ?: return null - val href = getProperAnimeLink(this.selectFirst("a")!!.attr("href")) - val posterUrl = fixUrlNull(this.select("img").attr("src")) - return newMovieSearchResponse(title, href, TvType.Movie) { - this.posterUrl = posterUrl - } - - } - - override suspend fun search(query: String): List { - val document = app.get("$mainUrl/?s=$query").document - - return document.select("div.item-wrap.clearfix div.item").mapNotNull { - it.toSearchResult() - } - } - - override suspend fun load(url: String): LoadResponse? { - val document = app.get(url).document - - val title = document.selectFirst("h1.entry-title")?.text() ?: return null - val poster = document.selectFirst("table#imdbinfo td img")?.attr("src") - val tags = document.select("span.categories > a").map { it.text() } - - val tvType = if (document.select("table#Sequel").isNullOrEmpty() - ) TvType.Movie else TvType.TvSeries - val description = document.select("div.entry-content.post_content p").text().trim() - val trailer = document.selectFirst("div#tabt iframe")?.attr("sub_src") - val rating = document.selectFirst("div.imdb-rating-content span")?.text()?.toRatingInt() - - val recommendations = document.select("div.item-wrap.clearfix div.item").mapNotNull { - it.toSearchResult() - } - - return if (tvType == TvType.TvSeries) { - val episodes = document.select("table#Sequel tbody tr").mapNotNull { - val href = fixUrl(it.selectFirst("a")?.attr("href") ?: return null) - val name = it.selectFirst("a")?.text()?.trim() ?: return null - Episode( - href, - name, - ) - } - newTvSeriesLoadResponse(title, url, TvType.TvSeries, episodes) { - this.posterUrl = poster - this.plot = description - this.tags = tags - this.rating = rating - this.recommendations = recommendations - addTrailer(trailer) - } - } else { - newMovieLoadResponse(title, url, TvType.Movie, url) { - this.posterUrl = poster - this.plot = description - this.tags = tags - this.rating = rating - this.recommendations = recommendations - addTrailer(trailer) - } - } - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - - val document = app.get(data).document - - document.select("script").find { it.data().contains("\$.getJSON(") }?.data() - ?.substringAfter("\$.getJSON( \"")?.substringBefore("\"+")?.let { iframe -> - val server = app.get("$iframe\\0&b=", referer = "$mainUrl/") - .parsedSafe()?.link - val id = app.post( - "https://vlp.77player.xyz/initPlayer/${server?.substringAfter("key=")}", - referer = server, - headers = mapOf("X-Requested-With" to "XMLHttpRequest") - ).parsedSafe()?.data?.substringAfter("id=") - -// M3u8Helper.generateM3u8( -// this.name, -// "https://xxx.77player.xyz/iosplaylist/$id/$id.m3u8", -// referer = "$mainUrl/", -// headers = mapOf( -//// "Origin" to "https://xxx.77player.xyz", -// ) -// ).forEach(callback) - - callback.invoke( - ExtractorLink( - this.name, - this.name, - "https://xxx.77player.xyz/iosplaylist/$id/$id.m3u8", - referer = "$mainUrl/", - quality = Qualities.Unknown.value, - isM3u8 = true - ) - ) - - } - - - return true - } - - data class Server( - @JsonProperty("ตัวเล่นไว") val link: String?, - ) - - data class Source( - @JsonProperty("data") val data: String?, - ) - - -} \ No newline at end of file diff --git a/IMoviehd/src/main/kotlin/com/hexated/IMoviehdPlugin.kt b/IMoviehd/src/main/kotlin/com/hexated/IMoviehdPlugin.kt deleted file mode 100644 index a416a029..00000000 --- a/IMoviehd/src/main/kotlin/com/hexated/IMoviehdPlugin.kt +++ /dev/null @@ -1,14 +0,0 @@ - -package com.hexated - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class IMoviehdPlugin: Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(IMoviehd()) - } -} \ No newline at end of file diff --git a/IdlixProvider/build.gradle.kts b/IdlixProvider/build.gradle.kts deleted file mode 100644 index e2fe47d4..00000000 --- a/IdlixProvider/build.gradle.kts +++ /dev/null @@ -1,28 +0,0 @@ -// use an integer for version numbers -version = 10 - - -cloudstream { - language = "id" - // All of these properties are optional, you can safely remove them - - // description = "Lorem Ipsum" - authors = listOf("Hexated") - - /** - * Status int as the following: - * 0: Down - * 1: Ok - * 2: Slow - * 3: Beta only - * */ - status = 1 // will be 3 if unspecified - tvTypes = listOf( - "TvSeries", - "Movie", - "Anime", - "AsianDrama", - ) - - iconUrl = "https://www.google.com/s2/favicons?domain=109.234.36.69&sz=%size%" -} \ No newline at end of file diff --git a/IdlixProvider/src/main/AndroidManifest.xml b/IdlixProvider/src/main/AndroidManifest.xml deleted file mode 100644 index c98063f8..00000000 --- a/IdlixProvider/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/IdlixProvider/src/main/kotlin/com/hexated/IdlixProvider.kt b/IdlixProvider/src/main/kotlin/com/hexated/IdlixProvider.kt deleted file mode 100644 index 56caefbc..00000000 --- a/IdlixProvider/src/main/kotlin/com/hexated/IdlixProvider.kt +++ /dev/null @@ -1,228 +0,0 @@ -package com.hexated - -import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.LoadResponse.Companion.addActors -import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer -import com.lagradost.cloudstream3.mvvm.safeApiCall -import com.lagradost.cloudstream3.utils.* -import org.jsoup.nodes.Element -import java.net.URI - -class IdlixProvider : MainAPI() { - override var mainUrl = "https://idlixian.com" - private var directUrl = mainUrl - override var name = "Idlix" - override val hasMainPage = true - override var lang = "id" - override val hasDownloadSupport = true - override val supportedTypes = setOf( - TvType.Movie, - TvType.TvSeries, - TvType.Anime, - TvType.AsianDrama - ) - - override val mainPage = mainPageOf( - "$mainUrl/" to "Featured", - "$mainUrl/trending/page/?get=movies" to "Trending Movies", - "$mainUrl/trending/page/?get=tv" to "Trending TV Series", - "$mainUrl/movie/page/" to "Movie Terbaru", - "$mainUrl/tvseries/page/" to "TV Series Terbaru", - "$mainUrl/network/netflix/page/" to "Netflix", - "$mainUrl/genre/anime/page/" to "Anime", - "$mainUrl/genre/drama-korea/page/" to "Drama Korea", - ) - - private fun getBaseUrl(url: String): String { - return URI(url).let { - "${it.scheme}://${it.host}" - } - } - - override suspend fun getMainPage( - page: Int, - request: MainPageRequest - ): HomePageResponse { - val url = request.data.split("?") - val nonPaged = request.name == "Featured" && page <= 1 - val req = if (nonPaged) { - app.get(request.data) - } else { - app.get("${url.first()}$page/?${url.lastOrNull()}") - } - mainUrl = getBaseUrl(req.url) - val document = req.document - val home = (if (nonPaged) { - document.select("div.items.featured article") - } else { - document.select("div.items.full article, div#archive-content article") - }).mapNotNull { - it.toSearchResult() - } - return newHomePageResponse(request.name, home) - } - - private fun getProperLink(uri: String): String { - return when { - uri.contains("/episode/") -> { - var title = uri.substringAfter("$mainUrl/episode/") - title = Regex("(.+?)-season").find(title)?.groupValues?.get(1).toString() - "$mainUrl/tvseries/$title" - } - uri.contains("/season/") -> { - var title = uri.substringAfter("$mainUrl/season/") - title = Regex("(.+?)-season").find(title)?.groupValues?.get(1).toString() - "$mainUrl/tvseries/$title" - } - else -> { - uri - } - } - } - - private fun Element.toSearchResult(): SearchResponse { - val title = this.selectFirst("h3 > a")!!.text().replace(Regex("\\(\\d{4}\\)"), "").trim() - val href = getProperLink(this.selectFirst("h3 > a")!!.attr("href")) - val posterUrl = this.select("div.poster > img").attr("src").toString() - val quality = getQualityFromString(this.select("span.quality").text()) - return newMovieSearchResponse(title, href, TvType.Movie) { - this.posterUrl = posterUrl - this.quality = quality - } - - } - - override suspend fun search(query: String): List { - val req = app.get("$mainUrl/search/$query") - mainUrl = getBaseUrl(req.url) - val document = req.document - return document.select("div.result-item").map { - val title = - it.selectFirst("div.title > a")!!.text().replace(Regex("\\(\\d{4}\\)"), "").trim() - val href = getProperLink(it.selectFirst("div.title > a")!!.attr("href")) - val posterUrl = it.selectFirst("img")!!.attr("src").toString() - newMovieSearchResponse(title, href, TvType.TvSeries) { - this.posterUrl = posterUrl - } - } - } - - override suspend fun load(url: String): LoadResponse { - val request = app.get(url) - directUrl = getBaseUrl(request.url) - val document = request.document - val title = - document.selectFirst("div.data > h1")?.text()?.replace(Regex("\\(\\d{4}\\)"), "") - ?.trim().toString() - val poster = document.select("div.poster > img").attr("src").toString() - val tags = document.select("div.sgeneros > a").map { it.text() } - - val year = Regex(",\\s?(\\d+)").find( - document.select("span.date").text().trim() - )?.groupValues?.get(1).toString().toIntOrNull() - val tvType = if (document.select("ul#section > li:nth-child(1)").text().contains("Episodes") - ) TvType.TvSeries else TvType.Movie - val description = document.select("div.wp-content > p").text().trim() - val trailer = document.selectFirst("div.embed iframe")?.attr("src") - val rating = - document.selectFirst("span.dt_rating_vgs")?.text()?.toRatingInt() - val actors = document.select("div.persons > div[itemprop=actor]").map { - Actor(it.select("meta[itemprop=name]").attr("content"), it.select("img").attr("src")) - } - - val recommendations = document.select("div.owl-item").map { - val recName = - it.selectFirst("a")!!.attr("href").toString().removeSuffix("/").split("/").last() - val recHref = it.selectFirst("a")!!.attr("href") - val recPosterUrl = it.selectFirst("img")?.attr("src").toString() - newTvSeriesSearchResponse(recName, recHref, TvType.TvSeries) { - this.posterUrl = recPosterUrl - } - } - - return if (tvType == TvType.TvSeries) { - val episodes = document.select("ul.episodios > li").map { - val href = it.select("a").attr("href") - val name = fixTitle(it.select("div.episodiotitle > a").text().trim()) - val image = it.select("div.imagen > img").attr("src") - val episode = it.select("div.numerando").text().replace(" ", "").split("-").last() - .toIntOrNull() - val season = it.select("div.numerando").text().replace(" ", "").split("-").first() - .toIntOrNull() - Episode( - href, - name, - season, - episode, - image - ) - } - newTvSeriesLoadResponse(title, url, TvType.TvSeries, episodes) { - this.posterUrl = poster - this.year = year - this.plot = description - this.tags = tags - this.rating = rating - addActors(actors) - this.recommendations = recommendations - addTrailer(trailer) - } - } else { - newMovieLoadResponse(title, url, TvType.Movie, url) { - this.posterUrl = poster - this.year = year - this.plot = description - this.tags = tags - this.rating = rating - addActors(actors) - this.recommendations = recommendations - addTrailer(trailer) - } - } - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - - val document = app.get(data).document - val id = document.select("meta#dooplay-ajax-counter").attr("data-postid") - val type = if (data.contains("/movie/")) "movie" else "tv" - - document.select("ul#playeroptionsul > li").map { - it.attr("data-nume") - }.apmap { nume -> - safeApiCall { - var source = app.post( - url = "$directUrl/wp-admin/admin-ajax.php", - data = mapOf( - "action" to "doo_player_ajax", - "post" to id, - "nume" to nume, - "type" to type - ), - headers = mapOf("X-Requested-With" to "XMLHttpRequest"), - referer = data - ).parsed().embed_url - - if (source.startsWith("https://uservideo.xyz")) { - source = app.get(source).document.select("iframe").attr("src") - } - loadExtractor(source, directUrl, subtitleCallback, callback) - - } - } - - return true - } - - data class ResponseHash( - @JsonProperty("embed_url") val embed_url: String, - @JsonProperty("type") val type: String?, - ) - -} \ No newline at end of file diff --git a/IdlixProvider/src/main/kotlin/com/hexated/IdlixProviderPlugin.kt b/IdlixProvider/src/main/kotlin/com/hexated/IdlixProviderPlugin.kt deleted file mode 100644 index 115b22a6..00000000 --- a/IdlixProvider/src/main/kotlin/com/hexated/IdlixProviderPlugin.kt +++ /dev/null @@ -1,14 +0,0 @@ - -package com.hexated - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class IdlixProviderPlugin: Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(IdlixProvider()) - } -} \ No newline at end of file diff --git a/Kickassanime/build.gradle.kts b/Kickassanime/build.gradle.kts deleted file mode 100644 index 562899be..00000000 --- a/Kickassanime/build.gradle.kts +++ /dev/null @@ -1,27 +0,0 @@ -// use an integer for version numbers -version = 10 - - -cloudstream { - language = "en" - // All of these properties are optional, you can safely remove them - - // description = "Lorem Ipsum" - authors = listOf("Hexated") - - /** - * Status int as the following: - * 0: Down - * 1: Ok - * 2: Slow - * 3: Beta only - * */ - status = 1 // will be 3 if unspecified - tvTypes = listOf( - "AnimeMovie", - "Anime", - "OVA", - ) - - iconUrl = "https://www.google.com/s2/favicons?domain=www2.kickassanime.ro&sz=%size%" -} \ No newline at end of file diff --git a/Kickassanime/src/main/AndroidManifest.xml b/Kickassanime/src/main/AndroidManifest.xml deleted file mode 100644 index 874740e3..00000000 --- a/Kickassanime/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/Kickassanime/src/main/kotlin/com/hexated/GogoExtractor.kt b/Kickassanime/src/main/kotlin/com/hexated/GogoExtractor.kt deleted file mode 100644 index ed19fd25..00000000 --- a/Kickassanime/src/main/kotlin/com/hexated/GogoExtractor.kt +++ /dev/null @@ -1,166 +0,0 @@ -package com.hexated - -import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.cloudstream3.app -import com.lagradost.cloudstream3.base64Decode -import com.lagradost.cloudstream3.base64DecodeArray -import com.lagradost.cloudstream3.base64Encode -import com.lagradost.cloudstream3.mvvm.normalSafeApiCall -import com.lagradost.cloudstream3.mvvm.safeApiCall -import com.lagradost.cloudstream3.utils.AppUtils -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.M3u8Helper -import com.lagradost.cloudstream3.utils.getQualityFromName -import org.jsoup.nodes.Document -import java.net.URI -import javax.crypto.Cipher -import javax.crypto.spec.IvParameterSpec -import javax.crypto.spec.SecretKeySpec - -object GogoExtractor { - - /** - * @param id base64Decode(show_id) + IV - * @return the encryption key - * */ - private fun getKey(id: String): String? { - return normalSafeApiCall { - id.map { - it.code.toString(16) - }.joinToString("").substring(0, 32) - } - } - - // https://github.com/saikou-app/saikou/blob/3e756bd8e876ad7a9318b17110526880525a5cd3/app/src/main/java/ani/saikou/anime/source/extractors/GogoCDN.kt#L60 - // No Licence on the function - private fun cryptoHandler( - string: String, - iv: String, - secretKeyString: String, - encrypt: Boolean = true - ): String { - //println("IV: $iv, Key: $secretKeyString, encrypt: $encrypt, Message: $string") - val ivParameterSpec = IvParameterSpec(iv.toByteArray()) - val secretKey = SecretKeySpec(secretKeyString.toByteArray(), "AES") - val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding") - return if (!encrypt) { - cipher.init(Cipher.DECRYPT_MODE, secretKey, ivParameterSpec) - String(cipher.doFinal(base64DecodeArray(string))) - } else { - cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivParameterSpec) - base64Encode(cipher.doFinal(string.toByteArray())) - } - } - - /** - * @param iframeUrl something like https://gogoplay4.com/streaming.php?id=XXXXXX - * @param mainApiName used for ExtractorLink names and source - * @param iv secret iv from site, required non-null if isUsingAdaptiveKeys is off - * @param secretKey secret key for decryption from site, required non-null if isUsingAdaptiveKeys is off - * @param secretDecryptKey secret key to decrypt the response json, required non-null if isUsingAdaptiveKeys is off - * @param isUsingAdaptiveKeys generates keys from IV and ID, see getKey() - * @param isUsingAdaptiveData generate encrypt-ajax data based on $("script[data-name='episode']")[0].dataset.value - * */ - suspend fun extractVidstream( - iframeUrl: String, - mainApiName: String, - callback: (ExtractorLink) -> Unit, - iv: String?, - secretKey: String?, - secretDecryptKey: String?, - // This could be removed, but i prefer it verbose - isUsingAdaptiveKeys: Boolean, - isUsingAdaptiveData: Boolean, - // If you don't want to re-fetch the document - iframeDocument: Document? = null - ) = safeApiCall { - // https://github.com/saikou-app/saikou/blob/3e756bd8e876ad7a9318b17110526880525a5cd3/app/src/main/java/ani/saikou/anime/source/extractors/GogoCDN.kt - // No Licence on the following code - // Also modified of https://github.com/jmir1/aniyomi-extensions/blob/master/src/en/gogoanime/src/eu/kanade/tachiyomi/animeextension/en/gogoanime/extractors/GogoCdnExtractor.kt - // License on the code above https://github.com/jmir1/aniyomi-extensions/blob/master/LICENSE - - if ((iv == null || secretKey == null || secretDecryptKey == null) && !isUsingAdaptiveKeys) - return@safeApiCall - - val id = Regex("id=([^&]+)").find(iframeUrl)!!.value.removePrefix("id=") - - var document: Document? = iframeDocument - val foundIv = - iv ?: (document ?: app.get(iframeUrl).document.also { document = it }) - .select("""div.wrapper[class*=container]""") - .attr("class").split("-").lastOrNull() ?: return@safeApiCall - val foundKey = secretKey ?: getKey(base64Decode(id) + foundIv) ?: return@safeApiCall - val foundDecryptKey = secretDecryptKey ?: foundKey - - val uri = URI(iframeUrl) - val mainUrl = "https://" + uri.host - - val encryptedId = cryptoHandler(id, foundIv, foundKey) - val encryptRequestData = if (isUsingAdaptiveData) { - // Only fetch the document if necessary - val realDocument = document ?: app.get(iframeUrl).document - val dataEncrypted = - realDocument.select("script[data-name='episode']").attr("data-value") - val headers = cryptoHandler(dataEncrypted, foundIv, foundKey, false) - "id=$encryptedId&alias=$id&" + headers.substringAfter("&") - } else { - "id=$encryptedId&alias=$id" - } - - val jsonResponse = - app.get( - "$mainUrl/encrypt-ajax.php?$encryptRequestData", - headers = mapOf("X-Requested-With" to "XMLHttpRequest") - ) - val dataencrypted = - jsonResponse.text.substringAfter("{\"data\":\"").substringBefore("\"}") - val datadecrypted = cryptoHandler(dataencrypted, foundIv, foundDecryptKey, false) - val sources = AppUtils.parseJson(datadecrypted) - - suspend fun invokeGogoSource( - source: GogoSource, - sourceCallback: (ExtractorLink) -> Unit - ) { - if (source.file.contains(".m3u8")) { - M3u8Helper.generateM3u8( - mainApiName, - source.file, - mainUrl, - headers = mapOf("Origin" to "https://plyr.link") - ).forEach(sourceCallback) - } else { - sourceCallback.invoke( - ExtractorLink( - mainApiName, - mainApiName, - source.file, - mainUrl, - getQualityFromName(source.label), - ) - ) - } - } - - sources.source?.forEach { - invokeGogoSource(it, callback) - } - sources.sourceBk?.forEach { - invokeGogoSource(it, callback) - } - } - - data class GogoSources( - @JsonProperty("source") val source: List?, - @JsonProperty("sourceBk") val sourceBk: List?, - //val track: List, - //val advertising: List, - //val linkiframe: String - ) - - data class GogoSource( - @JsonProperty("file") val file: String, - @JsonProperty("label") val label: String?, - @JsonProperty("type") val type: String?, - @JsonProperty("default") val default: String? = null - ) -} diff --git a/Kickassanime/src/main/kotlin/com/hexated/Kickassanime.kt b/Kickassanime/src/main/kotlin/com/hexated/Kickassanime.kt deleted file mode 100644 index 75184a6e..00000000 --- a/Kickassanime/src/main/kotlin/com/hexated/Kickassanime.kt +++ /dev/null @@ -1,382 +0,0 @@ -package com.hexated - -import com.fasterxml.jackson.annotation.JsonProperty -import com.hexated.KickassanimeExtractor.invokeAlpha -import com.hexated.KickassanimeExtractor.invokeBeta -import com.hexated.KickassanimeExtractor.invokeDailymotion -import com.hexated.KickassanimeExtractor.invokeGogo -import com.hexated.KickassanimeExtractor.invokeMave -import com.hexated.KickassanimeExtractor.invokePinkbird -import com.hexated.KickassanimeExtractor.invokeSapphire -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.LoadResponse.Companion.addAniListId -import com.lagradost.cloudstream3.LoadResponse.Companion.addMalId -import com.lagradost.cloudstream3.syncproviders.SyncIdName -import com.lagradost.cloudstream3.utils.* -import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson - -open class Kickassanime : MainAPI() { - final override var mainUrl = "https://www2.kickassanime.ro" - override var name = "Kickassanime" - override val hasMainPage = true - override var lang = "en" - override val hasDownloadSupport = true - - override val supportedSyncNames = setOf( - SyncIdName.MyAnimeList, - SyncIdName.Anilist - ) - - override val supportedTypes = setOf( - TvType.Anime, - TvType.AnimeMovie, - TvType.OVA - ) - - companion object { - const val kaast = "https://kaast1.com" - private const val consumetAnilist = "https://api.consumet.org/meta/anilist" - private const val consumetMal = "https://api.consumet.org/meta/mal" - fun getType(t: String): TvType { - return when { - t.contains("Ova", true) -> TvType.OVA - t.contains("Movie", true) -> TvType.AnimeMovie - else -> TvType.Anime - } - } - - fun getStatus(t: String): ShowStatus { - return when (t) { - "Finished Airing" -> ShowStatus.Completed - "Currently Airing" -> ShowStatus.Ongoing - else -> ShowStatus.Completed - } - } - } - - override val mainPage = mainPageOf( - "$mainUrl/api/get_anime_list/all/" to "All", - "$mainUrl/api/get_anime_list/sub/" to "Sub", - "$mainUrl/api/get_anime_list/dub/" to "Dub", - ) - - override suspend fun getMainPage( - page: Int, - request: MainPageRequest - ): HomePageResponse { - val home = app.get(request.data + page).parsedSafe()?.data?.mapNotNull { media -> - media.toSearchResponse() - } ?: throw ErrorLoadingException() - return newHomePageResponse(request.name, home) - } - - private fun getProperAnimeLink(uri: String): String { - return when { - uri.contains("/episode") -> fixUrl(uri.substringBeforeLast("/")) - else -> fixUrl(uri) - } - } - - private fun Animes.toSearchResponse(): AnimeSearchResponse? { - val href = getProperAnimeLink(this.slug ?: return null) - val title = this.name ?: return null - val posterUrl = getImageUrl(this.poster) - val episode = this.episode?.toIntOrNull() - val isDub = this.name.contains("(Dub)") - return newAnimeSearchResponse(title, href, TvType.Anime) { - this.posterUrl = posterUrl - addDubStatus(isDub, episode) - } - } - - override suspend fun search(query: String): List { - val document = app.get("$mainUrl/search?q=$query").document - val data = document.selectFirst("script:containsData(appData)")?.data() - ?.substringAfter("\"animes\":[")?.substringBefore("],") - return tryParseJson>("[$data]")?.mapNotNull { media -> media.toSearchResponse() } - ?: throw ErrorLoadingException() - } - - override suspend fun getLoadUrl(name: SyncIdName, id: String): String { - val syncId = id.split("/").last() - val url = if (name == SyncIdName.Anilist) { - "$consumetAnilist/info/$syncId" - } else { - "$consumetMal/info/$syncId" - } - - val res = app.get(url).parsedSafe()?.title - - val romanjiUrl = "$mainUrl/anime/${res?.romaji?.createSlug()}" - val englishUrl = "$mainUrl/anime/${res?.english?.createSlug()}" - - return if (app.get(romanjiUrl).url != "$mainUrl/") romanjiUrl else englishUrl - } - - override suspend fun load(url: String): LoadResponse? { - val document = app.get(url).document - - val res = document.selectFirst("script:containsData(appData)")?.data() - ?.substringAfter("\"anime\":{")?.substringBefore("},\"wkl\"")?.let { - tryParseJson("{$it}") - } ?: throw ErrorLoadingException() - - val title = res.name ?: return null - val trackerTitle = res.en_title.orEmpty().ifEmpty { res.name }.getTrackerTitle() - val poster = getImageUrl(res.image) - val tags = res.genres?.map { it.name ?: return null } - val year = res.startdate?.substringBefore("-")?.toIntOrNull() - val status = getStatus(res.status ?: return null) - val description = res.description - - val episodes = res.episodes?.mapNotNull { eps -> - Episode(fixUrl(eps.slug ?: return@mapNotNull null), episode = eps.num?.toIntOrNull()) - }?.reversed() ?: emptyList() - - val type = res.type?.substringBefore(",")?.trim()?.let { - when (it) { - "TV Series" -> "tv" - "Ova" -> "ova" - "ONA" -> "ona" - "Movie" -> "movie" - else -> "tv" - } - } ?: if (episodes.size == 1) "movie" else "tv" - - val (malId, anilistId, image, cover) = getTracker( - trackerTitle, - title.getTrackerTitle(), - type, - year - ) - - return newAnimeLoadResponse(title, url, getType(type)) { - engName = title - posterUrl = image ?: poster - backgroundPosterUrl = cover ?: image ?: poster - this.year = year - addEpisodes(DubStatus.Subbed, episodes) - showStatus = status - plot = description - this.tags = tags - addMalId(malId) - addAniListId(anilistId?.toIntOrNull()) - } - - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - - val document = app.get(data).document - val sources = document.selectFirst("script:containsData(appData)")?.data()?.let { - tryParseJson("{${Regex("(\"episode\":.*),\"wkl").find(it)?.groupValues?.get(1)}}") - }?.let { server -> - listOf( - server.episode?.link1.orEmpty().ifEmpty { server.episode?.link4 }, - server.ext_servers?.find { it.name == "Vidstreaming" }?.link - ) - }?.filterNotNull() - val isDub = data.contains("-dub-") - sources?.flatMap { - httpsify(it).fixIframe() - }?.apmap { (name, iframe) -> - val sourceName = fixTitle(name ?: this.name) - val link = httpsify(iframe ?: return@apmap null) - when { - link.startsWith("https://www.dailymotion.com") -> { - invokeDailymotion(link, subtitleCallback, callback) - } - name?.contains(Regex("(?i)(KICKASSANIMEV2|ORIGINAL-QUALITY-V2|BETA-SERVER|DAILYMOTION)")) == true -> { - invokeAlpha(sourceName, link, subtitleCallback, callback) - } - name?.contains(Regex("(?i)(BETAPLAYER)")) == true -> { - invokeBeta(sourceName, link, callback) - } - name?.contains(Regex("(?i)(MAVERICKKI)")) == true -> { - invokeMave(sourceName, link, subtitleCallback, callback) - } - name?.contains(Regex("(?i)(gogo)")) == true -> { - invokeGogo(link, subtitleCallback, callback) - } - name?.contains(Regex("(?i)(SAPPHIRE-DUCK)")) == true -> { - invokeSapphire(link, isDub, subtitleCallback, callback) - } - name?.contains(Regex("(?i)(PINK-BIRD)")) == true -> { - invokePinkbird(sourceName, link, callback) - } - else -> return@apmap null - } - } - - return true - } - - private suspend fun getTracker( - title: String?, - romajiTitle: String?, - type: String?, - year: Int? - ): Tracker { - val res = searchAnime(title).orEmpty().ifEmpty { searchAnime(romajiTitle) } - val media = res?.find { media -> - (media.title?.english.equals(title, true) || media.title?.romaji.equals( - title, - true - )) || (media.type.equals(type, true) && media.releaseDate == year) - } - return Tracker(media?.malId, media?.aniId, media?.image, media?.cover) - } - - private suspend fun searchAnime(title: String?): ArrayList? { - return app.get("$consumetAnilist/$title") - .parsedSafe()?.results - } - - data class Tracker( - val malId: Int? = null, - val aniId: String? = null, - val image: String? = null, - val cover: String? = null, - ) - - data class Title( - @JsonProperty("romaji") val romaji: String? = null, - @JsonProperty("english") val english: String? = null, - ) - - data class Results( - @JsonProperty("id") val aniId: String? = null, - @JsonProperty("malId") val malId: Int? = null, - @JsonProperty("title") val title: Title? = null, - @JsonProperty("releaseDate") val releaseDate: Int? = null, - @JsonProperty("type") val type: String? = null, - @JsonProperty("image") val image: String? = null, - @JsonProperty("cover") val cover: String? = null, - ) - - data class AniSearch( - @JsonProperty("results") val results: ArrayList? = arrayListOf(), - ) - - data class Genres( - @JsonProperty("name") val name: String? = null, - @JsonProperty("slug") val slug: String? = null, - ) - - data class Episodes( - @JsonProperty("epnum") val epnum: String? = null, - @JsonProperty("name") val name: String? = null, - @JsonProperty("slug") val slug: String? = null, - @JsonProperty("createddate") val createddate: String? = null, - @JsonProperty("num") val num: String? = null, - ) - - data class DetailAnime( - @JsonProperty("name") val name: String? = null, - @JsonProperty("en_title") val en_title: String? = null, - @JsonProperty("slug") val slug: String? = null, - @JsonProperty("description") val description: String? = null, - @JsonProperty("status") val status: String? = null, - @JsonProperty("image") val image: String? = null, - @JsonProperty("startdate") val startdate: String? = null, - @JsonProperty("broadcast_day") val broadcast_day: String? = null, - @JsonProperty("broadcast_time") val broadcast_time: String? = null, - @JsonProperty("type") val type: String? = null, - @JsonProperty("episodes") val episodes: ArrayList? = null, - @JsonProperty("genres") val genres: ArrayList? = null, - ) - - data class Animes( - @JsonProperty("episode") val episode: String? = null, - @JsonProperty("slug") val slug: String? = null, - @JsonProperty("type") val type: String? = null, - @JsonProperty("episode_date") val episode_date: String? = null, - @JsonProperty("name") val name: String? = null, - @JsonProperty("poster") val poster: String? = null, - ) - - data class Responses( - @JsonProperty("data") val data: ArrayList? = arrayListOf(), - ) - - data class Iframe( - @JsonProperty("name") val name: String? = null, - @JsonProperty("src") val src: String? = null, - ) - - data class ExtServers( - @JsonProperty("name") val name: String? = null, - @JsonProperty("link") val link: String? = null, - ) - - data class Eps( - @JsonProperty("link1") val link1: String? = null, - @JsonProperty("link4") val link4: String? = null, - ) - - data class Resources( - @JsonProperty("episode") val episode: Eps? = null, - @JsonProperty("ext_servers") val ext_servers: ArrayList? = arrayListOf(), - ) - - data class BetaSources( - @JsonProperty("file") val file: String? = null, - @JsonProperty("label") val label: String? = null, - ) - - data class AlphaSources( - @JsonProperty("file") val file: String? = null, - @JsonProperty("label") val label: String? = null, - ) - - data class MaveSubtitles( - @JsonProperty("name") val name: String? = null, - @JsonProperty("src") val src: String? = null, - ) - - data class MaveSources( - @JsonProperty("hls") val hls: String? = null, - @JsonProperty("subtitles") val subtitles: ArrayList? = arrayListOf(), - ) - - data class SapphireSubtitles( - @JsonProperty("language") val language: String? = null, - @JsonProperty("url") val url: String? = null, - ) - - data class SapphireStreams( - @JsonProperty("format") val format: String? = null, - @JsonProperty("audio_lang") val audio_lang: String? = null, - @JsonProperty("hardsub_lang") val hardsub_lang: String? = null, - @JsonProperty("url") val url: String? = null, - ) - - data class SapphireSources( - @JsonProperty("streams") val streams: ArrayList? = arrayListOf(), - @JsonProperty("subtitles") val subtitles: ArrayList? = arrayListOf(), - ) - - data class PinkbirdSources( - @JsonProperty("data") val data: ArrayList? = arrayListOf(), - ) - - data class PinkbirdData( - @JsonProperty("eid") val eid: String? = null, - @JsonProperty("lh") val lh: String? = null, - ) - - data class SyncTitle( - @JsonProperty("romaji") val romaji: String? = null, - @JsonProperty("english") val english: String? = null, - ) - - data class SyncInfo( - @JsonProperty("title") val title: SyncTitle? = null, - ) - -} \ No newline at end of file diff --git a/Kickassanime/src/main/kotlin/com/hexated/KickassanimeExtractor.kt b/Kickassanime/src/main/kotlin/com/hexated/KickassanimeExtractor.kt deleted file mode 100644 index 255ecb24..00000000 --- a/Kickassanime/src/main/kotlin/com/hexated/KickassanimeExtractor.kt +++ /dev/null @@ -1,201 +0,0 @@ -package com.hexated - -import com.lagradost.cloudstream3.SubtitleFile -import com.lagradost.cloudstream3.app -import com.lagradost.cloudstream3.argamap -import com.lagradost.cloudstream3.base64Decode -import com.lagradost.cloudstream3.utils.* -import org.jsoup.Jsoup - -object KickassanimeExtractor : Kickassanime() { - - suspend fun invokePinkbird( - name: String, - url: String? = null, - callback: (ExtractorLink) -> Unit - ) { - val fixUrl = url?.replace(Regex("(player|embed)\\.php"), "pref.php") - app.get(fixUrl ?: return, - ).parsedSafe()?.data?.map { source -> - val eid = base64Decode(source.eid ?: return@map null) - callback.invoke( - ExtractorLink( - name, - name, - "https://pb.kaast1.com/manifest/$eid/master.m3u8", - "$kaast/", - Qualities.P1080.value, - true - ) - ) - } - } - - suspend fun invokeAlpha( - name: String, - url: String? = null, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ) { - val fixUrl = url?.replace(Regex("(player|embed)\\.php"), "pref.php") - val data = app.get( - fixUrl ?: return, - referer = kaast - ).document.selectFirst("script:containsData(Base64.decode)")?.data() - ?.substringAfter("Base64.decode(\"")?.substringBefore("\")")?.let { base64Decode(it) } ?: return - - if (name == "Dailymotion") { - val iframe = Jsoup.parse(data).select("iframe").attr("src") - invokeDailymotion(iframe, subtitleCallback, callback) - } else { - val json = data.substringAfter("sources: [").substringBefore("],") - AppUtils.tryParseJson>("[$json]")?.map { - callback.invoke( - ExtractorLink( - name, - name, - it.file ?: return@map null, - url, - getQualityFromName(it.label) - ) - ) - } - } - - } - - suspend fun invokeBeta( - name: String, - url: String? = null, - callback: (ExtractorLink) -> Unit - ) { - app.get( - url ?: return, - referer = kaast - ).document.selectFirst("script:containsData(JSON.parse)")?.data() - ?.substringAfter("JSON.parse('")?.substringBeforeLast("')") - ?.let { AppUtils.tryParseJson>(it) }?.map { - callback.invoke( - ExtractorLink( - name, - name, - it.file ?: return@map null, - getBaseUrl(url), - getQualityFromName(it.label) - ) - ) - } - } - - suspend fun invokeMave( - name: String, - url: String? = null, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit, - ) { - val fixUrl = url?.replace("/embed/", "/api/source/") ?: return - val base = getBaseUrl(url) - val data = app.get(fixUrl, referer = url).parsedSafe() - - M3u8Helper.generateM3u8( - if(data?.subtitles.isNullOrEmpty()) "$name [Hardsub]" else "$name [Softsub]", - fixUrl(data?.hls ?: return, base), - url - ).forEach(callback) - - data.subtitles?.map { sub -> - subtitleCallback.invoke( - SubtitleFile( - sub.name ?: "", - fixUrl(sub.src ?: return@map null, base) - ) - ) - } - - } - - suspend fun invokeSapphire( - url: String? = null, - isDub: Boolean = false, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit, - ) { - var data = app.get("$url&action=config", referer = url).text - while(true) { - if(data.startsWith("{") || data == "null") break - data = data.base64Decode() - } - AppUtils.tryParseJson(data).let { res -> - res?.streams?.filter { it.format == "adaptive_hls" }?.reversed()?.map { source -> - val name = if(isDub) source.audio_lang else source.hardsub_lang.orEmpty().ifEmpty { "raw" } - M3u8Helper.generateM3u8( - "Crunchyroll [$name]", - source.url ?: return@map null, - "https://static.crunchyroll.com/", - ).forEach(callback) - } - res?.subtitles?.map { sub -> - subtitleCallback.invoke( - SubtitleFile( - getLanguage(sub.language ?: return@map null) ?: sub.language, - sub.url ?: return@map null - ) - ) - } - } - } - - suspend fun invokeGogo( - link: String, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ) { - val iframe = app.get(link) - val iframeDoc = iframe.document - argamap({ - iframeDoc.select(".list-server-items > .linkserver") - .forEach { element -> - val status = element.attr("data-status") ?: return@forEach - if (status != "1") return@forEach - val extractorData = element.attr("data-video") ?: return@forEach - loadExtractor(extractorData, iframe.url, subtitleCallback, callback) - } - }, { - val iv = "3134003223491201" - val secretKey = "37911490979715163134003223491201" - val secretDecryptKey = "54674138327930866480207815084989" - GogoExtractor.extractVidstream( - iframe.url, - "Gogoanime", - callback, - iv, - secretKey, - secretDecryptKey, - isUsingAdaptiveKeys = false, - isUsingAdaptiveData = true, - iframeDocument = iframeDoc - ) - }) - } - - suspend fun invokeDailymotion( - link: String, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ) { - loadExtractor(link, mainUrl, subtitleCallback) { video -> - callback.invoke( - ExtractorLink( - video.name, - video.name, - video.url, - video.referer, - Qualities.P1080.value, - video.isM3u8, - video.headers, - video.extractorData - ) - ) - } - } -} \ No newline at end of file diff --git a/Kickassanime/src/main/kotlin/com/hexated/KickassanimePlugin.kt b/Kickassanime/src/main/kotlin/com/hexated/KickassanimePlugin.kt deleted file mode 100644 index 5565d18d..00000000 --- a/Kickassanime/src/main/kotlin/com/hexated/KickassanimePlugin.kt +++ /dev/null @@ -1,14 +0,0 @@ - -package com.hexated - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class KickassanimePlugin: Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(Kickassanime()) - } -} \ No newline at end of file diff --git a/Kickassanime/src/main/kotlin/com/hexated/KickassanimeUtils.kt b/Kickassanime/src/main/kotlin/com/hexated/KickassanimeUtils.kt deleted file mode 100644 index e51df458..00000000 --- a/Kickassanime/src/main/kotlin/com/hexated/KickassanimeUtils.kt +++ /dev/null @@ -1,90 +0,0 @@ -package com.hexated - -import android.util.Base64 -import com.hexated.KickassanimeExtractor.mainUrl -import com.lagradost.cloudstream3.app -import com.lagradost.cloudstream3.utils.AppUtils -import com.lagradost.cloudstream3.utils.SubtitleHelper -import java.net.URI -import java.net.URLDecoder - -suspend fun String.fixIframe(): List> { - return when { - this.startsWith("${Kickassanime.kaast}/dust/") -> { - val document = app.get(this).document - document.selectFirst("script:containsData(sources =)")?.data() - ?.substringAfter("sources = [")?.substringBefore("];")?.let { - AppUtils.tryParseJson>("[$it]")?.map { source -> - source.name to source.src - } - } ?: emptyList() - } - this.startsWith("${Kickassanime.kaast}/axplayer/") -> { - val source = decode( - this.substringAfter("&data=").substringBefore("&vref=") - ) - listOf("gogo" to source) - } - else -> { - emptyList() - } - } -} - -fun String.base64Decode(): String { - return Base64.decode(this, Base64.DEFAULT).toString(Charsets.UTF_8) -} - -fun decode(input: String): String = - URLDecoder.decode(input, "utf-8").replace(" ", "%20") - -fun String.createSlug(): String { - return this.replace(Regex("[^\\w ]+"), "").replace(" ", "-").lowercase() -} - -fun String.getTrackerTitle(): String { - val blacklist = arrayOf( - "Dub", - "Uncensored", - "TV", - "JPN DUB", - "Uncensored" - ).joinToString("|") { "\\($it\\)" } - return this.replace(Regex(blacklist), "").trim() -} - -fun getImageUrl(link: String?): String? { - if (link == null) return null - return if (link.startsWith(mainUrl)) link else "$mainUrl/uploads/$link" -} - -fun getBaseUrl(url: String): String { - return URI(url).let { - "${it.scheme}://${it.host}" - } -} - - -fun getLanguage(language: String?): String? { - return SubtitleHelper.fromTwoLettersToLanguage(language ?: return null) - ?: SubtitleHelper.fromTwoLettersToLanguage(language.substringBefore("-")) -} - -fun fixUrl(url: String, domain: String): String { - if (url.startsWith("http")) { - return url - } - if (url.isEmpty()) { - return "" - } - - val startsWithNoHttp = url.startsWith("//") - if (startsWithNoHttp) { - return "https:$url" - } else { - if (url.startsWith('/')) { - return domain + url - } - return "$domain/$url" - } -} \ No newline at end of file diff --git a/Kissasian/build.gradle.kts b/Kissasian/build.gradle.kts deleted file mode 100644 index a81930da..00000000 --- a/Kissasian/build.gradle.kts +++ /dev/null @@ -1,23 +0,0 @@ -// use an integer for version numbers -version = 2 - - -cloudstream { - language = "en" - // All of these properties are optional, you can safely remove them - - // description = "Lorem Ipsum" - authors = listOf("Hexated") - - /** - * Status int as the following: - * 0: Down - * 1: Ok - * 2: Slow - * 3: Beta only - * */ - status = 1 // will be 3 if unspecified - tvTypes = listOf("AsianDrama",) - - iconUrl = "https://www.google.com/s2/favicons?domain=kissasian.pe&sz=%size%" -} \ No newline at end of file diff --git a/Kissasian/src/main/AndroidManifest.xml b/Kissasian/src/main/AndroidManifest.xml deleted file mode 100644 index c98063f8..00000000 --- a/Kissasian/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/Kissasian/src/main/kotlin/com/hexated/Kissasian.kt b/Kissasian/src/main/kotlin/com/hexated/Kissasian.kt deleted file mode 100644 index eab56061..00000000 --- a/Kissasian/src/main/kotlin/com/hexated/Kissasian.kt +++ /dev/null @@ -1,140 +0,0 @@ -package com.hexated - -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.extractors.helper.GogoHelper -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.httpsify -import com.lagradost.cloudstream3.utils.loadExtractor -import org.jsoup.nodes.Element - -class Kissasian : MainAPI() { - override var mainUrl = "https://kissasian.pe" - override var name = "Kissasian" - override val hasMainPage = true - override val hasDownloadSupport = true - override val supportedTypes = setOf(TvType.AsianDrama) - - companion object { - fun getStatus(t: String?): ShowStatus { - return when (t) { - "Completed" -> ShowStatus.Completed - "Ongoing" -> ShowStatus.Ongoing - else -> ShowStatus.Completed - } - } - } - - override val mainPage = mainPageOf( - "drama-list/ongoing.html?page=" to "Drama Ongoing", - "drama-list/completed.html?page=" to "Drama Completed", - "genre/variety/?page=" to "Variety Show", - "genre/romance/?page=" to "Romance", - "genre/action/?page=" to "Action", - "genre/mystery/?page=" to "Mystery", - ) - - override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse { - val document = app.get("$mainUrl/${request.data}$page").document - val home = document.select("div.list-drama div.item").mapNotNull { - it.toSearchResult() - } - return newHomePageResponse(request.name, home) - } - - private fun Element.toSearchResult(): SearchResponse? { - val href = fixUrl(this.selectFirst("a")?.attr("href") ?: return null) - val title = this.selectFirst("span.title")?.text()?.trim() ?: return null - val posterUrl = fixUrlNull(this.selectFirst("div.pic img")?.attr("src")) - - return newTvSeriesSearchResponse(title, href, TvType.AsianDrama) { - this.posterUrl = posterUrl - } - } - - override suspend fun search(query: String): List { - val document = app.get("$mainUrl/search.html?keyword=$query").document - return document.select("div.list-drama div.item").mapNotNull { - it.toSearchResult() - } - } - - override suspend fun load(url: String): LoadResponse? { - val document = app.get(url).document - - val title = document.selectFirst("div.barContentInfo a")?.text()?.trim() ?: return null - val poster = fixUrlNull(document.selectFirst("div.barContentInfo img")?.attr("src")) - val tags = document.select("div.barContentInfo p:contains(Genres:) a").map { it.text().removePrefix(",").trim() } - - val year = document.selectFirst("div.barContentInfo p.type.Releasea")?.text()?.trim()?.toIntOrNull() - val status = getStatus(document.selectFirst("div.barContentInfo p:contains(Status:)")?.ownText()?.trim()) - val description = document.selectFirst("div.barContentInfo p.des")?.nextElementSiblings()?.select("p")?.text() - - val episodes = document.select("ul.listing li").map { - val name = it.selectFirst("a")?.attr("title") - val link = fixUrlNull(it.selectFirst("a")?.attr("href")) - val epNum = Regex("Episode\\s(\\d+)").find("$name")?.groupValues?.getOrNull(1)?.toIntOrNull() - newEpisode(link) { - this.name = name - this.episode = epNum - } - }.reversed() - - if (episodes.size == 1) { - return newMovieLoadResponse(title, url, TvType.Movie, episodes[0].data) { - posterUrl = poster - this.year = year - plot = description - this.tags = tags - } - } else { - return newTvSeriesLoadResponse(title, url, TvType.AsianDrama, episodes = episodes) { - posterUrl = poster - this.year = year - showStatus = status - plot = description - this.tags = tags - } - } - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - - val document = app.get(data).document - val server = document.selectFirst("select#selectServer option")?.attr("value") - val iframe = app.get(httpsify(server ?: return false)) - val iframeDoc = iframe.document - - argamap({ - iframeDoc.select(".list-server-items > .linkserver") - .forEach { element -> - val status = element.attr("data-status") ?: return@forEach - if (status != "1") return@forEach - val extractorData = element.attr("data-video") ?: return@forEach - loadExtractor(extractorData, iframe.url, subtitleCallback, callback) - } - }, { - val iv = "9262859232435825" - val secretKey = "93422192433952489752342908585752" - val secretDecryptKey = secretKey - GogoHelper.extractVidstream( - iframe.url, - this.name, - callback, - iv, - secretKey, - secretDecryptKey, - isUsingAdaptiveKeys = false, - isUsingAdaptiveData = true, - iframeDocument = iframeDoc - ) - }) - - return true - } - -} diff --git a/Kissasian/src/main/kotlin/com/hexated/KissasianPlugin.kt b/Kissasian/src/main/kotlin/com/hexated/KissasianPlugin.kt deleted file mode 100644 index 932e9fc8..00000000 --- a/Kissasian/src/main/kotlin/com/hexated/KissasianPlugin.kt +++ /dev/null @@ -1,14 +0,0 @@ - -package com.hexated - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class KissasianPlugin: Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(Kissasian()) - } -} \ No newline at end of file diff --git a/KisskhProvider/build.gradle.kts b/KisskhProvider/build.gradle.kts deleted file mode 100644 index d2bcff25..00000000 --- a/KisskhProvider/build.gradle.kts +++ /dev/null @@ -1,28 +0,0 @@ -// use an integer for version numbers -version = 4 - - -cloudstream { - language = "en" - // All of these properties are optional, you can safely remove them - - // description = "Lorem Ipsum" - authors = listOf("Hexated") - - /** - * Status int as the following: - * 0: Down - * 1: Ok - * 2: Slow - * 3: Beta only - * */ - status = 1 // will be 3 if unspecified - tvTypes = listOf( - "AsianDrama", - "TvSeries", - "Anime", - "Movie", - ) - - iconUrl = "https://www.google.com/s2/favicons?domain=kisskh.me&sz=%size%" -} \ No newline at end of file diff --git a/KisskhProvider/src/main/AndroidManifest.xml b/KisskhProvider/src/main/AndroidManifest.xml deleted file mode 100644 index c98063f8..00000000 --- a/KisskhProvider/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/KisskhProvider/src/main/kotlin/com/hexated/KisskhProvider.kt b/KisskhProvider/src/main/kotlin/com/hexated/KisskhProvider.kt deleted file mode 100644 index 4fd4ed77..00000000 --- a/KisskhProvider/src/main/kotlin/com/hexated/KisskhProvider.kt +++ /dev/null @@ -1,217 +0,0 @@ -package com.hexated - -import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.mvvm.safeApiCall -import com.lagradost.cloudstream3.utils.AppUtils.parseJson -import com.lagradost.cloudstream3.utils.AppUtils.toJson -import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.M3u8Helper -import com.lagradost.cloudstream3.utils.loadExtractor -import java.util.ArrayList - -class KisskhProvider : MainAPI() { - override var mainUrl = "https://kisskh.co" - override var name = "Kisskh" - override val hasMainPage = true - override val hasDownloadSupport = true - override val supportedTypes = setOf( - TvType.AsianDrama, - TvType.Anime - ) - - override val mainPage = mainPageOf( - "&type=2&sub=0&country=2&status=0&order=1" to "Movie Popular", - "&type=2&sub=0&country=2&status=0&order=2" to "Movie Last Update", - "&type=1&sub=0&country=2&status=0&order=1" to "TVSeries Popular", - "&type=1&sub=0&country=2&status=0&order=2" to "TVSeries Last Update", - "&type=3&sub=0&country=0&status=0&order=1" to "Anime Popular", - "&type=3&sub=0&country=0&status=0&order=2" to "Anime Last Update", - "&type=4&sub=0&country=0&status=0&order=1" to "Hollywood Popular", - "&type=4&sub=0&country=0&status=0&order=2" to "Hollywood Last Update", - ) - - override suspend fun getMainPage( - page: Int, - request: MainPageRequest - ): HomePageResponse { - val home = app.get("$mainUrl/api/DramaList/List?page=$page${request.data}") - .parsedSafe()?.data - ?.mapNotNull { media -> - media.toSearchResponse() - } ?: throw ErrorLoadingException("Invalid Json reponse") - return newHomePageResponse( - list = HomePageList( - name = request.name, - list = home, - isHorizontalImages = true - ), - hasNext = true - ) - } - - private fun Media.toSearchResponse(): SearchResponse? { - - return newAnimeSearchResponse( - title ?: return null, - "$title/$id", - TvType.TvSeries, - ) { - this.posterUrl = thumbnail - addSub(episodesCount) - } - } - - override suspend fun search(query: String): List { - val searchResponse = - app.get("$mainUrl/api/DramaList/Search?q=$query&type=0", referer = "$mainUrl/").text - return tryParseJson>(searchResponse)?.mapNotNull { media -> - media.toSearchResponse() - } ?: throw ErrorLoadingException("Invalid Json reponse") - } - - private fun getTitle(str: String): String { - return str.replace(Regex("[^a-zA-Z0-9]"), "-") - } - - override suspend fun load(url: String): LoadResponse? { - val id = url.split("/") - val res = app.get( - "$mainUrl/api/DramaList/Drama/${id.last()}?isq=false", - referer = "$mainUrl/Drama/${ - getTitle(id.first()) - }?id=${id.last()}" - ).parsedSafe() - ?: throw ErrorLoadingException("Invalid Json reponse") - - val episodes = res.episodes?.map { eps -> - Episode( - data = Data(res.title, eps.number, res.id, eps.id).toJson(), - episode = eps.number - ) - } ?: throw ErrorLoadingException("No Episode") - - return newTvSeriesLoadResponse( - res.title ?: return null, - url, - if (res.type == "Movie" || episodes.size == 1) TvType.Movie else TvType.TvSeries, - episodes - ) { - this.posterUrl = res.thumbnail - this.year = res.releaseDate?.split("-")?.first()?.toIntOrNull() - this.plot = res.description - this.tags = listOf("${res.country}", "${res.status}", "${res.type}") - this.showStatus = when (res.status) { - "Completed" -> ShowStatus.Completed - "Ongoing" -> ShowStatus.Ongoing - else -> null - } - } - - } - - private fun getLanguage(str: String): String { - return when (str) { - "Indonesia" -> "Indonesian" - else -> str - } - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - - val loadData = parseJson(data) - - app.get( - "$mainUrl/api/DramaList/Episode/${loadData.epsId}.png?err=false&ts=&time=", - referer = "$mainUrl/Drama/${getTitle("${loadData.title}")}/Episode-${loadData.eps}?id=${loadData.id}&ep=${loadData.epsId}&page=0&pageSize=100" - ).parsedSafe()?.let { source -> - listOf(source.video, source.thirdParty).apmap { link -> - safeApiCall { - if (link?.contains(".m3u8") == true) { - M3u8Helper.generateM3u8( - this.name, - link, - referer = "$mainUrl/", - headers = mapOf("Origin" to mainUrl) - ).forEach(callback) - } else { - loadExtractor( - link?.substringBefore("=http") ?: return@safeApiCall, - "$mainUrl/", - subtitleCallback, - callback - ) - } - } - } - } - - // parsedSafe doesn't work in > - app.get("$mainUrl/api/Sub/${loadData.epsId}").text.let { res -> - tryParseJson>(res)?.map { sub -> - subtitleCallback.invoke( - SubtitleFile( - getLanguage(sub.label ?: return@map), - sub.src ?: return@map - ) - ) - } - } - - return true - - } - - data class Data( - val title: String?, - val eps: Int?, - val id: Int?, - val epsId: Int?, - ) - - data class Sources( - @JsonProperty("Video") val video: String?, - @JsonProperty("ThirdParty") val thirdParty: String?, - ) - - data class Subtitle( - @JsonProperty("src") val src: String?, - @JsonProperty("label") val label: String?, - ) - - data class Responses( - @JsonProperty("data") val data: ArrayList? = arrayListOf(), - ) - - data class Media( - @JsonProperty("episodesCount") val episodesCount: Int?, - @JsonProperty("thumbnail") val thumbnail: String?, - @JsonProperty("id") val id: Int?, - @JsonProperty("title") val title: String?, - ) - - data class Episodes( - @JsonProperty("id") val id: Int?, - @JsonProperty("number") val number: Int?, - @JsonProperty("sub") val sub: Int?, - ) - - data class MediaDetail( - @JsonProperty("description") val description: String?, - @JsonProperty("releaseDate") val releaseDate: String?, - @JsonProperty("status") val status: String?, - @JsonProperty("type") val type: String?, - @JsonProperty("country") val country: String?, - @JsonProperty("episodes") val episodes: ArrayList? = arrayListOf(), - @JsonProperty("thumbnail") val thumbnail: String?, - @JsonProperty("id") val id: Int?, - @JsonProperty("title") val title: String?, - ) - -} diff --git a/KisskhProvider/src/main/kotlin/com/hexated/KisskhProviderPlugin.kt b/KisskhProvider/src/main/kotlin/com/hexated/KisskhProviderPlugin.kt deleted file mode 100644 index 70f05e71..00000000 --- a/KisskhProvider/src/main/kotlin/com/hexated/KisskhProviderPlugin.kt +++ /dev/null @@ -1,14 +0,0 @@ - -package com.hexated - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class KisskhProviderPlugin: Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(KisskhProvider()) - } -} \ No newline at end of file diff --git a/KuramanimeProvider/build.gradle.kts b/KuramanimeProvider/build.gradle.kts deleted file mode 100644 index 31e82f6a..00000000 --- a/KuramanimeProvider/build.gradle.kts +++ /dev/null @@ -1,27 +0,0 @@ -// use an integer for version numbers -version = 12 - - -cloudstream { - language = "id" - // All of these properties are optional, you can safely remove them - - // description = "Lorem Ipsum" - authors = listOf("Hexated") - - /** - * Status int as the following: - * 0: Down - * 1: Ok - * 2: Slow - * 3: Beta only - * */ - status = 1 // will be 3 if unspecified - tvTypes = listOf( - "AnimeMovie", - "Anime", - "OVA", - ) - - iconUrl = "https://www.google.com/s2/favicons?domain=kuramanime.com&sz=%size%" -} \ No newline at end of file diff --git a/KuramanimeProvider/src/main/AndroidManifest.xml b/KuramanimeProvider/src/main/AndroidManifest.xml deleted file mode 100644 index c98063f8..00000000 --- a/KuramanimeProvider/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/KuramanimeProvider/src/main/kotlin/com/hexated/Extractors.kt b/KuramanimeProvider/src/main/kotlin/com/hexated/Extractors.kt deleted file mode 100644 index 521502ac..00000000 --- a/KuramanimeProvider/src/main/kotlin/com/hexated/Extractors.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.hexated - -import com.lagradost.cloudstream3.extractors.Filesim -import com.lagradost.cloudstream3.extractors.StreamSB - -class Nyomo : StreamSB() { - override var name: String = "Nyomo" - override var mainUrl = "https://nyomo.my.id" -} - -class Streamhide : Filesim() { - override var name: String = "Streamhide" - override var mainUrl: String = "https://streamhide.to" -} \ No newline at end of file diff --git a/KuramanimeProvider/src/main/kotlin/com/hexated/KuramanimeProvider.kt b/KuramanimeProvider/src/main/kotlin/com/hexated/KuramanimeProvider.kt deleted file mode 100644 index 5d24fcad..00000000 --- a/KuramanimeProvider/src/main/kotlin/com/hexated/KuramanimeProvider.kt +++ /dev/null @@ -1,211 +0,0 @@ -package com.hexated - -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.mvvm.safeApiCall -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.Qualities -import com.lagradost.cloudstream3.utils.loadExtractor -import org.jsoup.Jsoup -import org.jsoup.nodes.Element - -class KuramanimeProvider : MainAPI() { - override var mainUrl = "https://kuramanime.net" - override var name = "Kuramanime" - override val hasQuickSearch = false - override val hasMainPage = true - override var lang = "id" - override val hasDownloadSupport = true - - override val supportedTypes = setOf( - TvType.Anime, - TvType.AnimeMovie, - TvType.OVA - ) - - companion object { - fun getType(t: String, s: Int): TvType { - return if (t.contains("OVA", true) || t.contains("Special")) TvType.OVA - else if (t.contains("Movie", true) && s == 1) TvType.AnimeMovie - else TvType.Anime - } - - fun getStatus(t: String): ShowStatus { - return when (t) { - "Selesai Tayang" -> ShowStatus.Completed - "Sedang Tayang" -> ShowStatus.Ongoing - else -> ShowStatus.Completed - } - } - } - - override val mainPage = mainPageOf( - "$mainUrl/anime/ongoing?order_by=updated&page=" to "Sedang Tayang", - "$mainUrl/anime/finished?order_by=updated&page=" to "Selesai Tayang", - "$mainUrl/properties/season/summer-2022?order_by=most_viewed&page=" to "Dilihat Terbanyak Musim Ini", - "$mainUrl/anime/movie?order_by=updated&page=" to "Film Layar Lebar", - ) - - override suspend fun getMainPage( - page: Int, - request: MainPageRequest - ): HomePageResponse { - val document = app.get(request.data + page).document - - val home = document.select("div.col-lg-4.col-md-6.col-sm-6").mapNotNull { - it.toSearchResult() - } - - return newHomePageResponse(request.name, home) - } - - private fun getProperAnimeLink(uri: String): String { - return if (uri.contains("/episode")) { - Regex("(.*)/episode/.+").find(uri)?.groupValues?.get(1).toString() + "/" - } else { - uri - } - } - - private fun Element.toSearchResult(): AnimeSearchResponse? { - val href = getProperAnimeLink(fixUrl(this.selectFirst("a")!!.attr("href"))) - val title = this.selectFirst("h5 a")?.text() ?: return null - val posterUrl = fixUrl(this.select("div.product__item__pic.set-bg").attr("data-setbg")) - val episode = this.select("div.ep span").text().let { - Regex("Ep\\s(\\d+)\\s/").find(it)?.groupValues?.getOrNull(1)?.toIntOrNull() - } - - return newAnimeSearchResponse(title, href, TvType.Anime) { - this.posterUrl = posterUrl - addSub(episode) - } - - } - - override suspend fun search(query: String): List { - val link = "$mainUrl/anime?search=$query&order_by=latest" - val document = app.get(link).document - - return document.select("div#animeList div.product__item").mapNotNull { - it.toSearchResult() - } - } - - override suspend fun load(url: String): LoadResponse { - val document = app.get(url).document - - val title = document.selectFirst(".anime__details__title > h3")!!.text().trim() - val poster = document.selectFirst(".anime__details__pic")?.attr("data-setbg") - val tags = document.select("div.anime__details__widget > div > div:nth-child(2) > ul > li:nth-child(1)") - .text().trim().replace("Genre: ", "").split(", ") - - val year = Regex("\\D").replace( - document.select("div.anime__details__widget > div > div:nth-child(1) > ul > li:nth-child(5)") - .text().trim().replace("Musim: ", ""), "" - ).toIntOrNull() - val status = getStatus( - document.select("div.anime__details__widget > div > div:nth-child(1) > ul > li:nth-child(3)") - .text().trim().replace("Status: ", "") - ) - val type = document.selectFirst("div.col-lg-6.col-md-6 ul li:contains(Tipe:) a")?.text()?.lowercase() ?: "tv" - val description = document.select(".anime__details__text > p").text().trim() - - val episodes = mutableListOf() - - for (i in 1..6) { - val doc = app.get("$url?page=$i").document - val eps = Jsoup.parse(doc.select("#episodeLists").attr("data-content")).select("a.btn.btn-sm.btn-danger") - .mapNotNull { - val name = it.text().trim() - val episode = Regex("(\\d+[.,]?\\d*)").find(name)?.groupValues?.getOrNull(0) - ?.toIntOrNull() - val link = it.attr("href") - Episode(link, name, episode = episode) - } - if(eps.isEmpty()) break else episodes.addAll(eps) - } - - val recommendations = document.select("div#randomList > a").mapNotNull { - val epHref = it.attr("href") - val epTitle = it.select("h5.sidebar-title-h5.px-2.py-2").text() - val epPoster = it.select(".product__sidebar__view__item.set-bg").attr("data-setbg") - - newAnimeSearchResponse(epTitle, epHref, TvType.Anime) { - this.posterUrl = epPoster - addDubStatus(dubExist = false, subExist = true) - } - } - - return newAnimeLoadResponse(title, url, getType(type, episodes.size)) { - engName = title - posterUrl = poster - this.year = year - addEpisodes(DubStatus.Subbed, episodes) - showStatus = status - plot = description - this.tags = tags - this.recommendations = recommendations - } - - } - - private suspend fun invokeLocalSource( - url: String, - server: String, - ref: String, - callback: (ExtractorLink) -> Unit - ) { - val document = app.get( - url, - referer = ref, - headers = mapOf("X-Requested-With" to "XMLHttpRequest") - ).document - document.select("video#player > source").map { - val link = fixUrl(it.attr("src")) - val quality = it.attr("size").toIntOrNull() - callback.invoke( - ExtractorLink( - fixTitle(server), - fixTitle(server), - link, - referer = "$mainUrl/", - quality = quality ?: Qualities.Unknown.value, - headers = mapOf( - "Accept" to "video/webm,video/ogg,video/*;q=0.9,application/ogg;q=0.7,audio/*;q=0.6,*/*;q=0.5", - "Range" to "bytes=0-", - "Sec-Fetch-Dest" to "video", - "Sec-Fetch-Mode" to "no-cors", - ) - ) - ) - } - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - val res = app.get(data).document - res.select("select#changeServer option").apmap { source -> - safeApiCall { - val server = source.attr("value") - val link = "$data?activate_stream=1&stream_server=$server" - if (server == "kuramadrive" || server == "archive") { - invokeLocalSource(link, server, data, callback) - } else { - app.get( - link, - referer = data, - headers = mapOf("X-Requested-With" to "XMLHttpRequest") - ).document.select("div.iframe-container iframe").attr("src").let { videoUrl -> - loadExtractor(fixUrl(videoUrl), "$mainUrl/", subtitleCallback, callback) - } - } - } - } - - return true - } - -} \ No newline at end of file diff --git a/KuramanimeProvider/src/main/kotlin/com/hexated/KuramanimeProviderPlugin.kt b/KuramanimeProvider/src/main/kotlin/com/hexated/KuramanimeProviderPlugin.kt deleted file mode 100644 index da4282eb..00000000 --- a/KuramanimeProvider/src/main/kotlin/com/hexated/KuramanimeProviderPlugin.kt +++ /dev/null @@ -1,16 +0,0 @@ - -package com.hexated - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class KuramanimeProviderPlugin: Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(KuramanimeProvider()) - registerExtractorAPI(Nyomo()) - registerExtractorAPI(Streamhide()) - } -} \ No newline at end of file diff --git a/KuronimeProvider/build.gradle.kts b/KuronimeProvider/build.gradle.kts deleted file mode 100644 index c903bc0b..00000000 --- a/KuronimeProvider/build.gradle.kts +++ /dev/null @@ -1,27 +0,0 @@ -// use an integer for version numbers -version = 13 - - -cloudstream { - language = "id" - // All of these properties are optional, you can safely remove them - - // description = "Lorem Ipsum" - authors = listOf("Hexated") - - /** - * Status int as the following: - * 0: Down - * 1: Ok - * 2: Slow - * 3: Beta only - * */ - status = 1 // will be 3 if unspecified - tvTypes = listOf( - "AnimeMovie", - "Anime", - "OVA", - ) - - iconUrl = "https://www.google.com/s2/favicons?domain=45.12.2.2&sz=%size%" -} \ No newline at end of file diff --git a/KuronimeProvider/src/main/AndroidManifest.xml b/KuronimeProvider/src/main/AndroidManifest.xml deleted file mode 100644 index c98063f8..00000000 --- a/KuronimeProvider/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/KuronimeProvider/src/main/kotlin/com/hexated/KuronimeProvider.kt b/KuronimeProvider/src/main/kotlin/com/hexated/KuronimeProvider.kt deleted file mode 100644 index 2d050c26..00000000 --- a/KuronimeProvider/src/main/kotlin/com/hexated/KuronimeProvider.kt +++ /dev/null @@ -1,238 +0,0 @@ -package com.hexated - -import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer -import com.lagradost.cloudstream3.mvvm.safeApiCall -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.getQualityFromName -import com.lagradost.cloudstream3.utils.loadExtractor -import org.jsoup.Jsoup -import org.jsoup.nodes.Element -import java.net.URI -import java.util.ArrayList - -class KuronimeProvider : MainAPI() { - override var mainUrl = "https://kuronime.top" - override var name = "Kuronime" - override val hasQuickSearch = true - override val hasMainPage = true - override var lang = "id" - override val hasDownloadSupport = true - - override val supportedTypes = setOf( - TvType.Anime, - TvType.AnimeMovie, - TvType.OVA - ) - - companion object { - fun getType(t: String): TvType { - return if (t.contains("OVA", true) || t.contains("Special", true)) TvType.OVA - else if (t.contains("Movie", true)) TvType.AnimeMovie - else TvType.Anime - } - - fun getStatus(t: String): ShowStatus { - return when (t) { - "Completed" -> ShowStatus.Completed - "Ongoing" -> ShowStatus.Ongoing - else -> ShowStatus.Completed - } - } - } - - override val mainPage = mainPageOf( - "$mainUrl/page/" to "New Episodes", - "$mainUrl/popular-anime/page/" to "Popular Anime", - "$mainUrl/movies/page/" to "Movies", -// "$mainUrl/genres/donghua/page/" to "Donghua", -// "$mainUrl/live-action/page/" to "Live Action", - ) - - override suspend fun getMainPage( - page: Int, - request: MainPageRequest - ): HomePageResponse { - val req = app.get(request.data + page) - mainUrl = getBaseUrl(req.url) - val document = req.document - val home = document.select("article").map { - it.toSearchResult() - } - return newHomePageResponse(request.name, home) - } - - private fun getProperAnimeLink(uri: String): String { - return if (uri.contains("/anime/")) { - uri - } else { - var title = uri.substringAfter("$mainUrl/") - title = when { - (title.contains("-episode")) && !(title.contains("-movie")) -> Regex("nonton-(.+)-episode").find( - title - )?.groupValues?.get(1).toString() - (title.contains("-movie")) -> Regex("nonton-(.+)-movie").find(title)?.groupValues?.get( - 1 - ).toString() - else -> title - } - - "$mainUrl/anime/$title" - } - } - - private fun Element.toSearchResult(): AnimeSearchResponse { - val href = getProperAnimeLink(fixUrlNull(this.selectFirst("a")?.attr("href")).toString()) - val title = this.select(".bsuxtt, .tt > h4").text().trim() - val posterUrl = fixUrlNull( - this.selectFirst("div.view,div.bt")?.nextElementSibling()?.select("img") - ?.attr("data-src") - ) - val epNum = this.select(".ep").text().replace(Regex("\\D"), "").trim().toIntOrNull() - val tvType = getType(this.selectFirst(".bt > span")?.text().toString()) - return newAnimeSearchResponse(title, href, tvType) { - this.posterUrl = posterUrl - addSub(epNum) - } - - } - - override suspend fun quickSearch(query: String): List? = search(query) - - override suspend fun search(query: String): List? { - mainUrl = app.get(mainUrl).url - return app.post( - "$mainUrl/wp-admin/admin-ajax.php", data = mapOf( - "action" to "ajaxy_sf", - "sf_value" to query, - "search" to "false" - ), headers = mapOf("X-Requested-With" to "XMLHttpRequest") - ).parsedSafe()?.anime?.firstOrNull()?.all?.mapNotNull { - newAnimeSearchResponse(it.postTitle ?: "", it.postLink ?: return@mapNotNull null, TvType.Anime) { - this.posterUrl = it.postImage - addSub(it.postLatest?.toIntOrNull()) - } - } - } - - override suspend fun load(url: String): LoadResponse { - val document = app.get(url).document - - val title = document.selectFirst(".entry-title")?.text().toString().trim() - val poster = document.selectFirst("div.l[itemprop=image] > img")?.attr("data-src") - val tags = document.select(".infodetail > ul > li:nth-child(2) > a").map { it.text() } - val type = - document.selectFirst(".infodetail > ul > li:nth-child(7)")?.ownText()?.removePrefix(":") - ?.lowercase()?.trim() ?: "tv" - - val trailer = document.selectFirst("div.tply iframe")?.attr("data-src") - val year = Regex("\\d, (\\d*)").find( - document.select(".infodetail > ul > li:nth-child(5)").text() - )?.groupValues?.get(1)?.toIntOrNull() - val status = getStatus( - document.selectFirst(".infodetail > ul > li:nth-child(3)")!!.ownText() - .replace(Regex("\\W"), "") - ) - val description = document.select("span.const > p").text() - - val episodes = document.select("div.bixbox.bxcl > ul > li").mapNotNull { - val link = it.selectFirst("a")?.attr("href") ?: return@mapNotNull null - val name = it.selectFirst("a")?.text() ?: return@mapNotNull null - val episode = - Regex("(\\d+[.,]?\\d*)").find(name)?.groupValues?.getOrNull(0)?.toIntOrNull() - Episode(link, name, episode = episode) - }.reversed() - - return newAnimeLoadResponse(title, url, getType(type)) { - engName = title - posterUrl = poster - this.year = year - addEpisodes(DubStatus.Subbed, episodes) - showStatus = status - plot = description - addTrailer(trailer) - this.tags = tags - } - } - - private suspend fun invokeKuroSource( - url: String, - sourceCallback: (ExtractorLink) -> Unit - ) { - val doc = app.get(url, referer = "${mainUrl}/").document - - doc.select("script").map { script -> - if (script.data().contains("function jalankan_jwp() {")) { - val data = script.data() - val doma = data.substringAfter("var doma = \"").substringBefore("\";") - val token = data.substringAfter("var token = \"").substringBefore("\";") - val pat = data.substringAfter("var pat = \"").substringBefore("\";") - val link = "$doma$token$pat/index.m3u8" - val quality = - Regex("\\d{3,4}p").find(doc.select("title").text())?.groupValues?.get(0) - - sourceCallback.invoke( - ExtractorLink( - this.name, - this.name, - link, - referer = "https://animeku.org/", - quality = getQualityFromName(quality), - headers = mapOf("Origin" to "https://animeku.org"), - isM3u8 = true - ) - ) - } - } - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - val document = app.get(data).document - val sources = document.select(".mobius > .mirror > option").mapNotNull { - fixUrl(Jsoup.parse(base64Decode(it.attr("value"))).select("iframe").attr("data-src")) - } - - sources.apmap { - safeApiCall { - when { - it.startsWith("https://animeku.org") -> invokeKuroSource(it, callback) - else -> loadExtractor(it, mainUrl, subtitleCallback, callback) - } - } - } - return true - } - - private fun getBaseUrl(url: String): String { - return URI(url).let { - "${it.scheme}://${it.host}" - } - } - - data class All( - @JsonProperty("post_image") var postImage: String? = null, - @JsonProperty("post_image_html") var postImageHtml: String? = null, - @JsonProperty("ID") var ID: Int? = null, - @JsonProperty("post_title") var postTitle: String? = null, - @JsonProperty("post_genres") var postGenres: String? = null, - @JsonProperty("post_type") var postType: String? = null, - @JsonProperty("post_latest") var postLatest: String? = null, - @JsonProperty("post_sub") var postSub: String? = null, - @JsonProperty("post_link") var postLink: String? = null - ) - - data class Anime( - @JsonProperty("all") var all: ArrayList = arrayListOf(), - ) - - data class Search( - @JsonProperty("anime") var anime: ArrayList = arrayListOf() - ) - -} diff --git a/KuronimeProvider/src/main/kotlin/com/hexated/KuronimeProviderPlugin.kt b/KuronimeProvider/src/main/kotlin/com/hexated/KuronimeProviderPlugin.kt deleted file mode 100644 index acbc54ef..00000000 --- a/KuronimeProvider/src/main/kotlin/com/hexated/KuronimeProviderPlugin.kt +++ /dev/null @@ -1,14 +0,0 @@ - -package com.hexated - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class KuronimeProviderPlugin: Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(KuronimeProvider()) - } -} \ No newline at end of file diff --git a/LayarKacaProvider/build.gradle.kts b/LayarKacaProvider/build.gradle.kts deleted file mode 100644 index 1f1549b9..00000000 --- a/LayarKacaProvider/build.gradle.kts +++ /dev/null @@ -1,27 +0,0 @@ -// use an integer for version numbers -version = 13 - - -cloudstream { - language = "id" - // All of these properties are optional, you can safely remove them - - // description = "Lorem Ipsum" - authors = listOf("Hexated") - - /** - * Status int as the following: - * 0: Down - * 1: Ok - * 2: Slow - * 3: Beta only - * */ - status = 1 // will be 3 if unspecified - tvTypes = listOf( - "AsianDrama", - "TvSeries", - "Movie", - ) - - iconUrl = "https://www.google.com/s2/favicons?domain=lk21official.org&sz=%size%" -} \ No newline at end of file diff --git a/LayarKacaProvider/src/main/AndroidManifest.xml b/LayarKacaProvider/src/main/AndroidManifest.xml deleted file mode 100644 index c98063f8..00000000 --- a/LayarKacaProvider/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/LayarKacaProvider/src/main/kotlin/com/hexated/LayarKacaProvider.kt b/LayarKacaProvider/src/main/kotlin/com/hexated/LayarKacaProvider.kt deleted file mode 100644 index 8b7c03a5..00000000 --- a/LayarKacaProvider/src/main/kotlin/com/hexated/LayarKacaProvider.kt +++ /dev/null @@ -1,208 +0,0 @@ -package com.hexated - -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.LoadResponse.Companion.addActors -import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer -import com.lagradost.cloudstream3.utils.* -import org.jsoup.nodes.Element -import java.net.URLDecoder -import java.net.URI - -class LayarKacaProvider : MainAPI() { - override var mainUrl = "https://d21.fun" - private var seriesUrl = "https://tv.nontondrama.click" - override var name = "LayarKaca" - override val hasMainPage = true - override var lang = "id" - override val hasDownloadSupport = true - override val supportedTypes = setOf( - TvType.Movie, - TvType.TvSeries, - TvType.AsianDrama - ) - - companion object { - const val bananalicious = "https://bananalicious.xyz" - } - - override val mainPage = mainPageOf( - "$mainUrl/populer/page/" to "Film Terplopuler", - "$mainUrl/rating/page/" to "Film Berdasarkan IMDb Rating", - "$mainUrl/most-commented/page/" to "Film Dengan Komentar Terbanyak", - "$seriesUrl/latest/page/" to "Series Terbaru", - "$seriesUrl/series/asian/page/" to "Film Asian Terbaru", - "$mainUrl/latest/page/" to "Film Upload Terbaru", - ) - - override suspend fun getMainPage( - page: Int, - request: MainPageRequest - ): HomePageResponse { - val document = app.get(request.data + page).document - val home = document.select("article.mega-item").mapNotNull { - it.toSearchResult() - } - return newHomePageResponse(request.name, home) - } - - private suspend fun getProperLink(url: String): String? { - val res = app.get(url).document - return if (res.select("title").text().contains("- Nontondrama", true)) { - res.selectFirst("div#content a")?.attr("href") - } else { - url - } - } - - private fun Element.toSearchResult(): SearchResponse? { - val title = this.selectFirst("h1.grid-title > a")?.ownText()?.trim() ?: return null - val href = fixUrl(this.selectFirst("a")!!.attr("href")) - val posterUrl = fixUrlNull(this.selectFirst(".grid-poster > a > img")?.attr("src")) - val type = - if (this.selectFirst("div.last-episode") == null) TvType.Movie else TvType.TvSeries - return if (type == TvType.TvSeries) { - val episode = this.selectFirst("div.last-episode span")?.text()?.filter { it.isDigit() } - ?.toIntOrNull() - newAnimeSearchResponse(title, href, TvType.TvSeries) { - this.posterUrl = posterUrl - addSub(episode) - } - } else { - val quality = this.select("div.quality").text().trim() - newMovieSearchResponse(title, href, TvType.Movie) { - this.posterUrl = posterUrl - addQuality(quality) - } - } - } - - override suspend fun search(query: String): List { - val document = app.get("$mainUrl/?s=$query").document - return document.select("div.search-item").map { - val title = it.selectFirst("h2 > a")!!.text().trim() - val href = fixUrl(it.selectFirst("a")!!.attr("href")) - val posterUrl = fixUrl(it.selectFirst("img.img-thumbnail")?.attr("src").toString()) - newTvSeriesSearchResponse(title, href, TvType.TvSeries) { - this.posterUrl = posterUrl - } - } - } - - override suspend fun load(url: String): LoadResponse? { - val fixUrl = getProperLink(url) - val document = app.get(fixUrl ?: return null).document - - val title = document.selectFirst("li.last > span[itemprop=name]")?.text()?.trim().toString() - val poster = fixUrl(document.select("img.img-thumbnail").attr("src").toString()) - val tags = document.select("div.content > div:nth-child(5) > h3 > a").map { it.text() } - - val year = Regex("\\d, (\\d+)").find( - document.select("div.content > div:nth-child(7) > h3").text().trim() - )?.groupValues?.get(1).toString().toIntOrNull() - val tvType = if (document.select("div.serial-wrapper") - .isNotEmpty() - ) TvType.TvSeries else TvType.Movie - val description = document.select("div.content > blockquote").text().trim() - val trailer = document.selectFirst("div.action-player li > a.fancybox")?.attr("href") - val rating = - document.selectFirst("div.content > div:nth-child(6) > h3")?.text()?.toRatingInt() - val actors = - document.select("div.col-xs-9.content > div:nth-child(3) > h3 > a").map { it.text() } - - val recommendations = document.select("div.row.item-media").map { - val recName = it.selectFirst("h3")?.text()?.trim().toString() - val recHref = it.selectFirst(".content-media > a")!!.attr("href") - val recPosterUrl = - fixUrl(it.selectFirst(".poster-media > a > img")?.attr("src").toString()) - newTvSeriesSearchResponse(recName, recHref, TvType.TvSeries) { - this.posterUrl = recPosterUrl - } - } - - return if (tvType == TvType.TvSeries) { - val episodes = document.select("div.episode-list > a:matches(\\d+)").map { - val href = fixUrl(it.attr("href")) - val episode = it.text().toIntOrNull() - val season = - it.attr("href").substringAfter("season-").substringBefore("-").toIntOrNull() - Episode( - href, - "Episode $episode", - season, - episode, - ) - }.reversed() - newTvSeriesLoadResponse(title, url, TvType.TvSeries, episodes) { - this.posterUrl = poster - this.year = year - this.plot = description - this.tags = tags - this.rating = rating - addActors(actors) - this.recommendations = recommendations - addTrailer(trailer) - } - } else { - newMovieLoadResponse(title, url, TvType.Movie, url) { - this.posterUrl = poster - this.year = year - this.plot = description - this.tags = tags - this.rating = rating - addActors(actors) - this.recommendations = recommendations - addTrailer(trailer) - } - } - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - - val document = app.get(data).document - document.select("ul#loadProviders > li").map { - fixUrl(it.select("a").attr("href")) - }.apmap { - val link = when { - it.startsWith("https://layarkacaxxi.icu") -> { - it.substringBeforeLast("/") - } - it.startsWith(bananalicious) -> decode(it.substringAfter("url=")) - else -> { - it - } - } - invokeCast(link, callback) - } - - return true - } - - private suspend fun invokeCast( - url: String, - callback: (ExtractorLink) -> Unit - ) { - val response = app.get(url, referer = bananalicious).document - response.select("script[type=text/javascript]").map { script -> - if (script.data().contains(Regex("eval\\(function\\(p,a,c,k,e,[rd]"))) { - val unpackedscript = getAndUnpack(script.data()) - val m3u8Regex = Regex("file.\"(.*?m3u8.*?)\"") - val m3u8 = m3u8Regex.find(unpackedscript)?.destructured?.component1() ?: "" - if (m3u8.isNotEmpty()) { - M3u8Helper.generateM3u8( - fixTitle(URI(url).host).substringBefore("."), - m3u8, - mainUrl - ).forEach(callback) - } - } - } - } - - private fun decode(input: String): String = URLDecoder.decode(input, "utf-8").replace(" ", "%20") - -} diff --git a/LayarKacaProvider/src/main/kotlin/com/hexated/LayarKacaProviderPlugin.kt b/LayarKacaProvider/src/main/kotlin/com/hexated/LayarKacaProviderPlugin.kt deleted file mode 100644 index 9743b456..00000000 --- a/LayarKacaProvider/src/main/kotlin/com/hexated/LayarKacaProviderPlugin.kt +++ /dev/null @@ -1,14 +0,0 @@ - -package com.hexated - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class LayarKacaProviderPlugin: Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(LayarKacaProvider()) - } -} \ No newline at end of file diff --git a/Loklok/build.gradle.kts b/Loklok/build.gradle.kts deleted file mode 100644 index 2d502600..00000000 --- a/Loklok/build.gradle.kts +++ /dev/null @@ -1,29 +0,0 @@ -// use an integer for version numbers -version = 26 - - -cloudstream { - language = "en" - // All of these properties are optional, you can safely remove them - - description = "#2 best extension based on Loklok API" - authors = listOf("Hexated") - - /** - * Status int as the following: - * 0: Down - * 1: Ok - * 2: Slow - * 3: Beta only - * */ - status = 0 // will be 3 if unspecified - tvTypes = listOf( - "AsianDrama", - "Anime", - "TvSeries", - "Movie", - ) - - - iconUrl = "https://www.google.com/s2/favicons?domain=loklok.com&sz=%size%" -} \ No newline at end of file diff --git a/Loklok/src/main/AndroidManifest.xml b/Loklok/src/main/AndroidManifest.xml deleted file mode 100644 index c98063f8..00000000 --- a/Loklok/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/Loklok/src/main/kotlin/com/hexated/Loklok.kt b/Loklok/src/main/kotlin/com/hexated/Loklok.kt deleted file mode 100644 index 5e8c8ca5..00000000 --- a/Loklok/src/main/kotlin/com/hexated/Loklok.kt +++ /dev/null @@ -1,428 +0,0 @@ -package com.hexated - -import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.LoadResponse.Companion.addActors -import com.lagradost.cloudstream3.LoadResponse.Companion.addAniListId -import com.lagradost.cloudstream3.LoadResponse.Companion.addMalId -import com.lagradost.cloudstream3.utils.* -import com.lagradost.cloudstream3.utils.AppUtils.parseJson -import com.lagradost.cloudstream3.utils.AppUtils.toJson -import com.lagradost.nicehttp.RequestBodyTypes -import okhttp3.MediaType.Companion.toMediaTypeOrNull -import okhttp3.RequestBody.Companion.toRequestBody - -class Loklok : MainAPI() { - override var name = "Loklok" - override val hasMainPage = true - override val hasChromecastSupport = true - override val instantLinkLoading = true - override val hasQuickSearch = true - override val supportedTypes = setOf( - TvType.Movie, - TvType.TvSeries, - TvType.Anime, - TvType.AsianDrama, - ) - companion object { - private const val geoblockError = "Loklok is Geoblock, use vpn or give up" - private val api = base64DecodeAPI("dg==LnQ=b2s=a2w=bG8=aS4=YXA=ZS0=aWw=b2I=LW0=Z2E=Ly8=czo=dHA=aHQ=") - private val apiUrl = "$api/${base64Decode("Y21zL2FwcA==")}" - private val pcApiUrl = base64DecodeAPI("cGM=Yi8=d2U=cy8=Y20=di8=LnQ=b2s=a2w=bG8=aS4=YXA=ZS0=aWw=b2I=LW0=Z2E=Ly8=czo=dHA=aHQ=") - private val searchApi = base64Decode("aHR0cHM6Ly9sb2tsb2suY29t") - private const val mainImageUrl = "https://images.weserv.nl" - private val headers = mutableMapOf( - "lang" to "en", - "versioncode" to "999999999", - "clienttype" to "ios17", - "deviceid" to getDeviceId(), - ) - - private fun base64DecodeAPI(api: String): String { - return api.chunked(4).map { base64Decode(it) }.reversed().joinToString("") - } - - } - - private fun encode(input: String): String = - java.net.URLEncoder.encode(input, "utf-8").replace("+", "%20") - - override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse { - val home = ArrayList() - for (i in 0..6) { - app.get("$apiUrl/homePage/getHome?page=$i", headers = headers) - .parsedSafe()?.data?.recommendItems.orEmpty().ifEmpty { throw ErrorLoadingException(geoblockError) } - .filterNot { it.homeSectionType == "BLOCK_GROUP" } - .filterNot { it.homeSectionType == "BANNER" } - .mapNotNull { res -> - val header = res.homeSectionName ?: return@mapNotNull null - val media = res.media?.mapNotNull { media -> media.toSearchResponse() } - .orEmpty().ifEmpty { throw ErrorLoadingException(geoblockError) } - home.add(HomePageList(header, media)) - } - } - return HomePageResponse(home) - } - - private fun Media.toSearchResponse(): SearchResponse? { - - return newMovieSearchResponse( - title ?: name ?: return null, - UrlData(id, category ?: domainType).toJson(), - TvType.Movie, - ) { - this.posterUrl = (imageUrl ?: coverVerticalUrl)?.let { - "$mainImageUrl/?url=${encode(it)}&w=175&h=246&fit=cover&output=webp" - } - } - } - - override suspend fun quickSearch(query: String): List? { - val body = mapOf( - "searchKeyWord" to query, - "size" to "50", - "sort" to "", - "searchType" to "", - ).toJson().toRequestBody(RequestBodyTypes.JSON.toMediaTypeOrNull()) - - return app.post( - "$apiUrl/search/v1/searchWithKeyWord", - requestBody = body, - headers = headers - ).parsedSafe()?.data?.searchResults?.mapNotNull { media -> - media.toSearchResponse() - } - } - - override suspend fun search(query: String): List? = quickSearch(query) - -// override suspend fun search(query: String): List { -// val res = app.get( -// "$searchApi/search?keyword=$query", -// ).document -// -// val script = res.select("script").find { it.data().contains("function(a,b,c,d,e") }?.data() -// ?.substringAfter("searchResults:[")?.substringBefore("]}],fetch") -// -// return res.select("div.search-list div.search-video-card").mapIndexed { num, block -> -// val name = block.selectFirst("h2.title")?.text() -// val data = block.selectFirst("a")?.attr("href")?.split("/") -// val id = data?.last() -// val type = data?.get(2)?.toInt() -// val image = Regex("coverVerticalUrl:\"(.*?)\",").findAll(script.toString()) -// .map { it.groupValues[1] }.toList().getOrNull(num)?.replace("\\u002F", "/") -// -// -// newMovieSearchResponse( -// "$name", -// UrlData(id, type).toJson(), -// TvType.Movie, -// ) { -// this.posterUrl = image -// } -// -// } -// -// } - - override suspend fun load(url: String): LoadResponse? { - val data = parseJson(url) - val res = app.get( - "$apiUrl/movieDrama/get?id=${data.id}&category=${data.category}", - headers = headers - ).parsedSafe()?.data ?: throw ErrorLoadingException(geoblockError) - - val actors = res.starList?.mapNotNull { - Actor( - it.localName ?: return@mapNotNull null, it.image - ) - } - - val episodes = res.episodeVo?.map { eps -> - val definition = eps.definitionList?.map { - Definition( - it.code, - it.description, - ) - } - val subtitling = eps.subtitlingList?.map { - Subtitling( - it.languageAbbr, - it.language, - it.subtitlingUrl - ) - } - Episode( - data = UrlEpisode( - data.id.toString(), - data.category, - eps.id, - definition, - subtitling - ).toJson(), - episode = eps.seriesNo - ) - } ?: throw ErrorLoadingException("No Episode Found") - val recommendations = res.likeList?.mapNotNull { rec -> - rec.toSearchResponse() - } - - val type = when { - res.areaList?.firstOrNull()?.id == 44 && res.tagNameList?.contains("Anime") == true -> { - TvType.Anime - } - data.category == 0 -> { - TvType.Movie - } - else -> { - TvType.TvSeries - } - } - - val animeType = if(type == TvType.Anime && data.category == 0) "movie" else "tv" - val (malId, anilistId) = if (type == TvType.Anime) getTracker( - res.name, - animeType, - res.year - ) else Tracker() - - return newTvSeriesLoadResponse( - res.name ?: return null, - url, - if(data.category == 0) TvType.Movie else type, - episodes - ) { - this.posterUrl = res.coverVerticalUrl - this.backgroundPosterUrl = res.coverHorizontalUrl - this.year = res.year - this.plot = res.introduction - this.tags = res.tagNameList - this.rating = res.score.toRatingInt() - addActors(actors) - addMalId(malId) - addAniListId(anilistId?.toIntOrNull()) - this.recommendations = recommendations - } - - } - - private fun getLanguage(str: String): String { - return when (str) { - "in_ID" -> "Indonesian" - "pt" -> "Portuguese" - else -> str.split("_").first().let { - SubtitleHelper.fromTwoLettersToLanguage(it).toString() - } - } - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - val res = parseJson(data) - - res.definitionList?.apmap { video -> - val body = - """[{"category":${res.category},"contentId":"${res.id}","episodeId":${res.epId},"definition":"${video.code}"}]""".toRequestBody( - RequestBodyTypes.JSON.toMediaTypeOrNull() - ) - val json = app.get( - "$apiUrl/media/previewInfo?category=${res.category}&contentId=${res.id}&episodeId=${res.epId}&definition=${video.code}", - headers = headers, - ).parsedSafe()?.data - callback.invoke( - ExtractorLink( - this.name, - this.name, - json?.mediaUrl ?: return@apmap null, - "", - getQuality(json.currentDefinition ?: ""), - isM3u8 = true, - ) - ) - } - - res.subtitlingList?.map { sub -> - subtitleCallback.invoke( - SubtitleFile( - getLanguage(sub.languageAbbr ?: return@map), - sub.subtitlingUrl ?: return@map - ) - ) - } - - return true - } - - private fun getQuality(quality: String): Int { - return when (quality) { - "GROOT_FD" -> Qualities.P360.value - "GROOT_LD" -> Qualities.P480.value - "GROOT_SD" -> Qualities.P720.value - "GROOT_HD" -> Qualities.P1080.value - else -> Qualities.Unknown.value - } - } - - private suspend fun getTracker(title: String?, type: String?, year: Int?): Tracker { - val res = app.get("https://api.consumet.org/meta/anilist/$title") - .parsedSafe()?.results?.find { media -> - (media.title?.english.equals(title, true) || media.title?.romaji.equals( - title, - true - )) || (media.type.equals(type, true) && media.releaseDate == year) - } - return Tracker(res?.malId, res?.aniId, res?.image, res?.cover) - } - - data class Tracker( - val malId: Int? = null, - val aniId: String? = null, - val image: String? = null, - val cover: String? = null, - ) - - data class UrlData( - val id: Any? = null, - val category: Int? = null, - ) - - data class Subtitling( - val languageAbbr: String? = null, - val language: String? = null, - val subtitlingUrl: String? = null, - ) - - data class Definition( - val code: String? = null, - val description: String? = null, - ) - - data class UrlEpisode( - val id: String? = null, - val category: Int? = null, - val epId: Int? = null, - val definitionList: List? = arrayListOf(), - val subtitlingList: List? = arrayListOf(), - ) - - data class Title( - @JsonProperty("romaji") val romaji: String? = null, - @JsonProperty("english") val english: String? = null, - ) - - data class Results( - @JsonProperty("id") val aniId: String? = null, - @JsonProperty("malId") val malId: Int? = null, - @JsonProperty("title") val title: Title? = null, - @JsonProperty("releaseDate") val releaseDate: Int? = null, - @JsonProperty("type") val type: String? = null, - @JsonProperty("image") val image: String? = null, - @JsonProperty("cover") val cover: String? = null, - ) - - data class AniSearch( - @JsonProperty("results") val results: java.util.ArrayList? = arrayListOf(), - ) - - data class QuickSearchData( - @JsonProperty("searchResults") val searchResults: ArrayList? = arrayListOf(), - ) - - data class QuickSearchRes( - @JsonProperty("data") val data: QuickSearchData? = null, - ) - - data class PreviewResponse( - @JsonProperty("data") val data: PreviewVideos? = null, - ) - - data class PreviewVideos( - @JsonProperty("mediaUrl") val mediaUrl: String? = null, - @JsonProperty("currentDefinition") val currentDefinition: String? = null, - ) - - data class SubtitlingList( - @JsonProperty("languageAbbr") val languageAbbr: String? = null, - @JsonProperty("language") val language: String? = null, - @JsonProperty("subtitlingUrl") val subtitlingUrl: String? = null, - ) - - data class DefinitionList( - @JsonProperty("code") val code: String? = null, - @JsonProperty("description") val description: String? = null, - ) - - data class EpisodeVo( - @JsonProperty("id") val id: Int? = null, - @JsonProperty("seriesNo") val seriesNo: Int? = null, - @JsonProperty("definitionList") val definitionList: ArrayList? = arrayListOf(), - @JsonProperty("subtitlingList") val subtitlingList: ArrayList? = arrayListOf(), - ) - - data class Region( - @JsonProperty("id") val id: Int? = null, - @JsonProperty("name") val name: String? = null, - ) - - data class StarList( - @JsonProperty("image") val image: String? = null, - @JsonProperty("localName") val localName: String? = null, - ) - - data class MediaDetail( - @JsonProperty("name") val name: String? = null, - @JsonProperty("introduction") val introduction: String? = null, - @JsonProperty("year") val year: Int? = null, - @JsonProperty("category") val category: String? = null, - @JsonProperty("coverVerticalUrl") val coverVerticalUrl: String? = null, - @JsonProperty("coverHorizontalUrl") val coverHorizontalUrl: String? = null, - @JsonProperty("score") val score: String? = null, - @JsonProperty("starList") val starList: ArrayList? = arrayListOf(), - @JsonProperty("areaList") val areaList: ArrayList? = arrayListOf(), - @JsonProperty("episodeVo") val episodeVo: ArrayList? = arrayListOf(), - @JsonProperty("likeList") val likeList: ArrayList? = arrayListOf(), - @JsonProperty("tagNameList") val tagNameList: ArrayList? = arrayListOf(), - ) - - data class Load( - @JsonProperty("data") val data: MediaDetail? = null, - ) - - data class Media( - @JsonProperty("id") val id: Any? = null, - @JsonProperty("category") val category: Int? = null, - @JsonProperty("domainType") val domainType: Int? = null, - @JsonProperty("imageUrl") val imageUrl: String? = null, - @JsonProperty("coverVerticalUrl") val coverVerticalUrl: String? = null, - @JsonProperty("title") val title: String? = null, - @JsonProperty("name") val name: String? = null, - @JsonProperty("jumpAddress") val jumpAddress: String? = null, - ) - - data class RecommendItems( - @JsonProperty("homeSectionName") val homeSectionName: String? = null, - @JsonProperty("homeSectionType") val homeSectionType: String? = null, - @JsonProperty("recommendContentVOList") val media: ArrayList? = arrayListOf(), - ) - - data class Data( - @JsonProperty("recommendItems") val recommendItems: ArrayList? = arrayListOf(), - ) - - data class Home( - @JsonProperty("data") val data: Data? = null, - ) - -} - -fun getDeviceId(length: Int = 16): String { - val allowedChars = ('a'..'f') + ('0'..'9') - return (1..length) - .map { allowedChars.random() } - .joinToString("") -} - diff --git a/Loklok/src/main/kotlin/com/hexated/LoklokPlugin.kt b/Loklok/src/main/kotlin/com/hexated/LoklokPlugin.kt deleted file mode 100644 index 1d8e807a..00000000 --- a/Loklok/src/main/kotlin/com/hexated/LoklokPlugin.kt +++ /dev/null @@ -1,14 +0,0 @@ - -package com.hexated - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class LoklokPlugin: Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(Loklok()) - } -} \ No newline at end of file diff --git a/Minioppai/build.gradle.kts b/Minioppai/build.gradle.kts deleted file mode 100644 index 3d8a6e96..00000000 --- a/Minioppai/build.gradle.kts +++ /dev/null @@ -1,25 +0,0 @@ -// use an integer for version numbers -version = 3 - - -cloudstream { - language = "id" - // All of these properties are optional, you can safely remove them - - // description = "Lorem Ipsum" - authors = listOf("Sora") - - /** - * Status int as the following: - * 0: Down - * 1: Ok - * 2: Slow - * 3: Beta only - * */ - status = 1 // will be 3 if unspecified - tvTypes = listOf( - "NSFW", - ) - - iconUrl = "https://www.google.com/s2/favicons?domain=minioppai.org&sz=%size%" -} \ No newline at end of file diff --git a/Minioppai/src/main/AndroidManifest.xml b/Minioppai/src/main/AndroidManifest.xml deleted file mode 100644 index c98063f8..00000000 --- a/Minioppai/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/Minioppai/src/main/kotlin/com/hexated/Minioppai.kt b/Minioppai/src/main/kotlin/com/hexated/Minioppai.kt deleted file mode 100644 index ece21181..00000000 --- a/Minioppai/src/main/kotlin/com/hexated/Minioppai.kt +++ /dev/null @@ -1,243 +0,0 @@ -package com.hexated - -import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.utils.* -import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson -import org.jsoup.Jsoup -import org.jsoup.nodes.Element -import java.net.URLDecoder - -class Minioppai : MainAPI() { - override var mainUrl = "https://minioppai.org" - override var name = "Minioppai" - override val hasMainPage = true - override var lang = "id" - override val hasDownloadSupport = true - override val hasQuickSearch = true - - override val supportedTypes = setOf( - TvType.NSFW, - ) - - companion object { - const val libPaistream = "https://lb.paistream.my.id" - const val paistream = "https://paistream.my.id" - - fun getType(t: String): TvType { - return if (t.contains("OVA", true) || t.contains("Special")) TvType.OVA - else if (t.contains("Movie", true)) TvType.AnimeMovie - else TvType.Anime - } - - fun getStatus(t: String?): ShowStatus { - return when (t) { - "Completed" -> ShowStatus.Completed - "Ongoing" -> ShowStatus.Ongoing - else -> ShowStatus.Completed - } - } - - } - - override val mainPage = mainPageOf( - "$mainUrl/watch" to "New Episode", - "$mainUrl/popular" to "Popular Hentai", - ) - - override suspend fun getMainPage( - page: Int, - request: MainPageRequest - ): HomePageResponse { - val document = app.get("${request.data}/page/$page").document - val home = document.select("div.latest a").mapNotNull { - it.toSearchResult() - } - return newHomePageResponse( - list = HomePageList( - name = request.name, - list = home, - isHorizontalImages = request.name == "New Episode" - ), - hasNext = true - ) - } - - private fun getProperAnimeLink(uri: String): String { - return if (uri.contains("-episode-")) { - uri.substringBefore("-episode-") - } else { - uri - } - } - - private fun Element.toSearchResult(): AnimeSearchResponse? { - val title = this.selectFirst("h2.entry-title")?.text()?.trim() ?: return null - val href = getProperAnimeLink(this.attr("href")) - val posterUrl = fixUrlNull(this.selectFirst("img")?.attr("src")) - val epNum = this.selectFirst("i.dot")?.text()?.filter { it.isDigit() }?.toIntOrNull() - return newAnimeSearchResponse(title, href, TvType.NSFW) { - this.posterUrl = posterUrl - addSub(epNum) - } - - } - - override suspend fun quickSearch(query: String): List? = search(query) - - override suspend fun search(query: String): List? { - return app.post( - "$mainUrl/wp-admin/admin-ajax.php", data = mapOf( - "action" to "ts_ac_do_search", - "ts_ac_query" to query, - ), headers = mapOf("X-Requested-With" to "XMLHttpRequest") - ).parsedSafe()?.post?.firstOrNull()?.all?.mapNotNull { item -> - newAnimeSearchResponse( - item.postTitle ?: "", - item.postLink ?: return@mapNotNull null, - TvType.NSFW - ) { - this.posterUrl = item.postImage - } - } - } - - override suspend fun load(url: String): LoadResponse? { - val document = app.get(url).document - - val title = document.selectFirst("h1.entry-title")?.text()?.trim() ?: return null - val poster = fixUrlNull(document.selectFirst("div.limage img")?.attr("src")) - val table = document.select("ul.data") - val tags = table.select("ul.data li:nth-child(1) a").map { it.text() } - val year = - document.selectFirst("ul.data time[itemprop=dateCreated]")?.text()?.substringBefore("-") - ?.toIntOrNull() - val status = getStatus(document.selectFirst("ul.data li:nth-child(2) span")?.text()?.trim()) - val description = document.select("div[itemprop=description] > p").text() - - val episodes = document.select("div.epsdlist ul li").mapNotNull { - val name = it.selectFirst("div.epl-num")?.text() - val link = fixUrlNull(it.selectFirst("a")?.attr("href")) ?: return@mapNotNull null - Episode(link, name = name) - }.reversed() - - return newAnimeLoadResponse(title, url, TvType.NSFW) { - engName = title - posterUrl = poster - this.year = year - addEpisodes(DubStatus.Subbed, episodes) - showStatus = status - plot = description - this.tags = tags - } - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - - val document = app.get(data).document - document.select("div.server ul.mirror li a").mapNotNull { - fixUrl( - Jsoup.parse(base64Decode(it.attr("data-em"))).select("iframe").attr("src") - ) to it.text() - }.apmap { (link, server) -> - if (link.startsWith(paistream)) { - invokeLocal(link, server, subtitleCallback, callback) - } else { - loadExtractor(fixUrl(decode(link.substringAfter("data="))), mainUrl, subtitleCallback, callback) - } - } - - return true - } - - private suspend fun invokeLocal( - url: String, - server: String, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ) { - val script = getAndUnpack(app.get(url, referer="$mainUrl/").text) - val sources = script.substringAfter("sources:[").substringBefore("]").replace("'", "\"") - val subtitles = script.substringAfter("\"tracks\":[").substringBefore("]") - - tryParseJson>("[$sources]")?.map { source -> - val pStream = fixLink(source.file ?: return@map, paistream).takeIf { - app.get( - it, - referer = "$paistream/" - ).isSuccessful - } - callback.invoke( - ExtractorLink( - server, - server, - pStream ?: fixLink(source.file ?: return@map, libPaistream), - "$paistream/", - getQualityFromName(source.label) - ) - ) - } - - tryParseJson>("[$subtitles]")?.map { - subtitleCallback.invoke( - SubtitleFile( - it.label ?: "", - fixLink(it.file ?: return@map, paistream) - ) - ) - } - - } - - private fun decode(input: String): String = URLDecoder.decode(input, "utf-8") - - private fun fixLink(url: String, domain: String): String { - if (url.startsWith("http")) { - return url - } - if (url.isEmpty()) { - return "" - } - - val startsWithNoHttp = url.startsWith("//") - if (startsWithNoHttp) { - return "https:$url" - } else { - if (url.startsWith('/')) { - return domain + url - } - return "$domain/$url" - } - } - - data class Subtitles( - @JsonProperty("file") var file: String? = null, - @JsonProperty("label") var label: String? = null, - ) - - data class Sources( - @JsonProperty("label") var label: String? = null, - @JsonProperty("file") var file: String? = null, - ) - - data class SearchResponses( - @JsonProperty("post") var post: ArrayList = arrayListOf() - ) - - data class All( - @JsonProperty("ID") var ID: Int? = null, - @JsonProperty("post_image") var postImage: String? = null, - @JsonProperty("post_title") var postTitle: String? = null, - @JsonProperty("post_link") var postLink: String? = null, - ) - - data class Post( - @JsonProperty("all") var all: ArrayList = arrayListOf(), - ) - -} \ No newline at end of file diff --git a/Minioppai/src/main/kotlin/com/hexated/MinioppaiPlugin.kt b/Minioppai/src/main/kotlin/com/hexated/MinioppaiPlugin.kt deleted file mode 100644 index 3e99c433..00000000 --- a/Minioppai/src/main/kotlin/com/hexated/MinioppaiPlugin.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.hexated - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class MinioppaiPlugin: Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(Minioppai()) - } -} \ No newline at end of file diff --git a/Moviehab/build.gradle.kts b/Moviehab/build.gradle.kts deleted file mode 100644 index 9676a397..00000000 --- a/Moviehab/build.gradle.kts +++ /dev/null @@ -1,26 +0,0 @@ -// use an integer for version numbers -version = 1 - - -cloudstream { - language = "tl" - // All of these properties are optional, you can safely remove them - - // description = "Lorem Ipsum" - authors = listOf("Hexated") - - /** - * Status int as the following: - * 0: Down - * 1: Ok - * 2: Slow - * 3: Beta only - * */ - status = 1 // will be 3 if unspecified - tvTypes = listOf( - "TvSeries", - "Movie", - ) - - iconUrl = "https://www.google.com/s2/favicons?domain=moviehab.com&sz=%size%" -} \ No newline at end of file diff --git a/Moviehab/src/main/AndroidManifest.xml b/Moviehab/src/main/AndroidManifest.xml deleted file mode 100644 index c98063f8..00000000 --- a/Moviehab/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/Moviehab/src/main/kotlin/com/hexated/Moviehab.kt b/Moviehab/src/main/kotlin/com/hexated/Moviehab.kt deleted file mode 100644 index 8c37067b..00000000 --- a/Moviehab/src/main/kotlin/com/hexated/Moviehab.kt +++ /dev/null @@ -1,205 +0,0 @@ -package com.hexated - -import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer -import com.lagradost.cloudstream3.mvvm.safeApiCall -import com.lagradost.cloudstream3.utils.AppUtils.parseJson -import com.lagradost.cloudstream3.utils.AppUtils.toJson -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.M3u8Helper -import com.lagradost.cloudstream3.utils.loadExtractor -import org.jsoup.nodes.Element - -class Moviehab : MainAPI() { - override var mainUrl = "https://moviehab.com" - override var name = "Moviehab" - override val hasMainPage = true - override var lang = "tl" - override val hasDownloadSupport = true - override val supportedTypes = setOf( - TvType.Movie, - TvType.TvSeries, - ) - - companion object { - private const val mainServer = "https://play.moviehab.com" - } - - override val mainPage = mainPageOf( - "$mainUrl/library/movies?sort_by=imdb_rate&page=" to "Movies by IMDB Rating", - "$mainUrl/library/shows?&sort_by=imdb_rate&page=" to "TV Shows by IMDB Rating", - "$mainUrl/library/movies?&sort_by=year&page=" to "New Movies", - "$mainUrl/library/shows?&sort_by=year&page=" to "New TV Shows", - "$mainUrl/library/movies?country=Philippines&sort_by=year&page=" to "New Philippines Movies", - "$mainUrl/library/shows?&country=Philippines&sort_by=year&page=" to "New Philippines TV Shows", - ) - - override suspend fun getMainPage( - page: Int, - request: MainPageRequest - ): HomePageResponse { - val document = app.get(request.data + page).document - val home = document.select("div.row div.col-6.col-md-4.col-lg-3").mapNotNull { - it.toSearchResult() - } - return newHomePageResponse(request.name, home) - } - - private fun Element.toSearchResult(): SearchResponse? { - val title = this.selectFirst("p.title")?.text() ?: return null - val href = this.selectFirst("div.btn-list a")!!.attr("href") - val posterUrl = fixUrlNull( - this.select("div.poster-img").attr("data-bg-multi").substringAfter("url(") - .substringBefore(")") - ) - val quality = getQualityFromString(this.select("span.badge.bg-dark-dm").text()) - return newMovieSearchResponse(title, href, TvType.Movie) { - this.posterUrl = posterUrl - this.quality = quality - } - } - - override suspend fun search(query: String): List { - val document = app.get( - "$mainUrl/search?term=$query", - headers = mapOf("X-Requested-With" to "XMLHttpRequest") - ).document - return document.select("div.col-6.col-md-4.col-lg-3").mapNotNull { - it.toSearchResult() - } - } - - override suspend fun load(url: String): LoadResponse? { - val document = app.get(url).document - - val title = document.selectFirst("h1.content-title")?.text() ?: return null - val poster = fixUrlNull(document.selectFirst("img.img-fluid.w-full")?.attr("src")) - val tags = document.select("div.content div:nth-child(2) a").map { it.text() } - - val year = document.select("div.content div:nth-child(3) a").text().trim().toIntOrNull() - val tvType = if (document.select("div.card.seasons-list") - .isNullOrEmpty() - ) TvType.Movie else TvType.TvSeries - val description = document.select("div.card:contains(Storyline) p").text().trim() - val trailer = document.selectFirst("div#trailer-modal iframe")?.attr("data-src") - val rating = document.select("div.content div:nth-child(1) span").text().toRatingInt() - - return if (tvType == TvType.TvSeries) { - val episodes = - document.select("div.card.seasons-list select.episodes-select option").map { ele -> - val id = ele.attr("data-id") - val name = ele.text() - val episode = ele.attr("value").toIntOrNull() - val season = ele.parent()?.attr("id")?.filter { it.isDigit() }?.toIntOrNull() - Episode( - Episodes("$episode", "$season", id).toJson(), - name, - season, - episode, - ) - } - newTvSeriesLoadResponse(title, url, TvType.TvSeries, episodes) { - this.posterUrl = poster - this.year = year - this.plot = description - this.tags = tags - this.rating = rating - addTrailer(trailer) - } - } else { - val link = - document.select("div#direct-links-content input#link-1").attr("value").split("/") - .last() - newMovieLoadResponse(title, url, TvType.Movie, Episodes(id = link).toJson()) { - this.posterUrl = poster - this.year = year - this.plot = description - this.tags = tags - this.rating = rating - addTrailer(trailer) - } - } - } - - private suspend fun invokeLokalSource( - url: String, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ) { - val res = app.get(url) - res.document.select("video#player source").attr("src").let { - val link = app.get("$mainServer/$it", referer = url).url - M3u8Helper.generateM3u8( - this.name, - link, - url - ).forEach(callback) - } - - Regex("src[\"|'],\\s[\"|'](\\S+)[\"|']\\)").find(res.text)?.groupValues?.get(1).let {sub -> - subtitleCallback.invoke( - SubtitleFile( - "English", - "$mainServer/$sub" - ) - ) - } - - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - - val res = parseJson(data) - val url = if (res.season.isNullOrBlank()) { - "$mainUrl/embed/${res.id}" - } else { - "$mainUrl/embed/series?id=${res.id}&sea=${res.season}&epi=${res.episode}" - } - - app.get(url).document.select("div.dropdown-menu a.server").apmap { iframe -> - safeApiCall { - app.get( - "$mainUrl/ajax/get_stream_link?id=${iframe.attr("data-id")}&movie=${res.id}&is_init=false&captcha=&ref=", - referer = url, - headers = mapOf("X-Requested-With" to "XMLHttpRequest") - ).parsedSafe()?.data?.link?.let { link -> - if (link.startsWith(mainServer)) { - invokeLokalSource(link, subtitleCallback, callback) - } else { - loadExtractor( - link, - "$mainUrl/", - subtitleCallback, - callback - ) - } - } - } - } - - return true - } - - private data class Source( - @JsonProperty("link") val link: String? = null, - @JsonProperty("_played") val _played: String? = null, - @JsonProperty("token") val token: String? = null, - ) - - private data class Data( - @JsonProperty("data") val data: Source? = null, - ) - - data class Episodes( - val episode: String? = null, - val season: String? = null, - val id: String? = null, - ) - -} \ No newline at end of file diff --git a/Moviehab/src/main/kotlin/com/hexated/MoviehabPlugin.kt b/Moviehab/src/main/kotlin/com/hexated/MoviehabPlugin.kt deleted file mode 100644 index 35ca2e25..00000000 --- a/Moviehab/src/main/kotlin/com/hexated/MoviehabPlugin.kt +++ /dev/null @@ -1,14 +0,0 @@ - -package com.hexated - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class MoviehabPlugin: Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(Moviehab()) - } -} \ No newline at end of file diff --git a/Movierulzhd/build.gradle.kts b/Movierulzhd/build.gradle.kts deleted file mode 100644 index e25e9c0e..00000000 --- a/Movierulzhd/build.gradle.kts +++ /dev/null @@ -1,26 +0,0 @@ -// use an integer for version numbers -version = 36 - - -cloudstream { - language = "hi" - // All of these properties are optional, you can safely remove them - -// description = "Movierulzhd recently have prank feature that the enable and disable cloudflare a " - authors = listOf("Hexated") - - /** - * Status int as the following: - * 0: Down - * 1: Ok - * 2: Slow - * 3: Beta only - * */ - status = 1 // will be 3 if unspecified - tvTypes = listOf( - "TvSeries", - "Movie", - ) - - iconUrl = "https://www.google.com/s2/favicons?domain=movierulzhd.best&sz=%size%" -} diff --git a/Movierulzhd/src/main/AndroidManifest.xml b/Movierulzhd/src/main/AndroidManifest.xml deleted file mode 100644 index c98063f8..00000000 --- a/Movierulzhd/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/Movierulzhd/src/main/kotlin/com/hexated/Extractors.kt b/Movierulzhd/src/main/kotlin/com/hexated/Extractors.kt deleted file mode 100644 index d30ef49b..00000000 --- a/Movierulzhd/src/main/kotlin/com/hexated/Extractors.kt +++ /dev/null @@ -1,148 +0,0 @@ -package com.hexated - -import com.fasterxml.jackson.annotation.JsonProperty -import com.hexated.RabbitStream.extractRabbitStream -import com.lagradost.cloudstream3.APIHolder -import com.lagradost.cloudstream3.SubtitleFile -import com.lagradost.cloudstream3.apmap -import com.lagradost.cloudstream3.app -import com.lagradost.cloudstream3.utils.* -import kotlin.random.Random - -const val twoEmbedAPI = "https://www.2embed.to" - -class Sbrulz : Sbflix() { - override val name = "Sbrulz" - override var mainUrl = "https://sbrulz.xyz" -} - -class Sbmiz : Sbflix() { - override val name = "Sbmiz" - override var mainUrl = "https://sbmiz.site" - } - -open class Sbflix : ExtractorApi() { - override val mainUrl = "https://sbflix.xyz" - override val name = "Sbflix" - override val requiresReferer = false - private val alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" - - override suspend fun getUrl( - url: String, - referer: String?, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ) { - 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 { - it.value.replace(Regex("(embed-|/e/)"), "") - }.first() - val master = "$mainUrl/375664356a494546326c4b797c7c6e756577776778623171737/${encodeId(id)}" - val headers = mapOf( - "watchsb" to "sbstream", - ) - val mapped = app.get( - master.lowercase(), - headers = headers, - referer = url, - ).parsedSafe
() - callback.invoke( - ExtractorLink( - name, - name, - mapped?.streamData?.file ?: return, - url, - Qualities.P720.value, - isM3u8 = true, - headers = headers - ) - ) - - mapped.streamData.subs?.map {sub -> - subtitleCallback.invoke( - SubtitleFile( - sub.label.toString(), - sub.file ?: return@map null, - ) - ) - } - } - - private fun encodeId(id: String): String { - val code = "${createHashTable()}||$id||${createHashTable()}||streamsb" - return code.toCharArray().joinToString("") { char -> - char.code.toString(16) - } - } - - private fun createHashTable(): String { - return buildString { - repeat(12) { - append(alphabet[Random.nextInt(alphabet.length)]) - } - } - } - - data class Subs ( - @JsonProperty("file") val file: String? = null, - @JsonProperty("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, - ) - - data class Main ( - @JsonProperty("stream_data") val streamData: StreamData, - @JsonProperty("status_code") val statusCode: Int, - ) - -} - -suspend fun invokeTwoEmbed( - url: String? = null, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit -) { - val document = app.get(url ?: return).document - val captchaKey = - document.select("script[src*=https://www.google.com/recaptcha/api.js?render=]") - .attr("src").substringAfter("render=") - - document.select(".dropdown-menu a[data-id]").map { it.attr("data-id") }.apmap { serverID -> - val token = APIHolder.getCaptchaToken(url, captchaKey) - app.get( - "${twoEmbedAPI}/ajax/embed/play?id=$serverID&_token=$token", referer = url - ).parsedSafe()?.let { source -> - val link = source.link ?: return@let - if (link.contains("rabbitstream")) { - extractRabbitStream( - link, - subtitleCallback, - callback, - false, - decryptKey = RabbitStream.getKey() - ) { it } - } else { - loadExtractor( - link, twoEmbedAPI, subtitleCallback, callback - ) - } - } - } -} - -data class EmbedJson( - @JsonProperty("type") val type: String? = null, - @JsonProperty("link") val link: String? = null, - @JsonProperty("sources") val sources: List = arrayListOf(), - @JsonProperty("tracks") val tracks: List? = null, -) diff --git a/Movierulzhd/src/main/kotlin/com/hexated/Movierulzhd.kt b/Movierulzhd/src/main/kotlin/com/hexated/Movierulzhd.kt deleted file mode 100644 index 26bc47d1..00000000 --- a/Movierulzhd/src/main/kotlin/com/hexated/Movierulzhd.kt +++ /dev/null @@ -1,273 +0,0 @@ -package com.hexated - -import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.LoadResponse.Companion.addActors -import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer -import com.lagradost.cloudstream3.mvvm.safeApiCall -import com.lagradost.cloudstream3.network.CloudflareKiller -import com.lagradost.cloudstream3.utils.* -import com.lagradost.cloudstream3.utils.AppUtils.toJson -import org.jsoup.nodes.Element -import java.net.URI - -class Movierulzhd : MainAPI() { - override var mainUrl = "https://movierulzhd.trade" - private var directUrl = mainUrl - override var name = "Movierulzhd" - override val hasMainPage = true - override var lang = "hi" - override val hasDownloadSupport = true - override val supportedTypes = setOf( - TvType.Movie, - TvType.TvSeries, - ) - - override val mainPage = mainPageOf( - "$mainUrl/trending/page/" to "Trending", - "$mainUrl/movies/page/" to "Movies", - "$mainUrl/tvshows/page/" to "TV Shows", - "$mainUrl/genre/netflix/page/" to "Netflix", - "$mainUrl/genre/amazon-prime/page/" to "Amazon Prime", - "$mainUrl/genre/Zee5/page/" to "Zee5", - "$mainUrl/seasons/page/" to "Season", - "$mainUrl/episodes/page/" to "Episode", - ) - - private val interceptor = CloudflareKiller() - - override suspend fun getMainPage( - page: Int, - request: MainPageRequest - ): HomePageResponse { - var document = app.get(request.data + page).document - if(document.select("title").text() == "Just a moment...") { - document = app.get(request.data + page, interceptor = interceptor).document - } - val home = - document.select("div.items.normal article, div#archive-content article").mapNotNull { - it.toSearchResult() - } - return newHomePageResponse(request.name, home) - } - - private fun getProperLink(uri: String): String { - return when { - uri.contains("/episodes/") -> { - var title = uri.substringAfter("$mainUrl/episodes/") - title = Regex("(.+?)-season").find(title)?.groupValues?.get(1).toString() - "$mainUrl/tvshows/$title" - } - uri.contains("/seasons/") -> { - var title = uri.substringAfter("$mainUrl/seasons/") - title = Regex("(.+?)-season").find(title)?.groupValues?.get(1).toString() - "$mainUrl/tvshows/$title" - } - else -> { - uri - } - } - } - - private fun Element.toSearchResult(): SearchResponse? { - val title = this.selectFirst("h3 > a")?.text() ?: return null - val href = getProperLink(fixUrl(this.selectFirst("h3 > a")!!.attr("href"))) - val posterUrl = fixUrlNull(this.select("div.poster > img").attr("src")) - val quality = getQualityFromString(this.select("span.quality").text()) - return newMovieSearchResponse(title, href, TvType.Movie) { - this.posterUrl = posterUrl - this.quality = quality - posterHeaders = interceptor.getCookieHeaders(mainUrl).toMap() - } - - } - - override suspend fun search(query: String): List { - val link = "$mainUrl/search/$query" - var document = app.get(link).document - if(document.select("title").text() == "Just a moment...") { - document = app.get(link, interceptor = interceptor).document - } - - return document.select("div.result-item").map { - val title = - it.selectFirst("div.title > a")!!.text().replace(Regex("\\(\\d{4}\\)"), "").trim() - val href = getProperLink(it.selectFirst("div.title > a")!!.attr("href")) - val posterUrl = it.selectFirst("img")!!.attr("src").toString() - newMovieSearchResponse(title, href, TvType.TvSeries) { - this.posterUrl = posterUrl - posterHeaders = interceptor.getCookieHeaders(mainUrl).toMap() - } - } - } - - override suspend fun load(url: String): LoadResponse { - val request = app.get(url) - var document = request.document - if(document.select("title").text() == "Just a moment...") { - document = app.get(url, interceptor = interceptor).document - } - directUrl = getBaseUrl(request.url) - val title = - document.selectFirst("div.data > h1")?.text()?.trim().toString() - val poster = document.select("div.poster > img").attr("src").toString() - val tags = document.select("div.sgeneros > a").map { it.text() } - - val year = Regex(",\\s?(\\d+)").find( - document.select("span.date").text().trim() - )?.groupValues?.get(1).toString().toIntOrNull() - val tvType = if (document.select("ul#section > li:nth-child(1)").text() - .contains("Episodes") || document.selectFirst("ul#playeroptionsul li span.title") - ?.text()?.contains( - Regex("Episode\\s\\d+") - ) == true - ) TvType.TvSeries else TvType.Movie - val description = document.select("div.wp-content > p").text().trim() - val trailer = document.selectFirst("div.embed iframe")?.attr("src") - val rating = - document.selectFirst("span.dt_rating_vgs")?.text()?.toRatingInt() - val actors = document.select("div.persons > div[itemprop=actor]").map { - Actor(it.select("meta[itemprop=name]").attr("content"), it.select("img").attr("src")) - } - - val recommendations = document.select("div.owl-item").map { - val recName = - it.selectFirst("a")!!.attr("href").toString().removeSuffix("/").split("/").last() - val recHref = it.selectFirst("a")!!.attr("href") - val recPosterUrl = it.selectFirst("img")?.attr("src").toString() - newTvSeriesSearchResponse(recName, recHref, TvType.TvSeries) { - this.posterUrl = recPosterUrl - posterHeaders = interceptor.getCookieHeaders(url).toMap() - } - } - - return if (tvType == TvType.TvSeries) { - val episodes = if(document.select("ul.episodios > li").isNotEmpty()) { - document.select("ul.episodios > li").map { - val href = it.select("a").attr("href") - val name = fixTitle(it.select("div.episodiotitle > a").text().trim()) - val image = it.select("div.imagen > img").attr("src") - val episode = it.select("div.numerando").text().replace(" ", "").split("-").last() - .toIntOrNull() - val season = it.select("div.numerando").text().replace(" ", "").split("-").first() - .toIntOrNull() - Episode( - href, - name, - season, - episode, - image - ) - } - } else { - document.select("ul#playeroptionsul > li").map { - val name = it.selectFirst("span.title")?.text() - val type = it.attr("data-type") - val post = it.attr("data-post") - val nume = it.attr("data-nume") - Episode( - LinkData(type, post, nume).toJson(), - name, - ) - } - } - newTvSeriesLoadResponse(title, url, TvType.TvSeries, episodes) { - this.posterUrl = poster - this.year = year - this.plot = description - this.tags = tags - this.rating = rating - addActors(actors) - this.recommendations = recommendations - addTrailer(trailer) - posterHeaders = interceptor.getCookieHeaders(url).toMap() - } - } else { - newMovieLoadResponse(title, url, TvType.Movie, url) { - this.posterUrl = poster - this.year = year - this.plot = description - this.tags = tags - this.rating = rating - addActors(actors) - this.recommendations = recommendations - addTrailer(trailer) - posterHeaders = interceptor.getCookieHeaders(url).toMap() - } - } - } - - private fun getBaseUrl(url: String): String { - return URI(url).let { - "${it.scheme}://${it.host}" - } - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - - if(data.startsWith("{")) { - val loadData = AppUtils.tryParseJson(data) - val source = app.post( - url = "$directUrl/wp-admin/admin-ajax.php", - data = mapOf( - "action" to "doo_player_ajax", - "post" to "${loadData?.post}", - "nume" to "${loadData?.nume}", - "type" to "${loadData?.type}" - ), - referer = data, - headers = mapOf("X-Requested-With" to "XMLHttpRequest") - ).parsed().embed_url - if(!source.contains("youtube")) loadExtractor(source, data, subtitleCallback, callback) - } else { - var document = app.get(data).document - if (document.select("title").text() == "Just a moment...") { - document = app.get(data, interceptor = interceptor).document - } - val id = document.select("meta#dooplay-ajax-counter").attr("data-postid") - val type = if (data.contains("/movies/")) "movie" else "tv" - - document.select("ul#playeroptionsul > li").map { - it.attr("data-nume") - }.apmap { nume -> - safeApiCall { - val source = app.post( - url = "$directUrl/wp-admin/admin-ajax.php", - data = mapOf( - "action" to "doo_player_ajax", - "post" to id, - "nume" to nume, - "type" to type - ), - referer = data, - headers = mapOf("X-Requested-With" to "XMLHttpRequest") - ).parsed().embed_url - - when { - source.contains("2embed") -> invokeTwoEmbed(source,subtitleCallback, callback) - !source.contains("youtube") -> loadExtractor(source, data, subtitleCallback, callback) - else -> return@safeApiCall - } - } - } - } - return true - } - - data class LinkData( - val type: String? = null, - val post: String? = null, - val nume: String? = null, - ) - - data class ResponseHash( - @JsonProperty("embed_url") val embed_url: String, - @JsonProperty("type") val type: String?, - ) - -} diff --git a/Movierulzhd/src/main/kotlin/com/hexated/MovierulzhdPlugin.kt b/Movierulzhd/src/main/kotlin/com/hexated/MovierulzhdPlugin.kt deleted file mode 100644 index fb2bf567..00000000 --- a/Movierulzhd/src/main/kotlin/com/hexated/MovierulzhdPlugin.kt +++ /dev/null @@ -1,17 +0,0 @@ - -package com.hexated - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class MovierulzhdPlugin: Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(Movierulzhd()) - registerExtractorAPI(Sbflix()) - registerExtractorAPI(Sbrulz()) - registerExtractorAPI(Sbmiz()) - } -} \ No newline at end of file diff --git a/Movierulzhd/src/main/kotlin/com/hexated/RabbitStream.kt b/Movierulzhd/src/main/kotlin/com/hexated/RabbitStream.kt deleted file mode 100644 index 41b73992..00000000 --- a/Movierulzhd/src/main/kotlin/com/hexated/RabbitStream.kt +++ /dev/null @@ -1,302 +0,0 @@ -package com.hexated - -import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.mvvm.suspendSafeApiCall -import com.lagradost.cloudstream3.utils.AppUtils -import com.lagradost.cloudstream3.utils.Coroutines.ioSafe -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.M3u8Helper -import com.lagradost.cloudstream3.utils.getQualityFromName -import kotlinx.coroutines.delay -import okhttp3.RequestBody.Companion.toRequestBody -import java.net.URI -import java.nio.charset.StandardCharsets -import java.security.MessageDigest -import java.util.* -import javax.crypto.Cipher -import javax.crypto.spec.IvParameterSpec -import javax.crypto.spec.SecretKeySpec -import kotlin.collections.ArrayList - -object RabbitStream { - - suspend fun extractRabbitStream( - url: String, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit, - useSidAuthentication: Boolean, - /** Used for extractorLink name, input: Source name */ - extractorData: String? = null, - decryptKey: String? = null, - nameTransformer: (String) -> String, - ) = suspendSafeApiCall { - // https://rapid-cloud.ru/embed-6/dcPOVRE57YOT?z= -> https://rapid-cloud.ru/embed-6 - val mainIframeUrl = - url.substringBeforeLast("/") - val mainIframeId = url.substringAfterLast("/") - .substringBefore("?") // https://rapid-cloud.ru/embed-6/dcPOVRE57YOT?z= -> dcPOVRE57YOT - var sid: String? = null - if (useSidAuthentication && extractorData != null) { - negotiateNewSid(extractorData)?.also { pollingData -> - app.post( - "$extractorData&t=${generateTimeStamp()}&sid=${pollingData.sid}", - requestBody = "40".toRequestBody(), - timeout = 60 - ) - val text = app.get( - "$extractorData&t=${generateTimeStamp()}&sid=${pollingData.sid}", - timeout = 60 - ).text.replaceBefore("{", "") - - sid = AppUtils.parseJson(text).sid - ioSafe { app.get("$extractorData&t=${generateTimeStamp()}&sid=${pollingData.sid}") } - } - } - val getSourcesUrl = "${ - mainIframeUrl.replace( - "/embed", - "/ajax/embed" - ) - }/getSources?id=$mainIframeId${sid?.let { "$&sId=$it" } ?: ""}" - val response = app.get( - getSourcesUrl, - referer = "${Movierulzhd().mainUrl}/", - headers = mapOf( - "X-Requested-With" to "XMLHttpRequest", - "Accept" to "*/*", - "Accept-Language" to "en-US,en;q=0.5", - "Connection" to "keep-alive", - "TE" to "trailers" - ) - ) - - val sourceObject = if (decryptKey != null) { - val encryptedMap = response.parsedSafe() - val sources = encryptedMap?.sources - if (sources == null || encryptedMap.encrypted == false) { - response.parsedSafe() - } else { - val decrypted = - decryptMapped>(sources, decryptKey) - SourceObject( - sources = decrypted, - tracks = encryptedMap.tracks - ) - } - } else { - response.parsedSafe() - } ?: return@suspendSafeApiCall - - sourceObject.tracks?.forEach { track -> - track?.toSubtitleFile()?.let { subtitleFile -> - subtitleCallback.invoke(subtitleFile) - } - } - - val list = listOf( - sourceObject.sources to "source 1", - sourceObject.sources1 to "source 2", - sourceObject.sources2 to "source 3", - sourceObject.sourcesBackup to "source backup" - ) - - list.forEach { subList -> - subList.first?.forEach { source -> - source?.toExtractorLink( - "Vidcloud", - "$twoEmbedAPI/", - extractorData, - ) - ?.forEach { - // Sets Zoro SID used for video loading -// (this as? ZoroProvider)?.sid?.set(it.url.hashCode(), sid) - callback(it) - } - } - } - } - - private suspend fun Sources.toExtractorLink( - name: String, - referer: String, - extractorData: String? = null, - ): List? { - return this.file?.let { file -> - //println("FILE::: $file") - val isM3u8 = URI(this.file).path.endsWith(".m3u8") || this.type.equals( - "hls", - ignoreCase = true - ) - return if (isM3u8) { - suspendSafeApiCall { - M3u8Helper().m3u8Generation( - M3u8Helper.M3u8Stream( - this.file, - null, - mapOf("Referer" to "https://mzzcloud.life/") - ), false - ) - .map { stream -> - ExtractorLink( - name, - name, - stream.streamUrl, - referer, - getQualityFromName(stream.quality?.toString()), - true, - extractorData = extractorData - ) - } - }.takeIf { !it.isNullOrEmpty() } ?: listOf( - // Fallback if m3u8 extractor fails - ExtractorLink( - name, - name, - this.file, - referer, - getQualityFromName(this.label), - isM3u8, - extractorData = extractorData - ) - ) - } else { - listOf( - ExtractorLink( - name, - name, - file, - referer, - getQualityFromName(this.label), - false, - extractorData = extractorData - ) - ) - } - } - } - - private fun Tracks.toSubtitleFile(): SubtitleFile? { - return this.file?.let { - SubtitleFile( - this.label ?: "Unknown", - it - ) - } - } - - /** - * Generates a session - * 1 Get request. - * */ - private suspend fun negotiateNewSid(baseUrl: String): PollingData? { - // Tries multiple times - for (i in 1..5) { - val jsonText = - app.get("$baseUrl&t=${generateTimeStamp()}").text.replaceBefore( - "{", - "" - ) -// println("Negotiated sid $jsonText") - AppUtils.parseJson(jsonText)?.let { return it } - delay(1000L * i) - } - return null - } - - private fun generateTimeStamp(): String { - val chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_" - var code = "" - var time = APIHolder.unixTimeMS - while (time > 0) { - code += chars[(time % (chars.length)).toInt()] - time /= chars.length - } - return code.reversed() - } - - suspend fun getKey(): String { - return app.get("https://raw.githubusercontent.com/enimax-anime/key/e4/key.txt") - .text - } - - private inline fun decryptMapped(input: String, key: String): T? { - return AppUtils.tryParseJson(decrypt(input, key)) - } - - private fun decrypt(input: String, key: String): String { - return decryptSourceUrl( - generateKey( - base64DecodeArray(input).copyOfRange(8, 16), - key.toByteArray() - ), input - ) - } - - private fun generateKey(salt: ByteArray, secret: ByteArray): ByteArray { - var key = md5(secret + salt) - var currentKey = key - while (currentKey.size < 48) { - key = md5(key + secret + salt) - currentKey += key - } - return currentKey - } - - private fun md5(input: ByteArray): ByteArray { - return MessageDigest.getInstance("MD5").digest(input) - } - - private fun decryptSourceUrl(decryptionKey: ByteArray, sourceUrl: String): String { - val cipherData = base64DecodeArray(sourceUrl) - val encrypted = cipherData.copyOfRange(16, cipherData.size) - val aesCBC = Cipher.getInstance("AES/CBC/PKCS5Padding") - - Objects.requireNonNull(aesCBC).init( - Cipher.DECRYPT_MODE, SecretKeySpec( - decryptionKey.copyOfRange(0, 32), - "AES" - ), - IvParameterSpec(decryptionKey.copyOfRange(32, decryptionKey.size)) - ) - val decryptedData = aesCBC!!.doFinal(encrypted) - return String(decryptedData, StandardCharsets.UTF_8) - } - - data class PollingData( - @JsonProperty("sid") val sid: String? = null, - @JsonProperty("upgrades") val upgrades: ArrayList = arrayListOf(), - @JsonProperty("pingInterval") val pingInterval: Int? = null, - @JsonProperty("pingTimeout") val pingTimeout: Int? = null - ) - - data class Tracks( - @JsonProperty("file") val file: String?, - @JsonProperty("label") val label: String?, - @JsonProperty("kind") val kind: String? - ) - - data class Sources( - @JsonProperty("file") val file: String?, - @JsonProperty("type") val type: String?, - @JsonProperty("label") val label: String? - ) - - data class SourceObject( - @JsonProperty("sources") val sources: List? = null, - @JsonProperty("sources_1") val sources1: List? = null, - @JsonProperty("sources_2") val sources2: List? = null, - @JsonProperty("sourcesBackup") val sourcesBackup: List? = null, - @JsonProperty("tracks") val tracks: List? = null - ) - - data class SourceObjectEncrypted( - @JsonProperty("sources") val sources: String?, - @JsonProperty("encrypted") val encrypted: Boolean?, - @JsonProperty("sources_1") val sources1: String?, - @JsonProperty("sources_2") val sources2: String?, - @JsonProperty("sourcesBackup") val sourcesBackup: String?, - @JsonProperty("tracks") val tracks: List? - ) - -} \ No newline at end of file diff --git a/MultiplexProvider/build.gradle.kts b/MultiplexProvider/build.gradle.kts deleted file mode 100644 index aaeb74f5..00000000 --- a/MultiplexProvider/build.gradle.kts +++ /dev/null @@ -1,27 +0,0 @@ -// use an integer for version numbers -version = 1 - - -cloudstream { - language = "id" - // All of these properties are optional, you can safely remove them - - // description = "Lorem Ipsum" - authors = listOf("Hexated") - - /** - * Status int as the following: - * 0: Down - * 1: Ok - * 2: Slow - * 3: Beta only - * */ - status = 1 // will be 3 if unspecified - tvTypes = listOf( - "AsianDrama", - "TvSeries", - "Movie", - ) - - iconUrl = "https://www.google.com/s2/favicons?domain=146.19.24.137&sz=%size%" -} \ No newline at end of file diff --git a/MultiplexProvider/src/main/AndroidManifest.xml b/MultiplexProvider/src/main/AndroidManifest.xml deleted file mode 100644 index c98063f8..00000000 --- a/MultiplexProvider/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/MultiplexProvider/src/main/kotlin/com/hexated/MultiplexProvider.kt b/MultiplexProvider/src/main/kotlin/com/hexated/MultiplexProvider.kt deleted file mode 100644 index 851c267e..00000000 --- a/MultiplexProvider/src/main/kotlin/com/hexated/MultiplexProvider.kt +++ /dev/null @@ -1,188 +0,0 @@ -package com.hexated - -import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.LoadResponse.Companion.addActors -import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer -import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.getQualityFromName -import org.jsoup.nodes.Element - -class MultiplexProvider : MainAPI() { - override var mainUrl = "https://146.19.24.137" - override var name = "Multiplex" - override val hasMainPage = true - override var lang = "id" - override val hasDownloadSupport = true - override val supportedTypes = setOf( - TvType.Movie, - TvType.TvSeries, - TvType.AsianDrama - ) - - override val mainPage = mainPageOf( - "$mainUrl/genre/top-popular-movies/page/" to "Top Popolar Movies", - "$mainUrl/genre/series-ongoing/page/" to "Series Ongoing", - "$mainUrl/genre/series-barat/page/" to "Series Barat", - "$mainUrl/genre/series-korea/page/" to "Series Korea", - ) - - override suspend fun getMainPage( - page: Int, - request: MainPageRequest - ): HomePageResponse { - val document = app.get(request.data + page).document - val home = document.select("article.item").mapNotNull { - it.toSearchResult() - } - return newHomePageResponse(request.name, home) - } - - private fun Element.toSearchResult(): SearchResponse? { - val title = this.selectFirst("h2.entry-title > a")?.text()?.trim() ?: return null - val href = fixUrl(this.selectFirst("a")!!.attr("href")) - val posterUrl = fixUrlNull(this.selectFirst("a > img")?.attr("data-src")) - val quality = this.select("div.gmr-quality-item > a").text().trim() - return if (quality.isEmpty()) { - val episode = this.select("div.gmr-numbeps > span").text().toIntOrNull() - newAnimeSearchResponse(title, href, TvType.TvSeries) { - this.posterUrl = posterUrl - addSub(episode) - } - } else { - newMovieSearchResponse(title, href, TvType.Movie) { - this.posterUrl = posterUrl - addQuality(quality) - } - } - } - - private fun Element.toBottomSearchResult(): SearchResponse? { - val title = this.selectFirst("a > span.idmuvi-rp-title")?.text()?.trim() ?: return null - val href = this.selectFirst("a")!!.attr("href") - val posterUrl = fixUrl(this.selectFirst("a > img")?.attr("data-src").toString()) - return newMovieSearchResponse(title, href, TvType.Movie) { - this.posterUrl = posterUrl - } - } - - override suspend fun search(query: String): List { - val link = "$mainUrl/?s=$query&post_type[]=post&post_type[]=tv" - val document = app.get(link).document - return document.select("article.item").mapNotNull { - it.toSearchResult() - } - } - - override suspend fun load(url: String): LoadResponse { - val document = app.get(url).document - - val title = - document.selectFirst("h1.entry-title")?.text()?.substringBefore("Season")?.trim() - .toString() - val poster = - fixUrl(document.selectFirst("figure.pull-left > img")?.attr("data-src").toString()) - val tags = document.select("span.gmr-movie-genre:contains(Genre:) > a").map { it.text() } - - val year = - document.select("span.gmr-movie-genre:contains(Year:) > a").text().trim().toIntOrNull() - val tvType = if (url.contains("/tv/")) TvType.TvSeries else TvType.Movie - val description = document.selectFirst("div[itemprop=description] > p")?.text()?.trim() - val trailer = document.selectFirst("ul.gmr-player-nav li a.gmr-trailer-popup")?.attr("href") - val rating = - document.selectFirst("div.gmr-meta-rating > span[itemprop=ratingValue]")?.text() - ?.toRatingInt() - val actors = document.select("div.gmr-moviedata").last()?.select("span[itemprop=actors]") - ?.map { it.select("a").text() } - - val recommendations = document.select("div.idmuvi-rp ul li").mapNotNull { - it.toBottomSearchResult() - } - - return if (tvType == TvType.TvSeries) { - val episodes = document.select("div.gmr-listseries > a").map { - val href = fixUrl(it.attr("href")) - val episode = it.text().split(" ").last().toIntOrNull() - val season = it.text().split(" ").first().substringAfter("S").toIntOrNull() - Episode( - href, - "Episode $episode", - season, - episode, - ) - } - newTvSeriesLoadResponse(title, url, TvType.TvSeries, episodes) { - this.posterUrl = poster - this.year = year - this.plot = description - this.tags = tags - this.rating = rating - addActors(actors) - this.recommendations = recommendations - addTrailer(trailer) - } - } else { - newMovieLoadResponse(title, url, TvType.Movie, url) { - this.posterUrl = poster - this.year = year - this.plot = description - this.tags = tags - this.rating = rating - addActors(actors) - this.recommendations = recommendations - addTrailer(trailer) - } - } - } - - private data class ResponseSource( - @JsonProperty("file") val file: String, - @JsonProperty("type") val type: String?, - @JsonProperty("label") val label: String? - ) - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - - val document = app.get(data).document - - val id = document.selectFirst("div#muvipro_player_content_id")!!.attr("data-id") - val server = app.post( - "$mainUrl/wp-admin/admin-ajax.php", - data = mapOf("action" to "muvipro_player_content", "tab" to "player1", "post_id" to id) - ).document.select("iframe").attr("src") - - app.get(server, referer = "$mainUrl/").document.select("script").map { script -> - if (script.data().contains("var config = {")) { - val source = script.data().substringAfter("sources: [").substringBefore("],") - tryParseJson>("[$source]")?.map { m3u -> - val m3uData = app.get(m3u.file, referer = "https://gdriveplayer.link/").text - val quality = - Regex("\\d{3,4}\\.m3u8").findAll(m3uData).map { it.value }.toList() - quality.forEach { - callback.invoke( - ExtractorLink( - source = name, - name = name, - url = m3u.file.replace("video.m3u8", it), - referer = "https://gdriveplayer.link/", - quality = getQualityFromName("${it.replace(".m3u8", "")}p"), - isM3u8 = true - ) - ) - } - } - } - } - - return true - - } - - -} \ No newline at end of file diff --git a/MultiplexProvider/src/main/kotlin/com/hexated/MultiplexProviderPlugin.kt b/MultiplexProvider/src/main/kotlin/com/hexated/MultiplexProviderPlugin.kt deleted file mode 100644 index f689b83f..00000000 --- a/MultiplexProvider/src/main/kotlin/com/hexated/MultiplexProviderPlugin.kt +++ /dev/null @@ -1,14 +0,0 @@ - -package com.hexated - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class MultiplexProviderPlugin: Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(MultiplexProvider()) - } -} \ No newline at end of file diff --git a/NeonimeProvider/build.gradle.kts b/NeonimeProvider/build.gradle.kts deleted file mode 100644 index 657d7382..00000000 --- a/NeonimeProvider/build.gradle.kts +++ /dev/null @@ -1,28 +0,0 @@ -// use an integer for version numbers -version = 7 - - -cloudstream { - language = "id" - // All of these properties are optional, you can safely remove them - - // description = "Lorem Ipsum" - authors = listOf("Hexated") - - /** - * Status int as the following: - * 0: Down - * 1: Ok - * 2: Slow - * 3: Beta only - * */ - status = 1 // will be 3 if unspecified - tvTypes = listOf( - "AnimeMovie", - "Anime", - "Movie", - "OVA", - ) - - iconUrl = "https://www.google.com/s2/favicons?domain=neonime.watch&sz=%size%" -} \ No newline at end of file diff --git a/NeonimeProvider/src/main/AndroidManifest.xml b/NeonimeProvider/src/main/AndroidManifest.xml deleted file mode 100644 index c98063f8..00000000 --- a/NeonimeProvider/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/NeonimeProvider/src/main/kotlin/com/hexated/NeonimeProvider.kt b/NeonimeProvider/src/main/kotlin/com/hexated/NeonimeProvider.kt deleted file mode 100644 index bd7059d5..00000000 --- a/NeonimeProvider/src/main/kotlin/com/hexated/NeonimeProvider.kt +++ /dev/null @@ -1,186 +0,0 @@ -package com.hexated - -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.loadExtractor -import org.jsoup.nodes.Element -import java.net.URI - -class NeonimeProvider : MainAPI() { - override var mainUrl = "https://neonime.fun" - private var baseUrl = mainUrl - override var name = "Neonime" - override val hasQuickSearch = false - override val hasMainPage = true - override var lang = "id" - override val hasDownloadSupport = true - - override val supportedTypes = setOf( - TvType.Anime, - TvType.AnimeMovie, - TvType.OVA - ) - - companion object { - fun getType(t: String): TvType { - return if (t.contains("OVA", true) || t.contains("Special", true)) TvType.OVA - else if (t.contains("Movie", true)) TvType.AnimeMovie - else TvType.Anime - } - - fun getStatus(t: String): ShowStatus { - return when (t) { - "Ended" -> ShowStatus.Completed - "OnGoing" -> ShowStatus.Ongoing - "Ongoing" -> ShowStatus.Ongoing - "In Production" -> ShowStatus.Ongoing - "Returning Series" -> ShowStatus.Ongoing - else -> ShowStatus.Completed - } - } - } - - override val mainPage = mainPageOf( - "$mainUrl/episode/page/" to "Episode Terbaru", - "$mainUrl/tvshows/page/" to "Anime Terbaru", - "$mainUrl/movies/page/" to "Movie", - ) - - override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse { - val req = app.get(request.data + page) - baseUrl = getBaseUrl(req.url) - val home = req.document.select("tbody tr,div.item").mapNotNull { - it.toSearchResult() - } - return newHomePageResponse(request.name, home) - } - - private fun getProperAnimeLink(uri: String): String { - return when { - uri.contains("/episode") -> { - val title = uri.substringAfter("$baseUrl/episode/").let { tt -> - val fixTitle = Regex("(.*)-\\d{1,2}x\\d+").find(tt)?.groupValues?.getOrNull(1).toString() - when { - !tt.contains("-season") && !tt.contains(Regex("-1x\\d+")) && !tt.contains("one-piece") -> "$fixTitle-season-${Regex("-(\\d{1,2})x\\d+").find(tt)?.groupValues?.getOrNull(1).toString()}" - tt.contains("-special") -> fixTitle.replace(Regex("-x\\d+"), "") - !fixTitle.contains("-subtitle-indonesia") -> "$fixTitle-subtitle-indonesia" - else -> fixTitle - } - } - -// title = when { -// title.contains("youkoso-jitsuryoku") && !title.contains("-season") -> title.replace("-e-", "-e-tv-") -// else -> title -// } - - "$baseUrl/tvshows/$title" - } - else -> uri - } - } - - private fun Element.toSearchResult(): AnimeSearchResponse? { - val title = this.selectFirst("td.bb a")?.ownText() ?: this.selectFirst("h2")?.text() ?: return null - val href = getProperAnimeLink(fixUrl(this.select("a").attr("href"))) - val posterUrl = fixUrl(this.select("img").attr("data-src")) - val epNum = this.selectFirst("td.bb span")?.text()?.let { eps -> - Regex("Episode\\s?(\\d+)").find(eps)?.groupValues?.getOrNull(1)?.toIntOrNull() - } - - return newAnimeSearchResponse(title, href, TvType.Anime) { - this.posterUrl = posterUrl - addSub(epNum) - } - - } - - override suspend fun search(query: String): List { - val document = app.get("$baseUrl/?s=$query").document - return document.select("div.item.episode-home").mapNotNull { - val title = it.selectFirst("div.judul-anime > span")!!.text() - val poster = it.select("img").attr("data-src").toString().trim() - val episodes = it.selectFirst("div.fixyear > h2.text-center")!! - .text().replace(Regex("\\D"), "").trim().toIntOrNull() - val tvType = getType(it.selectFirst("span.calidad2.episode")?.text().toString()) - val href = getProperAnimeLink(fixUrl(it.selectFirst("a")!!.attr("href"))) - - newAnimeSearchResponse(title, href, tvType) { - this.posterUrl = poster - addSub(episodes) - } - } - } - - override suspend fun load(url: String): LoadResponse { - val document = app.get(url).document - - if (url.contains("movie") || url.contains("live-action")) { - val mTitle = document.selectFirst(".sbox > .data > h1[itemprop = name]")?.text().toString().replace("Subtitle Indonesia", "").trim() - val mPoster = document.selectFirst(".sbox > .imagen > .fix > img[itemprop = image]")?.attr("data-src") - val mTrailer = document.selectFirst("div.youtube_id iframe")?.attr("data-wpfc-original-src")?.substringAfterLast("html#")?.let{ "https://www.youtube.com/embed/$it"} - val year = document.selectFirst("a[href*=release-year]")!!.text().toIntOrNull() - return newMovieLoadResponse(name = mTitle, url = url, type = TvType.Movie, dataUrl = url) { - posterUrl = mPoster - this.year = year - plot = document.select("div[itemprop = description]").text().trim() - rating = document.select("span[itemprop = ratingValue]").text().toIntOrNull() - tags = document.select("p.meta_dd > a").map { it.text() } - addTrailer(mTrailer) - } - } - else { - val title = document.select("h1[itemprop = name]").text().replace("Subtitle Indonesia", "").trim() - val poster = document.selectFirst(".imagen > img")?.attr("data-src") - val trailer = document.selectFirst("div.youtube_id_tv iframe")?.attr("data-wpfc-original-src")?.substringAfterLast("html#")?.let{ "https://www.youtube.com/embed/$it"} - val year = document.select("#info a[href*=\"-year/\"]").text().toIntOrNull() - val episodes = document.select("ul.episodios > li").mapNotNull { - val link = fixUrl(it.selectFirst(".episodiotitle > a")!!.attr("href")) - val name = it.selectFirst(".episodiotitle > a")?.ownText().toString() - val episode = Regex("(\\d+[.,]?\\d*)").find(name)?.groupValues?.getOrNull(0)?.toIntOrNull() - Episode(link, name, episode = episode) - }.reversed() - - return newAnimeLoadResponse(title, url, TvType.Anime) { - engName = title - posterUrl = poster - this.year = year - addEpisodes(DubStatus.Subbed, episodes) - showStatus = getStatus(document.select("div.metadatac > span").last()!!.text().trim()) - plot = document.select("div[itemprop = description] > p").text().trim() - tags = document.select("#info a[href*=\"-genre/\"]").map { it.text() } - addTrailer(trailer) - } - } - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - val source = if(data.contains("movie") || data.contains("live-action")) { - app.get(data).document.select("#player2-1 > div[id*=div]").mapNotNull { - fixUrl(it.select("iframe").attr("data-src")) - } - } else { - app.get(data).document.select(".player2 > .embed2 > div[id*=player]").mapNotNull { - fixUrl(it.select("iframe").attr("data-src")) - } - } - - source.apmap { - loadExtractor(it, data, subtitleCallback, callback) - } - - return true - } - - private fun getBaseUrl(url: String): String { - return URI(url).let { - "${it.scheme}://${it.host}" - } - } - -} \ No newline at end of file diff --git a/NeonimeProvider/src/main/kotlin/com/hexated/NeonimeProviderPlugin.kt b/NeonimeProvider/src/main/kotlin/com/hexated/NeonimeProviderPlugin.kt deleted file mode 100644 index 9a6be431..00000000 --- a/NeonimeProvider/src/main/kotlin/com/hexated/NeonimeProviderPlugin.kt +++ /dev/null @@ -1,14 +0,0 @@ - -package com.hexated - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class NeonimeProviderPlugin: Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(NeonimeProvider()) - } -} \ No newline at end of file diff --git a/Ngefilm/build.gradle.kts b/Ngefilm/build.gradle.kts deleted file mode 100644 index d8eb0226..00000000 --- a/Ngefilm/build.gradle.kts +++ /dev/null @@ -1,27 +0,0 @@ -// use an integer for version numbers -version = 2 - - -cloudstream { - language = "id" - // All of these properties are optional, you can safely remove them - - // description = "Lorem Ipsum" - authors = listOf("Hexated") - - /** - * Status int as the following: - * 0: Down - * 1: Ok - * 2: Slow - * 3: Beta only - * */ - status = 1 // will be 3 if unspecified - tvTypes = listOf( - "AsianDrama", - "TvSeries", - "Movie", - ) - - iconUrl = "https://www.google.com/s2/favicons?domain=ngefilm21.club&sz=%size%" -} \ No newline at end of file diff --git a/Ngefilm/src/main/AndroidManifest.xml b/Ngefilm/src/main/AndroidManifest.xml deleted file mode 100644 index c98063f8..00000000 --- a/Ngefilm/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/Ngefilm/src/main/kotlin/com/hexated/Extractors.kt b/Ngefilm/src/main/kotlin/com/hexated/Extractors.kt deleted file mode 100644 index 9730276b..00000000 --- a/Ngefilm/src/main/kotlin/com/hexated/Extractors.kt +++ /dev/null @@ -1,149 +0,0 @@ -package com.hexated - -import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.extractors.DoodLaExtractor -import com.lagradost.cloudstream3.extractors.Filesim -import com.lagradost.cloudstream3.extractors.StreamSB -import com.lagradost.cloudstream3.utils.AppUtils -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.Qualities -import java.net.URI -import javax.crypto.Cipher -import javax.crypto.SecretKeyFactory -import javax.crypto.spec.IvParameterSpec -import javax.crypto.spec.PBEKeySpec -import javax.crypto.spec.SecretKeySpec - -class Dooood : DoodLaExtractor() { - override var mainUrl = "https://dooood.com" -} - -class Guccihide : Filesim() { - override val name = "Guccihide" - override var mainUrl = "https://guccihide.com" -} - -class Ahvsh : Filesim() { - override val name = "Ahvsh" - override var mainUrl = "https://ahvsh.com" -} - -class Lvturbo : StreamSB() { - override var name = "Lvturbo" - override var mainUrl = "https://lvturbo.com" -} - -class Sbrapid : StreamSB() { - override var name = "Sbrapid" - override var mainUrl = "https://sbrapid.com" -} - -class Sbface : StreamSB() { - override var name = "Sbface" - override var mainUrl = "https://sbface.com" -} - -class Sbsonic : StreamSB() { - override var name = "Sbsonic" - override var mainUrl = "https://sbsonic.com" -} - -object LocalServer { - private const val KEY = "4VqE3#N7zt&HEP^a" - - private fun getBaseUrl(url: String): String { - return URI(url).let { - "${it.scheme}://${it.host}" - } - } - - suspend fun getUrl( - url: String, - referer: String?, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ) { - val mainUrl = getBaseUrl(url) - val master = Regex("MasterJS\\s*=\\s*'([^']+)").find( - app.get( - url, - referer = referer - ).text - )?.groupValues?.get(1) - val encData = AppUtils.tryParseJson(base64Decode(master ?: return)) - val decrypt = cryptoAESHandler(encData ?: return, KEY, false) - - val source = Regex(""""?file"?:\s*"([^"]+)""").find(decrypt)?.groupValues?.get(1) - - // required - val headers = mapOf( - "Accept" to "*/*", - "Connection" to "keep-alive", - "Sec-Fetch-Dest" to "empty", - "Sec-Fetch-Mode" to "cors", - "Sec-Fetch-Site" to "cross-site", - "Origin" to mainUrl, - ) - - callback.invoke( - ExtractorLink( - Ngefilm().name, - Ngefilm().name, - source ?: return, - "$mainUrl/", - Qualities.P1080.value, - headers = headers, - isM3u8 = true - ) - ) - - } - - private fun cryptoAESHandler( - data: AESData, - pass: String, - encrypt: Boolean = true - ): String { - val factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512") - val spec = PBEKeySpec( - pass.toCharArray(), - data.salt?.hexToByteArray(), - data.iterations?.toIntOrNull() ?: 1, - 256 - ) - val key = factory.generateSecret(spec) - val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding") - return if (!encrypt) { - cipher.init( - Cipher.DECRYPT_MODE, - SecretKeySpec(key.encoded, "AES"), - IvParameterSpec(data.iv?.hexToByteArray()) - ) - String(cipher.doFinal(base64DecodeArray(data.ciphertext.toString()))) - } else { - cipher.init( - Cipher.ENCRYPT_MODE, - SecretKeySpec(key.encoded, "AES"), - IvParameterSpec(data.iv?.hexToByteArray()) - ) - base64Encode(cipher.doFinal(data.ciphertext?.toByteArray())) - } - } - - private fun String.hexToByteArray(): ByteArray { - check(length % 2 == 0) { "Must have an even length" } - return chunked(2) - .map { it.toInt(16).toByte() } - - .toByteArray() - } - - data class AESData( - @JsonProperty("ciphertext") val ciphertext: String? = null, - @JsonProperty("iv") val iv: String? = null, - @JsonProperty("salt") val salt: String? = null, - @JsonProperty("iterations") val iterations: String? = null, - ) - -} \ No newline at end of file diff --git a/Ngefilm/src/main/kotlin/com/hexated/Ngefilm.kt b/Ngefilm/src/main/kotlin/com/hexated/Ngefilm.kt deleted file mode 100644 index 753f59c5..00000000 --- a/Ngefilm/src/main/kotlin/com/hexated/Ngefilm.kt +++ /dev/null @@ -1,176 +0,0 @@ -package com.hexated - -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.LoadResponse.Companion.addActors -import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.loadExtractor -import org.jsoup.nodes.Element - -class Ngefilm : MainAPI() { - override var mainUrl = "https://ngefilm21.club" - override var name = "Ngefilm" - override val hasMainPage = true - override var lang = "id" - override val hasDownloadSupport = true - override val supportedTypes = setOf( - TvType.Movie, - TvType.TvSeries, - TvType.AsianDrama - ) - - companion object { - private val localServer = arrayOf( - "https://bestx.stream", - "https://chillx.top", - "https://watchx.top", - ) - } - - override val mainPage = mainPageOf( - "?s&search=advanced&post_type=movie&index&orderby&genre&movieyear&country&quality=" to "Movies Terbaru", - "?s=&search=advanced&post_type=tv&index=&orderby=&genre=&movieyear=&country=&quality=" to "Series Terbaru", - "?s=&search=advanced&post_type=tv&index=&orderby=&genre=drakor&movieyear=&country=&quality=" to "Series Korea", - "?s=&search=advanced&post_type=tv&index=&orderby=&genre=&movieyear=&country=indonesia&quality=" to "Series Indonesia", - ) - - override suspend fun getMainPage( - page: Int, - request: MainPageRequest - ): HomePageResponse { - val document = app.get("$mainUrl/page/$page/${request.data}").document - val home = document.select("main#main article").mapNotNull { - it.toSearchResult() - } - return newHomePageResponse(request.name, home) - } - - override suspend fun search(query: String): List { - val link = "$mainUrl/?s=$query&post_type[]=post&post_type[]=tv" - val document = app.get(link).document - return document.select("main#main article").mapNotNull { - it.toSearchResult() - } - } - - override suspend fun load(url: String): LoadResponse { - val document = app.get(url).document - - val title = - document.selectFirst("h1.entry-title")?.text()?.substringBefore("Season")?.trim() ?: "" - val poster = fixUrlNull( - document.selectFirst("figure.pull-left > img")?.attr("src")?.fixImageQuality() - ) - val tags = document.select("span.gmr-movie-genre:contains(Genre:) > a").map { it.text() } - - val year = - document.select("span.gmr-movie-genre:contains(Year:) > a").text().trim().toIntOrNull() - val tvType = if (url.contains("/tv/")) TvType.TvSeries else TvType.Movie - val description = document.selectFirst("div[itemprop=description] > p")?.text()?.trim() - val trailer = document.selectFirst("ul.gmr-player-nav li a.gmr-trailer-popup")?.attr("href") - val rating = - document.selectFirst("div.gmr-meta-rating > span[itemprop=ratingValue]")?.text() - ?.toRatingInt() - val actors = document.select("div.gmr-moviedata").last()?.select("span[itemprop=actors]") - ?.map { it.select("a").text() } - - val recommendations = document.select("div.idmuvi-rp ul li").mapNotNull { - it.toRecommendResult() - } - - return if (tvType == TvType.TvSeries) { - val episodes = document.select("div.gmr-listseries > a") - .filter { element -> !element.text().contains("Pilih Episode", true) } - .map { eps -> - val href = fixUrl(eps.attr("href")) - val episode = eps.text().substringAfter("Eps").toIntOrNull() - val season = - eps.text().split(" ").first().substringAfter("S").toIntOrNull() ?: 1 - Episode( - href, - season = season, - episode = episode, - ) - } - newTvSeriesLoadResponse(title, url, TvType.TvSeries, episodes) { - this.posterUrl = poster - this.year = year - this.plot = description - this.tags = tags - this.rating = rating - addActors(actors) - this.recommendations = recommendations - addTrailer(trailer) - } - } else { - newMovieLoadResponse(title, url, TvType.Movie, url) { - this.posterUrl = poster - this.year = year - this.plot = description - this.tags = tags - this.rating = rating - addActors(actors) - this.recommendations = recommendations - addTrailer(trailer) - } - } - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - - val document = app.get(data).document - - document.select("ul.muvipro-player-tabs li a").apmap { server -> - val iframe = app.get(fixUrl(server.attr("href"))).document.selectFirst("div.gmr-embed-responsive iframe") - ?.attr("src")?.let { fixUrl(it) } ?: return@apmap - if (localServer.any { iframe.startsWith(it) }) { - LocalServer.getUrl(iframe, "$mainUrl/", subtitleCallback, callback) - } else { - loadExtractor(iframe, "$mainUrl/", subtitleCallback, callback) - } - } - - return true - - } - - private fun Element.toSearchResult(): SearchResponse? { - val title = this.selectFirst("h2.entry-title > a")?.text()?.trim() ?: return null - val href = fixUrl(this.selectFirst("a")?.attr("href") ?: return null) - val posterUrl = fixUrlNull(this.selectFirst("a > img")?.attr("src").fixImageQuality()) - val quality = this.select("div.gmr-quality-item > a").text().trim() - return if (quality.isEmpty()) { - val episode = - this.select("div.gmr-numbeps > span").text().filter { it.isDigit() }.toIntOrNull() - newAnimeSearchResponse(title, href, TvType.TvSeries) { - this.posterUrl = posterUrl - addSub(episode) - } - } else { - newMovieSearchResponse(title, href, TvType.Movie) { - this.posterUrl = posterUrl - addQuality(quality.replace("-", "")) - } - } - } - - private fun Element.toRecommendResult(): SearchResponse? { - val title = this.selectFirst("a > span.idmuvi-rp-title")?.text()?.trim() ?: return null - val href = this.selectFirst("a")!!.attr("href") - val posterUrl = fixUrlNull(this.selectFirst("a > img")?.attr("src").fixImageQuality()) - return newMovieSearchResponse(title, href, TvType.Movie) { - this.posterUrl = posterUrl - } - } - - private fun String?.fixImageQuality(): String? { - val quality = Regex("(-\\d*x\\d*)").find(this ?: return null)?.groupValues?.get(0) - return this.replace(quality ?: return null, "") - } - -} \ No newline at end of file diff --git a/Ngefilm/src/main/kotlin/com/hexated/NgefilmPlugin.kt b/Ngefilm/src/main/kotlin/com/hexated/NgefilmPlugin.kt deleted file mode 100644 index cbf73937..00000000 --- a/Ngefilm/src/main/kotlin/com/hexated/NgefilmPlugin.kt +++ /dev/null @@ -1,21 +0,0 @@ - -package com.hexated - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class NgefilmPlugin: Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(Ngefilm()) - registerExtractorAPI(Sbsonic()) - registerExtractorAPI(Sbface()) - registerExtractorAPI(Sbrapid()) - registerExtractorAPI(Lvturbo()) - registerExtractorAPI(Ahvsh()) - registerExtractorAPI(Guccihide()) - registerExtractorAPI(Dooood()) - } -} \ No newline at end of file diff --git a/NontonAnimeIDProvider/build.gradle.kts b/NontonAnimeIDProvider/build.gradle.kts deleted file mode 100644 index 3da1506d..00000000 --- a/NontonAnimeIDProvider/build.gradle.kts +++ /dev/null @@ -1,27 +0,0 @@ -// use an integer for version numbers -version = 15 - - -cloudstream { - language = "id" - // All of these properties are optional, you can safely remove them - - // description = "Lorem Ipsum" - authors = listOf("Hexated") - - /** - * Status int as the following: - * 0: Down - * 1: Ok - * 2: Slow - * 3: Beta only - * */ - status = 1 // will be 3 if unspecified - tvTypes = listOf( - "AnimeMovie", - "Anime", - "OVA", - ) - - iconUrl = "https://www.google.com/s2/favicons?domain=nontonanimeid.site&sz=%size%" -} \ No newline at end of file diff --git a/NontonAnimeIDProvider/src/main/AndroidManifest.xml b/NontonAnimeIDProvider/src/main/AndroidManifest.xml deleted file mode 100644 index c98063f8..00000000 --- a/NontonAnimeIDProvider/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/NontonAnimeIDProvider/src/main/kotlin/com/hexated/NontonAnimeIDProvider.kt b/NontonAnimeIDProvider/src/main/kotlin/com/hexated/NontonAnimeIDProvider.kt deleted file mode 100644 index aa54f212..00000000 --- a/NontonAnimeIDProvider/src/main/kotlin/com/hexated/NontonAnimeIDProvider.kt +++ /dev/null @@ -1,252 +0,0 @@ -package com.hexated - -import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer -import com.lagradost.cloudstream3.extractors.Hxfile -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.loadExtractor -import org.jsoup.Jsoup -import org.jsoup.nodes.Element -import java.net.URI - -class NontonAnimeIDProvider : MainAPI() { - override var mainUrl = "https://nontonanimeid.bio" - override var name = "NontonAnimeID" - override val hasQuickSearch = false - override val hasMainPage = true - override var lang = "id" - override val hasDownloadSupport = true - - override val supportedTypes = setOf( - TvType.Anime, - TvType.AnimeMovie, - TvType.OVA - ) - - companion object { - fun getType(t: String): TvType { - return when { - t.contains("TV",true) -> TvType.Anime - t.contains("Movie",true) -> TvType.AnimeMovie - else -> TvType.OVA - } - } - - fun getStatus(t: String): ShowStatus { - return when (t) { - "Finished Airing" -> ShowStatus.Completed - "Currently Airing" -> ShowStatus.Ongoing - else -> ShowStatus.Completed - } - } - } - - override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { - val document = app.get(mainUrl).document - - val homePageList = ArrayList() - - document.select("section#postbaru").forEach { block -> - val header = block.selectFirst("h2")!!.text().trim() - val animes = block.select("article.animeseries").mapNotNull { - it.toSearchResult() - } - if (animes.isNotEmpty()) homePageList.add(HomePageList(header, animes)) - } - - document.select("aside#sidebar_right > div.side").forEach { block -> - val header = block.selectFirst("h3")!!.ownText().trim() - val animes = block.select("ul li.fullwdth").mapNotNull { - it.toSearchResultPopular() - } - if (animes.isNotEmpty()) homePageList.add(HomePageList(header, animes)) - } - - return HomePageResponse(homePageList) - } - - private fun Element.toSearchResult(): AnimeSearchResponse? { - val href = fixUrl(this.selectFirst("a")!!.attr("href")) - val title = this.selectFirst("h3.title")?.text() ?: return null - val posterUrl = fixUrl(this.select("img").attr("data-src")) - - return newAnimeSearchResponse(title, href, TvType.Anime) { - this.posterUrl = posterUrl - addDubStatus(dubExist = false, subExist = true) - } - - } - - private fun Element.toSearchResultPopular(): AnimeSearchResponse? { - val href = fixUrl(this.selectFirst("a")!!.attr("href")) - val title = this.selectFirst("h4")?.text()?.trim() ?: return null - val posterUrl = fixUrl(this.select("img").attr("data-src")) - - return newAnimeSearchResponse(title, href, TvType.Anime) { - this.posterUrl = posterUrl - addDubStatus(dubExist = false, subExist = true) - } - - } - - override suspend fun search(query: String): List { - val link = "$mainUrl/?s=$query" - val document = app.get(link).document - - return document.select(".result > ul > li").mapNotNull { - val title = it.selectFirst("h2")!!.text().trim() - val poster = it.selectFirst("img")!!.attr("src") - val tvType = getType( - it.selectFirst(".boxinfores > span.typeseries")!!.text().toString() - ) - val href = fixUrl(it.selectFirst("a")!!.attr("href")) - - newAnimeSearchResponse(title, href, tvType) { - this.posterUrl = poster - addDubStatus(dubExist = false, subExist = true) - } - } - } - - override suspend fun load(url: String): LoadResponse? { - val fixUrl = if (url.contains("/anime/")) { - url - } else { - app.get(url).document.selectFirst("div.nvs.nvsc a")?.attr("href") - } - - val req = app.get(fixUrl ?: return null) - mainUrl = getBaseUrl(req.url) - val document = req.document - - val title = document.selectFirst("h1.entry-title.cs")!!.text().removeSurrounding("Nonton Anime", "Sub Indo").trim() - val poster = document.selectFirst(".poster > img")?.attr("data-src") - val tags = document.select(".tagline > a").map { it.text() } - - val year = Regex("\\d, (\\d*)").find( - document.select(".bottomtitle > span:nth-child(5)").text() - )?.groupValues?.get(1)?.toIntOrNull() - val status = getStatus( - document.select("span.statusseries").text().trim() - ) - val type = document.select("span.typeseries").text().trim().lowercase() - val rating = document.select("span.nilaiseries").text().trim().toIntOrNull() - val description = document.select(".entry-content.seriesdesc > p").text().trim() - val trailer = document.selectFirst("a.trailerbutton")?.attr("href") - - val episodes = if (document.select("button.buttfilter").isNotEmpty()) { - val id = document.select("input[name=series_id]").attr("value") - val numEp = - document.selectFirst(".latestepisode > a")?.text()?.replace(Regex("\\D"), "") - .toString() - Jsoup.parse( - app.post( - url = "$mainUrl/wp-admin/admin-ajax.php", - data = mapOf( - "misha_number_of_results" to numEp, - "misha_order_by" to "date-DESC", - "action" to "mishafilter", - "series_id" to id - ) - ).parsed().content - ).select("li").map { - val episode = Regex("Episode\\s?(\\d+)").find( - it.selectFirst("a")?.text().toString() - )?.groupValues?.getOrNull(0) ?: it.selectFirst("a")?.text() - val link = fixUrl(it.selectFirst("a")!!.attr("href")) - Episode(link, episode = episode?.toIntOrNull()) - }.reversed() - } else { - document.select("ul.misha_posts_wrap2 > li").map { - val episode = Regex("Episode\\s?(\\d+)").find( - it.selectFirst("a")?.text().toString() - )?.groupValues?.getOrNull(0) ?: it.selectFirst("a")?.text() - val link = it.select("a").attr("href") - Episode(link, episode = episode?.toIntOrNull()) - }.reversed() - } - - - val recommendations = document.select(".result > li").mapNotNull { - val epHref = it.selectFirst("a")!!.attr("href") - val epTitle = it.selectFirst("h3")!!.text() - val epPoster = it.select(".top > img").attr("data-src") - - newAnimeSearchResponse(epTitle, epHref, TvType.Anime) { - this.posterUrl = epPoster - addDubStatus(dubExist = false, subExist = true) - } - } - - return newAnimeLoadResponse(title, url, getType(type)) { - engName = title - posterUrl = poster - this.year = year - addEpisodes(DubStatus.Subbed, episodes) - showStatus = status - this.rating = rating - plot = description - addTrailer(trailer) - this.tags = tags - this.recommendations = recommendations - } - - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - - val document = app.get(data).document - val sources = ArrayList() - - document.select(".container1 > ul > li:not(.boxtab)").apmap { - val dataPost = it.attr("data-post") - val dataNume = it.attr("data-nume") - val dataType = it.attr("data-type") - - val iframe = app.post( - url = "$mainUrl/wp-admin/admin-ajax.php", - data = mapOf( - "action" to "player_ajax", - "post" to dataPost, - "nume" to dataNume, - "type" to dataType - ), - referer = data, - headers = mapOf("X-Requested-With" to "XMLHttpRequest") - ).document.select("iframe").attr("src") - - sources.add(fixUrl(iframe)) - } - - sources.apmap { - loadExtractor(it, "$mainUrl/", subtitleCallback, callback) - } - - return true - } - - private fun getBaseUrl(url: String): String { - return URI(url).let { - "${it.scheme}://${it.host}" - } - } - private data class EpResponse( - @JsonProperty("posts") val posts: String?, - @JsonProperty("max_page") val max_page: Int?, - @JsonProperty("found_posts") val found_posts: Int?, - @JsonProperty("content") val content: String - ) - -} - -class KotakAnimeid2 : Hxfile() { - override val name = "KotakAnimeid2" - override val mainUrl = "https://embed2.kotakanimeid.com" - override val requiresReferer = true -} diff --git a/NontonAnimeIDProvider/src/main/kotlin/com/hexated/NontonAnimeIDProviderPlugin.kt b/NontonAnimeIDProvider/src/main/kotlin/com/hexated/NontonAnimeIDProviderPlugin.kt deleted file mode 100644 index 7c445f7f..00000000 --- a/NontonAnimeIDProvider/src/main/kotlin/com/hexated/NontonAnimeIDProviderPlugin.kt +++ /dev/null @@ -1,15 +0,0 @@ - -package com.hexated - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class NontonAnimeIDProviderPlugin: Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(NontonAnimeIDProvider()) - registerExtractorAPI(KotakAnimeid2()) - } -} \ No newline at end of file diff --git a/OnetwothreeTv/build.gradle.kts b/OnetwothreeTv/build.gradle.kts deleted file mode 100644 index a000e631..00000000 --- a/OnetwothreeTv/build.gradle.kts +++ /dev/null @@ -1,25 +0,0 @@ -// use an integer for version numbers -version = 3 - - -cloudstream { - language = "en" - // All of these properties are optional, you can safely remove them - - // description = "Lorem Ipsum" - authors = listOf("Hexated") - - /** - * Status int as the following: - * 0: Down - * 1: Ok - * 2: Slow - * 3: Beta only - * */ - status = 1 // will be 3 if unspecified - tvTypes = listOf( - "Live", - ) - - iconUrl = "https://www.google.com/s2/favicons?domain=123tv.live&sz=%size%" -} diff --git a/OnetwothreeTv/src/main/AndroidManifest.xml b/OnetwothreeTv/src/main/AndroidManifest.xml deleted file mode 100644 index c98063f8..00000000 --- a/OnetwothreeTv/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/OnetwothreeTv/src/main/kotlin/com/hexated/OnetwothreeTv.kt b/OnetwothreeTv/src/main/kotlin/com/hexated/OnetwothreeTv.kt deleted file mode 100644 index bcd7bc9d..00000000 --- a/OnetwothreeTv/src/main/kotlin/com/hexated/OnetwothreeTv.kt +++ /dev/null @@ -1,195 +0,0 @@ -package com.hexated - -import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.M3u8Helper -import com.lagradost.cloudstream3.utils.Qualities -import org.jsoup.nodes.Element -import javax.crypto.Cipher -import javax.crypto.SecretKeyFactory -import javax.crypto.spec.IvParameterSpec -import javax.crypto.spec.PBEKeySpec -import javax.crypto.spec.SecretKeySpec - -class OnetwothreeTv : MainAPI() { - override var mainUrl = "http://123tv.live" - override var name = "123tv" - override val hasDownloadSupport = false - override val hasMainPage = true - override val supportedTypes = setOf( - TvType.Live - ) - - override val mainPage = mainPageOf( - "1" to "United States (USA)", - "$mainUrl/top-streams/" to "Top Streams", - "$mainUrl/latest-streams/" to "Latest Streams", - ) - - override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse { - val items = mutableListOf() - val nonPaged = - (request.name == "Top Streams" || request.name == "Latest Streams") && page <= 1 - if (nonPaged) { - val res = app.get(request.data).document - val home = res.select("div.col-md-3.col-sm-6").mapNotNull { - it.toSearchResult() - } - items.add(HomePageList(request.name, home, true)) - } - - if (request.name == "United States (USA)") { - val res = app.post( - "$mainUrl/wp-admin/admin-ajax.php", data = mapOf( - "action" to "_123tv_load_more_videos_from_category", - "cat_id" to request.data, - "page_num" to "${page.minus(1)}" - ), headers = mapOf( - "X-Requested-With" to "XMLHttpRequest" - ) - ).document - val home = res.select("div.col-md-3.col-sm-6").mapNotNull { - it.toSearchResult() - } - items.add(HomePageList(request.name, home, true)) - } - - return newHomePageResponse(items) - - } - - private fun Element.toSearchResult(): LiveSearchResponse? { - return LiveSearchResponse( - this.selectFirst("div.video-title h4")?.text() ?: return null, - fixUrl(this.selectFirst("a")!!.attr("href")), - this@OnetwothreeTv.name, - TvType.Live, - fixUrlNull(this.selectFirst("img")?.attr("src")), - ) - - } - - override suspend fun search(query: String): List { - return app.get( - "$mainUrl/?s=$query" - ).document.select("div.videos-latest-list.row div.col-md-3.col-sm-6").mapNotNull { - it.toSearchResult() - } - } - - override suspend fun load(url: String): LoadResponse? { - val document = app.get(url).document - return LiveStreamLoadResponse( - document.selectFirst("div.video-big-title h1")?.text() ?: return null, - url, - this.name, - document.selectFirst("div.embed-responsive iframe")?.attr("src") ?: url, - fixUrlNull(document.selectFirst("meta[name=\"twitter:image\"]")?.attr("content")), - plot = document.select("div.watch-video-description p").text() ?: return null - ) - } - - private fun String.decodeHex(): ByteArray { - check(length % 2 == 0) { "Must have an even length" } - return chunked(2) - .map { it.toInt(16).toByte() } - .toByteArray() - } - - private fun cryptojsAESHandler( - data: AesData, - pass: String, - encrypt: Boolean = true - ): String { - val factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512") - val spec = PBEKeySpec(pass.toCharArray(), data.s.decodeHex(), 999, 256) - val key = factory.generateSecret(spec) - val cipher = Cipher.getInstance("AES/CBC/NoPadding") - return if (!encrypt) { - cipher.init( - Cipher.DECRYPT_MODE, - SecretKeySpec(key.encoded, "AES"), - IvParameterSpec(data.iv.decodeHex()) - ) - String(cipher.doFinal(base64DecodeArray(data.ct))) - } else { - cipher.init( - Cipher.ENCRYPT_MODE, - SecretKeySpec(key.encoded, "AES"), - IvParameterSpec(data.iv.decodeHex()) - ) - base64Encode(cipher.doFinal(data.ct.toByteArray())) - - } - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - if (data.contains(".m3u8")) { - app.get( - data, - referer = "$mainUrl/" - ).document.select("script").find { it.data().contains("var player=") }?.data() - ?.substringAfter("source:'")?.substringBefore("',")?.let { link -> - callback.invoke( - ExtractorLink( - source = name, - name = name, - url = link, - referer = "http://azureedge.xyz/", - quality = Qualities.Unknown.value, - isM3u8 = true, - headers = mapOf("Origin" to "http://azureedge.xyz") - ) - ) - } - } else { - val script = - app.get(data).document.select("script").find { it.data().contains("var post_id") } - ?.data() ?: throw ErrorLoadingException("No data found") - val encodeData = - Regex("\\w{6,10}=\\[(\\S+)];return").find(script)?.groupValues?.getOrNull(1) - ?.split(",")?.joinToString("") { it.replace("'", "") } - .let { base64Decode("$it") } - val aesData = tryParseJson(encodeData) - ?: throw ErrorLoadingException("Invalid json responses") - val pass = Regex("\\[((\\d{2,3},?\\s?){4})];").findAll(script).map { it.groupValues[1] } - .toList().flatMap { it.split(",") }.map { it.toInt().toChar() }.reversed() - .joinToString("") - val decryptData = cryptojsAESHandler(aesData, pass, false) - val jsonData = Regex("[\"|'](\\?1&json=\\S+)[\"|'];").find(script)?.groupValues?.get(1) - val m3uLink = "${decryptData.substringBefore(".m3u8")}.m3u8$jsonData" - app.get(m3uLink, referer = data).let { - tryParseJson>(it.text)?.map { res -> - M3u8Helper.generateM3u8( - this.name, - res.file, - "$mainUrl/", - headers = mapOf("Origin" to mainUrl) - ).forEach(callback) - } - } - - } - - return true - - } - - data class Source( - @JsonProperty("file") val file: String, - ) - - data class AesData( - @JsonProperty("ciphertext") val ct: String, - @JsonProperty("salt") val s: String, - @JsonProperty("iv") val iv: String, - @JsonProperty("iterations") val iterations: Int? = null, - ) -} \ No newline at end of file diff --git a/OnetwothreeTv/src/main/kotlin/com/hexated/OnetwothreeTvPlugin.kt b/OnetwothreeTv/src/main/kotlin/com/hexated/OnetwothreeTvPlugin.kt deleted file mode 100644 index 5b764581..00000000 --- a/OnetwothreeTv/src/main/kotlin/com/hexated/OnetwothreeTvPlugin.kt +++ /dev/null @@ -1,14 +0,0 @@ - -package com.hexated - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class OnetwothreeTvPlugin: Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(OnetwothreeTv()) - } -} \ No newline at end of file diff --git a/OploverzProvider/build.gradle.kts b/OploverzProvider/build.gradle.kts deleted file mode 100644 index 5c4494a5..00000000 --- a/OploverzProvider/build.gradle.kts +++ /dev/null @@ -1,27 +0,0 @@ -// use an integer for version numbers -version = 19 - - -cloudstream { - language = "id" - // All of these properties are optional, you can safely remove them - - // description = "Lorem Ipsum" - authors = listOf("Hexated") - - /** - * Status int as the following: - * 0: Down - * 1: Ok - * 2: Slow - * 3: Beta only - * */ - status = 1 // will be 3 if unspecified - tvTypes = listOf( - "AnimeMovie", - "Anime", - "OVA", - ) - - iconUrl = "https://www.google.com/s2/favicons?domain=oploverz.care&sz=%size%" -} diff --git a/OploverzProvider/src/main/AndroidManifest.xml b/OploverzProvider/src/main/AndroidManifest.xml deleted file mode 100644 index c98063f8..00000000 --- a/OploverzProvider/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/OploverzProvider/src/main/kotlin/com/hexated/OploverzProvider.kt b/OploverzProvider/src/main/kotlin/com/hexated/OploverzProvider.kt deleted file mode 100644 index 65836dd1..00000000 --- a/OploverzProvider/src/main/kotlin/com/hexated/OploverzProvider.kt +++ /dev/null @@ -1,251 +0,0 @@ -package com.hexated - -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.extractors.Filesim -import com.lagradost.cloudstream3.utils.* -import org.jsoup.nodes.Element - -class OploverzProvider : MainAPI() { - override var mainUrl = "https://oploverz.care" - override var name = "Oploverz" - override val hasMainPage = true - override var lang = "id" - override val hasDownloadSupport = true - - override val supportedTypes = setOf( - TvType.Anime, - TvType.AnimeMovie, - TvType.OVA - ) - - companion object { - const val acefile = "https://acefile.co" - fun getType(t: String): TvType { - return if (t.contains("OVA", true) || t.contains("Special")) TvType.OVA - else if (t.contains("Movie", true)) TvType.AnimeMovie - else TvType.Anime - } - - fun getStatus(t: String?): ShowStatus { - return when (t) { - "Finished Airing" -> ShowStatus.Completed - "Completed" -> ShowStatus.Completed - "Currently Airing" -> ShowStatus.Ongoing - "Ongoing" -> ShowStatus.Ongoing - else -> ShowStatus.Completed - } - } - - } - - override val mainPage = mainPageOf( - "update" to "Latest Update", - "latest" to "Latest Added", - "popular" to "Popular Anime", - "rating" to "Top Rated", - ) - - override suspend fun getMainPage( - page: Int, - request: MainPageRequest - ): HomePageResponse { - val document = app.get("$mainUrl/anime-list/page/$page/?title&order=${request.data}&status&type").document - val home = document.select("div.relat > article").mapNotNull { - it.toSearchResult() - } - return newHomePageResponse(request.name, home) - } - - private fun getProperAnimeLink(uri: String): String { - return if (uri.contains("/anime/")) { - uri - } else { - var title = uri.substringAfter("$mainUrl/") - title = when { - (title.contains("-episode")) && !(title.contains("-movie")) -> Regex("(.+)-episode").find( - title - )?.groupValues?.get(1).toString() - (title.contains("-movie")) -> Regex("(.+)-movie").find(title)?.groupValues?.get( - 1 - ).toString() - else -> title - } - "$mainUrl/anime/$title" - } - } - - private fun Element.toSearchResult(): AnimeSearchResponse? { - val title = this.selectFirst("div.title")?.text()?.trim() ?: return null - val href = getProperAnimeLink(this.selectFirst("a")!!.attr("href")) - val posterUrl = this.select("img[itemprop=image]").attr("src").toString() - val type = getType(this.select("div.type").text().trim()) - val epNum = - this.selectFirst("span.episode")?.ownText()?.replace(Regex("\\D"), "")?.trim() - ?.toIntOrNull() - return newAnimeSearchResponse(title, href, type) { - this.posterUrl = posterUrl - addSub(epNum) - } - - } - - override suspend fun search(query: String): List { - val anime = mutableListOf() - (1..2).forEach { page -> - val link = "$mainUrl/page/$page/?s=$query" - val document = app.get(link).document - val media = document.select(".site-main.relat > article").mapNotNull { - val title = it.selectFirst("div.title > h2")!!.ownText().trim() - val href = it.selectFirst("a")!!.attr("href") - val posterUrl = it.selectFirst("img")!!.attr("src").toString() - val type = getType(it.select("div.type").text().trim()) - newAnimeSearchResponse(title, href, type) { - this.posterUrl = posterUrl - } - } - if(media.isNotEmpty()) anime.addAll(media) - } - return anime - } - - override suspend fun load(url: String): LoadResponse { - val document = app.get(url).document - - val title = document.selectFirst("h1.entry-title")?.text() - ?.replace("Subtitle Indonesia", "")?.trim() ?: "" - val type = document.selectFirst("div.alternati span.type")?.text() ?: "" - - val episodes = document.select("div.lstepsiode.listeps ul li").mapNotNull { - val header = it.selectFirst("a") ?: return@mapNotNull null - val episode = header.text().trim().toIntOrNull() - val link = fixUrl(header.attr("href")) - Episode(link, header.text(), episode = episode) - }.reversed() - - return newAnimeLoadResponse(title, url, getType(type)) { - posterUrl = document.selectFirst("div.thumb > img")?.attr("src") - this.year = document.selectFirst("div.alternati a")?.text()?.filter { it.isDigit() }?.toIntOrNull() - addEpisodes(DubStatus.Subbed, episodes) - showStatus = - getStatus( - document.selectFirst("div.alternati span:nth-child(2)")?.text()?.trim() - ) - plot = document.selectFirst("div.entry-content > p")?.text()?.trim() - this.tags = - document.select("div.genre-info a").map { it.text() } - } - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - - val document = app.get(data).document - - argamap( - { - document.select("div#server ul li div").apmap { - val dataPost = it.attr("data-post") - val dataNume = it.attr("data-nume") - val dataType = it.attr("data-type") - - val iframe = app.post( - url = "$mainUrl/wp-admin/admin-ajax.php", - data = mapOf( - "action" to "player_ajax", - "post" to dataPost, - "nume" to dataNume, - "type" to dataType - ), - referer = data, - headers = mapOf("X-Requested-With" to "XMLHttpRequest") - ).document.select("iframe").attr("src") - - loadExtractor(fixedIframe(iframe), "$mainUrl/", subtitleCallback, callback) - - } - }, - { - document.select("div#download tr").map { el -> - el.select("a").apmap { - loadFixedExtractor(fixedIframe(it.attr("href")), el.select("strong").text(), "$mainUrl/", subtitleCallback, callback) - } - } - } - ) - - return true - } - - private suspend fun loadFixedExtractor( - url: String, - name: String, - referer: String? = null, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ) { - loadExtractor(url, referer, subtitleCallback) { link -> - callback.invoke( - ExtractorLink( - link.name, - link.name, - link.url, - link.referer, - name.fixQuality(), - link.isM3u8, - link.headers, - link.extractorData - ) - ) - } - } - - private fun String.fixQuality() : Int { - return when(this) { - "MP4HD" -> Qualities.P720.value - "FULLHD" -> Qualities.P1080.value - else -> Regex("(\\d{3,4})p").find(this)?.groupValues?.get(1)?.toIntOrNull() ?: Qualities.Unknown.value - } - } - - private fun fixedIframe(url: String): String { - val id = Regex("""(?:/f/|/file/)(\w+)""").find(url)?.groupValues?.getOrNull(1) - return when { - url.startsWith(acefile) -> "${acefile}/player/$id" - else -> fixUrl(url) - } - } - -} - -class Streamhide : Filesim() { - override val mainUrl = "https://streamhide.to" - override val name = "Streamhide" -} - -open class Pixeldrain : ExtractorApi() { - override val name = "Pixeldrain" - override val mainUrl = "https://pixeldrain.com" - override val requiresReferer = false - override suspend fun getUrl( - url: String, - referer: String?, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ) { - val mId = Regex("/([ul]/[\\da-zA-Z\\-]+)").find(url)?.groupValues?.get(1)?.split("/") - callback.invoke( - ExtractorLink( - this.name, - this.name, - "$mainUrl/api/file/${mId?.last() ?: return}?download", - url, - Qualities.Unknown.value, - ) - ) - } - -} diff --git a/OploverzProvider/src/main/kotlin/com/hexated/OploverzProviderPlugin.kt b/OploverzProvider/src/main/kotlin/com/hexated/OploverzProviderPlugin.kt deleted file mode 100644 index 4f313881..00000000 --- a/OploverzProvider/src/main/kotlin/com/hexated/OploverzProviderPlugin.kt +++ /dev/null @@ -1,16 +0,0 @@ - -package com.hexated - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class OploverzProviderPlugin: Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(OploverzProvider()) - registerExtractorAPI(Streamhide()) - registerExtractorAPI(Pixeldrain()) - } -} \ No newline at end of file diff --git a/OtakudesuProvider/build.gradle.kts b/OtakudesuProvider/build.gradle.kts deleted file mode 100644 index 5e05690f..00000000 --- a/OtakudesuProvider/build.gradle.kts +++ /dev/null @@ -1,27 +0,0 @@ -// use an integer for version numbers -version = 12 - - -cloudstream { - language = "id" - // All of these properties are optional, you can safely remove them - - // description = "Lorem Ipsum" - authors = listOf("Hexated") - - /** - * Status int as the following: - * 0: Down - * 1: Ok - * 2: Slow - * 3: Beta only - * */ - status = 1 // will be 3 if unspecified - tvTypes = listOf( - "AnimeMovie", - "Anime", - "OVA", - ) - - iconUrl = "https://www.google.com/s2/favicons?domain=otakudesu.watch&sz=%size%" -} \ No newline at end of file diff --git a/OtakudesuProvider/src/main/AndroidManifest.xml b/OtakudesuProvider/src/main/AndroidManifest.xml deleted file mode 100644 index c98063f8..00000000 --- a/OtakudesuProvider/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/OtakudesuProvider/src/main/kotlin/com/hexated/OtakudesuProvider.kt b/OtakudesuProvider/src/main/kotlin/com/hexated/OtakudesuProvider.kt deleted file mode 100644 index 761b5d62..00000000 --- a/OtakudesuProvider/src/main/kotlin/com/hexated/OtakudesuProvider.kt +++ /dev/null @@ -1,222 +0,0 @@ -package com.hexated - -import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.extractors.JWPlayer -import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.loadExtractor -import org.jsoup.Jsoup -import org.jsoup.nodes.Element - -class OtakudesuProvider : MainAPI() { - override var mainUrl = "https://otakudesu.lol" - override var name = "Otakudesu" - override val hasMainPage = true - override var lang = "id" - override val hasDownloadSupport = true - - override val supportedTypes = setOf( - TvType.Anime, - TvType.AnimeMovie, - TvType.OVA - ) - - companion object { - // private val interceptor = CloudflareKiller() - fun getType(t: String): TvType { - return if (t.contains("OVA", true) || t.contains("Special")) TvType.OVA - else if (t.contains("Movie", true)) TvType.AnimeMovie - else TvType.Anime - } - - fun getStatus(t: String): ShowStatus { - return when (t) { - "Completed" -> ShowStatus.Completed - "Ongoing" -> ShowStatus.Ongoing - else -> ShowStatus.Completed - } - } - } - - override val mainPage = mainPageOf( - "$mainUrl/ongoing-anime/page/" to "Anime Ongoing", - "$mainUrl/complete-anime/page/" to "Anime Completed" - ) - - override suspend fun getMainPage( - page: Int, - request: MainPageRequest - ): HomePageResponse { - val document = app.get( - request.data + page -// , interceptor = interceptor - ).document - val home = document.select("div.venz > ul > li").mapNotNull { - it.toSearchResult() - } - return newHomePageResponse(request.name, home) - } - - private fun Element.toSearchResult(): AnimeSearchResponse? { - val title = this.selectFirst("h2.jdlflm")?.text()?.trim() ?: return null - val href = this.selectFirst("a")!!.attr("href") - val posterUrl = this.select("div.thumbz > img").attr("src").toString() - val epNum = this.selectFirst("div.epz")?.ownText()?.replace(Regex("\\D"), "")?.trim() - ?.toIntOrNull() - return newAnimeSearchResponse(title, href, TvType.Anime) { - this.posterUrl = posterUrl - addSub(epNum) -// posterHeaders = interceptor.getCookieHeaders(url).toMap() - } - - } - - override suspend fun search(query: String): List { - val link = "$mainUrl/?s=$query&post_type=anime" - val document = app.get( - link -// , interceptor = interceptor - ).document - - return document.select("ul.chivsrc > li").map { - val title = it.selectFirst("h2 > a")!!.ownText().trim() - val href = it.selectFirst("h2 > a")!!.attr("href") - val posterUrl = it.selectFirst("img")!!.attr("src").toString() - newAnimeSearchResponse(title, href, TvType.Anime) { - this.posterUrl = posterUrl -// posterHeaders = interceptor.getCookieHeaders(url).toMap() - } - } - } - - - override suspend fun load(url: String): LoadResponse { - val document = app.get( - url -// , interceptor = interceptor - ).document - - val title = document.selectFirst("div.infozingle > p:nth-child(1) > span")?.ownText() - ?.replace(":", "")?.trim().toString() - val poster = document.selectFirst("div.fotoanime > img")?.attr("src") - val tags = document.select("div.infozingle > p:nth-child(11) > span > a").map { it.text() } - val type = document.selectFirst("div.infozingle > p:nth-child(5) > span")?.ownText() - ?.replace(":", "")?.trim() ?: "tv" - - val year = Regex("\\d, (\\d*)").find( - document.select("div.infozingle > p:nth-child(9) > span").text() - )?.groupValues?.get(1)?.toIntOrNull() - val status = getStatus( - document.selectFirst("div.infozingle > p:nth-child(6) > span")!!.ownText() - .replace(":", "") - .trim() - ) - val description = document.select("div.sinopc > p").text() - - val episodes = document.select("div.episodelist")[1].select("ul > li").mapNotNull { - val name = it.selectFirst("a")?.text() ?: return@mapNotNull null - val episode = Regex("Episode\\s?(\\d+)").find(name)?.groupValues?.getOrNull(0) - ?: it.selectFirst("a")?.text() - val link = fixUrl(it.selectFirst("a")!!.attr("href")) - Episode(link, name, episode = episode?.toIntOrNull()) - }.reversed() - - val recommendations = - document.select("div.isi-recommend-anime-series > div.isi-konten").map { - val recName = it.selectFirst("span.judul-anime > a")!!.text() - val recHref = it.selectFirst("a")!!.attr("href") - val recPosterUrl = it.selectFirst("a > img")?.attr("src").toString() - newAnimeSearchResponse(recName, recHref, TvType.Anime) { - this.posterUrl = recPosterUrl -// posterHeaders = interceptor.getCookieHeaders(url).toMap() - } - } - - return newAnimeLoadResponse(title, url, getType(type)) { - engName = title - posterUrl = poster - this.year = year - addEpisodes(DubStatus.Subbed, episodes) - showStatus = status - plot = description - this.tags = tags - this.recommendations = recommendations -// posterHeaders = interceptor.getCookieHeaders(url).toMap() - } - } - - - data class ResponseSources( - @JsonProperty("id") val id: String, - @JsonProperty("i") val i: String, - @JsonProperty("q") val q: String, - ) - - data class ResponseData( - @JsonProperty("data") val data: String - ) - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - - val document = app.get( - data -// , interceptor = interceptor - ).document - val scriptData = document.select("script:containsData(action:)").lastOrNull()?.data() - val token = scriptData?.substringAfter("{action:\"")?.substringBefore("\"}").toString() - - val nonce = app.post("$mainUrl/wp-admin/admin-ajax.php", data = mapOf("action" to token)) - .parsed().data - val action = scriptData?.substringAfter(",action:\"")?.substringBefore("\"}").toString() - - val mirrorData = document.select("div.mirrorstream > ul > li").mapNotNull { - base64Decode(it.select("a").attr("data-content")) - }.toString() - - tryParseJson>(mirrorData)?.apmap { res -> - val id = res.id - val i = res.i - val q = res.q - - var sources = Jsoup.parse( - base64Decode( - app.post( - "${mainUrl}/wp-admin/admin-ajax.php", data = mapOf( - "id" to id, - "i" to i, - "q" to q, - "nonce" to nonce, - "action" to action - ) - ).parsed().data - ) - ).select("iframe").attr("src") - - if (sources.startsWith("https://desustream.me")) { - if (!sources.contains(Regex("/arcg/|/odchan/|/desudrive/|/moedesu/"))) { - sources = app.get(sources).document.select("iframe").attr("src") - } - if (sources.startsWith("https://yourupload.com")) { - sources = sources.replace("//", "//www.") - } - } - - loadExtractor(sources, data, subtitleCallback, callback) - - } - - return true - } - -} - -class Moedesu : JWPlayer() { - override val name = "Moedesu" - override val mainUrl = "https://desustream.me/moedesu/" -} \ No newline at end of file diff --git a/OtakudesuProvider/src/main/kotlin/com/hexated/OtakudesuProviderPlugin.kt b/OtakudesuProvider/src/main/kotlin/com/hexated/OtakudesuProviderPlugin.kt deleted file mode 100644 index ccac8600..00000000 --- a/OtakudesuProvider/src/main/kotlin/com/hexated/OtakudesuProviderPlugin.kt +++ /dev/null @@ -1,15 +0,0 @@ - -package com.hexated - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class OtakudesuProviderPlugin: Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(OtakudesuProvider()) - registerExtractorAPI(Moedesu()) - } -} \ No newline at end of file diff --git a/Paradisehill/build.gradle.kts b/Paradisehill/build.gradle.kts deleted file mode 100644 index 167fdc84..00000000 --- a/Paradisehill/build.gradle.kts +++ /dev/null @@ -1,25 +0,0 @@ -// use an integer for version numbers -version = 4 - - -cloudstream { - language = "en" - // All of these properties are optional, you can safely remove them - - description = "Series porn (use VPN if links not working)" - authors = listOf("Sora") - - /** - * Status int as the following: - * 0: Down - * 1: Ok - * 2: Slow - * 3: Beta only - * */ - status = 1 // will be 3 if unspecified - tvTypes = listOf( - "NSFW", - ) - - iconUrl = "https://www.google.com/s2/favicons?domain=en.paradisehill.cc&sz=%size%" -} \ No newline at end of file diff --git a/Paradisehill/src/main/AndroidManifest.xml b/Paradisehill/src/main/AndroidManifest.xml deleted file mode 100644 index c98063f8..00000000 --- a/Paradisehill/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/Paradisehill/src/main/kotlin/com/hexated/Paradisehill.kt b/Paradisehill/src/main/kotlin/com/hexated/Paradisehill.kt deleted file mode 100644 index 70b5d5f6..00000000 --- a/Paradisehill/src/main/kotlin/com/hexated/Paradisehill.kt +++ /dev/null @@ -1,124 +0,0 @@ -package com.hexated - -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.LoadResponse.Companion.addActors -import com.lagradost.cloudstream3.utils.* -import org.jsoup.nodes.Element - -class Paradisehill : MainAPI() { - override var mainUrl = "https://en.paradisehill.cc" - override var name = "Paradisehill" - override val hasMainPage = true - override val hasDownloadSupport = true - override var sequentialMainPage = true - override val vpnStatus = VPNStatus.MightBeNeeded - override val supportedTypes = setOf(TvType.NSFW) - - override val mainPage = mainPageOf( - "$mainUrl/all/?sort=created_at&page=" to "New Porn Movies", - "$mainUrl/popular/?filter=all&sort=by_likes&page=" to "Popular Porn Movies", - "$mainUrl/studio/89/?sort=created_at&page=" to "Brazzers", - "$mainUrl/studio/29/?sort=created_at&page=" to "Digital Playground", - "$mainUrl/studio/16/?sort=created_at&page=" to "Evil Angel", - "$mainUrl/studio/6/?sort=created_at&page=" to "Bang Bros Productions", - "$mainUrl/studio/78/?sort=created_at&page=" to "Jules Jordan Video", - "$mainUrl/studio/64/?sort=created_at&page=" to "Reality Kings", - ) - - override suspend fun getMainPage( - page: Int, - request: MainPageRequest - ): HomePageResponse { - val document = app.get(request.data + page, verify = false).document - val home = - document.select("div.content div.item") - .mapNotNull { - it.toSearchResult() - } - return newHomePageResponse(request.name, home) - } - - private fun Element.toSearchResult(): SearchResponse? { - val title = this.selectFirst("span[itemprop=name]")?.text() ?: return null - val href = fixUrl(this.selectFirst("a")!!.attr("href")) - val posterUrl = fixUrlNull(this.selectFirst("img")?.attr("src")) - return newMovieSearchResponse(title, href, TvType.Movie) { - this.posterUrl = posterUrl - } - - } - - override suspend fun search(query: String): List { - val searchResponse = mutableListOf() - for (i in 1..10) { - val document = app.get("$mainUrl/search/?pattern=$query&what=1&page=$i").document - val results = document.select("div.content div.item") - .mapNotNull { - it.toSearchResult() - } - searchResponse.addAll(results) - if(results.isEmpty()) break - } - - return searchResponse - } - - override suspend fun load(url: String): LoadResponse? { - val document = app.get(url).document - - val title = document.selectFirst("h1.title-inside")?.text()?.trim() ?: return null - val poster = fixUrlNull(document.selectFirst("img[itemprop=thumbnailUrl]")?.attr("src")) - val tags = document.select("div.opisanie span[itemprop=genre] a").map { it.text() } - val year = document.select("div.opisanie span[itemprop=releasedEvent]").text() - .let { Regex("[0-9]{4}").find(it)?.groupValues?.getOrNull(0)?.toIntOrNull() } - val description = document.select("div.opisanie span[itemprop=description]").text().trim() - val actors = document.select("div.opisanie p:contains(Actors:) a").map { it.text() } - - val dataEps = - document.select("script").find { it.data().contains("var videoList =") }?.data() - ?.substringAfter("videoList = [")?.substringBefore("];")?.let { data -> - Regex("\"src\":\"(\\S*?.mp4)\",").findAll(data).map { it.groupValues[1] } - .toList() - } - val episodes = dataEps?.mapIndexed { index, link -> - Episode(link, episode = index + 1) - } ?: throw ErrorLoadingException("No Episode Found") - - val recommendations = - document.select("div.content div.item") - .mapNotNull { - it.toSearchResult() - } - - return newTvSeriesLoadResponse(title, url, TvType.NSFW, episodes) { - this.posterUrl = poster - this.plot = description - this.tags = tags - addActors(actors) - this.year = year - this.recommendations = recommendations - } - - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - - callback.invoke( - ExtractorLink( - this.name, - this.name, - data.replace("\\", ""), - referer = mainUrl, - quality = Qualities.Unknown.value, -// headers = mapOf("Range" to "bytes=0-"), - ) - ) - return true - } - -} \ No newline at end of file diff --git a/Paradisehill/src/main/kotlin/com/hexated/ParadisehillPlugin.kt b/Paradisehill/src/main/kotlin/com/hexated/ParadisehillPlugin.kt deleted file mode 100644 index 0c876f29..00000000 --- a/Paradisehill/src/main/kotlin/com/hexated/ParadisehillPlugin.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.hexated - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class ParadisehillPlugin: Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(Paradisehill()) - } -} \ No newline at end of file diff --git a/PhimmoichillProvider/build.gradle.kts b/PhimmoichillProvider/build.gradle.kts deleted file mode 100644 index 091dbc67..00000000 --- a/PhimmoichillProvider/build.gradle.kts +++ /dev/null @@ -1,28 +0,0 @@ -// use an integer for version numbers -version = 4 - - -cloudstream { - language = "vi" - // All of these properties are optional, you can safely remove them - - // description = "Lorem Ipsum" - authors = listOf("Hexated") - - /** - * Status int as the following: - * 0: Down - * 1: Ok - * 2: Slow - * 3: Beta only - * */ - status = 1 // will be 3 if unspecified - tvTypes = listOf( - "AsianDrama", - "Anime", - "TvSeries", - "Movie", - ) - - iconUrl = "https://www.google.com/s2/favicons?domain=phimmoichilla.net&sz=%size%" -} diff --git a/PhimmoichillProvider/src/main/AndroidManifest.xml b/PhimmoichillProvider/src/main/AndroidManifest.xml deleted file mode 100644 index c98063f8..00000000 --- a/PhimmoichillProvider/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/PhimmoichillProvider/src/main/kotlin/com/hexated/PhimmoichillProvider.kt b/PhimmoichillProvider/src/main/kotlin/com/hexated/PhimmoichillProvider.kt deleted file mode 100644 index 75e214ce..00000000 --- a/PhimmoichillProvider/src/main/kotlin/com/hexated/PhimmoichillProvider.kt +++ /dev/null @@ -1,192 +0,0 @@ -package com.hexated - -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.LoadResponse.Companion.addActors -import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer -import com.lagradost.cloudstream3.mvvm.safeApiCall -import com.lagradost.cloudstream3.utils.* -import org.jsoup.nodes.Element -import java.net.URLDecoder - -class PhimmoichillProvider : MainAPI() { - override var mainUrl = "https://phimmoichilla.net" - override var name = "Phimmoichill" - override val hasMainPage = true - override var lang = "vi" - override val hasDownloadSupport = true - override val supportedTypes = setOf( - TvType.Movie, - TvType.TvSeries, - TvType.Anime, - TvType.AsianDrama - ) - - override val mainPage = mainPageOf( - "$mainUrl/genre/phim-chieu-rap/page-" to "Phim Chiếu Rạp", - "$mainUrl/list/phim-le/page-" to "Phim Lẻ", - "$mainUrl/list/phim-bo/page-" to "Phim Bộ", - "$mainUrl/genre/phim-hoat-hinh/page-" to "Phim Hoạt Hình", - "$mainUrl/country/phim-han-quoc/page-" to "Phim Hàn Quốc", - "$mainUrl/country/phim-trung-quoc/page-" to "Phim Trung Quốc", - "$mainUrl/country/phim-thai-lan/page-" to "Phim Thái Lan", - ) - - override suspend fun getMainPage( - page: Int, - request: MainPageRequest - ): HomePageResponse { - val document = app.get(request.data + page).document - val home = document.select("li.item").mapNotNull { - it.toSearchResult() - } - return newHomePageResponse( - list = HomePageList( - name = request.name, - list = home, - isHorizontalImages = true - ), - hasNext = true - ) - } - - private fun decode(input: String): String? = URLDecoder.decode(input, "utf-8") - - private fun Element.toSearchResult(): SearchResponse { - val title = this.selectFirst("p,h3")?.text()?.trim().toString() - val href = fixUrl(this.selectFirst("a")!!.attr("href")) - val posterUrl = decode(this.selectFirst("img")!!.attr("src").substringAfter("url=")) - val temp = this.select("span.label").text() - return if (temp.contains(Regex("\\d"))) { - val episode = Regex("(\\((\\d+))|(\\s(\\d+))").find(temp)?.groupValues?.map { num -> - num.replace(Regex("\\(|\\s"), "") - }?.distinct()?.firstOrNull()?.toIntOrNull() - newAnimeSearchResponse(title, href, TvType.TvSeries) { - this.posterUrl = posterUrl - addSub(episode) - } - } else { - val quality = - temp.replace(Regex("(-.*)|(\\|.*)|(?i)(VietSub.*)|(?i)(Thuyết.*)"), "").trim() - newMovieSearchResponse(title, href, TvType.Movie) { - this.posterUrl = posterUrl - addQuality(quality) - } - } - } - - override suspend fun search(query: String): List { - val link = "$mainUrl/tim-kiem/$query" - val document = app.get(link).document - - return document.select("ul.list-film li").map { - it.toSearchResult() - } - } - - override suspend fun load(url: String): LoadResponse { - val document = app.get(url).document - - val title = document.selectFirst("h1[itemprop=name]")?.text()?.trim().toString() - val link = document.select("ul.list-button li:last-child a").attr("href") - val poster = document.selectFirst("div.image img[itemprop=image]")?.attr("src") - val tags = document.select("ul.entry-meta.block-film li:nth-child(4) a").map { it.text() } - val year = document.select("ul.entry-meta.block-film li:nth-child(2) a").text().trim() - .toIntOrNull() - val tvType = if (document.select("div.latest-episode").isNotEmpty() - ) TvType.TvSeries else TvType.Movie - val description = document.select("div#film-content").text().trim() - val trailer = - document.select("div#trailer script").last()?.data()?.substringAfter("file: \"") - ?.substringBefore("\",") - val rating = - document.select("ul.entry-meta.block-film li:nth-child(7) span").text().toRatingInt() - val actors = document.select("ul.entry-meta.block-film li:last-child a").map { it.text() } - val recommendations = document.select("ul#list-film-realted li.item").map { - it.toSearchResult().apply { - this.posterUrl = decode(it.selectFirst("img")!!.attr("data-src").substringAfter("url=")) - } - } - - return if (tvType == TvType.TvSeries) { - val docEpisodes = app.get(link).document - val episodes = docEpisodes.select("ul#list_episodes > li").map { - val href = it.select("a").attr("href") - val episode = - it.select("a").text().replace(Regex("[^0-9]"), "").trim().toIntOrNull() - val name = "Episode $episode" - Episode( - data = href, - name = name, - episode = episode, - ) - } - newTvSeriesLoadResponse(title, url, TvType.TvSeries, episodes) { - this.posterUrl = poster - this.year = year - this.plot = description - this.tags = tags - this.rating = rating - addActors(actors) - this.recommendations = recommendations - addTrailer(trailer) - } - } else { - newMovieLoadResponse(title, url, TvType.Movie, link) { - this.posterUrl = poster - this.year = year - this.plot = description - this.tags = tags - this.rating = rating - addActors(actors) - this.recommendations = recommendations - addTrailer(trailer) - } - } - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - val document = app.get(data).document - - val key = document.select("div#content script") - .find { it.data().contains("filmInfo.episodeID =") }?.data()?.let { script -> - val id = script.substringAfter("filmInfo.episodeID = parseInt('") - app.post( - // Not mainUrl - url = "https://phimmoichills.net/pmplayer.php", - data = mapOf("qcao" to id, "sv" to "0"), - referer = data, - headers = mapOf( - "X-Requested-With" to "XMLHttpRequest", - "Content-Type" to "application/x-www-form-urlencoded; charset=UTF-8" - ) - ).text.substringAfterLast("iniPlayers(\"") - .substringBefore("\",") - } - - listOf( - Pair("https://so-trym.topphimmoi.org/raw/$key/index.m3u8", "PMFAST"), - Pair("https://dash.megacdn.xyz/raw/$key/index.m3u8", "PMHLS"), - Pair("https://dash.megacdn.xyz/dast/$key/index.m3u8", "PMBK") - ).apmap { (link, source) -> - safeApiCall { - callback.invoke( - ExtractorLink( - source, - source, - link, - referer = "$mainUrl/", - quality = Qualities.P1080.value, - isM3u8 = true, - ) - ) - } - } - return true - } - -} diff --git a/PhimmoichillProvider/src/main/kotlin/com/hexated/PhimmoichillProviderPlugin.kt b/PhimmoichillProvider/src/main/kotlin/com/hexated/PhimmoichillProviderPlugin.kt deleted file mode 100644 index eebb608f..00000000 --- a/PhimmoichillProvider/src/main/kotlin/com/hexated/PhimmoichillProviderPlugin.kt +++ /dev/null @@ -1,14 +0,0 @@ - -package com.hexated - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class PhimmoichillProviderPlugin: Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(PhimmoichillProvider()) - } -} \ No newline at end of file diff --git a/README.md b/README.md deleted file mode 100644 index 7e1b7289..00000000 --- a/README.md +++ /dev/null @@ -1,43 +0,0 @@ -
- -# *Hexated-Extensions* - -## - - -

- - contributors - - - last update - - - forks - - - stars - - - open issues - - - license - - - hits - -

- -## - -# 🕹 Libraries: - - - -## - -[![Discord](https://invidget.switchblade.xyz/5Hus6fM)](https://discord.gg/5Hus6fM) - -
- diff --git a/RebahinProvider/build.gradle.kts b/RebahinProvider/build.gradle.kts deleted file mode 100644 index 6749964c..00000000 --- a/RebahinProvider/build.gradle.kts +++ /dev/null @@ -1,29 +0,0 @@ -// use an integer for version numbers -version = 5 - - -cloudstream { - language = "id" - // All of these properties are optional, you can safely remove them - - // description = "Lorem Ipsum" - authors = listOf("Hexated") - - /** - * Status int as the following: - * 0: Down - * 1: Ok - * 2: Slow - * 3: Beta only - * */ - status = 1 // will be 3 if unspecified - tvTypes = listOf( - "AsianDrama", - "Anime", - "TvSeries", - "Movie", - ) - - - iconUrl = "https://www.google.com/s2/favicons?domain=104.237.198.194&sz=%size%" -} \ No newline at end of file diff --git a/RebahinProvider/src/main/AndroidManifest.xml b/RebahinProvider/src/main/AndroidManifest.xml deleted file mode 100644 index c98063f8..00000000 --- a/RebahinProvider/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/RebahinProvider/src/main/kotlin/com/hexated/Kitanonton.kt b/RebahinProvider/src/main/kotlin/com/hexated/Kitanonton.kt deleted file mode 100644 index aae0b2fb..00000000 --- a/RebahinProvider/src/main/kotlin/com/hexated/Kitanonton.kt +++ /dev/null @@ -1,32 +0,0 @@ -package com.hexated - -import com.lagradost.cloudstream3.* - -class Kitanonton : RebahinProvider() { - override var mainUrl = "http://kitanonton.org" - override var name = "KitaNonton" - override var mainServer = "https://199.87.210.226" - - override val mainPage = mainPageOf( - "$mainUrl/genre/populer/page/" to "Populer Movies", - "$mainUrl/movies/page/" to "New Movies", - "$mainUrl/genre/westseries/page/" to "West TV Series", - "$mainUrl/genre/drama-korea/page/" to "Drama Korea", - "$mainUrl/genre/animation/page/" to "Anime", - "$mainUrl/genre/series-indonesia/page/" to "Drama Indonesia", - "$mainUrl/genre/drama-jepang/page/" to "Drama Jepang", - "$mainUrl/genre/drama-china/page/" to "Drama China", - "$mainUrl/genre/thailand-series/page/" to "Drama Thailand", - ) - - override suspend fun getMainPage( - page: Int, - request: MainPageRequest - ): HomePageResponse { - val document = app.get(request.data + page).document - val home = document.select("div#featured div.ml-item").mapNotNull { - it.toSearchResult() - } - return newHomePageResponse(request.name, home) - } -} \ No newline at end of file diff --git a/RebahinProvider/src/main/kotlin/com/hexated/RebahinProvider.kt b/RebahinProvider/src/main/kotlin/com/hexated/RebahinProvider.kt deleted file mode 100644 index e8e5ad1e..00000000 --- a/RebahinProvider/src/main/kotlin/com/hexated/RebahinProvider.kt +++ /dev/null @@ -1,315 +0,0 @@ -package com.hexated - -import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.LoadResponse.Companion.addActors -import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer -import com.lagradost.cloudstream3.mvvm.logError -import com.lagradost.cloudstream3.mvvm.safeApiCall -import com.lagradost.cloudstream3.network.WebViewResolver -import com.lagradost.cloudstream3.utils.* -import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson -import org.jsoup.nodes.Element -import java.net.URI - -open class RebahinProvider : MainAPI() { - override var mainUrl = "http://104.237.198.198" - override var name = "Rebahin" - override val hasMainPage = true - override var lang = "id" - override val hasDownloadSupport = true - open var mainServer = "http://172.96.161.72" - override val supportedTypes = setOf( - TvType.Movie, - TvType.TvSeries, - TvType.Anime, - TvType.AsianDrama - ) - - override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse { - val urls = listOf( - Pair("Featured", "xtab1"), - Pair("Film Terbaru", "xtab2"), - Pair("Romance", "xtab3"), - Pair("Drama", "xtab4"), - Pair("Action", "xtab5"), - Pair("Scifi", "xtab6"), - Pair("Tv Series Terbaru", "stab1"), - Pair("Anime Series", "stab2"), - Pair("Drakor Series", "stab3"), - Pair("West Series", "stab4"), - Pair("China Series", "stab5"), - Pair("Japan Series", "stab6"), - ) - - val items = ArrayList() - - for ((header, tab) in urls) { - try { - val home = - app.get("$mainUrl/wp-content/themes/indoxxi/ajax-top-$tab.php").document.select( - "div.ml-item" - ).mapNotNull { - it.toSearchResult() - } - items.add(HomePageList(header, home)) - } catch (e: Exception) { - logError(e) - } - } - - if (items.size <= 0) throw ErrorLoadingException() - return HomePageResponse(items) - } - - fun Element.toSearchResult(): SearchResponse? { - val title = this.selectFirst("span.mli-info > h2")?.text() ?: return null - val href = this.selectFirst("a")!!.attr("href") - val type = - if (this.select("span.mli-quality").isNotEmpty()) TvType.Movie else TvType.TvSeries - return if (type == TvType.Movie) { - val posterUrl = fixUrlNull(this.select("img").attr("src")) - val quality = getQualityFromString(this.select("span.mli-quality").text().trim()) - newMovieSearchResponse(title, href, TvType.Movie) { - this.posterUrl = posterUrl - this.quality = quality - } - } else { - val posterUrl = - fixUrlNull( - this.select("img").attr("src") - .ifEmpty { this.select("img").attr("data-original") }) - val episode = - this.select("div.mli-eps > span").text().replace(Regex("[^0-9]"), "").toIntOrNull() - newAnimeSearchResponse(title, href, TvType.TvSeries) { - this.posterUrl = posterUrl - addSub(episode) - } - } - } - - override suspend fun search(query: String): List { - val link = "$mainUrl/?s=$query" - val document = app.get(link).document - - return document.select("div.ml-item").mapNotNull { - it.toSearchResult() - } - } - - override suspend fun load(url: String): LoadResponse { - val document = app.get(url).document - - val title = document.selectFirst("h3[itemprop=name]")!!.ownText().trim() - val poster = document.select(".mvic-desc > div.thumb.mvic-thumb").attr("style") - .substringAfter("url(").substringBeforeLast(")") - val tags = document.select("span[itemprop=genre]").map { it.text() } - - val year = Regex("([0-9]{4}?)-").find( - document.selectFirst(".mvici-right > p:nth-child(3)")!!.ownText().trim() - )?.groupValues?.get(1).toString().toIntOrNull() - val tvType = if (url.contains("/series/")) TvType.TvSeries else TvType.Movie - val description = document.select("span[itemprop=reviewBody] > p").text().trim() - val trailer = fixUrlNull(document.selectFirst("div.modal-body-trailer iframe")?.attr("src")) - val rating = document.selectFirst("span[itemprop=ratingValue]")?.text()?.toRatingInt() - val duration = document.selectFirst(".mvici-right > p:nth-child(1)")!! - .ownText().replace(Regex("[^0-9]"), "").toIntOrNull() - val actors = document.select("span[itemprop=actor] > a").map { it.select("span").text() } - - val baseLink = fixUrl(document.select("div#mv-info > a").attr("href").toString()) - - return if (tvType == TvType.TvSeries) { - val episodes = app.get(baseLink).document.select("div#list-eps > a").map { - Pair(it.text(), it.attr("data-iframe")) - }.groupBy { it.first }.map { eps -> - Episode( - data = eps.value.map { fixUrl(base64Decode(it.second)) }.toString(), - name = eps.key, - episode = eps.key.filter { it.isDigit() }.toIntOrNull() - ) - - } - newTvSeriesLoadResponse(title, url, TvType.TvSeries, episodes) { - this.posterUrl = poster - this.year = year - this.plot = description - this.tags = tags - this.rating = rating - this.duration = duration - addActors(actors) - addTrailer(trailer) - } - } else { - val links = - app.get(baseLink).document.select("div#server-list div.server-wrapper div[id*=episode]") - .map { - fixUrl(base64Decode(it.attr("data-iframe"))) - }.toString() - newMovieLoadResponse(title, url, TvType.Movie, links) { - this.posterUrl = poster - this.year = year - this.plot = description - this.tags = tags - this.rating = rating - this.duration = duration - addActors(actors) - addTrailer(trailer) - } - } - } - - private fun getLanguage(str: String): String { - return when { - str.contains("indonesia", true) || str.contains("bahasa", true) -> "Indonesian" - else -> str - } - } - - private suspend fun invokeLokalSource( - url: String, - subCallback: (SubtitleFile) -> Unit, - sourceCallback: (ExtractorLink) -> Unit - ) { - val document = app.get( - url, - allowRedirects = false, - referer = mainUrl, - headers = mapOf("Accept" to "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8") - ).document - - document.select("script").find { it.data().contains("config =") }?.data()?.let { script -> - Regex("\"file\":\\s?\"(.+.m3u8)\"").find(script)?.groupValues?.getOrNull(1) - ?.let { link -> - M3u8Helper.generateM3u8( - name, - link, - referer = "$mainServer/", - headers = mapOf("Accept" to "*/*", "Origin" to mainServer) - ).forEach(sourceCallback) - } - - val subData = - Regex("\"?tracks\"?:\\s\\n?\\[(.*)],").find(script)?.groupValues?.getOrNull(1) - ?: Regex("\"?tracks\"?:\\s\\n?\\[\\s*(?s:(.+)],\\n\\s*\"sources)").find(script)?.groupValues?.getOrNull( - 1 - ) - tryParseJson>("[$subData]")?.map { - subCallback.invoke( - SubtitleFile( - getLanguage(it.label ?: return@map null), - if (it.file?.contains(".srt") == true) it.file else return@map null - ) - ) - - } - } - } - - private suspend fun invokeKotakAjairSource( - url: String, - subCallback: (SubtitleFile) -> Unit, - sourceCallback: (ExtractorLink) -> Unit - ) { - val domainUrl = "https://kotakajair.xyz" - val id = url.trimEnd('/').split("/").last() - val sources = app.post( - url = "$domainUrl/api/source/$id", - data = mapOf("r" to mainUrl, "d" to URI(url).host) - ).parsed() - - sources.data?.map { - sourceCallback.invoke( - ExtractorLink( - name, - "KotakAjair", - fixUrl(it.file), - referer = url, - quality = getQualityFromName(it.label) - ) - ) - } - val userData = sources.player.poster_file.split("/")[2] - sources.captions?.map { - subCallback.invoke( - SubtitleFile( - getLanguage(it.language), - "$domainUrl/asset/userdata/$userData/caption/${it.hash}/${it.id}.srt" - ) - ) - } - - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - - data.removeSurrounding("[", "]").split(",").map { it.trim() }.apmap { link -> - safeApiCall { - when { - link.startsWith(mainServer) -> invokeLokalSource( - link, - subtitleCallback, - callback - ) - link.startsWith("https://kotakajair.xyz") -> invokeKotakAjairSource( - link, - subtitleCallback, - callback - ) - else -> { - loadExtractor(link, "$mainUrl/", subtitleCallback, callback) - if (link.startsWith("https://sbfull.com")) { - val response = app.get( - link, interceptor = WebViewResolver( - Regex("""\.srt""") - ) - ) - subtitleCallback.invoke( - SubtitleFile( - "Indonesian", - response.url - ) - ) - } - } - } - } - } - - return true - } - - private data class Tracks( - @JsonProperty("file") val file: String? = null, - @JsonProperty("label") val label: String? = null, - @JsonProperty("kind") val kind: String? = null - ) - - private data class Captions( - @JsonProperty("id") val id: String, - @JsonProperty("hash") val hash: String, - @JsonProperty("language") val language: String, - ) - - private data class Data( - @JsonProperty("file") val file: String, - @JsonProperty("label") val label: String, - ) - - private data class Player( - @JsonProperty("poster_file") val poster_file: String, - ) - - private data class ResponseKotakAjair( - @JsonProperty("success") val success: Boolean, - @JsonProperty("player") val player: Player, - @JsonProperty("data") val data: List?, - @JsonProperty("captions") val captions: List? - ) - -} - diff --git a/RebahinProvider/src/main/kotlin/com/hexated/RebahinProviderPlugin.kt b/RebahinProvider/src/main/kotlin/com/hexated/RebahinProviderPlugin.kt deleted file mode 100644 index 3824f170..00000000 --- a/RebahinProvider/src/main/kotlin/com/hexated/RebahinProviderPlugin.kt +++ /dev/null @@ -1,15 +0,0 @@ - -package com.hexated - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class RebahinProviderPlugin: Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(RebahinProvider()) -// registerMainAPI(Kitanonton()) - } -} \ No newline at end of file diff --git a/Samehadaku/build.gradle.kts b/Samehadaku/build.gradle.kts deleted file mode 100644 index 4e5c21b1..00000000 --- a/Samehadaku/build.gradle.kts +++ /dev/null @@ -1,27 +0,0 @@ -// use an integer for version numbers -version = 10 - - -cloudstream { - language = "id" - // All of these properties are optional, you can safely remove them - - // description = "Lorem Ipsum" - authors = listOf("Hexated") - - /** - * Status int as the following: - * 0: Down - * 1: Ok - * 2: Slow - * 3: Beta only - * */ - status = 1 // will be 3 if unspecified - tvTypes = listOf( - "AnimeMovie", - "OVA", - "Anime", - ) - - iconUrl = "https://www.google.com/s2/favicons?domain=194.163.183.129&sz=%size%" -} \ No newline at end of file diff --git a/Samehadaku/src/main/AndroidManifest.xml b/Samehadaku/src/main/AndroidManifest.xml deleted file mode 100644 index 874740e3..00000000 --- a/Samehadaku/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/Samehadaku/src/main/kotlin/com/hexated/Extractors.kt b/Samehadaku/src/main/kotlin/com/hexated/Extractors.kt deleted file mode 100644 index f0b6db8f..00000000 --- a/Samehadaku/src/main/kotlin/com/hexated/Extractors.kt +++ /dev/null @@ -1,43 +0,0 @@ -package com.hexated - -import com.lagradost.cloudstream3.SubtitleFile -import com.lagradost.cloudstream3.app -import com.lagradost.cloudstream3.extractors.XStreamCdn -import com.lagradost.cloudstream3.utils.ExtractorApi -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.Qualities -import java.net.URI - -class Suzihaza: XStreamCdn() { - override val name: String = "Suzihaza" - override val mainUrl: String = "https://suzihaza.com" -} - -open class Wibufile : ExtractorApi() { - override val name: String = "Wibufile" - override val mainUrl: String = "https://wibufile.com" - override val requiresReferer = false - - override suspend fun getUrl( - url: String, - referer: String?, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ) { - val res = app.get(url).text - val video = Regex("src: ['\"](.*?)['\"]").find(res)?.groupValues?.get(1) - - callback.invoke( - ExtractorLink( - name, - name, - video ?: return, - "$mainUrl/", - Qualities.Unknown.value, - URI(url).path.endsWith(".m3u8") - ) - ) - - } - -} \ No newline at end of file diff --git a/Samehadaku/src/main/kotlin/com/hexated/Samehadaku.kt b/Samehadaku/src/main/kotlin/com/hexated/Samehadaku.kt deleted file mode 100644 index 773e2927..00000000 --- a/Samehadaku/src/main/kotlin/com/hexated/Samehadaku.kt +++ /dev/null @@ -1,234 +0,0 @@ -package com.hexated - -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.Qualities -import com.lagradost.cloudstream3.utils.loadExtractor -import org.jsoup.nodes.Element - -class Samehadaku : MainAPI() { - override var mainUrl = "https://samehadaku.day" - override var name = "Samehadaku" - override val hasMainPage = true - override var lang = "id" - override val hasDownloadSupport = true - - override val supportedTypes = setOf( - TvType.Anime, - TvType.AnimeMovie, - TvType.OVA - ) - - companion object { - const val acefile = "https://acefile.co" - - fun getType(t: String): TvType { - return if (t.contains("OVA", true) || t.contains("Special", true)) TvType.OVA - else if (t.contains("Movie", true)) TvType.AnimeMovie - else TvType.Anime - } - - fun getStatus(t: String): ShowStatus { - return when (t) { - "Completed" -> ShowStatus.Completed - "Ongoing" -> ShowStatus.Ongoing - else -> ShowStatus.Completed - } - } - } - - override val mainPage = mainPageOf( - "$mainUrl/page/" to "Episode Terbaru", - "$mainUrl/" to "HomePage", - ) - - override suspend fun getMainPage( - page: Int, - request: MainPageRequest - ): HomePageResponse { - val items = mutableListOf() - - if (request.name != "Episode Terbaru" && page <= 1) { - val doc = app.get(request.data).document - doc.select("div.widget_senction").forEach { block -> - val header = block.selectFirst("div.widget-title h3")?.ownText() ?: return@forEach - val home = block.select("div.animepost").mapNotNull { - it.toSearchResult() - } - if (home.isNotEmpty()) items.add(HomePageList(header, home)) - } - } - - if (request.name == "Episode Terbaru") { - val home = - app.get(request.data + page).document.selectFirst("div.post-show")?.select("ul li") - ?.mapNotNull { - it.toSearchResult() - } ?: throw ErrorLoadingException("No Media Found") - items.add(HomePageList(request.name, home, true)) - } - - return newHomePageResponse(items) - - } - - private fun Element.toSearchResult(): AnimeSearchResponse? { - val title = this.selectFirst("div.title, h2.entry-title a, div.lftinfo h2")?.text()?.trim() - ?: return null - val href = fixUrlNull(this.selectFirst("a")?.attr("href") ?: return null) - val posterUrl = fixUrlNull(this.select("img").attr("src")) - val epNum = this.selectFirst("div.dtla author")?.text()?.toIntOrNull() - return newAnimeSearchResponse(title, href ?: return null, TvType.Anime) { - this.posterUrl = posterUrl - addSub(epNum) - } - - } - - override suspend fun search(query: String): List { - val document = app.get("$mainUrl/?s=$query").document - return document.select("main#main div.animepost").mapNotNull { - it.toSearchResult() - } - } - - override suspend fun load(url: String): LoadResponse? { - val fixUrl = if (url.contains("/anime/")) { - url - } else { - app.get(url).document.selectFirst("div.nvs.nvsc a")?.attr("href") - } - - val document = app.get(fixUrl ?: return null).document - val title = document.selectFirst("h1.entry-title")?.text()?.removeBloat() ?: return null - val poster = document.selectFirst("div.thumb > img")?.attr("src") - val tags = document.select("div.genre-info > a").map { it.text() } - val year = document.selectFirst("div.spe > span:contains(Rilis)")?.ownText()?.let { - Regex("\\d,\\s(\\d*)").find(it)?.groupValues?.getOrNull(1)?.toIntOrNull() - } - val status = getStatus( - document.selectFirst("div.spe > span:contains(Status)")?.ownText() ?: return null - ) - val type = - document.selectFirst("div.spe > span:contains(Type)")?.ownText()?.trim()?.lowercase() - ?: "tv" - val rating = document.selectFirst("span.ratingValue")?.text()?.trim()?.toRatingInt() - val description = document.select("div.desc p").text().trim() - val trailer = document.selectFirst("div.trailer-anime iframe")?.attr("src") - - val episodes = document.select("div.lstepsiode.listeps ul li").mapNotNull { - val header = it.selectFirst("span.lchx > a") ?: return@mapNotNull null - val episode = Regex("Episode\\s?(\\d+)").find(header.text())?.groupValues?.getOrNull(1) - ?.toIntOrNull() - val link = fixUrl(header.attr("href")) - Episode(link, episode = episode) - }.reversed() - - val recommendations = document.select("aside#sidebar ul li").mapNotNull { - it.toSearchResult() - } - - return newAnimeLoadResponse(title, url, getType(type)) { - engName = title - posterUrl = poster - this.year = year - addEpisodes(DubStatus.Subbed, episodes) - showStatus = status - this.rating = rating - plot = description - addTrailer(trailer) - this.tags = tags - this.recommendations = recommendations - } - - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - - val document = app.get(data).document - - argamap( - { - document.select("div#server ul li div").apmap { - val dataPost = it.attr("data-post") - val dataNume = it.attr("data-nume") - val dataType = it.attr("data-type") - - val iframe = app.post( - url = "$mainUrl/wp-admin/admin-ajax.php", - data = mapOf( - "action" to "player_ajax", - "post" to dataPost, - "nume" to dataNume, - "type" to dataType - ), - referer = data, - headers = mapOf("X-Requested-With" to "XMLHttpRequest") - ).document.select("iframe").attr("src") - - loadFixedExtractor(fixedIframe(iframe), it.text(), "$mainUrl/", subtitleCallback, callback) - - } - }, - { - document.select("div#downloadb li").map { el -> - el.select("a").apmap { - loadFixedExtractor(fixedIframe(it.attr("href")), el.select("strong").text(), "$mainUrl/", subtitleCallback, callback) - } - } - } - ) - - return true - } - - private suspend fun loadFixedExtractor( - url: String, - name: String, - referer: String? = null, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ) { - loadExtractor(url, referer, subtitleCallback) { link -> - callback.invoke( - ExtractorLink( - link.name, - link.name, - link.url, - link.referer, - name.fixQuality(), - link.isM3u8, - link.headers, - link.extractorData - ) - ) - } - } - - private fun String.fixQuality() : Int { - return when(this) { - "MP4HD" -> Qualities.P720.value - "FULLHD" -> Qualities.P1080.value - else -> this.filter { it.isDigit() }.toIntOrNull() ?: Qualities.Unknown.value - } - } - - private fun fixedIframe(url: String): String { - val id = Regex("""(?:/f/|/file/)(\w+)""").find(url)?.groupValues?.getOrNull(1) - return when { - url.startsWith(acefile) -> "${acefile}/player/$id" - else -> fixUrl(url) - } - } - - private fun String.removeBloat(): String { - return this.replace(Regex("(Nonton)|(Anime)|(Subtitle\\sIndonesia)"), "").trim() - } - -} \ No newline at end of file diff --git a/Samehadaku/src/main/kotlin/com/hexated/SamehadakuPlugin.kt b/Samehadaku/src/main/kotlin/com/hexated/SamehadakuPlugin.kt deleted file mode 100644 index 65e90137..00000000 --- a/Samehadaku/src/main/kotlin/com/hexated/SamehadakuPlugin.kt +++ /dev/null @@ -1,16 +0,0 @@ - -package com.hexated - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class SamehadakuPlugin: Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(Samehadaku()) - registerExtractorAPI(Suzihaza()) - registerExtractorAPI(Wibufile()) - } -} \ No newline at end of file diff --git a/StremioX/build.gradle.kts b/StremioX/build.gradle.kts deleted file mode 100644 index 5e6b7146..00000000 --- a/StremioX/build.gradle.kts +++ /dev/null @@ -1,26 +0,0 @@ -// use an integer for version numbers -version = 9 - - -cloudstream { - language = "en" - // All of these properties are optional, you can safely remove them - - description = "[!] Requires Setup \n- StremioX allows you to use stream addons \n- StremioC allows you to use catalog addons" - authors = listOf("Hexated") - - /** - * Status int as the following: - * 0: Down - * 1: Ok - * 2: Slow - * 3: Beta only - * */ - status = 1 // will be 3 if unspecified - tvTypes = listOf( - "TvSeries", - "Movie", - ) - - iconUrl = "https://raw.githubusercontent.com/hexated/cloudstream-extensions-hexated/master/StremioX/icon.png" -} \ No newline at end of file diff --git a/StremioX/icon.png b/StremioX/icon.png deleted file mode 100644 index 6ca05ae63433004899d15c56b37e5c7de8429ec5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 52736 zcmeFY^;=Z$7cRVKsG&hpNkLix0i~HiKvcR>DH9YFkj?=SDJkhLK@=(J96~@sQt9sQ zn1PA2@$2Wk-rvqYa4v>xfIWNf^*n2>d)@0^n-E=Xb!v)B6aWBF-@T)%4**c`M<@Uz z1>X+cf1iSH#Ma7M$^cLrNr`<#0{+hZDgd~M1Hif!0LUc* z0EgEHJ7g^vHfxD{8hVJ8AIFIsc_mjEa;TmP!ybn^7#3U^nd%CiOMGC$zuWx_l{5_Tb zhZpAm+&>}uz9~1t4?=YN_VEP^cYj1XH2&JffT*j?qoW^d`F`v7#TmsN?7sKLEPK@3 z*B&SeC=uvOc^ehge#o9oyXpME`Pxsp^^OhMtN)ljV>_nvyOpIY$Vd_R|K9$uTTsbb z{)b7|nRGm!F;swTdYwXWoR*`!N*Z>I82qGYlcFcs&Z5OgRMl4?QL2>{;BoRuPUg?2 z7y7*=^%Q}RpNM~p;xwj*wS9Th@3_i6rP$B^zSty$m9Owtsk!)vl0t@Qts#5E>g>RN z|LQ3l;rfktvW2|%F}r%jDp{)%x0&)sc@_mPvHFP0lZ zPmhR|WU(}R&>IwcwK?W>Wmhqlt+_Ah=~G_rq@*N+PgdHpT4V}$$i}M5Gmgn@>u;KB zn4ER5?XN$st*#Y!uXl#0rJ$xzXXxj0tcPTPKHBH|*BxVW%p-U~V~%q5Cimv^$Em3tt-gIL8(12&;#So6()qzq2eP z9IF$q1Or~A=%TF?lK*fidGEk*=xwoW@dcn4Di?KhNl zIJYld+!>g8@Vf@RLEJ#4GkGh0{A_Y@1bz}zXX(9uaF#ypwplYJ>1COkoGPmnt=ZVG z;i=AWFa2`1B6l)hzm#7{VdG^m%7+H5mzn$$5;1yRYvIar)zdm7qJ>4*EWPWovn{{K zOo}Y3T~3?pR*)Neg=8ip&}Xb_{zOPb#pk9#VWNk`MnEO0(Xg*3x&u#9kDnZQj>2^5 z><}gmEWI6fB8V&#(^Gw3WlLi)77}|r{>Su7@%6w;Op0pGjX+a854+)5SF9x_4k>~O zW0D!O-@U{dM&&Dh=Zxhvpkk=oRwF~{L8`pV_HYO#9J|Ri1$Q}W;HyeZhG!;8WQH~| zycY&^n6*fdyjdvZNLEOOH_qf3<$+a)yE&W;S*9hW;<{Ar_ch4*`XJ>g!3tsNNrlZk zQhwr2Hyvbd&;+U0tDmU!SBwADG`22pKOm3 zats(GXS6>~+a?9-G<-uP;DUlfwK=~fUKC7h5GRs`lZXcK`WTG*{7AI`+*6TRP4t^O^TysF=W5D~(RUYE~`A@l9ig;LJu3}BYaYiq@oFlj>gX_7vEFbP+>A$xFEChKFKRje!c zAi%e4?av?lKEZoq?hj#S21&5@*=^7v;GaBQNG$;Jc}moDNvsy=oK8l_@sA@2b(ZKr zML6LP9G??(qHxf`?_~g2Rwc{qyOX6`&eGH4WE=2jrtx64eFj@`LTG=8@>)0HN|Jfn zg3_k%f8nq7mLefsIqnZv@-Cu=hqeL4DuB9N~zJ8?q@E63%z9bw?I30%=@7X6zre}a2xDeJnrgna$- z@##v)1&!T0FBfShe}iT)T)>xfS~6ztB@#0tI)mFR#*d)(xO^O5ZO;pD2ENVu0^wG; zmU*uU23q58`0L2zK2~ZhMH8O@$L+8a221U6d!PderXR2Lb>5m(k*a|_

)CfrCUEY_TyWE_isndIi zudqCgM7_cOv?X-?{&5H=wHg*1l{0J=aMeBt`DwridMUH&50ee~PYkEi(a8_X{UOWKfHQng zA1)B?aoDtn5|_Px#0&5THViadA_@3WS0(yL}?!K#~?8*_*Uuj{6V^UzFAHHk~u>4&MBORh^+>4r6)WRFNVaJ z5UMf`%PJsQsINkhBVt2@BJd~wJJ!5;hA7H=ZM4doP+^IO6AU_N8ctaR1oC{Ly&G;! zAPXFT#V|B( zEs%4gSti;jpKa-7@p>y0z9xTR;O6=}R9%0uw4$v1-VQbkwZBlzf$Ji?0OQ;`Q*28Q z+~i*f`}3Od`B>>BuDBkZrU6H9jucZ5Hip=KrOLm+ok@WR;cuDwj-}o5c)mV^6CiZK zJ)8-91cz+tJ-PKuCYw!Es@gMe5{kc7tqG&{MNs=yDH$I>oor5z5zSzRyWvhJ0b!_< zvH5lH&7WDUhU7o9C=l--1tRwcqeraqlVUh^BzAWDGr>F3Q~jAxvx?Z$FMbu4@xkAm z+d|ArW2#gyW;y&X#6%Am_`KY}U5mD_Ka8R6n^tf<#kZvO&o>F@UjU2%zvY&htH&D+ zn3l*b>i7L>J-ctB>gNC5mol(~x^8K|*2(@vDg!DJHuW_TR3ZdV|7 zGc|1*vjsXNr$Cy^Le_DknaK$H{+%%JtMZg7=*)ZN0BO~ifet|)Sx(k1%w`bT=p;pd zyKSlSV1|6MJjEjh&$-?D-2CL3$#xpsZSv9K zt`=8g;Qa->y#k>y4Y@8rI6}TS*=&RdGM27dJLFX^93ipDCF5dg_0q8 zew(Wi@SYyu@Fd703B%&MjU@DKO*zh5nnx~y;LjMU)w=k5u_wjSecLwdb8!PLns!EL!)Wxta31&K=_EWmxOMxv1^`KbpcV0Y98Hd+RfG4hS4xld-2 zz0M}>)6Fa%erS-8N%cv2BrgK&57l>7k(JIPU%tsZ*}=yU+PD6IP5oiP=!bXgfzs%bg5jTpYV=W$(4mAX)HiB?L#Qc-SQGkd%l8K9~7 zV*FUzkQe@nzvq4aHxxPZLvnjvvg9K17B>fLTW|a?&f<`&zEBYP6Efh3Z%)^aXs10& z9NYkvtyZ*fC3R)M`M!lY;i4�UvU82cHy+qBI~AoX{BM|r@O@*ynE?$4rB;d7S1 zgar&dmEDs(0c#5aw#>5TlopOhX1qtiqUeoba>Z26=8*gg_`xGMgc~CXKS8>mLkt~^iLdKSpZ6Lq zP9CId@WwxJ#^(Xl&Xct`+n5&Q`s>%s=~RK!ipl=$qh9h=_K$@{P5f_p9&LXmScoO9 z;+pMoYj7#~iUXFa+QGl^&fcTBjc!BEKSZ9=9(^RY?XE(Hb%twacXRqh9dSA12;Fc3 z3(9qKw*aY>wYY0+^5ERm6e6DF#H7i+8WvjA@ic8O{*-$3jI=V1C9{CSPyR&A1oyCo zAP-g?BKXlM9KWNbrHPb(Fm7B-ZS=R8n&7FGZ*}4ui!UJ`mPv) z=UF}rSR-$mel%n2V%l@@*-mkrsq=WhD5648yYOdY;{ zP-XWQ8GV;#q8}%ZD+XoE6 z`~rCyaL-xv1Xx?RrUh*_`?Pa3z`Q)N*T$x}Z>^euyfg*5Ig^;2h#9zgS&iZ<+ppEQ z>~Z&hi$m^X$@#pFd$v>LkQ<9TlV09V*JvX=h`JujzZDe*ijM4S@Q^Enf(*VL}qtPuH1*(Pg!@DD3i-$$(nW(te)27Qs1j`){-Du z&Z&flwExa0bnY_bSJ`(oXW))S;9dYEi3k56vQh-teoSS*G2>=&)+VNPoY(k?jBqY3 zX46o$gFTPN3=S*iOTKr_A3?_GUnvY)`ij$VugSCX$D-s}I)OaE$rrq5p2)IVYTu`hkis)h(LB6 zZgZV|5yN*q`q2OsGM-*|WemJL!6R|G8QxDoN+%1<-AKLepynR){#ksWE_dWVPem#c zRPE=TERkyhGe1@2JtVBy+#tGFdd$CFcd_Y{oZ-Lc>PeeHl

MqS+d|CVN3_;opv+ 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 Qr))NA zQXQSv>w1oN)d)IJMj+*!5m8sg1oPEhPsV>(VQG`fN3Xc0;GEdJR!SSTl|57YXp!N&A4(pl# z|GL5I2C;WArGPkp22xSM8&E`hWKQE$fgjtzW|$ugnB*8n2SR^`0CrP}+}^k(m9BMr{o>os>Hh|sUFbB@;%6QhoeZ>NjuO9(?8pVaLB6l55g z`4{-k_P1$?H3$|2t~)snxb0cb@WvplZ)3@^>nI!A=5_1x+}@sCGS2x8plY|l#6f8~ zU--9<=5VNw1BodJ+$9L?NOCrnu=nv9w^-7iC{lV#7y6w#^iAgQ+csQ+$o2EstrH90Ym<>7XM`4^~)u5C%-u}Mh+Oog)N?0PSix0oT}x~I@gQ--`TfvU$WWz^~}fG089MlF&*9qPg(Wa+#rq6p|<0y zO%TjcxW^k)?Bh(!|~6&)~9{4fh

  • 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

    W(P@zw9( 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

    *X;=G6}FlG z7hw}WdP5CMWTjR)ejsa+Kf+xsNu3(P{kmvr`DVCT*a6B^MUJwkgSE^Gsd@x z(j96)Iq$qxDI%+!k8*+L9Cs@PgjcQ^{AXt#0(Sn!MFT1%+i_7!1%;ns0jNb}7iLQC z9ncNgc7th}V_Rks#NR;%oDz}tbf9mfXFBDi5lJD0AWf~Kh5E~1DIKD%H#3lJ%@J5Q z&y2-`cPkUWyz_5=NIW+D|3g;=lz^3L^2=gBA+C?LtXw8jRpQsZQt(5xZP-%%^)PpC zH$ub!uY`oCqfZ7;k6L(1sT-kA?eFD#`5Qczk%}SKt&yy$&;dZj&j+X$*e#aLFx-)B z8>T<%HX;1Q)nJ_u^h|$c>CSi16QwK~okG$G8GQFtwH$u@R=-2JvuhV=U+~Qh7ndJ~ z=TUoc?w&Xuxk{k$vbRSmLIO9rx>kxfzB=5|jx1dw^{O2kt>51hz9qwpjn3ZZMH&UefJ5B{=Iw+ zCt}5?J67jIy941%WJ^eLm?((2ceT<%Yw&Yy}&#|L#t*6&SNLO1Ev->QOU$v9Nmqo;|h z4`lNsVdA`%y5N7piml!hS|b4o73?hgJ~P3VA)dh<>{l*!{>4$t1Q!B-$ZS*C@Ya!# zwqk4%!^Hx9_1AdxSSDga&}0;sKE9?5zDT7!&&N_yx=g@k?|fs9x)|9Hy59b~AIb=v zE{ir-Q9O75dTp+qWT=Duss-U;sZ{@MRXdk>wWG7#$FB%jVS1z^B_2n|6J z#63Ltdj5TMp<|9O@H_^qn|OgM$Xu-T@^n}_+?fg21~f}{HX}1wVrN(;>mjWwPuf$$ zF)faCVyExOyL925R*vn7hb#f2fP(Qe(gR?j$R%R1CI>xyuPJwtPQ~)7;JSs&b<|w^+A~dx8 z#9k{_dsAT`vncaIU+X6P33Ea7#F8?b=W^h$0pCbj9Vn`=4J49R83zB5OWwI@dsij* zy{OK)^cV0ZeTg1^Nq&dnlrdw44$nIh$y-}7qeb1|hnX3G$GK#v2qD5TD3a`OM_JQ{ zCM;Lfo+<5=(t&c*fd6lL-p=M(PXFs>wvHR_}{zlz9|eLu+Cc|S4p9!^`}#_y*5Z5ql&!kf}&cxU2<)C&R80_ z-DpFb7k)UI_nyN_F3OPnIZA2WZ^F^gBrDg^=|-Oi)GB1aOZ=4-e(7cKC|8U3uF-Po zGAqG0sO`MOGtJm^qoPWSZ#9bg%4fd%nURIIfQ4c4RKvGzn%&vA`k^M0TSxE2v&cQ* zQ)Fj#9NP4k!j~%5{bLHgK{}Fti$7Gr`}0^H3fykD;vt+8DAnVoo4YMCD96CvGr2}x zqK`k{WIf)u8=5ZMOZ$!wwD#UQ3GdR�ADl2>i<@}m&^6K;Ty4i5jWg4nS2k= z+VM5?#dhE}{XY9yTFjq*ub2;mI$df)Zf_9UJK z%|*K}xa_#5tG9-Gwbo+2b)G+}E^CRz9yB;qc*B(O@DbCwK`XTDr@RizQ7n0oYu*-F z5I!|+@tt3bx2fm0cfIWhWPQ5g9^?1#8?No<`qSsge(zmk#6ugOiqPu7?)Zff@@9vW z1-hlfDpqf}&vv14$8V0cXZV+92A$imhpROy*^J(pvxWZU^J-IflG{<7*5 zu;2Fa$cCo))<0%#e9yXio3^R8K04{xy3GkREWp(|lBll0kN??A-X)wqv3ip#0|SF< ziEBhjN@7W>RdP`(kYX@0Ff!CNFw-?M4KXmaGBvU?HPJROure_CqrdPqiiX_$l+3hB z+!}g|B*TFk1VJ{WR%E7GIp^o(7X=q2CZ{qO^3P`S1WF3QC0&6k{L|8kQ%e}kElysk zRtL&T!es+8D^hbJTrzW0^NKU`^B4>bj7&Zq4U@-I6p5}-2q~iRvlJdl& zRECU_l7eC@ef`Yb#Pn1k&P>kM%P&gbb6$2XP^|<=ZAeCGZjzOiOMY@G(9RMos{o+V zVg|$I|99)dEQPA^1es!G<&m11o>2mJ*>klUd(jMnDhkg`DanAiKA&X?nnAum@0C;* zq%wfrrC*SjZoeta52#2S$wj^)KvfKeW=1Av1_s9FCK_SE(}7Avkdy>xR;4l+8(Nqf znL0VybaVOxWfgzx$Us{lpp9q}&GBwk;$ac=DtTNX(HZV2TFD@xc z&CSe@&rM9uFOE;jOfN1iiqA~W&x<#-ur#wU*8@6l(fyyhfMzpzy85}Sb4q9e02G%R AhyVZp diff --git a/StremioX/src/main/AndroidManifest.xml b/StremioX/src/main/AndroidManifest.xml deleted file mode 100644 index c98063f8..00000000 --- a/StremioX/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/StremioX/src/main/kotlin/com/hexated/StremioC.kt b/StremioX/src/main/kotlin/com/hexated/StremioC.kt deleted file mode 100644 index 9c766240..00000000 --- a/StremioX/src/main/kotlin/com/hexated/StremioC.kt +++ /dev/null @@ -1,361 +0,0 @@ -package com.hexated - -import com.fasterxml.jackson.annotation.JsonProperty -import com.hexated.SubsExtractors.invokeOpenSubs -import com.hexated.SubsExtractors.invokeWatchsomuch -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.LoadResponse.Companion.addActors -import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer -import com.lagradost.cloudstream3.utils.* -import com.lagradost.cloudstream3.utils.AppUtils.parseJson -import com.lagradost.cloudstream3.utils.AppUtils.toJson -import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson -import java.net.URI - -private const val TRACKER_LIST_URL = - "https://raw.githubusercontent.com/ngosang/trackerslist/master/trackers_best.txt" - -class StremioC : MainAPI() { - override var mainUrl = "https://stremio.github.io/stremio-static-addon-example" - override var name = "StremioC" - override val supportedTypes = setOf(TvType.Others) - override val hasMainPage = true - private val cinemataUrl = "https://v3-cinemeta.strem.io" - - override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse? { - mainUrl = mainUrl.fixSourceUrl() - val res = tryParseJson(request("${mainUrl}/manifest.json").body.string()) ?: return null - val lists = mutableListOf() - res.catalogs.apmap { catalog -> - catalog.toHomePageList(this)?.let { - if (it.list.isNotEmpty()) lists.add(it) - } - } - return HomePageResponse( - lists, - false - ) - } - - override suspend fun search(query: String): List? { - mainUrl = mainUrl.fixSourceUrl() - val res = tryParseJson(request("${mainUrl}/manifest.json").body.string()) ?: return null - val list = mutableListOf() - res.catalogs.apmap { catalog -> - list.addAll(catalog.search(query, this)) - } - return list.distinct() - } - - override suspend fun load(url: String): LoadResponse? { - val res = parseJson(url) - mainUrl = - if ((res.type == "movie" || res.type == "series") && isImdborTmdb(res.id)) cinemataUrl else mainUrl - val json = app.get("${mainUrl}/meta/${res.type}/${res.id}.json") - .parsedSafe()?.meta ?: throw RuntimeException(url) - return json.toLoadResponse(this, res.id) - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - val loadData = parseJson(data) - val request = request("${mainUrl}/stream/${loadData.type}/${loadData.id}.json") - if (request.code.isSuccessful()) { - val res = tryParseJson(request.body.string()) ?: return false - res.streams.forEach { stream -> - stream.runCallback(subtitleCallback, callback) - } - } else { - argamap( - { - invokeStremioX(loadData.type, loadData.id, subtitleCallback, callback) - }, - { - invokeWatchsomuch( - loadData.imdbId, - loadData.season, - loadData.episode, - subtitleCallback - ) - }, - { - invokeOpenSubs( - loadData.imdbId, - loadData.season, - loadData.episode, - subtitleCallback - ) - }, - ) - } - - return true - } - - private suspend fun invokeStremioX( - type: String?, - id: String?, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ) { - val sites = - AcraApplication.getKey>(USER_PROVIDER_API)?.toMutableList() - ?: mutableListOf() - sites.filter { it.parentJavaClass == "StremioX" }.apmap { site -> - val request = request("${site.url.fixSourceUrl()}/stream/${type}/${id}.json").body.string() - val res = - tryParseJson(request) - ?: return@apmap - res.streams.forEach { stream -> - stream.runCallback(subtitleCallback, callback) - } - } - } - - data class LoadData( - val type: String? = null, - val id: String? = null, - val season: Int? = null, - val episode: Int? = null, - val imdbId: String? = null, - ) - - data class CustomSite( - @JsonProperty("parentJavaClass") val parentJavaClass: String, - @JsonProperty("name") val name: String, - @JsonProperty("url") val url: String, - @JsonProperty("lang") val lang: String, - ) - - // check if id is imdb/tmdb cause stremio addons like torrentio works base on imdbId - private fun isImdborTmdb(url: String?): Boolean { - return imdbUrlToIdNullable(url) != null || url?.startsWith("tmdb:") == true - } - - private data class Manifest(val catalogs: List) - private data class Catalog( - var name: String?, - val id: String, - val type: String?, - val types: MutableList = mutableListOf() - ) { - init { - if (type != null) types.add(type) - } - - suspend fun search(query: String, provider: StremioC): List { - val entries = mutableListOf() - types.forEach { type -> - val json = request("${provider.mainUrl}/catalog/${type}/${id}/search=${query}.json").body.string() - val res = - tryParseJson(json) - ?: return@forEach - res.metas?.forEach { entry -> - entries.add(entry.toSearchResponse(provider)) - } - } - return entries - } - - suspend fun toHomePageList(provider: StremioC): HomePageList? { - val entries = mutableListOf() - types.forEach { type -> - val json = request("${provider.mainUrl}/catalog/${type}/${id}.json").body.string() - val res = - tryParseJson(json) - ?: return@forEach - res.metas?.forEach { entry -> - entries.add(entry.toSearchResponse(provider)) - } - } - return HomePageList( - "$type - ${name ?: id}", - entries - ) - } - } - - private data class CatalogResponse(val metas: List?, val meta: CatalogEntry?) - - private data class Trailer( - val source: String?, - val type: String? - ) - private data class CatalogEntry( - @JsonProperty("name") val name: String, - @JsonProperty("id") val id: String, - @JsonProperty("poster") val poster: String?, - @JsonProperty("background") val background: String?, - @JsonProperty("description") val description: String?, - @JsonProperty("imdbRating") val imdbRating: String?, - @JsonProperty("type") val type: String?, - @JsonProperty("videos") val videos: List