From 1cdaf17b59501ec029d91c5f4f8891e8279615c8 Mon Sep 17 00:00:00 2001 From: kodjomoustapha <107993382+kodjodevf@users.noreply.github.com> Date: Sat, 30 Dec 2023 10:10:15 +0100 Subject: [PATCH] New source: 9AnimeTv (EN) --- anime/source_generator.dart | 4 +- anime/src/en/nineanimetv/icon.png | Bin 0 -> 6623 bytes anime/src/en/nineanimetv/nineanimetv.dart | 420 ++++++++++++++++++++++ anime/src/en/nineanimetv/source.dart | 16 + 4 files changed, 439 insertions(+), 1 deletion(-) create mode 100644 anime/src/en/nineanimetv/icon.png create mode 100644 anime/src/en/nineanimetv/nineanimetv.dart create mode 100644 anime/src/en/nineanimetv/source.dart diff --git a/anime/source_generator.dart b/anime/source_generator.dart index 91e5a54e..1227d430 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/nineanimetv/source.dart'; import 'src/fr/animesama/source.dart'; import 'src/hi/yomovies/source.dart'; import 'src/en/kisskh/source.dart'; @@ -42,7 +43,8 @@ void main() { filma24, dramacoolSource, yomoviesSource, - animesamaSource + animesamaSource, + nineanimetv ]; final List> jsonList = _sourcesList.map((source) => source.toJson()).toList(); diff --git a/anime/src/en/nineanimetv/icon.png b/anime/src/en/nineanimetv/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..e57234ee0c6cbd70c221ed2a2f4e729b0fc5d3ac GIT binary patch literal 6623 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 getPopular(MSource source, int page) async { + final data = {"url": "${source.baseUrl}/filter?sort=all&page=$page"}; + final res = await http('GET', json.encode(data)); + return parseAnimeList(res); + } + + @override + Future getLatestUpdates(MSource source, int page) async { + final data = { + "url": "${source.baseUrl}/filter?sort=recently_updated&page=$page" + }; + final res = await http('GET', json.encode(data)); + return parseAnimeList(res); + } + + @override + Future search( + MSource source, String query, int page, FilterList filterList) async { + final filters = filterList.filters; + String url = "${source.baseUrl}/filter?keyword=$query"; + + for (var filter in filters) { + if (filter.type == "GenreFilter") { + final genre = (filter.state as List).where((e) => e.state).toList(); + url += "${ll(url)}genre="; + if (genre.isNotEmpty) { + for (var st in genre) { + url += "${st.value}"; + if (genre.length > 1) { + url += "%2C"; + } + } + if (genre.length > 1) { + url = substringBeforeLast(url, '%2C'); + } + } + } else if (filter.type == "SeasonFilter") { + final season = (filter.state as List).where((e) => e.state).toList(); + url += "${ll(url)}season="; + if (season.isNotEmpty) { + for (var st in season) { + url += "${st.value}"; + if (season.length > 1) { + url += "%2C"; + } + } + if (season.length > 1) { + url = substringBeforeLast(url, '%2C'); + } + } + } else if (filter.type == "YearFilter") { + final year = (filter.state as List).where((e) => e.state).toList(); + url += "${ll(url)}year="; + if (year.isNotEmpty) { + for (var st in year) { + url += "${st.value}"; + if (year.length > 1) { + url += "%2C"; + } + } + if (year.length > 1) { + url = substringBeforeLast(url, '%2C'); + } + } + } else if (filter.type == "TypeFilter") { + final type = (filter.state as List).where((e) => e.state).toList(); + url += "${ll(url)}type="; + if (type.isNotEmpty) { + for (var st in type) { + url += "${st.value}"; + if (type.length > 1) { + url += "%2C"; + } + } + if (type.length > 1) { + url = substringBeforeLast(url, '%2C'); + } + } + } else if (filter.type == "StatusFilter") { + final status = filter.values[filter.state].value; + url += "${ll(url)}status=$status"; + } else if (filter.type == "LanguageFilter") { + final language = (filter.state as List).where((e) => e.state).toList(); + url += "${ll(url)}language="; + if (language.isNotEmpty) { + for (var st in language) { + url += "${st.value}"; + if (language.length > 1) { + url += "%2C"; + } + } + if (language.length > 1) { + url = substringBeforeLast(url, '%2C'); + } + } + } else if (filter.type == "SortFilter") { + final sort = filter.values[filter.state].value; + url += "${ll(url)}sort=$sort"; + } + } + final data = {"url": "$url&page=$page"}; + final res = await http('GET', json.encode(data)); + return parseAnimeList(res); + } + + @override + Future getDetail(MSource source, String url) async { + final statusList = [ + {"Currently Airing": 0, "Finished Airing": 1} + ]; + final data = {"url": "${source.baseUrl}${url}"}; + final res = await http('GET', json.encode(data)); + MManga anime = MManga(); + final document = parseHtml(res); + final infoElement = document.selectFirst("div.film-infor"); + final status = infoElement.xpathFirst( + '//div[contains(text(),"Status:")]/following-sibling::div/span/text()') ?? + ""; + anime.status = parseStatus(status, statusList); + anime.description = + infoElement.selectFirst("div.film-description > p").text ?? ""; + anime.author = infoElement.xpathFirst( + '//div[contains(text(),"Studios:")]/following-sibling::div/a/text()') ?? + ""; + + anime.genre = infoElement.xpath( + '//div[contains(text(),"Genre:")]/following-sibling::div/a/text()'); + final id = parseHtml(res).selectFirst("div[data-id]").attr("data-id"); + + final dataEp = {"url": "${source.baseUrl}/ajax/episode/list/$id"}; + + final resEp = await http('GET', json.encode(dataEp)); + final html = json.decode(resEp)["html"]; + + List? episodesList = []; + + final epsElements = parseHtml(html).select("a"); + for (var epElement in epsElements) { + final id = epElement.attr('data-id'); + + final title = epElement.attr('title') ?? ""; + + final epNum = epElement.attr('data-number'); + + MChapter episode = MChapter(); + episode.name = "Episode $epNum $title"; + episode.url = id; + episodesList.add(episode); + } + anime.chapters = episodesList.reversed.toList(); + return anime; + } + + @override + Future> getVideoList(MSource source, String url) async { + final res = await http( + 'GET', + json.encode( + {"url": "${source.baseUrl}/ajax/episode/servers?episodeId=$url"})); + final html = json.decode(res)["html"]; + + final serverElements = parseHtml(html).select("div.server-item"); + + List videos = []; + final hosterSelection = preferenceHosterSelection(source.id); + final typeSelection = preferenceTypeSelection(source.id); + for (var serverElement in serverElements) { + final name = serverElement.text; + final id = serverElement.attr("data-id"); + final subDub = serverElement.attr("data-type"); + final res = await http( + 'GET', + json.encode( + {"url": "${source.baseUrl}/ajax/episode/sources?id=$id"})); + final epUrl = json.decode(res)["link"]; + List a = []; + + if (hosterSelection.contains(name) && typeSelection.contains(subDub)) { + if (name.contains("Vidstreaming")) { + a = await rapidCloudExtractor(epUrl, "Vidstreaming - $subDub"); + } else if (name.contains("Vidcloud")) { + a = await rapidCloudExtractor(epUrl, "Vidcloud - $subDub"); + } + videos.addAll(a); + } + } + + return sortVideos(videos, source.id); + } + + MPages parseAnimeList(String res) { + final elements = parseHtml(res).select("div.film_list-wrap > div"); + List animeList = []; + for (var element in elements) { + MManga anime = MManga(); + anime.name = element.selectFirst("div.film-detail > h3 > a").text; + anime.imageUrl = element.selectFirst(" div.film-poster > img").getSrc; + anime.link = element.selectFirst("div.film-detail > h3 > a").getHref; + animeList.add(anime); + } + + return MPages(animeList, true); + } + + @override + List getFilterList(MSource source) { + return [ + GroupFilter("GenreFilter", "Genre", [ + CheckBoxFilter("Action", "1"), + CheckBoxFilter("Adventure", "2"), + CheckBoxFilter("Cars", "3"), + CheckBoxFilter("Comedy", "4"), + CheckBoxFilter("Dementia", "5"), + CheckBoxFilter("Demons", "6"), + CheckBoxFilter("Drama", "8"), + CheckBoxFilter("Ecchi", "9"), + CheckBoxFilter("Fantasy", "10"), + CheckBoxFilter("Game", "11"), + CheckBoxFilter("Harem", "35"), + CheckBoxFilter("Historical", "13"), + CheckBoxFilter("Horror", "14"), + CheckBoxFilter("Isekai", "44"), + CheckBoxFilter("Josei", "43"), + CheckBoxFilter("Kids", "15"), + CheckBoxFilter("Magic", "16"), + CheckBoxFilter("Martial Arts", "17"), + CheckBoxFilter("Mecha", "18"), + CheckBoxFilter("Military", "38"), + CheckBoxFilter("Music", "19"), + CheckBoxFilter("Mystery", "7"), + CheckBoxFilter("Parody", "20"), + CheckBoxFilter("Police", "39"), + CheckBoxFilter("Psychological", "40"), + CheckBoxFilter("Romance", "22"), + CheckBoxFilter("Samurai", "21"), + CheckBoxFilter("School", "23"), + CheckBoxFilter("Sci-Fi", "24"), + CheckBoxFilter("Seinen", "42"), + CheckBoxFilter("Shoujo", "25"), + CheckBoxFilter("Shoujo Ai", "26"), + CheckBoxFilter("Shounen", "27"), + CheckBoxFilter("Shounen Ai", "28"), + CheckBoxFilter("Slice of Life", "36"), + CheckBoxFilter("Space", "29"), + CheckBoxFilter("Sports", "30"), + CheckBoxFilter("Super Power", "31"), + CheckBoxFilter("Supernatural", "37"), + CheckBoxFilter("Thriller", "41"), + CheckBoxFilter("Vampire", "32") + ]), + GroupFilter("SeasonFilter", "Season", [ + CheckBoxFilter("Fall", "3"), + CheckBoxFilter("Summer", "2"), + CheckBoxFilter("Spring", "1"), + CheckBoxFilter("Winter", "4") + ]), + GroupFilter("YearFilter", "Year", [ + CheckBoxFilter("2023", "2023"), + CheckBoxFilter("2022", "2022"), + CheckBoxFilter("2021", "2021"), + CheckBoxFilter("2020", "2020"), + CheckBoxFilter("2019", "2019"), + CheckBoxFilter("2018", "2018"), + CheckBoxFilter("2017", "2017"), + CheckBoxFilter("2016", "2016"), + CheckBoxFilter("2015", "2015"), + CheckBoxFilter("2014", "2014"), + CheckBoxFilter("2013", "2013"), + CheckBoxFilter("2012", "2012"), + CheckBoxFilter("2011", "2011"), + CheckBoxFilter("2010", "2010"), + CheckBoxFilter("2009", "2009"), + CheckBoxFilter("2008", "2008"), + CheckBoxFilter("2007", "2007"), + CheckBoxFilter("2006", "2006"), + CheckBoxFilter("2005", "2005"), + CheckBoxFilter("2004", "2004"), + CheckBoxFilter("2003", "2003"), + CheckBoxFilter("2002", "2002"), + CheckBoxFilter("2001", "2001") + ]), + SelectFilter("SortFilter", "Sort by", 0, [ + SelectFilterOption("All", "all"), + SelectFilterOption("Default", "default"), + SelectFilterOption("Recently Added", "recently_added"), + SelectFilterOption("Recently Updated", "recently_updated"), + SelectFilterOption("Score", "score"), + SelectFilterOption("Name A-Z", "name_az"), + SelectFilterOption("Released Date", "released_date"), + SelectFilterOption("Most Watched", "most_watched") + ]), + GroupFilter("TypeFilter", "Type", [ + CheckBoxFilter("Movie", "1"), + CheckBoxFilter("TV Series", "2"), + CheckBoxFilter("OVA", "3"), + CheckBoxFilter("ONA", "4"), + CheckBoxFilter("Special", "5"), + CheckBoxFilter("Music", "6") + ]), + SelectFilter("StatusFilter", "Status", 0, [ + SelectFilterOption("All", "all"), + SelectFilterOption("Finished Airing", "1"), + SelectFilterOption("Currently Airing", "2"), + SelectFilterOption("Not yet aired", "3") + ]), + GroupFilter("LanguageFilter", "Language", + [CheckBoxFilter("Sub", "sub"), CheckBoxFilter("Dub", "dub")]), + ]; + } + + @override + List getSourcePreferences(MSource source) { + return [ + ListPreference( + key: "preferred_quality", + title: "Preferred Quality", + summary: "", + valueIndex: 1, + entries: ["1080p", "720p", "480p", "360p"], + entryValues: ["1080", "720", "480", "360"]), + ListPreference( + key: "preferred_server", + title: "Preferred server", + summary: "", + valueIndex: 0, + entries: ["Vidstreaming", "VidCloud"], + entryValues: ["Vidstreaming", "VidCloud"]), + ListPreference( + key: "preferred_type", + title: "Preferred Type", + summary: "", + valueIndex: 0, + entries: ["Sub", "Dub"], + entryValues: ["sub", "dub"]), + MultiSelectListPreference( + key: "hoster_selection", + title: "Enable/Disable Hosts", + summary: "", + entries: ["Vidstreaming", "VidCloud"], + entryValues: ["Vidstreaming", "Vidcloud"], + values: ["Vidstreaming", "Vidcloud"]), + MultiSelectListPreference( + key: "type_selection", + title: "Enable/Disable Types", + summary: "", + entries: ["Sub", "Dub"], + entryValues: ["sub", "dub"], + values: ["sub", "dub"]), + ]; + } + + List sortVideos(List videos, int sourceId) { + String quality = getPreferenceValue(sourceId, "preferred_quality"); + String server = getPreferenceValue(sourceId, "preferred_server"); + String type = getPreferenceValue(sourceId, "preferred_type"); + videos = videos + .where( + (MVideo e) => e.quality.toLowerCase().contains(type.toLowerCase())) + .toList(); + 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; + }); + + videos.sort((MVideo a, MVideo b) { + int serverMatchA = 0; + if (a.quality.toLowerCase().contains(server.toLowerCase())) { + serverMatchA = 1; + } + int serverMatchB = 0; + if (b.quality.toLowerCase().contains(server.toLowerCase())) { + serverMatchB = 1; + } + return serverMatchB - serverMatchA; + }); + return videos; + } + + List preferenceHosterSelection(int sourceId) { + return getPreferenceValue(sourceId, "hoster_selection"); + } + + List preferenceTypeSelection(int sourceId) { + return getPreferenceValue(sourceId, "type_selection"); + } + + String ll(String url) { + if (url.contains("?")) { + return "&"; + } + return "?"; + } +} + +NineAnimeTv main() { + return NineAnimeTv(); +} diff --git a/anime/src/en/nineanimetv/source.dart b/anime/src/en/nineanimetv/source.dart new file mode 100644 index 00000000..73b9db07 --- /dev/null +++ b/anime/src/en/nineanimetv/source.dart @@ -0,0 +1,16 @@ +import '../../../../model/source.dart'; + +Source get nineanimetv => _nineanimetv; +const _nineanimetvVersion = "0.0.1"; +const _nineanimetvCodeUrl = + "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/anime/src/en/nineanimetv/nineanimetv.dart"; +Source _nineanimetv = Source( + name: "9AnimeTv", + baseUrl: "https://9animetv.to", + lang: "en", + typeSource: "single", + iconUrl: + "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/anime/src/en/nineanimetv/icon.png", + sourceCodeUrl: _nineanimetvCodeUrl, + version: _nineanimetvVersion, + isManga: false);