Merge pull request #5 from kodjodevf/add-filter-method

Add filter method
This commit is contained in:
Moustapha Kodjo Amadou
2023-11-29 18:18:52 +01:00
committed by GitHub
49 changed files with 5679 additions and 1582 deletions

View File

@@ -1,7 +1,7 @@
import '../../../model/source.dart';
import '../../../utils/utils.dart';
const zorothemeVersion = "0.0.45";
const zorothemeVersion = "0.0.5";
const zorothemeSourceCodeUrl =
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/anime/multisrc/zorotheme/zorotheme-v$zorothemeVersion.dart";

View File

@@ -1,200 +0,0 @@
import 'package:mangayomi/bridge_lib.dart';
import 'dart:convert';
class ZoroTheme extends MProvider {
ZoroTheme();
@override
Future<MPages> getPopular(MSource source, int page) async {
final data = {"url": "${source.baseUrl}/most-popular?page=$page"};
final res = await http('GET', json.encode(data));
return animeElementM(res);
}
@override
Future<MPages> getLatestUpdates(MSource source, int page) async {
final data = {"url": "${source.baseUrl}/recently-updated?page=$page"};
final res = await http('GET', json.encode(data));
return animeElementM(res);
}
@override
Future<MPages> search(MSource source, String query, int page) async {
final data = {"url": "${source.baseUrl}/search?keyword=$query&page=$page"};
final res = await http('GET', json.encode(data));
return animeElementM(res);
}
@override
Future<MManga> getDetail(MSource source, String url) async {
final statusList = [
{
"Currently Airing": 0,
"Finished Airing": 1,
}
];
final data = {"url": "${source.baseUrl}$url"};
final res = await http('GET', json.encode(data));
MManga anime = MManga();
final status = xpath(res,
'//*[@class="anisc-info"]/div[contains(text(),"Status:")]/span[2]/text()')
.first;
anime.status = parseStatus(status, statusList);
anime.author = xpath(res,
'//*[@class="anisc-info"]/div[contains(text(),"Studios:")]/span/text()')
.first
.replaceAll("Studios:", "");
anime.description = xpath(res,
'//*[@class="anisc-info"]/div[contains(text(),"Overview:")]/text()')
.first
.replaceAll("Overview:", "");
final genre = xpath(res,
'//*[@class="anisc-info"]/div[contains(text(),"Genres:")]/a/text()');
anime.genre = genre;
final id = substringAfterLast(url, '-');
final urlEp =
"${source.baseUrl}/ajax${ajaxRoute('${source.baseUrl}')}/episode/list/$id";
final dataEp = {
"url": urlEp,
"headers": {"referer": url}
};
final resEp = await http('GET', json.encode(dataEp));
final html = json.decode(resEp)["html"];
final epUrls = querySelectorAll(html,
selector: "a.ep-item",
typeElement: 3,
attributes: "href",
typeRegExp: 0);
final numbers = querySelectorAll(html,
selector: "a.ep-item",
typeElement: 3,
attributes: "data-number",
typeRegExp: 0);
final titles = querySelectorAll(html,
selector: "a.ep-item",
typeElement: 3,
attributes: "title",
typeRegExp: 0);
List<String> episodes = [];
for (var i = 0; i < titles.length; i++) {
final number = numbers[i];
final title = titles[i];
episodes.add("Episode $number: $title");
}
List<MChapter>? episodesList = [];
for (var i = 0; i < episodes.length; i++) {
MChapter episode = MChapter();
episode.name = episodes[i];
episode.url = epUrls[i];
episodesList.add(episode);
}
anime.chapters = episodesList.reversed.toList();
return anime;
}
@override
Future<List<MVideo>> getVideoList(MSource source, String url) async {
final id = substringAfterLast(url, '?ep=');
final datas = {
"url":
"${source.baseUrl}/ajax${ajaxRoute('${source.baseUrl}')}/episode/servers?episodeId=$id",
"headers": {"referer": "${source.baseUrl}/$url"}
};
final res = await http('GET', json.encode(datas));
final html = json.decode(res)["html"];
final names = querySelectorAll(html,
selector: "div.server-item",
typeElement: 0,
attributes: "",
typeRegExp: 0);
final ids = querySelectorAll(html,
selector: "div.server-item",
typeElement: 3,
attributes: "data-id",
typeRegExp: 0);
final subDubs = querySelectorAll(html,
selector: "div.server-item",
typeElement: 3,
attributes: "data-type",
typeRegExp: 0);
List<MVideo> videos = [];
for (var i = 0; i < names.length; i++) {
final name = names[i];
final id = ids[i];
final subDub = subDubs[i];
final datasE = {
"url":
"${source.baseUrl}/ajax${ajaxRoute('${source.baseUrl}')}/episode/sources?id=$id",
"headers": {"referer": "${source.baseUrl}/$url"}
};
final resE = await http('GET', json.encode(datasE));
String epUrl = substringBefore(substringAfter(resE, "\"link\":\""), "\"");
print(epUrl);
List<MVideo> a = [];
if (name.contains("Vidstreaming")) {
a = await rapidCloudExtractor(epUrl, "Vidstreaming - $subDub");
} else if (name.contains("Vidcloud")) {
a = await rapidCloudExtractor(epUrl, "Vidcloud - $subDub");
} else if (name.contains("StreamTape")) {
a = await streamTapeExtractor(epUrl, "StreamTape - $subDub");
}
videos.addAll(a);
}
return videos;
}
MPages animeElementM(String res) {
List<MManga> animeList = [];
final urls = xpath(
res, '//*[@class^="flw-item"]/div[@class="film-detail"]/h3/a/@href');
final names = xpath(res,
'//*[@class^="flw-item"]/div[@class="film-detail"]/h3/a/@data-jname');
final images = xpath(
res, '//*[@class^="flw-item"]/div[@class="film-poster"]/img/@data-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);
}
final nextPage =
xpath(res, '//li[@class="page-item"]/a[@title="Next"]/@href', "");
return MPages(animeList, !nextPage.isEmpty);
}
String ajaxRoute(String baseUrl) {
if (baseUrl == "https://kaido.to") {
return "";
}
return "/v2";
}
}
ZoroTheme main() {
return ZoroTheme();
}

View File

@@ -0,0 +1,434 @@
import 'package:mangayomi/bridge_lib.dart';
import 'dart:convert';
class ZoroTheme extends MProvider {
ZoroTheme();
@override
Future<MPages> getPopular(MSource source, int page) async {
final data = {"url": "${source.baseUrl}/most-popular?page=$page"};
final res = await http('GET', json.encode(data));
return animeElementM(res);
}
@override
Future<MPages> getLatestUpdates(MSource source, int page) async {
final data = {"url": "${source.baseUrl}/recently-updated?page=$page"};
final res = await http('GET', json.encode(data));
return animeElementM(res);
}
@override
Future<MPages> search(
MSource source, String query, int page, FilterList filterList) async {
final filters = filterList.filters;
String url = "${source.baseUrl}/";
if (query.isEmpty) {
url += "filter?";
} else {
url += "search?keyword=$query";
}
for (var filter in filters) {
if (filter.type == "TypeFilter") {
final type = filter.values[filter.state].value;
if (type.isNotEmpty) {
url += "${ll(url)}type=$type";
}
} else if (filter.type == "StatusFilter") {
final status = filter.values[filter.state].value;
if (status.isNotEmpty) {
url += "${ll(url)}status=$status";
}
} else if (filter.type == "RatedFilter") {
final rated = filter.values[filter.state].value;
if (rated.isNotEmpty) {
url += "${ll(url)}rated=$rated";
}
} else if (filter.type == "ScoreFilter") {
final score = filter.values[filter.state].value;
if (score.isNotEmpty) {
url += "${ll(url)}score=$score";
}
} else if (filter.type == "SeasonFilter") {
final season = filter.values[filter.state].value;
if (season.isNotEmpty) {
url += "${ll(url)}season=$season";
}
} else if (filter.type == "LanguageFilter") {
final language = filter.values[filter.state].value;
if (language.isNotEmpty) {
url += "${ll(url)}language=$language";
}
} else if (filter.type == "SortFilter") {
final sort = filter.values[filter.state].value;
if (sort.isNotEmpty) {
url += "${ll(url)}sort=$sort";
}
} else if (filter.type == "StartYearFilter") {
final sy = filter.values[filter.state].value;
if (sy.isNotEmpty) {
url += "${ll(url)}sy=$sy";
}
} else if (filter.type == "StartMonthFilter") {
final sm = filter.values[filter.state].value;
if (sm.isNotEmpty) {
url += "${ll(url)}sm=$sm";
}
} else if (filter.type == "StartDayFilter") {
final sd = filter.values[filter.state].value;
if (sd.isNotEmpty) {
url += "${ll(url)}sd=$sd";
}
} else if (filter.type == "EndYearFilter") {
final ey = filter.values[filter.state].value;
if (ey.isNotEmpty) {
url += "${ll(url)}sy=$ey";
}
} else if (filter.type == "EndMonthFilter") {
final em = filter.values[filter.state].value;
if (em.isNotEmpty) {
url += "${ll(url)}sm=$em";
}
} else if (filter.type == "EndDayFilter") {
final ed = filter.values[filter.state].value;
if (ed.isNotEmpty) {
url += "${ll(url)}sd=$ed";
}
} else if (filter.type == "GenreFilter") {
final genre = (filter.state as List).where((e) => e.state).toList();
if (genre.isNotEmpty) {
url += "${ll(url)}genre=";
for (var st in genre) {
url += "${st.value},";
}
}
}
}
url += "${ll(url)}page=$page";
final data = {"url": url};
final res = await http('GET', json.encode(data));
return animeElementM(res);
}
@override
Future<MManga> getDetail(MSource source, String url) async {
final statusList = [
{
"Currently Airing": 0,
"Finished Airing": 1,
}
];
final data = {"url": "${source.baseUrl}$url"};
final res = await http('GET', json.encode(data));
MManga anime = MManga();
final status = xpath(res,
'//*[@class="anisc-info"]/div[contains(text(),"Status:")]/span[2]/text()')
.first;
anime.status = parseStatus(status, statusList);
anime.author = xpath(res,
'//*[@class="anisc-info"]/div[contains(text(),"Studios:")]/span/text()')
.first
.replaceAll("Studios:", "");
anime.description = xpath(res,
'//*[@class="anisc-info"]/div[contains(text(),"Overview:")]/text()')
.first
.replaceAll("Overview:", "");
final genre = xpath(res,
'//*[@class="anisc-info"]/div[contains(text(),"Genres:")]/a/text()');
anime.genre = genre;
final id = substringAfterLast(url, '-');
final urlEp =
"${source.baseUrl}/ajax${ajaxRoute('${source.baseUrl}')}/episode/list/$id";
final dataEp = {
"url": urlEp,
"headers": {"referer": url}
};
final resEp = await http('GET', json.encode(dataEp));
final html = json.decode(resEp)["html"];
final epUrls = querySelectorAll(html,
selector: "a.ep-item",
typeElement: 3,
attributes: "href",
typeRegExp: 0);
final numbers = querySelectorAll(html,
selector: "a.ep-item",
typeElement: 3,
attributes: "data-number",
typeRegExp: 0);
final titles = querySelectorAll(html,
selector: "a.ep-item",
typeElement: 3,
attributes: "title",
typeRegExp: 0);
List<String> episodes = [];
for (var i = 0; i < titles.length; i++) {
final number = numbers[i];
final title = titles[i];
episodes.add("Episode $number: $title");
}
List<MChapter>? episodesList = [];
for (var i = 0; i < episodes.length; i++) {
MChapter episode = MChapter();
episode.name = episodes[i];
episode.url = epUrls[i];
episodesList.add(episode);
}
anime.chapters = episodesList.reversed.toList();
return anime;
}
@override
Future<List<MVideo>> getVideoList(MSource source, String url) async {
final id = substringAfterLast(url, '?ep=');
final datas = {
"url":
"${source.baseUrl}/ajax${ajaxRoute('${source.baseUrl}')}/episode/servers?episodeId=$id",
"headers": {"referer": "${source.baseUrl}/$url"}
};
final res = await http('GET', json.encode(datas));
final html = json.decode(res)["html"];
final names = querySelectorAll(html,
selector: "div.server-item",
typeElement: 0,
attributes: "",
typeRegExp: 0);
final ids = querySelectorAll(html,
selector: "div.server-item",
typeElement: 3,
attributes: "data-id",
typeRegExp: 0);
final subDubs = querySelectorAll(html,
selector: "div.server-item",
typeElement: 3,
attributes: "data-type",
typeRegExp: 0);
List<MVideo> videos = [];
for (var i = 0; i < names.length; i++) {
final name = names[i];
final id = ids[i];
final subDub = subDubs[i];
final datasE = {
"url":
"${source.baseUrl}/ajax${ajaxRoute('${source.baseUrl}')}/episode/sources?id=$id",
"headers": {"referer": "${source.baseUrl}/$url"}
};
final resE = await http('GET', json.encode(datasE));
String epUrl = substringBefore(substringAfter(resE, "\"link\":\""), "\"");
print(epUrl);
List<MVideo> a = [];
if (name.contains("Vidstreaming")) {
a = await rapidCloudExtractor(epUrl, "Vidstreaming - $subDub");
} else if (name.contains("Vidcloud")) {
a = await rapidCloudExtractor(epUrl, "Vidcloud - $subDub");
} else if (name.contains("StreamTape")) {
a = await streamTapeExtractor(epUrl, "StreamTape - $subDub");
}
videos.addAll(a);
}
return videos;
}
MPages animeElementM(String res) {
List<MManga> animeList = [];
final urls = xpath(
res, '//*[@class^="flw-item"]/div[@class="film-detail"]/h3/a/@href');
final names = xpath(res,
'//*[@class^="flw-item"]/div[@class="film-detail"]/h3/a/@data-jname');
final images = xpath(
res, '//*[@class^="flw-item"]/div[@class="film-poster"]/img/@data-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);
}
final nextPage =
xpath(res, '//li[@class="page-item"]/a[@title="Next"]/@href', "");
return MPages(animeList, !nextPage.isEmpty);
}
String ajaxRoute(String baseUrl) {
if (baseUrl == "https://kaido.to") {
return "";
}
return "/v2";
}
List<SelectFilterOption> yearList = [
for (var i = 1917; i < 2024; i++)
SelectFilterOption(i.toString(), i.toString()),
SelectFilterOption("All", "")
];
@override
List<dynamic> getFilterList() {
return [
SelectFilter("TypeFilter", "Type", 0, [
SelectFilterOption("All", ""),
SelectFilterOption("Movie", "1"),
SelectFilterOption("TV", "2"),
SelectFilterOption("OVA", "3"),
SelectFilterOption("ONA", "4"),
SelectFilterOption("Special", "5"),
SelectFilterOption("Music", "6")
]),
SelectFilter("StatusFilter", "Status", 0, [
SelectFilterOption("All", ""),
SelectFilterOption("Finished Airing", "1"),
SelectFilterOption("Currently Airing", "2"),
SelectFilterOption("Not yet aired", "3")
]),
SelectFilter("RatedFilter", "Rated", 0, [
SelectFilterOption("All", ""),
SelectFilterOption("G", "1"),
SelectFilterOption("PG", "2"),
SelectFilterOption("PG-13", "3"),
SelectFilterOption("R", "4"),
SelectFilterOption("R+", "5"),
SelectFilterOption("Rx", "6")
]),
SelectFilter("ScoreFilter", "Score", 0, [
SelectFilterOption("All", ""),
SelectFilterOption("(1) Appalling", "1"),
SelectFilterOption("(2) Horrible", "2"),
SelectFilterOption("(3) Very Bad", "3"),
SelectFilterOption("(4) Bad", "4"),
SelectFilterOption("(5) Average", "5"),
SelectFilterOption("(6) Fine", "6"),
SelectFilterOption("(7) Good", "7"),
SelectFilterOption("(8) Very Good", "8"),
SelectFilterOption("(9) Great", "9"),
SelectFilterOption("(10) Masterpiece", "10")
]),
SelectFilter("SeasonFilter", "Season", 0, [
SelectFilterOption("All", ""),
SelectFilterOption("Spring", "1"),
SelectFilterOption("Summer", "2"),
SelectFilterOption("Fall", "3"),
SelectFilterOption("Winter", "4")
]),
SelectFilter("LanguageFilter", "Language", 0, [
SelectFilterOption("All", ""),
SelectFilterOption("SUB", "1"),
SelectFilterOption("DUB", "2"),
SelectFilterOption("SUB & DUB", "3")
]),
SelectFilter("SortFilter", "Sort by", 0, [
SelectFilterOption("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")
]),
SelectFilter(
"StartYearFilter", "Start year", 0, yearList.reversed.toList()),
SelectFilter("StartMonthFilter", "Start month", 0, [
SelectFilterOption("All", ""),
for (var i = 1; i < 13; i++)
SelectFilterOption(i.toString(), i.toString())
]),
SelectFilter("StartDayFilter", "Start day", 0, [
SelectFilterOption("All", ""),
for (var i = 1; i < 32; i++)
SelectFilterOption(i.toString(), i.toString()),
]),
SelectFilter("EndYearFilter", "End year", 0, yearList.reversed.toList()),
SelectFilter("EndmonthFilter", "End month", 0, [
SelectFilterOption("All", ""),
for (var i = 1; i < 32; i++)
SelectFilterOption(i.toString(), i.toString())
]),
SelectFilter("EndDayFilter", "End day", 0, [
SelectFilterOption("All", ""),
for (var i = 1; i < 32; i++)
SelectFilterOption(i.toString(), i.toString())
]),
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"),
CheckBoxFilter("Yaoi", "33"),
CheckBoxFilter("Yuri", "34")
]),
];
}
String ll(String url) {
if (url.contains("?")) {
return "&";
}
return "?";
}
}
ZoroTheme main() {
return ZoroTheme();
}

