add filter method

This commit is contained in:
kodjomoustapha
2023-11-24 17:53:52 +01:00
parent fb8801b329
commit bce9bc8724
13 changed files with 1089 additions and 216 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,647 @@
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 "?";
}
List<dynamic> getFilterList() {
return [
HeaderFilter("The filter is ignored when using text search."),
GroupFilter("GenreFilter", "Genre", [
{
"type": "TriState",
"filter": {"name": "4-Koma", "value": "4-koma"}
},
{
"type": "TriState",
"filter": {"name": "Action", "value": "action"}
},
{
"type": "TriState",
"filter": {"name": "Adaptation", "value": "adaptation"}
},
{
"type": "TriState",
"filter": {"name": "Adult", "value": "adult"}
},
{
"type": "TriState",
"filter": {"name": "Adventure", "value": "adventure"}
},
{
"type": "TriState",
"filter": {"name": "Aliens", "value": "aliens"}
},
{
"type": "TriState",
"filter": {"name": "Animals", "value": "animals"}
},
{
"type": "TriState",
"filter": {"name": "Anthology", "value": "anthology"}
},
{
"type": "TriState",
"filter": {"name": "Award Winning", "value": "award-winning"}
},
{
"type": "TriState",
"filter": {"name": "Comedy", "value": "comedy"}
},
{
"type": "TriState",
"filter": {"name": "Cooking", "value": "cooking"}
},
{
"type": "TriState",
"filter": {"name": "Crime", "value": "crime"}
},
{
"type": "TriState",
"filter": {"name": "Crossdressing", "value": "crossdressing"}
},
{
"type": "TriState",
"filter": {"name": "Delinquents", "value": "delinquents"}
},
{
"type": "TriState",
"filter": {"name": "Demons", "value": "demons"}
},
{
"type": "TriState",
"filter": {"name": "Doujinshi", "value": "doujinshi"}
},
{
"type": "TriState",
"filter": {"name": "Drama", "value": "drama"}
},
{
"type": "TriState",
"filter": {"name": "Ecchi", "value": "ecchi"}
},
{
"type": "TriState",
"filter": {"name": "Fan Colored", "value": "fan-colored"}
},
{
"type": "TriState",
"filter": {"name": "Fantasy", "value": "fantasy"}
},
{
"type": "TriState",
"filter": {"name": "Full Color", "value": "full-color"}
},
{
"type": "TriState",
"filter": {"name": "Gender Bender", "value": "gender-bender"}
},
{
"type": "TriState",
"filter": {"name": "Genderswap", "value": "genderswap"}
},
{
"type": "TriState",
"filter": {"name": "Ghosts", "value": "ghosts"}
},
{
"type": "TriState",
"filter": {"name": "Gore", "value": "gore"}
},
{
"type": "TriState",
"filter": {"name": "Gyaru", "value": "gyaru"}
},
{
"type": "TriState",
"filter": {"name": "Harem", "value": "harem"}
},
{
"type": "TriState",
"filter": {"name": "Historical", "value": "historical"}
},
{
"type": "TriState",
"filter": {"name": "Horror", "value": "horror"}
},
{
"type": "TriState",
"filter": {"name": "Incest", "value": "incest"}
},
{
"type": "TriState",
"filter": {"name": "Isekai", "value": "isekai"}
},
{
"type": "TriState",
"filter": {"name": "Loli", "value": "loli"}
},
{
"type": "TriState",
"filter": {"name": "Long Strip", "value": "long-strip"}
},
{
"type": "TriState",
"filter": {"name": "Mafia", "value": "mafia"}
},
{
"type": "TriState",
"filter": {"name": "Magic", "value": "magic"}
},
{
"type": "TriState",
"filter": {"name": "Magical Girls", "value": "magical-girls"}
},
{
"type": "TriState",
"filter": {"name": "Martial Arts", "value": "martial-arts"}
},
{
"type": "TriState",
"filter": {"name": "Mature", "value": "mature"}
},
{
"type": "TriState",
"filter": {"name": "Mecha", "value": "mecha"}
},
{
"type": "TriState",
"filter": {"name": "Medical", "value": "medical"}
},
{
"type": "TriState",
"filter": {"name": "Military", "value": "military"}
},
{
"type": "TriState",
"filter": {"name": "Monster Girls", "value": "monster-girls"}
},
{
"type": "TriState",
"filter": {"name": "Monsters", "value": "monsters"}
},
{
"type": "TriState",
"filter": {"name": "Music", "value": "music"}
},
{
"type": "TriState",
"filter": {"name": "Mystery", "value": "mystery"}
},
{
"type": "TriState",
"filter": {"name": "Ninja", "value": "ninja"}
},
{
"type": "TriState",
"filter": {"name": "Office Workers", "value": "office-workers"}
},
{
"type": "TriState",
"filter": {"name": "Official Colored", "value": "official-colored"}
},
{
"type": "TriState",
"filter": {"name": "Oneshot", "value": "oneshot"}
},
{
"type": "TriState",
"filter": {"name": "Philosophical", "value": "philosophical"}
},
{
"type": "TriState",
"filter": {"name": "Police", "value": "police"}
},
{
"type": "TriState",
"filter": {"name": "Post-Apocalyptic", "value": "post-apocalyptic"}
},
{
"type": "TriState",
"filter": {"name": "Psychological", "value": "psychological"}
},
{
"type": "TriState",
"filter": {"name": "Reincarnation", "value": "reincarnation"}
},
{
"type": "TriState",
"filter": {"name": "Reverse Harem", "value": "reverse-harem"}
},
{
"type": "TriState",
"filter": {"name": "Romance", "value": "romance"}
},
{
"type": "TriState",
"filter": {"name": "Samurai", "value": "samurai"}
},
{
"type": "TriState",
"filter": {"name": "School Life", "value": "school-life"}
},
{
"type": "TriState",
"filter": {"name": "Sci-Fi", "value": "sci-fi"}
},
{
"type": "TriState",
"filter": {"name": "Sexual Violence", "value": "sexual-violence"}
},
{
"type": "TriState",
"filter": {"name": "Shota", "value": "shota"}
},
{
"type": "TriState",
"filter": {"name": "Shoujo Ai", "value": "shoujo-ai"}
},
{
"type": "TriState",
"filter": {"name": "Shounen Ai", "value": "shounen-ai"}
},
{
"type": "TriState",
"filter": {"name": "Slice of Life", "value": "slice-of-life"}
},
{
"type": "TriState",
"filter": {"name": "Smut", "value": "smut"}
},
{
"type": "TriState",
"filter": {"name": "Sports", "value": "sports"}
},
{
"type": "TriState",
"filter": {"name": "Superhero", "value": "superhero"}
},
{
"type": "TriState",
"filter": {"name": "Supernatural", "value": "supernatural"}
},
{
"type": "TriState",
"filter": {"name": "Survival", "value": "survival"}
},
{
"type": "TriState",
"filter": {"name": "Thriller", "value": "thriller"}
},
{
"type": "TriState",
"filter": {"name": "Time Travel", "value": "time-travel"}
},
{
"type": "TriState",
"filter": {"name": "Traditional Games", "value": "traditional-games"}
},
{
"type": "TriState",
"filter": {"name": "Tragedy", "value": "tragedy"}
},
{
"type": "TriState",
"filter": {"name": "User Created", "value": "user-created"}
},
{
"type": "TriState",
"filter": {"name": "Vampires", "value": "vampires"}
},
{
"type": "TriState",
"filter": {"name": "Video Games", "value": "video-games"}
},
{
"type": "TriState",
"filter": {"name": "Villainess", "value": "villainess"}
},
{
"type": "TriState",
"filter": {"name": "Virtual Reality", "value": "virtual-reality"}
},
{
"type": "TriState",
"filter": {"name": "Web Comic", "value": "web-comic"}
},
{
"type": "TriState",
"filter": {"name": "Wuxia", "value": "wuxia"}
},
{
"type": "TriState",
"filter": {"name": "Yaoi", "value": "yaoi"}
},
{
"type": "TriState",
"filter": {"name": "Yuri", "value": "yuri"}
},
{
"type": "TriState",
"filter": {"name": "Zombies", "value": "zombies"}
}
]),
GroupFilter("DemographicFilter", "Demographic", [
TriStateFilter("Shounen", "1"),
TriStateFilter("Shoujo", "2"),
TriStateFilter("Seinen", "3"),
TriStateFilter("Josei", "4"),
]),
GroupFilter("TypeFilter", "Type", [
CheckBoxFilter("Manga", "jp"),
CheckBoxFilter("Manhwa", "kr"),
CheckBoxFilter("Manhua", "cn"),
]),
SelectFilter("SortFilter", "Sort", 0, [
SelectFilterOption("Most popular", "follow"),
SelectFilterOption("Most follows", "user_follow_count"),
SelectFilterOption("Most views", "view"),
SelectFilterOption("High rating", "rating"),
SelectFilterOption("Last updated", "uploaded"),
SelectFilterOption("Newest", "created_at"),
]),
SelectFilter("StatusFilter", "Status", 0, [
SelectFilterOption("All", "0"),
SelectFilterOption("Ongoing", "1"),
SelectFilterOption("Completed", "2"),
SelectFilterOption("Cancelled", "3"),
SelectFilterOption("Hiatus", "4"),
]),
CheckBoxFilter("Completely Scanlated?", "", "CompletedFilter"),
SelectFilter("CreatedAtFilter", "Created at", 0, [
SelectFilterOption("", ""),
SelectFilterOption("3 days", "3"),
SelectFilterOption("7 days", "7"),
SelectFilterOption("30 days", "30"),
SelectFilterOption("3 months", "90"),
SelectFilterOption("6 months", "180"),
SelectFilterOption("1 year", "365"),
]),
TextFilter("MinimumFilter", "Minimum Chapters"),
HeaderFilter("From Year, ex: 2010"),
TextFilter("FromYearFilter", "From"),
HeaderFilter("To Year, ex: 2021"),
TextFilter("ToYearFilter", "To"),
HeaderFilter("Separate tags with commas"),
TextFilter("TagFilter", "Tags")
];
}
String beautifyChapterName(
String vol, String chap, String title, String lang) {
String result = "";
if (vol != "null" && vol.isNotEmpty) {
if (chap != "null" && chap.isEmpty) {
result += "Volume $vol ";
} else {
result += "Vol. $vol ";
}
}
if (chap != "null" && chap.isNotEmpty) {
if (vol != "null" && vol.isEmpty) {
if (lang != "null" && lang == "fr") {
result += "Chapitre $chap";
} else {
result += "Chapter $chap";
}
} else {
result += "Ch. $chap ";
}
}
if (title != "null" && title.isNotEmpty) {
if (chap != "null" && chap.isEmpty) {
result += title;
} else {
result += " : $title";
}
}
return result;
}
}
Map<String, String> getHeader(String url) {
final headers = {
"Referer": "$url/",
'User-Agent':
"Tachiyomi Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:110.0) Gecko/20100101 Firefox/110.0"
};
return headers;
}
ComickFun main() {
return ComickFun();
}

View File

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

View File

@@ -35,7 +35,8 @@ class MangaDex extends MProvider {
}
@override
Future<MPages> search(MSource source, String query, int page) async {
Future<MPages> search(
MSource source, String query, int page, FilterList filterList) async {
final url =
"https://api.mangadex.org/manga?includes[]=cover_art&offset=0&limit=20&title=$query${getMDXContentRating()}&order[followedCount]=desc&availableTranslatedLanguage[]=${source.lang}";

View File

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

View File

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

View File

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