mirror of
https://github.com/kodjodevf/mangayomi-extensions.git
synced 2026-02-14 10:51:17 +00:00
385 lines
14 KiB
Dart
385 lines
14 KiB
Dart
import 'package:mangayomi/bridge_lib.dart';
|
||
import 'dart:convert';
|
||
|
||
class MangaReader extends MProvider {
|
||
MangaReader({required this.source});
|
||
|
||
MSource source;
|
||
|
||
final Client client = Client();
|
||
|
||
@override
|
||
String get baseUrl => getPreferenceValue(source.id, "override_baseurl");
|
||
|
||
@override
|
||
Map<String, String> get headers => {"Referer": "$baseUrl/"};
|
||
|
||
@override
|
||
Future<MPages> getPopular(int page) async {
|
||
final res =
|
||
(await client.get(
|
||
Uri.parse(
|
||
"$baseUrl${getMangaUrlDirectory(source.name)}/?page=$page&order=popular",
|
||
),
|
||
)).body;
|
||
return mangaRes(res);
|
||
}
|
||
|
||
@override
|
||
Future<MPages> getLatestUpdates(int page) async {
|
||
final res =
|
||
(await client.get(
|
||
Uri.parse(
|
||
"$baseUrl${getMangaUrlDirectory(source.name)}/?page=$page&order=update",
|
||
),
|
||
)).body;
|
||
return mangaRes(res);
|
||
}
|
||
|
||
@override
|
||
Future<MPages> search(String query, int page, FilterList filterList) async {
|
||
final filters = filterList.filters;
|
||
|
||
String url = "$baseUrl${getMangaSearchUrl(page, query)}";
|
||
|
||
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 res = (await client.get(Uri.parse(url))).body;
|
||
return mangaRes(res);
|
||
}
|
||
|
||
@override
|
||
Future<MManga> getDetail(String url) async {
|
||
final statusList = [
|
||
{
|
||
"مستمرة": 0,
|
||
"En curso": 0,
|
||
"Ongoing": 0,
|
||
"On going": 0,
|
||
"Ativo": 0,
|
||
"En Cours": 0,
|
||
"Berjalan": 0,
|
||
"Продолжается": 0,
|
||
"Updating": 0,
|
||
"Lançando": 0,
|
||
"In Arrivo": 0,
|
||
"OnGoing": 0,
|
||
"Đang tiến hành": 0,
|
||
"em lançamento": 0,
|
||
"Онгоінг": 0,
|
||
"Publishing": 0,
|
||
"Curso": 0,
|
||
"En marcha": 0,
|
||
"Publicandose": 0,
|
||
"连载中": 0,
|
||
"Devam Ediyor": 0,
|
||
"Em Andamento": 0,
|
||
"In Corso": 0,
|
||
"Güncel": 0,
|
||
"Emision": 0,
|
||
"En emision": 0,
|
||
"مستمر": 0,
|
||
"ยังไม่จบ": 0,
|
||
"curso": 0,
|
||
"en marcha": 0,
|
||
"publicandose": 0,
|
||
"publicando": 0,
|
||
"devam etmekte": 0,
|
||
"連載中": 0,
|
||
"Đã hoàn thành": 1,
|
||
"مكتملة": 1,
|
||
"Завершено": 1,
|
||
"Complété": 1,
|
||
"Fini": 1,
|
||
"Terminé": 1,
|
||
"Tamamlandı": 1,
|
||
"Tamat": 1,
|
||
"Completado": 1,
|
||
"Concluído": 1,
|
||
"Finished": 1,
|
||
"Completed": 1,
|
||
"Completo": 1,
|
||
"Concluido": 1,
|
||
"已完结": 1,
|
||
"Finalizado": 1,
|
||
"Completata": 1,
|
||
"One-Shot": 1,
|
||
"Bitti": 1,
|
||
"จบแล้ว": 1,
|
||
"tamat": 1,
|
||
"completado": 1,
|
||
"concluído": 1,
|
||
"完結": 1,
|
||
"concluido": 1,
|
||
"bitmiş": 1,
|
||
"hiatus": 2,
|
||
"พักชั่วคราว": 2,
|
||
"on hold": 2,
|
||
"pausado": 2,
|
||
"en espera": 2,
|
||
"en pause": 2,
|
||
"en attente": 2,
|
||
"canceled": 3,
|
||
"cancelled": 3,
|
||
"cancelado": 3,
|
||
"cancellato": 3,
|
||
"cancelados": 3,
|
||
"dropped": 3,
|
||
"discontinued": 3,
|
||
"abandonné": 3,
|
||
},
|
||
];
|
||
|
||
url = getUrlWithoutDomain(url);
|
||
MManga manga = MManga();
|
||
|
||
final res = (await client.get(Uri.parse("$baseUrl$url"))).body;
|
||
final document = parseHtml(res);
|
||
final seriesDetails = document.selectFirst(
|
||
"div.bigcontent, div.animefull, div.main-info, div.postbody",
|
||
);
|
||
manga.author =
|
||
seriesDetails
|
||
.selectFirst(
|
||
".infotable tr:contains(Author) td:last-child, .tsinfo .imptdt:contains(Author) i, .fmed b:contains(Author)+span, span:contains(Author), " +
|
||
".infotable tr:contains(Auteur) td:last-child, .tsinfo .imptdt:contains(Auteur) i, .fmed b:contains(Auteur)+span, span:contains(Auteur), " +
|
||
".infotable tr:contains(autor) td:last-child, .tsinfo .imptdt:contains(autor) i, .fmed b:contains(autor)+span, span:contains(autor), " +
|
||
".infotable tr:contains(المؤلف) td:last-child, .tsinfo .imptdt:contains(المؤلف) i, .fmed b:contains(المؤلف)+span, span:contains(المؤلف), " +
|
||
".infotable tr:contains(Mangaka) td:last-child, .tsinfo .imptdt:contains(Mangaka) i, .fmed b:contains(Mangaka)+span, span:contains(Mangaka), " +
|
||
".infotable tr:contains(seniman) td:last-child, .tsinfo .imptdt:contains(seniman) i, .fmed b:contains(seniman)+span, span:contains(seniman), " +
|
||
".infotable tr:contains(Pengarang) td:last-child, .tsinfo .imptdt:contains(Pengarang) i, .fmed b:contains(Pengarang)+span, span:contains(Pengarang), " +
|
||
".infotable tr:contains(Yazar) td:last-child, .tsinfo .imptdt:contains(Yazar) i, .fmed b:contains(Yazar)+span, span:contains(Yazar), " +
|
||
".infotable tr:contains(ผู้วาด) td:last-child, .tsinfo .imptdt:contains(ผู้วาด) i, .fmed b:contains(ผู้วาด)+span, span:contains(ผู้วาด), ",
|
||
)
|
||
.text;
|
||
|
||
manga.description =
|
||
seriesDetails
|
||
.selectFirst(".desc, .entry-content[itemprop=description]")
|
||
?.text;
|
||
final status =
|
||
seriesDetails
|
||
.selectFirst(
|
||
".infotable tr:contains(status) td:last-child, .tsinfo .imptdt:contains(status) i, .fmed b:contains(status)+span span:contains(status), " +
|
||
".infotable tr:contains(Statut) td:last-child, .tsinfo .imptdt:contains(Statut) i, .fmed b:contains(Statut)+span span:contains(Statut), " +
|
||
".infotable tr:contains(Durum) td:last-child, .tsinfo .imptdt:contains(Durum) i, .fmed b:contains(Durum)+span span:contains(Durum), " +
|
||
".infotable tr:contains(連載状況) td:last-child, .tsinfo .imptdt:contains(連載状況) i, .fmed b:contains(連載状況)+span span:contains(連載状況), " +
|
||
".infotable tr:contains(Estado) td:last-child, .tsinfo .imptdt:contains(Estado) i, .fmed b:contains(Estado)+span span:contains(Estado), " +
|
||
".infotable tr:contains(الحالة) td:last-child, .tsinfo .imptdt:contains(الحالة) i, .fmed b:contains(الحالة)+span span:contains(الحالة), " +
|
||
".infotable tr:contains(حالة العمل) td:last-child, .tsinfo .imptdt:contains(حالة العمل) i, .fmed b:contains(حالة العمل)+span span:contains(حالة العمل), " +
|
||
".infotable tr:contains(สถานะ) td:last-child, .tsinfo .imptdt:contains(สถานะ) i, .fmed b:contains(สถานะ)+span span:contains(สถานะ), " +
|
||
".infotable tr:contains(stato) td:last-child, .tsinfo .imptdt:contains(stato) i, .fmed b:contains(stato)+span span:contains(stato), " +
|
||
".infotable tr:contains(Statüsü) td:last-child, .tsinfo .imptdt:contains(Statüsü) i, .fmed b:contains(Statüsü)+span span:contains(Statüsü), " +
|
||
".infotable tr:contains(สถานะ) td:last-child, .tsinfo .imptdt:contains(สถานะ) i, .fmed b:contains(สถานะ)+span span:contains(สถานะ)",
|
||
)
|
||
?.text ??
|
||
"";
|
||
manga.status = parseStatus(status, statusList);
|
||
manga.genre =
|
||
seriesDetails
|
||
.select(
|
||
"div.gnr a, .mgen a, .seriestugenre a, " +
|
||
"span:contains(genre) , span:contains(التصنيف)",
|
||
)
|
||
.map((e) => e.text)
|
||
.toList();
|
||
final elements = document.select(
|
||
"div.bxcl li, div.cl li, #chapterlist li, ul li:has(div.chbox):has(div.eph-num)",
|
||
);
|
||
List<MChapter>? chaptersList = [];
|
||
for (var element in elements) {
|
||
final urlElements = element.selectFirst("a");
|
||
final name =
|
||
element.selectFirst(".lch a, .chapternum")?.text ?? urlElements.text;
|
||
var chapter = MChapter();
|
||
chapter.name = name;
|
||
chapter.url = urlElements.attr("href");
|
||
chapter.dateUpload =
|
||
parseDates(
|
||
[
|
||
element.selectFirst(".chapterdate")?.text ??
|
||
DateTime.now().millisecondsSinceEpoch.toString(),
|
||
],
|
||
source.dateFormat,
|
||
source.dateFormatLocale,
|
||
)[0];
|
||
chaptersList.add(chapter);
|
||
}
|
||
manga.chapters = chaptersList;
|
||
return manga;
|
||
}
|
||
|
||
@override
|
||
Future<List<String>> getPageList(String url) async {
|
||
url = getUrlWithoutDomain(url);
|
||
final res = (await client.get(Uri.parse('$baseUrl$url'))).body;
|
||
|
||
List<String> pages = [];
|
||
List<String> pagesUrl = [];
|
||
bool invalidImgs = false;
|
||
pages = xpath(res, '//*[@id="readerarea"]/p/img/@src');
|
||
if (pages.isEmpty || pages.length == 1) {
|
||
pages = xpath(res, '//*[@id="readerarea"]/img/@src');
|
||
}
|
||
if (pages.length > 1) {
|
||
for (var page in pages) {
|
||
if (page.contains("data:image")) {
|
||
invalidImgs = true;
|
||
}
|
||
}
|
||
if (invalidImgs) {
|
||
pages = xpath(res, '//*[@id="readerarea"]/img/@data-src');
|
||
}
|
||
}
|
||
if (pages.isEmpty || pages.length == 1) {
|
||
final images = regExp(res, "\"images\"\\s*:\\s*(\\[.*?])", "", 1, 1);
|
||
final pages = json.decode(images) as List;
|
||
for (var page in pages) {
|
||
pagesUrl.add(page);
|
||
}
|
||
} else {
|
||
return pages;
|
||
}
|
||
|
||
return pagesUrl;
|
||
}
|
||
|
||
MPages mangaRes(String res) {
|
||
List<MManga> mangaList = [];
|
||
final document = parseHtml(res);
|
||
final elements = document.select(
|
||
".utao .uta .imgu, .listupd .bs .bsx, .listo .bs .bsx",
|
||
);
|
||
for (var element in elements) {
|
||
String img = element.getSrc;
|
||
if (img.contains("data:image")) {
|
||
img = element.getDataSrc;
|
||
}
|
||
var manga = MManga();
|
||
manga.name = element.selectFirst("a").attr("title");
|
||
manga.imageUrl = img;
|
||
manga.link = element.selectFirst("a").attr("href");
|
||
mangaList.add(manga);
|
||
}
|
||
|
||
return MPages(mangaList, true);
|
||
}
|
||
|
||
@override
|
||
List<dynamic> getFilterList() {
|
||
return ignoreFilter()
|
||
? []
|
||
: [
|
||
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", ""),
|
||
]),
|
||
];
|
||
}
|
||
|
||
@override
|
||
List<dynamic> getSourcePreferences() {
|
||
return [
|
||
EditTextPreference(
|
||
key: "override_baseurl",
|
||
title: "Override BaseUrl",
|
||
summary: "",
|
||
value: source.baseUrl,
|
||
dialogTitle: "Override BaseUrl",
|
||
dialogMessage: "Default: ${source.baseUrl}",
|
||
text: source.baseUrl,
|
||
),
|
||
];
|
||
}
|
||
|
||
String ll(String url) {
|
||
if (url.contains("?")) {
|
||
return "&";
|
||
}
|
||
return "?";
|
||
}
|
||
|
||
String getMangaUrlDirectory(String sourceName) {
|
||
if (["Sushi-Scan"].contains(sourceName)) {
|
||
return "/catalogue";
|
||
}
|
||
return "/manga";
|
||
}
|
||
|
||
String getMangaSearchUrl(int page, String query) {
|
||
if (["Sushi-Scan"].contains(source.name)) {
|
||
return "/page/$page/?s=$query";
|
||
}
|
||
return "/?s=$query&page=$page";
|
||
}
|
||
|
||
bool ignoreFilter() {
|
||
return ["Sushi-Scan"].contains(source.name);
|
||
}
|
||
}
|
||
|
||
MangaReader main(MSource source) {
|
||
return MangaReader(source: source);
|
||
}
|