View File

@@ -53,7 +53,8 @@ class OkAnime extends MProvider {
}
@override
Future<MPages> search(MSource source, String query, int page) async {
Future<MPages> search(
MSource source, String query, int page, FilterList filterList) async {
String url = "${source.baseUrl}/search/?s=$query";
if (page > 1) {
url += "&page=$page";

View File

@@ -2,7 +2,7 @@ import '../../../../model/source.dart';
import '../../../../utils/utils.dart';
Source get okanimeSource => _okanimeSource;
const okanimeVersion = "0.0.25";
const okanimeVersion = "0.0.3";
const okanimeSourceCodeUrl =
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/anime/src/ar/okanime/okanime-v$okanimeVersion.dart";
Source _okanimeSource = Source(

View File

@@ -1,242 +0,0 @@
import 'package:mangayomi/bridge_lib.dart';
import 'dart:convert';
class Aniwave extends MProvider {
Aniwave();
@override
Future<MPages> 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<MPages> 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<MPages> 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<MManga> 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<MChapter>? 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<List<MVideo>> 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<MVideo> 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<MVideo> 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<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;
}
}
Map<String, String> getMirrorPref() {
return {
"aniwave.to": "https://aniwave.to",
"aniwave.bz": "https://aniwave.bz",
"aniwave.ws": "https://aniwave.ws",
};
}
Aniwave main() {
return Aniwave();
}

View File

@@ -0,0 +1,448 @@
import 'package:mangayomi/bridge_lib.dart';
import 'dart:convert';
class Aniwave extends MProvider {
Aniwave();
@override
Future<MPages> 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<MPages> 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<MPages> search(
MSource source, String query, int page, FilterList filterList) async {
final filters = filterList.filters;
String url = "${source.baseUrl}/filter?keyword=$query";
for (var filter in filters) {
if (filter.type == "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 data = {"url": "$url&page=$page"};
final res = await http('GET', json.encode(data));
return parseAnimeList(res);
}
@override
Future<MManga> 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<MChapter>? 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<List<MVideo>> 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<MVideo> 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<MVideo> 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<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;
}
@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")
]),
];
}
String ll(String url) {
if (url.contains("?")) {
return "&";
}
return "?";
}
}
Map<String, String> getMirrorPref() {
return {
"aniwave.to": "https://aniwave.to",
"aniwave.bz": "https://aniwave.bz",
"aniwave.ws": "https://aniwave.ws",
};
}
Aniwave main() {
return Aniwave();
}

View File

