From f4703303f4ec235bb895af136ee260985eb67d89 Mon Sep 17 00:00:00 2001 From: kodjomoustapha <107993382+kodjodevf@users.noreply.github.com> Date: Fri, 17 Nov 2023 18:56:08 +0100 Subject: [PATCH] New source Aniwave (EN) --- anime/source_generator.dart | 4 +- anime/src/en/aniwave/aniwave-v0.0.1.dart | 242 +++++++++++++++++++ anime/src/en/aniwave/source.dart | 17 ++ anime/src/id/nimegami/nimegami-v0.0.1.dart | 6 +- anime/src/id/nimegami/source.dart | 4 +- anime/src/id/otakudesu/otakudesu-v0.0.1.dart | 5 +- anime/src/id/otakudesu/source.dart | 4 +- icons/mangayomi-en-aniwave.png | Bin 0 -> 3775 bytes 8 files changed, 272 insertions(+), 10 deletions(-) create mode 100644 anime/src/en/aniwave/aniwave-v0.0.1.dart create mode 100644 anime/src/en/aniwave/source.dart create mode 100644 icons/mangayomi-en-aniwave.png diff --git a/anime/source_generator.dart b/anime/source_generator.dart index 8d3537e5..e1e64861 100644 --- a/anime/source_generator.dart +++ b/anime/source_generator.dart @@ -4,6 +4,7 @@ import 'dart:io'; import '../model/source.dart'; import 'multisrc/zorotheme/sources.dart'; import 'src/ar/okanime/source.dart'; +import 'src/en/aniwave/source.dart'; import 'src/en/gogoanime/source.dart'; import 'src/en/kisskh/source.dart'; import 'src/fr/animesultra/source.dart'; @@ -24,7 +25,8 @@ void main() { okanimeSource, otakudesu, nimegami, - oploverz + oploverz, + aniwave ]; final List> jsonList = _sourcesList.map((source) => source.toJson()).toList(); diff --git a/anime/src/en/aniwave/aniwave-v0.0.1.dart b/anime/src/en/aniwave/aniwave-v0.0.1.dart new file mode 100644 index 00000000..55029295 --- /dev/null +++ b/anime/src/en/aniwave/aniwave-v0.0.1.dart @@ -0,0 +1,242 @@ +import 'package:mangayomi/bridge_lib.dart'; +import 'dart:convert'; + +class Aniwave extends MProvider { + Aniwave(); + + @override + Future getPopular(MSource source, int page) async { + final data = {"url": "${source.baseUrl}/filter?sort=trending&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) async { + final data = {"url": "${source.baseUrl}/filter?keyword=$query"}; + final res = await http('GET', json.encode(data)); + return parseAnimeList(res); + } + + @override + Future getDetail(MSource source, String url) async { + final statusList = [ + {"Releasing": 0, "Completed": 1} + ]; + final data = {"url": "${source.baseUrl}${url}"}; + final res = await http('GET', json.encode(data)); + MManga anime = MManga(); + final status = xpath(res, '//div[contains(text(),"Status")]/span/text()'); + if (status.isNotEmpty) { + anime.status = parseStatus(status.first, statusList); + } + final description = xpath(res, + '//*[contains(@class,"synopsis")]/div[@class="shorting"]/div[@class="content"]/text()'); + if (description.isNotEmpty) { + anime.description = description.first; + } + final author = xpath(res, '//div[contains(text(),"Studio")]/span/text()'); + if (author.isNotEmpty) { + anime.author = author.first; + } + + anime.genre = xpath(res, '//div[contains(text(),"Genre")]/span/a/text()'); + final id = querySelectorAll(res, + selector: "div[data-id]", + typeElement: 3, + attributes: "data-id", + typeRegExp: 0) + .first; + final encrypt = vrfEncrypt(id); + final vrf = "vrf=${Uri.encodeComponent(encrypt)}"; + final dataEp = {"url": "${source.baseUrl}/ajax/episode/list/$id?$vrf"}; + final resEp = await http('GET', json.encode(dataEp)); + final html = json.decode(resEp)["result"]; + List? episodesList = []; + final epsHtml = querySelectorAll(html, + selector: "div.episodes ul > li", + typeElement: 2, + attributes: "", + typeRegExp: 0); + for (var epHtml in epsHtml) { + final title = xpath(epHtml, '//li/@title').isNotEmpty + ? xpath(epHtml, '//li/@title').first + : ""; + final ids = xpath(epHtml, '//a/@data-ids').first; + final sub = xpath(epHtml, '//a/@data-sub').first; + final dub = xpath(epHtml, '//a/@data-dub').first; + final softsub = title.toLowerCase().contains("softsub") ? "1" : ""; + final fillerEp = title.toLowerCase().contains("filler") ? "1" : ""; + final epNum = xpath(epHtml, '//a/@data-num').first; + String scanlator = ""; + if (sub == "1") { + scanlator += "Sub"; + } + if (softsub == "1") { + scanlator += ", Softsub"; + } + if (dub == "1") { + scanlator += ", Dub"; + } + if (fillerEp == "1") { + scanlator += ", • Filler Episode"; + } + MChapter episode = MChapter(); + episode.name = "Episode $epNum"; + episode.scanlator = scanlator; + episode.url = "$ids&epurl=$url/ep-$epNum"; + episodesList.add(episode); + } + + anime.chapters = episodesList.reversed.toList(); + return anime; + } + + @override + Future> getVideoList(MSource source, String url) async { + final ids = substringBefore(url, "&"); + final encrypt = vrfEncrypt(ids); + final vrf = "vrf=${Uri.encodeComponent(encrypt)}"; + final res = await http('GET', + json.encode({"url": "${source.baseUrl}/ajax/server/list/$ids?$vrf"})); + final html = json.decode(res)["result"]; + final vidsHtml = querySelectorAll(html, + selector: "div.servers > div", + typeElement: 2, + attributes: "", + typeRegExp: 0); + List videos = []; + for (var vidHtml in vidsHtml) { + final type = xpath(vidHtml, '//div/@data-type').first; + final serversIds = xpath(vidHtml, '//li/@data-link-id'); + for (int i = 0; i < serversIds.length; i++) { + final serverId = serversIds[i]; + + final encrypt = vrfEncrypt(serverId); + final vrf = "vrf=${Uri.encodeComponent(encrypt)}"; + final res = await http( + 'GET', + json.encode( + {"url": "${source.baseUrl}/ajax/server/$serverId?$vrf"})); + final status = json.decode(res)["status"]; + if (status == 200) { + List a = []; + final url = vrfDecrypt(json.decode(res)["result"]["url"]); + if (url.contains("mp4upload")) { + a = await mp4UploadExtractor(url, null, "", type); + } else if (url.contains("streamtape")) { + a = await streamTapeExtractor(url, "StreamTape - $type"); + } else if (url.contains("filemoon")) { + a = await filemoonExtractor(url, "", type); + } + videos.addAll(a); + } + } + } + + return videos; + } + + MPages parseAnimeList(String res) { + List animeList = []; + final urls = xpath(res, '//div[@class="item "]/div/div/div/a/@href'); + final names = xpath(res, '//div[@class="item "]/div/div/div/a/text()'); + final images = xpath(res, '//div[@class="item "]/div/div/a/img/@src'); + + for (var i = 0; i < names.length; i++) { + MManga anime = MManga(); + anime.name = names[i]; + anime.imageUrl = images[i]; + anime.link = urls[i]; + animeList.add(anime); + } + + return MPages(animeList, true); + } + + List rc4Encrypt(String key, List message) { + List _key = utf8.encode(key); + int _i = 0, _j = 0; + List _box = List.generate(256, (i) => i); + + int x = 0; + for (int i = 0; i < 256; i++) { + x = (x + _box[i] + _key[i % _key.length]) % 256; + var tmp = _box[i]; + _box[i] = _box[x]; + _box[x] = tmp; + } + + List out = []; + for (var char in message) { + _i = (_i + 1) % 256; + _j = (_j + _box[_i]) % 256; + + var tmp = _box[_i]; + _box[_i] = _box[_j]; + _box[_j] = tmp; + + final c = char ^ (_box[(_box[_i] + _box[_j]) % 256]); + out.add(c); + } + + return out; + } + + String vrfEncrypt(String input) { + final rc4 = rc4Encrypt("ysJhV6U27FVIjjuk", input.codeUnits); + final vrf = base64Url.encode(rc4); + final vrf1 = base64.encode(vrf.codeUnits); + List vrf2 = vrfShift(vrf1.codeUnits); + final vrf3 = base64.encode(vrf2); + return utf8.decode(rot13(vrf3.codeUnits)); + } + + String vrfDecrypt(String input) { + final decode = base64Url.decode(input); + final rc4 = rc4Encrypt("hlPeNwkncH0fq9so", decode); + return Uri.decodeComponent(utf8.decode(rc4)); + } + + List vrfShift(List vrf) { + var shifts = [-3, 3, -4, 2, -2, 5, 4, 5]; + for (var i = 0; i < vrf.length; i++) { + var shift = shifts[i % 8]; + vrf[i] = (vrf[i] + shift) & 0xFF; + } + return vrf; + } + + List rot13(List vrf) { + for (var i = 0; i < vrf.length; i++) { + var byte = vrf[i]; + if (byte >= 'A'.codeUnitAt(0) && byte <= 'Z'.codeUnitAt(0)) { + vrf[i] = (byte - 'A'.codeUnitAt(0) + 13) % 26 + 'A'.codeUnitAt(0); + } else if (byte >= 'a'.codeUnitAt(0) && byte <= 'z'.codeUnitAt(0)) { + vrf[i] = (byte - 'a'.codeUnitAt(0) + 13) % 26 + 'a'.codeUnitAt(0); + } + } + return vrf; + } +} + +Map getMirrorPref() { + return { + "aniwave.to": "https://aniwave.to", + "aniwave.bz": "https://aniwave.bz", + "aniwave.ws": "https://aniwave.ws", + }; +} + +Aniwave main() { + return Aniwave(); +} diff --git a/anime/src/en/aniwave/source.dart b/anime/src/en/aniwave/source.dart new file mode 100644 index 00000000..7ae0c8e6 --- /dev/null +++ b/anime/src/en/aniwave/source.dart @@ -0,0 +1,17 @@ +import '../../../../model/source.dart'; +import '../../../../utils/utils.dart'; + +Source get aniwave => _aniwave; +const aniwaveVersion = "0.0.1"; +const aniwaveCodeUrl = + "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/anime/src/en/aniwave/aniwave-v$aniwaveVersion.dart"; +Source _aniwave = Source( + name: "Aniwave", + baseUrl: "https://aniwave.to", + lang: "en", + typeSource: "single", + iconUrl: getIconUrl("aniwave", "en"), + sourceCodeUrl: aniwaveCodeUrl, + version: aniwaveVersion, + appMinVerReq: "0.0.8", + isManga: false); diff --git a/anime/src/id/nimegami/nimegami-v0.0.1.dart b/anime/src/id/nimegami/nimegami-v0.0.1.dart index bf778444..ebf36fbf 100644 --- a/anime/src/id/nimegami/nimegami-v0.0.1.dart +++ b/anime/src/id/nimegami/nimegami-v0.0.1.dart @@ -96,7 +96,7 @@ class NimeGami extends MProvider { episode.name = names[i]; episode.url = json.encode({ "episodeIndex": int.parse(substringAfterLast(epNums[i], '_')), - 'urls': json.decode(base64(epUrls[i], 0)) + 'urls': json.decode(utf8.decode(base64Url.decode(epUrls[i]))) }); episodesList.add(episode); } @@ -124,8 +124,8 @@ class NimeGami extends MProvider { List videos = []; List a = []; if (url.contains("video.nimegami.id")) { - final realUrl = - base64(substringBefore(substringAfter(url, "url="), "&"), 0); + final realUrl = utf8.decode( + base64Url.decode(substringBefore(substringAfter(url, "url="), "&"))); final a = await extractHXFileVideos(realUrl, quality); videos.addAll(a); } else if (url.contains("berkasdrive") || url.contains("drive.nimegami")) { diff --git a/anime/src/id/nimegami/source.dart b/anime/src/id/nimegami/source.dart index 24e0d64e..02224d21 100644 --- a/anime/src/id/nimegami/source.dart +++ b/anime/src/id/nimegami/source.dart @@ -2,7 +2,7 @@ import '../../../../model/source.dart'; import '../../../../utils/utils.dart'; Source get nimegami => _nimegami; -const nimegamiVersion = "0.0.1"; +const nimegamiVersion = "0.0.2"; const nimegamiCodeUrl = "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/anime/src/id/nimegami/nimegami-v$nimegamiVersion.dart"; Source _nimegami = Source( @@ -13,5 +13,5 @@ Source _nimegami = Source( iconUrl: getIconUrl("nimegami", "id"), sourceCodeUrl: nimegamiCodeUrl, version: nimegamiVersion, - appMinVerReq: "0.0.7", + appMinVerReq: "0.0.8", isManga: false); diff --git a/anime/src/id/otakudesu/otakudesu-v0.0.1.dart b/anime/src/id/otakudesu/otakudesu-v0.0.1.dart index fdbed306..3e811601 100644 --- a/anime/src/id/otakudesu/otakudesu-v0.0.1.dart +++ b/anime/src/id/otakudesu/otakudesu-v0.0.1.dart @@ -101,7 +101,7 @@ class OtakuDesu extends MProvider { xpath(res, '//*[@class="mirrorstream"]/ul/li/a/@data-content'); for (var stream in mirrorstream) { List a = []; - final decodedData = json.decode(base64(stream, 0)); + final decodedData = json.decode(utf8.decode(base64Url.decode(stream))); final q = decodedData["q"]; final id = decodedData["id"]; final i = decodedData["i"]; @@ -114,7 +114,8 @@ class OtakuDesu extends MProvider { "body": body, "url": "${source.baseUrl}/wp-admin/admin-ajax.php" })); - final html = base64(substringBefore(substringAfter(res, ":\""), '"'), 0); + final html = utf8.decode( + base64Url.decode(substringBefore(substringAfter(res, ":\""), '"'))); final url = xpath(html, '//iframe/@src').first; if (url.contains("yourupload")) { diff --git a/anime/src/id/otakudesu/source.dart b/anime/src/id/otakudesu/source.dart index eb05a87a..7c47291a 100644 --- a/anime/src/id/otakudesu/source.dart +++ b/anime/src/id/otakudesu/source.dart @@ -2,7 +2,7 @@ import '../../../../model/source.dart'; import '../../../../utils/utils.dart'; Source get otakudesu => _otakudesu; -const otakudesuVersion = "0.0.1"; +const otakudesuVersion = "0.0.2"; const otakudesuCodeUrl = "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/anime/src/id/otakudesu/otakudesu-v$otakudesuVersion.dart"; Source _otakudesu = Source( @@ -13,5 +13,5 @@ Source _otakudesu = Source( iconUrl: getIconUrl("otakudesu", "id"), sourceCodeUrl: otakudesuCodeUrl, version: otakudesuVersion, - appMinVerReq: "0.0.7", + appMinVerReq: "0.0.8", isManga: false); diff --git a/icons/mangayomi-en-aniwave.png b/icons/mangayomi-en-aniwave.png new file mode 100644 index 0000000000000000000000000000000000000000..516f974eca5f6968a871100fcabcc5acbb664188 GIT binary patch literal 3775 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 literal 0 HcmV?d00001