From e8d1712de995d4fd83a78e696135fd14036effcf Mon Sep 17 00:00:00 2001 From: kodjomoustapha <107993382+kodjodevf@users.noreply.github.com> Date: Mon, 30 Sep 2024 16:33:13 +0100 Subject: [PATCH] New source: AnimeOnlineNinja (es) --- dart/anime/anime_source_list.dart | 4 +- .../es/animeonlineninja/animeonlineninja.dart | 264 ++++++++++++++++++ dart/anime/src/es/animeonlineninja/icon.png | Bin 0 -> 4044 bytes .../anime/src/es/animeonlineninja/source.dart | 16 ++ 4 files changed, 283 insertions(+), 1 deletion(-) create mode 100644 dart/anime/src/es/animeonlineninja/animeonlineninja.dart create mode 100644 dart/anime/src/es/animeonlineninja/icon.png create mode 100644 dart/anime/src/es/animeonlineninja/source.dart diff --git a/dart/anime/anime_source_list.dart b/dart/anime/anime_source_list.dart index c3e01859..588bf52d 100644 --- a/dart/anime/anime_source_list.dart +++ b/dart/anime/anime_source_list.dart @@ -10,6 +10,7 @@ import 'src/en/animepahe/source.dart'; import 'src/en/dramacool/source.dart'; import 'src/en/gogoanime/source.dart'; import 'src/en/nineanimetv/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'; @@ -52,5 +53,6 @@ List dartAnimesourceList = [ animetoast, animesvision, diziwatchSource, - aniZoneSource + aniZoneSource, + animeonlineninjaSource ]; diff --git a/dart/anime/src/es/animeonlineninja/animeonlineninja.dart b/dart/anime/src/es/animeonlineninja/animeonlineninja.dart new file mode 100644 index 00000000..6e67f0c4 --- /dev/null +++ b/dart/anime/src/es/animeonlineninja/animeonlineninja.dart @@ -0,0 +1,264 @@ +import 'package:mangayomi/bridge_lib.dart'; +import 'dart:convert'; + +class AnimeOnlineNinja extends MProvider { + AnimeOnlineNinja({required this.source}); + + MSource source; + + final Client client = Client(source); + + @override + bool get supportsLatest => false; + + @override + Future getPopular(int page) async { + final res = + (await client.get(Uri.parse("${source.baseUrl}/tendencias"))).body; + return parseAnimeList(res); + } + + @override + Future search(String query, int page, FilterList filterList) async { + String pageStr = page == 1 ? "" : "page/$page/"; + final res = (await client.get(Uri.parse( + "${source.baseUrl}/$pageStr?s=${query.replaceAll(" ", "+")}"))) + .body; + return parseAnimeList(res, + selector: "div.result-item div.image a", + hasNextPage: parseHtml(res) + .selectFirst( + "div.pagination > *:last-child:not(span):not(.current)") + ?.text != + null); + } + + @override + Future getDetail(String url) async { + final res = (await client.get(Uri.parse("${source.baseUrl}$url"))).body; + MManga anime = MManga(); + final document = parseHtml(res); + anime.description = document.selectFirst("div#info").text; + anime.genre = document + .selectFirst("div.sheader") + .select("div.data > div.sgeneros > a") + .map((e) => e.text) + .toList(); + + List? episodesList = []; + final seasonElements = document.select("div#seasons > div"); + if (seasonElements.isEmpty) { + MChapter episode = MChapter(); + episode.name = "PelĂ­cula"; + episode.url = getUrlWithoutDomain(url); + episodesList.add(episode); + } else { + for (var seasonElement in seasonElements) { + final seasonName = seasonElement.selectFirst("span.se-t").text; + for (var epElement in seasonElement.select("ul.episodios > li")) { + final href = epElement.selectFirst("a[href]"); + final epNum = epElement.selectFirst('div.numerando')?.text ?? "0 - 0"; + MChapter episode = MChapter(); + episode.name = + "Season $seasonName x ${substringAfter(epNum, '- ')} ${href.text}"; + episode.url = getUrlWithoutDomain(href!.getHref); + 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; + final document = parseHtml(res); + final players = document.select("ul#playeroptionsul li"); + List videos = []; + for (var player in players) { + final name = player.selectFirst("span.title").text; + final type = player.attr("data-type"); + final id = player.attr("data-post"); + final num = player.attr("data-nume"); + final resUrl = (await client.get(Uri.parse( + "${source.baseUrl}/wp-json/dooplayer/v1/post/$id?type=$type&source=$num"))) + .body; + final url = + substringBefore(substringAfter(resUrl, "\"embed_url\":\""), "\",") + .replaceAll("\\", ""); + videos.addAll(await extractVideos(url, name)); + } + return sortVideos(videos, source.id); + } + + Future> extractVideos(String url, String lang) async { + List videos = []; + List a = []; + if (url.contains("saidochesto.top") || lang == "MULTISERVER") { + return await extractFromMulti(url); + } else if (url.contains("filemoon")) { + a = await filemoonExtractor(url, "", ""); + } else if (url.contains("https://dood") || + url.contains("https://ds2play") || + url.contains("https://d0")) { + a = await doodExtractor(url, "DoodStream"); + } else if (url.contains("streamtape")) { + a = await streamTapeExtractor(url, "StreamTape"); + } else if (url.contains("uqload")) { + a = await uqloadExtractor(url); + } else if (url.contains("wolfstream")) { + final resUrl = (await client.get(Uri.parse(url))).body; + final jsData = + parseHtml(resUrl).selectFirst("script:contains(sources)").text; + final videoUrl = + substringBefore(substringAfter(jsData, "{file:\""), "\""); + + MVideo video = MVideo(); + video + ..url = videoUrl + ..originalUrl = videoUrl + ..quality = "$lang WolfStream"; + + a = [video]; + } + videos.addAll(a); + + 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> extractFromMulti(String url) async { + final res = (await client.get(Uri.parse(url))).body; + final document = parseHtml(res); + + final prefLang = getPreferenceValue(source.id, "preferred_lang"); + String langSelector = ""; + if (prefLang.isEmpty) { + langSelector = "div.OD_$prefLang"; + } else { + langSelector = "div.OD_$prefLang"; + } + List videos = []; + for (var element in document.select("div.ODDIV $langSelector > li")) { + final hosterUrl = + substringBefore(substringAfter(element.attr("onclick"), "('"), "')"); + String lang = ""; + if (langSelector == "div") { + lang = substringBefore( + substringAfter(element.parent?.attr("class"), "OD_", ""), " "); + } else { + lang = prefLang; + } + videos.addAll(await extractVideos(hosterUrl, lang)); + } + + return videos; + } + + MPages parseAnimeList(String res, + {String selector = "article.w_item_a > a", bool hasNextPage = false}) { + final elements = parseHtml(res).select(selector); + List animeList = []; + for (var element in elements) { + final url = getUrlWithoutDomain(element.getHref); + if (!url.startsWith("/episodio/")) { + MManga anime = MManga(); + final img = element.selectFirst("img"); + anime.name = img.attr("alt"); + anime.imageUrl = img?.attr("data-src") ?? + img?.attr("data-lazy-src") ?? + img?.attr("srcset") ?? + img?.getSrc; + anime.link = url; + animeList.add(anime); + } + } + return MPages(animeList, hasNextPage); + } + + @override + List getSourcePreferences() { + return [ + ListPreference( + key: "preferred_lang", + title: "Preferred language", + summary: "", + valueIndex: 0, + entries: ["SUB", "All", "ES", "LAT"], + entryValues: ["SUB", "", "ES", "LAT"]), + ListPreference( + key: "preferred_server_", + title: "Preferred server", + summary: "", + valueIndex: 0, + entries: [ + "Filemoon", + "DoodStream", + "StreamTape", + "Uqload", + "WolfStream", + "saidochesto.top" + ], + entryValues: [ + "Filemoon", + "DoodStream", + "StreamTape", + "Uqload", + "WolfStream", + "saidochesto.top" + ]), + ]; + } + + List sortVideos(List videos, int sourceId) { + String prefLang = getPreferenceValue(source.id, "preferred_lang"); + String server = getPreferenceValue(sourceId, "preferred_server_"); + videos.sort((MVideo a, MVideo b) { + int qualityMatchA = 0; + + if (a.quality.toLowerCase().contains(prefLang.toLowerCase()) && + a.quality.toLowerCase().contains(server.toLowerCase())) { + qualityMatchA = 1; + } + int qualityMatchB = 0; + if (b.quality.toLowerCase().contains(prefLang.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; + } +} + +AnimeOnlineNinja main(MSource source) { + return AnimeOnlineNinja(source: source); +} diff --git a/dart/anime/src/es/animeonlineninja/icon.png b/dart/anime/src/es/animeonlineninja/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ab51a326a1bd40b66bd48161ccc3531db2c7f3da GIT binary patch literal 4044 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 _animeonlineninjaSource; +const _animeonlineninjaVersion = "0.0.1"; +const _animeonlineninjaSourceCodeUrl = + "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/es/animeonlineninja/animeonlineninja.dart"; +Source _animeonlineninjaSource = Source( + name: "AnimeOnline.Ninja", + baseUrl: "https://ww3.animeonline.ninja", + lang: "es", + typeSource: "single", + iconUrl: + "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/es/animeonlineninja/icon.png", + sourceCodeUrl: _animeonlineninjaSourceCodeUrl, + version: _animeonlineninjaVersion, + isManga: false);