@@ -2,7 +2,7 @@ import '../../../../model/source.dart';
import '../../../../utils/utils.dart';
Source get aniwave => _aniwave;
const aniwaveVersion = "0.0.1";
const aniwaveVersion = "0.0.15";
const aniwaveCodeUrl =
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/anime/src/en/aniwave/aniwave-v$aniwaveVersion.dart";
Source _aniwave = Source(
@@ -13,5 +13,4 @@ Source _aniwave = Source(
iconUrl: getIconUrl("aniwave", "en"),
sourceCodeUrl: aniwaveCodeUrl,
version: aniwaveVersion,
appMinVerReq: "0.0.8",
isManga: false);

View File

@@ -1,165 +0,0 @@
import 'package:mangayomi/bridge_lib.dart';
import 'dart:convert';
class GogoAnime extends MProvider {
GogoAnime();
@override
Future<MPages> getPopular(MSource source, int page) async {
final data = {"url": "${source.baseUrl}/popular.html?page=$page"};
final res = await http('GET', json.encode(data));
List<MManga> animeList = [];
final urls = xpath(res, '//*[@class="img"]/a/@href');
final names = xpath(res, '//*[@class="img"]/a/@title');
final images = xpath(res, '//*[@class="img"]/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);
}
@override
Future<MPages> getLatestUpdates(MSource source, int page) async {
final data = {
"url":
"https://ajax.gogo-load.com/ajax/page-recent-release-ongoing.html?page=$page&type=1"
};
final res = await http('GET', json.encode(data));
List<MManga> animeList = [];
final urls =
xpath(res, '//*[@class="added_series_body popular"]/ul/li/a[1]/@href');
final names = xpath(
res, '//*[//*[@class="added_series_body popular"]/ul/li/a[1]/@title');
List<String> images = [];
List<String> imagess = xpath(res,
'//*[//*[@class="added_series_body popular"]/ul/li/a/div[@class="thumbnail-popular"]/@style');
for (var url in imagess) {
images.add(url.replaceAll("background: url('", "").replaceAll("');", ""));
}
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);
}
@override
Future<MPages> search(MSource source, String query, int page) async {
final data = {
"url": "${source.baseUrl}/search.html?keyword=$query&page=$page"
};
final res = await http('GET', json.encode(data));
List<MManga> animeList = [];
final urls = xpath(res, '//*[@class="img"]/a/@href');
final names = xpath(res, '//*[@class="img"]/a/@title');
final images = xpath(res, '//*[@class="img"]/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);
}
@override
Future<MManga> getDetail(MSource source, String url) async {
final statusList = [
{
"Ongoing": 0,
"Completed": 1,
}
];
final data = {"url": "${source.baseUrl}$url"};
final res = await http('GET', json.encode(data));
MManga anime = MManga();
final status = xpath(
res, '//*[@class="anime_info_body_bg"]/p[@class="type"][5]/text()')
.first
.replaceAll("Status: ", "");
anime.description = xpath(
res, '//*[@class="anime_info_body_bg"]/p[@class="type"][2]/text()')
.first
.replaceAll("Plot Summary: ", "");
anime.status = parseStatus(status, statusList);
anime.genre = xpath(
res, '//*[@class="anime_info_body_bg"]/p[@class="type"][3]/text()')
.first
.replaceAll("Genre: ", "")
.split(",");
final id = xpath(res, '//*[@id="movie_id"]/@value').first;
final urlEp =
"https://ajax.gogo-load.com/ajax/load-list-episode?ep_start=0&ep_end=4000&id=$id";
final dataEp = {"url": urlEp};
final resEp = await http('GET', json.encode(dataEp));
final epUrls = xpath(resEp, '//*[@id="episode_related"]/li/a/@href');
final names = xpath(
resEp, '//*[@id="episode_related"]/li/a/div[@class="name"]/text()');
List<String> episodes = [];
for (var a in names) {
episodes.add("Episode ${substringAfterLast(a, ' ')}");
}
List<MChapter>? episodesList = [];
for (var i = 0; i < episodes.length; i++) {
MChapter episode = MChapter();
episode.name = episodes[i];
episode.url = epUrls[i];
episodesList.add(episode);
}
anime.chapters = episodesList;
return anime;
}
@override
Future<List<MVideo>> getVideoList(MSource source, String url) async {
final datas = {"url": "${source.baseUrl}$url"};
final res = await http('GET', json.encode(datas));
final serverUrls =
xpath(res, '//*[@class="anime_muti_link"]/ul/li/a/@data-video');
final classNames = xpath(res, '//*[@class="anime_muti_link"]/ul/li/@class');
List<MVideo> videos = [];
for (var i = 0; i < classNames.length; i++) {
final name = classNames[i];
final url = serverUrls[i];
List<MVideo> a = [];
if (name.contains("anime")) {
a = await gogoCdnExtractor(url);
} else if (name.contains("vidcdn")) {
a = await gogoCdnExtractor(url);
} else if (name.contains("doodstream")) {
a = await doodExtractor(url);
} else if (name.contains("mp4upload")) {
a = await mp4UploadExtractor(url, null, "", "");
} else if (name.contains("streamsb")) {}
videos.addAll(a);
}
return videos;
}
}
GogoAnime main() {
return GogoAnime();
}

File diff suppressed because it is too large Load Diff

View File

@@ -2,12 +2,12 @@ import '../../../../model/source.dart';
import '../../../../utils/utils.dart';
Source get gogoanimeSource => _gogoanimeSource;
const gogoanimeVersion = "0.0.35";
const gogoanimeVersion = "0.0.4";
const gogoanimeSourceCodeUrl =
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/anime/src/en/gogoanime/gogoanime-v$gogoanimeVersion.dart";
Source _gogoanimeSource = Source(
name: "Gogoanime",
baseUrl: "https://gogoanime.tel",
baseUrl: "https://gogoanime3.net",
lang: "en",
typeSource: "single",
iconUrl: getIconUrl("gogoanime", "en"),

View File

@@ -57,7 +57,8 @@ class KissKh extends MProvider {
}
@override
Future<MPages> search(MSource source, String query, int page) async {
Future<MPages> search(
MSource source, String query, int page, FilterList filterList) async {
final data = {
"url": "${source.baseUrl}/api/DramaList/Search?q=$query&type=0"
};

View File

@@ -2,7 +2,7 @@ import '../../../../model/source.dart';
import '../../../../utils/utils.dart';
Source get kisskhSource => _kisskhSource;
const kisskhVersion = "0.0.3";
const kisskhVersion = "0.0.35";
const kisskhSourceCodeUrl =
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/anime/src/en/kisskh/kisskh-v$kisskhVersion.dart";
Source _kisskhSource = Source(

View File

@@ -53,7 +53,8 @@ class AnimesUltra extends MProvider {
}
@override
Future<MPages> search(MSource source, String query, int page) async {
Future<MPages> search(
MSource source, String query, int page, FilterList filterList) async {
final data = {"url": "${source.baseUrl}/"};
final res = await http('GET', json.encode(data));

View File

@@ -2,7 +2,7 @@ import '../../../../model/source.dart';
import '../../../../utils/utils.dart';
Source get animesultraSource => _animesultraSource;
const animesultraVersion = "0.0.35";
const animesultraVersion = "0.0.4";
const animesultraSourceCodeUrl =
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/anime/src/fr/animesultra/animesultra-v$animesultraVersion.dart";
Source _animesultraSource = Source(

View File

@@ -24,7 +24,8 @@ class FrAnime extends MProvider {
}
@override
Future<MPages> search(MSource source, String query, int page) async {
Future<MPages> search(
MSource source, String query, int page, FilterList filterList) async {
final res = await dataBase();
return animeSeachFetch(res, query);

View File

@@ -2,7 +2,7 @@ import '../../../../model/source.dart';
import '../../../../utils/utils.dart';
Source get franimeSource => _franimeSource;
const franimeVersion = "0.0.35";
const franimeVersion = "0.0.4";
const franimeSourceCodeUrl =
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/anime/src/fr/franime/franime-v$franimeVersion.dart";
Source _franimeSource = Source(

View File

@@ -71,7 +71,8 @@ class OtakuFr extends MProvider {
}
@override
Future<MPages> search(MSource source, String query, int page) async {
Future<MPages> search(
MSource source, String query, int page, FilterList filterList) async {
final data = {
"url": "${source.baseUrl}/toute-la-liste-affiches/page/$page/?q=$query"
};

View File

@@ -2,7 +2,7 @@ import '../../../../model/source.dart';
import '../../../../utils/utils.dart';
Source get otakufr => _otakufr;
const otakufrVersion = "0.0.35";
const otakufrVersion = "0.0.4";
const otakufrCodeUrl =
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/anime/src/fr/otakufr/otakufr-v$otakufrVersion.dart";
Source _otakufr = Source(

View File

@@ -46,7 +46,8 @@ class NimeGami extends MProvider {
}
@override
Future<MPages> search(MSource source, String query, int page) async {
Future<MPages> search(
MSource source, String query, int page, FilterList filterList) async {
final data = {
"url": "${source.baseUrl}/page/$page/?s=$query&post_type=post"
};

View File

@@ -2,7 +2,7 @@ import '../../../../model/source.dart';
import '../../../../utils/utils.dart';
Source get nimegami => _nimegami;
const nimegamiVersion = "0.0.2";
const nimegamiVersion = "0.0.25";
const nimegamiCodeUrl =
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/anime/src/id/nimegami/nimegami-v$nimegamiVersion.dart";
Source _nimegami = Source(
@@ -13,5 +13,4 @@ Source _nimegami = Source(
iconUrl: getIconUrl("nimegami", "id"),
sourceCodeUrl: nimegamiCodeUrl,
version: nimegamiVersion,
appMinVerReq: "0.0.8",
isManga: false);

View File

@@ -23,7 +23,8 @@ class OploVerz extends MProvider {
}
@override
Future<MPages> search(MSource source, String query, int page) async {
Future<MPages> search(
MSource source, String query, int page, FilterList filterList) async {
final data = {
"url": "${source.baseUrl}/anime-list/page/$page/?title=$query"
};
@@ -95,15 +96,14 @@ class OploVerz extends MProvider {
}));
final playerLink =
xpath(ress, '//iframe[@class="playeriframe"]/@src').first;
print(playerLink);
final resPlayer = await http('GET', json.encode({"url": playerLink}));
var resJson = substringBefore(substringAfter(resPlayer, "= "), "<");
var streams = json.decode(resJson)["streams"] as List;
var streams =
json.decode(getMapValue(resJson, "streams", encode: true)) as List;
List<MVideo> videos = [];
for (var stream in streams) {
print(stream["play_url"]);
final videoUrl = stream["play_url"];
final quality = getQuality(stream["format_id"]);
final videoUrl = getMapValue(stream, "play_url");
final quality = getQuality(getMapValue(stream, "format_id"));
MVideo video = MVideo();
video

View File

@@ -2,7 +2,7 @@ import '../../../../model/source.dart';
import '../../../../utils/utils.dart';
Source get oploverz => _oploverz;
const oploverzVersion = "0.0.1";
const oploverzVersion = "0.0.15";
const oploverzCodeUrl =
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/anime/src/id/oploverz/oploverz-v$oploverzVersion.dart";
Source _oploverz = Source(
@@ -13,5 +13,4 @@ Source _oploverz = Source(
iconUrl: getIconUrl("oploverz", "id"),
sourceCodeUrl: oploverzCodeUrl,
version: oploverzVersion,
appMinVerReq: "0.0.7",
isManga: false);

View File

@@ -19,7 +19,8 @@ class OtakuDesu extends MProvider {
}
@override
Future<MPages> search(MSource source, String query, int page) async {
Future<MPages> search(
MSource source, String query, int page, FilterList filterList) async {
final data = {"url": "${source.baseUrl}/?s=$query&post_type=anime"};
final res = await http('GET', json.encode(data));
List<MManga> animeList = [];

View File

@@ -2,7 +2,7 @@ import '../../../../model/source.dart';
import '../../../../utils/utils.dart';
Source get otakudesu => _otakudesu;
const otakudesuVersion = "0.0.2";
const otakudesuVersion = "0.0.25";
const otakudesuCodeUrl =
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/anime/src/id/otakudesu/otakudesu-v$otakudesuVersion.dart";
Source _otakudesu = Source(
@@ -12,6 +12,4 @@ Source _otakudesu = Source(
typeSource: "single",
iconUrl: getIconUrl("otakudesu", "id"),
sourceCodeUrl: otakudesuCodeUrl,
version: otakudesuVersion,
appMinVerReq: "0.0.8",
isManga: false);

File diff suppressed because one or more lines are too long

View File

@@ -56,7 +56,8 @@ class HeanCms extends MProvider {
}
@override
Future<MPages> search(MSource source, String query, int page) async {
Future<MPages> search(
MSource source, String query, int page, FilterList filterList) async {
final headers = getHeader(source.baseUrl);
String res = "";
if (!useNewQueryEndpoint(source.source)) {

View File

@@ -1,7 +1,7 @@
import '../../../model/source.dart';
import '../../../utils/utils.dart';
const heancmsVersion = "0.0.35";
const heancmsVersion = "0.0.4";
const heancmsSourceCodeUrl =
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/manga/multisrc/heancms/heancms-v$heancmsVersion.dart";
const defaultDateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ";

View File

@@ -67,11 +67,53 @@ class Madara extends MProvider {
}
@override
Future<MPages> search(MSource source, String query, int page) async {
final data = {
"url": "${source.baseUrl}/?s=$query&post_type=wp-manga",
"sourceId": source.id
};
Future<MPages> search(
MSource source, String query, int page, FilterList filterList) async {
final filters = filterList.filters;
String url = "${source.baseUrl}/?s=$query&post_type=wp-manga";
for (var filter in filters) {
if (filter.type == "AuthorFilter") {
if (filter.state.isNotEmpty) {
url += "${ll(url)}author=${Uri.encodeComponent(filter.state)}";
}
} else if (filter.type == "ArtistFilter") {
if (filter.state.isNotEmpty) {
url += "${ll(url)}artist=${Uri.encodeComponent(filter.state)}";
}
} else if (filter.type == "YearFilter") {
if (filter.state.isNotEmpty) {
url += "${ll(url)}release=${Uri.encodeComponent(filter.state)}";
}
} 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 == "OrderByFilter") {
if (filter.state != 0) {
final order = filter.values[filter.state].value;
url += "${ll(url)}m_orderby=$order";
}
} else if (filter.type == "AdultContentFilter") {
final ctn = filter.values[filter.state].value;
if (ctn.isNotEmpty) {
url += "${ll(url)}adult=$ctn";
}
} else if (filter.type == "GenreListFilter") {
final genres = (filter.state as List).where((e) => e.state).toList();
if (genres.isNotEmpty) {
for (var genre in genres) {
url += "${ll(url)}genre[]=${genre.value},";
}
}
}
}
final data = {"url": url, "sourceId": source.id};
final res = await http('GET', json.encode(data));
List<MManga> mangaList = [];
@@ -312,6 +354,42 @@ class Madara extends MProvider {
}
return pageUrls;
}
@override
List<dynamic> getFilterList() {
return [
TextFilter("AuthorFilter", "Author"),
TextFilter("ArtistFilter", "Artist"),
TextFilter("YearFilter", "Year of Released"),
GroupFilter("StatusFilter", "Status", [
CheckBoxFilter("Completed", "end"),
CheckBoxFilter("Ongoing", "on-going"),
CheckBoxFilter("Canceled", "canceled"),
CheckBoxFilter("On Hold", "on-hold"),
]),
SelectFilter("OrderByFilter", "Order By", 0, [
SelectFilterOption("Relevance", ""),
SelectFilterOption("Latest", "latest"),
SelectFilterOption("A-Z", "alphabet"),
SelectFilterOption("Rating", "rating"),
SelectFilterOption("Trending", "trending"),
SelectFilterOption("Most Views", "views"),
SelectFilterOption("New", "new-manga"),
]),
SelectFilter("AdultContentFilter", "Adult Content", 0, [
SelectFilterOption("All", ""),
SelectFilterOption("None", "0"),
SelectFilterOption("Only", "1"),
])
];
}
String ll(String url) {
if (url.contains("?")) {
return "&";
}
return "?";
}
}
Madara main() {

View File

@@ -1,7 +1,7 @@
import '../../../model/source.dart';
import '../../../utils/utils.dart';
const madaraVersion = "0.0.35";
const madaraVersion = "0.0.4";
const madaraSourceCodeUrl =
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/manga/multisrc/madara/madara-v$madaraVersion.dart";
const defaultDateFormat = "MMMM dd, yyyy";

View File

@@ -25,9 +25,49 @@ class MangaReader extends MProvider {
}
@override
Future<MPages> search(MSource source, String query, int page) async {
final url =
Future<MPages> search(
MSource source, String query, int page, FilterList filterList) async {
final filters = filterList.filters;
String url =
"${source.baseUrl}${getMangaUrlDirectory(source.name)}/?&title=$query&page=$page";
for (var filter in filters) {
if (filter.type == "AuthorFilter") {
url += "${ll(url)}author=${Uri.encodeComponent(filter.state)}";
} else if (filter.type == "YearFilter") {
url += "${ll(url)}yearx=${Uri.encodeComponent(filter.state)}";
} else if (filter.type == "StatusFilter") {
final status = filter.values[filter.state].value;
url += "${ll(url)}status=$status";
} else if (filter.type == "TypeFilter") {
final type = filter.values[filter.state].value;
url += "${ll(url)}type=$type";
} else if (filter.type == "OrderByFilter") {
final order = filter.values[filter.state].value;
url += "${ll(url)}order=$order";
} else if (filter.type == "GenreListFilter") {
final included = (filter.state as List)
.where((e) => e.state == 1 ? true : false)
.toList();
final excluded = (filter.state as List)
.where((e) => e.state == 2 ? true : false)
.toList();
if (included.isNotEmpty) {
url += "${ll(url)}genres[]=";
for (var val in included) {
url += "${val.value},";
}
}
if (excluded.isNotEmpty) {
url += "${ll(url)}genres[]=";
for (var val in excluded) {
url += "-${val.value},";
}
}
}
}
final data = {"url": url, "sourceId": source.id};
final res = await http('GET', json.encode(data));
@@ -195,6 +235,48 @@ class MangaReader extends MProvider {
return MPages(mangaList, true);
}
@override
List<dynamic> getFilterList() {
return [
SeparatorFilter(),
TextFilter("AuthorFilter", "Author"),
TextFilter("YearFilter", "Year"),
SelectFilter("StatusFilter", "Status", 0, [
SelectFilterOption("All", ""),
SelectFilterOption("Ongoing", "ongoing"),
SelectFilterOption("Completed", "completed"),
SelectFilterOption("Hiatus", "hiatus"),
SelectFilterOption("Dropped", "dropped"),
]),
SelectFilter("TypeFilter", "Type", 0, [
SelectFilterOption("All", ""),
SelectFilterOption("Manga", "Manga"),
SelectFilterOption("Manhwa", "Manhwa"),
SelectFilterOption("Manhua", "Manhua"),
SelectFilterOption("Comic", "Comic"),
]),
SelectFilter("OrderByFilter", "Sort By", 0, [
SelectFilterOption("Default", ""),
SelectFilterOption("A-Z", "title"),
SelectFilterOption("Z-A", "titlereverse"),
SelectFilterOption("Latest Update", "update"),
SelectFilterOption("Latest Added", "latest"),
SelectFilterOption("Popular", "popular"),
]),
HeaderFilter("Genre exclusion is not available for all sources"),
GroupFilter("GenreListFilter", "Genre", [
TriStateFilter("Press reset to attempt to fetch genres", ""),
]),
];
}
String ll(String url) {
if (url.contains("?")) {
return "&";
}
return "?";
}
String getMangaUrlDirectory(String sourceName) {
if (sourceName == "Sushi-Scan") {
return "/catalogue";

View File

@@ -1,7 +1,7 @@
import '../../../model/source.dart';
import '../../../utils/utils.dart';
const mangareaderVersion = "0.0.45";
const mangareaderVersion = "0.0.5";
const mangareaderSourceCodeUrl =
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/manga/multisrc/mangareader/mangareader-v$mangareaderVersion.dart";
const defaultDateFormat = "MMMM dd, yyyy";

View File

@@ -68,32 +68,74 @@ class MMRCMS extends MProvider {
}
@override
Future<MPages> search(MSource source, String query, int page) async {
final url = "${source.baseUrl}/search?query=$query";
Future<MPages> search(
MSource source, String query, int page, FilterList filterList) async {
final filters = filterList.filters;
String url = "";
if (query.isNotEmpty) {
url = "${source.baseUrl}/search?query=$query";
} else {
url = "${source.baseUrl}/filterList?page=$page";
for (var filter in filters) {
if (filter.type == "AuthorFilter") {
url += "${ll(url)}author=${Uri.encodeComponent(filter.state)}";
} else if (filter.type == "SortFilter") {
url += "${ll(url)}sortBy=${filter.values[filter.state.index].value}";
final asc = filter.state.ascending ? "asc=true" : "asc=false";
url += "${ll(url)}$asc";
} else if (filter.type == "CategoryFilter") {
if (filter.state != 0) {
final cat = filter.values[filter.state].value;
url += "${ll(url)}cat=$cat";
}
} else if (filter.type == "BeginsWithFilter") {
if (filter.state != 0) {
final a = filter.values[filter.state].value;
url += "${ll(url)}alpha=$a";
}
}
}
}
final data = {"url": url, "sourceId": source.id};
final res = await http('GET', json.encode(data));
List<MManga> mangaList = [];
final jsonList = json.decode(res)["suggestions"];
List<String> urls = [];
List<String> names = [];
List<String> images = [];
for (var da in jsonList) {
String value = da["value"];
String data = da["data"];
if (source.name == 'Scan VF') {
urls.add('${source.baseUrl}/$data');
} else if (source.name == 'Manga-FR') {
urls.add('${source.baseUrl}/lecture-en-ligne/$data');
} else {
urls.add('${source.baseUrl}/manga/$data');
if (query.isNotEmpty) {
final jsonList = json.decode(res)["suggestions"];
for (var da in jsonList) {
String value = da["value"];
String data = da["data"];
if (source.name == 'Scan VF') {
urls.add('${source.baseUrl}/$data');
} else if (source.name == 'Manga-FR') {
urls.add('${source.baseUrl}/lecture-en-ligne/$data');
} else {
urls.add('${source.baseUrl}/manga/$data');
}
names.add(value);
if (source.name == "Manga-FR") {
images.add("${source.baseUrl}/uploads/manga/$data.jpg");
} else {
images.add(
"${source.baseUrl}/uploads/manga/$data/cover/cover_250x350.jpg");
}
}
names.add(value);
if (source.name == "Manga-FR") {
images.add("${source.baseUrl}/uploads/manga/$data.jpg");
} else {
images.add(
"${source.baseUrl}/uploads/manga/$data/cover/cover_250x350.jpg");
} else {
urls = xpath(res, '//div/div/div/a/@href');
names = xpath(res, '//div/div/div/a/text()');
for (var url in urls) {
String slug = substringAfterLast(url, '/');
if (source.name == "Manga-FR") {
images.add("${source.baseUrl}/uploads/manga/${slug}.jpg");
} else {
images.add(
"${source.baseUrl}/uploads/manga/${slug}/cover/cover_250x350.jpg");
}
}
}
@@ -189,6 +231,91 @@ class MMRCMS extends MProvider {
return pagesUrl;
}
List<dynamic> getFilterList() {
return [
HeaderFilter("NOTE: Ignored if using text search!"),
SeparatorFilter(),
TextFilter("AuthorFilter", "Author"),
SelectFilter("CategoryFilter", "Category", 0, [
SelectFilterOption("Any", ""),
SelectFilterOption("Action", "Action"),
SelectFilterOption("Adventure", "Adventure"),
SelectFilterOption("Comedy", "Comedy"),
SelectFilterOption("Doujinshi", "Doujinshi"),
SelectFilterOption("Drama", "Drama"),
SelectFilterOption("Ecchi", "Ecchi"),
SelectFilterOption("Fantasy", "Fantasy"),
SelectFilterOption("Gender Bender", "Gender Bender"),
SelectFilterOption("Harem", "Harem"),
SelectFilterOption("Historical", "Historical"),
SelectFilterOption("Horror", "Horror"),
SelectFilterOption("Josei", "Josei"),
SelectFilterOption("Martial Arts", "Martial Arts"),
SelectFilterOption("Mature", "Mature"),
SelectFilterOption("Mecha", "Mecha"),
SelectFilterOption("Mystery", "Mystery"),
SelectFilterOption("One Shot", "One Shot"),
SelectFilterOption("Psychological", "Psychological"),
SelectFilterOption("Romance", "Romance"),
SelectFilterOption("School Life", "School Life"),
SelectFilterOption("Sci-fi", "Sci-fi"),
SelectFilterOption("Seinen", "Seinen"),
SelectFilterOption("Shoujo", "Shoujo"),
SelectFilterOption("Shoujo Ai", "Shoujo Ai"),
SelectFilterOption("Shounen", "Shounen"),
SelectFilterOption("Shounen Ai", "Shounen Ai"),
SelectFilterOption("Slice of Life", "Slice of Life"),
SelectFilterOption("Sports", "Sports"),
SelectFilterOption("Supernatural", "Supernatural"),
SelectFilterOption("Tragedy", "Tragedy"),
SelectFilterOption("Yaoi", "Yaoi"),
SelectFilterOption("Yuri", "Yuri"),
]),
SelectFilter("BeginsWithFilter", "Begins with", 0, [
SelectFilterOption("Any", ""),
SelectFilterOption("#", "#"),
SelectFilterOption("A", "A"),
SelectFilterOption("B", "B"),
SelectFilterOption("C", "C"),
SelectFilterOption("D", "D"),
SelectFilterOption("E", "E"),
SelectFilterOption("F", "F"),
SelectFilterOption("G", "G"),
SelectFilterOption("H", "H"),
SelectFilterOption("I", "I"),
SelectFilterOption("J", "J"),
SelectFilterOption("K", "K"),
SelectFilterOption("L", "L"),
SelectFilterOption("M", "M"),
SelectFilterOption("N", "N"),
SelectFilterOption("O", "O"),
SelectFilterOption("P", "P"),
SelectFilterOption("Q", "Q"),
SelectFilterOption("R", "R"),
SelectFilterOption("S", "S"),
SelectFilterOption("T", "T"),
SelectFilterOption("U", "U"),
SelectFilterOption("V", "V"),
SelectFilterOption("W", "W"),
SelectFilterOption("X", "X"),
SelectFilterOption("Y", "Y"),
SelectFilterOption("Z", "Z"),
]),
SortFilter("SortFilter", "Sort", SortState(0, true), [
SelectFilterOption("Name", "name"),
SelectFilterOption("Popularity", "views"),
SelectFilterOption("Last update", "last_release"),
])
];
}
String ll(String url) {
if (url.contains("?")) {
return "&";
}
return "?";
}
}
MMRCMS main() {

View File

@@ -1,7 +1,7 @@
import '../../../model/source.dart';
import '../../../utils/utils.dart';
const mmrcmsVersion = "0.0.35";
const mmrcmsVersion = "0.0.4";
const mmrcmsSourceCodeUrl =
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/manga/multisrc/mmrcms/mmrcms-v$mmrcmsVersion.dart";
const defaultDateFormat = "d MMM. yyyy";

View File

@@ -1,225 +0,0 @@
import 'package:mangayomi/bridge_lib.dart';
import 'dart:convert';
class NepNep extends MProvider {
NepNep();
@override
Future<MPages> getPopular(MSource source, int page) async {
final data = {"url": "${source.baseUrl}/search/"};
final res = await http('GET', json.encode(data));
final directory = directoryFromDocument(res);
final resSort = sortMapList(json.decode(directory), "vm", 1);
return parseDirectory(resSort);
}
@override
Future<MPages> getLatestUpdates(MSource source, int page) async {
final data = {"url": "${source.baseUrl}/search/"};
final res = await http('GET', json.encode(data));
final directory = directoryFromDocument(res);
final resSort = sortMapList(json.decode(directory), "lt", 1);
return parseDirectory(resSort);
}
@override
Future<MPages> search(MSource source, String query, int page) async {
final data = {"url": "${source.baseUrl}/search/"};
final res = await http('GET', json.encode(data));
final directory = directoryFromDocument(res);
final resSort = sortMapList(json.decode(directory), "lt", 1);
final datas = json.decode(resSort) as List;
final queryRes = datas.where((e) {
String name = getMapValue(json.encode(e), 's');
return name.toLowerCase().contains(query.toLowerCase());
}).toList();
return parseDirectory(json.encode(queryRes));
}
@override
Future<MManga> getDetail(MSource source, String url) async {
final statusList = [
{"Ongoing": 0, "Completed": 1, "Cancelled": 3, "Hiatus": 2}
];
final headers = getHeader(source.baseUrl);
final data = {"url": '${source.baseUrl}/manga/$url', "headers": headers};
final res = await http('GET', json.encode(data));
MManga manga = MManga();
manga.author = xpath(res,
'//li[contains(@class,"list-group-item") and contains(text(),"Author")]/a/text()')
.first;
manga.description = xpath(res,
'//li[contains(@class,"list-group-item") and contains(text(),"Description:")]/div/text()')
.first;
final status = xpath(res,
'//li[contains(@class,"list-group-item") and contains(text(),"Status")]/a/text()')
.first;
manga.status = parseStatus(toStatus(status), statusList);
manga.genre = xpath(res,
'//li[contains(@class,"list-group-item") and contains(text(),"Genre(s)")]/a/text()');
final script =
xpath(res, '//script[contains(text(), "MainFunction")]/text()').first;
final vmChapters =
substringBefore(substringAfter(script, "vm.Chapters = "), ";");
final chapters = json.decode(vmChapters) as List;
List<MChapter> chaptersList = [];
for (var ch in chapters) {
final c = json.encode(ch);
MChapter chapter = MChapter();
String name = getMapValue(c, 'ChapterName');
String indexChapter = getMapValue(c, 'Chapter');
if (name.isEmpty) {
name = '${getMapValue(c, 'Type')} ${chapterImage(indexChapter, true)}';
}
chapter.name = name == "null" ? "" : name;
chapter.url =
'/read-online/${substringAfter(url, "/manga/")}${chapterURLEncode(getMapValue(c, 'Chapter'))}';
chapter.dateUpload = parseDates([getMapValue(c, 'Date')],
source.dateFormat, source.dateFormatLocale)
.first;
chaptersList.add(chapter);
}
manga.chapters = chaptersList;
return manga;
}
@override
Future<List<String>> getPageList(MSource source, String url) async {
final headers = getHeader(source.baseUrl);
List<String> pages = [];
final data = {"url": '${source.baseUrl}$url', "headers": headers};
print(data);
final res = await http('GET', json.encode(data));
final script =
xpath(res, '//script[contains(text(), "MainFunction")]/text()').first;
final chapScript =
substringBefore(substringAfter(script, "vm.CurChapter = "), ";");
final pathName = substringBefore(
substringAfter(script, "vm.CurPathName = \"", ""), "\"");
var directory = getMapValue(chapScript, 'Directory') == 'null'
? ''
: getMapValue(chapScript, 'Directory');
if (directory.length > 0) {
directory += '/';
}
final mangaName =
substringBefore(substringAfter(url, "/read-online/"), "-chapter");
var chNum = chapterImage(getMapValue(chapScript, 'Chapter'), false);
var totalPages = int.parse(getMapValue(chapScript, 'Page'));
for (int page = 1; page <= totalPages; page++) {
String paddedPageNumber = "$page".padLeft(3, '0');
String pageUrl =
'https://$pathName/manga/$mangaName/$directory$chNum-$paddedPageNumber.png';
pages.add(pageUrl);
}
return pages;
}
String directoryFromDocument(String res) {
final script =
xpath(res, '//script[contains(text(), "MainFunction")]/text()').first;
return substringBefore(
substringAfter(script, "vm.Directory = "), "vm.GetIntValue")
.replaceAll(";", " ");
}
MPages parseDirectory(String res) {
List<MManga> mangaList = [];
final datas = json.decode(res) as List;
for (var data in datas) {
final d = json.encode(data);
MManga manga = MManga();
manga.name = getMapValue(d, "s");
manga.imageUrl =
'https://temp.compsci88.com/cover/${getMapValue(d, "i")}.jpg';
manga.link = getMapValue(d, "i");
mangaList.add(manga);
}
return MPages(mangaList, true);
}
String chapterImage(String e, bool cleanString) {
var a = e.substring(1, e.length - 1);
if (cleanString) {
a = regExp(a, r'^0+', "", 0, 0);
}
var b = int.parse(e.substring(e.length - 1));
if (b == 0 && a.isNotEmpty) {
return a;
} else if (b == 0 && a.isEmpty) {
return '0';
} else {
return '$a.$b';
}
}
String toStatus(String status) {
if (status.contains("Ongoing")) {
return "Ongoing";
} else if (status.contains("Complete")) {
return "Complete";
} else if (status.contains("Cancelled")) {
return "Cancelled";
} else if (status.contains("Hiatus")) {
return "Hiatus";
}
return "";
}
String chapterURLEncode(String e) {
var index = ''.toString();
var t = int.parse(e.substring(0, 1));
if (t != 1) {
index = '-index-$t';
}
var dgt = 0;
var inta = int.parse(e);
if (inta < 100100) {
dgt = 4;
} else if (inta < 101000) {
dgt = 3;
} else if (inta < 110000) {
dgt = 2;
} else {
dgt = 1;
}
final n = e.substring(dgt, e.length - 1);
var suffix = ''.toString();
final path = int.parse(e.substring(e.length - 1));
if (path != 0) {
suffix = '.$path';
}
return '-chapter-$n$suffix$index.html';
}
}
Map<String, String> getHeader(String url) {
final headers = {
'Referer': '$url/',
"User-Agent":
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:71.0) Gecko/20100101 Firefox/77.0"
};
return headers;
}
NepNep main() {
return NepNep();
}

View File

@@ -0,0 +1,412 @@
import 'package:mangayomi/bridge_lib.dart';
import 'dart:convert';
class NepNep extends MProvider {
NepNep();
@override
Future<MPages> getPopular(MSource source, int page) async {
final data = {"url": "${source.baseUrl}/search/"};
final res = await http('GET', json.encode(data));
final directory = directoryFromDocument(res);
final resSort = sortMapList(json.decode(directory), "vm", 1);
return parseDirectory(resSort);
}
@override
Future<MPages> getLatestUpdates(MSource source, int page) async {
final data = {"url": "${source.baseUrl}/search/"};
final res = await http('GET', json.encode(data));
final directory = directoryFromDocument(res);
final resSort = sortMapList(json.decode(directory), "lt", 1);
return parseDirectory(resSort);
}
@override
Future<MPages> search(
MSource source, String query, int page, FilterList filterList) async {
final filters = filterList.filters;
List<dynamic> queryRes = [];
final data = {"url": "${source.baseUrl}/search/"};
final res = await http('GET', json.encode(data));
final directory = directoryFromDocument(res);
final resSort = sortMapList(json.decode(directory), "lt", 1);
final datas = json.decode(resSort) as List;
queryRes = datas.where((e) {
String name = getMapValue(json.encode(e), 's');
return name.toLowerCase().contains(query.toLowerCase());
}).toList();
for (var filter in filters) {
if (filter.type == "SortFilter") {
final ascending = filter.state.ascending;
String sortBy = "s";
if (filter.state.index == 1) {
sortBy = "v";
}
if (filter.state.index == 2) {
sortBy = "ls";
}
queryRes = json.decode(sortMapList(queryRes, sortBy, 1)) as List;
if (ascending) {
queryRes = queryRes.reversed.toList();
}
}
if (filter.type == "ScanStatusFilter") {
if (filter.state != 0) {
queryRes = queryRes.where((e) {
final value = getMapValue(json.encode(e), 'ss');
return value.toLowerCase().contains(
(filter.values[filter.state].value as String).toLowerCase());
}).toList();
}
} else if (filter.type == "PublishStatusFilter") {
if (filter.state != 0) {
queryRes = queryRes.where((e) {
final value = getMapValue(json.encode(e), 'ps');
return value.toLowerCase().contains(
(filter.values[filter.state].value as String).toLowerCase());
}).toList();
}
} else if (filter.type == "TypeFilter") {
if (filter.state != 0) {
queryRes = queryRes.where((e) {
final value = getMapValue(json.encode(e), 't');
return value.toLowerCase().contains(
(filter.values[filter.state].value as String).toLowerCase());
}).toList();
}
} else if (filter.type == "TranslationFilter") {
if (filter.state != 0) {
queryRes = queryRes.where((e) {
final value = getMapValue(json.encode(e), 'o');
return value.toLowerCase().contains("yes");
}).toList();
}
} else if (filter.type == "YearFilter") {
if (filter.state.isNotEmpty) {
queryRes = queryRes.where((e) {
final value = getMapValue(json.encode(e), 'y');
return value
.toLowerCase()
.contains((filter.name as String).toLowerCase());
}).toList();
}
} else if (filter.type == "AuthorFilter") {
if (filter.state.isNotEmpty) {
queryRes = queryRes.where((e) {
final value = getMapValue(json.encode(e), 'a');
return value
.toLowerCase()
.contains((filter.name as String).toLowerCase());
}).toList();
}
} else if (filter.type == "GenresFilter") {
final included = (filter.state as List)
.where((e) => e.state == 1 ? true : false)
.toList();
final excluded = (filter.state as List)
.where((e) => e.state == 2 ? true : false)
.toList();
if (included.isNotEmpty) {
for (var val in included) {
queryRes = queryRes.where((e) {
final value = getMapValue(json.encode(e), 'g');
return value
.toLowerCase()
.contains((val.value as String).toLowerCase());
}).toList();
}
}
if (excluded.isNotEmpty) {
for (var val in excluded) {
queryRes = queryRes.where((e) {
final value = getMapValue(json.encode(e), 'g');
return !(value
.toLowerCase()
.contains((val.value as String).toLowerCase()));
}).toList();
}
}
}
}
return parseDirectory(json.encode(queryRes));
}
@override
Future<MManga> getDetail(MSource source, String url) async {
final statusList = [
{"Ongoing": 0, "Completed": 1, "Cancelled": 3, "Hiatus": 2}
];
final headers = getHeader(source.baseUrl);
final data = {"url": '${source.baseUrl}/manga/$url', "headers": headers};
final res = await http('GET', json.encode(data));
MManga manga = MManga();
manga.author = xpath(res,
'//li[contains(@class,"list-group-item") and contains(text(),"Author")]/a/text()')
.first;
manga.description = xpath(res,
'//li[contains(@class,"list-group-item") and contains(text(),"Description:")]/div/text()')
.first;
final status = xpath(res,
'//li[contains(@class,"list-group-item") and contains(text(),"Status")]/a/text()')
.first;
manga.status = parseStatus(toStatus(status), statusList);
manga.genre = xpath(res,
'//li[contains(@class,"list-group-item") and contains(text(),"Genre(s)")]/a/text()');
final script =
xpath(res, '//script[contains(text(), "MainFunction")]/text()').first;
final vmChapters =
substringBefore(substringAfter(script, "vm.Chapters = "), ";");
final chapters = json.decode(vmChapters) as List;
List<MChapter> chaptersList = [];
for (var ch in chapters) {
final c = json.encode(ch);
MChapter chapter = MChapter();
String name = getMapValue(c, 'ChapterName');
String indexChapter = getMapValue(c, 'Chapter');
if (name.isEmpty) {
name = '${getMapValue(c, 'Type')} ${chapterImage(indexChapter, true)}';
}
chapter.name = name == "null" ? "" : name;
chapter.url =
'/read-online/${substringAfter(url, "/manga/")}${chapterURLEncode(getMapValue(c, 'Chapter'))}';
chapter.dateUpload = parseDates([getMapValue(c, 'Date')],
source.dateFormat, source.dateFormatLocale)
.first;
chaptersList.add(chapter);
}
manga.chapters = chaptersList;
return manga;
}
@override
Future<List<String>> getPageList(MSource source, String url) async {
final headers = getHeader(source.baseUrl);
List<String> pages = [];
final data = {"url": '${source.baseUrl}$url', "headers": headers};
print(data);
final res = await http('GET', json.encode(data));
final script =
xpath(res, '//script[contains(text(), "MainFunction")]/text()').first;
final chapScript =
substringBefore(substringAfter(script, "vm.CurChapter = "), ";");
final pathName = substringBefore(
substringAfter(script, "vm.CurPathName = \"", ""), "\"");
var directory = getMapValue(chapScript, 'Directory') == 'null'
? ''
: getMapValue(chapScript, 'Directory');
if (directory.length > 0) {
directory += '/';
}
final mangaName =
substringBefore(substringAfter(url, "/read-online/"), "-chapter");
var chNum = chapterImage(getMapValue(chapScript, 'Chapter'), false);
var totalPages = int.parse(getMapValue(chapScript, 'Page'));
for (int page = 1; page <= totalPages; page++) {
String paddedPageNumber = "$page".padLeft(3, '0');
String pageUrl =
'https://$pathName/manga/$mangaName/$directory$chNum-$paddedPageNumber.png';
pages.add(pageUrl);
}
return pages;
}
String directoryFromDocument(String res) {
final script =
xpath(res, '//script[contains(text(), "MainFunction")]/text()').first;
return substringBefore(
substringAfter(script, "vm.Directory = "), "vm.GetIntValue")
.replaceAll(";", " ");
}
MPages parseDirectory(String res) {
List<MManga> mangaList = [];
final datas = json.decode(res) as List;
for (var data in datas) {
final d = json.encode(data);
MManga manga = MManga();
manga.name = getMapValue(d, "s");
manga.imageUrl =
'https://temp.compsci88.com/cover/${getMapValue(d, "i")}.jpg';
manga.link = getMapValue(d, "i");
mangaList.add(manga);
}
return MPages(mangaList, true);
}
String chapterImage(String e, bool cleanString) {
var a = e.substring(1, e.length - 1);
if (cleanString) {
a = regExp(a, r'^0+', "", 0, 0);
}
var b = int.parse(e.substring(e.length - 1));
if (b == 0 && a.isNotEmpty) {
return a;
} else if (b == 0 && a.isEmpty) {
return '0';
} else {
return '$a.$b';
}
}
String toStatus(String status) {
if (status.contains("Ongoing")) {
return "Ongoing";
} else if (status.contains("Complete")) {
return "Complete";
} else if (status.contains("Cancelled")) {
return "Cancelled";
} else if (status.contains("Hiatus")) {
return "Hiatus";
}
return "";
}
String chapterURLEncode(String e) {
var index = ''.toString();
var t = int.parse(e.substring(0, 1));
if (t != 1) {
index = '-index-$t';
}
var dgt = 0;
var inta = int.parse(e);
if (inta < 100100) {
dgt = 4;
} else if (inta < 101000) {
dgt = 3;
} else if (inta < 110000) {
dgt = 2;
} else {
dgt = 1;
}
final n = e.substring(dgt, e.length - 1);
var suffix = ''.toString();
final path = int.parse(e.substring(e.length - 1));
if (path != 0) {
suffix = '.$path';
}
return '-chapter-$n$suffix$index.html';
}
@override
List<dynamic> getFilterList() {
return [
TextFilter("YearFilter", "Years"),
TextFilter("AuthorFilter", "Author"),
SelectFilter("ScanStatusFilter", "Scan Status", 0, [
SelectFilterOption("Any", "Any"),
SelectFilterOption("Complete", "Complete"),
SelectFilterOption("Discontinued", "Discontinued"),
SelectFilterOption("Hiatus", "Hiatus"),
SelectFilterOption("Incomplete", "Incomplete"),
SelectFilterOption("Ongoing", "Ongoing"),
]),
SelectFilter("PublishStatusFilter", "Publish Status", 0, [
SelectFilterOption("Any", "Any"),
SelectFilterOption("Cancelled", "Cancelled"),
SelectFilterOption("Complete", "Complete"),
SelectFilterOption("Discontinued", "Discontinued"),
SelectFilterOption("Hiatus", "Hiatus"),
SelectFilterOption("Incomplete", "Incomplete"),
SelectFilterOption("Ongoing", "Ongoing"),
SelectFilterOption("Unfinished", "Unfinished"),
]),
SelectFilter("TypeFilter", "Type", 0, [
SelectFilterOption("Any", "Any"),
SelectFilterOption("Doujinshi", "Doujinshi"),
SelectFilterOption("Manga", "Manga"),
SelectFilterOption("Manhua", "Manhua"),
SelectFilterOption("Manhwa", "Manhwa"),
SelectFilterOption("OEL", "OEL"),
SelectFilterOption("One-shot", "One-shot"),
]),
SelectFilter("TranslationFilter", "Translation", 0, [
SelectFilterOption("Any", "Any"),
SelectFilterOption("Official Only", "Official Only"),
]),
SortFilter("SortFilter", "Sort", SortState(2, false), [
SelectFilterOption("Alphabetically", "Alphabetically"),
SelectFilterOption("Date updated", "Date updated"),
SelectFilterOption("Popularity", "Popularity"),
]),
GroupFilter("GenresFilter", "Genres", [
TriStateFilter("Action", ""),
TriStateFilter("Adult", ""),
TriStateFilter("Adventure", ""),
TriStateFilter("Comedy", ""),
TriStateFilter("Doujinshi", ""),
TriStateFilter("Drama", ""),
TriStateFilter("Ecchi", ""),
TriStateFilter("Fantasy", ""),
TriStateFilter("Gender Bender", ""),
TriStateFilter("Harem", ""),
TriStateFilter("Hentai", ""),
TriStateFilter("Historical", ""),
TriStateFilter("Horror", ""),
TriStateFilter("Isekai", ""),
TriStateFilter("Josei", ""),
TriStateFilter("Lolicon", ""),
TriStateFilter("Martial Arts", ""),
TriStateFilter("Mature", ""),
TriStateFilter("Mecha", ""),
TriStateFilter("Mystery", ""),
TriStateFilter("Psychological", ""),
TriStateFilter("Romance", ""),
TriStateFilter("School Life", ""),
TriStateFilter("Sci-fi", ""),
TriStateFilter("Seinen", ""),
TriStateFilter("Shotacon", ""),
TriStateFilter("Shoujo", ""),
TriStateFilter("Shoujo Ai", ""),
TriStateFilter("Shounen", ""),
TriStateFilter("Shounen Ai", ""),
TriStateFilter("Slice of Life", ""),
TriStateFilter("Smut", ""),
TriStateFilter("Sports", ""),
TriStateFilter("Supernatural", ""),
TriStateFilter("Tragedy", ""),
TriStateFilter("Yaoi", ""),
TriStateFilter("Yuri", ""),
]),
];
}
String ll(String url) {
if (url.contains("?")) {
return "&";
}
return "?";
}
}
Map<String, String> getHeader(String url) {
final headers = {
'Referer': '$url/',
"User-Agent":
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:71.0) Gecko/20100101 Firefox/77.0"
};
return headers;
}
NepNep main() {
return NepNep();
}

View File

@@ -1,7 +1,7 @@
import '../../../model/source.dart';
import '../../../utils/utils.dart';
const nepnepVersion = "0.0.35";
const nepnepVersion = "0.0.4";
const nepnepSourceCodeUrl =
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/manga/multisrc/nepnep/nepnep-v$nepnepVersion.dart";
const defaultDateFormat = "yyyy-MM-dd HH:mm:ss";

View File

@@ -1,226 +0,0 @@
import 'package:mangayomi/bridge_lib.dart';
import 'dart:convert';
class Batoto extends MProvider {
Batoto();
@override
Future<MPages> getPopular(MSource source, int page) async {
final url =
"${source.baseUrl}/browse?${lang(source.lang)}&sort=views_a&page=$page";
final data = {"url": url};
final res = await http('GET', json.encode(data));
return mangaElementM(res, source);
}
@override
Future<MPages> getLatestUpdates(MSource source, int page) async {
final url =
"${source.baseUrl}/browse?${lang(source.lang)}&sort=update&page=$page";
final data = {"url": url};
final res = await http('GET', json.encode(data));
return mangaElementM(res, source);
}
@override
Future<MPages> search(MSource source, String query, int page) async {
final url = "${source.baseUrl}/search?word=$query&page=$page";
final data = {"url": url};
final res = await http('GET', json.encode(data));
return mangaElementM(res, source);
}
@override
Future<MManga> getDetail(MSource source, String url) async {
final statusList = [
{"Ongoing": 0, "Completed": 1, "Cancelled": 3, "Hiatus": 2}
];
final data = {"url": "${source.baseUrl}$url"};
final res = await http('GET', json.encode(data));
MManga manga = MManga();
final workStatus = xpath(res,
'//*[@class="attr-item"]/b[contains(text(),"Original work")]/following-sibling::span[1]/text()')
.first;
manga.status = parseStatus(workStatus, statusList);
manga.author = xpath(res,
'//*[@class="attr-item"]/b[contains(text(),"Authors")]/following-sibling::span[1]/text()')
.first;
manga.genre = xpath(res,
'//*[@class="attr-item"]/b[contains(text(),"Genres")]/following-sibling::span[1]/text()')
.first
.split(",");
manga.description = xpath(res, '//*[@class="limit-html"]/text()').first;
List<String> chapsElement = querySelectorAll(res,
selector: "div.main div.p-2",
typeElement: 2,
attributes: "",
typeRegExp: 0);
List<String> times = [];
List<String> chapsUrls = [];
List<String> chapsNames = [];
List<String> scanlators = [];
for (var element in chapsElement) {
final urlElement = querySelectorAll(element,
selector: "a.chapt",
typeElement: 2,
attributes: "",
typeRegExp: 0)
.first;
final group = xpath(element, '//*[@class="extra"]/a/text()').first;
final name = xpath(urlElement, '//a/text()').first;
final url = xpath(urlElement, '//a/@href').first;
final time =
xpath(element, '//*[@class="extra"]/i[@class="ps-3"]/text()').first;
times.add(time);
chapsUrls.add(url);
scanlators.add(group);
chapsNames.add(name.replaceAll("\n ", "").replaceAll(" ", ""));
}
var dateUploads =
parseDates(times, source.dateFormat, source.dateFormatLocale);
List<MChapter>? chaptersList = [];
for (var i = 0; i < chapsNames.length; i++) {
MChapter chapter = MChapter();
chapter.name = chapsNames[i];
chapter.url = chapsUrls[i];
chapter.scanlator = scanlators[i];
chapter.dateUpload = dateUploads[i];
chaptersList.add(chapter);
}
manga.chapters = chaptersList;
return manga;
}
@override
Future<List<String>> getPageList(MSource source, String url) async {
final datas = {"url": "${source.baseUrl}$url"};
final res = await http('GET', json.encode(datas));
final script = xpath(res,
'//script[contains(text(), "imgHttpLis") and contains(text(), "batoWord") and contains(text(), "batoPass")]/text()')
.first;
final imgHttpLisString =
substringBefore(substringAfterLast(script, 'const imgHttpLis ='), ';');
var imgHttpLis = json.decode(imgHttpLisString);
final batoWord =
substringBefore(substringAfterLast(script, 'const batoWord ='), ';');
final batoPass =
substringBefore(substringAfterLast(script, 'const batoPass ='), ';');
final evaluatedPass = deobfuscateJsPassword(batoPass);
final imgAccListString =
decryptAESCryptoJS(batoWord.replaceAll('"', ""), evaluatedPass);
var imgAccList = json.decode(imgAccListString);
List<String> pagesUrl = [];
for (int i = 0; i < imgHttpLis.length; i++) {
String imgUrl = imgHttpLis[i];
String imgAcc = imgAccList[i];
pagesUrl.add("$imgUrl?$imgAcc");
}
return pagesUrl;
}
MPages mangaElementM(String res, MSource source) async {
final lang = source.lang.replaceAll("-", "_");
var resB = querySelectorAll(res,
selector: "div#series-list div.col",
typeElement: 2,
attributes: "",
typeRegExp: 0);
List<String> images = [];
List<String> urls = [];
List<String> names = [];
for (var element in resB) {
if (source.lang == "all" ||
source.lang == "en" && element.contains('no-flag') ||
element.contains('data-lang="$lang"')) {
final item = querySelectorAll(element,
selector: "a.item-cover",
typeElement: 2,
attributes: "",
typeRegExp: 0)
.first;
final img = querySelectorAll(item,
selector: "img",
typeElement: 3,
attributes: "src",
typeRegExp: 0)
.first;
final url = querySelectorAll(item,
selector: "a",
typeElement: 3,
attributes: "href",
typeRegExp: 0)
.first;
images.add(img);
urls.add(url);
final title = querySelectorAll(element,
selector: "a.item-title",
typeElement: 0,
attributes: "",
typeRegExp: 0)
.first;
names.add(title);
}
}
List<MManga> mangaList = [];
for (var i = 0; i < urls.length; i++) {
MManga manga = MManga();
manga.name = names[i];
manga.imageUrl = images[i];
manga.link = urls[i];
mangaList.add(manga);
}
return MPages(mangaList, true);
}
String lang(String lang) {
lang = lang.replaceAll("-", "_");
if (lang == "all") {
return "";
}
return "langs=$lang";
}
}
Map<String, String> getMirrorPref() {
return {
"bato.to": "https://bato.to",
"batocomic.com": "https://batocomic.com",
"batocomic.net": "https://batocomic.net",
"batocomic.org": "https://batocomic.org",
"batotoo.com": "https://batotoo.com",
"batotwo.com": "https://batotwo.com",
"battwo.com": "https://battwo.com",
"comiko.net": "https://comiko.net",
"comiko.org": "https://comiko.org",
"mangatoto.com": "https://mangatoto.com",
"mangatoto.net": "https://mangatoto.net",
"mangatoto.org": "https://mangatoto.org",
"readtoto.com": "https://readtoto.com",
"readtoto.net": "https://readtoto.net",
"readtoto.org": "https://readtoto.org",
"dto.to": "https://dto.to",
"hto.to": "https://hto.to",
"mto.to": "https://mto.to",
"wto.to": "https://wto.to",
"xbato.com": "https://xbato.com",
"xbato.net": "https://xbato.net",
"xbato.org": "https://xbato.org",
"zbato.com": "https://zbato.com",
"zbato.net": "https://zbato.net",
"zbato.org": "https://zbato.org",
};
}
Batoto main() {
return Batoto();
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
import '../../../../model/source.dart';
import '../../../../utils/utils.dart';
const batotoVersion = "0.0.35";
const batotoVersion = "0.0.4";
const batotoSourceCodeUrl =
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/manga/src/all/batoto/batoto-v$batotoVersion.dart";

View File

@@ -1,182 +0,0 @@
import 'package:mangayomi/bridge_lib.dart';
import 'dart:convert';
class ComickFun extends MProvider {
ComickFun();
@override
Future<MPages> getPopular(MSource source, int page) async {
final url =
"${source.apiUrl}/v1.0/search?sort=follow&page=$page&tachiyomi=true";
final data = {"url": url, "headers": getHeader(source.baseUrl)};
final res = await http('GET', json.encode(data));
return mangaRes(res);
}
@override
Future<MPages> getLatestUpdates(MSource source, int page) async {
final url =
"${source.apiUrl}/v1.0/search?sort=uploaded&page=$page&tachiyomi=true";
final data = {"url": url, "headers": getHeader(source.baseUrl)};
final res = await http('GET', json.encode(data));
return mangaRes(res);
}
@override
Future<MPages> search(MSource source, String query, int page) async {
final url = "${source.apiUrl}/v1.0/search?q=$query&tachiyomi=true";
final data = {"url": url, "headers": getHeader(source.baseUrl)};
final res = await http('GET', json.encode(data));
return mangaRes(res);
}
@override
Future<MManga> getDetail(MSource source, String url) async {
final statusList = [
{"1": 0, "2": 1, "3": 3, "4": 2}
];
final headers = getHeader(source.baseUrl);
final urll = "${source.apiUrl}${url.replaceAll("#", '')}?tachiyomi=true";
final data = {"url": urll, "headers": headers};
final res = await http('GET', json.encode(data));
MManga manga = MManga();
manga.author = jsonPathToString(res, r'$.authors[*].name', '');
manga.genre = jsonPathToString(res, r'$.genres[*].name', "_.").split("_.");
manga.description = jsonPathToString(res, r'$..desc', '');
manga.status =
parseStatus(jsonPathToString(res, r'$..comic.status', ''), statusList);
final chapUrlReq =
"${source.apiUrl}${url.replaceAll("#", '')}chapters?lang=${source.lang}&tachiyomi=true&page=1";
final dataReq = {"url": chapUrlReq, "headers": headers};
final request = await http('GET', json.encode(dataReq));
var total = jsonPathToString(request, r'$.total', '');
final chapterLimit = int.parse(total);
final newChapUrlReq =
"${source.apiUrl}${url.replaceAll("#", '')}chapters?limit=$chapterLimit&lang=${source.lang}&tachiyomi=true&page=1";
final newDataReq = {"url": newChapUrlReq, "headers": headers};
final newRequest = await http('GET', json.encode(newDataReq));
final chapsUrls =
jsonPathToString(newRequest, r'$.chapters[*].hid', "_.").split("_.");
final chapDate =
jsonPathToString(newRequest, r'$.chapters[*].created_at', "_.")
.split("_.");
final chaptersVolumes =
jsonPathToString(newRequest, r'$.chapters[*].vol', "_.").split("_.");
final chaptersScanlators =
jsonPathToString(newRequest, r'$.chapters[*].group_name', "_.")
.split("_.");
final chapsNames =
jsonPathToString(newRequest, r'$.chapters[*].title', "_.").split("_.");
final chaptersChaps =
jsonPathToString(newRequest, r'$.chapters[*].chap', "_.").split("_.");
var dateUploads =
parseDates(chapDate, source.dateFormat, source.dateFormatLocale);
List<MChapter>? chaptersList = [];
for (var i = 0; i < chapsNames.length; i++) {
String title = "";
String scanlator = "";
if (chaptersChaps.isNotEmpty && chaptersVolumes.isNotEmpty) {
title = beautifyChapterName(
chaptersVolumes[i], chaptersChaps[i], chapsNames[i], source.lang);
} else {
title = chapsNames[i];
}
if (chaptersScanlators.isNotEmpty) {
scanlator = chaptersScanlators[i]
.toString()
.replaceAll(']', "")
.replaceAll("[", "");
}
MChapter chapter = MChapter();
chapter.name = title;
chapter.url = chapsUrls[i];
chapter.scanlator = scanlator == "null" ? "" : scanlator;
chapter.dateUpload = dateUploads[i];
chaptersList.add(chapter);
}
manga.chapters = chaptersList;
return manga;
}
@override
Future<List<String>> getPageList(MSource source, String url) async {
final urll = "${source.apiUrl}/chapter/$url?tachiyomi=true";
final data = {"url": urll, "headers": getHeader(url)};
final res = await http('GET', json.encode(data));
return jsonPathToString(res, r'$.chapter.images[*].url', '_.').split('_.');
}
MPages mangaRes(String res) async {
final names = jsonPathToList(res, r'$.title', 0);
List<String> ids = jsonPathToList(res, r'$.hid', 0);
List<String> mangaUrls = [];
for (var id in ids) {
mangaUrls.add("/comic/$id/#");
}
final urls = mangaUrls;
final images = jsonPathToList(res, r'$.cover_url', 0);
List<MManga> mangaList = [];
for (var i = 0; i < urls.length; i++) {
MManga manga = MManga();
manga.name = names[i];
manga.imageUrl = images[i];
manga.link = urls[i];
mangaList.add(manga);
}
return MPages(mangaList, true);
}
String beautifyChapterName(
String vol, String chap, String title, String lang) {
String result = "";
if (vol != "null" && vol.isNotEmpty) {
if (chap != "null" && chap.isEmpty) {
result += "Volume $vol ";
} else {
result += "Vol. $vol ";
}
}
if (chap != "null" && chap.isNotEmpty) {
if (vol != "null" && vol.isEmpty) {
if (lang != "null" && lang == "fr") {
result += "Chapitre $chap";
} else {
result += "Chapter $chap";
}
} else {
result += "Ch. $chap ";
}
}
if (title != "null" && title.isNotEmpty) {
if (chap != "null" && chap.isEmpty) {
result += title;
} else {
result += " : $title";
}
}
return result;
}
}
Map<String, String> getHeader(String url) {
final headers = {
"Referer": "$url/",
'User-Agent':
"Tachiyomi Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:110.0) Gecko/20100101 Firefox/110.0"
};
return headers;
}
ComickFun main() {
return ComickFun();
}

View File

@@ -0,0 +1,648 @@
import 'package:mangayomi/bridge_lib.dart';
import 'dart:convert';
class ComickFun extends MProvider {
ComickFun();
@override
Future<MPages> getPopular(MSource source, int page) async {
final url =
"${source.apiUrl}/v1.0/search?sort=follow&page=$page&tachiyomi=true";
final data = {"url": url, "headers": getHeader(source.baseUrl)};
final res = await http('GET', json.encode(data));
return mangaRes(res);
}
@override
Future<MPages> getLatestUpdates(MSource source, int page) async {
final url =
"${source.apiUrl}/v1.0/search?sort=uploaded&page=$page&tachiyomi=true";
final data = {"url": url, "headers": getHeader(source.baseUrl)};
final res = await http('GET', json.encode(data));
return mangaRes(res);
}
@override
Future<MPages> search(
MSource source, String query, int page, FilterList filterList) async {
final filters = filterList.filters;
String url = "";
if (query.isNotEmpty) {
url = "${source.apiUrl}/v1.0/search?q=$query&tachiyomi=true";
} else {
url = "${source.apiUrl}/v1.0/search";
for (var filter in filters) {
if (filter.type == "CompletedFilter") {
if (filter.state) {
url += "${ll(url)}completed=true";
}
} else if (filter.type == "GenreFilter") {
final included = (filter.state as List)
.where((e) => e.state == 1 ? true : false)
.toList();
final excluded = (filter.state as List)
.where((e) => e.state == 2 ? true : false)
.toList();
if (included.isNotEmpty) {
for (var val in included) {
url += "${ll(url)}genres=${val.value}";
}
}
if (excluded.isNotEmpty) {
for (var val in excluded) {
url += "${ll(url)}excludes=${val.value}";
}
}
} else if (filter.type == "DemographicFilter") {
final included = (filter.state as List)
.where((e) => e.state == 1 ? true : false)
.toList();
if (included.isNotEmpty) {
for (var val in included) {
url += "${ll(url)}demographic=${val.value}";
}
}
} else if (filter.type == "TypeFilter") {
final country = (filter.state as List).where((e) => e.state).toList();
if (country.isNotEmpty) {
for (var coun in country) {
url += "${ll(url)}country=${coun.value}";
}
}
} else if (filter.type == "SortFilter") {
url += "${ll(url)}sort=${filter.values[filter.state].value}";
} else if (filter.type == "StatusFilter") {
url += "${ll(url)}status=${filter.values[filter.state].value}";
} else if (filter.type == "CreatedAtFilter") {
if (filter.state > 0) {
url += "${ll(url)}time=${filter.values[filter.state].value}";
}
} else if (filter.type == "MinimumFilter") {
if (filter.state.isNotEmpty) {
url += "${ll(url)}minimum=${filter.state}";
}
} else if (filter.type == "FromYearFilter") {
if (filter.state.isNotEmpty) {
url += "${ll(url)}from=${filter.state}";
}
} else if (filter.type == "ToYearFilter") {
if (filter.state.isNotEmpty) {
url += "${ll(url)}to=${filter.state}";
}
} else if (filter.type == "TagFilter") {
if (filter.state.isNotEmpty) {
final tags = (filter.state as String).split(",");
for (var tag in tags) {
url += "${ll(url)}tags=$tag";
}
}
}
}
url += "${ll(url)}page=$page&tachiyomi=true";
}
final data = {"url": url, "headers": getHeader(source.baseUrl)};
final res = await http('GET', json.encode(data));
return mangaRes(res);
}
@override
Future<MManga> getDetail(MSource source, String url) async {
final statusList = [
{"1": 0, "2": 1, "3": 3, "4": 2}
];
final headers = getHeader(source.baseUrl);
final urll = "${source.apiUrl}${url.replaceAll("#", '')}?tachiyomi=true";
final data = {"url": urll, "headers": headers};
final res = await http('GET', json.encode(data));
MManga manga = MManga();
manga.author = jsonPathToString(res, r'$.authors[*].name', '');
manga.genre = jsonPathToString(res, r'$.genres[*].name', "_.").split("_.");
manga.description = jsonPathToString(res, r'$..desc', '');
manga.status =
parseStatus(jsonPathToString(res, r'$..comic.status', ''), statusList);
final chapUrlReq =
"${source.apiUrl}${url.replaceAll("#", '')}chapters?lang=${source.lang}&tachiyomi=true&page=1";
final dataReq = {"url": chapUrlReq, "headers": headers};
final request = await http('GET', json.encode(dataReq));
var total = jsonPathToString(request, r'$.total', '');
final chapterLimit = int.parse(total);
final newChapUrlReq =
"${source.apiUrl}${url.replaceAll("#", '')}chapters?limit=$chapterLimit&lang=${source.lang}&tachiyomi=true&page=1";
final newDataReq = {"url": newChapUrlReq, "headers": headers};
final newRequest = await http('GET', json.encode(newDataReq));
final chapsUrls =
jsonPathToString(newRequest, r'$.chapters[*].hid', "_.").split("_.");
final chapDate =
jsonPathToString(newRequest, r'$.chapters[*].created_at', "_.")
.split("_.");
final chaptersVolumes =
jsonPathToString(newRequest, r'$.chapters[*].vol', "_.").split("_.");
final chaptersScanlators =
jsonPathToString(newRequest, r'$.chapters[*].group_name', "_.")
.split("_.");
final chapsNames =
jsonPathToString(newRequest, r'$.chapters[*].title', "_.").split("_.");
final chaptersChaps =
jsonPathToString(newRequest, r'$.chapters[*].chap', "_.").split("_.");
var dateUploads =
parseDates(chapDate, source.dateFormat, source.dateFormatLocale);
List<MChapter>? chaptersList = [];
for (var i = 0; i < chapsNames.length; i++) {
String title = "";
String scanlator = "";
if (chaptersChaps.isNotEmpty && chaptersVolumes.isNotEmpty) {
title = beautifyChapterName(
chaptersVolumes[i], chaptersChaps[i], chapsNames[i], source.lang);
} else {
title = chapsNames[i];
}
if (chaptersScanlators.isNotEmpty) {
scanlator = chaptersScanlators[i]
.toString()
.replaceAll(']', "")
.replaceAll("[", "");
}
MChapter chapter = MChapter();
chapter.name = title;
chapter.url = chapsUrls[i];
chapter.scanlator = scanlator == "null" ? "" : scanlator;
chapter.dateUpload = dateUploads[i];
chaptersList.add(chapter);
}
manga.chapters = chaptersList;
return manga;
}
@override
Future<List<String>> getPageList(MSource source, String url) async {
final urll = "${source.apiUrl}/chapter/$url?tachiyomi=true";
final data = {"url": urll, "headers": getHeader(url)};
final res = await http('GET', json.encode(data));
return jsonPathToString(res, r'$.chapter.images[*].url', '_.').split('_.');
}
MPages mangaRes(String res) async {
final names = jsonPathToList(res, r'$.title', 0);
List<String> ids = jsonPathToList(res, r'$.hid', 0);
List<String> mangaUrls = [];
for (var id in ids) {
mangaUrls.add("/comic/$id/#");
}
final urls = mangaUrls;
final images = jsonPathToList(res, r'$.cover_url', 0);
List<MManga> mangaList = [];
for (var i = 0; i < urls.length; i++) {
MManga manga = MManga();
manga.name = names[i];
manga.imageUrl = images[i];
manga.link = urls[i];
mangaList.add(manga);
}
return MPages(mangaList, true);
}
String ll(String url) {
if (url.contains("?")) {
return "&";
}
return "?";
}
@override
List<dynamic> getFilterList() {
return [
HeaderFilter("The filter is ignored when using text search."),
GroupFilter("GenreFilter", "Genre", [
{
"type": "TriState",
"filter": {"name": "4-Koma", "value": "4-koma"}
},
{
"type": "TriState",
"filter": {"name": "Action", "value": "action"}
},
{
"type": "TriState",
"filter": {"name": "Adaptation", "value": "adaptation"}
},
{
"type": "TriState",
"filter": {"name": "Adult", "value": "adult"}
},
{
"type": "TriState",
"filter": {"name": "Adventure", "value": "adventure"}
},
{
"type": "TriState",
"filter": {"name": "Aliens", "value": "aliens"}
},
{
"type": "TriState",
"filter": {"name": "Animals", "value": "animals"}
},
{
"type": "TriState",
"filter": {"name": "Anthology", "value": "anthology"}
},
{
"type": "TriState",
"filter": {"name": "Award Winning", "value": "award-winning"}
},
{
"type": "TriState",
"filter": {"name": "Comedy", "value": "comedy"}
},
{
"type": "TriState",
"filter": {"name": "Cooking", "value": "cooking"}
},
{
"type": "TriState",
"filter": {"name": "Crime", "value": "crime"}
},
{
"type": "TriState",
"filter": {"name": "Crossdressing", "value": "crossdressing"}
},
{
"type": "TriState",
"filter": {"name": "Delinquents", "value": "delinquents"}
},
{
"type": "TriState",
"filter": {"name": "Demons", "value": "demons"}
},
{
"type": "TriState",
"filter": {"name": "Doujinshi", "value": "doujinshi"}
},
{
"type": "TriState",
"filter": {"name": "Drama", "value": "drama"}
},
{
"type": "TriState",
"filter": {"name": "Ecchi", "value": "ecchi"}
},
{
"type": "TriState",
"filter": {"name": "Fan Colored", "value": "fan-colored"}
},
{
"type": "TriState",
"filter": {"name": "Fantasy", "value": "fantasy"}
},
{
"type": "TriState",
"filter": {"name": "Full Color", "value": "full-color"}
},
{
"type": "TriState",
"filter": {"name": "Gender Bender", "value": "gender-bender"}
},
{
"type": "TriState",
"filter": {"name": "Genderswap", "value": "genderswap"}
},
{
"type": "TriState",
"filter": {"name": "Ghosts", "value": "ghosts"}
},
{
"type": "TriState",
"filter": {"name": "Gore", "value": "gore"}
},
{
"type": "TriState",
"filter": {"name": "Gyaru", "value": "gyaru"}
},
{
"type": "TriState",
"filter": {"name": "Harem", "value": "harem"}
},
{
"type": "TriState",
"filter": {"name": "Historical", "value": "historical"}
},
{
"type": "TriState",
"filter": {"name": "Horror", "value": "horror"}
},
{
"type": "TriState",
"filter": {"name": "Incest", "value": "incest"}
},
{
"type": "TriState",
"filter": {"name": "Isekai", "value": "isekai"}
},
{
"type": "TriState",
"filter": {"name": "Loli", "value": "loli"}
},
{
"type": "TriState",
"filter": {"name": "Long Strip", "value": "long-strip"}
},
{
"type": "TriState",
"filter": {"name": "Mafia", "value": "mafia"}
},
{
"type": "TriState",
"filter": {"name": "Magic", "value": "magic"}
},
{
"type": "TriState",
"filter": {"name": "Magical Girls", "value": "magical-girls"}
},
{
"type": "TriState",
"filter": {"name": "Martial Arts", "value": "martial-arts"}
},
{
"type": "TriState",
"filter": {"name": "Mature", "value": "mature"}
},
{
"type": "TriState",
"filter": {"name": "Mecha", "value": "mecha"}
},
{
"type": "TriState",
"filter": {"name": "Medical", "value": "medical"}
},
{
"type": "TriState",
"filter": {"name": "Military", "value": "military"}
},
{
"type": "TriState",
"filter": {"name": "Monster Girls", "value": "monster-girls"}
},
{
"type": "TriState",
"filter": {"name": "Monsters", "value": "monsters"}
},
{
"type": "TriState",
"filter": {"name": "Music", "value": "music"}
},
{
"type": "TriState",
"filter": {"name": "Mystery", "value": "mystery"}
},
{
"type": "TriState",
"filter": {"name": "Ninja", "value": "ninja"}
},
{
"type": "TriState",
"filter": {"name": "Office Workers", "value": "office-workers"}
},
{
"type": "TriState",
"filter": {"name": "Official Colored", "value": "official-colored"}
},
{
"type": "TriState",
"filter": {"name": "Oneshot", "value": "oneshot"}
},
{
"type": "TriState",
"filter": {"name": "Philosophical", "value": "philosophical"}
},
{
"type": "TriState",
"filter": {"name": "Police", "value": "police"}
},
{
"type": "TriState",
"filter": {"name": "Post-Apocalyptic", "value": "post-apocalyptic"}
},
{
"type": "TriState",
"filter": {"name": "Psychological", "value": "psychological"}
},
{
"type": "TriState",
"filter": {"name": "Reincarnation", "value": "reincarnation"}
},
{
"type": "TriState",
"filter": {"name": "Reverse Harem", "value": "reverse-harem"}
},
{
"type": "TriState",
"filter": {"name": "Romance", "value": "romance"}
},
{
"type": "TriState",
"filter": {"name": "Samurai", "value": "samurai"}
},
{
"type": "TriState",
"filter": {"name": "School Life", "value": "school-life"}
},
{
"type": "TriState",
"filter": {"name": "Sci-Fi", "value": "sci-fi"}
},
{
"type": "TriState",
"filter": {"name": "Sexual Violence", "value": "sexual-violence"}
},
{
"type": "TriState",
"filter": {"name": "Shota", "value": "shota"}
},
{
"type": "TriState",
"filter": {"name": "Shoujo Ai", "value": "shoujo-ai"}
},
{
"type": "TriState",
"filter": {"name": "Shounen Ai", "value": "shounen-ai"}
},
{
"type": "TriState",
"filter": {"name": "Slice of Life", "value": "slice-of-life"}
},
{
"type": "TriState",
"filter": {"name": "Smut", "value": "smut"}
},
{
"type": "TriState",
"filter": {"name": "Sports", "value": "sports"}
},
{
"type": "TriState",
"filter": {"name": "Superhero", "value": "superhero"}
},
{
"type": "TriState",
"filter": {"name": "Supernatural", "value": "supernatural"}
},
{
"type": "TriState",
"filter": {"name": "Survival", "value": "survival"}
},
{
"type": "TriState",
"filter": {"name": "Thriller", "value": "thriller"}
},
{
"type": "TriState",
"filter": {"name": "Time Travel", "value": "time-travel"}
},
{
"type": "TriState",
"filter": {"name": "Traditional Games", "value": "traditional-games"}
},
{
"type": "TriState",
"filter": {"name": "Tragedy", "value": "tragedy"}
},
{
"type": "TriState",
"filter": {"name": "User Created", "value": "user-created"}
},
{
"type": "TriState",
"filter": {"name": "Vampires", "value": "vampires"}
},
{
"type": "TriState",
"filter": {"name": "Video Games", "value": "video-games"}
},
{
"type": "TriState",
"filter": {"name": "Villainess", "value": "villainess"}
},
{
"type": "TriState",
"filter": {"name": "Virtual Reality", "value": "virtual-reality"}
},
{
"type": "TriState",
"filter": {"name": "Web Comic", "value": "web-comic"}
},
{
"type": "TriState",
"filter": {"name": "Wuxia", "value": "wuxia"}
},
{
"type": "TriState",
"filter": {"name": "Yaoi", "value": "yaoi"}
},
{
"type": "TriState",
"filter": {"name": "Yuri", "value": "yuri"}
},
{
"type": "TriState",
"filter": {"name": "Zombies", "value": "zombies"}
}
]),
GroupFilter("DemographicFilter", "Demographic", [
TriStateFilter("Shounen", "1"),
TriStateFilter("Shoujo", "2"),
TriStateFilter("Seinen", "3"),
TriStateFilter("Josei", "4"),
]),
GroupFilter("TypeFilter", "Type", [
CheckBoxFilter("Manga", "jp"),
CheckBoxFilter("Manhwa", "kr"),
CheckBoxFilter("Manhua", "cn"),
]),
SelectFilter("SortFilter", "Sort", 0, [
SelectFilterOption("Most popular", "follow"),
SelectFilterOption("Most follows", "user_follow_count"),
SelectFilterOption("Most views", "view"),
SelectFilterOption("High rating", "rating"),
SelectFilterOption("Last updated", "uploaded"),
SelectFilterOption("Newest", "created_at"),
]),
SelectFilter("StatusFilter", "Status", 0, [
SelectFilterOption("All", "0"),
SelectFilterOption("Ongoing", "1"),
SelectFilterOption("Completed", "2"),
SelectFilterOption("Cancelled", "3"),
SelectFilterOption("Hiatus", "4"),
]),
CheckBoxFilter("Completely Scanlated?", "", "CompletedFilter"),
SelectFilter("CreatedAtFilter", "Created at", 0, [
SelectFilterOption("", ""),
SelectFilterOption("3 days", "3"),
SelectFilterOption("7 days", "7"),
SelectFilterOption("30 days", "30"),
SelectFilterOption("3 months", "90"),
SelectFilterOption("6 months", "180"),
SelectFilterOption("1 year", "365"),
]),
TextFilter("MinimumFilter", "Minimum Chapters"),
HeaderFilter("From Year, ex: 2010"),
TextFilter("FromYearFilter", "From"),
HeaderFilter("To Year, ex: 2021"),
TextFilter("ToYearFilter", "To"),
HeaderFilter("Separate tags with commas"),
TextFilter("TagFilter", "Tags")
];
}
String beautifyChapterName(
String vol, String chap, String title, String lang) {
String result = "";
if (vol != "null" && vol.isNotEmpty) {
if (chap != "null" && chap.isEmpty) {
result += "Volume $vol ";
} else {
result += "Vol. $vol ";
}
}
if (chap != "null" && chap.isNotEmpty) {
if (vol != "null" && vol.isEmpty) {
if (lang != "null" && lang == "fr") {
result += "Chapitre $chap";
} else {
result += "Chapter $chap";
}
} else {
result += "Ch. $chap ";
}
}
if (title != "null" && title.isNotEmpty) {
if (chap != "null" && chap.isEmpty) {
result += title;
} else {
result += " : $title";
}
}
return result;
}
}
Map<String, String> getHeader(String url) {
final headers = {
"Referer": "$url/",
'User-Agent':
"Tachiyomi Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:110.0) Gecko/20100101 Firefox/110.0"
};
return headers;
}
ComickFun main() {
return ComickFun();
}

View File

@@ -1,7 +1,7 @@
import '../../../../model/source.dart';
import '../../../../utils/utils.dart';
const comickVersion = "0.0.35";
const comickVersion = "0.0.4";
const comickSourceCodeUrl =
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/manga/src/all/comick/comick-v$comickVersion.dart";

View File

@@ -1,273 +0,0 @@
import 'package:mangayomi/bridge_lib.dart';
import 'dart:convert';
class MangaDex extends MProvider {
MangaDex();
@override
Future<MPages> getPopular(MSource source, int page) async {
page = (20 * (page - 1));
final url =
"https://api.mangadex.org/manga?limit=20&offset=$page&availableTranslatedLanguage[]=en&includes[]=cover_art${getMDXContentRating()}&order[followedCount]=desc";
final datas = {"url": url};
final res = await http('GET', json.encode(datas));
return mangaRes(res, source);
}
@override
Future<MPages> getLatestUpdates(MSource source, int page) async {
page = (20 * (page - 1));
final urll =
"https://api.mangadex.org/chapter?limit=20&offset=$page&translatedLanguage[]=${source.lang}&includeFutureUpdates=0&order[publishAt]=desc&includeFuturePublishAt=0&includeEmptyPages=0";
final datas = {"url": urll};
final ress = await http('GET', json.encode(datas));
final mangaIds =
jsonPathToString(ress, r'$.data[*].relationships[*].id', '.--')
.split('.--');
String mangaIdss = "".toString();
for (var id in mangaIds) {
mangaIdss += "&ids[]=$id";
}
final newUrl =
"https://api.mangadex.org/manga?includes[]=cover_art&limit=${mangaIds.length}${getMDXContentRating()}$mangaIdss";
final res = await http('GET', json.encode({"url": newUrl}));
return mangaRes(res, source);
}
@override
Future<MPages> search(MSource source, String query, int page) async {
final url =
"https://api.mangadex.org/manga?includes[]=cover_art&offset=0&limit=20&title=$query${getMDXContentRating()}&order[followedCount]=desc&availableTranslatedLanguage[]=${source.lang}";
final res = await http('GET', json.encode({"url": url}));
return mangaRes(res, source);
}
@override
Future<MManga> getDetail(MSource source, String url) async {
final statusList = [
{"ongoing": 0, "completed": 1, "hiatus": 2, "cancelled": 3}
];
final urll =
"https://api.mangadex.org$url?includes[]=cover_art&includes[]=author&includes[]=artist";
final res = await http('GET', json.encode({"url": urll}));
MManga manga = MManga();
manga.author = jsonPathToString(
res, r'$..data.relationships[*].attributes.name', ', ');
String expressionDescriptionA = r'$..data.attributes.description.en';
String expressionDescription = regExp(r'$..data.attributes.description[a]',
r'\[a\]', ".${source.lang}", 0, 1);
String description = jsonPathToString(res, expressionDescription, '');
if (description.isEmpty) {
description = jsonPathToString(res, expressionDescriptionA, '');
}
manga.description = description;
List<String> genres = [];
genres = jsonPathToString(
res, r'$..data.attributes.tags[*].attributes.name.en', '.-')
.split('.-');
String contentRating =
jsonPathToString(res, r'$..data.attributes.contentRating', '');
if (contentRating != "safe") {
genres.add(contentRating);
}
String publicationDemographic =
jsonPathToString(res, r'$..data.attributes.publicationDemographic', '');
if (publicationDemographic == "null") {
} else {
genres.add(publicationDemographic);
}
manga.genre = genres;
String statusRes = jsonPathToString(res, r'$..data.attributes.status', '');
manga.status = parseStatus(statusRes, statusList);
final mangaId = url.split('/').last;
final paginatedChapterList =
await paginatedChapterListRequest(mangaId, 0, source.lang);
final chapterList =
jsonPathToString(paginatedChapterList, r'$.data[*]', '_.').split('_.');
int limit =
int.parse(jsonPathToString(paginatedChapterList, r'$.limit', ''));
int offset =
int.parse(jsonPathToString(paginatedChapterList, r'$.offset', ''));
int total =
int.parse(jsonPathToString(paginatedChapterList, r'$.total', ''));
List<MChapter> chapterListA = [];
final list =
getChapters(int.parse("${chapterList.length}"), paginatedChapterList);
chapterListA.addAll(list);
var hasMoreResults = (limit + offset) < total;
while (hasMoreResults) {
offset += limit;
var newRequest =
await paginatedChapterListRequest(mangaId, offset, source.lang);
int total = int.parse(jsonPathToString(newRequest, r'$.total', ''));
final chapterList =
jsonPathToString(paginatedChapterList, r'$.data[*]', '_.')
.split('_.');
final list = getChapters(int.parse("${chapterList.length}"), newRequest);
chapterListA.addAll(list);
hasMoreResults = (limit + offset) < total;
}
manga.chapters = chapterListA;
return manga;
}
@override
Future<List<String>> getPageList(MSource source, String url) async {
final urll = "https://api.mangadex.org/at-home/server/$url";
final res = await http('GET', json.encode({"url": urll}));
// final dataRes = json.decode(res);
final host = getMapValue(res, "baseUrl");
final chapter = getMapValue(res, "chapter", encode: true);
final hash = getMapValue(chapter, "hash");
final chapterDatas =
json.decode(getMapValue(chapter, "data", encode: true)) as List;
return chapterDatas.map((e) => "$host/data/$hash/$e").toList();
}
MPages mangaRes(String res, MSource source) {
final datasRes = getMapValue(res, "data", encode: true);
final resJson = json.decode(datasRes) as List;
List<MManga> mangaList = [];
for (var e in resJson) {
MManga manga = MManga();
manga.name = findTitle(json.encode(e), source.lang);
manga.imageUrl = getCover(json.encode(e));
manga.link = "/manga/${getMapValue(json.encode(e), "id")}";
mangaList.add(manga);
}
return MPages(mangaList, true);
}
List<MChapter> getChapters(int length, String paginatedChapterListA) {
List<MChapter> chaptersList = [];
String paginatedChapterList = paginatedChapterListA.toString();
final dataList = jsonPathToList(paginatedChapterList, r'$.data[*]', 0);
for (var res in dataList) {
String scan = "".toString();
final groups = jsonPathToList(res,
r'$.relationships[?@.id!="00e03853-1b96-4f41-9542-c71b8692033b"]', 0);
String chapName = "".toString();
for (var element in groups) {
final data = getMapValue(element, "attributes", encode: true);
if (data.isNotEmpty) {
final name = getMapValue(data, "name");
scan += "$name".toString();
final username = getMapValue(data, "username");
if (username.isNotEmpty) {
if (scan.isEmpty) {
scan += "Uploaded by $username".toString();
}
}
}
}
if (scan.isEmpty) {
scan = "No Group".toString();
}
final dataRes = getMapValue(res, "attributes", encode: true);
if (dataRes.isNotEmpty) {
final data = getMapValue(res, "attributes", encode: true);
final volume = getMapValue(data, "volume");
if (volume.isNotEmpty) {
if (volume != "null") {
chapName = "Vol.$volume ".toString();
}
}
final chapter = getMapValue(data, "chapter");
if (chapter.isNotEmpty) {
if (chapter != "null") {
chapName += "Ch.$chapter ".toString();
}
}
final title = getMapValue(data, "title");
if (title.isNotEmpty) {
if (title != "null") {
if (chapName.isNotEmpty) {
chapName += "- ".toString();
}
chapName += "$title".toString();
}
}
if (chapName.isEmpty) {
chapName += "Oneshot".toString();
}
final date = getMapValue(data, "publishAt");
final id = getMapValue(res, "id");
MChapter chapterr = MChapter();
chapterr.name = chapName;
chapterr.url = id;
chapterr.scanlator = scan;
chapterr.dateUpload =
parseDates([date], "yyyy-MM-dd'T'HH:mm:ss+SSS", "en_US").first;
chaptersList.add(chapterr);
}
}
return chaptersList;
}
Future<String> paginatedChapterListRequest(
String mangaId, int offset, String lang) async {
final url =
'https://api.mangadex.org/manga/$mangaId/feed?limit=500&offset=$offset&includes[]=user&includes[]=scanlation_group&order[volume]=desc&order[chapter]=desc&translatedLanguage[]=$lang&includeFuturePublishAt=0&includeEmptyPages=0${getMDXContentRating()}';
final res = await http('GET', json.encode({"url": url}));
return res;
}
String getMDXContentRating() {
String ctnRating =
"&contentRating[]=suggestive&contentRating[]=safe&contentRating[]=erotica&contentRating[]=pornographic";
return ctnRating;
}
String findTitle(String dataRes, String lang) {
final attributes = getMapValue(dataRes, "attributes", encode: true);
final altTitlesJ =
json.decode(getMapValue(attributes, "altTitles", encode: true));
final titleJ = getMapValue(attributes, "title", encode: true);
final title = getMapValue(titleJ, "en");
if (title.isEmpty) {
for (var r in altTitlesJ) {
final altTitle = getMapValue(json.encode(r), "en");
if (altTitle.isNotEmpty) {
return altTitle;
}
}
}
return title;
}
String getCover(String dataRes) {
final relationships = json
.decode(getMapValue(dataRes, "relationships", encode: true)) as List;
String coverFileName = "".toString();
for (var a in relationships) {
final relationType = getMapValue(json.encode(a), "type");
if (relationType == "cover_art") {
if (coverFileName.isEmpty) {
final attributes =
getMapValue(json.encode(a), "attributes", encode: true);
coverFileName =
"https://uploads.mangadex.org/covers/${getMapValue(dataRes, "id")}/${getMapValue(attributes, "fileName")}";
}
}
}
return coverFileName;
}
}
MangaDex main() {
return MangaDex();
}

View File

@@ -0,0 +1,523 @@
import 'package:mangayomi/bridge_lib.dart';
import 'dart:convert';
class MangaDex extends MProvider {
MangaDex();
@override
Future<MPages> getPopular(MSource source, int page) async {
page = (20 * (page - 1));
final url =
"https://api.mangadex.org/manga?limit=20&offset=$page&availableTranslatedLanguage[]=${source.lang}&includes[]=cover_art${getMDXContentRating()}&order[followedCount]=desc";
final datas = {"url": url};
final res = await http('GET', json.encode(datas));
return mangaRes(res, source);
}
@override
Future<MPages> getLatestUpdates(MSource source, int page) async {
page = (20 * (page - 1));
final urll =
"https://api.mangadex.org/chapter?limit=20&offset=$page&translatedLanguage[]=${source.lang}&includeFutureUpdates=0&order[publishAt]=desc&includeFuturePublishAt=0&includeEmptyPages=0";
final datas = {"url": urll};
final ress = await http('GET', json.encode(datas));
final mangaIds =
jsonPathToString(ress, r'$.data[*].relationships[*].id', '.--')
.split('.--');
String mangaIdss = "".toString();
for (var id in mangaIds) {
mangaIdss += "&ids[]=$id";
}
final newUrl =
"https://api.mangadex.org/manga?includes[]=cover_art&limit=${mangaIds.length}${getMDXContentRating()}$mangaIdss";
final res = await http('GET', json.encode({"url": newUrl}));
return mangaRes(res, source);
}
@override
Future<MPages> search(
MSource source, String query, int page, FilterList filterList) async {
page = (20 * (page - 1));
final filters = filterList.filters;
String url = "";
url =
"https://api.mangadex.org/manga?includes[]=cover_art&offset=$page&limit=20&title=$query";
for (var filter in filters) {
if (filter.type == "HasAvailableChaptersFilter") {
if (filter.state) {
url += "${ll(url)}hasAvailableChapters=true";
url += "${ll(url)}availableTranslatedLanguage[]=${source.lang}";
}
} else if (filter.type == "OriginalLanguageList") {
final langs = (filter.state as List).where((e) => e.state).toList();
if (langs.isNotEmpty) {
for (var lang in langs) {
url += "${ll(url)}${lang.value}";
}
}
} else if (filter.type == "ContentRatingList") {
final ctns = (filter.state as List).where((e) => e.state).toList();
if (ctns.isNotEmpty) {
for (var ctn in ctns) {
url += "${ll(url)}${ctn.value}";
}
}
} else if (filter.type == "DemographicList") {
final demogr = (filter.state as List).where((e) => e.state).toList();
if (demogr.isNotEmpty) {
for (var demog in demogr) {
url += "${ll(url)}${demog.value}";
}
}
} else if (filter.type == "StatusList") {
final statusL = (filter.state as List).where((e) => e.state).toList();
if (statusL.isNotEmpty) {
for (var status in statusL) {
url += "${ll(url)}${status.value}";
}
}
} else if (filter.type == "SortFilter") {
final value = filter.state.ascending ? "asc" : "desc";
url +=
"${ll(url)}order[${filter.values[filter.state.index].value}]=$value";
} else if (filter.type == "TagsFilter") {
for (var tag in filter.state) {
url += "${ll(url)}${tag.values[tag.state].value}";
}
} else if (filter.type == "FormatFilter") {
final included = (filter.state as List)
.where((e) => e.state == 1 ? true : false)
.toList();
final excluded = (filter.state as List)
.where((e) => e.state == 2 ? true : false)
.toList();
if (included.isNotEmpty) {
for (var val in included) {
url += "${ll(url)}includedTags[]=${val.value}";
}
}
if (excluded.isNotEmpty) {
for (var val in excluded) {
url += "${ll(url)}excludedTags[]=${val.value}";
}
}
} else if (filter.type == "GenreFilter") {
final included = (filter.state as List)
.where((e) => e.state == 1 ? true : false)
.toList();
final excluded = (filter.state as List)
.where((e) => e.state == 2 ? true : false)
.toList();
if (included.isNotEmpty) {
for (var val in included) {
url += "${ll(url)}includedTags[]=${val.value}";
}
}
if (excluded.isNotEmpty) {
for (var val in excluded) {
url += "${ll(url)}excludedTags[]=${val.value}";
}
}
} else if (filter.type == "ThemeFilter") {
final included = (filter.state as List)
.where((e) => e.state == 1 ? true : false)
.toList();
final excluded = (filter.state as List)
.where((e) => e.state == 2 ? true : false)
.toList();
if (included.isNotEmpty) {
for (var val in included) {
url += "${ll(url)}includedTags[]=${val.value}";
}
}
if (excluded.isNotEmpty) {
for (var val in excluded) {
url += "${ll(url)}excludedTags[]=${val.value}";
}
}
}
}
final res = await http('GET', json.encode({"url": url}));
return mangaRes(res, source);
}
@override
Future<MManga> getDetail(MSource source, String url) async {
final statusList = [
{"ongoing": 0, "completed": 1, "hiatus": 2, "cancelled": 3}
];
final urll =
"https://api.mangadex.org$url?includes[]=cover_art&includes[]=author&includes[]=artist";
final res = await http('GET', json.encode({"url": urll}));
MManga manga = MManga();
manga.author = jsonPathToString(
res, r'$..data.relationships[*].attributes.name', ', ');
String expressionDescriptionA = r'$..data.attributes.description.en';
String expressionDescription = regExp(r'$..data.attributes.description[a]',
r'\[a\]', ".${source.lang}", 0, 1);
String description = jsonPathToString(res, expressionDescription, '');
if (description.isEmpty) {
description = jsonPathToString(res, expressionDescriptionA, '');
}
manga.description = description;
List<String> genres = [];
genres = jsonPathToString(
res, r'$..data.attributes.tags[*].attributes.name.en', '.-')
.split('.-');
String contentRating =
jsonPathToString(res, r'$..data.attributes.contentRating', '');
if (contentRating != "safe") {
genres.add(contentRating);
}
String publicationDemographic =
jsonPathToString(res, r'$..data.attributes.publicationDemographic', '');
if (publicationDemographic == "null") {
} else {
genres.add(publicationDemographic);
}
manga.genre = genres;
String statusRes = jsonPathToString(res, r'$..data.attributes.status', '');
manga.status = parseStatus(statusRes, statusList);
final mangaId = url.split('/').last;
final paginatedChapterList =
await paginatedChapterListRequest(mangaId, 0, source.lang);
final chapterList =
jsonPathToString(paginatedChapterList, r'$.data[*]', '_.').split('_.');
int limit =
int.parse(jsonPathToString(paginatedChapterList, r'$.limit', ''));
int offset =
int.parse(jsonPathToString(paginatedChapterList, r'$.offset', ''));
int total =
int.parse(jsonPathToString(paginatedChapterList, r'$.total', ''));
List<MChapter> chapterListA = [];
final list =
getChapters(int.parse("${chapterList.length}"), paginatedChapterList);
chapterListA.addAll(list);
var hasMoreResults = (limit + offset) < total;
while (hasMoreResults) {
offset += limit;
var newRequest =
await paginatedChapterListRequest(mangaId, offset, source.lang);
int total = int.parse(jsonPathToString(newRequest, r'$.total', ''));
final chapterList =
jsonPathToString(paginatedChapterList, r'$.data[*]', '_.')
.split('_.');
final list = getChapters(int.parse("${chapterList.length}"), newRequest);
chapterListA.addAll(list);
hasMoreResults = (limit + offset) < total;
}
manga.chapters = chapterListA;
return manga;
}
@override
Future<List<String>> getPageList(MSource source, String url) async {
final urll = "https://api.mangadex.org/at-home/server/$url";
final res = await http('GET', json.encode({"url": urll}));
// final dataRes = json.decode(res);
final host = getMapValue(res, "baseUrl");
final chapter = getMapValue(res, "chapter", encode: true);
final hash = getMapValue(chapter, "hash");
final chapterDatas =
json.decode(getMapValue(chapter, "data", encode: true)) as List;
return chapterDatas.map((e) => "$host/data/$hash/$e").toList();
}
MPages mangaRes(String res, MSource source) {
final datasRes = getMapValue(res, "data", encode: true);
final resJson = json.decode(datasRes) as List;
List<MManga> mangaList = [];
for (var e in resJson) {
MManga manga = MManga();
manga.name = findTitle(json.encode(e), source.lang);
manga.imageUrl = getCover(json.encode(e));
manga.link = "/manga/${getMapValue(json.encode(e), "id")}";
mangaList.add(manga);
}
return MPages(mangaList, true);
}
List<MChapter> getChapters(int length, String paginatedChapterListA) {
List<MChapter> chaptersList = [];
String paginatedChapterList = paginatedChapterListA.toString();
final dataList = jsonPathToList(paginatedChapterList, r'$.data[*]', 0);
for (var res in dataList) {
String scan = "".toString();
final groups = jsonPathToList(res,
r'$.relationships[?@.id!="00e03853-1b96-4f41-9542-c71b8692033b"]', 0);
String chapName = "".toString();
for (var element in groups) {
final data = getMapValue(element, "attributes", encode: true);
if (data.isNotEmpty) {
final name = getMapValue(data, "name");
scan += "$name".toString();
final username = getMapValue(data, "username");
if (username.isNotEmpty) {
if (scan.isEmpty) {
scan += "Uploaded by $username".toString();
}
}
}
}
if (scan.isEmpty) {
scan = "No Group".toString();
}
final dataRes = getMapValue(res, "attributes", encode: true);
if (dataRes.isNotEmpty) {
final data = getMapValue(res, "attributes", encode: true);
final volume = getMapValue(data, "volume");
if (volume.isNotEmpty) {
if (volume != "null") {
chapName = "Vol.$volume ".toString();
}
}
final chapter = getMapValue(data, "chapter");
if (chapter.isNotEmpty) {
if (chapter != "null") {
chapName += "Ch.$chapter ".toString();
}
}
final title = getMapValue(data, "title");
if (title.isNotEmpty) {
if (title != "null") {
if (chapName.isNotEmpty) {
chapName += "- ".toString();
}
chapName += "$title".toString();
}
}
if (chapName.isEmpty) {
chapName += "Oneshot".toString();
}
final date = getMapValue(data, "publishAt");
final id = getMapValue(res, "id");
MChapter chapterr = MChapter();
chapterr.name = chapName;
chapterr.url = id;
chapterr.scanlator = scan;
chapterr.dateUpload =
parseDates([date], "yyyy-MM-dd'T'HH:mm:ss+SSS", "en_US").first;
chaptersList.add(chapterr);
}
}
return chaptersList;
}
Future<String> paginatedChapterListRequest(
String mangaId, int offset, String lang) async {
final url =
'https://api.mangadex.org/manga/$mangaId/feed?limit=500&offset=$offset&includes[]=user&includes[]=scanlation_group&order[volume]=desc&order[chapter]=desc&translatedLanguage[]=$lang&includeFuturePublishAt=0&includeEmptyPages=0${getMDXContentRating()}';
final res = await http('GET', json.encode({"url": url}));
return res;
}
String getMDXContentRating() {
String ctnRating =
"&contentRating[]=suggestive&contentRating[]=safe&contentRating[]=erotica&contentRating[]=pornographic";
return ctnRating;
}
String findTitle(String dataRes, String lang) {
final attributes = getMapValue(dataRes, "attributes", encode: true);
final altTitlesJ =
json.decode(getMapValue(attributes, "altTitles", encode: true));
final titleJ = getMapValue(attributes, "title", encode: true);
final title = getMapValue(titleJ, "en");
if (title.isEmpty) {
for (var r in altTitlesJ) {
final altTitle = getMapValue(json.encode(r), "en");
if (altTitle.isNotEmpty) {
return altTitle;
}
}
}
return title;
}
String getCover(String dataRes) {
final relationships = json
.decode(getMapValue(dataRes, "relationships", encode: true)) as List;
String coverFileName = "".toString();
for (var a in relationships) {
final relationType = getMapValue(json.encode(a), "type");
if (relationType == "cover_art") {
if (coverFileName.isEmpty) {
final attributes =
getMapValue(json.encode(a), "attributes", encode: true);
coverFileName =
"https://uploads.mangadex.org/covers/${getMapValue(dataRes, "id")}/${getMapValue(attributes, "fileName")}";
}
}
}
return coverFileName;
}
@override
List<dynamic> getFilterList() {
return [
CheckBoxFilter(
"Has available chapters", "", "HasAvailableChaptersFilter"),
GroupFilter("OriginalLanguageList", "Original language", [
CheckBoxFilter("Japanese (Manga)", "originalLanguage[]=ja"),
CheckBoxFilter("Chinese (Manhua)",
"originalLanguage[]=zh&originalLanguage[]=zh-hk"),
CheckBoxFilter("Korean (Manhwa)", "originalLanguage[]=ko"),
]),
GroupFilter("ContentRatingList", "Content rating", [
CheckBoxFilter("Safe", "contentRating[]=safe", state: true),
CheckBoxFilter("Suggestive", "contentRating[]=suggestive", state: true),
CheckBoxFilter("Erotica", "contentRating[]=erotica"),
CheckBoxFilter("Pornographic", "contentRating[]=pornographic"),
]),
GroupFilter("DemographicList", "Publication demographic", [
CheckBoxFilter("None", "publicationDemographic[]=none"),
CheckBoxFilter("Shounen", "publicationDemographic[]=shounen"),
CheckBoxFilter("Shoujo", "publicationDemographic[]=shoujo"),
CheckBoxFilter("Seinen", "publicationDemographic[]=seinen"),
CheckBoxFilter("Josei", "publicationDemographic[]=josei"),
]),
GroupFilter("StatusList", "Status", [
CheckBoxFilter("Ongoing", "status[]=ongoing"),
CheckBoxFilter("Completed", "status[]=completed"),
CheckBoxFilter("Hiatus", "status[]=hiatus"),
CheckBoxFilter("Cancelled", "status[]=cancelled"),
]),
SortFilter("SortFilter", "Sort", SortState(5, false), [
SelectFilterOption("Alphabetic", "title"),
SelectFilterOption("Chapter uploded at", "latestUploadedChapter"),
SelectFilterOption("Number of follows", "followedCount"),
SelectFilterOption("Content created at", "createdAt"),
SelectFilterOption("Content info updated at", "updatedAt"),
SelectFilterOption("Relevance", "relevance"),
SelectFilterOption("Year", "year"),
SelectFilterOption("Rating", "rating"),
]),
GroupFilter("TagsFilter", "Tags mode", [
SelectFilter("TagInclusionMode", "Included tags mode", 0, [
SelectFilterOption("AND", "includedTagsMode=AND"),
SelectFilterOption("OR", "includedTagsMode=OR"),
]),
SelectFilter("TagExclusionMode", "Excluded tags mode", 1, [
SelectFilterOption("AND", "excludedTagsMode=AND"),
SelectFilterOption("OR", "excludedTagsMode=OR"),
]),
]),
GroupFilter("ContentsFilter", "Content", [
TriStateFilter("Gore", "b29d6a3d-1569-4e7a-8caf-7557bc92cd5d"),
TriStateFilter(
"Sexual Violence", "97893a4c-12af-4dac-b6be-0dffb353568e"),
]),
GroupFilter("FormatFilter", "Format", [
TriStateFilter("4-Koma", "b11fda93-8f1d-4bef-b2ed-8803d3733170"),
TriStateFilter("Adaptation", "f4122d1c-3b44-44d0-9936-ff7502c39ad3"),
TriStateFilter("Anthology", "51d83883-4103-437c-b4b1-731cb73d786c"),
TriStateFilter("Award Winning", "0a39b5a1-b235-4886-a747-1d05d216532d"),
TriStateFilter("Doujinshi", "b13b2a48-c720-44a9-9c77-39c9979373fb"),
TriStateFilter("Fan Colored", "7b2ce280-79ef-4c09-9b58-12b7c23a9b78"),
TriStateFilter("Full Color", "f5ba408b-0e7a-484d-8d49-4e9125ac96de"),
TriStateFilter("Long Strip", "3e2b8dae-350e-4ab8-a8ce-016e844b9f0d"),
TriStateFilter(
"Official Colored", "320831a8-4026-470b-94f6-8353740e6f04"),
TriStateFilter("Oneshot", "0234a31e-a729-4e28-9d6a-3f87c4966b9e"),
TriStateFilter("User Created", "891cf039-b895-47f0-9229-bef4c96eccd4"),
TriStateFilter("Web Comic", "e197df38-d0e7-43b5-9b09-2842d0c326dd"),
]),
GroupFilter("GenreFilter", "Genre", [
TriStateFilter("Action", "391b0423-d847-456f-aff0-8b0cfc03066b"),
TriStateFilter("Adventure", "87cc87cd-a395-47af-b27a-93258283bbc6"),
TriStateFilter("Boys' Love", "5920b825-4181-4a17-beeb-9918b0ff7a30"),
TriStateFilter("Comedy", "4d32cc48-9f00-4cca-9b5a-a839f0764984"),
TriStateFilter("Crime", "5ca48985-9a9d-4bd8-be29-80dc0303db72"),
TriStateFilter("Drama", "b9af3a63-f058-46de-a9a0-e0c13906197a"),
TriStateFilter("Fantasy", "cdc58593-87dd-415e-bbc0-2ec27bf404cc"),
TriStateFilter("Girls' Love", "a3c67850-4684-404e-9b7f-c69850ee5da6"),
TriStateFilter("Historical", "33771934-028e-4cb3-8744-691e866a923e"),
TriStateFilter("Horror", "cdad7e68-1419-41dd-bdce-27753074a640"),
TriStateFilter("Isekai", "ace04997-f6bd-436e-b261-779182193d3d"),
TriStateFilter("Magical Girls", "81c836c9-914a-4eca-981a-560dad663e73"),
TriStateFilter("Mecha", "50880a9d-5440-4732-9afb-8f457127e836"),
TriStateFilter("Medical", "c8cbe35b-1b2b-4a3f-9c37-db84c4514856"),
TriStateFilter("Mystery", "ee968100-4191-4968-93d3-f82d72be7e46"),
TriStateFilter("Philosophical", "b1e97889-25b4-4258-b28b-cd7f4d28ea9b"),
TriStateFilter("Psychological", "3b60b75c-a2d7-4860-ab56-05f391bb889c"),
TriStateFilter("Romance", "423e2eae-a7a2-4a8b-ac03-a8351462d71d"),
TriStateFilter("Sci-Fi", "256c8bd9-4904-4360-bf4f-508a76d67183"),
TriStateFilter("Slice of Life", "e5301a23-ebd9-49dd-a0cb-2add944c7fe9"),
TriStateFilter("Sports", "69964a64-2f90-4d33-beeb-f3ed2875eb4c"),
TriStateFilter("Superhero", "7064a261-a137-4d3a-8848-2d385de3a99c"),
TriStateFilter("Thriller", "07251805-a27e-4d59-b488-f0bfbec15168"),
TriStateFilter("Tragedy", "f8f62932-27da-4fe4-8ee1-6779a8c5edba"),
TriStateFilter("Wuxia", "acc803a4-c95a-4c22-86fc-eb6b582d82a2"),
]),
GroupFilter("ThemeFilter", "Theme", [
TriStateFilter("Aliens", "e64f6742-c834-471d-8d72-dd51fc02b835"),
TriStateFilter("Animals", "3de8c75d-8ee3-48ff-98ee-e20a65c86451"),
TriStateFilter("Cooking", "ea2bc92d-1c26-4930-9b7c-d5c0dc1b6869"),
TriStateFilter("Crossdressing", "9ab53f92-3eed-4e9b-903a-917c86035ee3"),
TriStateFilter("Delinquents", "da2d50ca-3018-4cc0-ac7a-6b7d472a29ea"),
TriStateFilter("Demons", "39730448-9a5f-48a2-85b0-a70db87b1233"),
TriStateFilter("Genderswap", "2bd2e8d0-f146-434a-9b51-fc9ff2c5fe6a"),
TriStateFilter("Ghosts", "3bb26d85-09d5-4d2e-880c-c34b974339e9"),
TriStateFilter("Gyaru", "fad12b5e-68ba-460e-b933-9ae8318f5b65"),
TriStateFilter("Harem", "aafb99c1-7f60-43fa-b75f-fc9502ce29c7"),
TriStateFilter("Incest", "5bd0e105-4481-44ca-b6e7-7544da56b1a3"),
TriStateFilter("Loli", "2d1f5d56-a1e5-4d0d-a961-2193588b08ec"),
TriStateFilter("Mafia", "85daba54-a71c-4554-8a28-9901a8b0afad"),
TriStateFilter("Magic", "a1f53773-c69a-4ce5-8cab-fffcd90b1565"),
TriStateFilter("Martial Arts", "799c202e-7daa-44eb-9cf7-8a3c0441531e"),
TriStateFilter("Military", "ac72833b-c4e9-4878-b9db-6c8a4a99444a"),
TriStateFilter("Monster Girls", "dd1f77c5-dea9-4e2b-97ae-224af09caf99"),
TriStateFilter("Monsters", "36fd93ea-e8b8-445e-b836-358f02b3d33d"),
TriStateFilter("Music", "f42fbf9e-188a-447b-9fdc-f19dc1e4d685"),
TriStateFilter("Ninja", "489dd859-9b61-4c37-af75-5b18e88daafc"),
TriStateFilter(
"Office Workers", "92d6d951-ca5e-429c-ac78-451071cbf064"),
TriStateFilter("Police", "df33b754-73a3-4c54-80e6-1a74a8058539"),
TriStateFilter(
"Post-Apocalyptic", "9467335a-1b83-4497-9231-765337a00b96"),
TriStateFilter("Reincarnation", "0bc90acb-ccc1-44ca-a34a-b9f3a73259d0"),
TriStateFilter("Reverse Harem", "65761a2a-415e-47f3-bef2-a9dababba7a6"),
TriStateFilter("Samurai", "81183756-1453-4c81-aa9e-f6e1b63be016"),
TriStateFilter("School Life", "caaa44eb-cd40-4177-b930-79d3ef2afe87"),
TriStateFilter("Shota", "ddefd648-5140-4e5f-ba18-4eca4071d19b"),
TriStateFilter("Supernatural", "eabc5b4c-6aff-42f3-b657-3e90cbd00b75"),
TriStateFilter("Survival", "5fff9cde-849c-4d78-aab0-0d52b2ee1d25"),
TriStateFilter("Time Travel", "292e862b-2d17-4062-90a2-0356caa4ae27"),
TriStateFilter(
"Traditional Games", "31932a7e-5b8e-49a6-9f12-2afa39dc544c"),
TriStateFilter("Vampires", "d7d1730f-6eb0-4ba6-9437-602cac38664c"),
TriStateFilter("Video Games", "9438db5a-7e2a-4ac0-b39e-e0d95a34b8a8"),
TriStateFilter("Villainess", "d14322ac-4d6f-4e9b-afd9-629d5f4d8a41"),
TriStateFilter(
"Virtual Reality", "8c86611e-fab7-4986-9dec-d1a2f44acdd5"),
TriStateFilter("Zombies", "631ef465-9aba-4afb-b0fc-ea10efe274a8"),
]),
];
}
String ll(String url) {
if (url.contains("?")) {
return "&";
}
return "?";
}
}
MangaDex main() {
return MangaDex();
}

View File

@@ -4,7 +4,7 @@ import '../../../../utils/utils.dart';
const apiUrl = 'https://api.mangadex.org';
const baseUrl = 'https://mangadex.org';
const isNsfw = true;
const mangadexVersion = "0.0.4";
const mangadexVersion = "0.0.45";
const mangadexSourceCodeUrl =
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/manga/src/all/mangadex/mangadex-v$mangadexVersion.dart";
String _iconUrl = getIconUrl("mangadex", "all");

View File

@@ -59,10 +59,55 @@ class MangaHere extends MProvider {
}
@override
Future<MPages> search(MSource source, String query, int page) async {
Future<MPages> search(
MSource source, String query, int page, FilterList filterList) async {
final headers = getHeader(source.baseUrl);
final url = "${source.baseUrl}/search?title=$query&page=$page";
final filters = filterList.filters;
String url = "${source.baseUrl}/search";
for (var filter in filters) {
if (filter.type == "TypeList") {
final type = filter.values[filter.state].value;
url += "${ll(url)}type=$type";
} else if (filter.type == "CompletionList") {
final cmp = filter.values[filter.state].value;
url += "${ll(url)}st=$cmp";
} else if (filter.type == "RatingList") {
url += "${ll(url)}rating_method=gt";
final rt = filter.values[filter.state].value;
url += "${ll(url)}rating=$rt";
} else if (filter.type == "GenreList") {
final included = (filter.state as List)
.where((e) => e.state == 1 ? true : false)
.toList();
final excluded = (filter.state as List)
.where((e) => e.state == 2 ? true : false)
.toList();
if (included.isNotEmpty) {
url += "${ll(url)}genres=";
for (var val in included) {
url += "${val.value},";
}
}
if (excluded.isNotEmpty) {
url += "${ll(url)}nogenres=";
for (var val in excluded) {
url += "${val.value},";
}
}
} else if (filter.type == "ArtistFilter") {
url += "${ll(url)}artist_method=cw";
url += "${ll(url)}artist=${Uri.encodeComponent(filter.state)}";
} else if (filter.type == "AuthorFilter") {
url += "${ll(url)}author_method=cw";
url += "${ll(url)}author=${Uri.encodeComponent(filter.state)}";
} else if (filter.type == "YearFilter") {
url += "${ll(url)}released_method=cw";
url += "${ll(url)}released=${Uri.encodeComponent(filter.state)}";
}
}
url += "${ll(url)}title=$query&page=$page";
final data = {"url": url, "headers": headers};
final res = await http('POST', json.encode(data));
@@ -205,6 +250,84 @@ class MangaHere extends MProvider {
return pageUrls;
}
String ll(String url) {
if (url.contains("?")) {
return "&";
}
return "?";
}
@override
List<dynamic> getFilterList() {
return [
SelectFilter("TypeList", "Type", 1, [
SelectFilterOption("American Manga", "5"),
SelectFilterOption("Any", "0"),
SelectFilterOption("Chinese Manhua", "3"),
SelectFilterOption("European Manga", "4"),
SelectFilterOption("Hong Kong Manga", "6"),
SelectFilterOption("Japanese Manga", "1"),
SelectFilterOption("Korean Manhwa", "2"),
SelectFilterOption("Other Manga", "7"),
]),
TextFilter("ArtistFilter", "Artist"),
TextFilter("AuthorFilter", "Author"),
GroupFilter("GenreList", "Genres", [
TriStateFilter("Action", "1"),
TriStateFilter("Adventure", "2"),
TriStateFilter("Comedy", "3"),
TriStateFilter("Fantasy", "4"),
TriStateFilter("Historical", "5"),
TriStateFilter("Horror", "6"),
TriStateFilter("Martial Arts", "7"),
TriStateFilter("Mystery", "8"),
TriStateFilter("Romance", "9"),
TriStateFilter("Shounen Ai", "10"),
TriStateFilter("Supernatural", "11"),
TriStateFilter("Drama", "12"),
TriStateFilter("Shounen", "13"),
TriStateFilter("School Life", "14"),
TriStateFilter("Shoujo", "15"),
TriStateFilter("Gender Bender", "16"),
TriStateFilter("Josei", "17"),
TriStateFilter("Psychological", "18"),
TriStateFilter("Seinen", "19"),
TriStateFilter("Slice of Life", "20"),
TriStateFilter("Sci-fi", "21"),
TriStateFilter("Ecchi", "22"),
TriStateFilter("Harem", "23"),
TriStateFilter("Shoujo Ai", "24"),
TriStateFilter("Yuri", "25"),
TriStateFilter("Mature", "26"),
TriStateFilter("Tragedy", "27"),
TriStateFilter("Yaoi", "28"),
TriStateFilter("Doujinshi", "29"),
TriStateFilter("Sports", "30"),
TriStateFilter("Adult", "31"),
TriStateFilter("One Shot", "32"),
TriStateFilter("Smut", "33"),
TriStateFilter("Mecha", "34"),
TriStateFilter("Shotacon", "35"),
TriStateFilter("Lolicon", "36"),
TriStateFilter("Webtoons", "37"),
]),
SelectFilter("RatingList", "Minimum rating", 0, [
SelectFilterOption("No Stars", "0"),
SelectFilterOption("1 Star", "1"),
SelectFilterOption("2 Stars", "2"),
SelectFilterOption("3 Stars", "3"),
SelectFilterOption("4 Stars", "4"),
SelectFilterOption("5 Stars", "5"),
]),
TextFilter("YearFilter", "Year released"),
SelectFilter("CompletionList", "Completed series", 0, [
SelectFilterOption("Either", "0"),
SelectFilterOption("No", "1"),
SelectFilterOption("Yes", "2"),
]),
];
}
}
Map<String, String> getHeader(String url) {

View File

@@ -2,7 +2,7 @@ import '../../../../model/source.dart';
import '../../../../utils/utils.dart';
Source get mangahereSource => _mangahereSource;
const mangahereVersion = "0.0.35";
const mangahereVersion = "0.0.4";
const mangahereSourceCodeUrl =
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/manga/src/en/mangahere/mangahere-v$mangahereVersion.dart";
Source _mangahereSource = Source(

View File

@@ -47,7 +47,7 @@ class Source {
this.version = "",
this.isManga = true,
this.isFullData = false,
this.appMinVerReq = "0.0.6"});
this.appMinVerReq = "0.1.1"});
Map<String, dynamic> toJson() {
return {