diff --git a/anime/source_generator.dart b/anime/source_generator.dart index 386735ff..521a8e25 100644 --- a/anime/source_generator.dart +++ b/anime/source_generator.dart @@ -10,6 +10,7 @@ import 'src/all/nyaa/source.dart'; import 'src/all/yomiroll/source.dart'; import 'src/ar/okanime/source.dart'; import 'src/de/aniflix/source.dart'; +import 'src/de/animetoast/source.dart'; import 'src/en/animepahe/source.dart'; import 'src/en/aniwave/source.dart'; import 'src/en/dramacool/source.dart'; @@ -54,7 +55,8 @@ void main() { ...animeworldindiaSourcesList, nyaaSource, yomirollSource, - animepaheSource + animepaheSource, + animetoast ]; final List> jsonList = _sourcesList.map((source) => source.toJson()).toList(); diff --git a/anime/src/de/animetoast/animetoast.dart b/anime/src/de/animetoast/animetoast.dart new file mode 100644 index 00000000..0be52f54 --- /dev/null +++ b/anime/src/de/animetoast/animetoast.dart @@ -0,0 +1,234 @@ +import 'package:mangayomi/bridge_lib.dart'; + +class AnimeToast extends MProvider { + AnimeToast({required this.source}); + + MSource source; + + final Client client = Client(source); + + @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("div.tab-pane 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("div.tab-pane 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/anime/src/de/animetoast/icon.png b/anime/src/de/animetoast/icon.png new file mode 100644 index 00000000..172ddf3b Binary files /dev/null and b/anime/src/de/animetoast/icon.png differ diff --git a/anime/src/de/animetoast/source.dart b/anime/src/de/animetoast/source.dart new file mode 100644 index 00000000..b28dd8ad --- /dev/null +++ b/anime/src/de/animetoast/source.dart @@ -0,0 +1,16 @@ +import '../../../../model/source.dart'; + +Source get animetoast => _animetoast; +const _animetoastVersion = "0.0.1"; +const _animetoastCodeUrl = + "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/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/anime/src/de/animetoast/icon.png", + sourceCodeUrl: _animetoastCodeUrl, + version: _animetoastVersion, + isManga: false);