New sources multi MangaBox : Manganato (EN), Mangabat (EN), Mangairo (EN), Mangakakalot (EN)

This commit is contained in:
kodjomoustapha
2023-12-19 19:47:38 +01:00
parent 4d6bcf1ce2
commit 792c7dd9a6
13 changed files with 523 additions and 4 deletions

View File

@@ -0,0 +1,432 @@
import 'package:mangayomi/bridge_lib.dart';
import 'dart:convert';
class MangaBox extends MProvider {
MangaBox();
@override
Future<MPages> getPopular(MSource source, int page) async {
final url = "${source.baseUrl}/${popularUrlPath(source.name, page)}";
final data = {"url": url};
final res = await http('GET', json.encode(data));
return mangaRes(res);
}
@override
Future<MPages> getLatestUpdates(MSource source, int page) async {
final url = "${source.baseUrl}/${latestUrlPath(source.name, page)}";
final data = {"url": url, "sourceId": source.id};
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 &&
(source.name != "Manganato" && source.name != "Mangabat")) {
url = "${source.baseUrl}/${simpleQueryPath(source.name, page, query)}";
} else {
url = source.baseUrl;
if (source.name == "Manganato" || source.name == "Mangabat") {
url +=
"/advanced_search?page=$page&keyw=${normalizeSearchQuery(query)}";
String genreInclude = "";
String genreExclude = "";
for (var filter in filters) {
if (filter.type == "KeywordFilter") {
final key = filter.values[filter.state].value;
url += "${ll(url)}keyt=$key";
} else if (filter.type == "SortFilter") {
final sort = filter.values[filter.state].value;
url += "${ll(url)}orby=$sort";
} else if (filter.type == "StatusFilter") {
final status = filter.values[filter.state].value;
url += "${ll(url)}sts=$status";
} 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) {
for (var val in included) {
genreInclude += "_${val.value}";
}
}
if (excluded.isNotEmpty) {
for (var val in excluded) {
genreExclude += "_${val.value}";
}
}
}
}
url += "${ll(url)}g_i=$genreInclude";
url += "${ll(url)}g_e=$genreExclude";
} else {
for (var filter in filters) {
if (filter.type == "SortFilter") {
final sort = filter.values[filter.state].value;
url += "${ll(url)}type=$sort";
} else if (filter.type == "StatusFilter") {
final status = filter.values[filter.state].value;
url += "${ll(url)}state=$status";
} else if (filter.type == "GenreListFilter") {
final genre = filter.values[filter.state].value;
url += "${ll(url)}category=$genre";
}
}
}
}
final data = {"url": url};
final res = await http('GET', json.encode(data));
List<MManga> mangaList = [];
List<String> urls = [];
urls = xpath(res,
'//*[ @class^="genres-item" or @class="list-truyen-item-wrap" or @class="story-item" or @class="story_item_right"]/h3/a/@href');
List<String> names = [];
names = xpath(res,
'//*[ @class^="genres-item" or @class="list-truyen-item-wrap" or @class="story-item" or @class="story_item_right"]/h3/a/text()');
final images = xpath(res,
'//*[@class="search-story-item" or @class="story_item" or @class="content-genres-item" or @class="list-story-item" or @class="story-item" or @class="list-truyen-item-wrap"]/a/img/@src');
if (names.isEmpty) {
urls = xpath(res,
'//*[ @class^="genres-item" or @class="list-truyen-item-wrap" or @class="story-item"]/h2/a/@href');
names = xpath(res,
'//*[ @class^="genres-item" or @class="list-truyen-item-wrap" or @class="story-item"]/h2/a/text()');
}
if (names.isEmpty) {
names = xpath(res,
'//*[@class="search-story-item" or @class="list-story-item"]/a/@title');
urls = xpath(res,
'//*[@class="search-story-item" or @class="list-story-item"]/a/@href');
}
for (var i = 0; i < names.length; i++) {
MManga manga = MManga();
manga.name = names[i];
manga.imageUrl = images[i];
manga.link = urls[i];
mangaList.add(manga);
}
return MPages(mangaList, true);
}
@override
Future<MManga> getDetail(MSource source, String url) async {
final statusList = [
{"Ongoing": 0, "Completed": 1}
];
MManga manga = MManga();
final datas = {"url": url};
final res = await http('GET', json.encode(datas));
List<String> author = xpath(res,
'//*[@class="table-label" and contains(text(), "Author")]/parent::tr/td[2]/text()');
if (author.isEmpty) {
author = xpath(res, '//li[contains(text(), "Author")]/a/text()');
}
if (author.isNotEmpty) {
manga.author = author.first;
}
final alternative = xpath(res,
'//*[@class="table-label" and contains(text(), "Alternative")]/parent::tr/td[2]/text()');
if (author.isNotEmpty) {
manga.author = author.first;
}
List<String> description =
xpath(res, '//*[@id="panel-story-info-description" ]/text()');
if (description.isEmpty) {
description = xpath(res, '//*[@id="story_discription" ]/text()');
}
if (description.isNotEmpty) {
manga.description = description.first.replaceAll("\n", ' ');
if (alternative.isNotEmpty) {
manga.description =
"${manga.description}\n\nAlternative Name: ${alternative.first}";
}
}
List<String> status = xpath(
res,
'//*[@class="table-label" and contains(text(), "Status")]/parent::tr/td[2]/text()',
'');
if (status.isEmpty) {
status = xpath(res, '//li[contains(text(), "Status")]/a/text()', '');
}
if (status.isNotEmpty) {
manga.status = parseStatus(status.first, statusList);
}
manga.genre = xpath(res,
'//*[@class="table-label" and contains(text(), "Genres")]/parent::tr/td[2]/a/text()');
if (manga.genre.isEmpty) {
manga.genre = xpath(res, '//li[contains(text(), "Genres")]/a/text()');
}
List<String> chapUrls =
xpath(res, '//*[@class="row-content-chapter"]/li/a/@href');
if (chapUrls.isEmpty) {
chapUrls = xpath(res, '//div[@id="chapter_list"]/ul/li/a/@href');
}
List<String> chaptersNames =
xpath(res, '//*[@class="row-content-chapter"]/li/a/text()');
if (chaptersNames.isEmpty) {
chaptersNames = xpath(res, '//div[@id="chapter_list"]/ul/li/a/text()');
}
List<String> chapterDates =
xpath(res, '//*[@class="row-content-chapter"]/li/span[last()]/text()');
if (chapterDates.isEmpty) {
chapterDates = xpath(res, '//div[@id="chapter_list"]/ul/li/p/text()');
}
List<String> dateUploads =
parseDates(chapterDates, source.dateFormat, source.dateFormatLocale);
List<MChapter>? chaptersList = [];
for (var i = 0; i < chaptersNames.length; i++) {
MChapter chapter = MChapter();
chapter.name = chaptersNames[i];
chapter.url = chapUrls[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": url};
final res = await http('GET', json.encode(datas));
List<String> pageUrls = [];
final urls = xpath(res,
'//div[@class="container-chapter-reader" or @class="panel-read-story"]/img/@src');
for (var url in urls) {
if (url.startsWith("https://convert_image_digi.mgicdn.com")) {
pageUrls
.add("https://images.weserv.nl/?url=${substringAfter(url, "//")}");
} else {
pageUrls.add(url);
}
}
return pageUrls;
}
MPages mangaRes(String res) {
List<MManga> mangaList = [];
List<String> urls = [];
urls = xpath(res,
'//*[ @class^="genres-item" or @class="list-truyen-item-wrap" or @class="story-item"]/h3/a/@href');
List<String> names = [];
names = xpath(res,
'//*[ @class^="genres-item" or @class="list-truyen-item-wrap" or @class="story-item"]/h3/a/text()');
final images = xpath(res,
'//*[ @class="content-genres-item" or @class="list-story-item" or @class="story-item" or @class="list-truyen-item-wrap"]/a/img/@src');
if (names.isEmpty) {
names = xpath(res, '//*[@class="list-story-item"]/a/@title');
urls = xpath(res, '//*[@class="list-story-item"]/a/@href');
}
for (var i = 0; i < names.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 popularUrlPath(String sourceName, int page) {
if (sourceName == "Manganato") {
return "genre-all/$page?type=topview";
} else if (sourceName == "Mangabat") {
return "manga-list-all/$page?type=topview";
} else if (sourceName == "Mangairo") {
return "manga-list/type-topview/ctg-all/state-all/page-$page";
}
return "manga_list?type=topview&category=all&state=all&page=$page";
}
String latestUrlPath(String sourceName, int page) {
if (sourceName == "Manganato") {
return "genre-all/$page";
} else if (sourceName == "Mangabat") {
return "manga-list-all/$page";
} else if (sourceName == "Mangairo") {
return "manga-list/type-latest/ctg-all/state-all/page-$page";
}
return "manga_list?type=latest&category=all&state=all&page=$page";
}
String simpleQueryPath(String sourceName, int page, String query) {
if (sourceName == "Mangakakalot") {
return "search/story/${normalizeSearchQuery(query)}?page=$page";
} else if (sourceName == "Mangairo") {
return "list/search/${normalizeSearchQuery(query)}?page=$page";
} else if (sourceName == "Mangabat") {
return "search/manga/${normalizeSearchQuery(query)}?page=$page";
} else if (sourceName == "Manganato") {
return "search/story/${normalizeSearchQuery(query)}?page=$page";
}
return "search/${normalizeSearchQuery(query)}?page=$page";
}
String normalizeSearchQuery(String query) {
String str = query.toLowerCase();
str = str.replaceAll(RegExp(r'[àáạảãâầấậẩẫăằắặẳẵ]'), 'a');
str = str.replaceAll(RegExp(r'[èéẹẻẽêềếệểễ]'), 'e');
str = str.replaceAll(RegExp(r'[ìíịỉĩ]'), 'i');
str = str.replaceAll(RegExp(r'[òóọỏõôồốộổỗơờớợởỡ]'), 'o');
str = str.replaceAll(RegExp(r'[ùúụủũưừứựửữ]'), 'u');
str = str.replaceAll(RegExp(r'[ỳýỵỷỹ]'), 'y');
str = str.replaceAll(RegExp(r'đ'), 'd');
str = str.replaceAll(
RegExp(
r"""!|@|%|\^|\*|\(|\)|\+|=|<|>|\?|/|,|\.|:|;|'| |"|&|#|\[|]|~|-|$|_""",
),
"_");
str = str.replaceAll(RegExp(r'_+'), '_');
str = str.replaceAll(RegExp(r'^_+|_+$'), '');
return str;
}
@override
List<dynamic> getFilterList(MSource source) {
if (source.name == "Mangairo") {
return [];
}
return [
SelectFilter("KeywordFilter", "Keyword search:", 0, [
SelectFilterOption("Everything", ""),
SelectFilterOption("Title", "title"),
SelectFilterOption("Alt title", "alternative"),
SelectFilterOption("Author", "author"),
]),
SelectFilter("SortFilter", "Order by:", 0, [
SelectFilterOption("Latest", "latest"),
SelectFilterOption("Newest", "newest"),
SelectFilterOption("Top read", "topview"),
]),
SelectFilter("StatusFilter", "Status:", 0, [
SelectFilterOption("ALL", "all"),
SelectFilterOption("Completed", "completed"),
SelectFilterOption("Ongoing", "ongoing"),
SelectFilterOption("Dropped", "drop"),
]),
if (source.name == "Manganato" || source.name == "Mangabat")
GroupFilter("GenreListFilter", "Category:", [
TriStateFilter("Action", "2"),
TriStateFilter("Adult", "3"),
TriStateFilter("Adventure", "4"),
TriStateFilter("Comedy", "6"),
TriStateFilter("Cooking", "7"),
TriStateFilter("Doujinshi", "9"),
TriStateFilter("Drama", "10"),
TriStateFilter("Ecchi", "11"),
TriStateFilter("Fantasy", "12"),
TriStateFilter("Gender bender", "13"),
TriStateFilter("Harem", "14"),
TriStateFilter("Historical", "15"),
TriStateFilter("Horror", "16"),
TriStateFilter("Isekai", "45"),
TriStateFilter("Josei", "17"),
TriStateFilter("Manhua", "44"),
TriStateFilter("Manhwa", "43"),
TriStateFilter("Martial arts", "19"),
TriStateFilter("Mature", "20"),
TriStateFilter("Mecha", "21"),
TriStateFilter("Medical", "22"),
TriStateFilter("Mystery", "24"),
TriStateFilter("One shot", "25"),
TriStateFilter("Psychological", "26"),
TriStateFilter("Romance", "27"),
TriStateFilter("School life", "28"),
TriStateFilter("Sci fi", "29"),
TriStateFilter("Seinen", "30"),
TriStateFilter("Shoujo", "31"),
TriStateFilter("Shoujo ai", "32"),
TriStateFilter("Shounen", "33"),
TriStateFilter("Shounen ai", "34"),
TriStateFilter("Slice of life", "35"),
TriStateFilter("Smut", "36"),
TriStateFilter("Sports", "37"),
TriStateFilter("Supernatural", "38"),
TriStateFilter("Tragedy", "39"),
TriStateFilter("Webtoons", "40"),
TriStateFilter("Yaoi", "41"),
TriStateFilter("Yuri", "42"),
]),
if (source.name != "Manganato" && source.name != "Mangabat")
SelectFilter("GenreListFilter", "Category:", 0, [
SelectFilterOption("ALL", "all"),
SelectFilterOption("Action", "2"),
SelectFilterOption("Adult", "3"),
SelectFilterOption("Adventure", "4"),
SelectFilterOption("Comedy", "6"),
SelectFilterOption("Cooking", "7"),
SelectFilterOption("Doujinshi", "9"),
SelectFilterOption("Drama", "10"),
SelectFilterOption("Ecchi", "11"),
SelectFilterOption("Fantasy", "12"),
SelectFilterOption("Gender bender", "13"),
SelectFilterOption("Harem", "14"),
SelectFilterOption("Historical", "15"),
SelectFilterOption("Horror", "16"),
SelectFilterOption("Isekai", "45"),
SelectFilterOption("Josei", "17"),
SelectFilterOption("Manhua", "44"),
SelectFilterOption("Manhwa", "43"),
SelectFilterOption("Martial arts", "19"),
SelectFilterOption("Mature", "20"),
SelectFilterOption("Mecha", "21"),
SelectFilterOption("Medical", "22"),
SelectFilterOption("Mystery", "24"),
SelectFilterOption("One shot", "25"),
SelectFilterOption("Psychological", "26"),
SelectFilterOption("Romance", "27"),
SelectFilterOption("School life", "28"),
SelectFilterOption("Sci fi", "29"),
SelectFilterOption("Seinen", "30"),
SelectFilterOption("Shoujo", "31"),
SelectFilterOption("Shoujo ai", "32"),
SelectFilterOption("Shounen", "33"),
SelectFilterOption("Shounen ai", "34"),
SelectFilterOption("Slice of life", "35"),
SelectFilterOption("Smut", "36"),
SelectFilterOption("Sports", "37"),
SelectFilterOption("Supernatural", "38"),
SelectFilterOption("Tragedy", "39"),
SelectFilterOption("Webtoons", "40"),
SelectFilterOption("Yaoi", "41"),
SelectFilterOption("Yuri", "42"),
]),
];
}
String ll(String url) {
if (url.contains("?")) {
return "&";
}
return "?";
}
}
Map<String, String> getHeader(String url) {
final headers = {'referer': '$url/'};
return headers;
}
MangaBox main() {
return MangaBox();
}

View File

@@ -0,0 +1,25 @@
import '../../../model/source.dart';
import 'src/mangabat/mangabat.dart';
import 'src/mangairo/mangaeiro.dart';
import 'src/mangakakalot/mangakakalot.dart';
import 'src/manganato/manganato.dart';
const mangaboxVersion = "0.0.1";
const mangaboxSourceCodeUrl =
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/manga/multisrc/mangabox/mangabox-v$mangaboxVersion.dart";
List<Source> get mangaboxSourcesList => _mangaboxSourcesList;
List<Source> _mangaboxSourcesList = [
//Mangabat (EN)
mangabatSource,
//Mangairo (EN)
mangairoSource,
//Mangakakalot (EN)
mangakakalotSource,
//Manganato (EN)
manganatoSource
]
.map((e) => e
..sourceCodeUrl = mangaboxSourceCodeUrl
..version = mangaboxVersion)
.toList();

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@@ -0,0 +1,14 @@
import '../../../../../model/source.dart';
Source get mangabatSource => _mangabatSource;
Source _mangabatSource = Source(
name: "Mangabat",
baseUrl: "https://m.mangabat.com",
lang: "en",
typeSource: "mangabox",
iconUrl:
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/manga/multisrc/mangabox/src/mangabat/icon.png",
dateFormat: "MMM dd,yy",
dateFormatLocale: "en",
);

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@@ -0,0 +1,15 @@
import '../../../../../model/source.dart';
Source get mangairoSource => _mangairoSource;
Source _mangairoSource = Source(
name: "Mangairo",
baseUrl: "https://h.mangairo.com",
lang: "en",
isNsfw: true,
typeSource: "mangabox",
iconUrl:
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/manga/multisrc/mangabox/src/mangairo/icon.png",
dateFormat: "MMM-dd-yy",
dateFormatLocale: "en",
);

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

View File

@@ -0,0 +1,14 @@
import '../../../../../model/source.dart';
Source get mangakakalotSource => _mangakakalotSource;
Source _mangakakalotSource = Source(
name: "Mangakakalot",
baseUrl: "https://mangakakalot.com",
lang: "en",
typeSource: "mangabox",
iconUrl:
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/manga/multisrc/mangabox/src/mangakakalot/icon.png",
dateFormat: "MMM-dd-yy",
dateFormatLocale: "en",
);

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

@@ -0,0 +1,14 @@
import '../../../../../model/source.dart';
Source get manganatoSource => _manganatoSource;
Source _manganatoSource = Source(
name: "Manganato",
baseUrl: "https://manganato.com",
lang: "en",
typeSource: "mangabox",
iconUrl:
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/manga/multisrc/mangabox/src/manganato/icon.png",
dateFormat: "MMM dd,yy",
dateFormatLocale: "en",
);

View File

@@ -4,6 +4,7 @@ import 'dart:io';
import '../model/source.dart';
import 'multisrc/heancms/sources.dart';
import 'multisrc/madara/sources.dart';
import 'multisrc/mangabox/sources.dart';
import 'multisrc/mangareader/sources.dart';
import 'multisrc/mmrcms/sources.dart';
import 'multisrc/nepnep/sources.dart';
@@ -22,7 +23,8 @@ void main() {
...heancmsSourcesList,
mangahereSource,
...batotoSourcesList,
...nepnepSourcesList
...nepnepSourcesList,
...mangaboxSourcesList
];
final List<Map<String, dynamic>> jsonList =
sourcesList.map((source) => source.toJson()).toList();