diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ac7c1885..0f6a6fe7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -333,7 +333,8 @@ See [`MDocument` model](https://github.com/kodjodevf/mangayomi/blob/main/lib/eva - `String` substringBeforeLast(`String text`, `String pattern`) ### Crypto utils -- `String` evalJs(`String code`); +- `String` unpackJs(`String code`); +- `Future` evalJs(`String code`); - `String` deobfuscateJsPassword(`String inputString`) - `String` encryptAESCryptoJS(`String plainText`, `String passphrase`) - `String` decryptAESCryptoJS(`String encrypted`, `String passphrase`) diff --git a/anime/multisrc/datalifeengine/datalifeengine.dart b/anime/multisrc/datalifeengine/datalifeengine.dart index e1dbe215..adc0728d 100644 --- a/anime/multisrc/datalifeengine/datalifeengine.dart +++ b/anime/multisrc/datalifeengine/datalifeengine.dart @@ -168,7 +168,7 @@ class DataLifeEngine extends MProvider { final res = await http('GET', json.encode({"url": url})); final masterUrl = substringBefore( substringAfter( - substringAfter(substringAfter(evalJs(res), "sources:"), "file:\""), + substringAfter(substringAfter(unpackJs(res), "sources:"), "file:\""), "src:\""), '"'); final masterPlaylistRes = @@ -203,7 +203,7 @@ class DataLifeEngine extends MProvider { return []; } final masterUrl = - substringBefore(substringAfter(evalJs(js.first), "{file:\""), "\"}"); + substringBefore(substringAfter(unpackJs(js.first), "{file:\""), "\"}"); final masterPlaylistRes = await http('GET', json.encode({"url": masterUrl})); List videos = []; diff --git a/anime/src/en/aniwave/aniwave.dart b/anime/src/en/aniwave/aniwave.dart index cbfd7b47..ec01807b 100644 --- a/anime/src/en/aniwave/aniwave.dart +++ b/anime/src/en/aniwave/aniwave.dart @@ -204,7 +204,13 @@ class Aniwave extends MProvider { final hosterSelection = preferenceHosterSelection(source.id); final typeSelection = preferenceTypeSelection(source.id); if (typeSelection.contains(type.toLowerCase())) { - if (url.contains("mp4upload") && + if (url.contains("vidplay") || url.contains("mcloud")) { + final hosterName = + url.contains("vidplay") ? "VidPlay" : "MyCloud"; + if (hosterSelection.contains(hosterName.toLowerCase())) { + a = await vidsrcExtractor(url, hosterName, type); + } + } else if (url.contains("mp4upload") && hosterSelection.contains("mp4upload")) { a = await mp4UploadExtractor(url, null, "", type); } else if (url.contains("streamtape") && @@ -305,6 +311,111 @@ class Aniwave extends MProvider { return vrf; } + Future> vidsrcExtractor( + String url, String name, String type) async { + List keys = json.decode(await http( + 'GET', + json.encode({ + "url": + "https://raw.githubusercontent.com/Claudemirovsky/worstsource-keys/keys/keys.json" + }))); + final host = Uri.parse(url).host; + final apiUrl = await getApiUrl(url, keys); + final headers = { + "Accept": "application/json, text/javascript, */*; q=0.01", + "Host": host, + "Referer": Uri.decodeComponent(url), + "X-Requested-With": "XMLHttpRequest" + }; + final res = + await http('GET', json.encode({"url": apiUrl, "headers": headers})); + if (res == "error") return []; + String masterUrl = + ((json.decode(res)['result']['sources'] as List>) + .first)['file']; + final tracks = (json.decode(res)['result']['tracks'] as List) + .where((e) => e['kind'] == 'captions' ? true : false) + .toList(); + List subtitles = []; + + for (var sub in tracks) { + try { + MTrack subtitle = MTrack(); + subtitle + ..label = sub["label"] + ..file = sub["file"]; + subtitles.add(subtitle); + } catch (_) {} + } + List videoList = []; + 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"); + + if (!videoUrl.startsWith("http")) { + videoUrl = + "${masterUrl.split("/").sublist(0, masterUrl.split("/").length - 1).join("/")}/$videoUrl"; + } + + MVideo video = MVideo(); + video + ..url = videoUrl + ..originalUrl = videoUrl + ..quality = "$name - $type - $quality" + ..headers = {"Referer": "https://$host/"} + ..subtitles = subtitles; + videoList.add(video); + } + return videoList; + } + + Future getApiUrl(String url, List keyList) async { + final host = Uri.parse(url).host; + final paramsToString = Uri.parse(url) + .queryParameters + .entries + .map((e) => "${e.key}=${e.value}") + .join("&"); + var vidId = substringBefore(substringAfterLast(url, "/"), "?"); + var encodedID = encodeID(vidId, keyList); + final apiSlug = await callFromFuToken(host, encodedID); + String apiUrlString = ""; + apiUrlString += "https://$host/$apiSlug"; + if (paramsToString.isNotEmpty) { + apiUrlString += "?$paramsToString"; + } + + return apiUrlString; + } + + String encodeID(String vidId, List keyList) { + var rc4Key1 = keyList[0]; + var rc4Key2 = keyList[1]; + final rc4 = rc4Encrypt(rc4Key1, vidId.codeUnits); + final rc41 = rc4Encrypt(rc4Key2, rc4); + return base64.encode(rc41).replaceAll("/", "_").trim(); + } + + Future callFromFuToken(String host, String data) async { + final fuTokenScript = + await http('GET', json.encode({"url": "https://$host/futoken"})); + String js = ""; + js += "(function"; + js += substringBefore( + substringAfter(substringAfter(fuTokenScript, "window"), "function") + .replaceAll("jQuery.ajax(", ""), + "+location.search"); + js += "}(\"$data\"))"; + final jsRes = await evalJs(js); + if (jsRes == "error") return ""; + return jsRes; + } + @override List getFilterList(MSource source) { return [ diff --git a/anime/src/fr/otakufr/otakufr.dart b/anime/src/fr/otakufr/otakufr.dart index 86fca4d9..03b3fcc2 100644 --- a/anime/src/fr/otakufr/otakufr.dart +++ b/anime/src/fr/otakufr/otakufr.dart @@ -362,7 +362,7 @@ class OtakuFr extends MProvider { return []; } final masterUrl = - substringBefore(substringAfter(evalJs(js.first), "{file:\""), "\"}"); + substringBefore(substringAfter(unpackJs(js.first), "{file:\""), "\"}"); final masterPlaylistRes = await http('GET', json.encode({"url": masterUrl})); List videos = []; diff --git a/anime/src/id/nimegami/nimegami.dart b/anime/src/id/nimegami/nimegami.dart index 61bc269e..b7402e08 100644 --- a/anime/src/id/nimegami/nimegami.dart +++ b/anime/src/id/nimegami/nimegami.dart @@ -152,7 +152,7 @@ class NimeGami extends MProvider { '//script[contains(text(), "eval") and contains(text(), "p,a,c,k,e,d")]/text()'); if (script.isNotEmpty) { final videoUrl = substringBefore( - substringAfter(substringAfter(evalJs(script.first), "sources:[", ""), + substringAfter(substringAfter(unpackJs(script.first), "sources:[", ""), "file\":\"", ""), '"'); if (videoUrl.isNotEmpty) { diff --git a/manga/src/en/mangahere/mangahere.dart b/manga/src/en/mangahere/mangahere.dart index 91188591..ea4b431d 100644 --- a/manga/src/en/mangahere/mangahere.dart +++ b/manga/src/en/mangahere/mangahere.dart @@ -181,7 +181,7 @@ class MangaHere extends MProvider { res, "//script[contains(text(),'function(p,a,c,k,e,d)')]/text()") .first .replaceAll("eval", ""); - String deobfuscatedScript = evalJs(script); + String deobfuscatedScript = unpackJs(script); int a = deobfuscatedScript.indexOf("newImgs=['") + 10; int b = deobfuscatedScript.indexOf("'];"); List urls = deobfuscatedScript.substring(a, b).split("','"); @@ -197,7 +197,7 @@ class MangaHere extends MProvider { String secretKeyScript = res .substring(secretKeyScriptLocation, secretKeyScriptEndLocation) .replaceAll("eval", ""); - String secretKeyDeobfuscatedScript = evalJs(secretKeyScript); + String secretKeyDeobfuscatedScript = unpackJs(secretKeyScript); int secretKeyStartLoc = secretKeyDeobfuscatedScript.indexOf("'"); int secretKeyEndLoc = secretKeyDeobfuscatedScript.indexOf(";"); @@ -231,7 +231,7 @@ class MangaHere extends MProvider { } } } - String deobfuscatedScript = evalJs(responseText.replaceAll("eval", "")); + String deobfuscatedScript = unpackJs(responseText.replaceAll("eval", "")); int baseLinkStartPos = deobfuscatedScript.indexOf("pix=") + 5; int baseLinkEndPos =