diff --git a/anime/source_generator.dart b/anime/source_generator.dart index 1227d430..7ba2e412 100644 --- a/anime/source_generator.dart +++ b/anime/source_generator.dart @@ -6,6 +6,7 @@ import 'multisrc/datalifeengine/sources.dart'; import 'multisrc/dopeflix/sources.dart'; import 'multisrc/zorotheme/sources.dart'; import 'src/ar/okanime/source.dart'; +import 'src/de/aniflix/source.dart'; import 'src/en/aniwave/source.dart'; import 'src/en/dramacool/source.dart'; import 'src/en/gogoanime/source.dart'; @@ -44,7 +45,8 @@ void main() { dramacoolSource, yomoviesSource, animesamaSource, - nineanimetv + nineanimetv, + aniflix ]; final List> jsonList = _sourcesList.map((source) => source.toJson()).toList(); diff --git a/anime/src/de/aniflix/aniflix.dart b/anime/src/de/aniflix/aniflix.dart new file mode 100644 index 00000000..f8820e57 --- /dev/null +++ b/anime/src/de/aniflix/aniflix.dart @@ -0,0 +1,240 @@ +import 'package:mangayomi/bridge_lib.dart'; +import 'dart:convert'; + +class AniFlix extends MProvider { + AniFlix(); + + @override + Future getPopular(MSource source, int page) async { + final headers = getHeader(source.baseUrl); + final data = { + "url": "${source.baseUrl}/api/show/new/${page - 1}", + "headers": headers + }; + final res = await http('GET', json.encode(data)); + + return parseAnimeList(res, source.baseUrl, true); + } + + @override + Future getLatestUpdates(MSource source, int page) async { + final headers = getHeader(source.baseUrl); + final data = { + "url": "${source.baseUrl}/api/show/airing/${page - 1}", + "headers": headers + }; + final res = await http('GET', json.encode(data)); + final datas = json.decode(res); + List animeList = []; + List ids = []; + for (var data in datas) { + final anim = data["season"]["show"]; + if (!ids.contains(anim["id"])) { + ids.add(anim["id"]); + MManga anime = MManga(); + anime.name = anim["name"]; + anime.imageUrl = + "${source.baseUrl}/storage/" + (anim["cover_portrait"] ?? ""); + anime.link = + getUrlWithoutDomain("${source.baseUrl}/api/show/${anim['url']}"); + anime.description = anim["description"]; + if (anim["airing"] == 0) { + anime.status = MStatus.completed; + } else if (anim["airing"] == 1) { + anime.status = MStatus.ongoing; + } + animeList.add(anime); + } + } + return MPages(animeList, true); + } + + @override + Future search( + MSource source, String query, int page, FilterList filterList) async { + final data = { + "url": "${source.baseUrl}/api/show/search", + "body": {"search": query}, + "headers": { + 'Referer': source.baseUrl, + 'Accept': 'application/json', + 'Content-Type': 'application/json' + } + }; + final res = await http('POST', json.encode(data)); + return parseAnimeList(res, source.baseUrl, false); + } + + @override + Future getDetail(MSource source, String url) async { + final data = {"url": "${source.baseUrl}$url"}; + + final res = await http('GET', json.encode(data)); + MManga anime = MManga(); + final jsonRes = json.decode(res); + anime.name = jsonRes["name"]; + if (jsonRes["cover_portrait"] != null) { + anime.imageUrl = "${source.baseUrl}/storage/" + jsonRes["cover_portrait"]; + } + anime.description = jsonRes["description"]; + anime.genre = (jsonRes["genres"] as List>) + .map((e) => e["name"]) + .toList(); + var seasons = jsonRes["seasons"]; + final animeUrl = jsonRes["url"]; + List? episodesList = []; + for (var season in seasons) { + List> episodes = season["episodes"]; + for (var episode in episodes) { + String name = episode["name"] ?? ""; + if (name.toLowerCase().contains("folge") || + name.toLowerCase().contains("episode")) { + name = ""; + } else { + name = ": $name"; + } + MChapter ep = MChapter(); + ep.name = "Staffel ${season["number"]} Folge ${episode["number"]}$name"; + ep.url = + "/api/episode/show/$animeUrl/season/${season["number"]}/episode/${episode["number"]}"; + episodesList.add(ep); + } + } + + anime.chapters = episodesList.reversed.toList(); + return anime; + } + + @override + Future> getVideoList(MSource source, String url) async { + final headers = getHeader(source.baseUrl); + final data = {"url": "${source.baseUrl}$url", "headers": headers}; + final res = await http('GET', json.encode(data)); + final jsonRes = json.decode(res)["streams"]; + List videos = []; + final hosterSelection = preferenceHosterSelection(source.id); + for (var stream in jsonRes) { + List a = []; + String quality = '${stream["hoster"]["name"]} - ${stream["lang"]}'; + String link = stream["link"]; + print(link); + if (link.contains("https://dood") && + hosterSelection.contains("doodstream")) { + a = await doodExtractor(link, quality); + } else if (link.contains("https://streamtape") && + hosterSelection.contains("streamtape")) { + a = await streamTapeExtractor(link, quality); + } else if (link.contains("https://voe.sx") && + hosterSelection.contains("voe")) { + a = await voeExtractor(link, quality); + } else if (link.contains("https://streamlare") && + hosterSelection.contains("streamlare")) { + a = await streamlareExtractor(link, quality, '', ''); + } + videos.addAll(a); + } + + return sortVideos(videos, source.id); + } + + String getUrlWithoutDomain(String orig) { + final uri = Uri.parse(orig.replaceAll(' ', '%20')); + String out = uri.path; + if (uri.query.isNotEmpty) { + out += '?${uri.query}'; + } + if (uri.fragment.isNotEmpty) { + out += '#${uri.fragment}'; + } + return out; + } + + MPages parseAnimeList(String res, String baseUrl, bool hasNextPage) { + final datas = json.decode(res); + List animeList = []; + + for (var data in datas) { + MManga anime = MManga(); + anime.name = data["name"]; + anime.imageUrl = "$baseUrl/storage/" + (data["cover_portrait"] ?? ""); + anime.link = getUrlWithoutDomain("$baseUrl/api/show/${data['url']}"); + anime.description = data["description"]; + if (data["airing"] == 0) { + anime.status = MStatus.completed; + } else if (data["airing"] == 1) { + anime.status = MStatus.ongoing; + } + animeList.add(anime); + } + return MPages(animeList, hasNextPage); + } + + List sortVideos(List videos, int sourceId) { + print(videos.length); + String hoster = getPreferenceValue(sourceId, "preferred_hoster"); + String sub = getPreferenceValue(sourceId, "preferred_sub"); + videos.sort((MVideo a, MVideo b) { + int hosterMatchA = 0; + if (a.url.toLowerCase().contains(hoster.toLowerCase()) && + a.quality.toLowerCase().contains(sub.toLowerCase())) { + hosterMatchA = 1; + } + int hosterMatchB = 0; + if (b.url.toLowerCase().contains(hoster.toLowerCase()) && + b.quality.toLowerCase().contains(sub.toLowerCase())) { + hosterMatchB = 1; + } + return hosterMatchB - hosterMatchA; + }); + return videos; + } + + List preferenceHosterSelection(int sourceId) { + return getPreferenceValue(sourceId, "hoster_selectionn"); + } + + @override + List getSourcePreferences(MSource source) { + return [ + ListPreference( + key: "preferred_hoster", + title: "Standard-Hoster", + summary: "", + valueIndex: 0, + entries: [ + "Streamtape", + "Doodstream", + "Voe", + "Streamlare" + ], + entryValues: [ + "https://streamtape.com", + "https://dood", + "https://voe.sx", + "https://streamlare.com" + ]), + ListPreference( + key: "preferred_sub", + title: "Standardmäßig Sub oder Dub?", + summary: "", + valueIndex: 0, + entries: ["Sub", "Dub"], + entryValues: ["Sub", "Dub"]), + MultiSelectListPreference( + key: "hoster_selectionn", + title: "Hoster auswählen", + summary: "", + entries: ["Streamtape", "Doodstream", "Voe", "Streamlare"], + entryValues: ["streamtape", "doodstream", "voe", "streamlare"], + values: ["streamtape", "doodstream", "voe", "streamlare"]), + ]; + } +} + +Map getHeader(String url) { + return {'Referer': url}; +} + +AniFlix main() { + return AniFlix(); +} diff --git a/anime/src/de/aniflix/icon.png b/anime/src/de/aniflix/icon.png new file mode 100644 index 00000000..cd181272 Binary files /dev/null and b/anime/src/de/aniflix/icon.png differ diff --git a/anime/src/de/aniflix/source.dart b/anime/src/de/aniflix/source.dart new file mode 100644 index 00000000..dea3b9a2 --- /dev/null +++ b/anime/src/de/aniflix/source.dart @@ -0,0 +1,16 @@ +import '../../../../model/source.dart'; + +Source get aniflix => _aniflix; +const _aniflixVersion = "0.0.1"; +const _aniflixCodeUrl = + "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/anime/src/de/aniflix/aniflix.dart"; +Source _aniflix = Source( + name: "Aniflix", + baseUrl: "https://aniflix.cc", + lang: "de", + typeSource: "single", + iconUrl: + "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/anime/src/de/aniflix/icon.png", + sourceCodeUrl: _aniflixCodeUrl, + version: _aniflixVersion, + isManga: false);