From 500a5b5e01224764160ddf63edef8bb034660e9e Mon Sep 17 00:00:00 2001 From: kodjomoustapha <107993382+kodjodevf@users.noreply.github.com> Date: Tue, 26 Dec 2023 17:03:31 +0100 Subject: [PATCH] New source: YoMovies (HI) --- anime/source_generator.dart | 4 +- anime/src/en/hi/yomovies/icon.png | Bin 0 -> 3499 bytes anime/src/en/hi/yomovies/source.dart | 16 ++ anime/src/en/hi/yomovies/yomovies.dart | 346 +++++++++++++++++++++++++ 4 files changed, 365 insertions(+), 1 deletion(-) create mode 100644 anime/src/en/hi/yomovies/icon.png create mode 100644 anime/src/en/hi/yomovies/source.dart create mode 100644 anime/src/en/hi/yomovies/yomovies.dart diff --git a/anime/source_generator.dart b/anime/source_generator.dart index ca5fe23f..164298cf 100644 --- a/anime/source_generator.dart +++ b/anime/source_generator.dart @@ -9,6 +9,7 @@ import 'src/ar/okanime/source.dart'; import 'src/en/aniwave/source.dart'; import 'src/en/dramacool/source.dart'; import 'src/en/gogoanime/source.dart'; +import 'src/en/hi/yomovies/source.dart'; import 'src/en/kisskh/source.dart'; import 'src/en/uhdmovies/source.dart'; import 'src/fr/animesultra/source.dart'; @@ -38,7 +39,8 @@ void main() { uhdmoviesSource, ...datalifeengineSourcesList, filma24, - dramacoolSource + dramacoolSource, + yomoviesSource ]; final List> jsonList = _sourcesList.map((source) => source.toJson()).toList(); diff --git a/anime/src/en/hi/yomovies/icon.png b/anime/src/en/hi/yomovies/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..6afd2b3fc5e4c7c1f25c86a188edc544fcf06a57 GIT binary patch literal 3499 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 _yomoviesSource; +const _yomoviesVersion = "0.0.1"; +const _yomoviesSourceCodeUrl = + "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/anime/src/hi/yomovies/yomovies.dart"; +Source _yomoviesSource = Source( + name: "YoMovies", + baseUrl: "https://yomovies.boo", + lang: "hi", + typeSource: "single", + iconUrl: + "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/anime/src/hi/yomovies/icon.png", + sourceCodeUrl: _yomoviesSourceCodeUrl, + version: _yomoviesVersion, + isManga: false); diff --git a/anime/src/en/hi/yomovies/yomovies.dart b/anime/src/en/hi/yomovies/yomovies.dart new file mode 100644 index 00000000..e04385f3 --- /dev/null +++ b/anime/src/en/hi/yomovies/yomovies.dart @@ -0,0 +1,346 @@ +import 'package:mangayomi/bridge_lib.dart'; +import 'dart:convert'; + +class YoMovies extends MProvider { + YoMovies(); + + @override + bool get supportsLatest => false; + + @override + Future getPopular(MSource source, int page) async { + String pageNu = page == 1 ? "" : "page/$page/"; + final data = { + "url": "${preferenceBaseUrl(source.id)}/most-favorites/$pageNu" + }; + + final res = await http('GET', json.encode(data)); + final document = parseHtml(res); + return animeFromElement( + document.select("div.movies-list > div.ml-item"), + document.selectFirst("ul.pagination > li.active + li")?.getHref != + null); + } + + @override + Future getLatestUpdates(MSource source, int page) async { + return MPages([], false); + } + + @override + Future search( + MSource source, String query, int page, FilterList filterList) async { + final filters = filterList.filters; + String url = ""; + String pageNu = page == 1 ? "" : "/page/$page"; + if (query.isNotEmpty) { + url = "${preferenceBaseUrl(source.id)}$pageNu/?s=$query"; + } else { + for (var filter in filters) { + if (filter.type.isNotEmpty) { + final first = filter.values[filter.state].value; + if (first.isNotEmpty) { + url = first; + } + } + } + url = "${preferenceBaseUrl(source.id)}$url$pageNu"; + } + final res = await http('GET', json.encode({"url": url})); + final document = parseHtml(res); + return animeFromElement( + document.select("div.movies-list > div.ml-item"), + document.selectFirst("ul.pagination > li.active + li")?.getHref != + null); + } + + @override + Future getDetail(MSource source, String url) async { + url = Uri.parse(url).path; + + final data = {"url": "${preferenceBaseUrl(source.id)}$url"}; + final res = await http('GET', json.encode(data)); + final document = parseHtml(res); + MManga anime = MManga(); + var infoElement = document.selectFirst("div.mvi-content"); + anime.description = infoElement.selectFirst("p.f-desc")?.text ?? ""; + + anime.genre = xpath(res, + '//div[@class="mvici-left" and contains(text(),"Genre:")]/p/a/text()'); + + List episodeList = []; + final seasonListElements = document.select("div#seasons > div.tvseason"); + if (seasonListElements.isEmpty) { + MChapter ep = MChapter(); + ep.name = "Movie"; + ep.url = url; + episodeList.add(ep); + } else { + for (var season in seasonListElements) { + var seasonText = season.selectFirst("div.les-title").text.trim(); + for (var episode in season.select("div.les-content > a")) { + var epNumber = substringAfter(episode.text.trim(), "pisode "); + MChapter ep = MChapter(); + ep.name = "$seasonText Ep. $epNumber"; + ep.url = episode.getHref; + + episodeList.add(ep); + } + } + } + + anime.chapters = episodeList.reversed.toList(); + return anime; + } + + @override + Future> getVideoList(MSource source, String url) async { + url = Uri.parse(url).path; + final data = {"url": "${preferenceBaseUrl(source.id)}$url"}; + final res = await http('GET', json.encode(data)); + final document = parseHtml(res); + final serverElements = document.select("div.movieplay > iframe"); + List videos = []; + for (var serverElement in serverElements) { + var url = serverElement.getSrc; + List a = []; + if (url.contains("minoplres")) { + a = await minoplresExtractor(url); + } + videos.addAll(a); + } + return sortVideos(videos, source.id); + } + + @override + List getSourcePreferences(MSource source) { + return [ + EditTextPreference( + key: "overrideBaseUrl", + title: "Override BaseUrl", + summary: "", + value: "https://yomovies.boo", + dialogTitle: "Override BaseUrl", + dialogMessage: "", + text: "https://yomovies.boo"), + ListPreference( + key: "preferred_quality", + title: "Preferred quality", + summary: "", + valueIndex: 0, + entries: ["1080p", "720p", "480p", "360p"], + entryValues: ["1080", "720", "480", "360"]) + ]; + } + + Future> minoplresExtractor(String url) async { + List videos = []; + final res = await http( + 'GET', + json.encode({ + "url": url, + "headers": {"Referer": url} + })); + final script = xpath(res, '//script[contains(text(),"sources:")]/text()'); + if (script.isEmpty) return []; + final masterUrl = + substringBefore(substringAfter(script.first, "file:\""), '"'); + 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"); + + MVideo video = MVideo(); + video + ..url = videoUrl + ..originalUrl = videoUrl + ..quality = "Minoplres - $quality"; + videos.add(video); + } + return videos; + } + + String preferenceBaseUrl(int sourceId) { + return getPreferenceValue(sourceId, "overrideBaseUrl"); + } + + MPages animeFromElement(List elements, bool hasNextPage) { + List animeList = []; + for (var element in elements) { + MManga anime = MManga(); + anime.name = element.selectFirst("div.qtip-title").text; + anime.imageUrl = + element.selectFirst("img[data-original]")?.attr("data-original") ?? + ""; + anime.link = element.selectFirst("a[href]").getHref; + animeList.add(anime); + } + return MPages(animeList, hasNextPage); + } + + 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; + } + + @override + List getFilterList(MSource source) { + return [ + HeaderFilter( + "Note: Only one selection at a time works, and it ignores text search"), + SeparatorFilter(), + SelectFilter("BollywoodFilter", "Bollywood", 0, [ + SelectFilterOption("", ""), + SelectFilterOption("Dual Audio", "/genre/dual-audio"), + SelectFilterOption("Hollywood Dubbed", + "/account/?ptype=post&tax_category%5B%5D=dual-audio&wpas=1"), + SelectFilterOption("South Dubbed", + "/account/?ptype=post&tax_category%5B%5D=dual-audio&tax_category%5B%5D=south-special&wpas=1"), + ]), + SelectFilter("HollywoodFilter", "Hollywood", 0, [ + SelectFilterOption("", ""), + SelectFilterOption("English Series", "/series"), + ]), + SelectFilter("HindiSeriesFilter", "English Series", 0, [ + SelectFilterOption("", ""), + SelectFilterOption("Action", "/genre/action"), + SelectFilterOption("Adventure", "/genre/adventure"), + SelectFilterOption("Animation", "/genre/animation"), + SelectFilterOption("Biography", "/genre/biography"), + SelectFilterOption("Comedy", "/genre/comedy"), + SelectFilterOption("Crime", "/genre/crime"), + SelectFilterOption("Drama", "/genre/drama"), + SelectFilterOption("Music", "/genre/music"), + SelectFilterOption("Mystery", "/genre/mystery"), + SelectFilterOption("Family", "/genre/family"), + SelectFilterOption("Fantasy", "/genre/fantasy"), + SelectFilterOption("Horror", "/genre/horror"), + SelectFilterOption("History", "/genre/history"), + SelectFilterOption("Romance", "/genre/romantic"), + SelectFilterOption("Science Fiction", "/genre/science-fiction"), + SelectFilterOption("Thriller", "/genre/thriller"), + SelectFilterOption("War", "/genre/war"), + ]), + SelectFilter("ExtraMoviesFilter", "ExtraMovies", 0, [ + SelectFilterOption("", ""), + SelectFilterOption("Erotic", "/genre/erotic-movies"), + ]), + SelectFilter("HotSeriesFilter", "Hot Series", 0, [ + SelectFilterOption("