Reorganize folders

This commit is contained in:
kodjomoustapha
2024-03-28 11:13:42 +01:00
parent abdc9cab62
commit 67109cdbbc
565 changed files with 988 additions and 1863 deletions

View File

@@ -0,0 +1,317 @@
import 'package:mangayomi/bridge_lib.dart';
import 'dart:convert';
import 'dart:math';
class AnimePahe extends MProvider {
AnimePahe(this.source);
final MSource source;
final Client client = Client(source);
@override
String get baseUrl => getPreferenceValue(source.id, "preferred_domain");
@override
Future<MPages> getPopular(int page) async {
return await getLatestUpdates(page);
}
@override
Future<MPages> getLatestUpdates(int page) async {
final res =
(await client.get(Uri.parse("$baseUrl/api?m=airing&page=$page"))).body;
final jsonResult = json.decode(res);
final hasNextPage = jsonResult["current_page"] < jsonResult["last_page"];
List<MManga> animeList = [];
for (var item in jsonResult["data"]) {
MManga anime = MManga();
anime.name = item["anime_title"];
anime.imageUrl = item["snapshot"];
anime.link = "/anime/?anime_id=${item["id"]}&name=${item["anime_title"]}";
anime.artist = item["fansub"];
animeList.add(anime);
}
return MPages(animeList, hasNextPage);
}
@override
Future<MPages> search(String query, int page, FilterList filterList) async {
final res =
(await client.get(Uri.parse("$baseUrl/api?m=search&l=8&q=$query")))
.body;
final jsonResult = json.decode(res);
List<MManga> animeList = [];
for (var item in jsonResult["data"]) {
MManga anime = MManga();
anime.name = item["title"];
anime.imageUrl = item["poster"];
anime.link = "/anime/?anime_id=${item["id"]}&name=${item["title"]}";
animeList.add(anime);
}
return MPages(animeList, false);
}
@override
Future<MManga> getDetail(String url) async {
final statusList = [
{"Currently Airing": 0, "Finished Airing": 1}
];
MManga anime = MManga();
final id = substringBefore(substringAfterLast(url, "?anime_id="), "&name=");
final name = substringAfterLast(url, "&name=");
final session = await getSession(name, id);
final res =
(await client.get(Uri.parse("$baseUrl/anime/$session?anime_id=$id")))
.body;
final document = parseHtml(res);
final status =
(document.xpathFirst('//div/p[contains(text(),"Status:")]/text()') ??
"")
.replaceAll("Status:\n", "")
.trim();
anime.status = parseStatus(status, statusList);
anime.name = document.selectFirst("div.title-wrapper > h1 > span").text;
anime.author =
(document.xpathFirst('//div/p[contains(text(),"Studio:")]/text()') ??
"")
.replaceAll("Studio:\n", "")
.trim();
anime.imageUrl = document.selectFirst("div.anime-poster a").attr("href");
anime.genre =
xpath(res, '//*[contains(@class,"anime-genre")]/ul/li/text()');
final synonyms =
(document.xpathFirst('//div/p[contains(text(),"Synonyms:")]/text()') ??
"")
.replaceAll("Synonyms:\n", "")
.trim();
anime.description = document.selectFirst("div.anime-summary").text;
if (synonyms.isNotEmpty) {
anime.description += "\n\n$synonyms";
}
final epUrl = "$baseUrl/api?m=release&id=$session&sort=episode_desc&page=1";
final resEp = (await client.get(Uri.parse(epUrl))).body;
final episodes = await recursivePages(epUrl, resEp, session);
anime.chapters = episodes;
return anime;
}
Future<List<MChapter>> recursivePages(
String url, String res, String session) async {
final jsonResult = json.decode(res);
final page = jsonResult["current_page"];
final hasNextPage = page < jsonResult["last_page"];
List<MManga> animeList = [];
for (var item in jsonResult["data"]) {
MChapter episode = MChapter();
episode.name = "Episode ${item["episode"]}";
episode.url = "/play/$session/${item["session"]}";
episode.dateUpload =
parseDates([item["created_at"]], "yyyy-MM-dd HH:mm:ss", "en")[0];
animeList.add(episode);
}
if (hasNextPage) {
final newUrl = "${substringBeforeLast(url, "&page=")}&page=${page + 1}";
final newRes = (await client.get(Uri.parse(newUrl))).body;
animeList.addAll(await recursivePages(newUrl, newRes, session));
}
return animeList;
}
Future<String> getSession(String title, String animeId) async {
final res =
(await client.get(Uri.parse("$baseUrl/api?m=search&q=$title"))).body;
return substringBefore(
substringAfter(
substringAfter(res, "\"id\":$animeId"), "\"session\":\""),
"\"");
}
@override
Future<List<MVideo>> getVideoList(String url) async {
final res = (await client.get(Uri.parse("$baseUrl$url")));
final document = parseHtml(res.body);
final downloadLinks = document.select("div#pickDownload > a");
final buttons = document.select("div#resolutionMenu > button");
List<MVideo> videos = [];
for (var i = 0; i < buttons.length; i++) {
final btn = buttons[i];
final kwikLink = btn.attr("data-src");
final quality = btn.text;
final paheWinLink = downloadLinks[i].attr("href");
if (getPreferenceValue(source.id, "preffered_link_type")) {
final noRedirectClient =
Client(source, json.encode({"followRedirects": false}));
final kwikHeaders =
(await noRedirectClient.get(Uri.parse("${paheWinLink}/i"))).headers;
final kwikUrl =
"https://${substringAfterLast(getMapValue(json.encode(kwikHeaders), "location"), "https://")}";
final reskwik = (await client
.get(Uri.parse(kwikUrl), headers: {"Referer": "https://kwik.cx/"}));
final matches = RegExp(r'\("(\S+)",\d+,"(\S+)",(\d+),(\d+)')
.firstMatch(reskwik.body);
final token = decrypt(matches!.group(1)!, matches.group(2)!,
matches.group(3)!, int.parse(matches.group(4)!));
final url = RegExp(r'action="([^"]+)"').firstMatch(token)!.group(1)!;
final tok = RegExp(r'value="([^"]+)"').firstMatch(token)!.group(1)!;
var code = 419;
var tries = 0;
String location = "";
while (code != 302 && tries < 20) {
String cookie =
getMapValue(json.encode(res.request.headers), "cookie");
cookie +=
"; ${getMapValue(json.encode(reskwik.headers), "set-cookie").replaceAll("path=/;", "")}";
final resNo =
await Client(source, json.encode({"followRedirects": false}))
.post(Uri.parse(url), headers: {
"referer": reskwik.request.url.toString(),
"cookie": cookie,
"user-agent":
getMapValue(json.encode(res.request.headers), "user-agent")
}, body: {
"_token": tok
});
code = resNo.statusCode;
tries++;
location = getMapValue(json.encode(resNo.headers), "location");
}
if (tries > 19) {
throw ("Failed to extract the stream uri from kwik.");
}
MVideo video = MVideo();
video
..url = location
..originalUrl = location
..quality = quality;
videos.add(video);
} else {
final ress = (await client.get(Uri.parse(kwikLink),
headers: {"Referer": "https://animepahe.com"}));
final script = substringAfterLast(
xpath(ress.body,
'//script[contains(text(),"eval(function")]/text()')
.first,
"eval(function(");
final videoUrl = substringBefore(
substringAfter(unpackJsAndCombine("eval(function($script"),
"const source=\\'"),
"\\';");
MVideo video = MVideo();
video
..url = videoUrl
..originalUrl = videoUrl
..quality = quality
..headers = {"referer": "https://kwik.cx"};
videos.add(video);
}
}
return sortVideos(videos);
}
String getString(String ctn, int sep) {
int b = 10;
String cm =
"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/";
final n = cm.substring(0, b);
double mx = 0;
for (var index = 0; index < ctn.length; index++) {
mx += (int.tryParse(ctn[ctn.length - index - 1], radix: 10) ?? 0.0)
.toInt() *
(pow(sep, index));
}
var m = '';
while (mx > 0) {
m = n[(mx % b).toInt()] + m;
mx = (mx - (mx % b)) / b;
}
return m.isNotEmpty ? m : '0';
}
String decrypt(String fS, String key, String v1, int v2) {
var html = "";
var i = 0;
final ld = int.parse(v1);
while (i < fS.length) {
var s = "";
while (fS[i] != key[v2]) {
s += fS[i];
i++;
}
for (var index = 0; index < key.length; index++) {
s = s.replaceAll(key[index], index.toString());
}
html += String.fromCharCode(int.parse(getString(s, v2)) - ld);
i++;
}
return html;
}
List<MVideo> sortVideos(List<MVideo> videos) {
String quality = getPreferenceValue(source.id, "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<dynamic> getSourcePreferences() {
return [
ListPreference(
key: "preferred_domain",
title: "Preferred domain",
summary: "",
valueIndex: 1,
entries: [
"animepahe.com",
"animepahe.ru",
"animepahe.org"
],
entryValues: [
"https://animepahe.com",
"https://animepahe.ru",
"https://animepahe.org"
]),
SwitchPreferenceCompat(
key: "preffered_link_type",
title: "Use HLS links",
summary: "Enable this if you are having Cloudflare issues.",
value: false),
ListPreference(
key: "preferred_quality",
title: "Preferred Quality",
summary: "",
valueIndex: 0,
entries: ["1080p", "720p", "360p"],
entryValues: ["1080", "720", "360"]),
];
}
}
AnimePahe main(MSource source) {
return AnimePahe(source);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -0,0 +1,16 @@
import '../../../../../model/source.dart';
Source get animepaheSource => _animepaheSource;
const _animepaheVersion = "0.0.35";
const _animepaheSourceCodeUrl =
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/en/animepahe/animepahe.dart";
Source _animepaheSource = Source(
name: "AnimePahe",
baseUrl: "https://www.animepahe.ru",
lang: "en",
typeSource: "single",
iconUrl:
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/en/animepahe/icon.png",
sourceCodeUrl: _animepaheSourceCodeUrl,
version: _animepaheVersion,
isManga: false);

View File

@@ -0,0 +1,688 @@
import 'package:mangayomi/bridge_lib.dart';
import 'dart:convert';
class Aniwave extends MProvider {
Aniwave({required this.source});
MSource source;
final Client client = Client(source);
@override
String get baseUrl => getPreferenceValue(source.id, "preferred_domain1");
@override
Future<MPages> getPopular(int page) async {
final res = (await client
.get(Uri.parse("$baseUrl/filter?sort=trending&page=$page")))
.body;
return parseAnimeList(res);
}
@override
Future<MPages> getLatestUpdates(int page) async {
final res = (await client
.get(Uri.parse("$baseUrl/filter?sort=recently_updated&page=$page")))
.body;
return parseAnimeList(res);
}
@override
Future<MPages> search(String query, int page, FilterList filterList) async {
final filters = filterList.filters;
String url = "$baseUrl/filter?keyword=$query";
for (var filter in filters) {
if (filter.type == "OrderFilter") {
final order = filter.values[filter.state].value;
url += "${ll(url)}sort=$order";
} else if (filter.type == "GenreFilter") {
final genre = (filter.state as List).where((e) => e.state).toList();
if (genre.isNotEmpty) {
for (var st in genre) {
url += "${ll(url)}genre[]=${st.value}";
}
}
} else if (filter.type == "CountryFilter") {
final country = (filter.state as List).where((e) => e.state).toList();
if (country.isNotEmpty) {
for (var st in country) {
url += "${ll(url)}country[]=${st.value}";
}
}
} else if (filter.type == "SeasonFilter") {
final season = (filter.state as List).where((e) => e.state).toList();
if (season.isNotEmpty) {
for (var st in season) {
url += "${ll(url)}season[]=${st.value}";
}
}
} else if (filter.type == "YearFilter") {
final year = (filter.state as List).where((e) => e.state).toList();
if (year.isNotEmpty) {
for (var st in year) {
url += "${ll(url)}year[]=${st.value}";
}
}
} else if (filter.type == "TypeFilter") {
final type = (filter.state as List).where((e) => e.state).toList();
if (type.isNotEmpty) {
for (var st in type) {
url += "${ll(url)}type[]=${st.value}";
}
}
} else if (filter.type == "StatusFilter") {
final status = (filter.state as List).where((e) => e.state).toList();
if (status.isNotEmpty) {
for (var st in status) {
url += "${ll(url)}status[]=${st.value}";
}
}
} else if (filter.type == "LanguageFilter") {
final language = (filter.state as List).where((e) => e.state).toList();
if (language.isNotEmpty) {
for (var st in language) {
url += "${ll(url)}language[]=${st.value}";
}
}
} else if (filter.type == "RatingFilter") {
final rating = (filter.state as List).where((e) => e.state).toList();
if (rating.isNotEmpty) {
for (var st in rating) {
url += "${ll(url)}rating[]=${st.value}";
}
}
}
}
final res = (await client.get(Uri.parse("$url&page=$page"))).body;
return parseAnimeList(res);
}
@override
Future<MManga> getDetail(String url) async {
final statusList = [
{"Releasing": 0, "Completed": 1}
];
final res = (await client.get(Uri.parse("$baseUrl$url"))).body;
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 = parseHtml(res).selectFirst("div[data-id]").attr("data-id");
final encrypt = vrfEncrypt(id);
final vrf = "vrf=${Uri.encodeComponent(encrypt)}";
final resEp =
(await client.get(Uri.parse("$baseUrl/ajax/episode/list/$id?$vrf")))
.body;
final html = json.decode(resEp)["result"];
List<MChapter>? episodesList = [];
final epsHtmls = parseHtml(html).select("div.episodes ul > li");
for (var epH in epsHtmls) {
final epHtml = epH.outerHtml;
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<List<MVideo>> getVideoList(String url) async {
final ids = substringBefore(url, "&");
final encrypt = vrfEncrypt(ids);
final vrf = "vrf=${Uri.encodeComponent(encrypt)}";
final res =
(await client.get(Uri.parse("$baseUrl/ajax/server/list/$ids?$vrf")))
.body;
final html = json.decode(res)["result"];
final vidsHtmls = parseHtml(html).select("div.servers > div");
List<MVideo> videos = [];
for (var vidH in vidsHtmls) {
final vidHtml = vidH.outerHtml;
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 client.get(Uri.parse("$baseUrl/ajax/server/$serverId?$vrf")))
.body;
final status = json.decode(res)["status"];
if (status == 200) {
List<MVideo> a = [];
final url = vrfDecrypt(json.decode(res)["result"]["url"]);
final hosterSelection = preferenceHosterSelection(source.id);
final typeSelection = preferenceTypeSelection(source.id);
if (typeSelection.contains(type.toLowerCase())) {
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") &&
hosterSelection.contains("streamtape")) {
a = await streamTapeExtractor(url, "StreamTape - $type");
} else if (url.contains("filemoon") &&
hosterSelection.contains("filemoon")) {
a = await filemoonExtractor(url, "", type);
}
videos.addAll(a);
}
}
}
}
return sortVideos(videos, source.id);
}
MPages parseAnimeList(String res) {
List<MManga> 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<int> rc4Encrypt(String key, List<int> message) {
List<int> _key = utf8.encode(key);
int _i = 0, _j = 0;
List<int> _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<int> 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<int> 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<int> vrfShift(List<int> 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<int> rot13(List<int> 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;
}
Future<List<MVideo>> vidsrcExtractor(
String url, String name, String type) async {
List<String> keys = json.decode((await client.get(Uri.parse(
"https://raw.githubusercontent.com/KillerDogeEmpire/vidplay-keys/keys/keys.json")))
.body);
List<MVideo> videoList = [];
final host = Uri.parse(url).host;
final apiUrl = await getApiUrl(url, keys);
final res = await client.get(Uri.parse(apiUrl), headers: {
"Host": host,
"Referer": Uri.decodeComponent(url),
"X-Requested-With": "XMLHttpRequest"
});
final result = json.decode(res.body)['result'];
if (result != 404) {
String masterUrl =
((result['sources'] as List<Map<String, dynamic>>).first)['file'];
final tracks = (result['tracks'] as List)
.where((e) => e['kind'] == 'captions' ? true : false)
.toList();
List<MTrack> subtitles = [];
for (var sub in tracks) {
try {
MTrack subtitle = MTrack();
subtitle
..label = sub["label"]
..file = sub["file"];
subtitles.add(subtitle);
} catch (_) {}
}
final masterPlaylistRes = (await client.get(Uri.parse(masterUrl))).body;
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<String> getApiUrl(String url, List<String> 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<String> 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<String> callFromFuToken(String host, String data) async {
final fuTokenScript =
(await client.get(Uri.parse("https://$host/futoken"))).body;
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<dynamic> getFilterList() {
return [
SelectFilter("OrderFilter", "Sort order", 0, [
SelectFilterOption("Most relevance", "most_relevance"),
SelectFilterOption("Recently updated", "recently_updated"),
SelectFilterOption("Recently added", "recently_added"),
SelectFilterOption("Release date", "release_date"),
SelectFilterOption("Trending", "trending"),
SelectFilterOption("Name A-Z", "title_az"),
SelectFilterOption("Scores", "scores"),
SelectFilterOption("MAL scores", "mal_scores"),
SelectFilterOption("Most watched", "most_watched"),
SelectFilterOption("Most favourited", "most_favourited"),
SelectFilterOption("Number of episodes", "number_of_episodes"),
]),
SeparatorFilter(),
GroupFilter("GenreFilter", "Genre", [
CheckBoxFilter("Action", "1"),
CheckBoxFilter("Adventure", "2"),
CheckBoxFilter("Avant Garde", "2262888"),
CheckBoxFilter("Boys Love", "2262603"),
CheckBoxFilter("Comedy", "4"),
CheckBoxFilter("Demons", "4424081"),
CheckBoxFilter("Drama", "7"),
CheckBoxFilter("Ecchi", "8"),
CheckBoxFilter("Fantasy", "9"),
CheckBoxFilter("Girls Love", "2263743"),
CheckBoxFilter("Gourmet", "2263289"),
CheckBoxFilter("Harem", "11"),
CheckBoxFilter("Horror", "14"),
CheckBoxFilter("Isekai", "3457284"),
CheckBoxFilter("Iyashikei", "4398552"),
CheckBoxFilter("Josei", "15"),
CheckBoxFilter("Kids", "16"),
CheckBoxFilter("Magic", "4424082"),
CheckBoxFilter("Mahou Shoujo", "3457321"),
CheckBoxFilter("Martial Arts", "18"),
CheckBoxFilter("Mecha", "19"),
CheckBoxFilter("Military", "20"),
CheckBoxFilter("Music", "21"),
CheckBoxFilter("Mystery", "22"),
CheckBoxFilter("Parody", "23"),
CheckBoxFilter("Psychological", "25"),
CheckBoxFilter("Reverse Harem", "4398403"),
CheckBoxFilter("Romance", "26"),
CheckBoxFilter("School", "28"),
CheckBoxFilter("Sci-Fi", "29"),
CheckBoxFilter("Seinen", "30"),
CheckBoxFilter("Shoujo", "31"),
CheckBoxFilter("Shounen", "33"),
CheckBoxFilter("Slice of Life", "35"),
CheckBoxFilter("Space", "36"),
CheckBoxFilter("Sports", "37"),
CheckBoxFilter("Super Power", "38"),
CheckBoxFilter("Supernatural", "39"),
CheckBoxFilter("Suspense", "2262590"),
CheckBoxFilter("Thriller", "40"),
CheckBoxFilter("Vampire", "41")
]),
GroupFilter("CountryFilter", "Country", [
CheckBoxFilter("China", "120823"),
CheckBoxFilter("Japan", "120822")
]),
GroupFilter("SeasonFilter", "Season", [
CheckBoxFilter("Fall", "fall"),
CheckBoxFilter("Summer", "summer"),
CheckBoxFilter("Spring", "spring"),
CheckBoxFilter("Winter", "winter"),
CheckBoxFilter("Unknown", "unknown")
]),
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("2000s", "2000s"),
CheckBoxFilter("1990s", "1990s"),
CheckBoxFilter("1980s", "1980s"),
CheckBoxFilter("1970s", "1970s"),
CheckBoxFilter("1960s", "1960s"),
CheckBoxFilter("1950s", "1950s"),
CheckBoxFilter("1940s", "1940s"),
CheckBoxFilter("1930s", "1930s"),
CheckBoxFilter("1920s", "1920s"),
CheckBoxFilter("1910s", "1910s")
]),
GroupFilter("TypeFilter", "Type", [
CheckBoxFilter("Movie", "movie"),
CheckBoxFilter("TV", "tv"),
CheckBoxFilter("OVA", "ova"),
CheckBoxFilter("ONA", "ona"),
CheckBoxFilter("Special", "special"),
CheckBoxFilter("Music", "music")
]),
GroupFilter("StatusFilter", "Status", [
CheckBoxFilter("Not Yet Aired", "info"),
CheckBoxFilter("Releasing", "releasing"),
CheckBoxFilter("Completed", "completed")
]),
GroupFilter("LanguageFilter", "Language", [
CheckBoxFilter("Sub and Dub", "subdub"),
CheckBoxFilter("Sub", "sub"),
CheckBoxFilter("Dub", "dub")
]),
GroupFilter("RatingFilter", "Rating", [
CheckBoxFilter("G - All Ages", "g"),
CheckBoxFilter("PG - Children", "pg"),
CheckBoxFilter("PG 13 - Teens 13 and Older", "pg_13"),
CheckBoxFilter("R - 17+, Violence & Profanity", "r"),
CheckBoxFilter("R+ - Profanity & Mild Nudity", "r+"),
CheckBoxFilter("Rx - Hentai", "rx")
]),
];
}
@override
List<dynamic> getSourcePreferences() {
return [
ListPreference(
key: "preferred_domain1",
title: "Preferred domain",
summary: "",
valueIndex: 0,
entries: [
"aniwave.to",
"aniwave.ws",
"aniwave.li",
"aniwave.vc"
],
entryValues: [
"https://aniwave.to",
"https://aniwave.ws",
"https://aniwave.li",
"https://aniwave.vc"
]),
ListPreference(
key: "preferred_quality",
title: "Preferred Quality",
summary: "",
valueIndex: 0,
entries: ["1080p", "720p", "480p", "360p"],
entryValues: ["1080", "720", "480", "360"]),
ListPreference(
key: "preferred_language",
title: "Preferred Type",
summary: "",
valueIndex: 0,
entries: ["Sub", "Softsub", "Dub"],
entryValues: ["Sub", "Softsub", "Dub"]),
ListPreference(
key: "preferred_server",
title: "Preferred server",
summary: "",
valueIndex: 0,
entries: [
"VidPlay",
"MyCloud",
"Filemoon",
"StreamTape",
"Mp4Upload"
],
entryValues: [
"vidplay",
"mycloud",
"filemoon",
"streamtape",
"mp4upload"
]),
MultiSelectListPreference(
key: "hoster_selection",
title: "Enable/Disable Hosts",
summary: "",
entries: [
"VidPlay",
"MyCloud",
"Filemoon",
"StreamTape",
"Mp4Upload"
],
entryValues: [
"vidplay",
"mycloud",
"filemoon",
"streamtape",
"mp4upload"
],
values: [
"vidplay",
"mycloud",
"filemoon",
"streamtape",
"mp4upload"
]),
MultiSelectListPreference(
key: "type_selection",
title: "Enable/Disable Type",
summary: "",
entries: ["Sub", "Softsub", "Dub"],
entryValues: ["sub", "softsub", "dub"],
values: ["sub", "softsub", "dub"]),
];
}
List<String> preferenceHosterSelection(int sourceId) {
return getPreferenceValue(sourceId, "hoster_selection");
}
List<String> preferenceTypeSelection(int sourceId) {
return getPreferenceValue(sourceId, "type_selection");
}
List<MVideo> sortVideos(List<MVideo> videos, int sourceId) {
String quality = getPreferenceValue(sourceId, "preferred_quality");
String server = getPreferenceValue(sourceId, "preferred_server");
String lang = getPreferenceValue(sourceId, "preferred_language");
videos.sort((MVideo a, MVideo b) {
int qualityMatchA = 0;
if (a.quality.contains(quality) &&
a.quality.toLowerCase().contains(lang.toLowerCase()) &&
a.quality.toLowerCase().contains(server.toLowerCase())) {
qualityMatchA = 1;
}
int qualityMatchB = 0;
if (b.quality.contains(quality) &&
b.quality.toLowerCase().contains(lang.toLowerCase()) &&
b.quality.toLowerCase().contains(server.toLowerCase())) {
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;
}
String ll(String url) {
if (url.contains("?")) {
return "&";
}
return "?";
}
}
Aniwave main(MSource source) {
return Aniwave(source: source);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@@ -0,0 +1,16 @@
import '../../../../../model/source.dart';
Source get aniwave => _aniwave;
const _aniwaveVersion = "0.0.65";
const _aniwaveCodeUrl =
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/en/aniwave/aniwave.dart";
Source _aniwave = Source(
name: "Aniwave",
baseUrl: "https://aniwave.to",
lang: "en",
typeSource: "single",
iconUrl:
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/en/aniwave/icon.png",
sourceCodeUrl: _aniwaveCodeUrl,
version: _aniwaveVersion,
isManga: false);

View File

@@ -0,0 +1,210 @@
import 'package:mangayomi/bridge_lib.dart';
import 'dart:convert';
class DramaCool extends MProvider {
DramaCool({required this.source});
MSource source;
final Client client = Client(source);
@override
String get baseUrl => getPreferenceValue(source.id, "overrideBaseUrl");
@override
Future<MPages> getPopular(int page) async {
final res =
(await client.get(Uri.parse("$baseUrl/most-popular-drama?page=$page")))
.body;
final document = parseHtml(res);
return animeFromElement(document.select("ul.list-episode-item li a"),
document.selectFirst("li.next a")?.attr("href") != null);
}
@override
Future<MPages> getLatestUpdates(int page) async {
final res =
(await client.get(Uri.parse("$baseUrl/recently-added?page=$page")))
.body;
final document = parseHtml(res);
return animeFromElement(document.select("ul.switch-block a"),
document.selectFirst("li.next a")?.attr("href") != null);
}
@override
Future<MPages> search(String query, int page, FilterList filterList) async {
final res = (await client
.get(Uri.parse("$baseUrl/search?keyword=$query&page=$page")))
.body;
final document = parseHtml(res);
return animeFromElement(document.select("ul.list-episode-item li a"),
document.selectFirst("li.next a")?.attr("href") != null);
}
@override
Future<MManga> getDetail(String url) async {
final statusList = [
{"Ongoing": 0, "Completed": 1}
];
url = getUrlWithoutDomain(url);
if (url.contains("-episode-") && url.endsWith(".html")) {
final res = (await client.get(Uri.parse("$baseUrl$url"))).body;
url = parseHtml(res).selectFirst("div.category a").attr("href");
}
url = getUrlWithoutDomain(url);
final res = (await client.get(Uri.parse("$baseUrl$url"))).body;
final document = parseHtml(res);
MManga anime = MManga();
anime.description = document
.selectFirst("div.info")
.select("p")
.map((MElement e) {
if (!e.outerHtml.contains("<span")) {
return e.text;
}
return "";
})
.toList()
.join("\n");
final author =
xpath(res, '//p[contains(text(),"Original Network:")]/a/text()');
if (author.isNotEmpty) {
anime.author = author.first;
}
anime.genre = xpath(res, '//p[contains(text(),"Genre:")]/a/text()');
final status = xpath(res, '//p[contains(text(),"Status")]/a/text()');
if (status.isNotEmpty) {
anime.status = parseStatus(status.first, statusList);
}
List<MChapter> episodesList = [];
final episodeListElements = document.select("ul.all-episode li a");
for (var element in episodeListElements) {
var epNum =
substringAfterLast(element.selectFirst("h3").text, "Episode ");
var type = element.selectFirst("span.type")?.text ?? "RAW";
var date = element.selectFirst("span.time")?.text ?? "";
MChapter ep = MChapter();
ep.name = "$type: Episode $epNum".trim();
ep.url = element.getHref;
if (date.isNotEmpty)
ep.dateUpload = parseDates([element.selectFirst("span.time")?.text],
"yyyy-MM-dd HH:mm:ss", "en")
.first;
episodesList.add(ep);
}
anime.chapters = episodesList;
return anime;
}
@override
Future<List<MVideo>> getVideoList(String url) async {
url = getUrlWithoutDomain(url);
final res = (await client.get(Uri.parse("$baseUrl$url"))).body;
final document = parseHtml(res);
String iframeUrl = document.selectFirst("iframe")?.getSrc ?? "";
if (iframeUrl.isEmpty) return [];
if (iframeUrl.startsWith("//")) {
iframeUrl = "https:$iframeUrl";
}
var iframeDoc = parseHtml((await client.get(Uri.parse(iframeUrl))).body);
final serverElements = iframeDoc.select("ul.list-server-items li");
List<MVideo> videos = [];
for (var serverElement in serverElements) {
var url = serverElement.attr("data-video");
List<MVideo> a = [];
if (url.contains("dood")) {
a = await doodExtractor(url, "DoodStream");
} else if (url.contains("dwish")) {
a = await streamWishExtractor(url, "StreamWish");
} else if (url.contains("streamtape")) {
a = await streamTapeExtractor(url, "StreamTape");
}
videos.addAll(a);
}
return sortVideos(videos, source.id);
}
@override
List<dynamic> getSourcePreferences() {
return [
EditTextPreference(
key: "overrideBaseUrl",
title: "Override BaseUrl",
summary: "",
value: "https://dramacool.pa",
dialogTitle: "Override BaseUrl",
dialogMessage: "",
text: "https://dramacool.pa"),
ListPreference(
key: "preferred_quality",
title: "Preferred quality",
summary: "",
valueIndex: 0,
entries: [
"1080p",
"720p",
"480p",
"360p",
"Doodstream",
"StreamTape"
],
entryValues: [
"1080",
"720",
"480",
"360",
"Doodstream",
"StreamTape"
])
];
}
MPages animeFromElement(List<MElement> elements, bool hasNextPage) {
List<MManga> animeList = [];
for (var element in elements) {
MManga anime = MManga();
anime.name = element.selectFirst("h3")?.text ?? "Serie";
anime.imageUrl = (element.selectFirst("img")?.attr("data-original") ?? "")
.replaceAll(" ", "%20") ??
"";
anime.link = element.getHref;
animeList.add(anime);
}
return MPages(animeList, hasNextPage);
}
List<MVideo> sortVideos(List<MVideo> 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;
}
}
DramaCool main(MSource source) {
return DramaCool(source: source);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

View File

@@ -0,0 +1,16 @@
import '../../../../../model/source.dart';
Source get dramacoolSource => _dramacoolSource;
const _dramacoolVersion = "0.0.25";
const _dramacoolSourceCodeUrl =
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/en/dramacool/dramacool.dart";
Source _dramacoolSource = Source(
name: "DramaCool",
baseUrl: "https://dramacool.pa",
lang: "en",
typeSource: "single",
iconUrl:
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/en/dramacool/icon.png",
sourceCodeUrl: _dramacoolSourceCodeUrl,
version: _dramacoolVersion,
isManga: false);

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@@ -0,0 +1,16 @@
import '../../../../../model/source.dart';
Source get gogoanimeSource => _gogoanimeSource;
const _gogoanimeVersion = "0.0.9";
const _gogoanimeSourceCodeUrl =
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/en/gogoanime/gogoanime.dart";
Source _gogoanimeSource = Source(
name: "Gogoanime",
baseUrl: "https://anitaku.to",
lang: "en",
typeSource: "single",
iconUrl:
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/en/gogoanime/icon.png",
sourceCodeUrl: _gogoanimeSourceCodeUrl,
version: _gogoanimeVersion,
isManga: false);

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@@ -0,0 +1,157 @@
import 'package:mangayomi/bridge_lib.dart';
import 'dart:convert';
class KissKh extends MProvider {
KissKh({required this.source});
MSource source;
final Client client = Client(source);
@override
Future<MPages> getPopular(int page) async {
final res = (await client.get(Uri.parse(
"${source.baseUrl}/api/DramaList/List?page=$page&type=0&sub=0&country=0&status=0&order=1&pageSize=40")))
.body;
final jsonRes = json.decode(res);
final datas = jsonRes["data"];
List<MManga> animeList = [];
for (var data in datas) {
MManga anime = MManga();
anime.name = data["title"];
anime.imageUrl = data["thumbnail"] ?? "";
anime.link =
"${source.baseUrl}/api/DramaList/Drama/${data["id"]}?isq=false";
animeList.add(anime);
}
int lastPage = jsonRes["totalCount"];
int pages = jsonRes["page"];
return MPages(animeList, pages < lastPage);
}
@override
Future<MPages> getLatestUpdates(int page) async {
final res = (await client.get(Uri.parse(
"${source.baseUrl}/api/DramaList/List?page=$page&type=0&sub=0&country=0&status=0&order=12&pageSize=40")))
.body;
final jsonRes = json.decode(res);
final datas = jsonRes["data"];
List<MManga> animeList = [];
for (var data in datas) {
MManga anime = MManga();
anime.name = data["title"];
anime.imageUrl = data["thumbnail"] ?? "";
anime.link =
"${source.baseUrl}/api/DramaList/Drama/${data["id"]}?isq=false";
animeList.add(anime);
}
int lastPage = jsonRes["totalCount"];
int pages = jsonRes["page"];
return MPages(animeList, pages < lastPage);
}
@override
Future<MPages> search(String query, int page, FilterList filterList) async {
final res = (await client.get(Uri.parse(
"${source.baseUrl}/api/DramaList/Search?q=$query&type=0")))
.body;
final jsonRes = json.decode(res);
List<MManga> animeList = [];
for (var data in jsonRes) {
MManga anime = MManga();
anime.name = data["title"];
anime.imageUrl = data["thumbnail"] ?? "";
anime.link =
"${source.baseUrl}/api/DramaList/Drama/${data["id"]}?isq=false";
animeList.add(anime);
}
return MPages(animeList, false);
}
@override
Future<MManga> getDetail(String url) async {
final statusList = [
{"Ongoing": 0, "Completed": 1}
];
final res = (await client.get(Uri.parse(url))).body;
MManga anime = MManga();
final jsonRes = json.decode(res);
final status = jsonRes["status"] ?? "";
anime.description = jsonRes["description"];
anime.status = parseStatus(status, statusList);
anime.imageUrl = jsonRes["thumbnail"];
var episodes = jsonRes["episodes"];
String type = jsonRes["type"];
final episodesCount = jsonRes["episodesCount"] as int;
final containsAnime = type.contains("Anime");
final containsTVSeries = type.contains("TVSeries");
final containsHollywood = type.contains("Hollywood");
final containsMovie = type.contains("Movie");
List<MChapter>? episodesList = [];
for (var a in episodes) {
MChapter episode = MChapter();
String number = (a["number"] as double).toString().replaceAll(".0", "");
final id = a["id"];
if (containsAnime || containsTVSeries) {
episode.name = "Episode $number";
} else if (containsHollywood && episodesCount == 1 || containsMovie) {
episode.name = "Movie";
} else if (containsHollywood && episodesCount > 1) {
episode.name = "Episode $number";
}
episode.url =
"${source.baseUrl}/api/DramaList/Episode/$id.png?err=false&ts=&time=";
episodesList.add(episode);
}
anime.chapters = episodesList;
return anime;
}
@override
Future<List<MVideo>> getVideoList(String url) async {
final res = (await client.get(Uri.parse(url))).body;
final id = substringAfter(substringBefore(url, ".png"), "Episode/");
final jsonRes = json.decode(res);
final subRes =
(await client.get(Uri.parse("${source.baseUrl}/api/Sub/$id"))).body;
var jsonSubRes = json.decode(subRes);
List<MTrack> subtitles = [];
for (var sub in jsonSubRes) {
try {
final subUrl = sub["src"];
final label = sub["label"];
MTrack subtitle = MTrack();
subtitle
..label = label
..file = subUrl;
subtitles.add(subtitle);
} catch (_) {}
}
final videoUrl = jsonRes["Video"];
MVideo video = MVideo();
video
..url = videoUrl
..originalUrl = videoUrl
..quality = "kisskh"
..subtitles = subtitles
..headers = {
"referer": "https://kisskh.me/",
"origin": "https://kisskh.me"
};
return [video];
}
}
KissKh main(MSource source) {
return KissKh(source: source);
}

View File

@@ -0,0 +1,16 @@
import '../../../../../model/source.dart';
Source get kisskhSource => _kisskhSource;
const _kisskhVersion = "0.0.55";
const _kisskhSourceCodeUrl =
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/en/kisskh/kisskh.dart";
Source _kisskhSource = Source(
name: "KissKH",
baseUrl: "https://kisskh.co",
lang: "en",
typeSource: "single",
iconUrl:
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/en/kisskh/icon.png",
sourceCodeUrl: _kisskhSourceCodeUrl,
version: _kisskhVersion,
isManga: false);

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

View File

@@ -0,0 +1,546 @@
import 'package:mangayomi/bridge_lib.dart';
import 'dart:convert';
class NineAnimeTv extends MProvider {
NineAnimeTv({required this.source});
MSource source;
final Client client = Client(source);
@override
Future<MPages> getPopular(int page) async {
final res = (await client
.get(Uri.parse("${source.baseUrl}/filter?sort=all&page=$page")))
.body;
return parseAnimeList(res);
}
@override
Future<MPages> getLatestUpdates(int page) async {
final res = (await client.get(Uri.parse(
"${source.baseUrl}/filter?sort=recently_updated&page=$page")))
.body;
return parseAnimeList(res);
}
@override
Future<MPages> search(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 res = (await client.get(Uri.parse("$url&page=$page"))).body;
return parseAnimeList(res);
}
@override
Future<MManga> getDetail(String url) async {
final statusList = [
{"Currently Airing": 0, "Finished Airing": 1}
];
final res = (await client.get(Uri.parse("${source.baseUrl}$url"))).body;
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 resEp =
(await client.get(Uri.parse("${source.baseUrl}/ajax/episode/list/$id")))
.body;
final html = json.decode(resEp)["html"];
List<MChapter>? 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<List<MVideo>> getVideoList(String url) async {
final res = (await client.get(
Uri.parse("${source.baseUrl}/ajax/episode/servers?episodeId=$url")))
.body;
final html = json.decode(res)["html"];
final serverElements = parseHtml(html).select("div.server-item");
List<MVideo> 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 client
.get(Uri.parse("${source.baseUrl}/ajax/episode/sources?id=$id")))
.body;
final epUrl = json.decode(res)["link"];
List<MVideo> 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<MManga> 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);
}
Future<List<MVideo>> rapidCloudExtractor(String url, String name) async {
final serverUrl = ['https://megacloud.tv', 'https://rapid-cloud.co'];
final serverType = url.startsWith('https://megacloud.tv') ? 0 : 1;
final sourceUrl = [
'/embed-2/ajax/e-1/getSources?id=',
'/ajax/embed-6-v2/getSources?id='
];
final sourceSpliter = ['/e-1/', '/embed-6-v2/'];
final id = url.split(sourceSpliter[serverType]).last.split('?').first;
final resServer = (await client.get(
Uri.parse('${serverUrl[serverType]}${sourceUrl[serverType]}$id'),
headers: {"X-Requested-With": "XMLHttpRequest"}))
.body;
final encrypted = getMapValue(resServer, "encrypted");
String videoResJson = "";
List<MVideo> videos = [];
if (encrypted == "true") {
final ciphered = getMapValue(resServer, "sources");
List<List<int>> indexPairs = await generateIndexPairs(serverType);
var password = '';
String ciphertext = ciphered;
int index = 0;
for (List<int> item in json.decode(json.encode(indexPairs))) {
int start = item.first + index;
int end = start + item.last;
String passSubstr = ciphered.substring(start, end);
password += passSubstr;
ciphertext = ciphertext.replaceFirst(passSubstr, "");
index += item.last;
}
videoResJson = decryptAESCryptoJS(ciphertext, password);
} else {
videoResJson = resServer;
}
String masterUrl =
((json.decode(videoResJson) as List<Map<String, dynamic>>)
.first)['file'];
String type = ((json.decode(videoResJson) as List<Map<String, dynamic>>)
.first)['type'];
final tracks = (json.decode(resServer)['tracks'] as List)
.where((e) => e['kind'] == 'captions' ? true : false)
.toList();
List<MTrack> subtitles = [];
for (var sub in tracks) {
try {
MTrack subtitle = MTrack();
subtitle
..label = sub["label"]
..file = sub["file"];
subtitles.add(subtitle);
} catch (_) {}
}
if (type == "hls") {
final masterPlaylistRes = (await client.get(Uri.parse(masterUrl))).body;
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 - $quality"
..subtitles = subtitles;
videos.add(video);
}
} else {
MVideo video = MVideo();
video
..url = masterUrl
..originalUrl = masterUrl
..quality = "$name - Default"
..subtitles = subtitles;
videos.add(video);
}
return videos;
}
Future<List<List<int>>> generateIndexPairs(int serverType) async {
final jsPlayerUrl = [
"https://megacloud.tv/js/player/a/prod/e1-player.min.js",
"https://rapid-cloud.co/js/player/prod/e6-player-v2.min.js"
];
final scriptText =
(await client.get(Uri.parse(jsPlayerUrl[serverType]))).body;
final switchCode = scriptText.substring(
scriptText.lastIndexOf('switch'), scriptText.indexOf('=partKey'));
List<int> indexes = [];
for (var variableMatch
in RegExp(r'=(\w+)').allMatches(switchCode).toList()) {
final regex = RegExp(
',${(variableMatch as RegExpMatch).group(1)}=((?:0x)?([0-9a-fA-F]+))');
Match? match = regex.firstMatch(scriptText);
if (match != null) {
String value = match.group(1);
if (value.contains("0x")) {
indexes.add(int.parse(substringAfter(value, "0x"), radix: 16));
} else {
indexes.add(int.parse(value));
}
}
}
return chunked(indexes, 2);
}
List<List<int>> chunked(List<int> list, int size) {
List<List<int>> chunks = [];
for (int i = 0; i < list.length; i += size) {
int end = list.length;
if (i + size < list.length) {
end = i + size;
}
chunks.add(list.sublist(i, end));
}
return chunks;
}
@override
List<dynamic> getFilterList() {
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<dynamic> getSourcePreferences() {
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<MVideo> sortVideos(List<MVideo> videos, int sourceId) {
String quality = getPreferenceValue(sourceId, "preferred_quality");
String server = getPreferenceValue(sourceId, "preferred_server");
String type = getPreferenceValue(sourceId, "preferred_type");
videos.sort((MVideo a, MVideo b) {
int qualityMatchA = 0;
if (a.quality.contains(quality) &&
a.quality.toLowerCase().contains(type.toLowerCase()) &&
a.quality.toLowerCase().contains(server.toLowerCase())) {
qualityMatchA = 1;
}
int qualityMatchB = 0;
if (b.quality.contains(quality) &&
b.quality.toLowerCase().contains(type.toLowerCase()) &&
b.quality.toLowerCase().contains(server.toLowerCase())) {
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;
}
List<String> preferenceHosterSelection(int sourceId) {
return getPreferenceValue(sourceId, "hoster_selection");
}
List<String> preferenceTypeSelection(int sourceId) {
return getPreferenceValue(sourceId, "type_selection");
}
String ll(String url) {
if (url.contains("?")) {
return "&";
}
return "?";
}
}
NineAnimeTv main(MSource source) {
return NineAnimeTv(source: source);
}

View File

@@ -0,0 +1,16 @@
import '../../../../../model/source.dart';
Source get nineanimetv => _nineanimetv;
const _nineanimetvVersion = "0.0.3";
const _nineanimetvCodeUrl =
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/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/dart/anime/src/en/nineanimetv/icon.png",
sourceCodeUrl: _nineanimetvCodeUrl,
version: _nineanimetvVersion,
isManga: false);

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@@ -0,0 +1,16 @@
import '../../../../../model/source.dart';
Source get uhdmoviesSource => _uhdmoviesSource;
const _uhdmoviesVersion = "0.0.35";
const _uhdmoviesSourceCodeUrl =
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/en/uhdmovies/uhdmovies.dart";
Source _uhdmoviesSource = Source(
name: "UHD Movies",
baseUrl: "https://uhdmovies.zip",
lang: "en",
typeSource: "single",
iconUrl:
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/en/uhdmovies/icon.png",
sourceCodeUrl: _uhdmoviesSourceCodeUrl,
version: _uhdmoviesVersion,
isManga: false);

View File

@@ -0,0 +1,234 @@
import 'package:mangayomi/bridge_lib.dart';
import 'dart:convert';
class UHDMovies extends MProvider {
UHDMovies({required this.source});
MSource source;
final Client client = Client(source);
@override
bool get supportsLatest => false;
@override
String get baseUrl => getPreferenceValue(source.id, "pref_domain");
@override
Future<MPages> getPopular(int page) async {
final res = (await client.get(Uri.parse("$baseUrl/page/$page"))).body;
return animeFromElement(res);
}
@override
Future<MPages> getLatestUpdates(int page) async {
return MPages([], false);
}
@override
Future<MPages> search(String query, int page, FilterList filterList) async {
final res = (await client.get(
Uri.parse("$baseUrl/page/$page/?s=${query.replaceAll(" ", "+")}")))
.body;
return animeFromElement(res);
}
@override
Future<MManga> getDetail(String url) async {
url = getUrlWithoutDomain(url);
final res = (await client.get(Uri.parse("$baseUrl${url}"))).body;
MManga anime = MManga();
final description = xpath(res, '//pre/span/text()');
if (description.isNotEmpty) {
anime.description = description.first;
}
anime.status = MStatus.ongoing;
final episodesTitles = xpath(res,
'//*[contains(@style, "center") or contains(@class, "maxbutton")]/a[contains(@class, "maxbutton") or contains(@href, "?sid=")]/text()');
final episodesUrls = xpath(res,
'//*[contains(@style, "center") or contains(@class, "maxbutton")]/a[contains(@class, "maxbutton") or contains(@href, "?sid=")]/@href');
bool isSeries = false;
if (episodesTitles.first.contains("Episode") ||
episodesTitles.first.contains("Zip") ||
episodesTitles.first.contains("Pack")) {
isSeries = true;
}
List<MChapter>? episodesList = [];
if (!isSeries) {
List<String> moviesTitles = [];
moviesTitles = xpath(res,
'//*[contains(@style, "center") or contains(@class, "maxbutton")]/parent::p//preceding-sibling::p[contains(@style, "center")]/text()');
List<String> titles = [];
if (moviesTitles.isEmpty) {
moviesTitles = xpath(res, '//p[contains(@style, "center")]/text()');
}
for (var title in moviesTitles) {
if (title.isNotEmpty &&
!title.contains('Download') &&
!title.contains('Note:') &&
!title.contains('Copyright')) {
titles.add(title.split('[').first.trim());
}
}
for (var i = 0; i < titles.length; i++) {
final title = titles[i];
final quality = RegExp(r'\d{3,4}p').firstMatch(title)?.group(0) ?? "";
final url = episodesUrls[i];
MChapter ep = MChapter();
ep.name = title;
ep.url = url;
ep.scanlator = quality;
episodesList.add(ep);
}
} else {
List<String> seasonTitles = [];
final episodeTitles = xpath(res,
'//*[contains(@style, "center") or contains(@class, "maxbutton")]/parent::p//preceding-sibling::p[contains(@style, "center") and not(text()^="Episode")]/text()');
List<String> titles = [];
for (var title in episodeTitles) {
if (title.isNotEmpty) {
titles.add(title.split('[').first.trim());
}
}
int number = 0;
for (var i = 0; i < episodesTitles.length; i++) {
final episode = episodesTitles[i];
final episodeUrl = episodesUrls[i];
if (!episode.contains("Zip") || !episode.contains("Pack")) {
if (episode == "Episode 1" && seasonTitles.contains("Episode 1")) {
number++;
} else if (episode == "Episode 1") {
seasonTitles.add(episode);
}
final season =
RegExp(r'S(\d{2})').firstMatch(titles[number])?.group(1) ?? "";
final quality =
RegExp(r'\d{3,4}p').firstMatch(titles[number])?.group(0) ?? "";
MChapter ep = MChapter();
ep.name = "Season $season $episode $quality";
ep.url = episodeUrl;
ep.scanlator = quality;
episodesList.add(ep);
}
}
}
anime.chapters = episodesList.reversed.toList();
return anime;
}
@override
Future<List<MVideo>> getVideoList(String url) async {
final res = await getMediaUrl(url);
return await extractVideos(res);
}
@override
List<dynamic> getSourcePreferences() {
return [
EditTextPreference(
key: "pref_domain",
title: "Currently used domain",
summary: "",
value: "https://uhdmovies.zip",
dialogTitle: "Currently used domain",
dialogMessage: "",
text: "https://uhdmovies.zip"),
];
}
Future<List<MVideo>> extractVideos(String url) async {
List<MVideo> videos = [];
for (int type = 1; type < 3; type++) {
url = url.replaceAll("/file/", "/wfile/") + "?type=$type";
final res = (await client.get(Uri.parse(url))).body;
final links = xpath(res, '//div[@class="mb-4"]/a/@href');
for (int i = 0; i < links.length; i++) {
final link = links[i];
String decodedLink = link;
if (!link.contains("workers.dev")) {
decodedLink = utf8
.decode(base64Url.decode(substringAfter(link, "download?url=")));
}
MVideo video = MVideo();
video
..url = decodedLink
..originalUrl = decodedLink
..quality = "CF $type Worker ${i + 1}";
videos.add(video);
}
}
return videos;
}
Future<String> getMediaUrl(String url) async {
String res = "";
String host = "";
if (url.contains("?sid=")) {
final finalUrl = await redirectorBypasser(url);
host = Uri.parse(finalUrl).host;
res = (await client.get(Uri.parse(finalUrl))).body;
} else if (url.contains("r?key=")) {
res = (await client.get(Uri.parse(url))).body;
host = Uri.parse(url).host;
} else {
return "";
}
final path = substringBefore(substringAfter(res, "replace(\""), "\"");
if (path == "/404") return "";
return "https://$host$path";
}
Future<String> redirectorBypasser(String url) async {
final res = (await client.get(Uri.parse(url))).body;
String lastDoc = await recursiveDoc(url, res);
final js = xpath(lastDoc, '//script[contains(text(), "/?go=")]/text()');
if (js.isEmpty) return "";
String script = js.first;
String nextUrl =
substringBefore(substringAfter(script, "\"href\",\""), '"');
if (!nextUrl.contains("http")) return "";
String cookieName = substringAfter(nextUrl, "go=");
String cookieValue =
substringBefore(substringAfter(script, "'$cookieName', '"), "'");
final response = (await client.get(Uri.parse(nextUrl),
headers: {"referer": url, "Cookie": "$cookieName=$cookieValue"}))
.body;
final lastRes =
parseHtml(response).selectFirst("meta[http-equiv]").attr("content");
return substringAfter(lastRes, "url=");
}
MPages animeFromElement(String res) {
List<MManga> animeList = [];
final urls = xpath(res, '//*[@class="entry-image"]/a/@href');
final names = xpath(res, '//*[@class="entry-image"]/a/@title');
final images = xpath(res, '//*[@class="entry-image"]/a/img/@src');
for (var i = 0; i < names.length; i++) {
MManga anime = MManga();
anime.name = names[i].replaceAll("Download", "");
anime.imageUrl = images[i];
anime.link = urls[i];
animeList.add(anime);
}
final nextPage = xpath(res, '//a[@class="next page-numbers"]/@href');
return MPages(animeList, nextPage.isNotEmpty);
}
Future<String> recursiveDoc(String url, String html) async {
final urlR = xpath(html, '//form[@id="landing"]/@action');
if (urlR.isEmpty) return html;
final name = xpath(html, '//input/@name').first;
final value = xpath(html, '//input/@value').first;
final body = {"$name": value};
final response = (await client.post(Uri.parse(urlR.first),
headers: {"referer": url}, body: body))
.body;
return recursiveDoc(url, response);
}
}
UHDMovies main(MSource source) {
return UHDMovies(source: source);
}