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 00000000..6afd2b3f Binary files /dev/null and b/anime/src/en/hi/yomovies/icon.png differ diff --git a/anime/src/en/hi/yomovies/source.dart b/anime/src/en/hi/yomovies/source.dart new file mode 100644 index 00000000..fe7d5597 --- /dev/null +++ b/anime/src/en/hi/yomovies/source.dart @@ -0,0 +1,16 @@ +import '../../../../../model/source.dart'; + +Source get yomoviesSource => _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("