From a2abfc537f9c16540ad37de419b73c70910c0db6 Mon Sep 17 00:00:00 2001 From: kodjomoustapha <107993382+kodjodevf@users.noreply.github.com> Date: Mon, 4 Dec 2023 15:23:34 +0100 Subject: [PATCH] add source preference --- anime/multisrc/dopeflix/dopeflix-v0.0.2.dart | 25 +- .../multisrc/zorotheme/zorotheme-v0.0.55.dart | 2 +- anime/src/ar/okanime/okanime-v0.0.3.dart | 95 +++- anime/src/en/aniwave/aniwave-v0.0.2.dart | 2 +- anime/src/en/gogoanime/gogoanime-v0.0.5.dart | 2 +- ...anime-v0.0.4.dart => franime-v0.0.45.dart} | 20 +- anime/src/fr/franime/source.dart | 2 +- anime/src/fr/otakufr/otakufr-v0.0.4.dart | 197 -------- anime/src/fr/otakufr/otakufr-v0.0.45.dart | 448 ++++++++++++++++++ anime/src/fr/otakufr/source.dart | 2 +- ...verz-v0.0.15.dart => oploverz-v0.0.2.dart} | 7 +- anime/src/id/oploverz/source.dart | 2 +- manga/multisrc/madara/madara-v0.0.4.dart | 2 +- .../mangareader/mangareader-v0.0.5.dart | 2 +- manga/multisrc/mmrcms/mmrcms-v0.0.4.dart | 2 +- manga/multisrc/nepnep/nepnep-v0.0.4.dart | 12 +- ...batoto-v0.0.4.dart => batoto-v0.0.45.dart} | 87 ++-- manga/src/all/batoto/sources.dart | 2 +- manga/src/all/comick/comick-v0.0.4.dart | 2 +- ...adex-v0.0.45.dart => mangadex-v0.0.5.dart} | 130 +++-- manga/src/all/mangadex/sources.dart | 2 +- manga/src/en/mangahere/mangahere-v0.0.4.dart | 2 +- 22 files changed, 722 insertions(+), 325 deletions(-) rename anime/src/fr/franime/{franime-v0.0.4.dart => franime-v0.0.45.dart} (94%) delete mode 100644 anime/src/fr/otakufr/otakufr-v0.0.4.dart create mode 100644 anime/src/fr/otakufr/otakufr-v0.0.45.dart rename anime/src/id/oploverz/{oploverz-v0.0.15.dart => oploverz-v0.0.2.dart} (95%) rename manga/src/all/batoto/{batoto-v0.0.4.dart => batoto-v0.0.45.dart} (97%) rename manga/src/all/mangadex/{mangadex-v0.0.45.dart => mangadex-v0.0.5.dart} (85%) diff --git a/anime/multisrc/dopeflix/dopeflix-v0.0.2.dart b/anime/multisrc/dopeflix/dopeflix-v0.0.2.dart index 38c62203..41d0c328 100644 --- a/anime/multisrc/dopeflix/dopeflix-v0.0.2.dart +++ b/anime/multisrc/dopeflix/dopeflix-v0.0.2.dart @@ -321,7 +321,7 @@ class DopeFlix extends MProvider { } @override - List getFilterList() { + List getFilterList(MSource source) { return [ SelectFilter("TypeFilter", "Type", 0, [ SelectFilterOption("All", "all"), @@ -419,13 +419,22 @@ class DopeFlix extends MProvider { @override List getSourcePreferences(MSource source) { return [ - 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 == "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", diff --git a/anime/multisrc/zorotheme/zorotheme-v0.0.55.dart b/anime/multisrc/zorotheme/zorotheme-v0.0.55.dart index ad0cb3b1..c684e952 100644 --- a/anime/multisrc/zorotheme/zorotheme-v0.0.55.dart +++ b/anime/multisrc/zorotheme/zorotheme-v0.0.55.dart @@ -292,7 +292,7 @@ class ZoroTheme extends MProvider { ]; @override - List getFilterList() { + List getFilterList(MSource source) { return [ SelectFilter("TypeFilter", "Type", 0, [ SelectFilterOption("All", ""), diff --git a/anime/src/ar/okanime/okanime-v0.0.3.dart b/anime/src/ar/okanime/okanime-v0.0.3.dart index 8b5fdf02..6f7f1e77 100644 --- a/anime/src/ar/okanime/okanime-v0.0.3.dart +++ b/anime/src/ar/okanime/okanime-v0.0.3.dart @@ -10,12 +10,11 @@ class OkAnime extends MProvider { final res = await http('GET', json.encode(data)); List animeList = []; - final urls = xpath(res, - '//div[@class="section" and contains(text(),"افضل انميات")]/div[@class="section-content"]/div/div/div[contains(@class,"anime-card")]/div[@class="anime-title")]/h4/a/@href'); - final names = xpath(res, - '//div[@class="section" and contains(text(),"افضل انميات")]/div[@class="section-content"]/div/div/div[contains(@class,"anime-card")]/div[@class="anime-title")]/h4/a/text()'); - final images = xpath(res, - '//div[@class="section" and contains(text(),"افضل انميات")]/div[@class="section-content"]/div/div/div[contains(@class,"anime-card")]/div[@class="anime-image")]/img/@src'); + 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(); @@ -33,12 +32,10 @@ class OkAnime extends MProvider { final res = await http('GET', json.encode(data)); List animeList = []; - final urls = xpath(res, - '//*[contains(@class,"anime-card")]/div[@class="anime-title")]/h4/a/@href'); - final names = xpath(res, - '//*[contains(@class,"anime-card")]/div[@class="anime-title")]/h4/a/text()'); - final images = xpath(res, - '//*[contains(@class,"anime-card")]/div[@class="episode-image")]/img/@src'); + 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(); @@ -63,12 +60,10 @@ class OkAnime extends MProvider { final res = await http('GET', json.encode(data)); List animeList = []; - final urls = xpath(res, - '//*[contains(@class,"anime-card")]/div[@class="anime-title")]/h4/a/@href'); - final names = xpath(res, - '//*[contains(@class,"anime-card")]/div[@class="anime-title")]/h4/a/text()'); - final images = xpath(res, - '//*[contains(@class,"anime-card")]/div[@class="anime-image")]/img/@src'); + 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(); @@ -125,29 +120,81 @@ class OkAnime extends MProvider { 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")) { + if (url.contains("https://doo") && hosterSelection.contains("Dood")) { a = await doodExtractor(url, "DoodStream - $quality"); - } else if (url.contains("mp4upload")) { + } else if (url.contains("mp4upload") && + hosterSelection.contains("Mp4upload")) { a = await mp4UploadExtractor(url, null, "", ""); - } else if (url.contains("ok.ru")) { + } else if (url.contains("ok.ru") && hosterSelection.contains("Okru")) { a = await okruExtractor(url); - } else if (url.contains("voe.sx")) { + } else if (url.contains("voe.sx") && hosterSelection.contains("Voe")) { a = await voeExtractor(url, "VoeSX $quality"); - } else if (containsVidBom(url)) { + } else if (containsVidBom(url) && hosterSelection.contains("VidBom")) { a = await vidBomExtractor(url); } videos.addAll(a); } + return sortVideos(videos, source.id); + } + + @override + List getSourcePreferences(MSource source) { + 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") { diff --git a/anime/src/en/aniwave/aniwave-v0.0.2.dart b/anime/src/en/aniwave/aniwave-v0.0.2.dart index beb8d443..2ec4582a 100644 --- a/anime/src/en/aniwave/aniwave-v0.0.2.dart +++ b/anime/src/en/aniwave/aniwave-v0.0.2.dart @@ -312,7 +312,7 @@ class Aniwave extends MProvider { } @override - List getFilterList() { + List getFilterList(MSource source) { return [ SelectFilter("OrderFilter", "Sort order", 0, [ SelectFilterOption("Most relevance", "most_relevance"), diff --git a/anime/src/en/gogoanime/gogoanime-v0.0.5.dart b/anime/src/en/gogoanime/gogoanime-v0.0.5.dart index 2d63ecf0..4ae2c1b2 100644 --- a/anime/src/en/gogoanime/gogoanime-v0.0.5.dart +++ b/anime/src/en/gogoanime/gogoanime-v0.0.5.dart @@ -251,7 +251,7 @@ class GogoAnime extends MProvider { } @override - List getFilterList() { + List getFilterList(MSource source) { return [ HeaderFilter("Advanced search"), GroupFilter("GenreFilter", "Genre", [ diff --git a/anime/src/fr/franime/franime-v0.0.4.dart b/anime/src/fr/franime/franime-v0.0.45.dart similarity index 94% rename from anime/src/fr/franime/franime-v0.0.4.dart rename to anime/src/fr/franime/franime-v0.0.45.dart index 9aedeb81..f411dc06 100644 --- a/anime/src/fr/franime/franime-v0.0.4.dart +++ b/anime/src/fr/franime/franime-v0.0.45.dart @@ -136,6 +136,7 @@ class FrAnime extends MProvider { } else if (language == "vf" && hasVf) { players = vfPlayers; } + print(players); List videos = []; for (var i = 0; i < players.length; i++) { String apiUrl = "$videoBaseUrl/$language/$i"; @@ -155,8 +156,6 @@ class FrAnime extends MProvider { ..url = playerUrl ..originalUrl = playerUrl ..quality = "FRAnime (Vido)"); - } else if (playerName.contains("myvi")) { - a = await myTvExtractor(playerUrl); } else if (playerName.contains("sendvid")) { a = await sendVidExtractor( playerUrl, json.encode({"Referer": "https://franime.fr/"}), ""); @@ -191,7 +190,7 @@ class FrAnime extends MProvider { } } - final titleO = animeJson["titleO"]; + String titleO = animeJson["titleO"]; final title = animeJson["title"]; final genre = animeJson["themes"]; final description = animeJson["description"]; @@ -228,7 +227,7 @@ class FrAnime extends MProvider { anime.name = seasonTitle; anime.imageUrl = imageUrl; anime.link = - "/anime/${regExp(titleO, "[^A-Za-z0-9 ]", "", 0, 0).replaceAll(" ", "-").toLowerCase()}?lang=$lang&s=$ind"; + "/anime/${titleO.replaceAll(RegExp("[^A-Za-z0-9 ]"), "").replaceAll(" ", "-").toLowerCase()}?lang=$lang&s=$ind"; animeList.add(anime); } @@ -276,7 +275,7 @@ class FrAnime extends MProvider { vfListName.add(vf["lecteurs"].isNotEmpty); } } - final titleO = animeJson["titleO"]; + String titleO = animeJson["titleO"]; final title = animeJson["title"]; final genre = animeJson["themes"]; final description = animeJson["description"]; @@ -314,7 +313,7 @@ class FrAnime extends MProvider { anime.name = seasonTitle; anime.imageUrl = imageUrl; anime.link = - "/anime/${regExp(titleO, "[^A-Za-z0-9 ]", "", 0, 0).replaceAll(" ", "-").toLowerCase()}?lang=$lang&s=$ind"; + "/anime/${titleO.replaceAll(RegExp("[^A-Za-z0-9 ]"), "").replaceAll(" ", "-").toLowerCase()}?lang=$lang&s=$ind"; animeList.add(anime); } @@ -335,12 +334,11 @@ class FrAnime extends MProvider { String databaseAnimeByTitleO(String res, String titleO) { print(titleO); - final datas = json.decode(res) as List; + final datas = json.decode(res) as List>; for (var data in datas) { - if (regExp(data["titleO"], "[^A-Za-z0-9 ]", "", 0, 0) - .replaceAll(" ", "-") - .toLowerCase() == - "${titleO}") { + String title = + (data["titleO"] as String).replaceAll(RegExp("[^A-Za-z0-9 ]"), ""); + if (title.replaceAll(" ", "-").toLowerCase() == "${titleO}") { return json.encode(data); } } diff --git a/anime/src/fr/franime/source.dart b/anime/src/fr/franime/source.dart index 0643414b..1073e464 100644 --- a/anime/src/fr/franime/source.dart +++ b/anime/src/fr/franime/source.dart @@ -2,7 +2,7 @@ import '../../../../model/source.dart'; import '../../../../utils/utils.dart'; Source get franimeSource => _franimeSource; -const franimeVersion = "0.0.4"; +const franimeVersion = "0.0.45"; const franimeSourceCodeUrl = "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/anime/src/fr/franime/franime-v$franimeVersion.dart"; Source _franimeSource = Source( diff --git a/anime/src/fr/otakufr/otakufr-v0.0.4.dart b/anime/src/fr/otakufr/otakufr-v0.0.4.dart deleted file mode 100644 index 5e0d0cc3..00000000 --- a/anime/src/fr/otakufr/otakufr-v0.0.4.dart +++ /dev/null @@ -1,197 +0,0 @@ -import 'package:mangayomi/bridge_lib.dart'; -import 'dart:convert'; - -class OtakuFr extends MProvider { - OtakuFr(); - - @override - Future getPopular(MSource source, int page) async { - final data = { - "url": "${source.baseUrl}/toute-la-liste-affiches/page/$page/?q=." - }; - final res = await http('GET', json.encode(data)); - - List animeList = []; - final urls = - xpath(res, '//*[@class="list"]/article/div/div/figure/a/@href'); - final names = - xpath(res, '//*[@class="list"]/article/div/div/figure/a/img/@title'); - final images = - xpath(res, '//*[@class="list"]/article/div/div/figure/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); - } - final nextPage = xpath(res, '//a[@class="next page-link"]/@href'); - return MPages(animeList, nextPage.isNotEmpty); - } - - @override - Future getLatestUpdates(MSource source, int page) async { - final data = {"url": "${source.baseUrl}/page/$page/"}; - final res = await http('GET', json.encode(data)); - - List animeList = []; - final urls = xpath(res, '//*[@class="episode"]/div/a/@href'); - final namess = xpath(res, '//*[@class="episode"]/div/a/text()'); - List names = []; - for (var name in namess) { - names.add(regExp( - name, - r'(?<=\bS\d\s*|)\d{2}\s*(?=\b(Vostfr|vostfr|VF|Vf|vf|\(VF\)|\(vf\)|\(Vf\)|\(Vostfr\)\b))?', - '', - 0, - 0) - .replaceAll(' vostfr', '') - .replaceAll(' Vostfr', '') - .replaceAll(' VF', '') - .replaceAll(' Vf', '') - .replaceAll(' vf', '') - .replaceAll(' (VF)', '') - .replaceAll(' (vf)', '') - .replaceAll(' (vf)', '') - .replaceAll(' (Vf)', '') - .replaceAll(' (Vostfr)', '')); - } - final images = xpath(res, '//*[@class="episode"]/div/figure/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); - } - final nextPage = xpath(res, '//a[@class="next page-link"]/@href'); - return MPages(animeList, nextPage.isNotEmpty); - } - - @override - Future search( - MSource source, String query, int page, FilterList filterList) async { - final data = { - "url": "${source.baseUrl}/toute-la-liste-affiches/page/$page/?q=$query" - }; - final res = await http('GET', json.encode(data)); - - List animeList = []; - final urls = - xpath(res, '//*[@class="list"]/article/div/div/figure/a/@href'); - final names = - xpath(res, '//*[@class="list"]/article/div/div/figure/a/img/@title'); - final images = - xpath(res, '//*[@class="list"]/article/div/div/figure/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); - } - final nextPage = xpath(res, '//a[@class="next page-link"]/@href'); - return MPages(animeList, nextPage.isNotEmpty); - } - - @override - Future getDetail(MSource source, String url) async { - final statusList = [ - { - "En cours": 0, - "Terminé": 1, - } - ]; - final data = {"url": url}; - String res = await http('GET', json.encode(data)); - MManga anime = MManga(); - final originalUrl = xpath(res, - '//*[@class="breadcrumb"]/li[@class="breadcrumb-item"][2]/a/@href') - .first; - if (originalUrl.isNotEmpty) { - final newData = {"url": originalUrl}; - res = await http('GET', json.encode(newData)); - } - - anime.description = xpath(res, '//*[@class="episode fz-sm synop"]/p/text()') - .first - .replaceAll("Synopsis:", ""); - final status = xpath(res, - '//*[@class="list-unstyled"]/li[contains(text(),"Statut")]/text()') - .first - .replaceAll("Statut: ", ""); - anime.status = parseStatus(status, statusList); - anime.genre = xpath(res, - '//*[@class="list-unstyled"]/li[contains(text(),"Genre")]/ul/li/a/text()'); - - final epUrls = xpath(res, '//*[@class="list-episodes list-group"]/a/@href'); - final dates = - xpath(res, '//*[@class="list-episodes list-group"]/a/span/text()'); - final names = xpath(res, '//*[@class="list-episodes list-group"]/a/text()'); - List episodes = []; - - for (var i = 0; i < names.length; i++) { - final date = dates[i]; - final name = names[i]; - episodes.add( - "Episode ${regExp(name.replaceAll(date, ""), r".* (\d*) [VvfF]{1,1}", '', 1, 1)}"); - } - final dateUploads = parseDates(dates, "dd MMMM yyyy", "fr"); - - List? episodesList = []; - for (var i = 0; i < episodes.length; i++) { - MChapter episode = MChapter(); - episode.name = episodes[i]; - episode.url = epUrls[i]; - episode.dateUpload = dateUploads[i]; - episodesList.add(episode); - } - - anime.chapters = episodesList; - return anime; - } - - @override - Future> getVideoList(MSource source, String url) async { - final res = await http('GET', json.encode({"url": url})); - - final servers = xpath(res, '//*[@id="nav-tabContent"]/div/iframe/@src'); - List videos = []; - for (var url in servers) { - final datasServer = { - "url": fixUrl(url), - "headers": {"X-Requested-With": "XMLHttpRequest"} - }; - - final resServer = await http('GET', json.encode(datasServer)); - final serverUrl = - fixUrl(regExp(resServer, r"data-url='([^']+)'", '', 1, 1)); - List a = []; - if (serverUrl.contains("https://streamwish")) { - a = await streamWishExtractor(serverUrl, "StreamWish"); - } else if (serverUrl.contains("sibnet")) { - a = await sibnetExtractor(serverUrl); - } else if (serverUrl.contains("https://doo")) { - a = await doodExtractor(serverUrl); - } else if (serverUrl.contains("https://voe.sx")) { - a = await voeExtractor(serverUrl, null); - } else if (serverUrl.contains("https://ok.ru")) { - a = await okruExtractor(serverUrl); - } - videos.addAll(a); - } - - return videos; - } - - String fixUrl(String url) { - return regExp(url, r"^(?:(?:https?:)?//|www\.)", 'https://', 0, 0); - } -} - -OtakuFr main() { - return OtakuFr(); -} diff --git a/anime/src/fr/otakufr/otakufr-v0.0.45.dart b/anime/src/fr/otakufr/otakufr-v0.0.45.dart new file mode 100644 index 00000000..29b40b13 --- /dev/null +++ b/anime/src/fr/otakufr/otakufr-v0.0.45.dart @@ -0,0 +1,448 @@ +import 'package:mangayomi/bridge_lib.dart'; +import 'dart:convert'; + +class OtakuFr extends MProvider { + OtakuFr(); + + @override + Future getPopular(MSource source, int page) async { + final data = { + "url": "${source.baseUrl}/toute-la-liste-affiches/page/$page/?q=." + }; + final res = await http('GET', json.encode(data)); + + List animeList = []; + final urls = + xpath(res, '//*[@class="list"]/article/div/div/figure/a/@href'); + final names = + xpath(res, '//*[@class="list"]/article/div/div/figure/a/img/@title'); + final images = + xpath(res, '//*[@class="list"]/article/div/div/figure/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); + } + final nextPage = xpath(res, '//a[@class="next page-link"]/@href'); + return MPages(animeList, nextPage.isNotEmpty); + } + + @override + Future getLatestUpdates(MSource source, int page) async { + final data = {"url": "${source.baseUrl}/page/$page/"}; + final res = await http('GET', json.encode(data)); + + List animeList = []; + final urls = xpath(res, '//*[@class="episode"]/div/a/@href'); + final namess = xpath(res, '//*[@class="episode"]/div/a/text()'); + List names = []; + for (var name in namess) { + names.add(regExp( + name, + r'(?<=\bS\d\s*|)\d{2}\s*(?=\b(Vostfr|vostfr|VF|Vf|vf|\(VF\)|\(vf\)|\(Vf\)|\(Vostfr\)\b))?', + '', + 0, + 0) + .replaceAll(' vostfr', '') + .replaceAll(' Vostfr', '') + .replaceAll(' VF', '') + .replaceAll(' Vf', '') + .replaceAll(' vf', '') + .replaceAll(' (VF)', '') + .replaceAll(' (vf)', '') + .replaceAll(' (vf)', '') + .replaceAll(' (Vf)', '') + .replaceAll(' (Vostfr)', '')); + } + final images = xpath(res, '//*[@class="episode"]/div/figure/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); + } + final nextPage = xpath(res, '//a[@class="next page-link"]/@href'); + return MPages(animeList, nextPage.isNotEmpty); + } + + @override + Future search( + MSource source, String query, int page, FilterList filterList) async { + final filters = filterList.filters; + String url = ""; + if (query.isNotEmpty) { + url = "${source.baseUrl}/toute-la-liste-affiches/page/$page/?q=$query"; + } else { + for (var filter in filters) { + if (filter.type == "GenreFilter") { + if (filter.state != 0) { + url = + "${source.baseUrl}/${filter.values[filter.state].value}page/$page"; + } + } else if (filter.type == "SubPageFilter") { + if (url.isEmpty) { + if (filter.state != 0) { + url = + "${source.baseUrl}/${filter.values[filter.state].value}page/$page"; + } + } + } + } + } + final data = {"url": url}; + final res = await http('GET', json.encode(data)); + + List animeList = []; + final urls = + xpath(res, '//*[@class="list"]/article/div/div/figure/a/@href'); + final names = + xpath(res, '//*[@class="list"]/article/div/div/figure/a/img/@title'); + final images = + xpath(res, '//*[@class="list"]/article/div/div/figure/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); + } + final nextPage = xpath(res, '//a[@class="next page-link"]/@href'); + return MPages(animeList, nextPage.isNotEmpty); + } + + @override + Future getDetail(MSource source, String url) async { + final statusList = [ + {"En cours": 0, "Terminé": 1} + ]; + final data = {"url": url}; + String res = await http('GET', json.encode(data)); + MManga anime = MManga(); + final originalUrl = xpath(res, + '//*[@class="breadcrumb"]/li[@class="breadcrumb-item"][2]/a/@href'); + if (originalUrl.isNotEmpty) { + final newData = {"url": originalUrl.first}; + res = await http('GET', json.encode(newData)); + } + final description = + xpath(res, '//*[@class="episode fz-sm synop"]/p/text()'); + if (description.isNotEmpty) { + anime.description = description.first.replaceAll("Synopsis:", ""); + } + final status = xpath(res, + '//*[@class="list-unstyled"]/li[contains(text(),"Statut")]/text()'); + if (status.isNotEmpty) { + anime.status = + parseStatus(status.first.replaceAll("Statut: ", ""), statusList); + } + + anime.genre = xpath(res, + '//*[@class="list-unstyled"]/li[contains(text(),"Genre")]/ul/li/a/text()'); + + final epUrls = xpath(res, '//*[@class="list-episodes list-group"]/a/@href'); + final dates = + xpath(res, '//*[@class="list-episodes list-group"]/a/span/text()'); + final names = xpath(res, '//*[@class="list-episodes list-group"]/a/text()'); + List episodes = []; + + for (var i = 0; i < names.length; i++) { + final date = dates[i]; + final name = names[i]; + episodes.add( + "Episode ${regExp(name.replaceAll(date, ""), r".* (\d*) [VvfF]{1,1}", '', 1, 1)}"); + } + final dateUploads = parseDates(dates, "dd MMMM yyyy", "fr"); + + List? episodesList = []; + for (var i = 0; i < episodes.length; i++) { + MChapter episode = MChapter(); + episode.name = episodes[i]; + episode.url = epUrls[i]; + episode.dateUpload = dateUploads[i]; + episodesList.add(episode); + } + + anime.chapters = episodesList; + return anime; + } + + @override + Future> getVideoList(MSource source, String url) async { + final res = await http('GET', json.encode({"url": url})); + + final servers = xpath(res, '//*[@id="nav-tabContent"]/div/iframe/@src'); + List videos = []; + final hosterSelection = preferenceHosterSelection(source.id); + for (var url in servers) { + final datasServer = { + "url": fixUrl(url), + "headers": {"X-Requested-With": "XMLHttpRequest"} + }; + + final resServer = await http('GET', json.encode(datasServer)); + final serverUrl = + fixUrl(regExp(resServer, r"data-url='([^']+)'", '', 1, 1)); + List a = []; + if (serverUrl.contains("https://streamwish") && + hosterSelection.contains("Streamwish")) { + a = await streamWishExtractor(serverUrl, "StreamWish"); + } else if (serverUrl.contains("sibnet") && + hosterSelection.contains("Sibnet")) { + a = await sibnetExtractor(serverUrl); + } else if (serverUrl.contains("https://doo") && + hosterSelection.contains("Doodstream")) { + a = await doodExtractor(serverUrl); + } else if (serverUrl.contains("https://voe.sx") && + hosterSelection.contains("Voe")) { + a = await voeExtractor(serverUrl, null); + } else if (serverUrl.contains("https://ok.ru") && + hosterSelection.contains("Okru")) { + a = await okruExtractor(serverUrl); + } else if (serverUrl.contains("vadbam") && + hosterSelection.contains("Vidbm")) { + a = await vidbmExtractor(serverUrl); + } else if (serverUrl.contains("upstream") && + hosterSelection.contains("Upstream")) { + a = await upstreamExtractor(serverUrl); + } else if (serverUrl.contains("sendvid") && + hosterSelection.contains("Sendvid")) { + a = await sendVidExtractor(serverUrl, null, ""); + } + videos.addAll(a); + } + + return videos; + } + + String fixUrl(String url) { + return regExp(url, r"^(?:(?:https?:)?//|www\.)", 'https://', 0, 0); + } + + @override + List getFilterList(MSource source) { + return [ + HeaderFilter("La recherche de texte ignore les filtres"), + SelectFilter("GenreFilter", "Genre", 0, [ + SelectFilterOption("", ""), + SelectFilterOption("Action", "/genre/action/"), + SelectFilterOption("Aventure", "/genre/aventure/"), + SelectFilterOption("Comedie", "/genre/comedie/"), + SelectFilterOption("Crime", "/genre/crime/"), + SelectFilterOption("Démons", "/genre/demons/"), + SelectFilterOption("Drame", "/genre/drame/"), + SelectFilterOption("Ecchi", "/genre/ecchi/"), + SelectFilterOption("Espace", "/genre/espace/"), + SelectFilterOption("Fantastique", "/genre/fantastique/"), + SelectFilterOption("Gore", "/genre/gore/"), + SelectFilterOption("Harem", "/genre/harem/"), + SelectFilterOption("Historique", "/genre/historique/"), + SelectFilterOption("Horreur", "/genre/horreur/"), + SelectFilterOption("Isekai", "/genre/isekai/"), + SelectFilterOption("Jeux", "/genre/jeu/"), + SelectFilterOption("L'école", "/genre/lecole/"), + SelectFilterOption("Magical girls", "/genre/magical-girls/"), + SelectFilterOption("Magie", "/genre/magie/"), + SelectFilterOption("Martial Arts", "/genre/martial-arts/"), + SelectFilterOption("Mecha", "/genre/mecha/"), + SelectFilterOption("Militaire", "/genre/militaire/"), + SelectFilterOption("Musique", "/genre/musique/"), + SelectFilterOption("Mysterieux", "/genre/mysterieux/"), + SelectFilterOption("Parodie", "/genre/Parodie/"), + SelectFilterOption("Police", "/genre/police/"), + SelectFilterOption("Psychologique", "/genre/psychologique/"), + SelectFilterOption("Romance", "/genre/romance/"), + SelectFilterOption("Samurai", "/genre/samurai/"), + SelectFilterOption("Sci-Fi", "/genre/sci-fi/"), + SelectFilterOption("Seinen", "/genre/seinen/"), + SelectFilterOption("Shoujo", "/genre/shoujo/"), + SelectFilterOption("Shoujo Ai", "/genre/shoujo-ai/"), + SelectFilterOption("Shounen", "/genre/shounen/"), + SelectFilterOption("Shounen Ai", "/genre/shounen-ai/"), + SelectFilterOption("Sport", "/genre/sport/"), + SelectFilterOption("Super Power", "/genre/super-power/"), + SelectFilterOption("Surnaturel", "/genre/surnaturel/"), + SelectFilterOption("Suspense", "/genre/suspense/"), + SelectFilterOption("Thriller", "/genre/thriller/"), + SelectFilterOption("Tranche de vie", "/genre/tranche-de-vie/"), + SelectFilterOption("Vampire", "/genre/vampire/") + ]), + SelectFilter("SubPageFilter", "Sous page", 0, [ + SelectFilterOption("", ""), + SelectFilterOption("Terminé", "/termine/"), + SelectFilterOption("Film", "/film/"), + ]) + ]; + } + + @override + List getSourcePreferences(MSource source) { + 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: [ + "Streamwish", + "Doodstream", + "Sendvid", + "Vidbm", + "Okru", + "Voe", + "Sibnet", + "Upstream" + ], + entryValues: [ + "Streamwish", + "Doodstream", + "Sendvid", + "Vidbm", + "Okru", + "Voe", + "Sibnet", + "Upstream" + ], + values: [ + "Streamwish", + "Doodstream", + "Sendvid", + "Vidbm", + "Okru", + "Voe", + "Sibnet", + "Upstream" + ]), + ]; + } + + 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"); + } + + Future> upstreamExtractor(String url) async { + final res = await http('GET', json.encode({"url": url})); + final js = xpath(res, '//script[contains(text(), "m3u8")]/text()'); + if (js.isEmpty) { + return []; + } + final masterUrl = + substringBefore(substringAfter(evalJs(js.first), "{file:\""), "\"}"); + final masterPlaylistRes = + await http('GET', json.encode({"url": masterUrl})); + 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> vidbmExtractor(String url) async { + final res = await http('GET', json.encode({"url": url})); + final js = xpath(res, + '//script[contains(text(), "m3u8") or contains(text(), "mp4")]/text()'); + if (js.isEmpty) { + return []; + } + final masterUrl = substringBefore(substringAfter(js.first, "source"), "\""); + final quality = substringBefore( + substringAfter( + substringBefore( + substringAfter(substringAfter(js.first, "source"), "file"), + "]"), + "label:\""), + "\""); + List videos = []; + if (masterUrl.contains("m3u8")) { + final masterPlaylistRes = + await http('GET', json.encode({"url": masterUrl})); + + 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 = "Vidbm - $quality"; + videos.add(video); + } + return videos; + } else { + MVideo video = MVideo(); + video + ..url = masterUrl + ..originalUrl = masterUrl + ..quality = "Vidbm - $quality"; + videos.add(video); + } + return videos; + } +} + +OtakuFr main() { + return OtakuFr(); +} diff --git a/anime/src/fr/otakufr/source.dart b/anime/src/fr/otakufr/source.dart index c9495929..e54341c5 100644 --- a/anime/src/fr/otakufr/source.dart +++ b/anime/src/fr/otakufr/source.dart @@ -2,7 +2,7 @@ import '../../../../model/source.dart'; import '../../../../utils/utils.dart'; Source get otakufr => _otakufr; -const otakufrVersion = "0.0.4"; +const otakufrVersion = "0.0.45"; const otakufrCodeUrl = "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/anime/src/fr/otakufr/otakufr-v$otakufrVersion.dart"; Source _otakufr = Source( diff --git a/anime/src/id/oploverz/oploverz-v0.0.15.dart b/anime/src/id/oploverz/oploverz-v0.0.2.dart similarity index 95% rename from anime/src/id/oploverz/oploverz-v0.0.15.dart rename to anime/src/id/oploverz/oploverz-v0.0.2.dart index d2e00156..6ffe04d1 100644 --- a/anime/src/id/oploverz/oploverz-v0.0.15.dart +++ b/anime/src/id/oploverz/oploverz-v0.0.2.dart @@ -98,12 +98,11 @@ class OploVerz extends MProvider { xpath(ress, '//iframe[@class="playeriframe"]/@src').first; final resPlayer = await http('GET', json.encode({"url": playerLink})); var resJson = substringBefore(substringAfter(resPlayer, "= "), "<"); - var streams = - json.decode(getMapValue(resJson, "streams", encode: true)) as List; + var streams = json.decode(resJson)["streams"] as List>; List videos = []; for (var stream in streams) { - final videoUrl = getMapValue(stream, "play_url"); - final quality = getQuality(getMapValue(stream, "format_id")); + String videoUrl = stream["play_url"]; + final quality = getQuality(stream["format_id"]); MVideo video = MVideo(); video diff --git a/anime/src/id/oploverz/source.dart b/anime/src/id/oploverz/source.dart index 290be7f0..be58a1ea 100644 --- a/anime/src/id/oploverz/source.dart +++ b/anime/src/id/oploverz/source.dart @@ -2,7 +2,7 @@ import '../../../../model/source.dart'; import '../../../../utils/utils.dart'; Source get oploverz => _oploverz; -const oploverzVersion = "0.0.15"; +const oploverzVersion = "0.0.2"; const oploverzCodeUrl = "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/anime/src/id/oploverz/oploverz-v$oploverzVersion.dart"; Source _oploverz = Source( diff --git a/manga/multisrc/madara/madara-v0.0.4.dart b/manga/multisrc/madara/madara-v0.0.4.dart index 7393036b..c6c0c3e7 100644 --- a/manga/multisrc/madara/madara-v0.0.4.dart +++ b/manga/multisrc/madara/madara-v0.0.4.dart @@ -356,7 +356,7 @@ class Madara extends MProvider { } @override - List getFilterList() { + List getFilterList(MSource source) { return [ TextFilter("AuthorFilter", "Author"), TextFilter("ArtistFilter", "Artist"), diff --git a/manga/multisrc/mangareader/mangareader-v0.0.5.dart b/manga/multisrc/mangareader/mangareader-v0.0.5.dart index 20d8bc0f..e1b392b6 100644 --- a/manga/multisrc/mangareader/mangareader-v0.0.5.dart +++ b/manga/multisrc/mangareader/mangareader-v0.0.5.dart @@ -236,7 +236,7 @@ class MangaReader extends MProvider { } @override - List getFilterList() { + List getFilterList(MSource source) { return [ SeparatorFilter(), TextFilter("AuthorFilter", "Author"), diff --git a/manga/multisrc/mmrcms/mmrcms-v0.0.4.dart b/manga/multisrc/mmrcms/mmrcms-v0.0.4.dart index d0f522cf..d46a8f9c 100644 --- a/manga/multisrc/mmrcms/mmrcms-v0.0.4.dart +++ b/manga/multisrc/mmrcms/mmrcms-v0.0.4.dart @@ -232,7 +232,7 @@ class MMRCMS extends MProvider { return pagesUrl; } - List getFilterList() { + List getFilterList(MSource source) { return [ HeaderFilter("NOTE: Ignored if using text search!"), SeparatorFilter(), diff --git a/manga/multisrc/nepnep/nepnep-v0.0.4.dart b/manga/multisrc/nepnep/nepnep-v0.0.4.dart index 86f4a6cc..b7b014f9 100644 --- a/manga/multisrc/nepnep/nepnep-v0.0.4.dart +++ b/manga/multisrc/nepnep/nepnep-v0.0.4.dart @@ -10,9 +10,15 @@ class NepNep extends MProvider { final res = await http('GET', json.encode(data)); final directory = directoryFromDocument(res); - final resSort = sortMapList(json.decode(directory), "vm", 1); + final resSort = (json.decode(directory) as List>); + resSort.sort( + (Map a, Map b) => + (a["vm"] as String).compareTo(b["vm"] as String), + ); - return parseDirectory(resSort); + // sortMapList(json.decode(directory), "vm", 1); + + return parseDirectory(json.encode(resSort)); } @override @@ -308,7 +314,7 @@ class NepNep extends MProvider { } @override - List getFilterList() { + List getFilterList(MSource source) { return [ TextFilter("YearFilter", "Years"), TextFilter("AuthorFilter", "Author"), diff --git a/manga/src/all/batoto/batoto-v0.0.4.dart b/manga/src/all/batoto/batoto-v0.0.45.dart similarity index 97% rename from manga/src/all/batoto/batoto-v0.0.4.dart rename to manga/src/all/batoto/batoto-v0.0.45.dart index 060f8176..4be7bdee 100644 --- a/manga/src/all/batoto/batoto-v0.0.4.dart +++ b/manga/src/all/batoto/batoto-v0.0.45.dart @@ -7,7 +7,7 @@ class Batoto extends MProvider { @override Future getPopular(MSource source, int page) async { final url = - "${source.baseUrl}/browse?${lang(source.lang)}&sort=views_a&page=$page"; + "${preferenceMirror(source.id)}/browse?${lang(source.lang)}&sort=views_a&page=$page"; final data = {"url": url}; final res = await http('GET', json.encode(data)); return mangaElementM(res, source); @@ -16,7 +16,7 @@ class Batoto extends MProvider { @override Future getLatestUpdates(MSource source, int page) async { final url = - "${source.baseUrl}/browse?${lang(source.lang)}&sort=update&page=$page"; + "${preferenceMirror(source.id)}/browse?${lang(source.lang)}&sort=update&page=$page"; final data = {"url": url}; final res = await http('GET', json.encode(data)); return mangaElementM(res, source); @@ -30,7 +30,7 @@ class Batoto extends MProvider { String min = ""; String max = ""; if (query.isNotEmpty) { - url = "${source.baseUrl}/search?word=$query&page=$page"; + url = "${preferenceMirror(source.id)}/search?word=$query&page=$page"; for (var filter in filters) { if (filter.type == "LetterFilter") { if (filter.state == 1) { @@ -39,7 +39,7 @@ class Batoto extends MProvider { } } } else { - url = "${source.baseUrl}/browse"; + url = "${preferenceMirror(source.id)}/browse"; for (var filter in filters) { if (filter.type == "LangGroupFilter") { final langs = (filter.state as List).where((e) => e.state).toList(); @@ -111,7 +111,7 @@ class Batoto extends MProvider { {"Ongoing": 0, "Completed": 1, "Cancelled": 3, "Hiatus": 2} ]; - final data = {"url": "${source.baseUrl}$url"}; + final data = {"url": "${preferenceMirror(source.id)}$url"}; final res = await http('GET', json.encode(data)); MManga manga = MManga(); final workStatus = xpath(res, @@ -171,7 +171,7 @@ class Batoto extends MProvider { @override Future> getPageList(MSource source, String url) async { - final datas = {"url": "${source.baseUrl}$url"}; + final datas = {"url": "${preferenceMirror(source.id)}$url"}; final res = await http('GET', json.encode(datas)); final script = xpath(res, @@ -273,7 +273,7 @@ class Batoto extends MProvider { } @override - List getFilterList() { + List getFilterList(MSource source) { return [ SelectFilter("LetterFilter", "Letter matching mode (Slow)", 0, [ SelectFilterOption("Disabled", "disabled"), @@ -1663,36 +1663,51 @@ class Batoto extends MProvider { SeparatorFilter(), ]; } -} -Map getMirrorPref() { - return { - "bato.to": "https://bato.to", - "batocomic.com": "https://batocomic.com", - "batocomic.net": "https://batocomic.net", - "batocomic.org": "https://batocomic.org", - "batotoo.com": "https://batotoo.com", - "batotwo.com": "https://batotwo.com", - "battwo.com": "https://battwo.com", - "comiko.net": "https://comiko.net", - "comiko.org": "https://comiko.org", - "mangatoto.com": "https://mangatoto.com", - "mangatoto.net": "https://mangatoto.net", - "mangatoto.org": "https://mangatoto.org", - "readtoto.com": "https://readtoto.com", - "readtoto.net": "https://readtoto.net", - "readtoto.org": "https://readtoto.org", - "dto.to": "https://dto.to", - "hto.to": "https://hto.to", - "mto.to": "https://mto.to", - "wto.to": "https://wto.to", - "xbato.com": "https://xbato.com", - "xbato.net": "https://xbato.net", - "xbato.org": "https://xbato.org", - "zbato.com": "https://zbato.com", - "zbato.net": "https://zbato.net", - "zbato.org": "https://zbato.org", - }; + @override + List getSourcePreferences(MSource source) { + return [ + ListPreference( + key: "mirror", + title: "Mirror", + summary: "", + valueIndex: 0, + entries: mirrorEntries, + entryValues: mirrorEntries.map((e) => "https://$e").toList()), + ]; + } + + List mirrorEntries = [ + "bato.to", + "batocomic.com", + "batocomic.net", + "batocomic.org", + "batotoo.com", + "batotwo.com", + "battwo.com", + "comiko.net", + "comiko.org", + "mangatoto.com", + "mangatoto.net", + "mangatoto.org", + "readtoto.com", + "readtoto.net", + "readtoto.org", + "dto.to", + "hto.to", + "mto.to", + "wto.to", + "xbato.com", + "xbato.net", + "xbato.org", + "zbato.com", + "zbato.net", + "zbato.org", + ]; + + String preferenceMirror(int sourceId) { + return getPreferenceValue(sourceId, "mirror"); + } } Batoto main() { diff --git a/manga/src/all/batoto/sources.dart b/manga/src/all/batoto/sources.dart index e394fbf0..70f5efd8 100644 --- a/manga/src/all/batoto/sources.dart +++ b/manga/src/all/batoto/sources.dart @@ -1,7 +1,7 @@ import '../../../../model/source.dart'; import '../../../../utils/utils.dart'; -const batotoVersion = "0.0.4"; +const batotoVersion = "0.0.45"; const batotoSourceCodeUrl = "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/manga/src/all/batoto/batoto-v$batotoVersion.dart"; diff --git a/manga/src/all/comick/comick-v0.0.4.dart b/manga/src/all/comick/comick-v0.0.4.dart index 3afbaebc..69822d77 100644 --- a/manga/src/all/comick/comick-v0.0.4.dart +++ b/manga/src/all/comick/comick-v0.0.4.dart @@ -215,7 +215,7 @@ class ComickFun extends MProvider { } @override - List getFilterList() { + List getFilterList(MSource source) { return [ HeaderFilter("The filter is ignored when using text search."), GroupFilter("GenreFilter", "Genre", [ diff --git a/manga/src/all/mangadex/mangadex-v0.0.45.dart b/manga/src/all/mangadex/mangadex-v0.0.5.dart similarity index 85% rename from manga/src/all/mangadex/mangadex-v0.0.45.dart rename to manga/src/all/mangadex/mangadex-v0.0.5.dart index 3660487d..77d681e6 100644 --- a/manga/src/all/mangadex/mangadex-v0.0.45.dart +++ b/manga/src/all/mangadex/mangadex-v0.0.5.dart @@ -8,7 +8,7 @@ class MangaDex extends MProvider { Future getPopular(MSource source, int page) async { page = (20 * (page - 1)); final url = - "https://api.mangadex.org/manga?limit=20&offset=$page&availableTranslatedLanguage[]=${source.lang}&includes[]=cover_art${getMDXContentRating()}&order[followedCount]=desc"; + "https://api.mangadex.org/manga?limit=20&offset=$page&availableTranslatedLanguage[]=${source.lang}&includes[]=cover_art${preferenceContentRating(source.id)}${preferenceOriginalLanguages(source.id)}&order[followedCount]=desc"; final datas = {"url": url}; final res = await http('GET', json.encode(datas)); return mangaRes(res, source); @@ -24,12 +24,12 @@ class MangaDex extends MProvider { final mangaIds = jsonPathToString(ress, r'$.data[*].relationships[*].id', '.--') .split('.--'); - String mangaIdss = "".toString(); + String mangaIdss = ""; for (var id in mangaIds) { mangaIdss += "&ids[]=$id"; } final newUrl = - "https://api.mangadex.org/manga?includes[]=cover_art&limit=${mangaIds.length}${getMDXContentRating()}$mangaIdss"; + "https://api.mangadex.org/manga?includes[]=cover_art&limit=${mangaIds.length}${preferenceContentRating(source.id)}${preferenceOriginalLanguages(source.id)}$mangaIdss"; final res = await http('GET', json.encode({"url": newUrl})); return mangaRes(res, source); } @@ -188,7 +188,7 @@ class MangaDex extends MProvider { final mangaId = url.split('/').last; final paginatedChapterList = - await paginatedChapterListRequest(mangaId, 0, source.lang); + await paginatedChapterListRequest(mangaId, 0, source.lang, source.id); final chapterList = jsonPathToString(paginatedChapterList, r'$.data[*]', '_.').split('_.'); int limit = @@ -206,8 +206,8 @@ class MangaDex extends MProvider { var hasMoreResults = (limit + offset) < total; while (hasMoreResults) { offset += limit; - var newRequest = - await paginatedChapterListRequest(mangaId, offset, source.lang); + var newRequest = await paginatedChapterListRequest( + mangaId, offset, source.lang, source.id); int total = int.parse(jsonPathToString(newRequest, r'$.total', '')); final chapterList = jsonPathToString(paginatedChapterList, r'$.data[*]', '_.') @@ -244,7 +244,7 @@ class MangaDex extends MProvider { for (var e in resJson) { MManga manga = MManga(); manga.name = findTitle(json.encode(e), source.lang); - manga.imageUrl = getCover(json.encode(e)); + manga.imageUrl = getCover(json.encode(e), source.id); manga.link = "/manga/${getMapValue(json.encode(e), "id")}"; mangaList.add(manga); } @@ -253,28 +253,28 @@ class MangaDex extends MProvider { List getChapters(int length, String paginatedChapterListA) { List chaptersList = []; - String paginatedChapterList = paginatedChapterListA.toString(); + String paginatedChapterList = paginatedChapterListA; final dataList = jsonPathToList(paginatedChapterList, r'$.data[*]', 0); for (var res in dataList) { - String scan = "".toString(); + String scan = ""; final groups = jsonPathToList(res, r'$.relationships[?@.id!="00e03853-1b96-4f41-9542-c71b8692033b"]', 0); - String chapName = "".toString(); + String chapName = ""; for (var element in groups) { final data = getMapValue(element, "attributes", encode: true); if (data.isNotEmpty) { final name = getMapValue(data, "name"); - scan += "$name".toString(); + scan += "$name"; final username = getMapValue(data, "username"); if (username.isNotEmpty) { if (scan.isEmpty) { - scan += "Uploaded by $username".toString(); + scan += "Uploaded by $username"; } } } } if (scan.isEmpty) { - scan = "No Group".toString(); + scan = "No Group"; } final dataRes = getMapValue(res, "attributes", encode: true); if (dataRes.isNotEmpty) { @@ -282,26 +282,26 @@ class MangaDex extends MProvider { final volume = getMapValue(data, "volume"); if (volume.isNotEmpty) { if (volume != "null") { - chapName = "Vol.$volume ".toString(); + chapName = "Vol.$volume "; } } final chapter = getMapValue(data, "chapter"); if (chapter.isNotEmpty) { if (chapter != "null") { - chapName += "Ch.$chapter ".toString(); + chapName += "Ch.$chapter "; } } final title = getMapValue(data, "title"); if (title.isNotEmpty) { if (title != "null") { if (chapName.isNotEmpty) { - chapName += "- ".toString(); + chapName += "- "; } - chapName += "$title".toString(); + chapName += "$title"; } } if (chapName.isEmpty) { - chapName += "Oneshot".toString(); + chapName += "Oneshot"; } final date = getMapValue(data, "publishAt"); final id = getMapValue(res, "id"); @@ -319,19 +319,13 @@ class MangaDex extends MProvider { } Future paginatedChapterListRequest( - String mangaId, int offset, String lang) async { + String mangaId, int offset, String lang, int sourceId) async { final url = - 'https://api.mangadex.org/manga/$mangaId/feed?limit=500&offset=$offset&includes[]=user&includes[]=scanlation_group&order[volume]=desc&order[chapter]=desc&translatedLanguage[]=$lang&includeFuturePublishAt=0&includeEmptyPages=0${getMDXContentRating()}'; + 'https://api.mangadex.org/manga/$mangaId/feed?limit=500&offset=$offset&includes[]=user&includes[]=scanlation_group&order[volume]=desc&order[chapter]=desc&translatedLanguage[]=$lang&includeFuturePublishAt=0&includeEmptyPages=0${preferenceContentRating(sourceId)}'; final res = await http('GET', json.encode({"url": url})); return res; } - String getMDXContentRating() { - String ctnRating = - "&contentRating[]=suggestive&contentRating[]=safe&contentRating[]=erotica&contentRating[]=pornographic"; - return ctnRating; - } - String findTitle(String dataRes, String lang) { final attributes = getMapValue(dataRes, "attributes", encode: true); final altTitlesJ = @@ -349,7 +343,8 @@ class MangaDex extends MProvider { return title; } - String getCover(String dataRes) { + String getCover(String dataRes, int sourceId) { + final coverQuality = getPreferenceValue(sourceId, "cover_quality"); final relationships = json .decode(getMapValue(dataRes, "relationships", encode: true)) as List; String coverFileName = "".toString(); @@ -360,7 +355,7 @@ class MangaDex extends MProvider { final attributes = getMapValue(json.encode(a), "attributes", encode: true); coverFileName = - "https://uploads.mangadex.org/covers/${getMapValue(dataRes, "id")}/${getMapValue(attributes, "fileName")}"; + "https://uploads.mangadex.org/covers/${getMapValue(dataRes, "id")}/${getMapValue(attributes, "fileName")}$coverQuality"; } } } @@ -368,7 +363,7 @@ class MangaDex extends MProvider { } @override - List getFilterList() { + List getFilterList(MSource source) { return [ CheckBoxFilter( "Has available chapters", "", "HasAvailableChaptersFilter"), @@ -510,6 +505,83 @@ class MangaDex extends MProvider { ]; } + @override + List getSourcePreferences(MSource source) { + return [ + ListPreference( + key: "cover_quality", + title: "Cover quality", + summary: "", + valueIndex: 0, + entries: ["Original", "Medium", "Low"], + entryValues: ["", ".512.jpg", ".256.jpg"]), + MultiSelectListPreference( + key: "content_rating", + title: "Default content rating", + summary: "Show content with the selected rating by default", + valueIndex: 0, + entries: [ + "safe", + "suggestive", + "erotica", + "pornographic" + ], + entryValues: [ + "contentRating[]=safe", + "contentRating[]=suggestive", + "contentRating[]=erotica", + "contentRating[]=pornographic" + ], + values: [ + "contentRating[]=safe", + "contentRating[]=suggestive" + ]), + MultiSelectListPreference( + key: "original_languages", + title: "Filter original languages", + summary: + "Only show content that was originaly published in the selected languages in both latest and browse", + valueIndex: 0, + entries: [ + "Japanese", + "Chinese", + "Korean" + ], + entryValues: [ + "originalLanguage[]=ja", + "originalLanguage[]=zh&originalLanguage[]=zh-hk", + "originalLanguage[]=ko" + ], + values: []), + ]; + } + + String preferenceContentRating(int sourceId) { + final contentRating = + getPreferenceValue(sourceId, "content_rating") as List; + String contentRatingStr = ""; + if (contentRating.isNotEmpty) { + contentRatingStr = "&"; + for (var ctn in contentRating) { + contentRatingStr += "&$ctn"; + } + } + return contentRatingStr; + } + + String preferenceOriginalLanguages(int sourceId) { + final originalLanguages = + getPreferenceValue(sourceId, "original_languages") as List; + String originalLanguagesStr = ""; + if (originalLanguages.isNotEmpty) { + originalLanguagesStr = "&"; + for (var language in originalLanguages) { + originalLanguagesStr += "&$language"; + } + } + return originalLanguagesStr; + } + String ll(String url) { if (url.contains("?")) { return "&"; diff --git a/manga/src/all/mangadex/sources.dart b/manga/src/all/mangadex/sources.dart index 917c2fec..778ecfe9 100644 --- a/manga/src/all/mangadex/sources.dart +++ b/manga/src/all/mangadex/sources.dart @@ -4,7 +4,7 @@ import '../../../../utils/utils.dart'; const apiUrl = 'https://api.mangadex.org'; const baseUrl = 'https://mangadex.org'; const isNsfw = true; -const mangadexVersion = "0.0.45"; +const mangadexVersion = "0.0.5"; const mangadexSourceCodeUrl = "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/manga/src/all/mangadex/mangadex-v$mangadexVersion.dart"; String _iconUrl = getIconUrl("mangadex", "all"); diff --git a/manga/src/en/mangahere/mangahere-v0.0.4.dart b/manga/src/en/mangahere/mangahere-v0.0.4.dart index fb20f8da..91188591 100644 --- a/manga/src/en/mangahere/mangahere-v0.0.4.dart +++ b/manga/src/en/mangahere/mangahere-v0.0.4.dart @@ -259,7 +259,7 @@ class MangaHere extends MProvider { } @override - List getFilterList() { + List getFilterList(MSource source) { return [ SelectFilter("TypeList", "Type", 1, [ SelectFilterOption("American Manga", "5"),