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("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); }