mirror of
https://github.com/kodjodevf/mangayomi-extensions.git
synced 2026-02-14 02:41:39 +00:00
390 lines
12 KiB
Dart
390 lines
12 KiB
Dart
import 'package:mangayomi/bridge_lib.dart';
|
|
import 'dart:convert';
|
|
|
|
class MangaHere extends MProvider {
|
|
MangaHere({required this.source});
|
|
|
|
MSource source;
|
|
|
|
final Client client = Client();
|
|
|
|
@override
|
|
Future<MPages> getPopular(int page) async {
|
|
final res =
|
|
(await client.get(
|
|
Uri.parse("${source.baseUrl}/directory/$page.htm"),
|
|
headers: getHeader(source.baseUrl),
|
|
)).body;
|
|
|
|
List<MManga> mangaList = [];
|
|
final names = xpath(
|
|
res,
|
|
'//*[ contains(@class, "manga-list-1-list")]/li/a/@title',
|
|
);
|
|
final images = xpath(
|
|
res,
|
|
'//*[ contains(@class, "manga-list-1-list")]/li/a/img[@class="manga-list-1-cover"]/@src',
|
|
);
|
|
final urls = xpath(
|
|
res,
|
|
'//*[ contains(@class, "manga-list-1-list")]/li/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<MPages> getLatestUpdates(int page) async {
|
|
final res =
|
|
(await client.get(
|
|
Uri.parse("${source.baseUrl}/directory/$page.htm?latest"),
|
|
headers: getHeader(source.baseUrl),
|
|
)).body;
|
|
|
|
List<MManga> mangaList = [];
|
|
final names = xpath(
|
|
res,
|
|
'//*[ contains(@class, "manga-list-1-list")]/li/a/@title',
|
|
);
|
|
final images = xpath(
|
|
res,
|
|
'//*[ contains(@class, "manga-list-1-list")]/li/a/img[@class="manga-list-1-cover"]/@src',
|
|
);
|
|
final urls = xpath(
|
|
res,
|
|
'//*[ contains(@class, "manga-list-1-list")]/li/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<MPages> search(String query, int page, FilterList filterList) async {
|
|
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 res =
|
|
(await client.get(
|
|
Uri.parse(url),
|
|
headers: getHeader(source.baseUrl),
|
|
)).body;
|
|
|
|
List<MManga> mangaList = [];
|
|
final names = xpath(
|
|
res,
|
|
'//*[contains(@class, "manga-list-4-list")]/li/a/@title',
|
|
);
|
|
final images = xpath(
|
|
res,
|
|
'//*[contains(@class, "manga-list-4-list")]/li/a/img[@class="manga-list-4-cover"]/@src',
|
|
);
|
|
final urls = xpath(
|
|
res,
|
|
'//*[contains(@class, "manga-list-4-list")]/li/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(String url) async {
|
|
final statusList = [
|
|
{"Ongoing": 0, "Completed": 1},
|
|
];
|
|
final res =
|
|
(await client.get(
|
|
Uri.parse("${source.baseUrl}/$url"),
|
|
headers: getHeader(source.baseUrl),
|
|
)).body;
|
|
MManga manga = MManga();
|
|
manga.author =
|
|
xpath(res, '//*[@class="detail-info-right-say"]/a/text()').first;
|
|
manga.description = xpath(res, '//*[@class="fullcontent"]/text()').first;
|
|
final status =
|
|
xpath(res, '//*[@class="detail-info-right-title-tip"]/text()').first;
|
|
manga.status = parseStatus(status, statusList);
|
|
manga.genre = xpath(
|
|
res,
|
|
'//*[@class="detail-info-right-tag-list"]/a/text()',
|
|
);
|
|
|
|
var chapUrls = xpath(res, '//*[@class="detail-main-list"]/li/a/@href');
|
|
var chaptersNames = xpath(
|
|
res,
|
|
'//*[@class="detail-main-list"]/li/a/div/p[@class="title3"]/text()',
|
|
);
|
|
final chapterDates = xpath(
|
|
res,
|
|
'//*[@class="detail-main-list"]/li/a/div/p[@class="title2"]/text()',
|
|
);
|
|
var 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(String url) async {
|
|
final headers = getHeader(source.baseUrl);
|
|
final urll = "${source.baseUrl}$url";
|
|
final res = (await client.get(Uri.parse(urll), headers: headers)).body;
|
|
final pages = xpath(res, "//body/div/div/span/a/text()");
|
|
List<String> pageUrls = [];
|
|
if (pages.isEmpty) {
|
|
final script = xpath(
|
|
res,
|
|
"//script[contains(text(),'function(p,a,c,k,e,d)')]/text()",
|
|
).first.replaceAll("eval", "");
|
|
String deobfuscatedScript = unpackJs(script);
|
|
int a = deobfuscatedScript.indexOf("newImgs=['") + 10;
|
|
int b = deobfuscatedScript.indexOf("'];");
|
|
List<String> urls = deobfuscatedScript.substring(a, b).split("','");
|
|
for (var url in urls) {
|
|
pageUrls.add("https:$url");
|
|
}
|
|
} else {
|
|
final pagesNumberList = pages;
|
|
int pagesNumber = int.parse(pagesNumberList[pagesNumberList.length - 2]);
|
|
int secretKeyScriptLocation = res.indexOf("eval(function(p,a,c,k,e,d)");
|
|
int secretKeyScriptEndLocation = res.indexOf(
|
|
"</script>",
|
|
secretKeyScriptLocation,
|
|
);
|
|
String secretKeyScript = res
|
|
.substring(secretKeyScriptLocation, secretKeyScriptEndLocation)
|
|
.replaceAll("eval", "");
|
|
String secretKeyDeobfuscatedScript = unpackJs(secretKeyScript);
|
|
int secretKeyStartLoc = secretKeyDeobfuscatedScript.indexOf("'");
|
|
int secretKeyEndLoc = secretKeyDeobfuscatedScript.indexOf(";");
|
|
|
|
String secretKey = secretKeyDeobfuscatedScript.substring(
|
|
secretKeyStartLoc,
|
|
secretKeyEndLoc,
|
|
);
|
|
int chapterIdStartLoc = res.indexOf("chapterid");
|
|
String chapterId = res.substring(
|
|
chapterIdStartLoc + 11,
|
|
res.indexOf(";", chapterIdStartLoc),
|
|
);
|
|
String pageBase = urll.substring(0, urll.lastIndexOf("/"));
|
|
for (int i = 1; i <= pagesNumber; i++) {
|
|
String pageLink =
|
|
"$pageBase/chapterfun.ashx?cid=$chapterId&page=$i&key=$secretKey";
|
|
String responseText = "".toString();
|
|
for (int tr = 1; tr <= 3; tr++) {
|
|
if (responseText.isEmpty) {
|
|
final headers = {
|
|
"Referer": urll,
|
|
"Accept": "*/*",
|
|
"Accept-Language": "en-US,en;q=0.9",
|
|
"Connection": "keep-alive",
|
|
"Host": "www.mangahere.cc",
|
|
"X-Requested-With": "XMLHttpRequest",
|
|
};
|
|
|
|
final ress =
|
|
(await client.get(Uri.parse(pageLink), headers: headers)).body;
|
|
|
|
responseText = ress;
|
|
|
|
if (responseText.isEmpty) {
|
|
secretKey = "";
|
|
}
|
|
}
|
|
}
|
|
String deobfuscatedScript = unpackJs(
|
|
responseText.replaceAll("eval", ""),
|
|
);
|
|
|
|
int baseLinkStartPos = deobfuscatedScript.indexOf("pix=") + 5;
|
|
int baseLinkEndPos =
|
|
deobfuscatedScript.indexOf(";", baseLinkStartPos) - 1;
|
|
String baseLink = deobfuscatedScript.substring(
|
|
baseLinkStartPos,
|
|
baseLinkEndPos,
|
|
);
|
|
|
|
int imageLinkStartPos = deobfuscatedScript.indexOf("pvalue=") + 9;
|
|
int imageLinkEndPos = deobfuscatedScript.indexOf(
|
|
"\"",
|
|
imageLinkStartPos,
|
|
);
|
|
String imageLink = deobfuscatedScript.substring(
|
|
imageLinkStartPos,
|
|
imageLinkEndPos,
|
|
);
|
|
pageUrls.add("https:$baseLink$imageLink");
|
|
}
|
|
}
|
|
|
|
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) {
|
|
final headers = {'Referer': '$url/', "Cookie": "isAdult=1"};
|
|
return headers;
|
|
}
|
|
|
|
MangaHere main(MSource source) {
|
|
return MangaHere(source: source);
|
|
}
|