mirror of
https://github.com/kodjodevf/mangayomi-extensions.git
synced 2026-02-17 12:20:28 +00:00
Merge pull request #5 from kodjodevf/add-filter-method
Add filter method
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
import '../../../model/source.dart';
|
import '../../../model/source.dart';
|
||||||
import '../../../utils/utils.dart';
|
import '../../../utils/utils.dart';
|
||||||
|
|
||||||
const zorothemeVersion = "0.0.45";
|
const zorothemeVersion = "0.0.5";
|
||||||
const zorothemeSourceCodeUrl =
|
const zorothemeSourceCodeUrl =
|
||||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/anime/multisrc/zorotheme/zorotheme-v$zorothemeVersion.dart";
|
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/anime/multisrc/zorotheme/zorotheme-v$zorothemeVersion.dart";
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
|
||||||
}
|
|
||||||
434
anime/multisrc/zorotheme/zorotheme-v0.0.5.dart
Normal file
434
anime/multisrc/zorotheme/zorotheme-v0.0.5.dart
Normal 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();
|
||||||
|
}
|
||||||
@@ -53,7 +53,8 @@ class OkAnime extends MProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@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";
|
String url = "${source.baseUrl}/search/?s=$query";
|
||||||
if (page > 1) {
|
if (page > 1) {
|
||||||
url += "&page=$page";
|
url += "&page=$page";
|
||||||
@@ -2,7 +2,7 @@ import '../../../../model/source.dart';
|
|||||||
import '../../../../utils/utils.dart';
|
import '../../../../utils/utils.dart';
|
||||||
|
|
||||||
Source get okanimeSource => _okanimeSource;
|
Source get okanimeSource => _okanimeSource;
|
||||||
const okanimeVersion = "0.0.25";
|
const okanimeVersion = "0.0.3";
|
||||||
const okanimeSourceCodeUrl =
|
const okanimeSourceCodeUrl =
|
||||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/anime/src/ar/okanime/okanime-v$okanimeVersion.dart";
|
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/anime/src/ar/okanime/okanime-v$okanimeVersion.dart";
|
||||||
Source _okanimeSource = Source(
|
Source _okanimeSource = Source(
|
||||||
|
|||||||
@@ -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();
|
|
||||||
}
|
|
||||||
448
anime/src/en/aniwave/aniwave-v0.0.15.dart
Normal file
448
anime/src/en/aniwave/aniwave-v0.0.15.dart
Normal 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();
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@ import '../../../../model/source.dart';
|
|||||||
import '../../../../utils/utils.dart';
|
import '../../../../utils/utils.dart';
|
||||||
|
|
||||||
Source get aniwave => _aniwave;
|
Source get aniwave => _aniwave;
|
||||||
const aniwaveVersion = "0.0.1";
|
const aniwaveVersion = "0.0.15";
|
||||||
const aniwaveCodeUrl =
|
const aniwaveCodeUrl =
|
||||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/anime/src/en/aniwave/aniwave-v$aniwaveVersion.dart";
|
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/anime/src/en/aniwave/aniwave-v$aniwaveVersion.dart";
|
||||||
Source _aniwave = Source(
|
Source _aniwave = Source(
|
||||||
@@ -13,5 +13,4 @@ Source _aniwave = Source(
|
|||||||
iconUrl: getIconUrl("aniwave", "en"),
|
iconUrl: getIconUrl("aniwave", "en"),
|
||||||
sourceCodeUrl: aniwaveCodeUrl,
|
sourceCodeUrl: aniwaveCodeUrl,
|
||||||
version: aniwaveVersion,
|
version: aniwaveVersion,
|
||||||
appMinVerReq: "0.0.8",
|
|
||||||
isManga: false);
|
isManga: false);
|
||||||
|
|||||||
@@ -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();
|
|
||||||
}
|
|
||||||
1032
anime/src/en/gogoanime/gogoanime-v0.0.4.dart
Normal file
1032
anime/src/en/gogoanime/gogoanime-v0.0.4.dart
Normal file
File diff suppressed because it is too large
Load Diff
@@ -2,12 +2,12 @@ import '../../../../model/source.dart';
|
|||||||
import '../../../../utils/utils.dart';
|
import '../../../../utils/utils.dart';
|
||||||
|
|
||||||
Source get gogoanimeSource => _gogoanimeSource;
|
Source get gogoanimeSource => _gogoanimeSource;
|
||||||
const gogoanimeVersion = "0.0.35";
|
const gogoanimeVersion = "0.0.4";
|
||||||
const gogoanimeSourceCodeUrl =
|
const gogoanimeSourceCodeUrl =
|
||||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/anime/src/en/gogoanime/gogoanime-v$gogoanimeVersion.dart";
|
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/anime/src/en/gogoanime/gogoanime-v$gogoanimeVersion.dart";
|
||||||
Source _gogoanimeSource = Source(
|
Source _gogoanimeSource = Source(
|
||||||
name: "Gogoanime",
|
name: "Gogoanime",
|
||||||
baseUrl: "https://gogoanime.tel",
|
baseUrl: "https://gogoanime3.net",
|
||||||
lang: "en",
|
lang: "en",
|
||||||
typeSource: "single",
|
typeSource: "single",
|
||||||
iconUrl: getIconUrl("gogoanime", "en"),
|
iconUrl: getIconUrl("gogoanime", "en"),
|
||||||
|
|||||||
@@ -57,7 +57,8 @@ class KissKh extends MProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@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 = {
|
final data = {
|
||||||
"url": "${source.baseUrl}/api/DramaList/Search?q=$query&type=0"
|
"url": "${source.baseUrl}/api/DramaList/Search?q=$query&type=0"
|
||||||
};
|
};
|
||||||
@@ -2,7 +2,7 @@ import '../../../../model/source.dart';
|
|||||||
import '../../../../utils/utils.dart';
|
import '../../../../utils/utils.dart';
|
||||||
|
|
||||||
Source get kisskhSource => _kisskhSource;
|
Source get kisskhSource => _kisskhSource;
|
||||||
const kisskhVersion = "0.0.3";
|
const kisskhVersion = "0.0.35";
|
||||||
const kisskhSourceCodeUrl =
|
const kisskhSourceCodeUrl =
|
||||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/anime/src/en/kisskh/kisskh-v$kisskhVersion.dart";
|
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/anime/src/en/kisskh/kisskh-v$kisskhVersion.dart";
|
||||||
Source _kisskhSource = Source(
|
Source _kisskhSource = Source(
|
||||||
|
|||||||
@@ -53,7 +53,8 @@ class AnimesUltra extends MProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@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 data = {"url": "${source.baseUrl}/"};
|
||||||
final res = await http('GET', json.encode(data));
|
final res = await http('GET', json.encode(data));
|
||||||
|
|
||||||
@@ -2,7 +2,7 @@ import '../../../../model/source.dart';
|
|||||||
import '../../../../utils/utils.dart';
|
import '../../../../utils/utils.dart';
|
||||||
|
|
||||||
Source get animesultraSource => _animesultraSource;
|
Source get animesultraSource => _animesultraSource;
|
||||||
const animesultraVersion = "0.0.35";
|
const animesultraVersion = "0.0.4";
|
||||||
const animesultraSourceCodeUrl =
|
const animesultraSourceCodeUrl =
|
||||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/anime/src/fr/animesultra/animesultra-v$animesultraVersion.dart";
|
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/anime/src/fr/animesultra/animesultra-v$animesultraVersion.dart";
|
||||||
Source _animesultraSource = Source(
|
Source _animesultraSource = Source(
|
||||||
|
|||||||
@@ -24,7 +24,8 @@ class FrAnime extends MProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@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();
|
final res = await dataBase();
|
||||||
|
|
||||||
return animeSeachFetch(res, query);
|
return animeSeachFetch(res, query);
|
||||||
@@ -2,7 +2,7 @@ import '../../../../model/source.dart';
|
|||||||
import '../../../../utils/utils.dart';
|
import '../../../../utils/utils.dart';
|
||||||
|
|
||||||
Source get franimeSource => _franimeSource;
|
Source get franimeSource => _franimeSource;
|
||||||
const franimeVersion = "0.0.35";
|
const franimeVersion = "0.0.4";
|
||||||
const franimeSourceCodeUrl =
|
const franimeSourceCodeUrl =
|
||||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/anime/src/fr/franime/franime-v$franimeVersion.dart";
|
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/anime/src/fr/franime/franime-v$franimeVersion.dart";
|
||||||
Source _franimeSource = Source(
|
Source _franimeSource = Source(
|
||||||
|
|||||||
@@ -71,7 +71,8 @@ class OtakuFr extends MProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@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 = {
|
final data = {
|
||||||
"url": "${source.baseUrl}/toute-la-liste-affiches/page/$page/?q=$query"
|
"url": "${source.baseUrl}/toute-la-liste-affiches/page/$page/?q=$query"
|
||||||
};
|
};
|
||||||
@@ -2,7 +2,7 @@ import '../../../../model/source.dart';
|
|||||||
import '../../../../utils/utils.dart';
|
import '../../../../utils/utils.dart';
|
||||||
|
|
||||||
Source get otakufr => _otakufr;
|
Source get otakufr => _otakufr;
|
||||||
const otakufrVersion = "0.0.35";
|
const otakufrVersion = "0.0.4";
|
||||||
const otakufrCodeUrl =
|
const otakufrCodeUrl =
|
||||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/anime/src/fr/otakufr/otakufr-v$otakufrVersion.dart";
|
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/anime/src/fr/otakufr/otakufr-v$otakufrVersion.dart";
|
||||||
Source _otakufr = Source(
|
Source _otakufr = Source(
|
||||||
|
|||||||
@@ -46,7 +46,8 @@ class NimeGami extends MProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@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 = {
|
final data = {
|
||||||
"url": "${source.baseUrl}/page/$page/?s=$query&post_type=post"
|
"url": "${source.baseUrl}/page/$page/?s=$query&post_type=post"
|
||||||
};
|
};
|
||||||
@@ -2,7 +2,7 @@ import '../../../../model/source.dart';
|
|||||||
import '../../../../utils/utils.dart';
|
import '../../../../utils/utils.dart';
|
||||||
|
|
||||||
Source get nimegami => _nimegami;
|
Source get nimegami => _nimegami;
|
||||||
const nimegamiVersion = "0.0.2";
|
const nimegamiVersion = "0.0.25";
|
||||||
const nimegamiCodeUrl =
|
const nimegamiCodeUrl =
|
||||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/anime/src/id/nimegami/nimegami-v$nimegamiVersion.dart";
|
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/anime/src/id/nimegami/nimegami-v$nimegamiVersion.dart";
|
||||||
Source _nimegami = Source(
|
Source _nimegami = Source(
|
||||||
@@ -13,5 +13,4 @@ Source _nimegami = Source(
|
|||||||
iconUrl: getIconUrl("nimegami", "id"),
|
iconUrl: getIconUrl("nimegami", "id"),
|
||||||
sourceCodeUrl: nimegamiCodeUrl,
|
sourceCodeUrl: nimegamiCodeUrl,
|
||||||
version: nimegamiVersion,
|
version: nimegamiVersion,
|
||||||
appMinVerReq: "0.0.8",
|
|
||||||
isManga: false);
|
isManga: false);
|
||||||
|
|||||||
@@ -23,7 +23,8 @@ class OploVerz extends MProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@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 = {
|
final data = {
|
||||||
"url": "${source.baseUrl}/anime-list/page/$page/?title=$query"
|
"url": "${source.baseUrl}/anime-list/page/$page/?title=$query"
|
||||||
};
|
};
|
||||||
@@ -95,15 +96,14 @@ class OploVerz extends MProvider {
|
|||||||
}));
|
}));
|
||||||
final playerLink =
|
final playerLink =
|
||||||
xpath(ress, '//iframe[@class="playeriframe"]/@src').first;
|
xpath(ress, '//iframe[@class="playeriframe"]/@src').first;
|
||||||
print(playerLink);
|
|
||||||
final resPlayer = await http('GET', json.encode({"url": playerLink}));
|
final resPlayer = await http('GET', json.encode({"url": playerLink}));
|
||||||
var resJson = substringBefore(substringAfter(resPlayer, "= "), "<");
|
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 = [];
|
List<MVideo> videos = [];
|
||||||
for (var stream in streams) {
|
for (var stream in streams) {
|
||||||
print(stream["play_url"]);
|
final videoUrl = getMapValue(stream, "play_url");
|
||||||
final videoUrl = stream["play_url"];
|
final quality = getQuality(getMapValue(stream, "format_id"));
|
||||||
final quality = getQuality(stream["format_id"]);
|
|
||||||
|
|
||||||
MVideo video = MVideo();
|
MVideo video = MVideo();
|
||||||
video
|
video
|
||||||
@@ -2,7 +2,7 @@ import '../../../../model/source.dart';
|
|||||||
import '../../../../utils/utils.dart';
|
import '../../../../utils/utils.dart';
|
||||||
|
|
||||||
Source get oploverz => _oploverz;
|
Source get oploverz => _oploverz;
|
||||||
const oploverzVersion = "0.0.1";
|
const oploverzVersion = "0.0.15";
|
||||||
const oploverzCodeUrl =
|
const oploverzCodeUrl =
|
||||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/anime/src/id/oploverz/oploverz-v$oploverzVersion.dart";
|
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/anime/src/id/oploverz/oploverz-v$oploverzVersion.dart";
|
||||||
Source _oploverz = Source(
|
Source _oploverz = Source(
|
||||||
@@ -13,5 +13,4 @@ Source _oploverz = Source(
|
|||||||
iconUrl: getIconUrl("oploverz", "id"),
|
iconUrl: getIconUrl("oploverz", "id"),
|
||||||
sourceCodeUrl: oploverzCodeUrl,
|
sourceCodeUrl: oploverzCodeUrl,
|
||||||
version: oploverzVersion,
|
version: oploverzVersion,
|
||||||
appMinVerReq: "0.0.7",
|
|
||||||
isManga: false);
|
isManga: false);
|
||||||
|
|||||||
@@ -19,7 +19,8 @@ class OtakuDesu extends MProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@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 data = {"url": "${source.baseUrl}/?s=$query&post_type=anime"};
|
||||||
final res = await http('GET', json.encode(data));
|
final res = await http('GET', json.encode(data));
|
||||||
List<MManga> animeList = [];
|
List<MManga> animeList = [];
|
||||||
@@ -2,7 +2,7 @@ import '../../../../model/source.dart';
|
|||||||
import '../../../../utils/utils.dart';
|
import '../../../../utils/utils.dart';
|
||||||
|
|
||||||
Source get otakudesu => _otakudesu;
|
Source get otakudesu => _otakudesu;
|
||||||
const otakudesuVersion = "0.0.2";
|
const otakudesuVersion = "0.0.25";
|
||||||
const otakudesuCodeUrl =
|
const otakudesuCodeUrl =
|
||||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/anime/src/id/otakudesu/otakudesu-v$otakudesuVersion.dart";
|
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/anime/src/id/otakudesu/otakudesu-v$otakudesuVersion.dart";
|
||||||
Source _otakudesu = Source(
|
Source _otakudesu = Source(
|
||||||
@@ -12,6 +12,4 @@ Source _otakudesu = Source(
|
|||||||
typeSource: "single",
|
typeSource: "single",
|
||||||
iconUrl: getIconUrl("otakudesu", "id"),
|
iconUrl: getIconUrl("otakudesu", "id"),
|
||||||
sourceCodeUrl: otakudesuCodeUrl,
|
sourceCodeUrl: otakudesuCodeUrl,
|
||||||
version: otakudesuVersion,
|
|
||||||
appMinVerReq: "0.0.8",
|
|
||||||
isManga: false);
|
isManga: false);
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -56,7 +56,8 @@ class HeanCms extends MProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@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 headers = getHeader(source.baseUrl);
|
||||||
String res = "";
|
String res = "";
|
||||||
if (!useNewQueryEndpoint(source.source)) {
|
if (!useNewQueryEndpoint(source.source)) {
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import '../../../model/source.dart';
|
import '../../../model/source.dart';
|
||||||
import '../../../utils/utils.dart';
|
import '../../../utils/utils.dart';
|
||||||
|
|
||||||
const heancmsVersion = "0.0.35";
|
const heancmsVersion = "0.0.4";
|
||||||
const heancmsSourceCodeUrl =
|
const heancmsSourceCodeUrl =
|
||||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/manga/multisrc/heancms/heancms-v$heancmsVersion.dart";
|
"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";
|
const defaultDateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ";
|
||||||
|
|||||||
@@ -67,11 +67,53 @@ class Madara extends MProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<MPages> search(MSource source, String query, int page) async {
|
Future<MPages> search(
|
||||||
final data = {
|
MSource source, String query, int page, FilterList filterList) async {
|
||||||
"url": "${source.baseUrl}/?s=$query&post_type=wp-manga",
|
final filters = filterList.filters;
|
||||||
"sourceId": source.id
|
|
||||||
};
|
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));
|
final res = await http('GET', json.encode(data));
|
||||||
|
|
||||||
List<MManga> mangaList = [];
|
List<MManga> mangaList = [];
|
||||||
@@ -312,6 +354,42 @@ class Madara extends MProvider {
|
|||||||
}
|
}
|
||||||
return pageUrls;
|
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() {
|
Madara main() {
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import '../../../model/source.dart';
|
import '../../../model/source.dart';
|
||||||
import '../../../utils/utils.dart';
|
import '../../../utils/utils.dart';
|
||||||
|
|
||||||
const madaraVersion = "0.0.35";
|
const madaraVersion = "0.0.4";
|
||||||
const madaraSourceCodeUrl =
|
const madaraSourceCodeUrl =
|
||||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/manga/multisrc/madara/madara-v$madaraVersion.dart";
|
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/manga/multisrc/madara/madara-v$madaraVersion.dart";
|
||||||
const defaultDateFormat = "MMMM dd, yyyy";
|
const defaultDateFormat = "MMMM dd, yyyy";
|
||||||
|
|||||||
@@ -25,9 +25,49 @@ class MangaReader extends MProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<MPages> search(MSource source, String query, int page) async {
|
Future<MPages> search(
|
||||||
final url =
|
MSource source, String query, int page, FilterList filterList) async {
|
||||||
|
final filters = filterList.filters;
|
||||||
|
|
||||||
|
String url =
|
||||||
"${source.baseUrl}${getMangaUrlDirectory(source.name)}/?&title=$query&page=$page";
|
"${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 data = {"url": url, "sourceId": source.id};
|
||||||
final res = await http('GET', json.encode(data));
|
final res = await http('GET', json.encode(data));
|
||||||
|
|
||||||
@@ -195,6 +235,48 @@ class MangaReader extends MProvider {
|
|||||||
return MPages(mangaList, true);
|
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) {
|
String getMangaUrlDirectory(String sourceName) {
|
||||||
if (sourceName == "Sushi-Scan") {
|
if (sourceName == "Sushi-Scan") {
|
||||||
return "/catalogue";
|
return "/catalogue";
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import '../../../model/source.dart';
|
import '../../../model/source.dart';
|
||||||
import '../../../utils/utils.dart';
|
import '../../../utils/utils.dart';
|
||||||
|
|
||||||
const mangareaderVersion = "0.0.45";
|
const mangareaderVersion = "0.0.5";
|
||||||
const mangareaderSourceCodeUrl =
|
const mangareaderSourceCodeUrl =
|
||||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/manga/multisrc/mangareader/mangareader-v$mangareaderVersion.dart";
|
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/manga/multisrc/mangareader/mangareader-v$mangareaderVersion.dart";
|
||||||
const defaultDateFormat = "MMMM dd, yyyy";
|
const defaultDateFormat = "MMMM dd, yyyy";
|
||||||
|
|||||||
@@ -68,16 +68,45 @@ class MMRCMS extends MProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<MPages> search(MSource source, String query, int page) async {
|
Future<MPages> search(
|
||||||
final url = "${source.baseUrl}/search?query=$query";
|
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 data = {"url": url, "sourceId": source.id};
|
||||||
final res = await http('GET', json.encode(data));
|
final res = await http('GET', json.encode(data));
|
||||||
|
|
||||||
List<MManga> mangaList = [];
|
List<MManga> mangaList = [];
|
||||||
final jsonList = json.decode(res)["suggestions"];
|
|
||||||
List<String> urls = [];
|
List<String> urls = [];
|
||||||
List<String> names = [];
|
List<String> names = [];
|
||||||
List<String> images = [];
|
List<String> images = [];
|
||||||
|
|
||||||
|
if (query.isNotEmpty) {
|
||||||
|
final jsonList = json.decode(res)["suggestions"];
|
||||||
for (var da in jsonList) {
|
for (var da in jsonList) {
|
||||||
String value = da["value"];
|
String value = da["value"];
|
||||||
String data = da["data"];
|
String data = da["data"];
|
||||||
@@ -96,6 +125,19 @@ class MMRCMS extends MProvider {
|
|||||||
"${source.baseUrl}/uploads/manga/$data/cover/cover_250x350.jpg");
|
"${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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (var i = 0; i < names.length; i++) {
|
for (var i = 0; i < names.length; i++) {
|
||||||
MManga manga = MManga();
|
MManga manga = MManga();
|
||||||
@@ -189,6 +231,91 @@ class MMRCMS extends MProvider {
|
|||||||
|
|
||||||
return pagesUrl;
|
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() {
|
MMRCMS main() {
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import '../../../model/source.dart';
|
import '../../../model/source.dart';
|
||||||
import '../../../utils/utils.dart';
|
import '../../../utils/utils.dart';
|
||||||
|
|
||||||
const mmrcmsVersion = "0.0.35";
|
const mmrcmsVersion = "0.0.4";
|
||||||
const mmrcmsSourceCodeUrl =
|
const mmrcmsSourceCodeUrl =
|
||||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/manga/multisrc/mmrcms/mmrcms-v$mmrcmsVersion.dart";
|
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/manga/multisrc/mmrcms/mmrcms-v$mmrcmsVersion.dart";
|
||||||
const defaultDateFormat = "d MMM. yyyy";
|
const defaultDateFormat = "d MMM. yyyy";
|
||||||
|
|||||||
@@ -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();
|
|
||||||
}
|
|
||||||
412
manga/multisrc/nepnep/nepnep-v0.0.4.dart
Normal file
412
manga/multisrc/nepnep/nepnep-v0.0.4.dart
Normal 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();
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import '../../../model/source.dart';
|
import '../../../model/source.dart';
|
||||||
import '../../../utils/utils.dart';
|
import '../../../utils/utils.dart';
|
||||||
|
|
||||||
const nepnepVersion = "0.0.35";
|
const nepnepVersion = "0.0.4";
|
||||||
const nepnepSourceCodeUrl =
|
const nepnepSourceCodeUrl =
|
||||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/manga/multisrc/nepnep/nepnep-v$nepnepVersion.dart";
|
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/manga/multisrc/nepnep/nepnep-v$nepnepVersion.dart";
|
||||||
const defaultDateFormat = "yyyy-MM-dd HH:mm:ss";
|
const defaultDateFormat = "yyyy-MM-dd HH:mm:ss";
|
||||||
|
|||||||
@@ -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();
|
|
||||||
}
|
|
||||||
1700
manga/src/all/batoto/batoto-v0.0.4.dart
Normal file
1700
manga/src/all/batoto/batoto-v0.0.4.dart
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
|||||||
import '../../../../model/source.dart';
|
import '../../../../model/source.dart';
|
||||||
import '../../../../utils/utils.dart';
|
import '../../../../utils/utils.dart';
|
||||||
|
|
||||||
const batotoVersion = "0.0.35";
|
const batotoVersion = "0.0.4";
|
||||||
const batotoSourceCodeUrl =
|
const batotoSourceCodeUrl =
|
||||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/manga/src/all/batoto/batoto-v$batotoVersion.dart";
|
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/manga/src/all/batoto/batoto-v$batotoVersion.dart";
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
|
||||||
}
|
|
||||||
648
manga/src/all/comick/comick-v0.0.4.dart
Normal file
648
manga/src/all/comick/comick-v0.0.4.dart
Normal 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();
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import '../../../../model/source.dart';
|
import '../../../../model/source.dart';
|
||||||
import '../../../../utils/utils.dart';
|
import '../../../../utils/utils.dart';
|
||||||
|
|
||||||
const comickVersion = "0.0.35";
|
const comickVersion = "0.0.4";
|
||||||
const comickSourceCodeUrl =
|
const comickSourceCodeUrl =
|
||||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/manga/src/all/comick/comick-v$comickVersion.dart";
|
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/manga/src/all/comick/comick-v$comickVersion.dart";
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
|
||||||
}
|
|
||||||
523
manga/src/all/mangadex/mangadex-v0.0.45.dart
Normal file
523
manga/src/all/mangadex/mangadex-v0.0.45.dart
Normal 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();
|
||||||
|
}
|
||||||
@@ -4,7 +4,7 @@ import '../../../../utils/utils.dart';
|
|||||||
const apiUrl = 'https://api.mangadex.org';
|
const apiUrl = 'https://api.mangadex.org';
|
||||||
const baseUrl = 'https://mangadex.org';
|
const baseUrl = 'https://mangadex.org';
|
||||||
const isNsfw = true;
|
const isNsfw = true;
|
||||||
const mangadexVersion = "0.0.4";
|
const mangadexVersion = "0.0.45";
|
||||||
const mangadexSourceCodeUrl =
|
const mangadexSourceCodeUrl =
|
||||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/manga/src/all/mangadex/mangadex-v$mangadexVersion.dart";
|
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/manga/src/all/mangadex/mangadex-v$mangadexVersion.dart";
|
||||||
String _iconUrl = getIconUrl("mangadex", "all");
|
String _iconUrl = getIconUrl("mangadex", "all");
|
||||||
|
|||||||
@@ -59,10 +59,55 @@ class MangaHere extends MProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@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 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 data = {"url": url, "headers": headers};
|
||||||
final res = await http('POST', json.encode(data));
|
final res = await http('POST', json.encode(data));
|
||||||
|
|
||||||
@@ -205,6 +250,84 @@ class MangaHere extends MProvider {
|
|||||||
|
|
||||||
return pageUrls;
|
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) {
|
Map<String, String> getHeader(String url) {
|
||||||
@@ -2,7 +2,7 @@ import '../../../../model/source.dart';
|
|||||||
import '../../../../utils/utils.dart';
|
import '../../../../utils/utils.dart';
|
||||||
|
|
||||||
Source get mangahereSource => _mangahereSource;
|
Source get mangahereSource => _mangahereSource;
|
||||||
const mangahereVersion = "0.0.35";
|
const mangahereVersion = "0.0.4";
|
||||||
const mangahereSourceCodeUrl =
|
const mangahereSourceCodeUrl =
|
||||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/manga/src/en/mangahere/mangahere-v$mangahereVersion.dart";
|
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/manga/src/en/mangahere/mangahere-v$mangahereVersion.dart";
|
||||||
Source _mangahereSource = Source(
|
Source _mangahereSource = Source(
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ class Source {
|
|||||||
this.version = "",
|
this.version = "",
|
||||||
this.isManga = true,
|
this.isManga = true,
|
||||||
this.isFullData = false,
|
this.isFullData = false,
|
||||||
this.appMinVerReq = "0.0.6"});
|
this.appMinVerReq = "0.1.1"});
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
return {
|
return {
|
||||||
|
|||||||
Reference in New Issue
Block a user