diff --git a/README.md b/README.md index 4b0efb75..1d0ed0b8 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ This repository contains the available extension catalogues for the Mangayomi ap ## How to add the extensions -Click on one of the buttons below to add the corresponding repository/repositories: +Click on one of the buttons below to add the corresponding repository/repositories (Manga & novels only): Add all repositories diff --git a/dart/anime/anime_source_list.dart b/dart/anime/anime_source_list.dart deleted file mode 100644 index 7ab0bc58..00000000 --- a/dart/anime/anime_source_list.dart +++ /dev/null @@ -1,60 +0,0 @@ -import '../../model/source.dart'; -import 'multisrc/datalifeengine/sources.dart'; -import 'multisrc/dopeflix/sources.dart'; -import 'multisrc/zorotheme/sources.dart'; -import 'src/all/animeworldindia/sources.dart'; -import 'src/all/nyaa/source.dart'; -import 'src/ar/okanime/source.dart'; -import 'src/de/animetoast/source.dart'; -import 'src/en/animepahe/source.dart'; -import 'src/en/gogoanime/source.dart'; -import 'src/en/kisskh/source.dart'; -import 'src/en/nineanimetv/source.dart'; -import 'src/en/vumeto/source.dart'; -import 'src/es/animeonlineninja/source.dart'; -import 'src/fr/animesama/source.dart'; -import 'src/fr/anizone/source.dart'; -import 'src/hi/yomovies/source.dart'; -import 'src/en/uhdmovies/source.dart'; -import 'src/fr/animesultra/source.dart'; -import 'src/fr/franime/source.dart'; -import 'src/fr/otakufr/source.dart'; -import 'src/id/nimegami/source.dart'; -import 'src/id/oploverz/source.dart'; -import 'src/id/otakudesu/source.dart'; -import 'src/it/animesaturn/source.dart'; -import 'src/pt/animesvision/source.dart'; -import 'src/sq/filma24/source.dart'; -import 'src/tr/diziwatch/source.dart'; -import 'src/en/donghuastream/source.dart'; - -List dartAnimesourceList = [ - gogoanimeSource, - franimeSource, - otakufr, - animesultraSource, - ...zorothemeSourcesList, - okanimeSource, - otakudesu, - nimegami, - oploverz, - ...dopeflixSourcesList, - animesaturn, - uhdmoviesSource, - ...datalifeengineSourcesList, - filma24, - yomoviesSource, - animesamaSource, - nineanimetv, - ...animeworldindiaSourcesList, - nyaaSource, - animepaheSource, - animetoast, - animesvision, - diziwatchSource, - aniZoneSource, - animeonlineninjaSource, - kisskhSource, - vumetoSource, - donghuastreamSource, -]; diff --git a/dart/anime/multisrc/datalifeengine/datalifeengine.dart b/dart/anime/multisrc/datalifeengine/datalifeengine.dart deleted file mode 100644 index 0989ed20..00000000 --- a/dart/anime/multisrc/datalifeengine/datalifeengine.dart +++ /dev/null @@ -1,457 +0,0 @@ -import 'dart:convert'; -import 'package:mangayomi/bridge_lib.dart'; - -class DataLifeEngine extends MProvider { - DataLifeEngine({required this.source}); - - MSource source; - - final Client client = Client(); - - @override - bool get supportsLatest => false; - - @override - String get baseUrl => getPreferenceValue(source.id, "overrideBaseUrl"); - - @override - Future getPopular(int page) async { - final res = - (await client.get( - Uri.parse("$baseUrl${getPath(source)}page/$page"), - )).body; - return animeFromElement(res); - } - - @override - Future getLatestUpdates(int page) async { - return MPages([], false); - } - - @override - Future search(String query, int page, FilterList filterList) async { - final filters = filterList.filters; - String res = ""; - if (query.isNotEmpty) { - if (query.length < 4) - throw "La recherche est suspendue! La chaîne de recherche est vide ou contient moins de 4 caractères."; - final headers = { - "Host": Uri.parse(baseUrl).host, - "Origin": baseUrl, - "Referer": "$baseUrl/", - }; - final cleanQuery = query.replaceAll(" ", "+"); - if (page == 1) { - res = - (await client.post( - Uri.parse( - "$baseUrl?do=search&subaction=search&story=$cleanQuery", - ), - headers: headers, - )).body; - } else { - res = - (await client.post( - Uri.parse( - "$baseUrl?do=search&subaction=search&search_start=$page&full_search=0&result_from=11&story=$cleanQuery", - ), - headers: headers, - )).body; - } - } else { - String url = ""; - for (var filter in filters) { - if (filter.type == "CategoriesFilter") { - if (filter.state != 0) { - url = "$baseUrl${filter.values[filter.state].value}page/$page/"; - } - } else if (filter.type == "GenresFilter") { - if (filter.state != 0) { - url = "$baseUrl${filter.values[filter.state].value}page/$page/"; - } - } - } - res = (await client.get(Uri.parse(url))).body; - } - - return animeFromElement(res); - } - - @override - Future getDetail(String url) async { - String res = - (await client.get( - Uri.parse("$baseUrl${getUrlWithoutDomain(url)}"), - )).body; - MManga anime = MManga(); - final description = xpath(res, '//span[@itemprop="description"]/text()'); - anime.description = description.isNotEmpty ? description.first : ""; - anime.genre = xpath(res, '//span[@itemprop="genre"]/a/text()'); - - List? episodesList = []; - - if (source.name == "French Anime") { - final epsData = xpath(res, '//div[@class="eps"]/text()'); - for (var epData in epsData.first.split('\n')) { - final data = epData.split('!'); - MChapter ep = MChapter(); - ep.name = "Episode ${data.first}"; - ep.url = data.last; - episodesList.add(ep); - } - } else { - final doc = parseHtml(res); - final elements = - doc - .select(".hostsblock div:has(a)") - .where( - (MElement e) => e.outerHtml.contains("loadVideo('https://"), - ) - .toList(); - if (elements.isNotEmpty) { - for (var element in elements) { - element = element as MElement; - MChapter ep = MChapter(); - ep.name = element.className - .replaceAll("ep", "Episode ") - .replaceAll("vs", " VOSTFR") - .replaceAll("vf", " VF"); - ep.url = element - .select("a") - .map( - (MElement e) => substringBefore( - substringAfter(e.attr('onclick'), "loadVideo('"), - "')", - ), - ) - .toList() - .join(",") - .replaceAll("/vd.php?u=", ""); - ep.scanlator = element.className.contains('vf') ? 'VF' : 'VOSTFR'; - episodesList.add(ep); - } - } else { - MChapter ep = MChapter(); - ep.name = "Film"; - ep.url = doc - .select("a") - .where((MElement e) => e.outerHtml.contains("loadVideo('https://")) - .map( - (MElement e) => substringBefore( - substringAfter(e.attr('onclick'), "loadVideo('"), - "')", - ), - ) - .toList() - .join(",") - .replaceAll("/vd.php?u=", ""); - episodesList.add(ep); - } - } - - anime.chapters = episodesList.reversed.toList(); - return anime; - } - - @override - Future> getVideoList(String url) async { - List videos = []; - final sUrls = url.split(','); - for (var sUrl in sUrls) { - List a = []; - if (sUrl.contains("dood") || sUrl.contains("d000")) { - a = await doodExtractor(sUrl, "DoodStream"); - } else if ([ - "streamhide", - "guccihide", - "streamvid", - "dhtpre", - ].any((a) => sUrl.contains(a))) { - a = await streamHideExtractor(sUrl); - } else if (sUrl.contains("uqload")) { - a = await uqloadExtractor(sUrl); - } else if (sUrl.contains("upstream")) { - a = await upstreamExtractor(sUrl); - } else if (sUrl.contains("sibnet")) { - a = await sibnetExtractor(sUrl); - } else if (sUrl.contains("ok.ru")) { - a = await okruExtractor(sUrl); - } else if (sUrl.contains("vidmoly")) { - a = await vidmolyExtractor(sUrl); - } else if (sUrl.contains("streamtape")) { - a = await streamTapeExtractor(sUrl, ""); - } else if (sUrl.contains("voe.sx")) { - a = await voeExtractor(sUrl, ""); - } - videos.addAll(a); - } - return videos; - } - - MPages animeFromElement(String res) { - final htmls = parseHtml(res).select("div#dle-content > div.mov"); - List animeList = []; - for (var h in htmls) { - final html = h.innerHtml; - final url = xpath(html, '//a/@href').first; - final name = xpath(html, '//a/text()').first; - final image = xpath(html, '//div[contains(@class,"mov")]/img/@src').first; - final season = xpath(html, '//div/span[@class="block-sai"]/text()'); - MManga anime = MManga(); - anime.name = - "$name ${season.isNotEmpty ? season.first.replaceAll("\n", " ") : ""}"; - anime.imageUrl = "$baseUrl$image"; - anime.link = url; - animeList.add(anime); - } - final hasNextPage = xpath(res, '//span[@class="pnext"]/a/@href').isNotEmpty; - return MPages(animeList, hasNextPage); - } - - Future> streamHideExtractor(String url) async { - final res = (await client.get(Uri.parse(url))).body; - final masterUrl = substringBefore( - substringAfter( - substringAfter(substringAfter(unpackJs(res), "sources:"), "file:\""), - "src:\"", - ), - '"', - ); - final masterPlaylistRes = (await client.get(Uri.parse(masterUrl))).body; - List videos = []; - for (var it in substringAfter( - masterPlaylistRes, - "#EXT-X-STREAM-INF:", - ).split("#EXT-X-STREAM-INF:")) { - final quality = - "${substringBefore(substringBefore(substringAfter(substringAfter(it, "RESOLUTION="), "x"), ","), "\n")}p"; - - String videoUrl = substringBefore(substringAfter(it, "\n"), "\n"); - - if (!videoUrl.startsWith("http")) { - videoUrl = - "${masterUrl.split("/").sublist(0, masterUrl.split("/").length - 1).join("/")}/$videoUrl"; - } - - MVideo video = MVideo(); - video - ..url = videoUrl - ..originalUrl = videoUrl - ..quality = "StreamHideVid - $quality"; - videos.add(video); - } - return videos; - } - - Future> upstreamExtractor(String url) async { - final res = (await client.get(Uri.parse(url))).body; - final js = xpath(res, '//script[contains(text(), "m3u8")]/text()'); - if (js.isEmpty) { - return []; - } - final masterUrl = substringBefore( - substringAfter(unpackJs(js.first), "{file:\""), - "\"}", - ); - final masterPlaylistRes = (await client.get(Uri.parse(masterUrl))).body; - List videos = []; - for (var it in substringAfter( - masterPlaylistRes, - "#EXT-X-STREAM-INF:", - ).split("#EXT-X-STREAM-INF:")) { - final quality = - "${substringBefore(substringBefore(substringAfter(substringAfter(it, "RESOLUTION="), "x"), ","), "\n")}p"; - - String videoUrl = substringBefore(substringAfter(it, "\n"), "\n"); - - if (!videoUrl.startsWith("http")) { - videoUrl = - "${masterUrl.split("/").sublist(0, masterUrl.split("/").length - 1).join("/")}/$videoUrl"; - } - - MVideo video = MVideo(); - video - ..url = videoUrl - ..originalUrl = videoUrl - ..quality = "Upstream - $quality"; - videos.add(video); - } - return videos; - } - - Future> uqloadExtractor(String url) async { - final res = (await client.get(Uri.parse(url))).body; - final js = xpath(res, '//script[contains(text(), "sources:")]/text()'); - if (js.isEmpty) { - return []; - } - - final videoUrl = substringBefore( - substringAfter(js.first, "sources: [\""), - '"', - ); - MVideo video = MVideo(); - video - ..url = videoUrl - ..originalUrl = videoUrl - ..quality = "Uqload" - ..headers = {"Referer": "${Uri.parse(url).origin}/"}; - return [video]; - } - - Future> vidmolyExtractor(String url) async { - final headers = {'Referer': 'https://vidmoly.to'}; - List videos = []; - final playListUrlResponse = (await client.get(Uri.parse(url))).body; - final playlistUrl = - RegExp(r'file:"(\S+?)"').firstMatch(playListUrlResponse)?.group(1) ?? - ""; - if (playlistUrl.isEmpty) return []; - final masterPlaylistRes = await client.get( - Uri.parse(playlistUrl), - headers: headers, - ); - - if (masterPlaylistRes.statusCode == 200) { - for (var it in substringAfter( - masterPlaylistRes.body, - "#EXT-X-STREAM-INF:", - ).split("#EXT-X-STREAM-INF:")) { - final quality = - "${substringBefore(substringBefore(substringAfter(substringAfter(it, "RESOLUTION="), "x"), ","), "\n")}p"; - - String videoUrl = substringBefore(substringAfter(it, "\n"), "\n"); - - MVideo video = MVideo(); - video - ..url = videoUrl - ..originalUrl = videoUrl - ..quality = "Vidmoly $quality" - ..headers = headers; - videos.add(video); - } - } - - return videos; - } - - String getPath() { - if (source.name == "French Anime") return "/animes-vostfr/"; - return "/serie-en-streaming/"; - } - - @override - List getSourcePreferences() { - return [ - if (source.name == "Wiflix") - EditTextPreference( - key: "overrideBaseUrl", - title: "Changer l'url de base", - summary: "", - value: "https://wiflix-hd.vip", - dialogTitle: "Changer l'url de base", - dialogMessage: "", - text: "https://wiflix-hd.vip", - ), - if (source.name == "French Anime") - EditTextPreference( - key: "overrideBaseUrl", - title: "Changer l'url de base", - summary: "", - value: "https://french-anime.com", - dialogTitle: "Changer l'url de base", - dialogMessage: "", - text: "https://french-anime.com", - ), - ]; - } - - @override - List getFilterList() { - return [ - HeaderFilter("La recherche de texte ignore les filtres"), - if (source.name == "French Anime") - SelectFilter("CategoriesFilter", "Catégories", 0, [ - SelectFilterOption("", ""), - SelectFilterOption("Action", "/genre/action/"), - SelectFilterOption("Aventure", "/genre/aventure/"), - SelectFilterOption("Arts martiaux", "/genre/arts-martiaux/"), - SelectFilterOption("Combat", "/genre/combat/"), - SelectFilterOption("Comédie", "/genre/comedie/"), - SelectFilterOption("Drame", "/genre/drame/"), - SelectFilterOption("Epouvante", "/genre/epouvante/"), - SelectFilterOption("Fantastique", "/genre/fantastique/"), - SelectFilterOption("Fantasy", "/genre/fantasy/"), - SelectFilterOption("Mystère", "/genre/mystere/"), - SelectFilterOption("Romance", "/genre/romance/"), - SelectFilterOption("Shonen", "/genre/shonen/"), - SelectFilterOption("Surnaturel", "/genre/surnaturel/"), - SelectFilterOption("Sci-Fi", "/genre/sci-fi/"), - SelectFilterOption("School life", "/genre/school-life/"), - SelectFilterOption("Ninja", "/genre/ninja/"), - SelectFilterOption("Seinen", "/genre/seinen/"), - SelectFilterOption("Horreur", "/genre/horreur/"), - SelectFilterOption("Tranche de vie", "/genre/tranchedevie/"), - SelectFilterOption("Psychologique", "/genre/psychologique/"), - ]), - if (source.name == "French Anime") - SelectFilter("GenresFilter", "Genres", 0, [ - SelectFilterOption("", ""), - SelectFilterOption("Animes VF", "/animes-vf/"), - SelectFilterOption("Animes VOSTFR", "/animes-vostfr/"), - SelectFilterOption("Films VF et VOSTFR", "/films-vf-vostfr/"), - ]), - if (source.name == "Wiflix") - SelectFilter("CategoriesFilter", "Catégories", 0, [ - SelectFilterOption("", ""), - SelectFilterOption("Séries", "/serie-en-streaming/"), - SelectFilterOption("Films", "/film-en-streaming/"), - ]), - if (source.name == "Wiflix") - SelectFilter("GenresFilter", "Genres", 0, [ - SelectFilterOption("", ""), - SelectFilterOption("Action", "/film-en-streaming/action/"), - SelectFilterOption("Animation", "/film-en-streaming/animation/"), - SelectFilterOption( - "Arts Martiaux", - "/film-en-streaming/arts-martiaux/", - ), - SelectFilterOption("Aventure", "/film-en-streaming/aventure/"), - SelectFilterOption("Biopic", "/film-en-streaming/biopic/"), - SelectFilterOption("Comédie", "/film-en-streaming/comedie/"), - SelectFilterOption( - "Comédie Dramatique", - "/film-en-streaming/comedie-dramatique/", - ), - SelectFilterOption( - "Épouvante Horreur", - "/film-en-streaming/horreur/", - ), - SelectFilterOption("Drame", "/film-en-streaming/drame/"), - SelectFilterOption( - "Documentaire", - "/film-en-streaming/documentaire/", - ), - SelectFilterOption("Espionnage", "/film-en-streaming/espionnage/"), - SelectFilterOption("Famille", "/film-en-streaming/famille/"), - SelectFilterOption("Fantastique", "/film-en-streaming/fantastique/"), - SelectFilterOption("Guerre", "/film-en-streaming/guerre/"), - SelectFilterOption("Historique", "/film-en-streaming/historique/"), - SelectFilterOption("Musical", "/film-en-streaming/musical/"), - SelectFilterOption("Policier", "/film-en-streaming/policier/"), - SelectFilterOption("Romance", "/film-en-streaming/romance/"), - SelectFilterOption( - "Science-Fiction", - "/film-en-streaming/science-fiction/", - ), - SelectFilterOption("Spectacles", "/film-en-streaming/spectacles/"), - SelectFilterOption("Thriller", "/film-en-streaming/thriller/"), - SelectFilterOption("Western", "/film-en-streaming/western/"), - ]), - ]; - } -} - -DataLifeEngine main(MSource source) { - return DataLifeEngine(source: source); -} diff --git a/dart/anime/multisrc/datalifeengine/sources.dart b/dart/anime/multisrc/datalifeengine/sources.dart deleted file mode 100644 index 9d2403ed..00000000 --- a/dart/anime/multisrc/datalifeengine/sources.dart +++ /dev/null @@ -1,23 +0,0 @@ -import '../../../../model/source.dart'; -import 'src/frenchanime/frenchanime.dart'; -import 'src/wiflix/wiflix.dart'; - -const _datalifeengineVersion = "0.0.65"; -const _datalifeengineSourceCodeUrl = - "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/multisrc/datalifeengine/datalifeengine.dart"; - -List get datalifeengineSourcesList => _datalifeengineSourcesList; -List _datalifeengineSourcesList = - [ - //French Anime (FR) - frenchanimeSource, - //Wiflix (FR) - wiflixSource, - ] - .map( - (e) => - e - ..sourceCodeUrl = _datalifeengineSourceCodeUrl - ..version = _datalifeengineVersion, - ) - .toList(); diff --git a/dart/anime/multisrc/datalifeengine/src/frenchanime/frenchanime.dart b/dart/anime/multisrc/datalifeengine/src/frenchanime/frenchanime.dart deleted file mode 100644 index 719dcece..00000000 --- a/dart/anime/multisrc/datalifeengine/src/frenchanime/frenchanime.dart +++ /dev/null @@ -1,13 +0,0 @@ -import '../../../../../../model/source.dart'; - -Source get frenchanimeSource => _frenchanimeSource; - -Source _frenchanimeSource = Source( - name: "French Anime", - baseUrl: "https://french-anime.com", - lang: "fr", - typeSource: "datalifeengine", - itemType: ItemType.anime, - iconUrl: - "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/multisrc/datalifeengine/src/frenchanime/icon.png", -); diff --git a/dart/anime/multisrc/datalifeengine/src/frenchanime/icon.png b/dart/anime/multisrc/datalifeengine/src/frenchanime/icon.png deleted file mode 100644 index 62591a8e..00000000 Binary files a/dart/anime/multisrc/datalifeengine/src/frenchanime/icon.png and /dev/null differ diff --git a/dart/anime/multisrc/datalifeengine/src/wiflix/icon.png b/dart/anime/multisrc/datalifeengine/src/wiflix/icon.png deleted file mode 100644 index 3317e744..00000000 Binary files a/dart/anime/multisrc/datalifeengine/src/wiflix/icon.png and /dev/null differ diff --git a/dart/anime/multisrc/datalifeengine/src/wiflix/wiflix.dart b/dart/anime/multisrc/datalifeengine/src/wiflix/wiflix.dart deleted file mode 100644 index c4682ee4..00000000 --- a/dart/anime/multisrc/datalifeengine/src/wiflix/wiflix.dart +++ /dev/null @@ -1,13 +0,0 @@ -import '../../../../../../model/source.dart'; - -Source get wiflixSource => _wiflixSource; - -Source _wiflixSource = Source( - name: "Wiflix", - baseUrl: "https://wiflix-hd.vip", - lang: "fr", - typeSource: "datalifeengine", - itemType: ItemType.anime, - iconUrl: - "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/multisrc/datalifeengine/src/wiflix/icon.png", -); diff --git a/dart/anime/multisrc/dopeflix/dopeflix.dart b/dart/anime/multisrc/dopeflix/dopeflix.dart deleted file mode 100644 index 112259d8..00000000 --- a/dart/anime/multisrc/dopeflix/dopeflix.dart +++ /dev/null @@ -1,577 +0,0 @@ -import 'package:mangayomi/bridge_lib.dart'; -import 'dart:convert'; - -class DopeFlix extends MProvider { - DopeFlix({required this.source}); - - MSource source; - - final Client client = Client(); - - @override - String get baseUrl => getPreferenceValue(source.id, "preferred_domain"); - - @override - Future getPopular(int page) async { - final res = - (await client.get( - Uri.parse( - "$baseUrl/${getPreferenceValue(source.id, "preferred_popular_page")}?page=$page", - ), - )).body; - return parseAnimeList(res); - } - - @override - Future getLatestUpdates(int page) async { - final res = (await client.get(Uri.parse("$baseUrl/home"))).body; - List animeList = []; - final path = - '//section[contains(text(),"${getPreferenceValue(source.id, "preferred_latest_page")}")]/div/div[@class="film_list-wrap"]/div[@class="flw-item"]/div[@class="film-poster"]'; - final urls = xpath(res, '$path/a/@href'); - final names = xpath(res, '$path/a/@title'); - final images = xpath(res, '$path/img/@data-src'); - - for (var i = 0; i < names.length; i++) { - MManga anime = MManga(); - anime.name = names[i]; - anime.imageUrl = images[i]; - anime.link = urls[i]; - animeList.add(anime); - } - return MPages(animeList, false); - } - - @override - Future search(String query, int page, FilterList filterList) async { - final filters = filterList.filters; - String url = "$baseUrl"; - - if (query.isNotEmpty) { - url += "/search/${query.replaceAll(" ", "-")}?page=$page"; - } else { - url += "/filter/?page=$page"; - for (var filter in filters) { - if (filter.type == "TypeFilter") { - final type = filter.values[filter.state].value; - url += "${ll(url)}type=$type"; - } else if (filter.type == "QualityFilter") { - final quality = filter.values[filter.state].value; - url += "${ll(url)}quality=$quality"; - } else if (filter.type == "ReleaseYearFilter") { - final year = filter.values[filter.state].value; - url += "${ll(url)}release_year=$year"; - } else if (filter.type == "GenresFilter") { - final genre = (filter.state as List).where((e) => e.state).toList(); - if (genre.isNotEmpty) { - url += "${ll(url)}genre="; - for (var st in genre) { - url += "${st.value}-"; - } - } - } else if (filter.type == "CountriesFilter") { - final country = (filter.state as List).where((e) => e.state).toList(); - if (country.isNotEmpty) { - url += "${ll(url)}country="; - for (var st in country) { - url += "${st.value}-"; - } - } - } - } - } - - final res = (await client.get(Uri.parse(url))).body; - return parseAnimeList(res); - } - - @override - Future getDetail(String url) async { - url = getUrlWithoutDomain(url); - final res = (await client.get(Uri.parse("$baseUrl$url"))).body; - MManga anime = MManga(); - final description = xpath(res, '//div[@class="description"]/text()'); - if (description.isNotEmpty) { - anime.description = description.first.replaceAll("Overview:", ""); - } - final author = xpath(res, '//div[contains(text(),"Production")]/a/text()'); - if (author.isNotEmpty) { - anime.author = author.first; - } - anime.genre = xpath(res, '//div[contains(text(),"Genre")]/a/text()'); - List episodesList = []; - final id = xpath(res, '//div[@class="detail_page-watch"]/@data-id').first; - final dataType = - xpath(res, '//div[@class="detail_page-watch"]/@data-type').first; - if (dataType == "1") { - MChapter episode = MChapter(); - episode.name = "Movie"; - episode.url = "$baseUrl/ajax/movie/episodes/$id"; - episodesList.add(episode); - } else { - final resS = - (await client.get(Uri.parse("$baseUrl/ajax/v2/tv/seasons/$id"))).body; - - final seasonIds = xpath( - resS, - '//a[@class="dropdown-item ss-item"]/@data-id', - ); - final seasonNames = xpath( - resS, - '//a[@class="dropdown-item ss-item"]/text()', - ); - for (int i = 0; i < seasonIds.length; i++) { - final seasonId = seasonIds[i]; - final seasonName = seasonNames[i]; - - final html = - (await client.get( - Uri.parse("$baseUrl/ajax/v2/season/episodes/$seasonId"), - )).body; - - final epsHtmls = parseHtml(html).select("div.eps-item"); - - for (var epH in epsHtmls) { - final epHtml = epH.outerHtml; - final episodeId = - xpath( - epHtml, - '//div[contains(@class,"eps-item")]/@data-id', - ).first; - final epNum = - xpath(epHtml, '//div[@class="episode-number"]/text()').first; - final epName = xpath(epHtml, '//h3[@class="film-name"]/text()').first; - MChapter episode = MChapter(); - episode.name = "$seasonName $epNum $epName"; - episode.url = "$baseUrl/ajax/v2/episode/servers/$episodeId"; - episodesList.add(episode); - } - } - } - anime.chapters = episodesList.reversed.toList(); - return anime; - } - - @override - Future> getVideoList(String url) async { - url = getUrlWithoutDomain(url); - final res = (await client.get(Uri.parse("$baseUrl/$url"))).body; - - final vidsHtmls = parseHtml(res).select("ul.fss-list a.btn-play"); - - List videos = []; - for (var vidH in vidsHtmls) { - final vidHtml = vidH.outerHtml; - final id = xpath(vidHtml, '//a/@data-id').first; - final name = xpath(vidHtml, '//span/text()').first; - final resSource = - (await client.get(Uri.parse("$baseUrl/ajax/sources/$id"))).body; - - final vidUrl = substringBefore( - substringAfter(resSource, "\"link\":\""), - "\"", - ); - List a = []; - String masterUrl = ""; - String type = ""; - if (name.contains("DoodStream")) { - a = await doodExtractor(vidUrl, "DoodStream"); - } else if (["Vidcloud", "UpCloud"].contains(name)) { - final id = substringBefore(substringAfter(vidUrl, "/embed-4/"), "?"); - final serverUrl = substringBefore(vidUrl, "/embed"); - - final resServer = - (await client.get( - Uri.parse("$serverUrl/ajax/embed-4/getSources?id=$id"), - headers: {"X-Requested-With": "XMLHttpRequest"}, - )).body; - final encrypted = getMapValue(resServer, "encrypted"); - - String videoResJson = ""; - if (encrypted == "true") { - final ciphered = getMapValue(resServer, "sources"); - - List> indexPairs = await generateIndexPairs(); - - var password = ''; - String ciphertext = ciphered; - int index = 0; - for (List item in json.decode(json.encode(indexPairs))) { - int start = item.first + index; - int end = start + item.last; - String passSubstr = ciphered.substring(start, end); - password += passSubstr; - ciphertext = ciphertext.replaceFirst(passSubstr, ""); - index += item.last; - } - videoResJson = decryptAESCryptoJS(ciphertext, password); - masterUrl = - ((json.decode(videoResJson) as List>) - .first)['file']; - - type = - ((json.decode(videoResJson) as List>) - .first)['type']; - } else { - masterUrl = - ((json.decode(resServer)["sources"] as List>) - .first)['file']; - - type = - ((json.decode(resServer)["sources"] as List>) - .first)['type']; - } - - final tracks = - (json.decode(resServer)['tracks'] as List) - .where((e) => e['kind'] == 'captions' ? true : false) - .toList(); - List subtitles = []; - - for (var sub in tracks) { - try { - MTrack subtitle = MTrack(); - subtitle - ..label = sub["label"] - ..file = sub["file"]; - subtitles.add(subtitle); - } catch (_) {} - } - - subtitles = sortSubs(subtitles, source.id); - if (type == "hls") { - final masterPlaylistRes = - (await client.get(Uri.parse(masterUrl))).body; - - for (var it in substringAfter( - masterPlaylistRes, - "#EXT-X-STREAM-INF:", - ).split("#EXT-X-STREAM-INF:")) { - final quality = - "${substringBefore(substringBefore(substringAfter(substringAfter(it, "RESOLUTION="), "x"), ","), "\n")}p"; - - String videoUrl = substringBefore(substringAfter(it, "\n"), "\n"); - - if (!videoUrl.startsWith("http")) { - videoUrl = - "${(masterUrl as String).split("/").sublist(0, (masterUrl as String).split("/").length - 1).join("/")}/$videoUrl"; - } - - MVideo video = MVideo(); - video - ..url = videoUrl - ..originalUrl = videoUrl - ..quality = "$name - $quality" - ..subtitles = subtitles; - a.add(video); - } - } else { - MVideo video = MVideo(); - video - ..url = masterUrl - ..originalUrl = masterUrl - ..quality = "$name - Default" - ..subtitles = subtitles; - a.add(video); - } - } - videos.addAll(a); - } - - return sortVideos(videos, source.id); - } - - Future>> generateIndexPairs() async { - final res = - (await client.get( - Uri.parse("https://rabbitstream.net/js/player/prod/e4-player.min.js"), - )).body; - - String script = substringBefore(substringAfter(res, "const "), "()"); - script = script.substring(0, script.lastIndexOf(',')); - final list = - script - .split(",") - .map((String e) { - String value = substringAfter(e, "="); - if (value.contains("0x")) { - return int.parse(substringAfter(value, "0x"), radix: 16); - } else { - return int.parse(value); - } - }) - .toList() - .skip(1) - .toList(); - return chunked( - list, - 2, - ).map((List list) => list.reversed.toList()).toList(); - } - - List> chunked(List list, int size) { - List> chunks = []; - for (int i = 0; i < list.length; i += size) { - int end = list.length; - if (i + size < list.length) { - end = i + size; - } - chunks.add(list.sublist(i, end)); - } - return chunks; - } - - MPages parseAnimeList(String res) { - List animeList = []; - final path = - '//div[@class="film_list-wrap"]/div[@class="flw-item"]/div[@class="film-poster"]'; - final urls = xpath(res, '$path/a/@href'); - final names = xpath(res, '$path/a/@title'); - final images = xpath(res, '$path/img/@data-src'); - - for (var i = 0; i < names.length; i++) { - MManga anime = MManga(); - anime.name = names[i]; - anime.imageUrl = images[i]; - anime.link = urls[i]; - animeList.add(anime); - } - final pages = xpath( - res, - '//ul[contains(@class,"pagination")]/li/a[@title="Next"]/@title', - ); - return MPages(animeList, pages.isNotEmpty); - } - - @override - List getFilterList() { - return [ - SelectFilter("TypeFilter", "Type", 0, [ - SelectFilterOption("All", "all"), - SelectFilterOption("Movies", "movies"), - SelectFilterOption("TV Shows", "tv"), - ]), - SelectFilter("QualityFilter", "Quality", 0, [ - SelectFilterOption("All", "all"), - SelectFilterOption("HD", "HD"), - SelectFilterOption("SD", "SD"), - SelectFilterOption("CAM", "CAM"), - ]), - SelectFilter("ReleaseYearFilter", "Released at", 0, [ - SelectFilterOption("All", "all"), - SelectFilterOption("2024", "2024"), - SelectFilterOption("2023", "2023"), - SelectFilterOption("2022", "2022"), - SelectFilterOption("2021", "2021"), - SelectFilterOption("2020", "2020"), - SelectFilterOption("2019", "2019"), - SelectFilterOption("2018", "2018"), - SelectFilterOption("Older", "older-2018"), - ]), - SeparatorFilter(), - GroupFilter("GenresFilter", "Genre", [ - CheckBoxFilter("Action", "10"), - CheckBoxFilter("Action & Adventure", "24"), - CheckBoxFilter("Adventure", "18"), - CheckBoxFilter("Animation", "3"), - CheckBoxFilter("Biography", "37"), - CheckBoxFilter("Comedy", "7"), - CheckBoxFilter("Crime", "2"), - CheckBoxFilter("Documentary", "11"), - CheckBoxFilter("Drama", "4"), - CheckBoxFilter("Family", "9"), - CheckBoxFilter("Fantasy", "13"), - CheckBoxFilter("History", "19"), - CheckBoxFilter("Horror", "14"), - CheckBoxFilter("Kids", "27"), - CheckBoxFilter("Music", "15"), - CheckBoxFilter("Mystery", "1"), - CheckBoxFilter("News", "34"), - CheckBoxFilter("Reality", "22"), - CheckBoxFilter("Romance", "12"), - CheckBoxFilter("Sci-Fi & Fantasy", "31"), - CheckBoxFilter("Science Fiction", "5"), - CheckBoxFilter("Soap", "35"), - CheckBoxFilter("Talk", "29"), - CheckBoxFilter("Thriller", "16"), - CheckBoxFilter("TV Movie", "8"), - CheckBoxFilter("War", "17"), - CheckBoxFilter("War & Politics", "28"), - CheckBoxFilter("Western", "6"), - ]), - GroupFilter("CountriesFilter", "Countries", [ - CheckBoxFilter("Argentina", "11"), - CheckBoxFilter("Australia", "151"), - CheckBoxFilter("Austria", "4"), - CheckBoxFilter("Belgium", "44"), - CheckBoxFilter("Brazil", "190"), - CheckBoxFilter("Canada", "147"), - CheckBoxFilter("China", "101"), - CheckBoxFilter("Czech Republic", "231"), - CheckBoxFilter("Denmark", "222"), - CheckBoxFilter("Finland", "158"), - CheckBoxFilter("France", "3"), - CheckBoxFilter("Germany", "96"), - CheckBoxFilter("Hong Kong", "93"), - CheckBoxFilter("Hungary", "72"), - CheckBoxFilter("India", "105"), - CheckBoxFilter("Ireland", "196"), - CheckBoxFilter("Israel", "24"), - CheckBoxFilter("Italy", "205"), - CheckBoxFilter("Japan", "173"), - CheckBoxFilter("Luxembourg", "91"), - CheckBoxFilter("Mexico", "40"), - CheckBoxFilter("Netherlands", "172"), - CheckBoxFilter("New Zealand", "122"), - CheckBoxFilter("Norway", "219"), - CheckBoxFilter("Poland", "23"), - CheckBoxFilter("Romania", "170"), - CheckBoxFilter("Russia", "109"), - CheckBoxFilter("South Africa", "200"), - CheckBoxFilter("South Korea", "135"), - CheckBoxFilter("Spain", "62"), - CheckBoxFilter("Sweden", "114"), - CheckBoxFilter("Switzerland", "41"), - CheckBoxFilter("Taiwan", "119"), - CheckBoxFilter("Thailand", "57"), - CheckBoxFilter("United Kingdom", "180"), - CheckBoxFilter("United States of America", "129"), - ]), - ]; - } - - @override - List getSourcePreferences() { - return [ - if (source.name == "DopeBox") - ListPreference( - key: "preferred_domain", - title: "Preferred domain", - summary: "", - valueIndex: 0, - entries: ["dopebox.to", "dopebox.se"], - entryValues: ["https://dopebox.to", "https://dopebox.se"], - ), - if (source.name == "SFlix") - ListPreference( - key: "preferred_domain", - title: "Preferred domain", - summary: "", - valueIndex: 0, - entries: ["sflix.to", "sflix.se"], - entryValues: ["https://sflix.to", "https://sflix.se"], - ), - ListPreference( - key: "preferred_quality", - title: "Preferred Quality", - summary: "", - valueIndex: 0, - entries: ["1080p", "720p", "480p", "360p"], - entryValues: ["1080p", "720p", "480p", "360p"], - ), - ListPreference( - key: "preferred_subLang", - title: "Preferred sub language", - summary: "", - valueIndex: 1, - entries: [ - "Arabic", - "English", - "French", - "German", - "Hungarian", - "Italian", - "Japanese", - "Portuguese", - "Romanian", - "Russian", - "Spanish", - ], - entryValues: [ - "Arabic", - "English", - "French", - "German", - "Hungarian", - "Italian", - "Japanese", - "Portuguese", - "Romanian", - "Russian", - "Spanish", - ], - ), - ListPreference( - key: "preferred_latest_page", - title: "Preferred latest page", - summary: "", - valueIndex: 0, - entries: ["Movies", "TV Shows"], - entryValues: ["Latest Movies", "Latest TV Shows"], - ), - ListPreference( - key: "preferred_popular_page", - title: "Preferred popular page", - summary: "", - valueIndex: 0, - entries: ["Movies", "TV Shows"], - entryValues: ["movie", "tv-show"], - ), - ]; - } - - List sortVideos(List videos, int sourceId) { - String quality = getPreferenceValue(sourceId, "preferred_quality"); - - videos.sort((MVideo a, MVideo b) { - int qualityMatchA = 0; - if (a.quality.contains(quality)) { - qualityMatchA = 1; - } - int qualityMatchB = 0; - if (b.quality.contains(quality)) { - qualityMatchB = 1; - } - if (qualityMatchA != qualityMatchB) { - return qualityMatchB - qualityMatchA; - } - - final regex = RegExp(r'(\d+)p'); - final matchA = regex.firstMatch(a.quality); - final matchB = regex.firstMatch(b.quality); - final int qualityNumA = int.tryParse(matchA?.group(1) ?? '0') ?? 0; - final int qualityNumB = int.tryParse(matchB?.group(1) ?? '0') ?? 0; - return qualityNumB - qualityNumA; - }); - - return videos; - } - - List sortSubs(List subs, int sourceId) { - String lang = getPreferenceValue(sourceId, "preferred_subLang"); - - subs.sort((MTrack a, MTrack b) { - int langMatchA = 0; - if (a.label.toLowerCase().contains(lang.toLowerCase())) { - langMatchA = 1; - } - int langMatchB = 0; - if (b.label.toLowerCase().contains(lang.toLowerCase())) { - langMatchB = 1; - } - return langMatchB - langMatchA; - }); - return subs; - } - - String ll(String url) { - if (url.contains("?")) { - return "&"; - } - return "?"; - } -} - -DopeFlix main(MSource source) { - return DopeFlix(source: source); -} diff --git a/dart/anime/multisrc/dopeflix/sources.dart b/dart/anime/multisrc/dopeflix/sources.dart deleted file mode 100644 index 2a4c484b..00000000 --- a/dart/anime/multisrc/dopeflix/sources.dart +++ /dev/null @@ -1,23 +0,0 @@ -import '../../../../model/source.dart'; -import 'src/dopebox/dopebox.dart'; -import 'src/sflix/sflix.dart'; - -const _dopeflixVersion = "0.0.6"; -const _dopeflixSourceCodeUrl = - "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/multisrc/dopeflix/dopeflix.dart"; - -List get dopeflixSourcesList => _dopeflixSourcesList; -List _dopeflixSourcesList = - [ - //DopeBox (EN) - dopeboxSource, - //SFlix (EN) - sflixSource, - ] - .map( - (e) => - e - ..sourceCodeUrl = _dopeflixSourceCodeUrl - ..version = _dopeflixVersion, - ) - .toList(); diff --git a/dart/anime/multisrc/dopeflix/src/dopebox/dopebox.dart b/dart/anime/multisrc/dopeflix/src/dopebox/dopebox.dart deleted file mode 100644 index ef8a39c7..00000000 --- a/dart/anime/multisrc/dopeflix/src/dopebox/dopebox.dart +++ /dev/null @@ -1,13 +0,0 @@ -import '../../../../../../model/source.dart'; - -Source get dopeboxSource => _dopeboxSource; - -Source _dopeboxSource = Source( - name: "DopeBox", - baseUrl: "https://dopebox.to", - lang: "en", - typeSource: "dopeflix", - itemType: ItemType.anime, - iconUrl: - "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/multisrc/dopeflix/src/dopebox/icon.png", -); diff --git a/dart/anime/multisrc/dopeflix/src/dopebox/icon.png b/dart/anime/multisrc/dopeflix/src/dopebox/icon.png deleted file mode 100644 index 0e467600..00000000 Binary files a/dart/anime/multisrc/dopeflix/src/dopebox/icon.png and /dev/null differ diff --git a/dart/anime/multisrc/dopeflix/src/sflix/icon.png b/dart/anime/multisrc/dopeflix/src/sflix/icon.png deleted file mode 100644 index afdbec53..00000000 Binary files a/dart/anime/multisrc/dopeflix/src/sflix/icon.png and /dev/null differ diff --git a/dart/anime/multisrc/dopeflix/src/sflix/sflix.dart b/dart/anime/multisrc/dopeflix/src/sflix/sflix.dart deleted file mode 100644 index c19dbd1e..00000000 --- a/dart/anime/multisrc/dopeflix/src/sflix/sflix.dart +++ /dev/null @@ -1,13 +0,0 @@ -import '../../../../../../model/source.dart'; - -Source get sflixSource => _sflixSource; - -Source _sflixSource = Source( - name: "SFlix", - baseUrl: "https://sflix.to", - lang: "en", - typeSource: "dopeflix", - itemType: ItemType.anime, - iconUrl: - "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/multisrc/dopeflix/src/sflix/icon.png", -); diff --git a/dart/anime/multisrc/zorotheme/sources.dart b/dart/anime/multisrc/zorotheme/sources.dart deleted file mode 100644 index df781707..00000000 --- a/dart/anime/multisrc/zorotheme/sources.dart +++ /dev/null @@ -1,22 +0,0 @@ -import '../../../../model/source.dart'; -import 'src/hianime/hianime.dart'; -import 'src/kaido/kaido.dart'; - -const _zorothemeVersion = "0.1.75"; -const _zorothemeSourceCodeUrl = - "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/multisrc/zorotheme/zorotheme.dart"; - -List get zorothemeSourcesList => _zorothemeSourcesList; -List _zorothemeSourcesList = - [ - //AniWatch.to (EN) - aniwatchSource, - //Kaido.to (EN) - kaidoSource, - ] - .map( - (e) => e - ..sourceCodeUrl = _zorothemeSourceCodeUrl - ..version = _zorothemeVersion, - ) - .toList(); diff --git a/dart/anime/multisrc/zorotheme/src/hianime/hianime.dart b/dart/anime/multisrc/zorotheme/src/hianime/hianime.dart deleted file mode 100644 index 27bc3449..00000000 --- a/dart/anime/multisrc/zorotheme/src/hianime/hianime.dart +++ /dev/null @@ -1,14 +0,0 @@ -import '../../../../../../model/source.dart'; - -Source get aniwatchSource => _aniwatchSource; - -Source _aniwatchSource = Source( - id: 814067600, - name: "HiAnime", - baseUrl: "https://hianime.to", - itemType: ItemType.anime, - lang: "en", - typeSource: "zorotheme", - iconUrl: - "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/multisrc/zorotheme/src/hianime/icon.png", -); diff --git a/dart/anime/multisrc/zorotheme/src/hianime/icon.png b/dart/anime/multisrc/zorotheme/src/hianime/icon.png deleted file mode 100644 index 02dc16cf..00000000 Binary files a/dart/anime/multisrc/zorotheme/src/hianime/icon.png and /dev/null differ diff --git a/dart/anime/multisrc/zorotheme/src/kaido/icon.png b/dart/anime/multisrc/zorotheme/src/kaido/icon.png deleted file mode 100644 index 62ce8957..00000000 Binary files a/dart/anime/multisrc/zorotheme/src/kaido/icon.png and /dev/null differ diff --git a/dart/anime/multisrc/zorotheme/src/kaido/kaido.dart b/dart/anime/multisrc/zorotheme/src/kaido/kaido.dart deleted file mode 100644 index 59929d21..00000000 --- a/dart/anime/multisrc/zorotheme/src/kaido/kaido.dart +++ /dev/null @@ -1,13 +0,0 @@ -import '../../../../../../model/source.dart'; - -Source get kaidoSource => _kaidoSource; - -Source _kaidoSource = Source( - name: "Kaido.to", - baseUrl: "https://kaido.to", - lang: "en", - itemType: ItemType.anime, - typeSource: "zorotheme", - iconUrl: - "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/multisrc/zorotheme/src/kaido/icon.png", -); diff --git a/dart/anime/multisrc/zorotheme/zorotheme.dart b/dart/anime/multisrc/zorotheme/zorotheme.dart deleted file mode 100644 index 908330db..00000000 --- a/dart/anime/multisrc/zorotheme/zorotheme.dart +++ /dev/null @@ -1,710 +0,0 @@ -import 'package:mangayomi/bridge_lib.dart'; -import 'dart:convert'; - -class ZoroTheme extends MProvider { - ZoroTheme({required this.source}); - - MSource source; - - final Client client = Client(); - - @override - Future getPopular(int page) async { - final res = (await client.get( - Uri.parse("${source.baseUrl}/most-popular?page=$page"), - )).body; - - return animeElementM(res); - } - - @override - Future getLatestUpdates(int page) async { - final res = (await client.get( - Uri.parse("${source.baseUrl}/recently-updated?page=$page"), - )).body; - - return animeElementM(res); - } - - @override - Future search(String query, int page, FilterList filterList) async { - final filters = filterList.filters; - String url = "${source.baseUrl}/"; - - if (query.isEmpty) { - url += "filter?"; - } else { - url += "search?keyword=$query"; - } - - for (var filter in filters) { - if (filter.type == "TypeFilter") { - final type = filter.values[filter.state].value; - if (type.isNotEmpty) { - url += "${ll(url)}type=$type"; - } - } else if (filter.type == "StatusFilter") { - final status = filter.values[filter.state].value; - if (status.isNotEmpty) { - url += "${ll(url)}status=$status"; - } - } else if (filter.type == "RatedFilter") { - final rated = filter.values[filter.state].value; - if (rated.isNotEmpty) { - url += "${ll(url)}rated=$rated"; - } - } else if (filter.type == "ScoreFilter") { - final score = filter.values[filter.state].value; - if (score.isNotEmpty) { - url += "${ll(url)}score=$score"; - } - } else if (filter.type == "SeasonFilter") { - final season = filter.values[filter.state].value; - if (season.isNotEmpty) { - url += "${ll(url)}season=$season"; - } - } else if (filter.type == "LanguageFilter") { - final language = filter.values[filter.state].value; - if (language.isNotEmpty) { - url += "${ll(url)}language=$language"; - } - } else if (filter.type == "SortFilter") { - final sort = filter.values[filter.state].value; - if (sort.isNotEmpty) { - url += "${ll(url)}sort=$sort"; - } - } else if (filter.type == "StartYearFilter") { - final sy = filter.values[filter.state].value; - if (sy.isNotEmpty) { - url += "${ll(url)}sy=$sy"; - } - } else if (filter.type == "StartMonthFilter") { - final sm = filter.values[filter.state].value; - if (sm.isNotEmpty) { - url += "${ll(url)}sm=$sm"; - } - } else if (filter.type == "StartDayFilter") { - final sd = filter.values[filter.state].value; - if (sd.isNotEmpty) { - url += "${ll(url)}sd=$sd"; - } - } else if (filter.type == "EndYearFilter") { - final ey = filter.values[filter.state].value; - if (ey.isNotEmpty) { - url += "${ll(url)}sy=$ey"; - } - } else if (filter.type == "EndMonthFilter") { - final em = filter.values[filter.state].value; - if (em.isNotEmpty) { - url += "${ll(url)}sm=$em"; - } - } else if (filter.type == "EndDayFilter") { - final ed = filter.values[filter.state].value; - if (ed.isNotEmpty) { - url += "${ll(url)}sd=$ed"; - } - } else if (filter.type == "GenreFilter") { - final genre = (filter.state as List).where((e) => e.state).toList(); - if (genre.isNotEmpty) { - url += "${ll(url)}genre="; - for (var st in genre) { - url += "${st.value},"; - } - } - } - } - url += "${ll(url)}page=$page"; - final res = (await client.get(Uri.parse(url))).body; - - return animeElementM(res); - } - - @override - Future getDetail(String url) async { - final statusList = [ - {"Currently Airing": 0, "Finished Airing": 1}, - ]; - final res = (await client.get(Uri.parse("${source.baseUrl}$url"))).body; - MManga anime = MManga(); - final status = xpath( - res, - '//*[@class="anisc-info"]/div[contains(text(),"Status:")]/span[2]/text()', - ); - if (status.isNotEmpty) { - anime.status = parseStatus(status.first, statusList); - } - - final author = xpath( - res, - '//*[@class="anisc-info"]/div[contains(text(),"Studios:")]/span/text()', - ); - if (author.isNotEmpty) { - anime.author = author.first.replaceAll("Studios:", ""); - } - final description = xpath( - res, - '//*[@class="anisc-info"]/div[contains(text(),"Overview:")]/text()', - ); - if (description.isNotEmpty) { - anime.description = description.first.replaceAll("Overview:", ""); - } - final genre = xpath( - res, - '//*[@class="anisc-info"]/div[contains(text(),"Genres:")]/a/text()', - ); - - anime.genre = genre; - final id = substringAfterLast(url, '-'); - - final urlEp = - "${source.baseUrl}/ajax${ajaxRoute('${source.baseUrl}')}/episode/list/$id"; - - final resEp = (await client.get( - Uri.parse(urlEp), - headers: {"referer": url}, - )).body; - - final html = json.decode(resEp)["html"]; - final epElements = parseHtml(html).select("a.ep-item"); - - List? episodesList = []; - - for (var epElement in epElements) { - final number = epElement.attr("data-number"); - final title = epElement.attr("title"); - - MChapter episode = MChapter(); - episode.name = "Episode $number: $title"; - episode.url = epElement.getHref; - episodesList.add(episode); - } - - anime.chapters = episodesList.reversed.toList(); - return anime; - } - - @override - Future> getVideoList(String url) async { - final id = substringAfterLast(url, '?ep='); - - final res = (await client.get( - Uri.parse( - "${source.baseUrl}/ajax${ajaxRoute('${source.baseUrl}')}/episode/servers?episodeId=$id", - ), - headers: {"referer": "${source.baseUrl}/$url"}, - )).body; - final html = json.decode(res)["html"]; - - final serverElements = parseHtml(html).select("div.server-item"); - - List videos = []; - final hosterSelection = preferenceHosterSelection(source.id); - final typeSelection = preferenceTypeSelection(source.id); - for (var serverElement in serverElements) { - final name = serverElement.text; - final id = serverElement.attr("data-id"); - final subDub = serverElement.attr("data-type"); - - final resE = (await client.get( - Uri.parse( - "${source.baseUrl}/ajax${ajaxRoute('${source.baseUrl}')}/episode/sources?id=$id", - ), - headers: {"referer": "${source.baseUrl}/$url"}, - )).body; - String epUrl = substringBefore(substringAfter(resE, "\"link\":\""), "\""); - List a = []; - if (hosterSelection.contains(name) && typeSelection.contains(subDub)) { - if (name.contains("Vidstreaming")) { - a = await rapidCloudExtractor(epUrl, "Vidstreaming - $subDub", id); - } else if (name.contains("Vidcloud")) { - a = await rapidCloudExtractor(epUrl, "Vidcloud - $subDub", id); - } else if (name.contains("StreamTape")) { - a = await streamTapeExtractor(epUrl, "StreamTape - $subDub"); - } else if ([ - "HD-1", - "HD-2", - "HD-3", - ].any((element) => name.contains(element))) { - a = await rapidCloudExtractor(epUrl, "$name - $subDub", id); - } - - videos.addAll(a); - } - } - - return sortVideos(videos, source.id); - } - - Future> rapidCloudExtractor( - String url, - String name, - String dataID, - ) async { - try { - final headers = {'Referer': 'https://megacloud.club/'}; - final serverUrl = ['https://megacloud.tv', 'https://rapid-cloud.co']; - - final serverType = RegExp(r'https://megacloud\..*').hasMatch(url) ? 0 : 1; - final sourceUrl = [ - '/embed-2/v2/e-1/getSources?id=', - '/ajax/embed-6-v2/getSources?id=', - ]; - final sourceSpliter = ['/e-1/', '/embed-6-v2/']; - final id = url.split(sourceSpliter[serverType]).last.split('?').first; - final response = await client.get( - Uri.parse('${serverUrl[serverType]}${sourceUrl[serverType]}$id'), - headers: {"X-Requested-With": "XMLHttpRequest"}, - ); - if (response.statusCode != 200) { - return []; - } - final resServer = response.body; - - final encrypted = getMapValue(resServer, "encrypted"); - List> videoResJson = []; - List videos = []; - if (encrypted == "true") { - final key = await getWorkingKey(dataID); - if (key == null) { - return []; - } - final sources = await getSource(dataID, key); - if (sources == null || sources['sources'] == null) { - return []; - } - videoResJson = sources['sources']; - } else { - videoResJson = json.decode(resServer)["sources"]; - } - - String masterUrl = videoResJson[0]['file']; - String type = videoResJson[0]['type']; - - final tracks = (json.decode(resServer)['tracks'] as List) - .where((e) => e['kind'] == 'captions' ? true : false) - .toList(); - List subtitles = []; - - for (var sub in tracks) { - try { - MTrack subtitle = MTrack(); - subtitle - ..label = sub["label"] - ..file = sub["file"]; - subtitles.add(subtitle); - } catch (_) {} - } - - if (type == "hls") { - final masterPlaylistRes = (await client.get( - Uri.parse(masterUrl), - headers: headers, - )).body; - - for (var it in substringAfter( - masterPlaylistRes, - "#EXT-X-STREAM-INF:", - ).split("#EXT-X-STREAM-INF:")) { - final quality = - "${substringBefore(substringBefore(substringAfter(substringAfter(it, "RESOLUTION="), "x"), ","), "\n")}p"; - - String videoUrl = substringBefore(substringAfter(it, "\n"), "\n"); - - if (!videoUrl.startsWith("http")) { - videoUrl = - "${masterUrl.split("/").sublist(0, masterUrl.split("/").length - 1).join("/")}/$videoUrl"; - } - - MVideo video = MVideo(); - video - ..url = videoUrl - ..originalUrl = videoUrl - ..quality = "$name - $quality" - ..subtitles = subtitles - ..headers = headers; - videos.add(video); - } - } else { - MVideo video = MVideo(); - video - ..url = masterUrl - ..originalUrl = masterUrl - ..quality = "$name - Default" - ..subtitles = subtitles - ..headers = headers; - videos.add(video); - } - return videos; - } catch (e) { - return []; - } - } - - // credits: https://github.com/50n50/sources/blob/main/hianime/hianime.js - Future getWorkingKey(String dataID) async { - try { - final res = await client.get( - Uri.parse( - 'https://raw.githubusercontent.com/itzzzme/megacloud-keys/refs/heads/main/key.txt', - ), - ); - final key = res.body.trim(); - final sources = await getSource(dataID, key); - - if (sources != null && sources['sources'] != null) return key; - } catch (e) {} - - try { - final res = await client.get( - Uri.parse( - 'https://justarion.github.io/keys/e1-player/src/data/keys.json', - ), - ); - final jsonRes = json.decode(res.body); - final key = jsonRes['megacloud']['anime']['key']; - final sources = await getSource(dataID, key); - if (sources != null && sources['sources'] != null) return key; - } catch (e) {} - - try { - final res = await client.get( - Uri.parse( - 'https://raw.githubusercontent.com/yogesh-hacker/MegacloudKeys/refs/heads/main/keys.json', - ), - ); - final jsonRes = json.decode(res.body); - final key = jsonRes['mega']; - final sources = await getSource(dataID, key); - if (sources != null && sources['sources'] != null) return key; - } catch (e) {} - - try { - final res = await client.get( - Uri.parse( - 'https://raw.githubusercontent.com/SpencerDevs/megacloud-key-updater/refs/heads/master/key.txt', - ), - ); - final key = res.body.trim(); - final sources = await getSource(dataID, key); - if (sources != null && sources['sources'] != null) return key; - } catch (e) {} - - return null; - } - - // credits: https://github.com/50n50/sources/blob/main/hianime/hianime.js - Future?> getSource(String sourceId, String key) async { - final res1 = await client.get( - Uri.parse("${source.baseUrl}/ajax/v2/episode/sources?id=$sourceId"), - ); - final json1 = json.decode(res1.body); - final link = json1['link'] ?? ""; - final idMatch = RegExp(r'/e-1/([^/?]+)').firstMatch(link); - final streamId = idMatch?.group(1); - - if (streamId == null) return null; - - final res2 = await client.get( - Uri.parse( - 'https://megacloud.blog/embed-2/v2/e-1/getSources?id=$streamId', - ), - ); - final json2 = json.decode(res2.body); - final encrypted = json2['sources']; - - if (encrypted == null) return null; - - final result = {}; - - final sources = json.decode(decryptAESCryptoJS(encrypted, key)); - if (sources == null) return null; - - result['sources'] = sources; - return result; - } - - MPages animeElementM(String res) { - List animeList = []; - final doc = parseHtml(res); - final animeElements = doc.select('.flw-item'); - for (var element in animeElements) { - final linkElement = element.selectFirst('.film-detail h3 a'); - final imageElement = element.selectFirst('.film-poster img'); - MManga anime = MManga(); - anime.name = linkElement.attr('data-jname') ?? linkElement.text; - anime.imageUrl = imageElement.getSrc ?? imageElement.attr('data-src'); - anime.link = linkElement.getHref; - animeList.add(anime); - } - final nextPageElement = doc.selectFirst('li.page-item a[title="Next"]'); - return MPages(animeList, nextPageElement != null); - } - - String ajaxRoute(String baseUrl) { - if (baseUrl == "https://kaido.to") { - return ""; - } - return "/v2"; - } - - List yearList = [ - for (var i = 1917; i < 2026; i++) - SelectFilterOption(i.toString(), i.toString()), - SelectFilterOption("All", ""), - ]; - - @override - List getFilterList() { - return [ - SelectFilter("TypeFilter", "Type", 0, [ - SelectFilterOption("All", ""), - SelectFilterOption("Movie", "1"), - SelectFilterOption("TV", "2"), - SelectFilterOption("OVA", "3"), - SelectFilterOption("ONA", "4"), - SelectFilterOption("Special", "5"), - SelectFilterOption("Music", "6"), - ]), - SelectFilter("StatusFilter", "Status", 0, [ - SelectFilterOption("All", ""), - SelectFilterOption("Finished Airing", "1"), - SelectFilterOption("Currently Airing", "2"), - SelectFilterOption("Not yet aired", "3"), - ]), - SelectFilter("RatedFilter", "Rated", 0, [ - SelectFilterOption("All", ""), - SelectFilterOption("G", "1"), - SelectFilterOption("PG", "2"), - SelectFilterOption("PG-13", "3"), - SelectFilterOption("R", "4"), - SelectFilterOption("R+", "5"), - SelectFilterOption("Rx", "6"), - ]), - SelectFilter("ScoreFilter", "Score", 0, [ - SelectFilterOption("All", ""), - SelectFilterOption("(1) Appalling", "1"), - SelectFilterOption("(2) Horrible", "2"), - SelectFilterOption("(3) Very Bad", "3"), - SelectFilterOption("(4) Bad", "4"), - SelectFilterOption("(5) Average", "5"), - SelectFilterOption("(6) Fine", "6"), - SelectFilterOption("(7) Good", "7"), - SelectFilterOption("(8) Very Good", "8"), - SelectFilterOption("(9) Great", "9"), - SelectFilterOption("(10) Masterpiece", "10"), - ]), - SelectFilter("SeasonFilter", "Season", 0, [ - SelectFilterOption("All", ""), - SelectFilterOption("Spring", "1"), - SelectFilterOption("Summer", "2"), - SelectFilterOption("Fall", "3"), - SelectFilterOption("Winter", "4"), - ]), - SelectFilter("LanguageFilter", "Language", 0, [ - SelectFilterOption("All", ""), - SelectFilterOption("SUB", "1"), - SelectFilterOption("DUB", "2"), - SelectFilterOption("SUB & DUB", "3"), - ]), - SelectFilter("SortFilter", "Sort by", 0, [ - SelectFilterOption("All", ""), - SelectFilterOption("Default", "default"), - SelectFilterOption("Recently Added", "recently_added"), - SelectFilterOption("Recently Updated", "recently_updated"), - SelectFilterOption("Score", "score"), - SelectFilterOption("Name A-Z", "name_az"), - SelectFilterOption("Released Date", "released_date"), - SelectFilterOption("Most Watched", "most_watched"), - ]), - SelectFilter( - "StartYearFilter", - "Start year", - 0, - yearList.reversed.toList(), - ), - SelectFilter("StartMonthFilter", "Start month", 0, [ - SelectFilterOption("All", ""), - for (var i = 1; i < 13; i++) - SelectFilterOption(i.toString(), i.toString()), - ]), - SelectFilter("StartDayFilter", "Start day", 0, [ - SelectFilterOption("All", ""), - for (var i = 1; i < 32; i++) - SelectFilterOption(i.toString(), i.toString()), - ]), - SelectFilter("EndYearFilter", "End year", 0, yearList.reversed.toList()), - SelectFilter("EndmonthFilter", "End month", 0, [ - SelectFilterOption("All", ""), - for (var i = 1; i < 32; i++) - SelectFilterOption(i.toString(), i.toString()), - ]), - SelectFilter("EndDayFilter", "End day", 0, [ - SelectFilterOption("All", ""), - for (var i = 1; i < 32; i++) - SelectFilterOption(i.toString(), i.toString()), - ]), - GroupFilter("GenreFilter", "Genre", [ - CheckBoxFilter("Action", "1"), - CheckBoxFilter("Adventure", "2"), - CheckBoxFilter("Cars", "3"), - CheckBoxFilter("Comedy", "4"), - CheckBoxFilter("Dementia", "5"), - CheckBoxFilter("Demons", "6"), - CheckBoxFilter("Drama", "8"), - CheckBoxFilter("Ecchi", "9"), - CheckBoxFilter("Fantasy", "10"), - CheckBoxFilter("Game", "11"), - CheckBoxFilter("Harem", "35"), - CheckBoxFilter("Historical", "13"), - CheckBoxFilter("Horror", "14"), - CheckBoxFilter("Isekai", "44"), - CheckBoxFilter("Josei", "43"), - CheckBoxFilter("Kids", "15"), - CheckBoxFilter("Magic", "16"), - CheckBoxFilter("Martial Arts", "17"), - CheckBoxFilter("Mecha", "18"), - CheckBoxFilter("Military", "38"), - CheckBoxFilter("Music", "19"), - CheckBoxFilter("Mystery", "7"), - CheckBoxFilter("Parody", "20"), - CheckBoxFilter("Police", "39"), - CheckBoxFilter("Psychological", "40"), - CheckBoxFilter("Romance", "22"), - CheckBoxFilter("Samurai", "21"), - CheckBoxFilter("School", "23"), - CheckBoxFilter("Sci-Fi", "24"), - CheckBoxFilter("Seinen", "42"), - CheckBoxFilter("Shoujo", "25"), - CheckBoxFilter("Shoujo Ai", "26"), - CheckBoxFilter("Shounen", "27"), - CheckBoxFilter("Shounen Ai", "28"), - CheckBoxFilter("Slice of Life", "36"), - CheckBoxFilter("Space", "29"), - CheckBoxFilter("Sports", "30"), - CheckBoxFilter("Super Power", "31"), - CheckBoxFilter("Supernatural", "37"), - CheckBoxFilter("Thriller", "41"), - CheckBoxFilter("Vampire", "32"), - CheckBoxFilter("Yaoi", "33"), - CheckBoxFilter("Yuri", "34"), - ]), - ]; - } - - @override - List getSourcePreferences() { - return [ - ListPreference( - key: "preferred_quality", - title: "Preferred Quality", - summary: "", - valueIndex: 1, - entries: ["1080p", "720p", "480p", "360p"], - entryValues: ["1080", "720", "480", "360"], - ), - if (source.name == "HiAnime") - ListPreference( - key: "preferred_server2", - title: "Preferred server", - summary: "", - valueIndex: 0, - entries: ["HD-1", "HD-2", "HD-3", "StreamTape"], - entryValues: ["HD-1", "HD-2", "HD-3", "StreamTape"], - ), - if (source.name != "HiAnime") - ListPreference( - key: "preferred_server2", - title: "Preferred server", - summary: "", - valueIndex: 0, - entries: ["Vidstreaming", "VidCloud", "StreamTape"], - entryValues: ["Vidstreaming", "VidCloud", "StreamTape"], - ), - ListPreference( - key: "preferred_type1", - title: "Preferred Type", - summary: "", - valueIndex: 0, - entries: ["Sub", "Dub"], - entryValues: ["sub", "dub"], - ), - if (source.name != "HiAnime") - MultiSelectListPreference( - key: "hoster_selection2", - title: "Enable/Disable Hosts", - summary: "", - entries: ["Vidstreaming", "VidCloud", "StreamTape"], - entryValues: ["Vidstreaming", "VidCloud", "StreamTape"], - values: ["Vidstreaming", "VidCloud", "StreamTape"], - ), - if (source.name == "HiAnime") - MultiSelectListPreference( - key: "hoster_selection2", - title: "Enable/Disable Hosts", - summary: "", - entries: ["HD-1", "HD-2", "HD-3", "StreamTape"], - entryValues: ["HD-1", "HD-2", "HD-3", "StreamTape"], - values: ["HD-1", "HD-2", "HD-3", "StreamTape"], - ), - MultiSelectListPreference( - key: "type_selection_1", - title: "Enable/Disable Types", - summary: "", - entries: ["Sub", "Dub", "Raw"], - entryValues: ["sub", "dub", "raw"], - values: ["sub", "dub", "raw"], - ), - ]; - } - - List sortVideos(List videos, int sourceId) { - String quality = getPreferenceValue(sourceId, "preferred_quality"); - String server = getPreferenceValue(sourceId, "preferred_server2"); - String type = getPreferenceValue(sourceId, "preferred_type1"); - videos.sort((MVideo a, MVideo b) { - int qualityMatchA = 0; - - if (a.quality.contains(quality) && - a.quality.toLowerCase().contains(type.toLowerCase()) && - a.quality.toLowerCase().contains(server.toLowerCase())) { - qualityMatchA = 1; - } - int qualityMatchB = 0; - if (b.quality.contains(quality) && - b.quality.toLowerCase().contains(type.toLowerCase()) && - b.quality.toLowerCase().contains(server.toLowerCase())) { - qualityMatchB = 1; - } - if (qualityMatchA != qualityMatchB) { - return qualityMatchB - qualityMatchA; - } - - final regex = RegExp(r'(\d+)p'); - final matchA = regex.firstMatch(a.quality); - final matchB = regex.firstMatch(b.quality); - final int qualityNumA = int.tryParse(matchA?.group(1) ?? '0') ?? 0; - final int qualityNumB = int.tryParse(matchB?.group(1) ?? '0') ?? 0; - return qualityNumB - qualityNumA; - }); - return videos; - } - - List preferenceHosterSelection(int sourceId) { - return getPreferenceValue(sourceId, "hoster_selection2"); - } - - List preferenceTypeSelection(int sourceId) { - return getPreferenceValue(sourceId, "type_selection_1"); - } - - String ll(String url) { - if (url.contains("?")) { - return "&"; - } - return "?"; - } -} - -ZoroTheme main(MSource source) { - return ZoroTheme(source: source); -} diff --git a/dart/anime/src/all/animeworldindia/animeworldindia.dart b/dart/anime/src/all/animeworldindia/animeworldindia.dart deleted file mode 100644 index 8256bd54..00000000 --- a/dart/anime/src/all/animeworldindia/animeworldindia.dart +++ /dev/null @@ -1,430 +0,0 @@ -import 'package:mangayomi/bridge_lib.dart'; -import 'dart:convert'; - -class AnimeWorldIndia extends MProvider { - AnimeWorldIndia({required this.source}); - - MSource source; - - final Client client = Client(); - - @override - Future getPopular(int page) async { - final res = - (await client.get( - Uri.parse( - "${source.baseUrl}/advanced-search/page/$page/?s_lang=${source.lang}&s_orderby=viewed", - ), - )).body; - - return parseAnimeList(res); - } - - @override - Future getLatestUpdates(int page) async { - final res = - (await client.get( - Uri.parse( - "${source.baseUrl}/advanced-search/page/$page/?s_lang=${source.lang}&s_orderby=update", - ), - )).body; - - return parseAnimeList(res); - } - - @override - Future search(String query, int page, FilterList filterList) async { - final filters = filterList.filters; - String url = - "${source.baseUrl}/advanced-search/page/$page/?s_keyword=$query&s_lang=${source.lang}"; - for (var filter in filters) { - if (filter.type == "TypeFilter") { - final type = filter.values[filter.state].value; - url += "${ll(url)}s_type=$type"; - } else if (filter.type == "StatusFilter") { - final status = filter.values[filter.state].value; - url += "${ll(url)}s_status=$status"; - } else if (filter.type == "StyleFilter") { - final style = filter.values[filter.state].value; - url += "${ll(url)}s_sub_type=$style"; - } else if (filter.type == "YearFilter") { - final year = filter.values[filter.state].value; - url += "${ll(url)}s_year=$year"; - } else if (filter.type == "SortFilter") { - final sort = filter.values[filter.state].value; - url += "${ll(url)}s_orderby=$sort"; - } else if (filter.type == "GenresFilter") { - final genre = (filter.state as List).where((e) => e.state).toList(); - url += "${ll(url)}s_genre="; - if (genre.isNotEmpty) { - for (var st in genre) { - String value = st.value; - url += value.toLowerCase().replaceAll(" ", "-"); - if (genre.length > 1) { - url += "%2C"; - } - } - if (genre.length > 1) { - url = substringBeforeLast(url, '%2C'); - } - } - } - } - - final res = (await client.get(Uri.parse(url))).body; - return parseAnimeList(res); - } - - @override - Future getDetail(String url) async { - final res = (await client.get(Uri.parse(url))).body; - MManga anime = MManga(); - final document = parseHtml(res); - final isMovie = - document.xpath('//li/a[contains(text(),"Movie")]/text()').isNotEmpty; - if (isMovie) { - anime.status = MStatus.completed; - } else { - final eps = xpath( - res, - '//ul/li/a[contains(@href,"${source.baseUrl}/watch")]/text()', - ); - if (eps.isNotEmpty) { - final epParts = eps.first - .substring(3) - .replaceAll(" ", "") - .replaceAll("\n", "") - .split('/'); - if (epParts.length == 2) { - if (epParts[0].compareTo(epParts[1]) == 0) { - anime.status = MStatus.completed; - } else { - anime.status = MStatus.ongoing; - } - } - } - } - anime.description = document.selectFirst("div[data-synopsis]")?.text ?? ""; - anime.author = document - .xpath('//li[contains(text(),"Producers:")]/span/a/text()') - .join(', '); - anime.genre = document.xpath( - '//span[@class="leading-6"]/a[contains(@class,"border-opacity-30")]/text()', - ); - final seasonsJson = - json.decode( - substringBeforeLast( - substringBefore( - substringAfter(res, "var season_list = "), - "var season_label =", - ), - ";", - ), - ) - as List>; - bool isSingleSeason = seasonsJson.length == 1; - List? episodesList = []; - for (var i = 0; i < seasonsJson.length; i++) { - final seasonJson = seasonsJson[i]; - final seasonName = isSingleSeason ? "" : "Season ${i + 1}"; - final episodesJson = - (seasonJson["episodes"]["all"] as List>).reversed - .toList(); - for (var j = 0; j < episodesJson.length; j++) { - final episodeJson = episodesJson[j]; - final episodeTitle = episodeJson["metadata"]["title"] ?? ""; - String episodeName = ""; - if (isMovie) { - episodeName = "Movie"; - } else { - if (seasonName.isNotEmpty) { - episodeName = "$seasonName - "; - } - episodeName += "Episode ${j + 1} "; - if (episodeTitle.isNotEmpty) { - episodeName += "- $episodeTitle"; - } - } - MChapter episode = MChapter(); - episode.name = episodeName; - - episode.dateUpload = - "${int.parse(episodeJson["metadata"]["released"] ?? "0") * 1000}"; - episode.url = "/wp-json/kiranime/v1/episode?id=${episodeJson["id"]}"; - episodesList.add(episode); - } - } - - anime.chapters = episodesList.reversed.toList(); - return anime; - } - - @override - Future> getVideoList(String url) async { - final res = (await client.get(Uri.parse("${source.baseUrl}$url"))).body; - var resJson = substringBefore( - substringAfterLast(res, "\"players\":"), - ",\"noplayer\":", - ); - var streams = - (json.decode(resJson) as List>) - .where( - (e) => - (e["type"] == "stream" ? true : false) && - (e["url"] as String).isNotEmpty, - ) - .toList() - .where( - (e) => - language(source.lang).isEmpty || - language(source.lang) == e["language"] - ? true - : false, - ) - .toList(); - List videos = []; - for (var stream in streams) { - String videoUrl = stream["url"]; - final language = stream["language"]; - final video = await mystreamExtractor(videoUrl, language); - videos.addAll(video); - } - - return sortVideos(videos, source.id); - } - - MPages parseAnimeList(String res) { - List animeList = []; - final document = parseHtml(res); - - for (var element in document.select("div.col-span-1")) { - MManga anime = MManga(); - anime.name = - element.selectFirst("div.font-medium.line-clamp-2.mb-3").text; - anime.link = element.selectFirst("a").getHref; - anime.imageUrl = - "${source.baseUrl}${getUrlWithoutDomain(element.selectFirst("img").getSrc)}"; - animeList.add(anime); - } - final hasNextPage = - xpath( - res, - '//li/span[@class="page-numbers current"]/parent::li//following-sibling::li/a/@href', - ).isNotEmpty; - return MPages(animeList, hasNextPage); - } - - String language(String lang) { - final languages = { - "all": "", - "bn": "bengali", - "en": "english", - "hi": "hindi", - "ja": "japanese", - "ml": "malayalam", - "mr": "marathi", - "ta": "tamil", - "te": "telugu", - }; - return languages[lang] ?? ""; - } - - Future> mystreamExtractor(String url, String language) async { - List videos = []; - final res = (await client.get(Uri.parse(url))).body; - final streamCode = substringBefore( - substringAfter(substringAfter(res, "sniff("), ", \""), - '"', - ); - - final streamUrl = - "${substringBefore(url, "/watch")}/m3u8/$streamCode/master.txt?s=1&cache=1"; - final masterPlaylistRes = (await client.get(Uri.parse(streamUrl))).body; - - List audios = []; - for (var it in substringAfter( - masterPlaylistRes, - "#EXT-X-MEDIA:TYPE=AUDIO", - ).split("#EXT-X-MEDIA:TYPE=AUDIO")) { - final line = substringBefore( - substringAfter(it, "#EXT-X-MEDIA:TYPE=AUDIO"), - "\n", - ); - final audioUrl = substringBefore(substringAfter(line, "URI=\""), "\""); - MTrack audio = MTrack(); - audio - ..label = substringBefore(substringAfter(line, "NAME=\""), "\"") - ..file = audioUrl; - audios.add(audio); - } - - for (var it in substringAfter( - masterPlaylistRes, - "#EXT-X-STREAM-INF:", - ).split("#EXT-X-STREAM-INF:")) { - final quality = - "${substringBefore(substringBefore(substringAfter(substringAfter(it, "RESOLUTION="), "x"), ","), "\n")}p"; - - String videoUrl = substringBefore(substringAfter(it, "\n"), "\n"); - - MVideo video = MVideo(); - video - ..url = videoUrl - ..originalUrl = videoUrl - ..quality = "[$language] MyStream - $quality" - ..audios = audios; - videos.add(video); - } - return videos; - } - - @override - List getFilterList() { - return [ - SelectFilter("TypeFilter", "Type", 0, [ - SelectFilterOption("Any", "all"), - SelectFilterOption("TV", "tv"), - SelectFilterOption("Movie", "movies"), - ]), - SelectFilter("StatusFilter", "Status", 0, [ - SelectFilterOption("Any", "all"), - SelectFilterOption("Currently Airing", "airing"), - SelectFilterOption("Finished Airing", "completed"), - ]), - SelectFilter("StyleFilter", "Style", 0, [ - SelectFilterOption("Any", "all"), - SelectFilterOption("Anime", "anime"), - SelectFilterOption("Cartoon", "cartoon"), - ]), - SelectFilter("YearFilter", "Year", 0, [ - SelectFilterOption("Any", "all"), - SelectFilterOption("2024", "2024"), - SelectFilterOption("2023", "2023"), - SelectFilterOption("2022", "2022"), - SelectFilterOption("2021", "2021"), - SelectFilterOption("2020", "2020"), - SelectFilterOption("2019", "2019"), - SelectFilterOption("2018", "2018"), - SelectFilterOption("2017", "2017"), - SelectFilterOption("2016", "2016"), - SelectFilterOption("2015", "2015"), - SelectFilterOption("2014", "2014"), - SelectFilterOption("2013", "2013"), - SelectFilterOption("2012", "2012"), - SelectFilterOption("2011", "2011"), - SelectFilterOption("2010", "2010"), - SelectFilterOption("2009", "2009"), - SelectFilterOption("2008", "2008"), - SelectFilterOption("2007", "2007"), - SelectFilterOption("2006", "2006"), - SelectFilterOption("2005", "2005"), - SelectFilterOption("2004", "2004"), - SelectFilterOption("2003", "2003"), - SelectFilterOption("2002", "2002"), - SelectFilterOption("2001", "2001"), - SelectFilterOption("2000", "2000"), - SelectFilterOption("1999", "1999"), - SelectFilterOption("1998", "1998"), - SelectFilterOption("1997", "1997"), - SelectFilterOption("1996", "1996"), - SelectFilterOption("1995", "1995"), - SelectFilterOption("1994", "1994"), - SelectFilterOption("1993", "1993"), - SelectFilterOption("1992", "1992"), - SelectFilterOption("1991", "1991"), - SelectFilterOption("1990", "1990"), - ]), - SelectFilter("SortFilter", "Sort", 0, [ - SelectFilterOption("Default", "default"), - SelectFilterOption("Ascending", "title_a_z"), - SelectFilterOption("Descending", "title_z_a"), - SelectFilterOption("Updated", "update"), - SelectFilterOption("Published", "date"), - SelectFilterOption("Most Viewed", "viewed"), - SelectFilterOption("Favourite", "favorite"), - ]), - GroupFilter("GenresFilter", "Genres", [ - CheckBoxFilter("Action", "Action"), - CheckBoxFilter("Adult Cast", "Adult Cast"), - CheckBoxFilter("Adventure", "Adventure"), - CheckBoxFilter("Animation", "Animation"), - CheckBoxFilter("Comedy", "Comedy"), - CheckBoxFilter("Detective", "Detective"), - CheckBoxFilter("Drama", "Drama"), - CheckBoxFilter("Ecchi", "Ecchi"), - CheckBoxFilter("Family", "Family"), - CheckBoxFilter("Fantasy", "Fantasy"), - CheckBoxFilter("Isekai", "Isekai"), - CheckBoxFilter("Kids", "Kids"), - CheckBoxFilter("Martial Arts", "Martial Arts"), - CheckBoxFilter("Mecha", "Mecha"), - CheckBoxFilter("Military", "Military"), - CheckBoxFilter("Mystery", "Mystery"), - CheckBoxFilter("Otaku Culture", "Otaku Culture"), - CheckBoxFilter("Reality", "Reality"), - CheckBoxFilter("Romance", "Romance"), - CheckBoxFilter("School", "School"), - CheckBoxFilter("Sci-Fi", "Sci-Fi"), - CheckBoxFilter("Seinen", "Seinen"), - CheckBoxFilter("Shounen", "Shounen"), - CheckBoxFilter("Slice of Life", "Slice of Life"), - CheckBoxFilter("Sports", "Sports"), - CheckBoxFilter("Super Power", "Super Power"), - CheckBoxFilter("SuperHero", "SuperHero"), - CheckBoxFilter("Supernatural", "Supernatural"), - CheckBoxFilter("TV Movie", "TV Movie"), - ]), - ]; - } - - @override - List getSourcePreferences() { - return [ - ListPreference( - key: "preferred_quality", - title: "Preferred Quality", - summary: "", - valueIndex: 0, - entries: ["1080p", "720p", "480p", "360p", "240p"], - entryValues: ["1080", "720", "480", "360", "240"], - ), - ]; - } - - List sortVideos(List videos, int sourceId) { - String quality = getPreferenceValue(sourceId, "preferred_quality"); - videos.sort((MVideo a, MVideo b) { - int qualityMatchA = 0; - - if (a.quality.contains(quality)) { - qualityMatchA = 1; - } - int qualityMatchB = 0; - if (b.quality.contains(quality)) { - qualityMatchB = 1; - } - if (qualityMatchA != qualityMatchB) { - return qualityMatchB - qualityMatchA; - } - - final regex = RegExp(r'(\d+)p'); - final matchA = regex.firstMatch(a.quality); - final matchB = regex.firstMatch(b.quality); - final int qualityNumA = int.tryParse(matchA?.group(1) ?? '0') ?? 0; - final int qualityNumB = int.tryParse(matchB?.group(1) ?? '0') ?? 0; - return qualityNumB - qualityNumA; - }); - return videos; - } - - String ll(String url) { - if (url.contains("?")) { - return "&"; - } - return "?"; - } -} - -AnimeWorldIndia main(MSource source) { - return AnimeWorldIndia(source: source); -} diff --git a/dart/anime/src/all/animeworldindia/icon.png b/dart/anime/src/all/animeworldindia/icon.png deleted file mode 100644 index 0426e29d..00000000 Binary files a/dart/anime/src/all/animeworldindia/icon.png and /dev/null differ diff --git a/dart/anime/src/all/animeworldindia/sources.dart b/dart/anime/src/all/animeworldindia/sources.dart deleted file mode 100644 index 0840b4e2..00000000 --- a/dart/anime/src/all/animeworldindia/sources.dart +++ /dev/null @@ -1,37 +0,0 @@ -import '../../../../../model/source.dart'; - -const _animeworldindiaVersion = "0.0.35"; -const _animeworldindiaSourceCodeUrl = - "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/all/animeworldindia/animeworldindia.dart"; - -String _iconUrl = - "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/all/animeworldindia/icon.png"; - -List _languages = [ - "all", - "en", - "bn", - "hi", - "ja", - "ml", - "mr", - "ta", - "te", -]; - -List get animeworldindiaSourcesList => _animeworldindiaSourcesList; -List _animeworldindiaSourcesList = - _languages - .map( - (e) => Source( - name: 'AnimeWorld India', - baseUrl: "https://anime-world.in", - lang: e, - typeSource: "multiple", - iconUrl: _iconUrl, - version: _animeworldindiaVersion, - itemType: ItemType.anime, - sourceCodeUrl: _animeworldindiaSourceCodeUrl, - ), - ) - .toList(); diff --git a/dart/anime/src/all/nyaa/icon.png b/dart/anime/src/all/nyaa/icon.png deleted file mode 100644 index b57dba5f..00000000 Binary files a/dart/anime/src/all/nyaa/icon.png and /dev/null differ diff --git a/dart/anime/src/all/nyaa/nyaa.dart b/dart/anime/src/all/nyaa/nyaa.dart deleted file mode 100644 index ef21fcb8..00000000 --- a/dart/anime/src/all/nyaa/nyaa.dart +++ /dev/null @@ -1,220 +0,0 @@ -import 'package:mangayomi/bridge_lib.dart'; - -class Nyaa extends MProvider { - Nyaa({required this.source}); - - MSource source; - - final Client client = Client(); - - @override - Future getPopular(int page) async { - final res = (await client.get( - Uri.parse( - "${getBaseUrl()}/?f=0&c=${getPreferenceValue(source.id, "preferred_categorie_page")}&q=&s=downloads&o=desc&p=$page", - ), - )).body; - return parseAnimeList(res); - } - - @override - Future getLatestUpdates(int page) async { - final res = (await client.get( - Uri.parse( - "${getBaseUrl()}/?f=0&c=${getPreferenceValue(source.id, "preferred_categorie_page")}&q=$page", - ), - )).body; - return parseAnimeList(res); - } - - @override - Future search(String query, int page, FilterList filterList) async { - final filters = filterList.filters; - String url = ""; - url = - "${getBaseUrl()}/?f=0&c=${getPreferenceValue(source.id, "preferred_categorie_page")}&q=${query.replaceAll(" ", "+")}&p=$page"; - for (var filter in filters) { - if (filter.type == "SortFilter") { - url += "${ll(url)}s=${filter.values[filter.state.index].value}"; - final asc = filter.state.ascending ? "&o=asc" : "&o=desc"; - url += "${ll(url)}$asc"; - } - } - final res = (await client.get(Uri.parse(url))).body; - return parseAnimeList(res); - } - - String extractPanelBody(MDocument document) { - final panelBody = document.selectFirst('.panel-body'); - if (panelBody == null) return ""; - - final rows = panelBody.select('.row'); - - final Map info = {}; - for (var row in rows) { - final labels = row.select('.col-md-1'); - for (var label in labels) { - final key = label.text.replaceAll(":", "").trim(); - final valueDiv = label.nextElementSibling; - if (valueDiv == null) continue; - - final links = valueDiv.select('a'); - String value; - if (links.isNotEmpty) { - value = links.map((a) => a.text.trim()).join(' - '); - } else { - value = valueDiv.text.trim(); - } - - info[key] = value; - } - } - - final buffer = StringBuffer(); - buffer.writeln("Torrent Info:\n"); - info.forEach((k, v) { - buffer.writeln("${k.padRight(11)}: $v"); - }); - if (getPreferenceValue(source.id, "torrent_description_visible")) { - buffer.writeln("\n\n"); - buffer.writeln("Torrent Description: \n"); - buffer.writeln( - document - .select("#torrent-description") - .map((e) => e.text.trim()) - .join("\n\n"), - ); - } - - return buffer.toString(); - } - - @override - Future getDetail(String url) async { - MManga anime = MManga(); - final res = (await client.get(Uri.parse(url))).body; - final document = parseHtml(res); - - anime.description = extractPanelBody(document); - - List chapters = []; - chapters.add( - MChapter( - name: "Torrent", - url: "${getBaseUrl()}/download/${substringAfterLast(url, '/')}.torrent", - ), - ); - anime.chapters = chapters; - - return anime; - } - - @override - Future> getVideoList(String url) async { - var video = MVideo(); - video - ..url = url - ..originalUrl = url - ..quality = ""; - return [video]; - } - - @override - List getFilterList() { - return [ - SortFilter("SortFilter", "Sort by", SortState(0, true), [ - SelectFilterOption("None", ""), - SelectFilterOption("Size", "size"), - SelectFilterOption("Date", "id"), - SelectFilterOption("Seeders", "seeders"), - SelectFilterOption("Leechers", "leechers"), - SelectFilterOption("Download", "downloads"), - ]), - ]; - } - - @override - List getSourcePreferences() { - return [ - ListPreference( - key: "preferred_categorie_page", - title: "Preferred categorie page", - summary: "", - valueIndex: 0, - entries: ["Anime", "Live Action"], - entryValues: ["1_0", "4_0"], - ), - SwitchPreferenceCompat( - key: "torrent_description_visible", - title: "Display Torrent Description", - summary: - "Enable to show the full torrent description in the details view.", - value: false, - ), - EditTextPreference( - key: "domain_url", - title: 'Edit URL', - summary: "", - value: source.baseUrl, - dialogTitle: "URL", - dialogMessage: "", - ), - ]; - } - - String getBaseUrl() { - final baseUrl = getPreferenceValue(source.id, "domain_url")?.trim(); - - if (baseUrl == null || baseUrl.isEmpty) { - return source.baseUrl; - } - - return baseUrl.endsWith("/") - ? baseUrl.substring(0, baseUrl.length - 1) - : baseUrl; - } - - MPages parseAnimeList(String res) { - List animeList = []; - final document = parseHtml(res); - - final values = document.select( - "body > div > div.table-responsive > table > tbody > tr", - ); - for (var value in values) { - MManga anime = MManga(); - anime.imageUrl = - "${getBaseUrl()}${getUrlWithoutDomain(value.selectFirst("td:nth-child(1) > a > img").getSrc)}"; - MElement firstElement = value - .select("td > a") - .where( - (MElement e) => - e.outerHtml.contains("/view/") && - !e.outerHtml.contains("#comments"), - ) - .toList() - .first; - anime.link = - "${getBaseUrl()}${getUrlWithoutDomain(firstElement.getHref)}"; - anime.name = firstElement.attr("title"); - animeList.add(anime); - } - - final hasNextPage = xpath( - res, - '//ul[@class="pagination"]/li[contains(text(),"»")]/a/@href', - ).isNotEmpty; - return MPages(animeList, hasNextPage); - } - - String ll(String url) { - if (url.contains("?")) { - return "&"; - } - return "?"; - } -} - -Nyaa main(MSource source) { - return Nyaa(source: source); -} diff --git a/dart/anime/src/all/nyaa/source.dart b/dart/anime/src/all/nyaa/source.dart deleted file mode 100644 index 59168bfe..00000000 --- a/dart/anime/src/all/nyaa/source.dart +++ /dev/null @@ -1,20 +0,0 @@ -import '../../../../../model/source.dart'; - -const _nyaaVersion = "0.0.4"; -const _nyaaSourceCodeUrl = - "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/all/nyaa/nyaa.dart"; - -String _iconUrl = - "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/all/nyaa/icon.png"; - -Source get nyaaSource => _nyaaSource; -Source _nyaaSource = Source( - name: 'Nyaa', - baseUrl: "https://nyaa.si", - lang: "all", - typeSource: "torrent", - iconUrl: _iconUrl, - version: _nyaaVersion, - itemType: ItemType.anime, - sourceCodeUrl: _nyaaSourceCodeUrl, -); diff --git a/dart/anime/src/ar/okanime/icon.png b/dart/anime/src/ar/okanime/icon.png deleted file mode 100644 index 6057076d..00000000 Binary files a/dart/anime/src/ar/okanime/icon.png and /dev/null differ diff --git a/dart/anime/src/ar/okanime/okanime.dart b/dart/anime/src/ar/okanime/okanime.dart deleted file mode 100644 index edd502c0..00000000 --- a/dart/anime/src/ar/okanime/okanime.dart +++ /dev/null @@ -1,234 +0,0 @@ -import 'package:mangayomi/bridge_lib.dart'; -import 'dart:convert'; - -class OkAnime extends MProvider { - OkAnime({required this.source}); - - MSource source; - - final Client client = Client(); - - @override - Future getPopular(int page) async { - final res = (await client.get(Uri.parse(source.baseUrl))).body; - List animeList = []; - String path = - '//div[@class="section" and contains(text(),"افضل انميات")]/div[@class="section-content"]/div/div/div[contains(@class,"anime-card")]'; - final urls = xpath(res, '$path/div[@class="anime-title")]/h4/a/@href'); - final names = xpath(res, '$path/div[@class="anime-title")]/h4/a/text()'); - final images = xpath(res, '$path/div[@class="anime-image")]/img/@src'); - - for (var i = 0; i < names.length; i++) { - MManga anime = MManga(); - anime.name = names[i]; - anime.imageUrl = images[i]; - anime.link = urls[i]; - animeList.add(anime); - } - return MPages(animeList, false); - } - - @override - Future getLatestUpdates(int page) async { - final res = - (await client.get( - Uri.parse("${source.baseUrl}/espisode-list?page=$page"), - )).body; - List animeList = []; - String path = '//*[contains(@class,"anime-card")]'; - final urls = xpath(res, '$path/div[@class="anime-title")]/h4/a/@href'); - final names = xpath(res, '$path/div[@class="anime-title")]/h4/a/text()'); - final images = xpath(res, '$path/div[@class="episode-image")]/img/@src'); - - for (var i = 0; i < names.length; i++) { - MManga anime = MManga(); - anime.name = names[i]; - anime.imageUrl = images[i]; - anime.link = urls[i]; - animeList.add(anime); - } - final nextPage = xpath( - res, - '//li[@class="page-item"]/a[@rel="next"]/@href', - ); - return MPages(animeList, nextPage.isNotEmpty); - } - - @override - Future search(String query, int page, FilterList filterList) async { - String url = "${source.baseUrl}/search/?s=$query"; - if (page > 1) { - url += "&page=$page"; - } - - final res = (await client.get(Uri.parse(url))).body; - - List animeList = []; - String path = '//*[contains(@class,"anime-card")]'; - final urls = xpath(res, '$path/div[@class="anime-title")]/h4/a/@href'); - final names = xpath(res, '$path/div[@class="anime-title")]/h4/a/text()'); - final images = xpath(res, '$path/div[@class="anime-image")]/img/@src'); - - for (var i = 0; i < names.length; i++) { - MManga anime = MManga(); - anime.name = names[i]; - anime.imageUrl = images[i]; - anime.link = urls[i]; - animeList.add(anime); - } - final nextPage = xpath( - res, - '//li[@class="page-item"]/a[@rel="next"]/@href', - ); - return MPages(animeList, nextPage.isNotEmpty); - } - - @override - Future getDetail(String url) async { - final statusList = [ - {"يعرض الان": 0, "مكتمل": 1}, - ]; - final res = (await client.get(Uri.parse(url))).body; - MManga anime = MManga(); - final status = xpath( - res, - '//*[@class="full-list-info" and contains(text(),"حالة الأنمي")]/small/a/text()', - ); - if (status.isNotEmpty) { - anime.status = parseStatus(status.first, statusList); - } - anime.description = xpath(res, '//*[@class="review-content"]/text()').first; - - anime.genre = xpath(res, '//*[@class="review-author-info"]/a/text()'); - final epUrls = - xpath( - res, - '//*[contains(@class,"anime-card")]/div[@class="anime-title")]/h5/a/@href', - ).reversed.toList(); - final names = - xpath( - res, - '//*[contains(@class,"anime-card")]/div[@class="anime-title")]/h5/a/text()', - ).reversed.toList(); - - List? episodesList = []; - for (var i = 0; i < epUrls.length; i++) { - MChapter episode = MChapter(); - episode.name = names[i]; - episode.url = epUrls[i]; - episodesList.add(episode); - } - - anime.chapters = episodesList; - return anime; - } - - @override - Future> getVideoList(String url) async { - final res = (await client.get(Uri.parse(url))).body; - final urls = xpath(res, '//*[@id="streamlinks"]/a/@data-src'); - final qualities = xpath(res, '//*[@id="streamlinks"]/a/span/text()'); - final hosterSelection = preferenceHosterSelection(source.id); - List videos = []; - for (var i = 0; i < urls.length; i++) { - final url = urls[i]; - final quality = getQuality(qualities[i]); - List a = []; - - if (url.contains("https://doo") && hosterSelection.contains("Dood")) { - a = await doodExtractor(url, "DoodStream - $quality"); - } else if (url.contains("mp4upload") && - hosterSelection.contains("Mp4upload")) { - a = await mp4UploadExtractor(url, null, "", ""); - } else if (url.contains("ok.ru") && hosterSelection.contains("Okru")) { - a = await okruExtractor(url); - } else if (url.contains("voe.sx") && hosterSelection.contains("Voe")) { - a = await voeExtractor(url, "VoeSX $quality"); - } else if (containsVidBom(url) && hosterSelection.contains("VidBom")) { - a = await vidBomExtractor(url); - } - videos.addAll(a); - } - return sortVideos(videos, source.id); - } - - @override - List getSourcePreferences() { - return [ - ListPreference( - key: "preferred_quality", - title: "Preferred Quality", - summary: "", - valueIndex: 1, - entries: ["1080p", "720p", "480p", "360p"], - entryValues: ["1080", "720", "480", "360"], - ), - MultiSelectListPreference( - key: "hoster_selection", - title: "Enable/Disable Hosts", - summary: "", - entries: ["Dood", "Voe", "Mp4upload", "VidBom", "Okru"], - entryValues: ["Dood", "Voe", "Mp4upload", "VidBom", "Okru"], - values: ["Dood", "Voe", "Mp4upload", "VidBom", "Okru"], - ), - ]; - } - - List sortVideos(List videos, int sourceId) { - String quality = getPreferenceValue(sourceId, "preferred_quality"); - - videos.sort((MVideo a, MVideo b) { - int qualityMatchA = 0; - if (a.quality.contains(quality)) { - qualityMatchA = 1; - } - int qualityMatchB = 0; - if (b.quality.contains(quality)) { - qualityMatchB = 1; - } - if (qualityMatchA != qualityMatchB) { - return qualityMatchB - qualityMatchA; - } - - final regex = RegExp(r'(\d+)p'); - final matchA = regex.firstMatch(a.quality); - final matchB = regex.firstMatch(b.quality); - final int qualityNumA = int.tryParse(matchA?.group(1) ?? '0') ?? 0; - final int qualityNumB = int.tryParse(matchB?.group(1) ?? '0') ?? 0; - return qualityNumB - qualityNumA; - }); - - return videos; - } - - List preferenceHosterSelection(int sourceId) { - return getPreferenceValue(sourceId, "hoster_selection"); - } - - String getQuality(String quality) { - quality = quality.replaceAll(" ", ""); - if (quality == "HD") { - return "720p"; - } else if (quality == "FHD") { - return "1080p"; - } else if (quality == "SD") { - return "480p"; - } - return "240p"; - } - - bool containsVidBom(String url) { - url = url; - final list = ["vidbam", "vadbam", "vidbom", "vidbm"]; - for (var n in list) { - if (url.contains(n)) { - return true; - } - } - return false; - } -} - -OkAnime main(MSource source) { - return OkAnime(source: source); -} diff --git a/dart/anime/src/ar/okanime/source.dart b/dart/anime/src/ar/okanime/source.dart deleted file mode 100644 index 1587a4da..00000000 --- a/dart/anime/src/ar/okanime/source.dart +++ /dev/null @@ -1,17 +0,0 @@ -import '../../../../../model/source.dart'; - -Source get okanimeSource => _okanimeSource; -const _okanimeVersion = "0.0.6"; -const _okanimeSourceCodeUrl = - "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/ar/okanime/okanime.dart"; -Source _okanimeSource = Source( - name: "Okanime", - baseUrl: "https://www.okanime.xyz", - lang: "ar", - typeSource: "single", - iconUrl: - "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/ar/okanime/icon.png", - sourceCodeUrl: _okanimeSourceCodeUrl, - version: _okanimeVersion, - itemType: ItemType.anime, -); diff --git a/dart/anime/src/de/animetoast/animetoast.dart b/dart/anime/src/de/animetoast/animetoast.dart deleted file mode 100644 index 6df66300..00000000 --- a/dart/anime/src/de/animetoast/animetoast.dart +++ /dev/null @@ -1,250 +0,0 @@ -import 'package:mangayomi/bridge_lib.dart'; - -class AnimeToast extends MProvider { - AnimeToast({required this.source}); - - MSource source; - - final Client client = Client(); - - @override - bool get supportsLatest => false; - - @override - String get baseUrl => source.baseUrl; - - @override - Future getPopular(int page) async { - final res = (await client.get(Uri.parse(baseUrl))).body; - final document = parseHtml(res); - final elements = document.select("div.row div.col-md-4 div.video-item"); - List animeList = []; - for (var element in elements) { - MManga anime = MManga(); - anime.name = element.selectFirst("div.item-thumbnail a").attr("title"); - anime.link = getUrlWithoutDomain( - element.selectFirst("div.item-thumbnail a").attr("href"), - ); - anime.imageUrl = element - .selectFirst("div.item-thumbnail a img") - .attr("src"); - animeList.add(anime); - } - return MPages(animeList, false); - } - - @override - Future search(String query, int page, FilterList filterList) async { - final res = - (await client.get(Uri.parse("$baseUrl/page/$page/?s=$query"))).body; - final document = parseHtml(res); - final elements = document.select("div.item-thumbnail a[href]"); - List animeList = []; - for (var element in elements) { - MManga anime = MManga(); - anime.name = element.attr("title"); - anime.link = getUrlWithoutDomain(element.attr("href")); - anime.imageUrl = element.selectFirst("a img").attr("src"); - animeList.add(anime); - } - return MPages( - animeList, - document.selectFirst("li.next a")?.attr("href") != null, - ); - } - - @override - Future getDetail(String url) async { - MManga anime = MManga(); - final res = (await client.get(Uri.parse("$baseUrl$url"))).body; - final document = parseHtml(res); - anime.imageUrl = document.selectFirst(".item-content p img").attr("src"); - anime.genre = - (document.xpathFirst('//p[contains(text(),"Genre:")]/text()') ?? "") - .replaceAll("Genre:", "") - .split(","); - anime.description = document.selectFirst("div.item-content div + p").text; - final categoryTag = document.xpath('//*[@rel="category tag"]/text()'); - if (categoryTag.isNotEmpty) { - if (categoryTag.contains("Airing")) { - anime.status = MStatus.ongoing; - } else { - anime.status = MStatus.completed; - } - } - List? episodesList = []; - if (categoryTag.contains("Serie")) { - List elements = []; - if (document.selectFirst("#multi_link_tab0")?.attr("id") != null) { - elements = document.select("#multi_link_tab0"); - } else { - elements = document.select("#multi_link_tab1"); - } - - for (var element in elements) { - final episodeElement = element.selectFirst("a"); - final epT = episodeElement.text; - if (epT.contains(":") || epT.contains("-")) { - final url = episodeElement.attr("href"); - final document = parseHtml((await client.get(Uri.parse(url))).body); - final nUrl = document.selectFirst("#player-embed a").attr("href"); - final nDoc = parseHtml((await client.get(Uri.parse(nUrl))).body); - final nEpEl = nDoc.select("div.tab-pane a"); - for (var epElement in nEpEl) { - MChapter ep = MChapter(); - ep.name = epElement.text; - ep.url = getUrlWithoutDomain(epElement.attr("href")); - episodesList.add(ep); - } - } else { - final episodeElements = element.select("a"); - for (var epElement in episodeElements) { - MChapter ep = MChapter(); - ep.name = epElement.text; - ep.url = getUrlWithoutDomain(epElement.attr("href")); - episodesList.add(ep); - } - } - } - } else { - MChapter ep = MChapter(); - ep.name = document.selectFirst("h1.light-title")?.text ?? "Film"; - ep.url = getUrlWithoutDomain( - document.selectFirst("link[rel=canonical]").attr("href"), - ); - episodesList.add(ep); - } - anime.chapters = episodesList.reversed.toList(); - return anime; - } - - List preferenceHosterSelection() { - return getPreferenceValue(source.id, "hoster_selection"); - } - - @override - Future> getVideoList(String url) async { - final res = (await client.get(Uri.parse("$baseUrl$url"))).body; - final document = parseHtml(res); - final fEp = document.selectFirst("div.tab-pane"); - List videos = []; - List ep = []; - int epcu = 100; - - if (fEp.text.contains(":") || fEp.text.contains("-")) { - final tx = document.select("div.tab-pane"); - - for (var e in tx) { - final sUrl = e.selectFirst("a").attr("href"); - final doc = parseHtml((await client.get(Uri.parse(sUrl))).body); - final nUrl = doc.selectFirst("#player-embed a").attr("href"); - final nDoc = parseHtml((await client.get(Uri.parse(nUrl))).body); - epcu = - int.tryParse( - substringAfter( - document.selectFirst("div.tab-pane a.current-link")?.text ?? "", - "Ep.", - ), - ) ?? - 100; - ep = nDoc.select("div.tab-pane a"); - } - } else { - epcu = - int.tryParse( - substringAfter( - document.selectFirst("div.tab-pane a.current-link")?.text ?? "", - "Ep.", - ), - ) ?? - 100; - ep = document.select("div.tab-pane a"); - } - final hosterSelection = preferenceHosterSelection(); - for (var e in ep) { - if (int.tryParse(substringAfter(e.text, "Ep.")) == epcu) { - final epUrl = e.attr("href"); - final newdoc = parseHtml((await client.get(Uri.parse(epUrl))).body); - final elements = newdoc.select("#player-embed"); - for (var element in elements) { - final link = element.selectFirst("a").getHref ?? ""; - if (link.contains("https://voe.sx") && - hosterSelection.contains("voe")) { - videos.addAll(await voeExtractor(link, "Voe")); - } - } - for (var element in elements) { - List a = []; - final link = element.selectFirst("iframe").getSrc ?? ""; - if ((link.contains("https://dood") || - link.contains("https://ds2play") || - link.contains("https://d0")) && - hosterSelection.contains("dood")) { - a = await doodExtractor(link, "DoodStream"); - } else if (link.contains("filemoon") && - hosterSelection.contains("filemoon")) { - a = await filemoonExtractor(link, "", ""); - } else if (link.contains("mp4upload") && - hosterSelection.contains("mp4upload")) { - a = await mp4UploadExtractor(url, null, "", ""); - } - videos.addAll(a); - } - } - } - return sortVideos(videos); - } - - List sortVideos(List videos) { - String server = getPreferenceValue(source.id, "preferred_hoster"); - - videos.sort((MVideo a, MVideo b) { - int qualityMatchA = 0; - if (a.quality.toLowerCase().contains(server)) { - qualityMatchA = 1; - } - int qualityMatchB = 0; - if (b.quality.toLowerCase().contains(server)) { - qualityMatchB = 1; - } - if (qualityMatchA != qualityMatchB) { - return qualityMatchB - qualityMatchA; - } - - final regex = RegExp(r'(\d+)p'); - final matchA = regex.firstMatch(a.quality); - final matchB = regex.firstMatch(b.quality); - final int qualityNumA = int.tryParse(matchA?.group(1) ?? '0') ?? 0; - final int qualityNumB = int.tryParse(matchB?.group(1) ?? '0') ?? 0; - return qualityNumB - qualityNumA; - }); - - return videos; - } - - @override - List getSourcePreferences() { - return [ - ListPreference( - key: "preferred_hoster", - title: "Standard-Hoster", - summary: "", - valueIndex: 0, - entries: ["Voe", "DoodStream", "Filemoon", "Mp4upload"], - entryValues: ["voe", "doodStream", "filemoon", "mp4upload"], - ), - MultiSelectListPreference( - key: "hoster_selection", - title: "Hoster auswählen", - summary: "", - entries: ["Voe", "DoodStream", "Filemoon", "Mp4upload"], - entryValues: ["voe", "dood", "filemoon", "mp4upload"], - values: ["voe", "dood", "filemoon", "mp4upload"], - ), - ]; - } -} - -AnimeToast main(MSource source) { - return AnimeToast(source: source); -} diff --git a/dart/anime/src/de/animetoast/icon.png b/dart/anime/src/de/animetoast/icon.png deleted file mode 100644 index 172ddf3b..00000000 Binary files a/dart/anime/src/de/animetoast/icon.png and /dev/null differ diff --git a/dart/anime/src/de/animetoast/source.dart b/dart/anime/src/de/animetoast/source.dart deleted file mode 100644 index ee20039f..00000000 --- a/dart/anime/src/de/animetoast/source.dart +++ /dev/null @@ -1,17 +0,0 @@ -import '../../../../../model/source.dart'; - -Source get animetoast => _animetoast; -const _animetoastVersion = "0.0.25"; -const _animetoastCodeUrl = - "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/de/animetoast/animetoast.dart"; -Source _animetoast = Source( - name: "AnimeToast", - baseUrl: "https://animetoast.cc", - lang: "de", - typeSource: "single", - iconUrl: - "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/de/animetoast/icon.png", - sourceCodeUrl: _animetoastCodeUrl, - version: _animetoastVersion, - itemType: ItemType.anime, -); diff --git a/dart/anime/src/en/animepahe/animepahe.dart b/dart/anime/src/en/animepahe/animepahe.dart deleted file mode 100644 index 71d6ea23..00000000 --- a/dart/anime/src/en/animepahe/animepahe.dart +++ /dev/null @@ -1,415 +0,0 @@ -import 'package:mangayomi/bridge_lib.dart'; -import 'dart:convert'; -import 'dart:math'; - -class AnimePahe extends MProvider { - AnimePahe(this.source); - - final MSource source; - - final Client client = Client(); - - @override - String get baseUrl => getPreferenceValue(source.id, "preferred_domain"); - - @override - Map get headers => {'cookie': '__ddg1_=;__ddg2_=;'}; - - @override - Future getPopular(int page) async { - return await getLatestUpdates(page); - } - - @override - Future getLatestUpdates(int page) async { - final res = (await client.get( - Uri.parse("$baseUrl/api?m=airing&page=$page"), - headers: headers, - )).body; - final jsonResult = json.decode(res); - final hasNextPage = jsonResult["current_page"] < jsonResult["last_page"]; - List animeList = []; - for (var item in jsonResult["data"]) { - MManga anime = MManga(); - anime.name = item["anime_title"]; - anime.imageUrl = item["snapshot"]; - anime.link = "/anime/?anime_id=${item["id"]}&name=${item["anime_title"]}"; - anime.artist = item["fansub"]; - animeList.add(anime); - } - return MPages(animeList, hasNextPage); - } - - @override - Future search(String query, int page, FilterList filterList) async { - final res = (await client.get( - Uri.parse("$baseUrl/api?m=search&l=8&q=$query"), - headers: headers, - )).body; - final jsonResult = json.decode(res); - List animeList = []; - for (var item in jsonResult["data"]) { - MManga anime = MManga(); - anime.name = item["title"]; - anime.imageUrl = item["poster"]; - anime.link = "/anime/?anime_id=${item["id"]}&name=${item["title"]}"; - animeList.add(anime); - } - return MPages(animeList, false); - } - - @override - Future getDetail(String url) async { - final statusList = [ - {"Currently Airing": 0, "Finished Airing": 1}, - ]; - MManga anime = MManga(); - final id = substringBefore(substringAfterLast(url, "?anime_id="), "&name="); - final name = substringAfterLast(url, "&name="); - final session = await getSession(name, id); - final res = (await client.get( - Uri.parse("$baseUrl/anime/$session?anime_id=$id"), - headers: headers, - )).body; - final document = parseHtml(res); - final status = - (document.xpathFirst('//div/p[contains(text(),"Status:")]/text()') ?? - "") - .replaceAll("Status:\n", "") - .trim(); - anime.status = parseStatus(status, statusList); - - anime.name = document.selectFirst("div.title-wrapper > h1 > span").text; - anime.author = - (document.xpathFirst('//div/p[contains(text(),"Studio:")]/text()') ?? - "") - .replaceAll("Studio:\n", "") - .trim(); - anime.imageUrl = document.selectFirst("div.anime-poster a").attr("href"); - anime.genre = xpath( - res, - '//*[contains(@class,"anime-genre")]/ul/li/text()', - ); - final synonyms = - (document.xpathFirst('//div/p[contains(text(),"Synonyms:")]/text()') ?? - "") - .replaceAll("Synonyms:\n", "") - .trim(); - anime.description = document.selectFirst("div.anime-summary").text; - if (synonyms.isNotEmpty) { - anime.description += "\n\n$synonyms"; - } - final epUrl = "$baseUrl/api?m=release&id=$session&sort=episode_desc&page=1"; - final resEp = (await client.get(Uri.parse(epUrl), headers: headers)).body; - final episodes = await recursivePages(epUrl, resEp, session); - - anime.chapters = episodes; - return anime; - } - - Future> recursivePages( - String url, - String res, - String session, - ) async { - final jsonResult = json.decode(res); - final page = jsonResult["current_page"]; - final hasNextPage = page < jsonResult["last_page"]; - List animeList = []; - for (var item in jsonResult["data"]) { - MChapter episode = MChapter(); - episode.name = "Episode ${item["episode"]}"; - episode.url = "/play/$session/${item["session"]}"; - episode.dateUpload = parseDates( - [item["created_at"]], - "yyyy-MM-dd HH:mm:ss", - "en", - )[0]; - animeList.add(episode); - } - if (hasNextPage) { - final newUrl = "${substringBeforeLast(url, "&page=")}&page=${page + 1}"; - final newRes = (await client.get( - Uri.parse(newUrl), - headers: headers, - )).body; - animeList.addAll(await recursivePages(newUrl, newRes, session)); - } - return animeList; - } - - Future getSession(String title, String animeId) async { - final noRedirect = Client( - source, - json.encode({"followRedirects": false, "useDartHttpClient": true}), - ); - - final res = await noRedirect.get( - Uri.parse("$baseUrl/a/$animeId"), - headers: headers, - ); - - final location = - "https://${substringAfterLast(getMapValue(json.encode(res.headers), "location"), "https://")}"; - - if (location == '$baseUrl/anime') { - final res = (await client.get( - Uri.parse("$baseUrl/api?m=search&q=$title"), - headers: headers, - )).body; - return substringBefore( - substringAfter( - substringAfter(res, "\"id\":$animeId"), - "\"session\":\"", - ), - "\"", - ); - } - return substringAfterLast(location, '/'); - } - - @override - Future> getVideoList(String url) async { - //by default we use rhttp package but it does not support `followRedirects` - //so setting `useDartHttpClient` to true allows us to use a Dart http package that supports `followRedirects` - final client = Client(source, json.encode({"useDartHttpClient": true})); - final res = (await client.get(Uri.parse("$baseUrl$url"), headers: headers)); - final document = parseHtml(res.body); - final downloadLinks = document.select("div#pickDownload > a"); - final buttons = document.select("div#resolutionMenu > button"); - List videos = []; - - for (var i = 0; i < buttons.length; i++) { - final btn = buttons[i]; - final audio = btn.attr( - "data-audio", - ); // Get audio type (jpn/eng). Japanese or Dubbed. - final kwikLink = btn.attr("data-src"); - final quality = btn.text; - final paheWinLink = downloadLinks[i].attr("href"); - - if (getPreferenceValue(source.id, "preffered_link_type")) { - final noRedirectClient = Client( - source, - json.encode({"followRedirects": false, "useDartHttpClient": true}), - ); - final kwikHeaders = (await noRedirectClient.get( - Uri.parse("${paheWinLink}/i"), - )).headers; - final kwikUrl = - "https://${substringAfterLast(getMapValue(json.encode(kwikHeaders), "location"), "https://")}"; - final reskwik = (await client.get( - Uri.parse(kwikUrl), - headers: {"Referer": "https://kwik.cx/"}, - )); - final matches = RegExp( - r'\("(\S+)",\d+,"(\S+)",(\d+),(\d+)', - ).firstMatch(reskwik.body); - final token = decrypt( - matches!.group(1)!, - matches.group(2)!, - matches.group(3)!, - int.parse(matches.group(4)!), - ); - final url = RegExp(r'action="([^"]+)"').firstMatch(token)!.group(1)!; - final tok = RegExp(r'value="([^"]+)"').firstMatch(token)!.group(1)!; - var code = 419; - var tries = 0; - String location = ""; - - while (code != 302 && tries < 20) { - String cookie = getMapValue( - json.encode(res.request.headers), - "cookie", - ); - cookie += - "; ${getMapValue(json.encode(reskwik.headers), "set-cookie").replaceAll("path=/;", "")}"; - final resNo = - await Client( - source, - json.encode({ - "followRedirects": false, - "useDartHttpClient": true, - }), - ).post( - Uri.parse(url), - headers: { - "referer": reskwik.request.url.toString(), - "cookie": cookie, - "user-agent": getMapValue( - json.encode(res.request.headers), - "user-agent", - ), - }, - body: {"_token": tok}, - ); - code = resNo.statusCode; - tries++; - location = getMapValue(json.encode(resNo.headers), "location"); - } - if (tries > 19) { - throw ("Failed to extract the stream uri from kwik."); - } - MVideo video = MVideo(); - video - ..url = location - ..originalUrl = location - ..quality = quality; - videos.add(video); - } else { - final ress = (await client.get( - Uri.parse(kwikLink), - headers: {"Referer": "https://animepahe.com"}, - )); - final script = substringAfterLast( - xpath( - ress.body, - '//script[contains(text(),"eval(function")]/text()', - ).first, - "eval(function(", - ); - final videoUrl = substringBefore( - substringAfter( - unpackJsAndCombine("eval(function($script"), - "const source=\\'", - ), - "\\';", - ); - MVideo video = MVideo(); - video - ..url = videoUrl - ..originalUrl = videoUrl - ..quality = quality - ..headers = {"referer": "https://kwik.cx"}; - videos.add(video); - } - } - return sortVideos(videos); - } - - String getString(String ctn, int sep) { - int b = 10; - String cm = - "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/"; - final n = cm.substring(0, b); - double mx = 0; - for (var index = 0; index < ctn.length; index++) { - mx += - (int.tryParse(ctn[ctn.length - index - 1], radix: 10) ?? 0.0) - .toInt() * - (pow(sep, index)); - } - var m = ''; - while (mx > 0) { - m = n[(mx % b).toInt()] + m; - mx = (mx - (mx % b)) / b; - } - return m.isNotEmpty ? m : '0'; - } - - String decrypt(String fS, String key, String v1, int v2) { - var html = ""; - var i = 0; - final ld = int.parse(v1); - while (i < fS.length) { - var s = ""; - while (fS[i] != key[v2]) { - s += fS[i]; - i++; - } - for (var index = 0; index < key.length; index++) { - s = s.replaceAll(key[index], index.toString()); - } - html += String.fromCharCode(int.parse(getString(s, v2)) - ld); - i++; - } - - return html; - } - - List sortVideos(List videos) { - String quality = getPreferenceValue(source.id, "preferred_quality"); - String preferredAudio = getPreferenceValue( - source.id, - "preferred_audio", - ); // get user's audio preference - - videos.sort((MVideo a, MVideo b) { - // Prioritize audio first. - // Preferred Audio: Videos with matching preferred audio are ranked highest. - int audioMatchA = a.quality.contains(preferredAudio) ? 1 : 0; - int audioMatchB = b.quality.contains(preferredAudio) ? 1 : 0; - if (audioMatchA != audioMatchB) { - return audioMatchB - audioMatchA; - } - - // quality prioritized next - // Preferred Video Quality: If audio matches, videos with preferred video quality are ranked higher. - int qualityMatchA = 0; - if (a.quality.contains(quality)) { - qualityMatchA = 1; - } - int qualityMatchB = 0; - if (b.quality.contains(quality)) { - qualityMatchB = 1; - } - if (qualityMatchA != qualityMatchB) { - return qualityMatchB - qualityMatchA; - } - - final regex = RegExp(r'(\d+)p'); - final matchA = regex.firstMatch(a.quality); - final matchB = regex.firstMatch(b.quality); - final int qualityNumA = int.tryParse(matchA?.group(1) ?? '0') ?? 0; - final int qualityNumB = int.tryParse(matchB?.group(1) ?? '0') ?? 0; - return qualityNumB - qualityNumA; - }); - - return videos; - } - - @override - List getSourcePreferences() { - return [ - ListPreference( - key: "preferred_domain", - title: "Preferred domain", - summary: "", - valueIndex: 1, - entries: ["animepahe.com", "animepahe.ru", "animepahe.org"], - entryValues: [ - "https://animepahe.com", - "https://animepahe.ru", - "https://animepahe.org", - ], - ), - SwitchPreferenceCompat( - key: "preffered_link_type", - title: "Use HLS links", - summary: "Enable this if you are having Cloudflare issues.", - value: false, - ), - ListPreference( - key: "preferred_quality", - title: "Preferred Quality", - summary: "", - valueIndex: 0, - entries: ["1080p", "720p", "360p"], - entryValues: ["1080", "720", "360"], - ), - - ListPreference( - key: "preferred_audio", // Add new preference for audio - title: "Preferred Audio", - summary: "Select your preferred audio language (Japanese or English).", - valueIndex: 0, // Default to Japanese (or whichever you prefer) - entries: ["Japanese", "English"], - entryValues: ["jpn", "eng"], - ), - ]; - } -} - -AnimePahe main(MSource source) { - return AnimePahe(source); -} diff --git a/dart/anime/src/en/animepahe/icon.png b/dart/anime/src/en/animepahe/icon.png deleted file mode 100644 index 4c3b0098..00000000 Binary files a/dart/anime/src/en/animepahe/icon.png and /dev/null differ diff --git a/dart/anime/src/en/animepahe/source.dart b/dart/anime/src/en/animepahe/source.dart deleted file mode 100644 index 028c1b76..00000000 --- a/dart/anime/src/en/animepahe/source.dart +++ /dev/null @@ -1,17 +0,0 @@ -import '../../../../../model/source.dart'; - -Source get animepaheSource => _animepaheSource; -const _animepaheVersion = "0.0.75"; -const _animepaheSourceCodeUrl = - "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/en/animepahe/animepahe.dart"; -Source _animepaheSource = Source( - name: "AnimePahe", - baseUrl: "https://www.animepahe.ru", - lang: "en", - typeSource: "single", - iconUrl: - "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/en/animepahe/icon.png", - sourceCodeUrl: _animepaheSourceCodeUrl, - version: _animepaheVersion, - itemType: ItemType.anime, -); diff --git a/dart/anime/src/en/donghuastream/donghuastream.dart b/dart/anime/src/en/donghuastream/donghuastream.dart deleted file mode 100644 index afed2002..00000000 --- a/dart/anime/src/en/donghuastream/donghuastream.dart +++ /dev/null @@ -1,241 +0,0 @@ -import 'package:mangayomi/bridge_lib.dart'; -import 'dart:convert'; - -class DonghuaStream extends MProvider { - DonghuaStream({required this.source}); - - MSource source; - - final Client client = Client(source); - - @override - bool get supportsLatest => true; - - @override - Map get headers => {}; - - @override - Future getPopular(int page) async { - final res = (await client.get(Uri.parse("${source.baseUrl}/anime?page=${page}&sub=&order=popular"))).body; - List animeList = []; - final urls = xpath(res, '//article[@class="bs"]/div/a/@href'); - final names = xpath(res, '//article[@class="bs"]/div/a/@title'); - final images = xpath(res, '//article[@class="bs"]/div/a/div/img/@data-src'); - for (var i = 0; i < names.length; i++) { - MManga anime = MManga(); - anime.name = names[i]; - anime.imageUrl = images[i]; - anime.link = urls[i]; - animeList.add(anime); - } - final nextPage = xpath(res, '//a[@class="r"]/@href'); - return MPages(animeList, nextPage.isNotEmpty); - } - - @override - Future getLatestUpdates(int page) async { - final res = (await client.get(Uri.parse("${source.baseUrl}/anime?page=${page}&sub=&order=update"))).body; - List animeList = []; - final urls = xpath(res, '//article[@class="bs"]/div/a/@href'); - final names = xpath(res, '//article[@class="bs"]/div/a/@title'); - final images = xpath(res, '//article[@class="bs"]/div/a/div/img/@data-src'); - for (var i = 0; i < names.length; i++) { - MManga anime = MManga(); - anime.name = names[i]; - anime.imageUrl = images[i]; - anime.link = urls[i]; - animeList.add(anime); - } - final nextPage = xpath(res, '//a[@class="r"]/@href'); - return MPages(animeList, nextPage.isNotEmpty); - } - - @override - Future search(String query, int page, FilterList filterList) async { - final res = (await client.get(Uri.parse("${source.baseUrl}/page/${page}/?s=${query}"))).body; - List animeList = []; - final urls = xpath(res, '//article[@class="bs"]/div/a/@href'); - final names = xpath(res, '//article[@class="bs"]/div/a/@title'); - final images = xpath(res, '//article[@class="bs"]/div/a/div/img/@src'); - for (var i = 0; i < names.length; i++) { - MManga anime = MManga(); - anime.name = names[i]; - anime.imageUrl = images[i]; - anime.link = urls[i]; - animeList.add(anime); - } - final nextPage = xpath(res, '//a[@class="next page-numbers"]/@href'); - return MPages(animeList, nextPage.isNotEmpty); - } - - @override - Future getDetail(String url) async { - final res = (await client.get(Uri.parse(url))).body; - MManga anime = MManga(); - var genre = xpath(res,'//div[@class="genxed"]/a/text()'); - genre.remove('MY FAVOURITE'); - anime.genre = genre; - anime.description = xpath(res,'//div[@class="entry-content"]/p/text()').join("\n"); - - final statusList = [{"Status: Ongoing": 0, "Status: Completed": 1}]; - final infoContent = xpath(res,'//div[@class="info-content"]/div[@class="spe"]/span/text()'); - anime.status = parseStatus(infoContent[0], statusList); - anime.author = infoContent[1].replaceFirst('Network: ','').replaceFirst('Donghua Stream, ',''); - anime.artist = infoContent[2].replaceFirst('Studio: ',''); - final epElements = parseHtml(res).select('div.eplister > ul > li >a'); - List? episodesList = []; - - for (var epElement in epElements) { - final number = epElement.selectFirst("div.epl-num").text; - final title = epElement.selectFirst("div.epl-title").text; - final dateString = epElement.selectFirst("div.epl-date").text; - MChapter episode = MChapter(); - episode.name = "Episode $number"; - episode.url = epElement.getHref; - episode.dateUpload = parseDates([dateString],"MMMM d, yyyy","en",)[0]; - episodesList.add(episode); - } - anime.chapters = episodesList; - return anime; - } - - - // For anime episode video list - @override - Future> getVideoList(String url) async { - final res = (await client.get(Uri.parse(url))).body; - var servers = parseHtml(res).select('select.mirror > option[data-index]'); - if(servers.length==0){ - final src_data = parseHtml(res).selectFirst('article[id] > script').attr('src').replaceAll ('data:text/javascript;base64,',''); - final src_function = utf8.decode(base64Url.decode(src_data)); - final html_data = RegExp(r'"html":"(.*?)","autoplayIndex"').firstMatch(src_function).group(1); - servers = parseHtml(html_data.replaceAll(r'\t', '\t').replaceAll(r'\n', '\n').replaceAll(r'\"', '"').replaceAll(r'\/', '/')).select('select.mirror > option[data-index]'); - } - List videos = []; - for (var i = 0; i < servers.length; i++) { - String name = '${servers[i].attr("data-index")}: ${servers[i].text}'; - String valueHtml = utf8.decode(base64Url.decode(servers[i].attr('value'))); - final serverUrlList = xpath(valueHtml,'//iframe/@src'); - if (serverUrlList.length>0){ - String serverUrl = serverUrlList[0]; - if(serverUrl.contains('https://geo.dailymotion.com/player')){ - String videoId = RegExp(r'[?&]video=([a-zA-Z0-9]+)').firstMatch(serverUrl).group(1); - videos.addAll(await dailymotionVideosFetcher(videoId,name)); - }else if(serverUrl.contains('//play.streamplay.co.in/')){ - String videoId = serverUrl.split('/embed/')[1]; - videos.addAll(await streamplayVideosFetcher(videoId,name)); - } - } - } - return videos; - } - - Future> dailymotionVideosFetcher(String videoID, String name) async { - String metaDataUrl = 'https://www.dailymotion.com/player/metadata/video/$videoID'; - final res = (await client.get(Uri.parse(metaDataUrl))).body; - final jsonRes = json.decode(res); - String masterUrl = jsonRes["qualities"]["auto"][0]["url"]; - return m3u8extractor(masterUrl, name); - } - - Future> streamplayVideosFetcher(String videoID, String name) async { - String url = 'https://play.streamplay.co.in/embed/'+videoID; - final res = (await client.get(Uri.parse(url))).body; - final match = RegExp(r"eval\(function\(p,a,c,k,e,d\)\{[\s\S]*?\}\((.*?)\)").firstMatch(res); - if (match == null) { - return []; - } - final argsStr = match.group(1); - final argsPattern = RegExp(r"'(.*?)',(.*?),(.*?),'(.*?)'\.split"); - final argsMatches = argsPattern.firstMatch(argsStr); - final arg_p = argsMatches.group(1); - final arg_a =int.parse(argsMatches.group(2)); - final arg_c =int.parse(argsMatches.group(3)); - final arg_k =argsMatches.group(4).split('|').toList(); - final unpacked_js = unpack(arg_p,arg_a,arg_c,arg_k); - final kakenMatch = RegExp(r'window\.kaken\s*=\s*"([^"]+)"').firstMatch(unpacked_js); - if (kakenMatch == null) { - return []; - } - final kakenValue = kakenMatch.group(1); - final apiUrl = 'https://play.streamplay.co.in/api/?$kakenValue'; - final apiRes = (await client.get(Uri.parse(apiUrl))).body; - final jsonRes = json.decode(apiRes); - String masterUrl = jsonRes['sources'][0]['file']; - List subtitles = []; - for (final track in jsonRes['tracks']){ - MTrack subtitle = MTrack(); - subtitle.label = name + ' - ' + track['label']; - subtitle.file = track['file']; - subtitles.add(subtitle); - } - List videos = await m3u8extractor(masterUrl, name); - if(videos.length>0){ - videos[0].subtitles = subtitles; - } - return videos; - } - - String unpack(String p, int a, int c, List k) { - for (int i = c - 1; i >= 0; i--) { - String word = (i < k.length) ? k[i] : baseN(i, a); - String pattern = r'\b' + baseN(i, a) + r'\b'; - p = p.replaceAll(RegExp(pattern), word); - } - return p; - } - - String baseN(int num, int base) { - const digits = '0123456789abcdefghijklmnopqrstuvwxyz'; - if (num == 0) return '0'; - String result = ''; - while (num > 0) { - result = digits[num % base] + result; - num ~/= base; - } - return result; - } - - Future> m3u8extractor(String masterUrl, String name) async { - List videos = []; - List subtitles = []; - final masterPlaylistRes = (await client.get(Uri.parse(masterUrl), headers: headers)).body; - final subtitleRegExp = RegExp(r'#EXT-X-MEDIA:TYPE=SUBTITLES.*?NAME="(.*?)".*?URI="(.*?)"', dotAll: true); - for (final match in subtitleRegExp.allMatches(masterPlaylistRes)) { - MTrack subtitle = MTrack(); - subtitle.label = name + ' - ' + match.group(1) ?? 'Subtitle'; - subtitle.file = match.group(2) ?? ''; - subtitles.add(subtitle); - } - - for (var it in substringAfter( - masterPlaylistRes, - "#EXT-X-STREAM-INF:", - ).split("#EXT-X-STREAM-INF:")) { - final quality = - "${substringBefore(substringBefore(substringAfter(substringAfter(it, "RESOLUTION="), "x"), ","), "\n")}p"; - - String videoUrl = substringBefore(substringAfter(it, "\n"), "\n"); - - if (!videoUrl.startsWith("http")) { - videoUrl = - "${masterUrl.split("/").sublist(0, masterUrl.split("/").length - 1).join("/")}/$videoUrl"; - } - - MVideo video = MVideo(); - video - ..url = videoUrl - ..originalUrl = videoUrl - ..quality = "$name - $quality"; - videos.add(video); - } - if(videos.length>0){ - videos[0].subtitles = subtitles; - } - return videos; - } -} - -DonghuaStream main(MSource source) { - return DonghuaStream(source:source); -} \ No newline at end of file diff --git a/dart/anime/src/en/donghuastream/icon.png b/dart/anime/src/en/donghuastream/icon.png deleted file mode 100644 index 7340cd62..00000000 Binary files a/dart/anime/src/en/donghuastream/icon.png and /dev/null differ diff --git a/dart/anime/src/en/donghuastream/source.dart b/dart/anime/src/en/donghuastream/source.dart deleted file mode 100644 index 0f461706..00000000 --- a/dart/anime/src/en/donghuastream/source.dart +++ /dev/null @@ -1,17 +0,0 @@ -import '../../../../../model/source.dart'; - -Source get donghuastreamSource => _donghuastreamSource; -const _donghuastreamVersion = "0.0.2"; -const _donghuastreamSourceCodeUrl = - "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/en/donghuastream/donghuastream.dart"; -Source _donghuastreamSource = Source( - name: "DonghuaStream", - baseUrl: "https://donghuastream.org", - lang: "en", - typeSource: "single", - iconUrl: - "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/en/donghuastream/icon.png", - sourceCodeUrl: _donghuastreamSourceCodeUrl, - version: _donghuastreamVersion, - itemType: ItemType.anime, -); diff --git a/dart/anime/src/en/gogoanime/gogoanime.dart b/dart/anime/src/en/gogoanime/gogoanime.dart deleted file mode 100644 index d9aff598..00000000 --- a/dart/anime/src/en/gogoanime/gogoanime.dart +++ /dev/null @@ -1,1149 +0,0 @@ -import 'package:mangayomi/bridge_lib.dart'; -import 'dart:convert'; - -class GogoAnime extends MProvider { - GogoAnime({required this.source}); - - MSource source; - - final Client client = Client(); - - @override - String get baseUrl => - getPreferenceValue(source.id, "override_baseurl_v${source.id}"); - - @override - Future getPopular(int page) async { - final res = - (await client.get(Uri.parse("$baseUrl/popular.html?page=$page"))).body; - - List animeList = []; - final urls = xpath(res, '//*[@class="img"]/a/@href'); - final names = xpath(res, '//*[@class="img"]/a/@title'); - final images = xpath(res, '//*[@class="img"]/a/img/@src'); - - for (var i = 0; i < names.length; i++) { - MManga anime = MManga(); - anime.name = names[i]; - anime.imageUrl = images[i]; - anime.link = urls[i]; - animeList.add(anime); - } - - return MPages(animeList, true); - } - - @override - Future getLatestUpdates(int page) async { - String url = baseUrl; - if (baseUrl.toLowerCase().contains("gogo")) { - url = url + "/?page=$page"; - } else { - url = url + "/home.html?page=$page"; - } - final res = (await client.get(Uri.parse(url))).body; - final document = parseHtml(res); - final elements = document.select("div.img a"); - List animeList = []; - - for (var element in elements) { - var anime = MManga(); - anime.name = element.attr("title"); - anime.imageUrl = element.selectFirst("img")?.attr("src") ?? ""; - final slug = substringBefore(element.attr("href"), "-episode-"); - anime.link = "/category/$slug"; - animeList.add(anime); - } - - return MPages(animeList, true); - } - - @override - Future search(String query, int page, FilterList filterList) async { - final filters = filterList.filters; - String filterStr = ""; - String url = ""; - - String genre = ""; - String recent = ""; - String season = ""; - - for (var filter in filters) { - if (filter.type == "SortFilter") { - final sort = filter.values[filter.state].value; - filterStr += "&sort=$sort"; - } else if (filter.type == "GenreFilter") { - final genre = (filter.state as List).where((e) => e.state).toList(); - if (genre.isNotEmpty) { - for (var st in genre) { - filterStr += "&genre[]=${st.value}"; - } - } - } else if (filter.type == "CountryFilter") { - final country = (filter.state as List).where((e) => e.state).toList(); - if (country.isNotEmpty) { - for (var st in country) { - filterStr += "&country[]=${st.value}"; - } - } - } else if (filter.type == "SeasonFilter") { - final season = (filter.state as List).where((e) => e.state).toList(); - if (season.isNotEmpty) { - for (var st in season) { - filterStr += "&season[]=${st.value}"; - } - } - } else if (filter.type == "YearFilter") { - final year = (filter.state as List).where((e) => e.state).toList(); - if (year.isNotEmpty) { - for (var st in year) { - filterStr += "&year[]=${st.value}"; - } - } - } else if (filter.type == "TypeFilter") { - final type = (filter.state as List).where((e) => e.state).toList(); - if (type.isNotEmpty) { - for (var st in type) { - filterStr += "&type[]=${st.value}"; - } - } - } else if (filter.type == "StatusFilter") { - final status = (filter.state as List).where((e) => e.state).toList(); - if (status.isNotEmpty) { - for (var st in status) { - filterStr += "&status[]=${st.value}"; - } - } - } else if (filter.type == "LanguageFilter") { - final language = (filter.state as List).where((e) => e.state).toList(); - if (language.isNotEmpty) { - for (var st in language) { - filterStr += "&language[]=${st.value}"; - } - } - } - if (filter.type == "GenreIFilter") { - genre = filter.values[filter.state].value; - } else if (filter.type == "RecentFilter") { - recent = filter.values[filter.state].value; - } else if (filter.type == "SeasonIFilter") { - season = filter.values[filter.state].value; - } - } - if (genre.isNotEmpty) { - url = "$baseUrl/genre/$genre?page=$page"; - } else if (recent.isNotEmpty) { - url = - "https://ajax.gogo-load.com/ajax/page-recent-release.html?page=$page&type=$recent"; - } else if (season.isNotEmpty) { - url = "$baseUrl/$season?page=$page"; - } else { - url = "$baseUrl/filter.html?keyword=$query$filterStr&page=$page"; - } - - final res = (await client.get(Uri.parse(url))).body; - - List animeList = []; - final urls = xpath(res, '//*[@class="img"]/a/@href'); - final names = xpath(res, '//*[@class="img"]/a/@title'); - final images = xpath(res, '//*[@class="img"]/a/img/@src'); - - for (var i = 0; i < names.length; i++) { - MManga anime = MManga(); - anime.name = names[i]; - anime.imageUrl = images[i]; - anime.link = urls[i]; - animeList.add(anime); - } - - return MPages(animeList, true); - } - - @override - Future getDetail(String url) async { - final statusList = [ - {"Ongoing": 0, "Completed": 1}, - ]; - - final res = (await client.get(Uri.parse("$baseUrl$url"))).body; - MManga anime = MManga(); - final status = xpath( - res, - '//*[@class="anime_info_body_bg"]/p[@class="type"][5]/text()', - ).first.replaceAll("Status: ", ""); - anime.description = - parseHtml( - res, - ).selectFirst("div.anime_info_body_bg > div.description")?.text ?? - ""; - anime.status = parseStatus(status, statusList); - anime.genre = xpath( - res, - '//*[@class="anime_info_body_bg"]/p[@class="type"][3]/text()', - ).first.replaceAll("Genre: ", "").split(","); - - final id = xpath(res, '//*[@id="movie_id"]/@value').first; - final urlEp = - "https://ajax.gogocdn.net/ajax/load-list-episode?ep_start=0&ep_end=4000&id=$id"; - - final resEp = (await client.get(Uri.parse(urlEp))).body; - - final epUrls = xpath(resEp, '//*[@id="episode_related"]/li/a/@href'); - final names = xpath( - resEp, - '//*[@id="episode_related"]/li/a/div[@class="name"]/text()', - ); - List episodes = []; - - for (var a in names) { - episodes.add("Episode ${substringAfterLast(a, ' ')}"); - } - List? episodesList = []; - for (var i = 0; i < episodes.length; i++) { - MChapter episode = MChapter(); - episode.name = episodes[i]; - episode.url = epUrls[i]; - episodesList.add(episode); - } - - anime.chapters = episodesList; - return anime; - } - - @override - Future> getVideoList(String url) async { - final res = (await client.get(Uri.parse("$baseUrl$url"))).body; - final serverUrls = xpath( - res, - '//*[@class="anime_muti_link"]/ul/li/a/@data-video', - ); - final serverNames = xpath( - res, - '//*[@class="anime_muti_link"]/ul/li/@class', - ); - List videos = []; - final hosterSelection = preferenceHosterSelection(source.id); - for (var i = 0; i < serverNames.length; i++) { - final name = serverNames[i]; - final url = serverUrls[i]; - List a = []; - if (hosterSelection.contains(name)) { - if (name.contains("anime")) { - a = await gogoCdnExtractor(url); - } else if (name.contains("vidcdn")) { - a = await gogoCdnExtractor(url); - } else if (name.contains("doodstream")) { - a = await doodExtractor(url); - } else if (name.contains("mp4upload")) { - a = await mp4UploadExtractor(url, null, "", ""); - } else if (name.contains("filelions")) { - a = await streamWishExtractor(url, "FileLions"); - } else if (name.contains("streamwish")) { - a = await streamWishExtractor(url, "StreamWish"); - } - videos.addAll(a); - } - } - - return sortVideos(videos, source.id); - } - - @override - List getFilterList() { - return [ - HeaderFilter("Advanced search"), - GroupFilter("GenreFilter", "Genre", [ - { - "type": "CheckBox", - "filter": {"name": "Action", "value": "action"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Adult Cast", "value": "adult-cast"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Adventure", "value": "adventure"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Anthropomorphic", "value": "anthropomorphic"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Avant Garde", "value": "avant-garde"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Boys Love", "value": "shounen-ai"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Cars", "value": "cars"}, - }, - { - "type": "CheckBox", - "filter": {"name": "CGDCT", "value": "cgdct"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Childcare", "value": "childcare"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Comedy", "value": "comedy"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Comic", "value": "comic"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Crime", "value": "crime"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Crossdressing", "value": "crossdressing"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Delinquents", "value": "delinquents"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Dementia", "value": "dementia"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Demons", "value": "demons"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Detective", "value": "detective"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Drama", "value": "drama"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Dub", "value": "dub"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Ecchi", "value": "ecchi"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Erotica", "value": "erotica"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Family", "value": "family"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Fantasy", "value": "fantasy"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Gag Humor", "value": "gag-humor"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Game", "value": "game"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Gender Bender", "value": "gender-bender"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Gore", "value": "gore"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Gourmet", "value": "gourmet"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Harem", "value": "harem"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Hentai", "value": "hentai"}, - }, - { - "type": "CheckBox", - "filter": {"name": "High Stakes Game", "value": "high-stakes-game"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Historical", "value": "historical"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Horror", "value": "horror"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Isekai", "value": "isekai"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Iyashikei", "value": "iyashikei"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Josei", "value": "josei"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Kids", "value": "kids"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Magic", "value": "magic"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Magical Sex Shift", "value": "magical-sex-shift"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Mahou Shoujo", "value": "mahou-shoujo"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Martial Arts", "value": "martial-arts"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Mecha", "value": "mecha"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Medical", "value": "medical"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Military", "value": "military"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Music", "value": "music"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Mystery", "value": "mystery"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Mythology", "value": "mythology"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Organized Crime", "value": "organized-crime"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Parody", "value": "parody"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Performing Arts", "value": "performing-arts"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Pets", "value": "pets"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Police", "value": "police"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Psychological", "value": "psychological"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Racing", "value": "racing"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Reincarnation", "value": "reincarnation"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Romance", "value": "romance"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Romantic Subtext", "value": "romantic-subtext"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Samurai", "value": "samurai"}, - }, - { - "type": "CheckBox", - "filter": {"name": "School", "value": "school"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Sci-Fi", "value": "sci-fi"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Seinen", "value": "seinen"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Shoujo", "value": "shoujo"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Shoujo Ai", "value": "shoujo-ai"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Shounen", "value": "shounen"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Showbiz", "value": "showbiz"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Slice of Life", "value": "slice-of-life"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Space", "value": "space"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Sports", "value": "sports"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Strategy Game", "value": "strategy-game"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Super Power", "value": "super-power"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Supernatural", "value": "supernatural"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Survival", "value": "survival"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Suspense", "value": "suspense"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Team Sports", "value": "team-sports"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Thriller", "value": "thriller"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Time Travel", "value": "time-travel"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Vampire", "value": "vampire"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Visual Arts", "value": "visual-arts"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Work Life", "value": "work-life"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Workplace", "value": "workplace"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Yaoi", "value": "yaoi"}, - }, - { - "type": "CheckBox", - "filter": {"name": "Yuri", "value": "yuri"}, - }, - ]), - GroupFilter("CountryFilter", "Country", [ - CheckBoxFilter("China", "5"), - CheckBoxFilter("Japan", "2"), - ]), - GroupFilter("SeasonFilter", "Season", [ - CheckBoxFilter("Fall", "fall"), - CheckBoxFilter("Summer", "summer"), - CheckBoxFilter("Spring", "spring"), - CheckBoxFilter("Winter", "winter"), - ]), - GroupFilter("YearFilter", "Year", [ - CheckBoxFilter("2024", "2024"), - CheckBoxFilter("2023", "2023"), - CheckBoxFilter("2022", "2022"), - CheckBoxFilter("2021", "2021"), - CheckBoxFilter("2020", "2020"), - CheckBoxFilter("2019", "2019"), - CheckBoxFilter("2018", "2018"), - CheckBoxFilter("2017", "2017"), - CheckBoxFilter("2016", "2016"), - CheckBoxFilter("2015", "2015"), - CheckBoxFilter("2014", "2014"), - CheckBoxFilter("2013", "2013"), - CheckBoxFilter("2012", "2012"), - CheckBoxFilter("2011", "2011"), - CheckBoxFilter("2010", "2010"), - CheckBoxFilter("2009", "2009"), - CheckBoxFilter("2008", "2008"), - CheckBoxFilter("2007", "2007"), - CheckBoxFilter("2006", "2006"), - CheckBoxFilter("2005", "2005"), - CheckBoxFilter("2004", "2004"), - CheckBoxFilter("2003", "2003"), - CheckBoxFilter("2002", "2002"), - CheckBoxFilter("2001", "2001"), - CheckBoxFilter("2000", "2000"), - CheckBoxFilter("1999", "1999"), - ]), - GroupFilter("LanguageFilter", "Language", [ - CheckBoxFilter("Sub & Dub", "subdub"), - CheckBoxFilter("Sub", "sub"), - CheckBoxFilter("Dub", "dub"), - ]), - GroupFilter("TypeFilter", "Type", [ - CheckBoxFilter("Movie", "3"), - CheckBoxFilter("TV", "1"), - CheckBoxFilter("OVA", "26"), - CheckBoxFilter("ONA", "30"), - CheckBoxFilter("Special", "2"), - CheckBoxFilter("Music", "32"), - ]), - GroupFilter("StatusFilter", "Status", [ - CheckBoxFilter("Not Yet Aired", "Upcoming"), - CheckBoxFilter("Ongoing", "Ongoing"), - CheckBoxFilter("Completed", "Completed"), - ]), - SelectFilter("SortFilter", "Sort by", 0, [ - SelectFilterOption("Name A-Z", "title_az"), - SelectFilterOption("Recently updated", "recently_updated"), - SelectFilterOption("Recently added", "recently_added"), - SelectFilterOption("Release date", "release_date"), - ]), - SeparatorFilter(), - HeaderFilter("Select sub-page"), - HeaderFilter("Note: Ignores search & other filters"), - SelectFilter("GenreIFilter", "Genre", 0, [ - { - "type": "SelectOption", - "filter": {"name": "", "value": ""}, - }, - { - "type": "SelectOption", - "filter": {"name": "Action", "value": "action"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Adult Cast", "value": "adult-cast"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Adventure", "value": "adventure"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Anthropomorphic", "value": "anthropomorphic"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Avant Garde", "value": "avant-garde"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Boys Love", "value": "shounen-ai"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Cars", "value": "cars"}, - }, - { - "type": "SelectOption", - "filter": {"name": "CGDCT", "value": "cgdct"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Childcare", "value": "childcare"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Comedy", "value": "comedy"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Comic", "value": "comic"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Crime", "value": "crime"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Crossdressing", "value": "crossdressing"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Delinquents", "value": "delinquents"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Dementia", "value": "dementia"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Demons", "value": "demons"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Detective", "value": "detective"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Drama", "value": "drama"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Dub", "value": "dub"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Ecchi", "value": "ecchi"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Erotica", "value": "erotica"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Family", "value": "family"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Fantasy", "value": "fantasy"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Gag Humor", "value": "gag-humor"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Game", "value": "game"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Gender Bender", "value": "gender-bender"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Gore", "value": "gore"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Gourmet", "value": "gourmet"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Harem", "value": "harem"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Hentai", "value": "hentai"}, - }, - { - "type": "SelectOption", - "filter": {"name": "High Stakes Game", "value": "high-stakes-game"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Historical", "value": "historical"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Horror", "value": "horror"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Isekai", "value": "isekai"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Iyashikei", "value": "iyashikei"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Josei", "value": "josei"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Kids", "value": "kids"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Magic", "value": "magic"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Magical Sex Shift", "value": "magical-sex-shift"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Mahou Shoujo", "value": "mahou-shoujo"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Martial Arts", "value": "martial-arts"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Mecha", "value": "mecha"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Medical", "value": "medical"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Military", "value": "military"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Music", "value": "music"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Mystery", "value": "mystery"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Mythology", "value": "mythology"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Organized Crime", "value": "organized-crime"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Parody", "value": "parody"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Performing Arts", "value": "performing-arts"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Pets", "value": "pets"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Police", "value": "police"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Psychological", "value": "psychological"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Racing", "value": "racing"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Reincarnation", "value": "reincarnation"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Romance", "value": "romance"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Romantic Subtext", "value": "romantic-subtext"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Samurai", "value": "samurai"}, - }, - { - "type": "SelectOption", - "filter": {"name": "School", "value": "school"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Sci-Fi", "value": "sci-fi"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Seinen", "value": "seinen"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Shoujo", "value": "shoujo"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Shoujo Ai", "value": "shoujo-ai"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Shounen", "value": "shounen"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Showbiz", "value": "showbiz"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Slice of Life", "value": "slice-of-life"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Space", "value": "space"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Sports", "value": "sports"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Strategy Game", "value": "strategy-game"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Super Power", "value": "super-power"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Supernatural", "value": "supernatural"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Survival", "value": "survival"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Suspense", "value": "suspense"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Team Sports", "value": "team-sports"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Thriller", "value": "thriller"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Time Travel", "value": "time-travel"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Vampire", "value": "vampire"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Visual Arts", "value": "visual-arts"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Work Life", "value": "work-life"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Workplace", "value": "workplace"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Yaoi", "value": "yaoi"}, - }, - { - "type": "SelectOption", - "filter": {"name": "Yuri", "value": "yuri"}, - }, - ]), - SelectFilter("RecentFilter", "Recent", 0, [ - SelectFilterOption("", ""), - SelectFilterOption("Recent Release", "1"), - SelectFilterOption("Recent Dub", "2"), - SelectFilterOption("Recent Chinese", "3"), - ]), - SelectFilter("SeasonIFilter", "Season", 0, [ - SelectFilterOption("", ""), - SelectFilterOption("Latest season", "new-season.html"), - SelectFilterOption("Summer 2024", "sub-category/summer-2024-anime"), - SelectFilterOption("Spring 2024", "sub-category/spring-2024-anime"), - SelectFilterOption("Winter 2024", "sub-category/winter-2024-anime"), - SelectFilterOption("Summer 2023", "sub-category/summer-2023-anime"), - SelectFilterOption("Spring 2023", "sub-category/spring-2023-anime"), - SelectFilterOption("Winter 2023", "sub-category/winter-2023-anime"), - SelectFilterOption("Fall 2022", "sub-category/fall-2022-anime"), - SelectFilterOption("Summer 2022", "sub-category/summer-2022-anime"), - SelectFilterOption("Spring 2022", "sub-category/spring-2022-anime"), - SelectFilterOption("Winter 2022", "sub-category/winter-2022-anime"), - SelectFilterOption("Fall 2021", "sub-category/fall-2021-anime"), - SelectFilterOption("Summer 2021", "sub-category/summer-2021-anime"), - SelectFilterOption("Spring 2021", "sub-category/spring-2021-anime"), - SelectFilterOption("Winter 2021", "sub-category/winter-2021-anime"), - SelectFilterOption("Fall 2020", "sub-category/fall-2020-anime"), - SelectFilterOption("Summer 2020", "sub-category/summer-2020-anime"), - SelectFilterOption("Spring 2020", "sub-category/spring-2020-anime"), - SelectFilterOption("Winter 2020", "sub-category/winter-2020-anime"), - SelectFilterOption("Fall 2019", "sub-category/fall-2019-anime"), - SelectFilterOption("Summer 2019", "sub-category/summer-2019-anime"), - SelectFilterOption("Spring 2019", "sub-category/spring-2019-anime"), - SelectFilterOption("Winter 2019", "sub-category/winter-2019-anime"), - SelectFilterOption("Fall 2018", "sub-category/fall-2018-anime"), - SelectFilterOption("Summer 2018", "sub-category/summer-2018-anime"), - SelectFilterOption("Spring 2018", "sub-category/spring-2018-anime"), - SelectFilterOption("Winter 2018", "sub-category/winter-2018-anime"), - SelectFilterOption("Fall 2017", "sub-category/fall-2017-anime"), - SelectFilterOption("Summer 2017", "sub-category/summer-2017-anime"), - SelectFilterOption("Spring 2017", "sub-category/spring-2017-anime"), - SelectFilterOption("Winter 2017", "sub-category/winter-2017-anime"), - SelectFilterOption("Fall 2016", "sub-category/fall-2016-anime"), - SelectFilterOption("Summer 2016", "sub-category/summer-2016-anime"), - SelectFilterOption("Spring 2016", "sub-category/spring-2016-anime"), - SelectFilterOption("Winter 2016", "sub-category/winter-2016-anime"), - SelectFilterOption("Fall 2015", "sub-category/fall-2015-anime"), - SelectFilterOption("Summer 2015", "sub-category/summer-2015-anime"), - SelectFilterOption("Spring 2015", "sub-category/spring-2015-anime"), - SelectFilterOption("Winter 2015", "sub-category/winter-2015-anime"), - SelectFilterOption("Fall 2014", "sub-category/fall-2014-anime"), - SelectFilterOption("Summer 2014", "sub-category/summer-2014-anime"), - SelectFilterOption("Spring 2014", "sub-category/spring-2014-anime"), - SelectFilterOption("Winter 2014", "sub-category/winter-2014-anime"), - ]), - ]; - } - - @override - List getSourcePreferences() { - return [ - EditTextPreference( - key: "override_baseurl_v${source.id}", - title: "Override BaseUrl", - summary: - "For temporary uses. Updating the extension will erase this setting.", - value: "https://anitaku.to", - dialogTitle: "Override BaseUrl", - dialogMessage: "Default: https://anitaku.to", - text: "https://anitaku.to", - ), - ListPreference( - key: "preferred_quality", - title: "Preferred quality", - summary: "", - valueIndex: 0, - entries: ["1080p", "720p", "480p", "360p"], - entryValues: ["1080", "720", "480", "360"], - ), - ListPreference( - key: "preferred_server", - title: "Preferred server", - summary: "", - valueIndex: 0, - entries: [ - "Gogostream", - "Vidstreaming", - "Doodstream", - "StreamWish", - "Mp4upload", - "FileLions", - ], - entryValues: [ - "Gogostream", - "Vidstreaming", - "Doodstream", - "StreamWish", - "Mp4upload", - "FileLions", - ], - ), - MultiSelectListPreference( - key: "hoster_selection", - title: "Enable/Disable Hosts", - summary: "", - entries: [ - "Gogostream", - "Vidstreaming", - "Doodstream", - "StreamWish", - "Mp4upload", - "FileLions", - ], - entryValues: [ - "vidcdn", - "anime", - "doodstream", - "streamwish", - "mp4upload", - "filelions", - ], - values: [ - "vidcdn", - "anime", - "doodstream", - "streamwish", - "mp4upload", - "filelions", - ], - ), - ]; - } - - List preferenceHosterSelection(int sourceId) { - return getPreferenceValue(sourceId, "hoster_selection"); - } - - List sortVideos(List videos, int sourceId) { - String quality = getPreferenceValue(sourceId, "preferred_quality"); - String server = getPreferenceValue(sourceId, "preferred_server"); - - videos.sort((MVideo a, MVideo b) { - int qualityMatchA = 0; - if (a.quality.contains(quality) && a.quality.contains(server)) { - qualityMatchA = 1; - } - int qualityMatchB = 0; - if (b.quality.contains(quality) && b.quality.contains(server)) { - qualityMatchB = 1; - } - if (qualityMatchA != qualityMatchB) { - return qualityMatchB - qualityMatchA; - } - - final regex = RegExp(r'(\d+)p'); - final matchA = regex.firstMatch(a.quality); - final matchB = regex.firstMatch(b.quality); - final int qualityNumA = int.tryParse(matchA?.group(1) ?? '0') ?? 0; - final int qualityNumB = int.tryParse(matchB?.group(1) ?? '0') ?? 0; - return qualityNumB - qualityNumA; - }); - - return videos; - } -} - -GogoAnime main(MSource source) { - return GogoAnime(source: source); -} diff --git a/dart/anime/src/en/gogoanime/icon.png b/dart/anime/src/en/gogoanime/icon.png deleted file mode 100644 index 29abaec0..00000000 Binary files a/dart/anime/src/en/gogoanime/icon.png and /dev/null differ diff --git a/dart/anime/src/en/gogoanime/source.dart b/dart/anime/src/en/gogoanime/source.dart deleted file mode 100644 index d95c9b2a..00000000 --- a/dart/anime/src/en/gogoanime/source.dart +++ /dev/null @@ -1,17 +0,0 @@ -import '../../../../../model/source.dart'; - -Source get gogoanimeSource => _gogoanimeSource; -const _gogoanimeVersion = "0.1.2"; -const _gogoanimeSourceCodeUrl = - "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/en/gogoanime/gogoanime.dart"; -Source _gogoanimeSource = Source( - name: "Gogoanime", - baseUrl: "https://anitaku.to", - lang: "en", - typeSource: "single", - iconUrl: - "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/en/gogoanime/icon.png", - sourceCodeUrl: _gogoanimeSourceCodeUrl, - version: _gogoanimeVersion, - itemType: ItemType.anime, -); diff --git a/dart/anime/src/en/kisskh/icon.png b/dart/anime/src/en/kisskh/icon.png deleted file mode 100644 index 5c440d80..00000000 Binary files a/dart/anime/src/en/kisskh/icon.png and /dev/null differ diff --git a/dart/anime/src/en/kisskh/kisskh.dart b/dart/anime/src/en/kisskh/kisskh.dart deleted file mode 100644 index 89632358..00000000 --- a/dart/anime/src/en/kisskh/kisskh.dart +++ /dev/null @@ -1,223 +0,0 @@ -import 'package:mangayomi/bridge_lib.dart'; -import 'dart:convert'; - -class KissKh extends MProvider { - KissKh({required this.source}); - - MSource source; - - final Client client = Client(); - - @override - Future getPopular(int page) async { - final res = - (await client.get( - Uri.parse( - "${source.baseUrl}/api/DramaList/List?page=$page&type=0&sub=0&country=0&status=0&order=1&pageSize=40", - ), - )).body; - final jsonRes = json.decode(res); - final datas = jsonRes["data"]; - List animeList = []; - - for (var data in datas) { - var anime = MManga(); - anime.name = data["title"]; - anime.imageUrl = data["thumbnail"] ?? ""; - anime.link = - "${source.baseUrl}/api/DramaList/Drama/${data["id"]}?isq=false"; - animeList.add(anime); - } - - int lastPage = jsonRes["totalCount"]; - int pages = jsonRes["page"]; - return MPages(animeList, pages < lastPage); - } - - @override - Future getLatestUpdates(int page) async { - final res = - (await client.get( - Uri.parse( - "${source.baseUrl}/api/DramaList/List?page=$page&type=0&sub=0&country=0&status=0&order=12&pageSize=40", - ), - )).body; - final jsonRes = json.decode(res); - final datas = jsonRes["data"]; - - List animeList = []; - - for (var data in datas) { - var anime = MManga(); - anime.name = data["title"]; - anime.imageUrl = data["thumbnail"] ?? ""; - anime.link = - "${source.baseUrl}/api/DramaList/Drama/${data["id"]}?isq=false"; - animeList.add(anime); - } - - int lastPage = jsonRes["totalCount"]; - int pages = jsonRes["page"]; - return MPages(animeList, pages < lastPage); - } - - @override - Future search(String query, int page, FilterList filterList) async { - final res = - (await client.get( - Uri.parse("${source.baseUrl}/api/DramaList/Search?q=$query&type=0"), - )).body; - final jsonRes = json.decode(res); - List animeList = []; - for (var data in jsonRes) { - var anime = MManga(); - anime.name = data["title"]; - anime.imageUrl = data["thumbnail"] ?? ""; - anime.link = - "${source.baseUrl}/api/DramaList/Drama/${data["id"]}?isq=false"; - animeList.add(anime); - } - return MPages(animeList, false); - } - - @override - Future getDetail(String url) async { - final statusList = [ - {"Ongoing": 0, "Completed": 1}, - ]; - final res = (await client.get(Uri.parse(url))).body; - var anime = MManga(); - final jsonRes = json.decode(res); - final status = jsonRes["status"] ?? ""; - anime.description = jsonRes["description"]; - anime.status = parseStatus(status, statusList); - anime.imageUrl = jsonRes["thumbnail"]; - var episodes = jsonRes["episodes"]; - String type = jsonRes["type"]; - final episodesCount = jsonRes["episodesCount"] as int; - final containsAnime = type.contains("Anime"); - final containsTVSeries = type.contains("TVSeries"); - final containsHollywood = type.contains("Hollywood"); - final containsMovie = type.contains("Movie"); - List? episodesList = []; - - for (var a in episodes) { - MChapter episode = MChapter(); - String number = (a["number"] as double).toString().replaceAll(".0", ""); - final id = a["id"]; - if (containsAnime || containsTVSeries) { - episode.name = "Episode $number"; - } else if (containsHollywood && episodesCount == 1 || containsMovie) { - episode.name = "Movie"; - } else if (containsHollywood && episodesCount > 1) { - episode.name = "Episode $number"; - } - episode.url = - "${source.baseUrl}/api/DramaList/Episode/$id.png?err=false&ts=&time="; - episodesList.add(episode); - } - - anime.chapters = episodesList; - return anime; - } - - @override - Future> getVideoList(String url) async { - final res = (await client.get(Uri.parse(url))).body; - final id = substringAfter(substringBefore(url, ".png"), "Episode/"); - final jsonRes = json.decode(res); - - final subRes = - (await client.get(Uri.parse("${source.baseUrl}/api/Sub/$id"))).body; - var jsonSubRes = json.decode(subRes); - List subtitles = []; - - for (var sub in jsonSubRes) { - final subUrl = sub["src"] as String; - final label = sub["label"]; - if (subUrl.endsWith("txt")) { - var subtitle = await getSubtitle(subUrl, label); - subtitles.add(subtitle); - } else { - var subtitle = MTrack(); - subtitle - ..label = label - ..file = subUrl; - subtitles.add(subtitle); - } - } - final videoUrl = jsonRes["Video"]; - var video = MVideo(); - video - ..url = videoUrl - ..originalUrl = videoUrl - ..quality = "kisskh" - ..subtitles = subtitles - ..headers = { - "referer": "https://kisskh.me/", - "origin": "https://kisskh.me", - }; - return [video]; - } - - Future getSubtitle(String subUrl, String subLang) async { - final response = await client.get( - Uri.parse(subUrl), - headers: {"referer": "https://kisskh.me/", "origin": "https://kisskh.me"}, - ); - final subtitleData = response.body; - String decrypted = "\n"; - for (String line in subtitleData.split('\n')) { - decrypted += "${decrypt(line.trim())}\n"; - } - var subtitle = MTrack(); - subtitle - ..label = subLang - ..file = decrypted; - return subtitle; - } - - String decrypt(String data) { - final key = utf8.decode([ - 56, - 48, - 53, - 54, - 52, - 56, - 51, - 54, - 52, - 54, - 51, - 50, - 56, - 55, - 54, - 51, - ]); - final iv = utf8.decode([ - 54, - 56, - 53, - 50, - 54, - 49, - 50, - 51, - 55, - 48, - 49, - 56, - 53, - 50, - 55, - 51, - ]); - return cryptoHandler(data, iv, key, false); - } -} - -KissKh main(MSource source) { - return KissKh(source: source); -} diff --git a/dart/anime/src/en/kisskh/source.dart b/dart/anime/src/en/kisskh/source.dart deleted file mode 100644 index 42142091..00000000 --- a/dart/anime/src/en/kisskh/source.dart +++ /dev/null @@ -1,17 +0,0 @@ -import '../../../../../model/source.dart'; - -Source get kisskhSource => _kisskhSource; -const _kisskhVersion = "0.0.7"; -const _kisskhSourceCodeUrl = - "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/en/kisskh/kisskh.dart"; -Source _kisskhSource = Source( - name: "KissKH", - baseUrl: "https://kisskh.co", - lang: "en", - typeSource: "single", - iconUrl: - "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/en/kisskh/icon.png", - sourceCodeUrl: _kisskhSourceCodeUrl, - version: _kisskhVersion, - itemType: ItemType.anime, -); diff --git a/dart/anime/src/en/nineanimetv/icon.png b/dart/anime/src/en/nineanimetv/icon.png deleted file mode 100644 index e57234ee..00000000 Binary files a/dart/anime/src/en/nineanimetv/icon.png and /dev/null differ diff --git a/dart/anime/src/en/nineanimetv/nineanimetv.dart b/dart/anime/src/en/nineanimetv/nineanimetv.dart deleted file mode 100644 index b6ac76cc..00000000 --- a/dart/anime/src/en/nineanimetv/nineanimetv.dart +++ /dev/null @@ -1,580 +0,0 @@ -import 'package:mangayomi/bridge_lib.dart'; -import 'dart:convert'; - -class NineAnimeTv extends MProvider { - NineAnimeTv({required this.source}); - - MSource source; - - final Client client = Client(); - - @override - Future getPopular(int page) async { - final res = - (await client.get( - Uri.parse("${source.baseUrl}/filter?sort=all&page=$page"), - )).body; - return parseAnimeList(res); - } - - @override - Future getLatestUpdates(int page) async { - final res = - (await client.get( - Uri.parse( - "${source.baseUrl}/filter?sort=recently_updated&page=$page", - ), - )).body; - return parseAnimeList(res); - } - - @override - Future search(String query, int page, FilterList filterList) async { - final filters = filterList.filters; - String url = "${source.baseUrl}/filter?keyword=$query"; - - for (var filter in filters) { - if (filter.type == "GenreFilter") { - final genre = (filter.state as List).where((e) => e.state).toList(); - url += "${ll(url)}genre="; - if (genre.isNotEmpty) { - for (var st in genre) { - url += "${st.value}"; - if (genre.length > 1) { - url += "%2C"; - } - } - if (genre.length > 1) { - url = substringBeforeLast(url, '%2C'); - } - } - } else if (filter.type == "SeasonFilter") { - final season = (filter.state as List).where((e) => e.state).toList(); - url += "${ll(url)}season="; - if (season.isNotEmpty) { - for (var st in season) { - url += "${st.value}"; - if (season.length > 1) { - url += "%2C"; - } - } - if (season.length > 1) { - url = substringBeforeLast(url, '%2C'); - } - } - } else if (filter.type == "YearFilter") { - final year = (filter.state as List).where((e) => e.state).toList(); - url += "${ll(url)}year="; - if (year.isNotEmpty) { - for (var st in year) { - url += "${st.value}"; - if (year.length > 1) { - url += "%2C"; - } - } - if (year.length > 1) { - url = substringBeforeLast(url, '%2C'); - } - } - } else if (filter.type == "TypeFilter") { - final type = (filter.state as List).where((e) => e.state).toList(); - url += "${ll(url)}type="; - if (type.isNotEmpty) { - for (var st in type) { - url += "${st.value}"; - if (type.length > 1) { - url += "%2C"; - } - } - if (type.length > 1) { - url = substringBeforeLast(url, '%2C'); - } - } - } else if (filter.type == "StatusFilter") { - final status = filter.values[filter.state].value; - url += "${ll(url)}status=$status"; - } else if (filter.type == "LanguageFilter") { - final language = (filter.state as List).where((e) => e.state).toList(); - url += "${ll(url)}language="; - if (language.isNotEmpty) { - for (var st in language) { - url += "${st.value}"; - if (language.length > 1) { - url += "%2C"; - } - } - if (language.length > 1) { - url = substringBeforeLast(url, '%2C'); - } - } - } else if (filter.type == "SortFilter") { - final sort = filter.values[filter.state].value; - url += "${ll(url)}sort=$sort"; - } - } - - final res = (await client.get(Uri.parse("$url&page=$page"))).body; - return parseAnimeList(res); - } - - @override - Future getDetail(String url) async { - final statusList = [ - {"Currently Airing": 0, "Finished Airing": 1}, - ]; - - final res = (await client.get(Uri.parse("${source.baseUrl}$url"))).body; - MManga anime = MManga(); - final document = parseHtml(res); - final infoElement = document.selectFirst("div.film-infor"); - final status = - infoElement.xpathFirst( - '//div[contains(text(),"Status:")]/following-sibling::div/span/text()', - ) ?? - ""; - anime.status = parseStatus(status, statusList); - anime.description = - infoElement.selectFirst("div.film-description > p")?.text ?? ""; - anime.author = - infoElement.xpathFirst( - '//div[contains(text(),"Studios:")]/following-sibling::div/a/text()', - ) ?? - ""; - - anime.genre = infoElement.xpath( - '//div[contains(text(),"Genre:")]/following-sibling::div/a/text()', - ); - final id = parseHtml(res).selectFirst("div[data-id]").attr("data-id"); - - final resEp = - (await client.get( - Uri.parse("${source.baseUrl}/ajax/episode/list/$id"), - )).body; - final html = json.decode(resEp)["html"]; - - List? episodesList = []; - - final epsElements = parseHtml(html).select("a"); - for (var epElement in epsElements) { - final id = epElement.attr('data-id'); - - final title = epElement.attr('title') ?? ""; - - final epNum = epElement.attr('data-number'); - - MChapter episode = MChapter(); - episode.name = "Episode $epNum $title"; - episode.url = id; - episodesList.add(episode); - } - anime.chapters = episodesList.reversed.toList(); - return anime; - } - - @override - Future> getVideoList(String url) async { - final res = - (await client.get( - Uri.parse("${source.baseUrl}/ajax/episode/servers?episodeId=$url"), - )).body; - - final html = json.decode(res)["html"]; - - final serverElements = parseHtml(html).select("div.server-item"); - - List videos = []; - final hosterSelection = preferenceHosterSelection(source.id); - final typeSelection = preferenceTypeSelection(source.id); - for (var serverElement in serverElements) { - final name = serverElement.text; - final id = serverElement.attr("data-id"); - final subDub = serverElement.attr("data-type"); - final res = - (await client.get( - Uri.parse("${source.baseUrl}/ajax/episode/sources?id=$id"), - )).body; - final epUrl = json.decode(res)["link"]; - List a = []; - - if (hosterSelection.contains(name) && typeSelection.contains(subDub)) { - if (name.contains("Vidstreaming")) { - a = await rapidCloudExtractor(epUrl, "Vidstreaming - $subDub"); - } else if (name.contains("Vidcloud")) { - a = await rapidCloudExtractor(epUrl, "Vidcloud - $subDub"); - } - videos.addAll(a); - } - } - - return sortVideos(videos, source.id); - } - - MPages parseAnimeList(String res) { - final elements = parseHtml(res).select("div.film_list-wrap > div"); - List animeList = []; - for (var element in elements) { - MManga anime = MManga(); - anime.name = element.selectFirst("div.film-detail > h3 > a").text; - anime.imageUrl = element.selectFirst(" div.film-poster > img").getSrc; - anime.link = element.selectFirst("div.film-detail > h3 > a").getHref; - animeList.add(anime); - } - - return MPages(animeList, true); - } - - Future> rapidCloudExtractor(String url, String name) async { - final serverUrl = ['https://megacloud.tv', 'https://rapid-cloud.co']; - - final serverType = - url.startsWith('https://megacloud.tv') || - url.startsWith('https://megacloud.club') - ? 0 - : 1; - final sourceUrl = [ - '/embed-2/ajax/e-1/getSources?id=', - '/ajax/embed-6-v2/getSources?id=', - ]; - final sourceSpliter = ['/e-1/', '/embed-6-v2/']; - final id = url.split(sourceSpliter[serverType]).last.split('?').first; - final resServer = - (await client.get( - Uri.parse('${serverUrl[serverType]}${sourceUrl[serverType]}$id'), - headers: {"X-Requested-With": "XMLHttpRequest"}, - )).body; - final encrypted = getMapValue(resServer, "encrypted"); - String videoResJson = ""; - List videos = []; - if (encrypted == "true") { - final ciphered = getMapValue(resServer, "sources"); - List> indexPairs = await generateIndexPairs(serverType); - var password = ''; - String ciphertext = ciphered; - int index = 0; - for (List item in json.decode(json.encode(indexPairs))) { - int start = item.first + index; - int end = start + item.last; - String passSubstr = ciphered.substring(start, end); - password += passSubstr; - ciphertext = ciphertext.replaceFirst(passSubstr, ""); - index += item.last; - } - videoResJson = decryptAESCryptoJS(ciphertext, password); - } else { - videoResJson = json.encode( - (json.decode(resServer)["sources"] as List>), - ); - } - - String masterUrl = - ((json.decode(videoResJson) as List>) - .first)['file']; - String type = - ((json.decode(videoResJson) as List>) - .first)['type']; - - final tracks = - (json.decode(resServer)['tracks'] as List) - .where((e) => e['kind'] == 'captions' ? true : false) - .toList(); - List subtitles = []; - - for (var sub in tracks) { - try { - MTrack subtitle = MTrack(); - subtitle - ..label = sub["label"] - ..file = sub["file"]; - subtitles.add(subtitle); - } catch (_) {} - } - - if (type == "hls") { - final masterPlaylistRes = (await client.get(Uri.parse(masterUrl))).body; - - for (var it in substringAfter( - masterPlaylistRes, - "#EXT-X-STREAM-INF:", - ).split("#EXT-X-STREAM-INF:")) { - final quality = - "${substringBefore(substringBefore(substringAfter(substringAfter(it, "RESOLUTION="), "x"), ","), "\n")}p"; - - String videoUrl = substringBefore(substringAfter(it, "\n"), "\n"); - - if (!videoUrl.startsWith("http")) { - videoUrl = - "${masterUrl.split("/").sublist(0, masterUrl.split("/").length - 1).join("/")}/$videoUrl"; - } - - MVideo video = MVideo(); - video - ..url = videoUrl - ..originalUrl = videoUrl - ..quality = "$name - $quality" - ..subtitles = subtitles; - videos.add(video); - } - } else { - MVideo video = MVideo(); - video - ..url = masterUrl - ..originalUrl = masterUrl - ..quality = "$name - Default" - ..subtitles = subtitles; - videos.add(video); - } - return videos; - } - - Future>> generateIndexPairs(int serverType) async { - final jsPlayerUrl = [ - "https://megacloud.tv/js/player/a/prod/e1-player.min.js", - "https://rapid-cloud.co/js/player/prod/e6-player-v2.min.js", - ]; - final scriptText = - (await client.get(Uri.parse(jsPlayerUrl[serverType]))).body; - - final switchCode = scriptText.substring( - scriptText.lastIndexOf('switch'), - scriptText.indexOf('=partKey'), - ); - - List indexes = []; - for (var variableMatch - in RegExp(r'=(\w+)').allMatches(switchCode).toList()) { - final regex = RegExp( - ',${(variableMatch as RegExpMatch).group(1)}=((?:0x)?([0-9a-fA-F]+))', - ); - Match? match = regex.firstMatch(scriptText); - - if (match != null) { - String value = match.group(1); - if (value.contains("0x")) { - indexes.add(int.parse(substringAfter(value, "0x"), radix: 16)); - } else { - indexes.add(int.parse(value)); - } - } - } - - return chunked(indexes, 2); - } - - List> chunked(List list, int size) { - List> chunks = []; - for (int i = 0; i < list.length; i += size) { - int end = list.length; - if (i + size < list.length) { - end = i + size; - } - chunks.add(list.sublist(i, end)); - } - return chunks; - } - - @override - List getFilterList() { - return [ - GroupFilter("GenreFilter", "Genre", [ - CheckBoxFilter("Action", "1"), - CheckBoxFilter("Adventure", "2"), - CheckBoxFilter("Cars", "3"), - CheckBoxFilter("Comedy", "4"), - CheckBoxFilter("Dementia", "5"), - CheckBoxFilter("Demons", "6"), - CheckBoxFilter("Drama", "8"), - CheckBoxFilter("Ecchi", "9"), - CheckBoxFilter("Fantasy", "10"), - CheckBoxFilter("Game", "11"), - CheckBoxFilter("Harem", "35"), - CheckBoxFilter("Historical", "13"), - CheckBoxFilter("Horror", "14"), - CheckBoxFilter("Isekai", "44"), - CheckBoxFilter("Josei", "43"), - CheckBoxFilter("Kids", "15"), - CheckBoxFilter("Magic", "16"), - CheckBoxFilter("Martial Arts", "17"), - CheckBoxFilter("Mecha", "18"), - CheckBoxFilter("Military", "38"), - CheckBoxFilter("Music", "19"), - CheckBoxFilter("Mystery", "7"), - CheckBoxFilter("Parody", "20"), - CheckBoxFilter("Police", "39"), - CheckBoxFilter("Psychological", "40"), - CheckBoxFilter("Romance", "22"), - CheckBoxFilter("Samurai", "21"), - CheckBoxFilter("School", "23"), - CheckBoxFilter("Sci-Fi", "24"), - CheckBoxFilter("Seinen", "42"), - CheckBoxFilter("Shoujo", "25"), - CheckBoxFilter("Shoujo Ai", "26"), - CheckBoxFilter("Shounen", "27"), - CheckBoxFilter("Shounen Ai", "28"), - CheckBoxFilter("Slice of Life", "36"), - CheckBoxFilter("Space", "29"), - CheckBoxFilter("Sports", "30"), - CheckBoxFilter("Super Power", "31"), - CheckBoxFilter("Supernatural", "37"), - CheckBoxFilter("Thriller", "41"), - CheckBoxFilter("Vampire", "32"), - ]), - GroupFilter("SeasonFilter", "Season", [ - CheckBoxFilter("Fall", "3"), - CheckBoxFilter("Summer", "2"), - CheckBoxFilter("Spring", "1"), - CheckBoxFilter("Winter", "4"), - ]), - GroupFilter("YearFilter", "Year", [ - CheckBoxFilter("2024", "2024"), - CheckBoxFilter("2023", "2023"), - CheckBoxFilter("2022", "2022"), - CheckBoxFilter("2021", "2021"), - CheckBoxFilter("2020", "2020"), - CheckBoxFilter("2019", "2019"), - CheckBoxFilter("2018", "2018"), - CheckBoxFilter("2017", "2017"), - CheckBoxFilter("2016", "2016"), - CheckBoxFilter("2015", "2015"), - CheckBoxFilter("2014", "2014"), - CheckBoxFilter("2013", "2013"), - CheckBoxFilter("2012", "2012"), - CheckBoxFilter("2011", "2011"), - CheckBoxFilter("2010", "2010"), - CheckBoxFilter("2009", "2009"), - CheckBoxFilter("2008", "2008"), - CheckBoxFilter("2007", "2007"), - CheckBoxFilter("2006", "2006"), - CheckBoxFilter("2005", "2005"), - CheckBoxFilter("2004", "2004"), - CheckBoxFilter("2003", "2003"), - CheckBoxFilter("2002", "2002"), - CheckBoxFilter("2001", "2001"), - ]), - SelectFilter("SortFilter", "Sort by", 0, [ - SelectFilterOption("All", "all"), - SelectFilterOption("Default", "default"), - SelectFilterOption("Recently Added", "recently_added"), - SelectFilterOption("Recently Updated", "recently_updated"), - SelectFilterOption("Score", "score"), - SelectFilterOption("Name A-Z", "name_az"), - SelectFilterOption("Released Date", "released_date"), - SelectFilterOption("Most Watched", "most_watched"), - ]), - GroupFilter("TypeFilter", "Type", [ - CheckBoxFilter("Movie", "1"), - CheckBoxFilter("TV Series", "2"), - CheckBoxFilter("OVA", "3"), - CheckBoxFilter("ONA", "4"), - CheckBoxFilter("Special", "5"), - CheckBoxFilter("Music", "6"), - ]), - SelectFilter("StatusFilter", "Status", 0, [ - SelectFilterOption("All", "all"), - SelectFilterOption("Finished Airing", "1"), - SelectFilterOption("Currently Airing", "2"), - SelectFilterOption("Not yet aired", "3"), - ]), - GroupFilter("LanguageFilter", "Language", [ - CheckBoxFilter("Sub", "sub"), - CheckBoxFilter("Dub", "dub"), - ]), - ]; - } - - @override - List getSourcePreferences() { - return [ - ListPreference( - key: "preferred_quality", - title: "Preferred Quality", - summary: "", - valueIndex: 1, - entries: ["1080p", "720p", "480p", "360p"], - entryValues: ["1080", "720", "480", "360"], - ), - ListPreference( - key: "preferred_server", - title: "Preferred server", - summary: "", - valueIndex: 0, - entries: ["Vidstreaming", "VidCloud"], - entryValues: ["Vidstreaming", "VidCloud"], - ), - ListPreference( - key: "preferred_type", - title: "Preferred Type", - summary: "", - valueIndex: 0, - entries: ["Sub", "Dub"], - entryValues: ["sub", "dub"], - ), - MultiSelectListPreference( - key: "hoster_selection", - title: "Enable/Disable Hosts", - summary: "", - entries: ["Vidstreaming", "VidCloud"], - entryValues: ["Vidstreaming", "Vidcloud"], - values: ["Vidstreaming", "Vidcloud"], - ), - MultiSelectListPreference( - key: "type_selection", - title: "Enable/Disable Types", - summary: "", - entries: ["Sub", "Dub"], - entryValues: ["sub", "dub"], - values: ["sub", "dub"], - ), - ]; - } - - List sortVideos(List videos, int sourceId) { - String quality = getPreferenceValue(sourceId, "preferred_quality"); - String server = getPreferenceValue(sourceId, "preferred_server"); - String type = getPreferenceValue(sourceId, "preferred_type"); - videos.sort((MVideo a, MVideo b) { - int qualityMatchA = 0; - - if (a.quality.contains(quality) && - a.quality.toLowerCase().contains(type.toLowerCase()) && - a.quality.toLowerCase().contains(server.toLowerCase())) { - qualityMatchA = 1; - } - int qualityMatchB = 0; - if (b.quality.contains(quality) && - b.quality.toLowerCase().contains(type.toLowerCase()) && - b.quality.toLowerCase().contains(server.toLowerCase())) { - qualityMatchB = 1; - } - if (qualityMatchA != qualityMatchB) { - return qualityMatchB - qualityMatchA; - } - - final regex = RegExp(r'(\d+)p'); - final matchA = regex.firstMatch(a.quality); - final matchB = regex.firstMatch(b.quality); - final int qualityNumA = int.tryParse(matchA?.group(1) ?? '0') ?? 0; - final int qualityNumB = int.tryParse(matchB?.group(1) ?? '0') ?? 0; - return qualityNumB - qualityNumA; - }); - return videos; - } - - List preferenceHosterSelection(int sourceId) { - return getPreferenceValue(sourceId, "hoster_selection"); - } - - List preferenceTypeSelection(int sourceId) { - return getPreferenceValue(sourceId, "type_selection"); - } - - String ll(String url) { - if (url.contains("?")) { - return "&"; - } - return "?"; - } -} - -NineAnimeTv main(MSource source) { - return NineAnimeTv(source: source); -} diff --git a/dart/anime/src/en/nineanimetv/source.dart b/dart/anime/src/en/nineanimetv/source.dart deleted file mode 100644 index 216597cc..00000000 --- a/dart/anime/src/en/nineanimetv/source.dart +++ /dev/null @@ -1,17 +0,0 @@ -import '../../../../../model/source.dart'; - -Source get nineanimetv => _nineanimetv; -const _nineanimetvVersion = "0.0.55"; -const _nineanimetvCodeUrl = - "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/en/nineanimetv/nineanimetv.dart"; -Source _nineanimetv = Source( - name: "9AnimeTv", - baseUrl: "https://9animetv.to", - lang: "en", - typeSource: "single", - iconUrl: - "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/en/nineanimetv/icon.png", - sourceCodeUrl: _nineanimetvCodeUrl, - version: _nineanimetvVersion, - itemType: ItemType.anime, -); diff --git a/dart/anime/src/en/uhdmovies/icon.png b/dart/anime/src/en/uhdmovies/icon.png deleted file mode 100644 index 073e44f1..00000000 Binary files a/dart/anime/src/en/uhdmovies/icon.png and /dev/null differ diff --git a/dart/anime/src/en/uhdmovies/source.dart b/dart/anime/src/en/uhdmovies/source.dart deleted file mode 100644 index e60e73f8..00000000 --- a/dart/anime/src/en/uhdmovies/source.dart +++ /dev/null @@ -1,17 +0,0 @@ -import '../../../../../model/source.dart'; - -Source get uhdmoviesSource => _uhdmoviesSource; -const _uhdmoviesVersion = "0.0.5"; -const _uhdmoviesSourceCodeUrl = - "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/en/uhdmovies/uhdmovies.dart"; -Source _uhdmoviesSource = Source( - name: "UHD Movies", - baseUrl: "https://uhdmovies.fans", - lang: "en", - typeSource: "single", - iconUrl: - "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/en/uhdmovies/icon.png", - sourceCodeUrl: _uhdmoviesSourceCodeUrl, - version: _uhdmoviesVersion, - itemType: ItemType.anime, -); diff --git a/dart/anime/src/en/uhdmovies/uhdmovies.dart b/dart/anime/src/en/uhdmovies/uhdmovies.dart deleted file mode 100644 index 1f6f598f..00000000 --- a/dart/anime/src/en/uhdmovies/uhdmovies.dart +++ /dev/null @@ -1,255 +0,0 @@ -import 'package:mangayomi/bridge_lib.dart'; -import 'dart:convert'; - -class UHDMovies extends MProvider { - UHDMovies({required this.source}); - - MSource source; - - final Client client = Client(); - - @override - bool get supportsLatest => false; - - @override - String get baseUrl => getPreferenceValue(source.id, "pref_domain_new"); - - @override - Future getPopular(int page) async { - final res = (await client.get(Uri.parse("$baseUrl/page/$page"))).body; - return animeFromElement(res); - } - - @override - Future getLatestUpdates(int page) async { - return MPages([], false); - } - - @override - Future search(String query, int page, FilterList filterList) async { - final res = - (await client.get( - Uri.parse("$baseUrl/page/$page/?s=${query.replaceAll(" ", "+")}"), - )).body; - return animeFromElement(res); - } - - @override - Future getDetail(String url) async { - url = getUrlWithoutDomain(url); - final res = (await client.get(Uri.parse("$baseUrl${url}"))).body; - MManga anime = MManga(); - final description = xpath(res, '//pre/span/text()'); - if (description.isNotEmpty) { - anime.description = description.first; - } - anime.status = MStatus.ongoing; - final episodesTitles = xpath( - res, - '//*[contains(@style, "center") or contains(@class, "maxbutton")]/a[contains(@class, "maxbutton") or contains(@href, "?sid=")]/text()', - ); - final episodesUrls = xpath( - res, - '//*[contains(@style, "center") or contains(@class, "maxbutton")]/a[contains(@class, "maxbutton") or contains(@href, "?sid=")]/@href', - ); - bool isSeries = false; - if (episodesTitles.first.contains("Episode") || - episodesTitles.first.contains("Zip") || - episodesTitles.first.contains("Pack")) { - isSeries = true; - } - List? episodesList = []; - if (!isSeries) { - List moviesTitles = []; - moviesTitles = xpath( - res, - '//*[contains(@style, "center") or contains(@class, "maxbutton")]/parent::p//preceding-sibling::p[contains(@style, "center")]/text()', - ); - List titles = []; - if (moviesTitles.isEmpty) { - moviesTitles = xpath(res, '//p[contains(@style, "center")]/text()'); - } - for (var title in moviesTitles) { - if (title.isNotEmpty && - !title.contains('Download') && - !title.contains('Note:') && - !title.contains('Copyright')) { - titles.add(title.split('[').first.trim()); - } - } - for (var i = 0; i < titles.length; i++) { - final title = titles[i]; - final quality = RegExp(r'\d{3,4}p').firstMatch(title)?.group(0) ?? ""; - final url = episodesUrls[i]; - MChapter ep = MChapter(); - ep.name = title; - ep.url = url; - ep.scanlator = quality; - episodesList.add(ep); - } - } else { - List seasonTitles = []; - final episodeTitles = xpath( - res, - '//*[contains(@style, "center") or contains(@class, "maxbutton")]/parent::p//preceding-sibling::p[contains(@style, "center") and not(text()^="Episode")]/text()', - ); - List titles = []; - for (var title in episodeTitles) { - if (title.isNotEmpty) { - titles.add(title.split('[').first.trim()); - } - } - int number = 0; - for (var i = 0; i < episodesTitles.length; i++) { - final episode = episodesTitles[i]; - final episodeUrl = episodesUrls[i]; - if (!episode.contains("Zip") || !episode.contains("Pack")) { - if (episode == "Episode 1" && seasonTitles.contains("Episode 1")) { - number++; - } else if (episode == "Episode 1") { - seasonTitles.add(episode); - } - final season = - RegExp(r'S(\d{2})').firstMatch(titles[number])?.group(1) ?? ""; - final quality = - RegExp(r'\d{3,4}p').firstMatch(titles[number])?.group(0) ?? ""; - MChapter ep = MChapter(); - ep.name = "Season $season $episode $quality"; - ep.url = episodeUrl; - ep.scanlator = quality; - episodesList.add(ep); - } - } - } - anime.chapters = episodesList.reversed.toList(); - return anime; - } - - @override - Future> getVideoList(String url) async { - final res = await getMediaUrl(url); - return await extractVideos(res); - } - - @override - List getSourcePreferences() { - return [ - EditTextPreference( - key: "pref_domain_new", - title: "Currently used domain", - summary: "", - value: "https://uhdmovies.fans", - dialogTitle: "Currently used domain", - dialogMessage: "", - text: "https://uhdmovies.fans", - ), - ]; - } - - Future> extractVideos(String url) async { - List videos = []; - for (int type = 1; type < 3; type++) { - url = url.replaceAll("/file/", "/wfile/") + "?type=$type"; - final res = (await client.get(Uri.parse(url))).body; - final links = xpath(res, '//div[@class="mb-4"]/a/@href'); - for (int i = 0; i < links.length; i++) { - final link = links[i]; - String decodedLink = link; - if (!link.contains("workers.dev")) { - decodedLink = utf8.decode( - base64Url.decode(substringAfter(link, "download?url=")), - ); - } - MVideo video = MVideo(); - video - ..url = decodedLink - ..originalUrl = decodedLink - ..quality = "CF $type Worker ${i + 1}"; - videos.add(video); - } - } - return videos; - } - - Future getMediaUrl(String url) async { - String res = ""; - String host = ""; - if (url.contains("?sid=")) { - final finalUrl = await redirectorBypasser(url); - host = Uri.parse(finalUrl).host; - res = (await client.get(Uri.parse(finalUrl))).body; - } else if (url.contains("r?key=")) { - res = (await client.get(Uri.parse(url))).body; - host = Uri.parse(url).host; - } else { - return ""; - } - final path = substringBefore(substringAfter(res, "replace(\""), "\""); - if (path == "/404") return ""; - return "https://$host$path"; - } - - Future redirectorBypasser(String url) async { - final res = (await client.get(Uri.parse(url))).body; - String lastDoc = await recursiveDoc(url, res); - final js = xpath(lastDoc, '//script[contains(text(), "/?go=")]/text()'); - if (js.isEmpty) return ""; - String script = js.first; - String nextUrl = substringBefore( - substringAfter(script, "\"href\",\""), - '"', - ); - if (!nextUrl.contains("http")) return ""; - String cookieName = substringAfter(nextUrl, "go="); - String cookieValue = substringBefore( - substringAfter(script, "'$cookieName', '"), - "'", - ); - final response = - (await client.get( - Uri.parse(nextUrl), - headers: {"referer": url, "Cookie": "$cookieName=$cookieValue"}, - )).body; - - final lastRes = parseHtml( - response, - ).selectFirst("meta[http-equiv]").attr("content"); - return substringAfter(lastRes, "url="); - } - - MPages animeFromElement(String res) { - List animeList = []; - final urls = xpath(res, '//*[@class="entry-image"]/a/@href'); - final names = xpath(res, '//*[@class="entry-image"]/a/@title'); - final images = xpath(res, '//*[@class="entry-image"]/a/img/@src'); - - for (var i = 0; i < names.length; i++) { - MManga anime = MManga(); - anime.name = names[i].replaceAll("Download", ""); - anime.imageUrl = images[i]; - anime.link = urls[i]; - animeList.add(anime); - } - final nextPage = xpath(res, '//a[@class="next page-numbers"]/@href'); - return MPages(animeList, nextPage.isNotEmpty); - } - - Future recursiveDoc(String url, String html) async { - final urlR = xpath(html, '//form[@id="landing"]/@action'); - if (urlR.isEmpty) return html; - final name = xpath(html, '//input/@name').first; - final value = xpath(html, '//input/@value').first; - final body = {"$name": value}; - final response = - (await client.post( - Uri.parse(urlR.first), - headers: {"referer": url}, - body: body, - )).body; - return recursiveDoc(url, response); - } -} - -UHDMovies main(MSource source) { - return UHDMovies(source: source); -} diff --git a/dart/anime/src/en/vumeto/icon.png b/dart/anime/src/en/vumeto/icon.png deleted file mode 100644 index 56be717a..00000000 Binary files a/dart/anime/src/en/vumeto/icon.png and /dev/null differ diff --git a/dart/anime/src/en/vumeto/source.dart b/dart/anime/src/en/vumeto/source.dart deleted file mode 100644 index bb7fb1d9..00000000 --- a/dart/anime/src/en/vumeto/source.dart +++ /dev/null @@ -1,19 +0,0 @@ -// from https://github.com/MiraiEnoki/anymex-extensions/blob/main/dart/anime/src/en/vumeto - -import '../../../../../model/source.dart'; - -Source get vumetoSource => _vumetoSource; -const _vumetoVersion = "0.0.55"; -const _vumetoSourceCodeUrl = - "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/en/vumeto/vumeto.dart"; -Source _vumetoSource = Source( - name: "Vumeto", - baseUrl: "https://vumeto.com", - lang: "en", - typeSource: "single", - iconUrl: - "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/en/vumeto/icon.png", - sourceCodeUrl: _vumetoSourceCodeUrl, - version: _vumetoVersion, - itemType: ItemType.anime, -); diff --git a/dart/anime/src/en/vumeto/vumeto.dart b/dart/anime/src/en/vumeto/vumeto.dart deleted file mode 100644 index 03ef6bff..00000000 --- a/dart/anime/src/en/vumeto/vumeto.dart +++ /dev/null @@ -1,336 +0,0 @@ -import 'package:mangayomi/bridge_lib.dart'; -import 'dart:convert'; - -class Vumeto extends MProvider { - Vumeto({required this.source}); - - MSource source; - - final Client client = Client(); - - @override - bool get supportsLatest => true; - - @override - Map get headers => { - "Cookie": - "_ga=GA1.1.2064759276.1741681027; _ga_5HMNDC3ZE4=GS1.1.1741824276.8.1.1741824749.0.0.0", - "User-Agent": - "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36", - "Referer": "https://vumeto.com/", - }; - - @override - Future getPopular(int page) async { - final url = 'https://vumeto.com/most-popular'; - final resp = await client.get(Uri.parse(url)); - - final document = parseHtml(resp.body); - - return MPages(scrapeAnimeList(document), false); - } - - @override - Future getLatestUpdates(int page) async { - final url = 'https://vumeto.com/recently-updated'; - final resp = await client.get(Uri.parse(url)); - - final document = parseHtml(resp.body); - - return MPages(scrapeAnimeList(document), false); - } - - @override - Future search(String query, int page, FilterList filterList) async { - final url = 'https://vumeto.com/search?q=$query'; - final resp = await client.get(Uri.parse(url)); - - final document = parseHtml(resp.body); - - return MPages(scrapeAnimeList(document), false); - } - - String fixUrl(String encodedUrl) { - return encodedUrl - .split('url=') - .last - .split('&w') - .first - .replaceAll('%3A', ':') - .replaceAll('%2F', '/') - .replaceAll('%3F', '?') - .replaceAll('%3D', '=') - .replaceAll('%26', '&'); - } - - List scrapeAnimeList(MDocument document) { - List? animeElements = document.getElementsByClassName( - 'relative group border-0', - ); - List results = []; - - if (animeElements != null) { - for (var anime in animeElements) { - String? title = anime.selectFirst('h3')?.text ?? ''; - String? animeUrl = anime.selectFirst('a')?.attr('href') ?? ''; - String? imageUrl = anime.selectFirst('img')?.attr('src') ?? ''; - - MManga manga = MManga(); - manga.name = title; - manga.link = - "https://vumeto.com/watch/" + - animeUrl.replaceAll('/info/', '').split('/').first + - '?ep=1'; - manga.imageUrl = fixUrl(imageUrl.split('url=').last); - - results.add(manga); - } - } - return results; - } - - @override - Future getDetail(String url) async { - final statusList = [ - {"Releasing": 0, "Finished": 1}, - ]; - - final uri = Uri.parse(url); - final resp = await client.get(uri, headers); - final document = parseHtml(resp.body); - - final description = - document.selectFirst("meta[name='description']").attr("content") ?? ''; - - MStatus status = MStatus.unknown; - final statusStart = resp.body.indexOf( - ":", - resp.body.indexOf("\\\"status\\\""), - ); - final statusEnd = resp.body.indexOf("\\\",", statusStart); - if (statusStart != -1 && statusEnd != -1) { - final rawStatus = resp.body.substring(statusStart + 1, statusEnd); - status = parseStatus(rawStatus.replaceAll("\\\"", ""), statusList); - } - - final genresStart = resp.body.indexOf( - "[", - resp.body.indexOf("\\\"genres\\\":"), - ); - final genresEnd = resp.body.indexOf("]", genresStart); - var genres = []; - if (genresStart != -1 && genresEnd != -1) { - final genreLinks = resp.body - .substring(genresStart + 1, genresEnd) - .split(","); - genres = genreLinks.map((String e) => e.replaceAll("\\\"", "")).toList(); - } - - List chapters = []; - final scripts = document.getElementsByTagName("script"); - - String jsonData = ""; - for (var script in scripts!) { - if (script.text!.contains("episodesData")) { - final regex = RegExp( - r'self\.__next_f\.push\(\[1,".*?",null,(.*?)\]\)', - dotAll: true, - ); - final match = regex.firstMatch(script.text!); - - if (match != null && match.groupCount >= 1) { - String cleaned = match.group(1)!.replaceAll(r'\', ''); - - jsonData = cleaned.substring(0, cleaned.length - 3); - break; - } else { - print("Regex did not match."); - } - } - } - Map parsedData = json.decode(jsonData); - List episodesData = parsedData['episodesData']; - for (var ep in episodesData) { - MChapter ch = MChapter(); - final number = - (ep?['episodeNo'] ?? episodesData.indexOf(ep) + 1).toString(); - - ch.name = "Episode $number"; - - ch.url = url.split('?').first + '?ep=$number'; - - if (!chapters.any((c) => c.name == ch.name)) { - chapters.add(ch); - } - } - - MManga result = MManga(); - result.description = description; - result.status = status; - result.genre = genres; - result.chapters = chapters.reversed.toList(); - - return result; - } - - String stripTags(String htmlString) { - final RegExp exp = RegExp( - r'<[^>]*>', - multiLine: true, - caseSensitive: false, - ); - return htmlString.replaceAll(exp, '').trim(); - } - - @override - Future> getVideoList(String url) async { - try { - final resp = await client.get(Uri.parse(url), headers); - - final document = parseHtml(resp.body); - - final scripts = document.getElementsByTagName("script"); - if (scripts.isEmpty) { - print("No