refactor code

This commit is contained in:
kodjomoustapha 2023-05-12 13:00:16 +01:00
parent a27cd5ae8d
commit f7affe2114
44 changed files with 1992 additions and 1590 deletions

View file

@ -1,5 +1,4 @@
// ignore_for_file: depend_on_referenced_packages
import 'dart:developer';
import 'dart:io';
import 'package:fast_cached_network_image/fast_cached_network_image.dart';
import 'package:flex_color_scheme/flex_color_scheme.dart';
@ -16,7 +15,7 @@ import 'package:mangayomi/models/history.dart';
import 'package:mangayomi/utils/constant.dart';
import 'package:mangayomi/models/manga.dart';
import 'package:mangayomi/router/router.dart';
import 'package:mangayomi/source/source_model.dart';
import 'package:mangayomi/models/source_model.dart';
import 'package:mangayomi/views/manga/reader/providers/reader_controller_provider.dart';
import 'package:mangayomi/views/more/settings/appearance/providers/blend_level_state_provider.dart';
import 'package:mangayomi/views/more/settings/appearance/providers/flex_scheme_color_state_provider.dart';

View file

@ -1,7 +1,7 @@
import 'package:hive_flutter/hive_flutter.dart';
import 'package:mangayomi/models/download_model.dart';
import 'package:mangayomi/utils/constant.dart';
import 'package:mangayomi/source/source_model.dart';
import 'package:mangayomi/models/source_model.dart';
import 'package:mangayomi/views/manga/reader/providers/reader_controller_provider.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';

View file

@ -1,31 +1,28 @@
// ignore_for_file: depend_on_referenced_packages
// ignore_for_file: depend_o
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:html/dom.dart';
import 'package:http/http.dart' as http;
import 'package:html/dom.dart' as dom;
import 'package:mangayomi/models/chapter.dart';
import 'package:mangayomi/models/comick/chapter_page_comick.dart';
import 'package:mangayomi/models/source_model.dart';
import 'package:mangayomi/providers/hive_provider.dart';
import 'package:mangayomi/providers/storage_provider.dart';
import 'package:mangayomi/services/http_service/cloudflare/cloudflare_bypass.dart';
import 'package:mangayomi/services/get_popular_manga.dart';
import 'package:mangayomi/services/http_service/http_service.dart';
import 'package:mangayomi/source/source_model.dart';
import 'package:mangayomi/sources/src/all/comick/src/comick.dart';
import 'package:mangayomi/sources/src/en/mangahere/src/mangahere.dart';
import 'package:mangayomi/sources/src/fr/japscan/src/japscan.dart';
import 'package:mangayomi/sources/src/fr/mangakawaii/src/mangakawaii.dart';
import 'package:mangayomi/sources/src/multi/mangathemesia/src/mangathemesia.dart';
import 'package:mangayomi/sources/src/multi/mmrcms/src/mmrcms.dart';
import 'package:mangayomi/sources/utils/utils.dart';
import 'package:mangayomi/utils/reg_exp_matcher.dart';
import 'package:mangayomi/views/more/settings/providers/incognito_mode_state_provider.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:flutter_js/flutter_js.dart';
import 'package:collection/collection.dart';
part 'get_manga_chapter_url.g.dart';
class GetMangaChapterUrlModel {
Directory? path;
List urll = [];
List pageUrls = [];
List<bool> isLocaleList = [];
GetMangaChapterUrlModel(
{required this.path, required this.urll, required this.isLocaleList});
{required this.path, required this.pageUrls, required this.isLocaleList});
}
@riverpod
@ -33,127 +30,35 @@ Future<GetMangaChapterUrlModel> getMangaChapterUrl(
GetMangaChapterUrlRef ref, {
required Chapter chapter,
}) async {
bool isOk = false;
Directory? path;
List urll = [];
String? baseUrl;
String? zjsUrl;
List pageUrls = [];
final manga = chapter.manga.value!;
zjs() async {
final html = await cloudflareBypassHtml(
url: zjsUrl!, source: manga.source!.toLowerCase(), useUserAgent: true);
dom.Document htmll = dom.Document.html(baseUrl!);
final strings = html
.replaceAll(RegExp(r'\\[(.*?)\\]'), '')
.split(",")
.map((s) => s.trim().replaceAll("'", "").split('').reversed.join());
final stringLookupTables = strings
.where((s) =>
s.length == 62 &&
s.split('').toSet().toList().sorted().join() ==
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz")
.toList();
if (stringLookupTables.length != 2) {
throw Exception("Expected only two lookup tables in ZJS");
}
final scrambledData =
htmll.getElementById("data")!.attributes['data-data']!;
for (var i = 0; i <= 1; i++) {
final otherIndex = i == 0 ? 1 : 0;
final lookupTable = Map.fromIterables(stringLookupTables[i].split(''),
stringLookupTables[otherIndex].split(''));
try {
final unscrambledData = scrambledData
.split('')
.map((char) => lookupTable[char] ?? char)
.join();
final decoded = utf8.decode(base64.decode(unscrambledData));
final data = jsonDecode(decoded);
urll = data["imagesLink"].map((it) => it).toList();
} catch (_) {}
}
isOk = true;
}
List<bool> isLocaleList = [];
String source = manga.source!.toLowerCase();
List pagesUrl = ref.watch(hiveBoxMangaProvider).get(
List hivePagesUrls = ref.watch(hiveBoxMangaProvider).get(
"${manga.lang}-${manga.source}/${manga.name}/${chapter.name}-pageurl",
defaultValue: []);
final incognitoMode = ref.watch(incognitoModeStateProvider);
path = await StorageProvider().getMangaChapterDirectory(chapter);
if (pagesUrl.isNotEmpty) {
urll = pagesUrl;
if (hivePagesUrls.isNotEmpty) {
pageUrls = hivePagesUrls;
}
/*********/
/*comick*/
/********/
else if (getWpMangTypeSource(source) == TypeSource.comick) {
String mangaId = chapter.url!.split('/').last.split('-').first;
final response = await httpGet(
url: 'https://api.comick.fun/chapter/$mangaId?tachiyomi=true',
source: source,
resDom: false) as String?;
var data = jsonDecode(response!) as Map<String, dynamic>;
var page = ChapterPageComick.fromJson(data);
for (var url in page.chapter!.images!) {
urll.add(url.url);
}
else if (getWpMangTypeSource(source) == TypeSource.comick) {
pageUrls = await Comick().getMangaChapterUrl(chapter: chapter);
}
/*************/
/*mangathemesia*/
/**************/
else if (getWpMangTypeSource(source) == TypeSource.mangathemesia) {
final dom = await httpGet(
useUserAgent: true,
url: chapter.url!,
source: source,
resDom: true) as Document?;
if (dom!.querySelectorAll('#readerarea').isNotEmpty) {
final ta =
dom.querySelectorAll('#readerarea').map((e) => e.outerHtml).toList();
final RegExp regex = RegExp(r'<img[^>]+src="([^"]+)"');
final Iterable<Match> matches = regex.allMatches(ta.first);
final List<String?> urls = matches.map((m) => m.group(1)).toList();
Iterable<Match> matchess = [];
if (dom.querySelectorAll(' #select-paged ').isNotEmpty) {
final ee = dom
.querySelectorAll(' #select-paged ')
.map((e) => e.outerHtml)
.toList();
final RegExp regexx = RegExp(r'value="([^"]+)"');
matchess = regexx.allMatches(ee.first);
}
final List<String?> urlss = matchess.map((m) => m.group(1)).toList();
if (urls.length == 1 && urls.isNotEmpty) {
for (var i = 0; i < urlss.length; i++) {
if (urlss[i]!.length == 1) {
urll.add(
urls.first!.replaceAll("001", '00${int.parse(urlss[i]!) + 1}'));
} else if (urlss[i]!.length == 2) {
urll.add(
urls.first!.replaceAll("001", '0${int.parse(urlss[i]!) + 1}'));
} else if (urlss[i]!.length == 3) {
urll.add(
urls.first!.replaceAll("001", '${int.parse(urlss[i]!) + 1}'));
}
}
} else if (urls.length > 1 && urls.isNotEmpty) {
for (var tt in urls) {
urll.add(tt);
}
}
}
pageUrls = await MangaThemeSia().getMangaChapterUrl(chapter: chapter);
}
/***********/
@ -161,25 +66,7 @@ Future<GetMangaChapterUrlModel> getMangaChapterUrl(
/***********/
else if (source == 'mangakawaii') {
final response =
await httpGet(url: chapter.url!, source: source, resDom: false)
as String?;
var chapterSlug = RegExp("""var chapter_slug = "([^"]*)";""")
.allMatches(response!)
.last
.group(1);
var mangaSlug = RegExp("""var oeuvre_slug = "([^"]*)";""")
.allMatches(response)
.last
.group(1);
var pages = RegExp('''"page_image":"([^"]*)"''')
.allMatches(response)
.map((e) => e.group(1));
for (var tt in pages) {
urll.add(
'https://cdn.mangakawaii.pics/uploads/manga/$mangaSlug/chapters_fr/$chapterSlug/$tt');
}
pageUrls = await MangaKawaii().getMangaChapterUrl(chapter: chapter);
}
/***********/
@ -187,168 +74,32 @@ Future<GetMangaChapterUrlModel> getMangaChapterUrl(
/***********/
else if (getWpMangTypeSource(source) == TypeSource.mmrcms) {
final dom = await httpGet(
useUserAgent: true,
url: chapter.url!,
source: source,
resDom: true) as Document?;
if (dom!.querySelectorAll('#all > .img-responsive').isNotEmpty) {
urll = dom.querySelectorAll('#all > .img-responsive').map((e) {
final RegExp regexx = RegExp(r'data-src="([^"]+)"');
if (manga.source!.toLowerCase() == 'jpmangas' ||
manga.source!.toLowerCase() == 'fr scan') {
return regexx
.allMatches(e.outerHtml)
.first
.group(1)!
.replaceAll('//', 'https://')
.replaceAll(RegExp(r"\s+\b|\b\s"), "");
}
return regexx
.allMatches(e.outerHtml)
.first
.group(1)!
.replaceAll(RegExp(r"\s+\b|\b\s"), "");
}).toList();
}
pageUrls = await Mmrcms().getMangaChapterUrl(chapter: chapter);
}
/***********/
/*mangahere*/
/***********/
else if (source == 'mangahere') {
JavascriptRuntime? flutterJs;
flutterJs = getJavascriptRuntime();
extractSecretKey(String response, JavascriptRuntime? flutterJs) {
var secretKeyScriptLocation =
response.indexOf("eval(function(p,a,c,k,e,d)");
var secretKeyScriptEndLocation =
response.indexOf("</script>", secretKeyScriptLocation);
var secretKeyScript = response
.substring(secretKeyScriptLocation, secretKeyScriptEndLocation)
.replaceAll("eval", "");
var secretKeyDeobfuscatedScript =
flutterJs!.evaluate(secretKeyScript).toString();
var secretKeyStartLoc = secretKeyDeobfuscatedScript.indexOf("'");
var secretKeyEndLoc = secretKeyDeobfuscatedScript.indexOf(";");
var secretKeyResultScript = secretKeyDeobfuscatedScript.substring(
secretKeyStartLoc, secretKeyEndLoc);
return secretKeyResultScript;
}
var link = "http://www.mangahere.cc${chapter.url!}";
final response =
await httpGet(url: link, source: source, resDom: false) as String?;
dom.Document htmll = dom.Document.html(response!);
int? pagesNumber = -1;
if (htmll.querySelectorAll('body > div > div > span > a:').isNotEmpty) {
final ta = htmll
.querySelectorAll('body > div > div > span > a:')
.map((e) => e.text.trim())
.toList();
ta.removeLast();
pagesNumber = int.parse(ta.last);
}
if (pagesNumber == -1) {
final script = htmll
.getElementsByTagName("script")
.firstWhere((e) => e.innerHtml.contains(
"function(p,a,c,k,e,d)",
))
.innerHtml
.replaceAll("eval", "");
String deobfuscatedScript = flutterJs.evaluate(script).toString();
List<String> urlss = deobfuscatedScript
.substring(
deobfuscatedScript.indexOf("newImgs=['") + "newImgs=['".length,
deobfuscatedScript.indexOf("'];"))
.split("','");
for (var tt in urlss) {
urll.add("https:$tt");
}
} else {
var secretKey = extractSecretKey(response, flutterJs);
var chapterIdStartLoc = response.indexOf("chapterid");
var chapterId = response
.substring(
chapterIdStartLoc + 11, response.indexOf(";", chapterIdStartLoc))
.trim();
var pageBase = link.substring(0, link.lastIndexOf("/"));
for (int i = 1; i <= pagesNumber; i++) {
var pageLink =
"$pageBase/chapterfun.ashx?cid=$chapterId&page=$i&key=$secretKey";
var responseText = "";
for (int tr = 1; tr <= 3; tr++) {
var response = await http.get(Uri.parse(pageLink), headers: {
"Referer": link,
"Accept": "*/*",
"Accept-Language": "en-US,en;q=0.9",
"Connection": "keep-alive",
"Host": "www.mangahere.cc",
"X-Requested-With": "XMLHttpRequest"
});
responseText = response.body;
if (responseText.isNotEmpty) {
break;
} else {
secretKey = "";
}
}
var deobfuscatedScript =
flutterJs.evaluate(responseText.replaceAll("eval", "")).toString();
var baseLinkStartPos = deobfuscatedScript.indexOf("pix=") + 5;
var baseLinkEndPos =
deobfuscatedScript.indexOf(";", baseLinkStartPos) - 1;
var baseLink =
deobfuscatedScript.substring(baseLinkStartPos, baseLinkEndPos);
var imageLinkStartPos = deobfuscatedScript.indexOf("pvalue=") + 9;
var imageLinkEndPos =
deobfuscatedScript.indexOf("\"", imageLinkStartPos);
var imageLink =
deobfuscatedScript.substring(imageLinkStartPos, imageLinkEndPos);
urll.add("https:$baseLink$imageLink");
}
flutterJs.dispose();
}
} else if (source == 'japscan') {
final response = await httpGet(
useUserAgent: true,
url: chapter.url!,
source: source,
resDom: false) as String?;
RegExp regex = RegExp(r'<script src="/zjs/(.*?)"');
Match? match = regex.firstMatch(response!);
String zjsurl = match!.group(1)!;
baseUrl = response;
zjsUrl = "https://www.japscan.lol/zjs/$zjsurl";
zjs();
await Future.doWhile(() async {
await Future.delayed(const Duration(seconds: 1));
if (isOk == true) {
return false;
}
return true;
});
pageUrls = await Mangahere().getMangaChapterUrl(chapter: chapter);
}
if (urll.isNotEmpty) {
/***********/
/*japscan*/
/***********/
else if (source == 'japscan') {
pageUrls = await Japscan().getMangaChapterUrl(chapter: chapter);
}
if (pageUrls.isNotEmpty) {
if (!incognitoMode) {
ref.watch(hiveBoxMangaProvider).put(
"${manga.lang}-${manga.source}/${manga.name}/${chapter.name}-pageurl",
urll);
pageUrls);
}
for (var i = 0; i < urll.length; i++) {
for (var i = 0; i < pageUrls.length; i++) {
if (await File("${path!.path}" "${padIndex(i + 1)}.jpg").exists()) {
isLocaleList.add(true);
} else {
@ -358,5 +109,5 @@ Future<GetMangaChapterUrlModel> getMangaChapterUrl(
}
return GetMangaChapterUrlModel(
path: path, urll: urll, isLocaleList: isLocaleList);
path: path, pageUrls: pageUrls, isLocaleList: isLocaleList);
}

View file

@ -7,7 +7,7 @@ part of 'get_manga_chapter_url.dart';
// **************************************************************************
String _$getMangaChapterUrlHash() =>
r'bb23f4789336a4970dddaaf61707998a9c79bcb5';
r'4daf1dc296255c64ca0ee2cf963dd98b3595d93c';
/// Copied from Dart SDK
class _SystemHash {

View file

@ -1,89 +1,16 @@
import 'dart:async';
import 'dart:convert';
import 'dart:developer';
import 'package:html/dom.dart';
import 'package:http/http.dart' as http;
import 'package:intl/intl.dart';
import 'package:mangayomi/models/chapter.dart';
import 'package:mangayomi/models/comick/manga_chapter_detail.dart';
import 'package:mangayomi/models/comick/manga_detail_comick.dart';
import 'package:mangayomi/services/get_popular_manga.dart';
import 'package:mangayomi/services/http_service/http_res_to_dom_html.dart';
import 'package:mangayomi/services/http_service/http_service.dart';
import 'package:mangayomi/source/source_model.dart';
import 'package:mangayomi/utils/headers.dart';
import 'package:mangayomi/utils/reg_exp_matcher.dart';
import 'package:mangayomi/models/source_model.dart';
import 'package:mangayomi/sources/service/service.dart';
import 'package:mangayomi/sources/src/all/comick/src/comick.dart';
import 'package:mangayomi/sources/src/en/mangahere/src/mangahere.dart';
import 'package:mangayomi/sources/src/fr/japscan/src/japscan.dart';
import 'package:mangayomi/sources/src/fr/mangakawaii/src/mangakawaii.dart';
import 'package:mangayomi/sources/src/multi/mangathemesia/src/mangathemesia.dart';
import 'package:mangayomi/sources/src/multi/mmrcms/src/mmrcms.dart';
import 'package:mangayomi/sources/utils/utils.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'get_manga_detail.g.dart';
class GetMangaDetailModel {
List<String> genre = [];
List<Chapter> chapters = [];
String? author;
String? status;
String? source;
String? url;
String? name;
String? imageUrl;
String? description;
GetMangaDetailModel({
required this.genre,
required this.author,
required this.status,
required this.chapters,
required this.imageUrl,
required this.description,
required this.url,
required this.name,
required this.source,
});
}
_parseStatut(int i) {
if (i == 1) {
return 'Ongoing';
} else if (i == 2) {
return 'Completed';
} else if (i == 3) {
return 'Canceled';
} else if (i == 4) {
return '';
} else {
return 'Unknown';
}
}
Future findCurrentSlug(String oldSlug) async {
var request = http.Request('GET',
Uri.parse('https://api.comick.fun/tachiyomi/mapping?slugs=$oldSlug'));
request.headers.addAll(headers("comick"));
http.StreamedResponse response = await request.send();
if (response.statusCode == 200) {
return await response.stream.bytesToString();
} else {
return response.reasonPhrase;
}
}
beautifyChapterName(String? vol, String? chap, String? title, String? lang) {
return "${vol!.isNotEmpty ? chap!.isEmpty ? "Volume $vol " : "Vol. $vol " : ""}${chap!.isNotEmpty ? vol.isEmpty ? lang == "fr" ? "Chapitre $chap" : "Chapter $chap" : "Ch. $chap " : ""}${title!.isNotEmpty ? chap.isEmpty ? title : " : $title" : ""}";
}
String utilDate(String data) {
DateTime date = DateTime.parse(data);
return date.millisecondsSinceEpoch.toString();
}
parseDate(String data, String source) {
source = source.toLowerCase();
DateTime date = DateFormat(getFormatDate(source), getFormatDateLocale(source))
.parse(data);
return date.millisecondsSinceEpoch.toString();
}
@riverpod
Future<GetMangaDetailModel> getMangaDetail(GetMangaDetailRef ref,
{required String imageUrl,
@ -91,305 +18,30 @@ Future<GetMangaDetailModel> getMangaDetail(GetMangaDetailRef ref,
required String title,
required String lang,
required String source}) async {
List<String> genre = [];
String? author;
String? status;
List<String> chapterTitle = [];
List<String> chapterUrl = [];
List<String> chapterDate = [];
String? description;
List<Chapter> chapters = [];
List<String> scanlators = [];
GetMangaDetailModel? mangadetail;
/********/
/*comick*/
/********/
if (getWpMangTypeSource(source.toLowerCase()) == TypeSource.comick) {
final response = await httpGet(
url: 'https://api.comick.fun$url?tachiyomi=true',
source: source,
resDom: false) as String?;
var mangaDetail = jsonDecode(response!) as Map<String, dynamic>;
var mangaDetailLMap = MangaDetailModelComick.fromJson(mangaDetail);
RegExp regExp = RegExp(r'name:\s*(.*?),');
String authorr =
regExp.firstMatch(mangaDetailLMap.authors![0].toString())?.group(1) ??
'';
String statuss = _parseStatut(mangaDetailLMap.comic!.status!);
status = statuss;
author = authorr;
RegExp regExp1 = RegExp(r'name:\s*(.*?)}');
for (var ok in mangaDetailLMap.genres!) {
genre.add(regExp1.firstMatch(ok.toString())!.group(1)!);
}
description = mangaDetailLMap.comic!.desc;
String tt = await findCurrentSlug(mangaDetailLMap.comic!.slug!);
String mangaId = tt.split('":"').last.replaceAll('"}', '');
String limit = mangaDetailLMap.comic!.chapterCount.toString();
final responsee = await httpGet(
url:
'https://api.comick.fun/comic/$mangaId/chapters?lang=$lang&limit=$limit',
source: source,
resDom: false) as String?;
var chapterDetail = jsonDecode(responsee!) as Map<String, dynamic>;
var chapterDetailMap = MangaChapterModelComick.fromJson(chapterDetail);
for (var chapter in chapterDetailMap.chapters!) {
scanlators.add(chapter.groupName!.isNotEmpty
? chapter.groupName!.first.toString() != 'null'
? chapter.groupName!.first
: ""
: "");
chapterUrl.add(
"/comic/${mangaDetailLMap.comic!.slug}/${chapter.hid}-chapter-${chapter.chap}-en");
chapterDate.add(parseDate(chapter.createdAt!, source));
chapterTitle.add(beautifyChapterName(
chapter.vol ?? "", chapter.chap ?? "", chapter.title ?? "", lang));
}
mangadetail = await Comick().getMangaDetail(
imageUrl: imageUrl, url: url, title: title, lang: lang, source: source);
}
/*************/
/*mangathemesia*/
/**************/
if (getWpMangTypeSource(source.toLowerCase()) == TypeSource.mangathemesia) {
final dom = await httpGet(
url: url,
source: source,
resDom: true,
useUserAgent: true) as Document?;
if (dom!
.querySelectorAll(
'div.bigcontent, div.animefull, div.main-info, div.postbody')
.isNotEmpty) {
final resHtml = dom.querySelector(
'div.bigcontent, div.animefull, div.main-info, div.postbody');
if (resHtml!.querySelectorAll('.tsinfo .imptdt').isNotEmpty) {
status = resHtml
.querySelectorAll('.tsinfo .imptdt')
.where((e) =>
e.innerHtml.contains("Status") ||
e.innerHtml.contains("Situação"))
.map((e) => e.innerHtml.contains("Situação")
? e.text.replaceAll('Situação', '').trim()
: e.text.replaceAll('Status', '').trim())
.toList()
.last;
} else if (resHtml.querySelectorAll('.infotable tr').isNotEmpty) {
status = resHtml
.querySelectorAll('.infotable tr')
.where((e) =>
e.innerHtml.toLowerCase().contains('statut') ||
e.innerHtml.toLowerCase().contains('status'))
.map((e) => e.querySelector('td:last-child')!.text)
.toList()
.first;
} else if (resHtml.querySelectorAll('.fmed').isNotEmpty) {
status = resHtml
.querySelectorAll('.tsinfo .imptdt')
.map((e) => e.text.replaceAll('Status', '').trim())
.toList()
.first;
} else {
status = "";
}
//2
if (resHtml.querySelectorAll('.fmed').isNotEmpty) {
author = resHtml
.querySelectorAll('.fmed')
.where((e) => e.innerHtml.contains("Author"))
.map((e) => e.text.replaceAll('Author', '').trim())
.toList()
.first;
} else if (resHtml.querySelectorAll('.tsinfo .imptdt').isNotEmpty) {
author = resHtml
.querySelectorAll('.tsinfo .imptdt')
.where((e) =>
e.innerHtml.contains("Author") ||
e.innerHtml.contains("Auteur") ||
e.innerHtml.contains("Autor"))
.map((e) => e.innerHtml.contains("Autor")
? e.text.replaceAll('Autor', '').trim()
: e.innerHtml.contains("Autheur")
? e.text.replaceAll('Auteur', '').trim()
: e.text.replaceAll('Author', '').trim())
.toList()
.first;
} else if (resHtml.querySelectorAll('.infotable tr').isNotEmpty) {
author = resHtml
.querySelectorAll('.infotable tr')
.where((e) =>
e.innerHtml.toLowerCase().contains('auteur') ||
e.innerHtml.toLowerCase().contains('author'))
.map((e) => e.querySelector('td:last-child')!.text)
.toList()
.first;
} else {
author = "";
}
description = resHtml
.querySelector(".desc, .entry-content[itemprop=description]")!
.text;
if (resHtml
.querySelectorAll('div.gnr a, .mgen a, .seriestugenre a')
.isNotEmpty) {
final tt = resHtml
.querySelectorAll('div.gnr a, .mgen a, .seriestugenre a')
.map((e) => e.text.trim())
.toList();
for (var ok in tt) {
genre.add(ok);
}
} else {
genre.add('');
}
if (resHtml.querySelectorAll('#chapterlist a').isNotEmpty) {
final udl = resHtml
.querySelectorAll('#chapterlist a ')
.where((e) => e.attributes.containsKey('href'))
.map((e) => e.attributes['href'])
.toList();
for (var ok in udl) {
chapterUrl.add(ok!);
}
}
if (resHtml.querySelectorAll('.lch a, .chapternum').isNotEmpty) {
final tt = resHtml
.querySelectorAll('.lch a, .chapternum')
.map((e) => e.text.trim())
.toList();
tt.removeWhere((element) => element.contains('{{number}}'));
for (var ok in tt) {
chapterTitle.add(ok.trimLeft());
}
}
if (resHtml.querySelectorAll('.chapterdate').isNotEmpty) {
final tt = resHtml
.querySelectorAll('.chapterdate')
.map((e) => e.text.trim())
.toList();
tt.removeWhere((element) => element.contains('{{date}}'));
for (var ok in tt) {
chapterDate.add(parseDate(ok, source));
}
}
}
mangadetail = await MangaThemeSia().getMangaDetail(
imageUrl: imageUrl, url: url, title: title, lang: lang, source: source);
}
/***********/
/*mangakawaii*/
/***********/
else if (source.toLowerCase() == "mangakawaii") {
final dom = await httpGet(
url: 'https://www.mangakawaii.io$url',
source: source,
resDom: true) as Document?;
List detail = [];
imageUrl =
"https://cdn.mangakawaii.pics/uploads$url/cover/cover_250x350.jpg";
if (dom!.querySelectorAll('dd.text-justify.text-break').isNotEmpty) {
final tt = dom
.querySelectorAll('dd.text-justify.text-break')
.map((e) => e.text.trim())
.toList();
description = tt[0];
}
if (dom
.querySelectorAll('span.badge.bg-success.text-uppercase')
.isNotEmpty) {
final tt = dom
.querySelectorAll('span.badge.bg-success.text-uppercase')
.map((e) => e.text.trim())
.toList();
detail.add(tt[0]);
} else {
detail.add("");
}
if (dom.querySelectorAll('a[href*=author]').isNotEmpty) {
final tt = dom
.querySelectorAll('a[href*=author]')
.map((e) => e.text.trim())
.toList();
detail.add(tt[0]);
} else {
detail.add("");
}
if (dom.querySelectorAll('a[href*=category]').isNotEmpty) {
final tt = dom
.querySelectorAll('a[href*=category]')
.map((e) => e.text.trim())
.toList();
for (var ok in tt) {
genre.add(ok);
}
}
detail = detail.toSet().toList();
status = detail[0];
author = detail[1];
if (dom.querySelectorAll("tr[class*='volume-']").isNotEmpty) {
final url = dom.querySelectorAll("tr[class*='volume-']").map((e) {
RegExp exp = RegExp(r'<a href="([^"]+)"');
Iterable<Match> matches = exp.allMatches(e.outerHtml);
String? firstMatch = matches.first.group(1);
return firstMatch;
}).toList();
final htm = await httpResToDom(
url: 'https://www.mangakawaii.io${url[0]}',
headers: {"Accept-Language": "fr"});
if (htm
.querySelectorAll(
'#bottom_nav_reader > div > div > ul.chapter-pager.navbar-nav > li.nav-item.dropup.d-inline-block > ul > li > a')
.isNotEmpty) {
final tt = htm
.querySelectorAll(
'#bottom_nav_reader > div > div > ul.chapter-pager.navbar-nav > li.nav-item.dropup.d-inline-block > ul > li > a')
.map((e) => e.innerHtml)
.toList();
final urlz = htm
.querySelectorAll(
"#bottom_nav_reader > div > div > ul.chapter-pager.navbar-nav > li.nav-item.dropup.d-inline-block > ul > li ")
.map((e) => e.innerHtml)
.toList();
for (var ok in urlz) {
chapterUrl.add(ok.split('href="')[1].split('"').first);
}
for (var ok in tt) {
chapterTitle.add(ok);
}
if (dom.querySelectorAll("tr[class*='volume-']").isNotEmpty) {
final url = dom
.querySelectorAll("tr[class*='volume-']")
.map((e) => e
.querySelectorAll('td.table__date')
.map((e) => e.text.trim())
.toList()[0])
.toList();
if (urlz.length > url.length) {
for (var _ in urlz) {
chapterDate.add(parseDate(
"${DateTime.now().day}.${DateTime.now().month}.${DateTime.now().year}",
source));
}
} else {
for (var ok in url) {
chapterDate.add(parseDate(
ok.split(" ").first.toString().substring(0, 10), source));
}
}
}
}
}
mangadetail = await MangaKawaii().getMangaDetail(
imageUrl: imageUrl, url: url, title: title, lang: lang, source: source);
}
/***********/
@ -397,293 +49,25 @@ Future<GetMangaDetailModel> getMangaDetail(GetMangaDetailRef ref,
/***********/
else if (getWpMangTypeSource(source.toLowerCase()) == TypeSource.mmrcms) {
final dom =
await httpGet(url: url, source: source, resDom: true) as Document?;
description = dom!
.querySelectorAll('.row .well p')
.map((e) => e.text.trim())
.toList()
.first;
status = dom
.querySelectorAll('.row .dl-horizontal dt')
.where((e) =>
e.innerHtml.toString().toLowerCase().contains("status") ||
e.innerHtml.toString().toLowerCase().contains("statut") ||
e.innerHtml.toString().toLowerCase().contains("estado") ||
e.innerHtml.toString().toLowerCase().contains("durum"))
.map((e) => e.nextElementSibling!.text.trim())
.toList()
.first;
if (dom.querySelectorAll(".row .dl-horizontal dt").isNotEmpty) {
author = dom
.querySelectorAll('.row .dl-horizontal dt')
.where((e) =>
e.innerHtml.toString().toLowerCase().contains("auteur(s)") ||
e.innerHtml.toString().toLowerCase().contains("author(s)") ||
e.innerHtml.toString().toLowerCase().contains("autor(es)") ||
e.innerHtml.toString().toLowerCase().contains("yazar(lar)") ||
e.innerHtml.toString().toLowerCase().contains("mangaka(lar)") ||
e.innerHtml
.toString()
.toLowerCase()
.contains("pengarang/penulis") ||
e.innerHtml.toString().toLowerCase().contains("autor") ||
e.innerHtml.toString().toLowerCase().contains("penulis"))
.map((e) => e.nextElementSibling!.text
.trim()
.replaceAll(RegExp(r"\s+\b|\b\s"), ""))
.toList()
.first;
final genr = dom
.querySelectorAll('.row .dl-horizontal dt')
.where((e) =>
e.innerHtml.toString().toLowerCase().contains("categories") ||
e.innerHtml.toString().toLowerCase().contains("categorías") ||
e.innerHtml.toString().toLowerCase().contains("catégories") ||
e.innerHtml.toString().toLowerCase().contains("kategoriler") ||
e.innerHtml.toString().toLowerCase().contains("categorias") ||
e.innerHtml.toString().toLowerCase().contains("kategorie") ||
e.innerHtml.toString().toLowerCase().contains("kategori") ||
e.innerHtml.toString().toLowerCase().contains("tagi"))
.map((e) => e.nextElementSibling!.text.trim())
.toList();
if (genr.isNotEmpty) {
genre = genr.first.replaceAll(RegExp(r"\s+\b|\b\s"), "").split(',');
}
}
final rrr = dom.querySelectorAll(".row [class^=img-responsive]");
final data = rrr.map((e) => e.outerHtml).toList();
if (source.toLowerCase() == 'jpmangas' ||
source.toLowerCase() == 'fr scan') {
imageUrl = regSrcMatcher(data.first).replaceAll('//', 'https://');
} else {
imageUrl = regSrcMatcher(data.first);
}
final ttt = dom
.querySelectorAll("ul[class^=chapters] > li:not(.btn), table.table tr");
if (ttt.isNotEmpty) {
final data = ttt
.map((e) => e.querySelector("[class^=chapter-title-rtl]")!)
.toList();
var name = data;
for (var iaa in name) {
chapterTitle.add(iaa.getElementsByTagName("a").first.text);
chapterUrl
.add(regHrefMatcher(iaa.getElementsByTagName("a").first.outerHtml));
}
final date = ttt
.map((e) => e
.getElementsByClassName("date-chapter-title-rtl")
.map((e) => e.text.trim())
.first)
.toList();
for (var da in date) {
chapterDate.add(parseDate(da, source));
}
}
mangadetail = await Mmrcms().getMangaDetail(
imageUrl: imageUrl, url: url, title: title, lang: lang, source: source);
}
/***********/
/*mangahere*/
/***********/
else if (source.toLowerCase() == "mangahere") {
final dom = await httpGet(
url: "http://www.mangahere.cc$url",
source: source,
resDom: true) as Document?;
if (dom!
.querySelectorAll(
' body > div > div > div.detail-info-right > p.detail-info-right-title > span.detail-info-right-title-tip')
.isNotEmpty) {
final tt = dom
.querySelectorAll(
' body > div > div > div.detail-info-right > p.detail-info-right-title > span.detail-info-right-title-tip')
.map((e) => e.text.trim())
.toList();
status = tt[0];
} else {
status = "";
}
if (dom
.querySelectorAll(
' body > div > div > div.detail-info-right > p.detail-info-right-say > a')
.isNotEmpty) {
final tt = dom
.querySelectorAll(
' body > div > div > div.detail-info-right > p.detail-info-right-say > a')
.map((e) => e.text.trim())
.toList();
author = tt[0];
} else {
author = "";
}
if (dom
.querySelectorAll(
'body > div > div > div.detail-info-right > p.detail-info-right-content')
.isNotEmpty) {
final tt = dom
.querySelectorAll(
'body > div > div > div.detail-info-right > p.detail-info-right-content')
.map((e) => e.text.trim())
.toList();
description = tt.first;
}
if (dom.querySelectorAll('ul > li > a').isNotEmpty) {
final udl = dom
.querySelectorAll('ul > li > a ')
.where((e) => e.attributes.containsKey('href'))
.map((e) => e.attributes['href'])
.toList();
for (var ok in udl) {
chapterUrl.add(ok!);
}
}
if (dom.querySelectorAll('ul > li > a > div > p.title3').isNotEmpty) {
final tt = dom
.querySelectorAll('ul > li > a > div > p.title3')
.map((e) => e.text.trim())
.toList();
for (var ok in tt) {
chapterTitle.add(ok);
}
}
if (dom.querySelectorAll('ul > li > a > div > p.title2').isNotEmpty) {
final tt = dom
.querySelectorAll('ul > li > a > div > p.title2')
.map((e) => e.text.trim())
.toList();
for (var ok in tt) {
chapterDate.add(parseDate(ok, source));
}
}
if (dom
.querySelectorAll(
' body > div > div > div.detail-info-right > p.detail-info-right-tag-list > a')
.isNotEmpty) {
final tt = dom
.querySelectorAll(
' body > div > div > div.detail-info-right > p.detail-info-right-tag-list > a')
.map((e) => e.text.trim())
.toList();
for (var ok in tt) {
genre.add(ok);
}
}
} else if (source.toLowerCase() == "japscan") {
final dom =
await httpGet(url: url, source: source, resDom: true) as Document?;
if (dom!.querySelectorAll('.col-7 > p').isNotEmpty) {
final images =
dom.querySelectorAll('.col-5 ').map((e) => e.outerHtml).toList();
RegExp exp = RegExp(r'src="([^"]+)"');
String? srcValue = exp.firstMatch(images[0])?.group(1);
imageUrl = 'https://www.japscan.lol$srcValue';
if (dom.querySelectorAll('.col-7 > p').isNotEmpty) {
final stat = dom
.querySelectorAll('.col-7 > p')
.where((element) => element.innerHtml.contains('Statut:'))
.map((e) => e.text)
.toList();
if (stat.isNotEmpty) {
status = stat[0].replaceAll('Statut:', '').trim();
}
final auth = dom
.querySelectorAll('.col-7 > p')
.where((element) => element.innerHtml.contains('Auteur(s):'))
.map((e) => e.text)
.toList();
if (auth.isNotEmpty) {
author = auth[0].replaceAll('Auteur(s):', '').trim();
}
} else {
author = "";
status = "";
}
final genres = dom
.querySelectorAll('.col-7 > p')
.where((element) => element.innerHtml.contains('Genre(s):'))
.map((e) => e.text.replaceAll('Genre(s):', '').trim())
.toList();
if (genres.isNotEmpty) {
for (var ok in genres[0].split(',')) {
genre.add(ok);
}
}
final synop = dom
.querySelectorAll('p.list-group-item ')
.map((e) => e.text.trim())
.toList();
if (synop.isNotEmpty) {
description = synop[0];
}
}
final urls =
dom.querySelectorAll('.col-8 ').map((e) => e.outerHtml).toList();
for (var ok in urls) {
RegExp exp = RegExp(r'href="([^"]+)"');
String? srcValue = exp.firstMatch(ok)?.group(1);
chapterUrl.add('https://www.japscan.lol$srcValue');
}
final chapterTitlee =
dom.querySelectorAll('.col-8').map((e) => e.text.trim()).toList();
if (chapterTitlee.isNotEmpty) {
for (var ok in chapterTitlee) {
chapterTitle.add(ok);
}
}
final chapterDatee =
dom.querySelectorAll('.col-4').map((e) => e.text.trim()).toList();
if (chapterDatee.isNotEmpty) {
for (var ok in chapterDatee) {
chapterDate.add(parseDate(ok, source));
}
}
}
if (chapterDate.isNotEmpty &&
chapterTitle.isNotEmpty &&
chapterUrl.isNotEmpty) {
for (var i = 0; i < chapterUrl.length; i++) {
chapters.add(Chapter(
name: chapterTitle[i],
url: chapterUrl[i],
dateUpload: chapterDate[i],
isBookmarked: false,
scanlator: scanlators.isEmpty ? "" : scanlators[i],
isRead: false,
lastPageRead: '',
mangaId: null));
}
mangadetail = await Mangahere().getMangaDetail(
imageUrl: imageUrl, url: url, title: title, lang: lang, source: source);
}
return GetMangaDetailModel(
status: status,
genre: genre,
author: author,
description: description,
name: title,
url: url,
source: source,
imageUrl: imageUrl,
chapters: chapters,
);
/***********/
/*japscan*/
/***********/
else if (source.toLowerCase() == "japscan") {
mangadetail = await Japscan().getMangaDetail(
imageUrl: imageUrl, url: url, title: title, lang: lang, source: source);
}
return mangadetail!;
}

View file

@ -6,7 +6,7 @@ part of 'get_manga_detail.dart';
// RiverpodGenerator
// **************************************************************************
String _$getMangaDetailHash() => r'42e2ad9fac5c3a0db0a6aefe8f77f2c97eaaf235';
String _$getMangaDetailHash() => r'989fdfaf80bf546d1f9910b0715afc9a0e64f33a';
/// Copied from Dart SDK
class _SystemHash {

View file

@ -1,171 +1,43 @@
// ignore_for_file: unused_local_variable
import 'dart:async';
import 'dart:convert';
import 'package:html/dom.dart';
import 'package:mangayomi/models/comick/popular_manga_comick.dart';
import 'package:mangayomi/services/http_service/http_service.dart';
import 'package:mangayomi/source/source_list.dart';
import 'package:mangayomi/source/source_model.dart';
import 'package:mangayomi/models/source_model.dart';
import 'package:mangayomi/sources/service/service.dart';
import 'package:mangayomi/sources/src/all/comick/src/comick.dart';
import 'package:mangayomi/sources/src/en/mangahere/src/mangahere.dart';
import 'package:mangayomi/sources/src/fr/japscan/src/japscan.dart';
import 'package:mangayomi/sources/src/fr/mangakawaii/src/mangakawaii.dart';
import 'package:mangayomi/sources/src/multi/mangathemesia/src/mangathemesia.dart';
import 'package:mangayomi/sources/src/multi/mmrcms/src/mmrcms.dart';
import 'package:mangayomi/sources/utils/utils.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'get_popular_manga.g.dart';
class GetMangaModel {
late List<String?> url;
late List<String?> name;
late List<String?> image;
GetMangaModel({
required this.name,
required this.url,
required this.image,
});
}
String getWpMangaUrl(String source) {
String url = "";
for (var i = 0; i < sourcesList.length; i++) {
if (sourcesList[i].sourceName.toLowerCase() == source.toLowerCase()) {
url = sourcesList[i].url;
}
}
return url;
}
TypeSource getWpMangTypeSource(String source) {
TypeSource? typeSource;
for (var i = 0; i < sourcesList.length; i++) {
if (sourcesList[i].sourceName.toLowerCase() == source.toLowerCase()) {
typeSource = sourcesList[i].typeSource;
}
}
return typeSource!;
}
String getFormatDate(String source) {
String? dateFormat;
for (var i = 0; i < sourcesList.length; i++) {
if (sourcesList[i].sourceName.toLowerCase() == source.toLowerCase()) {
dateFormat = sourcesList[i].dateFormat;
}
}
return dateFormat!;
}
String getFormatDateLocale(String source) {
String? dateFormatLocale;
for (var i = 0; i < sourcesList.length; i++) {
if (sourcesList[i].sourceName.toLowerCase() == source.toLowerCase()) {
dateFormatLocale = sourcesList[i].dateFormatLocale;
}
}
return dateFormatLocale!;
}
bool isCloudflare(String source) {
bool? isCloudflare;
for (var i = 0; i < sourcesList.length; i++) {
if (sourcesList[i].sourceName.toLowerCase() == source.toLowerCase()) {
isCloudflare = sourcesList[i].isCloudflare;
}
}
return isCloudflare!;
}
@riverpod
Future<GetMangaModel> getPopularManga(GetPopularMangaRef ref,
{required String source, required int page}) async {
List<String?> url = [];
List<String?> name = [];
List<String?> image = [];
GetMangaModel? popularManga;
source = source.toLowerCase();
/*********/
/*comick*/
/*******/
if (getWpMangTypeSource(source) == TypeSource.comick) {
final response = await httpGet(
url:
'https://api.comick.fun/v1.0/search?sort=follow&page=$page&tachiyomi=true',
source: source,
resDom: false) as String?;
var popularManga = jsonDecode(response!) as List;
var popularMangaList =
popularManga.map((e) => PopularMangaModelComick.fromJson(e)).toList();
for (var popular in popularMangaList) {
url.add("/comic/${popular.slug}");
name.add(popular.title);
image.add(popular.coverUrl);
}
popularManga = await Comick().getPopularManga(source: source, page: page);
}
/***************/
/*mangathemesia*/
/**************/
if (getWpMangTypeSource(source) == TypeSource.mangathemesia) {
final dom = await httpGet(
useUserAgent: true,
url: '${getWpMangaUrl(source)}/manga/?title=&page=$page&order=popular',
source: source,
resDom: true) as Document?;
if (dom!
.querySelectorAll(
'.utao .uta .imgu, .listupd .bs .bsx, .listo .bs .bsx')
.isNotEmpty) {
url = dom
.querySelectorAll(
'.utao .uta .imgu, .listupd .bs .bsx, .listo .bs .bsx')
.map((e) {
RegExp exp = RegExp(r'href="([^"]+)"');
Iterable<Match> matches = exp.allMatches(e.innerHtml);
String? firstMatch = matches.first.group(1);
return firstMatch;
}).toList();
image = dom
.querySelectorAll(
'.utao .uta .imgu, .listupd .bs .bsx, .listo .bs .bsx')
.map((e) {
RegExp exp = RegExp(r'src="([^"]+)"');
Iterable<Match> matches = exp.allMatches(e.innerHtml);
String? firstMatch = matches.first.group(1);
return firstMatch;
}).toList();
name = dom
.querySelectorAll(
'.utao .uta .imgu, .listupd .bs .bsx, .listo .bs .bsx ')
.map((e) {
RegExp exp = RegExp(r'title="([^"]+)"');
Iterable<Match> matches = exp.allMatches(e.innerHtml);
String? firstMatch = matches.first.group(1);
return firstMatch;
}).toList();
}
popularManga =
await MangaThemeSia().getPopularManga(source: source, page: page);
}
/***********/
/*mangakawaii*/
/***********/
if (source == "mangakawaii") {
final dom = await httpGet(
url: 'https://www.mangakawaii.io/',
source: source,
resDom: true) as Document?;
if (dom!.querySelectorAll('a.hot-manga__item').isNotEmpty) {
url = dom
.querySelectorAll('a.hot-manga__item ')
.where((e) => e.attributes.containsKey('href'))
.map((e) => e.attributes['href'])
.toList();
name = dom
.querySelectorAll('a > div > div.hot-manga__item-name')
.map((e) => e.innerHtml)
.toList();
for (var ia in name) {
image.add("");
}
}
popularManga =
await MangaKawaii().getPopularManga(source: source, page: page);
}
/***********/
@ -173,93 +45,21 @@ Future<GetMangaModel> getPopularManga(GetPopularMangaRef ref,
/***********/
else if (getWpMangTypeSource(source) == TypeSource.mmrcms) {
final dom = await httpGet(
url:
'${getWpMangaUrl(source)}/filterList?page=$page&sortBy=views&asc=false',
source: source,
resDom: true) as Document?;
final urlElement = dom!.getElementsByClassName('chart-title');
for (var e in urlElement) {
RegExp exp = RegExp(r'href="([^"]+)"');
Iterable<Match> matches = exp.allMatches(e.outerHtml);
String? firstMatch = matches.first.group(1);
url.add(firstMatch);
name.add(e.text);
}
final imgElement = dom.getElementsByTagName('img');
for (var e in imgElement) {
RegExp exp = RegExp(r'src="([^"]+)"');
Iterable<Match> matches = exp.allMatches(e.outerHtml);
String? firstMatch = matches.first.group(1);
image.add(firstMatch);
}
popularManga = await Mmrcms().getPopularManga(source: source, page: page);
}
/***********/
/*mangahere*/
/***********/
else if (source == "mangahere") {
final dom = await httpGet(
url: 'https://www.mangahere.cc/ranking/',
source: source,
resDom: true) as Document?;
if (dom!
.querySelectorAll(
'body > div.container.weekrank.ranking > div > div > ul > li > a')
.isNotEmpty) {
url = dom
.querySelectorAll(
'body > div.container.weekrank.ranking > div > div > ul > li > a ')
.where((e) => e.attributes.containsKey('href'))
.map((e) => e.attributes['href'])
.toList();
image = dom
.querySelectorAll(
' body > div.container.weekrank.ranking > div > div > ul > li > a > img')
.where((e) => e.attributes.containsKey('src'))
.where((e) => e.attributes['src']!.contains("cover"))
.map((e) => e.attributes['src'])
.toList();
name = dom
.querySelectorAll(
'body > div.container.weekrank.ranking > div > div > ul > li > a ')
.where((e) => e.attributes.containsKey('title'))
.map((e) => e.attributes['title'])
.toList();
}
popularManga =
await Mangahere().getPopularManga(source: source, page: page);
}
/***********/
/*japscan*/
/***********/
else if (source == "japscan") {
final dom = await httpGet(
url: "https://www.japscan.lol/",
source: source,
resDom: true) as Document?;
if (dom!.querySelectorAll('#top_mangas_week > ul > li ').isNotEmpty) {
final urls = dom
.querySelectorAll('#top_mangas_week > ul > li > a')
.where((e) => e.attributes['href'].toString().contains('manga'))
.map((e) => e.attributes['href'])
.toList();
for (var ok in urls) {
url.add("https://www.japscan.lol$ok");
}
name = dom
.querySelectorAll(
'#top_mangas_week > ul > li > a.text-dark.font-weight-bold')
.map((e) => e.innerHtml)
.toList();
for (var ia in name) {
image.add("");
}
}
popularManga = await Japscan().getPopularManga(source: source, page: page);
}
return GetMangaModel(
name: name,
url: url,
image: image,
);
return popularManga!;
}

View file

@ -6,7 +6,7 @@ part of 'get_popular_manga.dart';
// RiverpodGenerator
// **************************************************************************
String _$getPopularMangaHash() => r'cf3f1c29feafa93591a44549b5d81f25ff65bf4f';
String _$getPopularMangaHash() => r'cec4ced8864aca6f08b29a8ef9405fd21b68927f';
/// Copied from Dart SDK
class _SystemHash {

View file

@ -3,7 +3,10 @@ import 'package:http/http.dart' as http;
Future<Document> httpResToDom(
{required String url, required Map<String, String>? headers}) async {
final response = await http.get(Uri.parse(url), headers: headers);
return Document.html(response.body);
try {
final response = await http.get(Uri.parse(url), headers: headers);
return Document.html(response.body);
} catch (e) {
rethrow;
}
}

View file

@ -1,8 +1,8 @@
import 'package:html/dom.dart';
import 'package:mangayomi/services/http_service/cloudflare/cloudflare_bypass.dart';
import 'package:mangayomi/services/get_popular_manga.dart';
import 'package:mangayomi/services/http_service/http_res_to_dom_html.dart';
import 'package:http/http.dart' as http;
import 'package:mangayomi/sources/utils/utils.dart';
import 'package:mangayomi/utils/headers.dart';
Future<dynamic> httpGet(
@ -29,8 +29,13 @@ Future<dynamic> httpGet(
useUserAgent: useUserAgent,
);
} else {
final response = await http.get(Uri.parse(url), headers: headers(source));
resHtml = response.body;
try {
final response =
await http.get(Uri.parse(url), headers: headers(source));
resHtml = response.body;
} catch (e) {
rethrow;
}
}
return resHtml;
}

View file

@ -1,177 +1,68 @@
import 'dart:convert';
import 'package:html/dom.dart';
import 'package:mangayomi/models/comick/search_manga_cimick.dart';
import 'package:mangayomi/services/get_popular_manga.dart';
import 'package:mangayomi/services/http_service/http_service.dart';
import 'package:mangayomi/source/source_model.dart';
import 'package:mangayomi/models/source_model.dart';
import 'package:mangayomi/sources/service/service.dart';
import 'package:mangayomi/sources/src/all/comick/src/comick.dart';
import 'package:mangayomi/sources/src/en/mangahere/src/mangahere.dart';
import 'package:mangayomi/sources/src/fr/japscan/src/japscan.dart';
import 'package:mangayomi/sources/src/fr/mangakawaii/src/mangakawaii.dart';
import 'package:mangayomi/sources/src/multi/mangathemesia/src/mangathemesia.dart';
import 'package:mangayomi/sources/src/multi/mmrcms/src/mmrcms.dart';
import 'package:mangayomi/sources/utils/utils.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'search_manga.g.dart';
class SearchMangaModel {
late List<String?> url;
late List<String?> name;
late List<String?> image;
SearchMangaModel({
required this.name,
required this.url,
required this.image,
});
}
@riverpod
Future<SearchMangaModel> searchManga(SearchMangaRef ref,
Future<GetMangaModel> searchManga(SearchMangaRef ref,
{required String source, required String query}) async {
List<String?> url = [];
List<String?> name = [];
List<String?> image = [];
GetMangaModel? manga;
source = source.toLowerCase();
/********/
/*comick*/
/********/
if (getWpMangTypeSource(source) == TypeSource.comick) {
final response = await httpGet(
url:
'https://api.comick.fun/search?q=${query.trim()}&tachiyomi=true&page=1',
source: source,
resDom: false) as String?;
var popularManga = jsonDecode(response!) as List;
var popularMangaList =
popularManga.map((e) => MangaSearchModelComick.fromJson(e)).toList();
for (var popular in popularMangaList) {
url.add("/comic/${popular.slug}");
name.add(popular.title);
image.add(popular.coverUrl);
}
manga = await Comick().searchManga(source: source, query: query);
}
/***************/
/*mangathemesia*/
/***************/
else if (getWpMangTypeSource(source) == TypeSource.mangathemesia) {
final dom = await httpGet(
url: '${getWpMangaUrl(source)}/?s=${query.trim()}',
source: source,
resDom: true) as Document?;
if (dom!
.querySelectorAll(
'#content > div > div.postbody > div > div.listupd > div > div > a')
.isNotEmpty) {
url = dom
.querySelectorAll(
'#content > div > div.postbody > div > div.listupd > div > div > a ')
.where((e) => e.attributes.containsKey('href'))
.map((e) => e.attributes['href'])
.toList();
image = dom
.querySelectorAll(
' #content > div > div.postbody > div > div.listupd > div > div > a > div > img')
.where((e) => e.attributes.containsKey('src'))
.map((e) => e.attributes['src'])
.toList();
name = dom
.querySelectorAll(
'#content > div > div.postbody > div > div.listupd > div > div > a ')
.where((e) => e.attributes.containsKey('title'))
.map((e) => e.attributes['title'])
.toList();
}
manga = await MangaThemeSia().searchManga(source: source, query: query);
}
/***********/
/*mangakawaii*/
/***********/
else if (source == "mangakawaii") {
final dom = await httpGet(
url:
'https://www.mangakawaii.io/search?query=${query.trim()}&search_type=manga',
source: source,
resDom: true) as Document?;
if (dom!
.querySelectorAll(
'#page-content > div > div > ul > li > div.section__list-group-right > div.section__list-group-header > div > h4 > a')
.isNotEmpty) {
final ur = dom
.querySelectorAll(
'#page-content > div > div > ul > li > div.section__list-group-right > div.section__list-group-header > div > h4 > a')
.where((e) => e.attributes.containsKey('href'))
.map((e) => e.attributes['href'])
.toList();
for (var a in ur) {
url.add(a!);
}
final nam = dom
.querySelectorAll(
'#page-content > div > div > ul > li > div.section__list-group-right > div.section__list-group-header > div > h4 > a')
.map((e) => e.text)
.toList();
for (var a in nam) {
name.add(a);
image.add('');
}
}
else if (source == "mangakawaii") {
manga = await MangaKawaii().searchManga(source: source, query: query);
}
/***********/
/*mmrcms*/
/***********/
else if (getWpMangTypeSource(source) == TypeSource.mmrcms) {
final response = await httpGet(
url: '${getWpMangaUrl(source)}/search?query=${query.trim()}',
source: source,
resDom: false) as String?;
final rep = jsonDecode(response!);
for (var ok in rep['suggestions']) {
if (source == 'Read Comics Online') {
url.add('${getWpMangaUrl(source)}/comic/${ok['data']}');
} else if (source == 'Scan VF') {
url.add('${getWpMangaUrl(source)}/${ok['data']}');
} else {
url.add('${getWpMangaUrl(source)}/manga/${ok['data']}');
}
name.add(ok["value"]);
image.add('');
}
manga = await Mmrcms().searchManga(source: source, query: query);
}
/***********/
/*mangahere*/
/***********/
else if (source == "mangahere") {
final dom = await httpGet(
url:
'${getWpMangaUrl(source)}/search?title=${query.trim()}&genres=&nogenres=&sort=&stype=1&name=&type=0&author_method=cw&author=&artist_method=cw&artist=&rating_method=eq&rating=&released_method=eq&released=&st=0',
source: source,
resDom: true) as Document?;
if (dom!
.querySelectorAll(
'body > div.container > div > div > ul > li > p.manga-list-4-item-title > a')
.isNotEmpty) {
url = dom
.querySelectorAll(
'body > div.container > div > div > ul > li > p.manga-list-4-item-title > a')
.where((e) => e.attributes.containsKey('href'))
.map((e) => e.attributes['href'])
.toList();
image = dom
.querySelectorAll(
'body > div.container > div > div > ul > li > a > img')
.where((e) => e.attributes.containsKey('src'))
.where((e) => e.attributes['src']!.contains("cover"))
.map((e) => e.attributes['src'])
.toList();
name = dom
.querySelectorAll(
'body > div.container > div > div > ul > li > p.manga-list-4-item-title > a')
.where((e) => e.attributes.containsKey('title'))
.map((e) => e.attributes['title'])
.toList();
}
manga = await Mangahere().searchManga(source: source, query: query);
}
return SearchMangaModel(name: name, url: url, image: image);
/***********/
/*japscan*/
/***********/
else if (source == "japscan") {
manga = await Japscan().searchManga(source: source, query: query);
}
return manga!;
}

View file

@ -6,7 +6,7 @@ part of 'search_manga.dart';
// RiverpodGenerator
// **************************************************************************
String _$searchMangaHash() => r'8efe07dbe0f844d9d17ecf6962ac7f03ebcc9d57';
String _$searchMangaHash() => r'1694578ec0cc207b533f4709427d3f68b691b1b7';
/// Copied from Dart SDK
class _SystemHash {
@ -29,14 +29,14 @@ class _SystemHash {
}
}
typedef SearchMangaRef = AutoDisposeFutureProviderRef<SearchMangaModel>;
typedef SearchMangaRef = AutoDisposeFutureProviderRef<GetMangaModel>;
/// See also [searchManga].
@ProviderFor(searchManga)
const searchMangaProvider = SearchMangaFamily();
/// See also [searchManga].
class SearchMangaFamily extends Family<AsyncValue<SearchMangaModel>> {
class SearchMangaFamily extends Family<AsyncValue<GetMangaModel>> {
/// See also [searchManga].
const SearchMangaFamily();
@ -77,7 +77,7 @@ class SearchMangaFamily extends Family<AsyncValue<SearchMangaModel>> {
}
/// See also [searchManga].
class SearchMangaProvider extends AutoDisposeFutureProvider<SearchMangaModel> {
class SearchMangaProvider extends AutoDisposeFutureProvider<GetMangaModel> {
/// See also [searchManga].
SearchMangaProvider({
required this.source,

View file

@ -0,0 +1,108 @@
import 'package:mangayomi/models/chapter.dart';
abstract class MangaYomiServices {
List<String?> url = [];
List<String?> name = [];
List<String?> image = [];
List<String> genre = [];
String? author;
String? status;
List<String> chapterTitle = [];
List<String> chapterUrl = [];
List<String> chapterDate = [];
String? description;
List<Chapter> chapters = [];
List<String> scanlators = [];
List pageUrls = [];
GetMangaModel mangaRes() {
return GetMangaModel(
name: name,
url: url,
image: image,
);
}
GetMangaDetailModel mangadetailRes(
{required String imageUrl,
required String url,
required String title,
required String source}) {
if (chapterDate.isNotEmpty &&
chapterTitle.isNotEmpty &&
chapterUrl.isNotEmpty) {
for (var i = 0; i < chapterUrl.length; i++) {
chapters.add(Chapter(
name: chapterTitle[i],
url: chapterUrl[i],
dateUpload: chapterDate[i],
isBookmarked: false,
scanlator: scanlators.isEmpty ? "" : scanlators[i],
isRead: false,
lastPageRead: '',
mangaId: null));
}
}
return GetMangaDetailModel(
status: status,
genre: genre,
author: author,
description: description,
name: title,
url: url,
source: source,
imageUrl: imageUrl,
chapters: chapters,
);
}
Future<GetMangaModel?> getPopularManga(
{required String source, required int page});
Future<GetMangaDetailModel?> getMangaDetail(
{required String imageUrl,
required String url,
required String title,
required String lang,
required String source});
Future<List<dynamic>?> getMangaChapterUrl({
required Chapter chapter,
});
Future<GetMangaModel?> searchManga(
{required String source, required String query});
}
class GetMangaModel {
late List<String?> url;
late List<String?> name;
late List<String?> image;
GetMangaModel({
required this.name,
required this.url,
required this.image,
});
}
class GetMangaDetailModel {
List<String> genre = [];
List<Chapter> chapters = [];
String? author;
String? status;
String? source;
String? url;
String? name;
String? imageUrl;
String? description;
GetMangaDetailModel({
required this.genre,
required this.author,
required this.status,
required this.chapters,
required this.imageUrl,
required this.description,
required this.url,
required this.name,
required this.source,
});
}

View file

@ -0,0 +1,17 @@
import 'package:mangayomi/models/source_model.dart';
import 'package:mangayomi/sources/src/all/comick/comick_source_list.dart';
import 'package:mangayomi/sources/src/en/mangahere/mangahere_source.dart';
import 'package:mangayomi/sources/src/fr/japscan/japscan_source.dart';
import 'package:mangayomi/sources/src/fr/mangakawaii/mangakawaii_source.dart';
import 'package:mangayomi/sources/src/multi/mangathemesia/mangathemesia_source_list.dart';
import 'package:mangayomi/sources/src/multi/mmrcms/mmrcms_source_list.dart';
List<SourceModel> get sourcesList => _sourcesList;
List<SourceModel> _sourcesList = [
mangahereSource,
mangakawaiiSource,
...mangathemesiaSourcesList,
...comickSourcesList,
...mmrcmsSourcesList,
japscanSource
];

View file

@ -0,0 +1,159 @@
import 'package:mangayomi/models/source_model.dart';
String logoUrl =
'https://comick.app/_next/image?url=%2Fstatic%2Ficons%2Funicorn-64.png&w=144&q=75';
String apiUrl = 'https://api.comick.fun/';
List<SourceModel> comickSourcesList = [
SourceModel(
sourceName: 'Comick',
url: apiUrl,
lang: 'en',
typeSource: TypeSource.comick,
logoUrl: logoUrl,
dateFormat: "yyyy-MM-dd'T'HH:mm:ss'Z'",
dateFormatLocale: "en"),
SourceModel(
sourceName: 'Comick',
url: apiUrl,
lang: 'ar',
typeSource: TypeSource.comick,
logoUrl: logoUrl,
dateFormat: "yyyy-MM-dd'T'HH:mm:ss'Z'",
dateFormatLocale: "en"),
SourceModel(
sourceName: 'Comick',
url: apiUrl,
lang: 'pt',
typeSource: TypeSource.comick,
logoUrl: logoUrl,
dateFormat: "yyyy-MM-dd'T'HH:mm:ss'Z'",
dateFormatLocale: "en"),
SourceModel(
sourceName: 'Comick',
url: apiUrl,
lang: 'pt-br',
typeSource: TypeSource.comick,
logoUrl: logoUrl,
dateFormat: "yyyy-MM-dd'T'HH:mm:ss'Z'",
dateFormatLocale: "en"),
SourceModel(
sourceName: 'Comick',
url: apiUrl,
lang: 'it',
typeSource: TypeSource.comick,
logoUrl: logoUrl,
dateFormat: "yyyy-MM-dd'T'HH:mm:ss'Z'",
dateFormatLocale: "en"),
SourceModel(
sourceName: 'Comick',
url: apiUrl,
lang: 'ru',
typeSource: TypeSource.comick,
logoUrl: logoUrl,
dateFormat: "yyyy-MM-dd'T'HH:mm:ss'Z'",
dateFormatLocale: "en"),
SourceModel(
sourceName: 'Comick',
url: apiUrl,
lang: 'es',
typeSource: TypeSource.comick,
logoUrl: logoUrl,
dateFormat: "yyyy-MM-dd'T'HH:mm:ss'Z'",
dateFormatLocale: "en"),
SourceModel(
sourceName: 'Comick',
url: apiUrl,
lang: 'es-419',
typeSource: TypeSource.comick,
logoUrl: logoUrl,
dateFormat: "yyyy-MM-dd'T'HH:mm:ss'Z'",
dateFormatLocale: "en"),
SourceModel(
sourceName: 'Comick',
url: apiUrl,
lang: 'id',
typeSource: TypeSource.comick,
logoUrl: logoUrl,
dateFormat: "yyyy-MM-dd'T'HH:mm:ss'Z'",
dateFormatLocale: "en"),
SourceModel(
sourceName: 'Comick',
url: apiUrl,
lang: 'hi',
typeSource: TypeSource.comick,
logoUrl: logoUrl,
dateFormat: "yyyy-MM-dd'T'HH:mm:ss'Z'",
dateFormatLocale: "en"),
SourceModel(
sourceName: 'Comick',
url: apiUrl,
lang: 'de',
typeSource: TypeSource.comick,
logoUrl: logoUrl,
dateFormat: "yyyy-MM-dd'T'HH:mm:ss'Z'",
dateFormatLocale: "en"),
SourceModel(
sourceName: 'Comick',
url: apiUrl,
lang: 'ja',
typeSource: TypeSource.comick,
logoUrl: logoUrl,
dateFormat: "yyyy-MM-dd'T'HH:mm:ss'Z'",
dateFormatLocale: "en"),
SourceModel(
sourceName: 'Comick',
url: apiUrl,
lang: 'tr',
typeSource: TypeSource.comick,
logoUrl: logoUrl,
dateFormat: "yyyy-MM-dd'T'HH:mm:ss'Z'",
dateFormatLocale: "en"),
SourceModel(
sourceName: 'Comick',
url: apiUrl,
lang: 'pl',
typeSource: TypeSource.comick,
logoUrl: logoUrl,
dateFormat: "yyyy-MM-dd'T'HH:mm:ss'Z'",
dateFormatLocale: "en"),
SourceModel(
sourceName: 'Comick',
url: apiUrl,
lang: 'zh',
typeSource: TypeSource.comick,
logoUrl: logoUrl,
dateFormat: "yyyy-MM-dd'T'HH:mm:ss'Z'",
dateFormatLocale: "en"),
SourceModel(
sourceName: 'Comick',
url: apiUrl,
lang: 'zh-hk',
typeSource: TypeSource.comick,
logoUrl: logoUrl,
dateFormat: "yyyy-MM-dd'T'HH:mm:ss'Z'",
dateFormatLocale: "en"),
SourceModel(
sourceName: 'Comick',
url: apiUrl,
lang: 'fr',
typeSource: TypeSource.comick,
logoUrl: logoUrl,
dateFormat: "yyyy-MM-dd'T'HH:mm:ss'Z'",
dateFormatLocale: "en"),
SourceModel(
sourceName: "KomikLab",
url: "https://komiklab.com",
lang: "en",
typeSource: TypeSource.mangathemesia,
logoUrl: '',
dateFormat: "MMMM dd, yyyy",
dateFormatLocale: "en"),
SourceModel(
sourceName: "AnimatedGlitchedScans",
url: "https://anigliscans.com",
lang: "en",
typeSource: TypeSource.mangathemesia,
logoUrl: '',
dateFormat: "MMMM dd, yyyy",
dateFormatLocale: "en"),
];

View file

@ -0,0 +1,127 @@
import 'dart:convert';
import 'package:mangayomi/models/chapter.dart';
import 'package:mangayomi/models/comick/chapter_page_comick.dart';
import 'package:mangayomi/models/comick/manga_chapter_detail.dart';
import 'package:mangayomi/models/comick/manga_detail_comick.dart';
import 'package:mangayomi/models/comick/popular_manga_comick.dart';
import 'package:mangayomi/models/comick/search_manga_cimick.dart';
import 'package:mangayomi/services/http_service/http_service.dart';
import 'package:mangayomi/sources/service/service.dart';
import 'package:mangayomi/sources/src/all/comick/src/utils/utils.dart';
import 'package:mangayomi/sources/utils/utils.dart';
class Comick extends MangaYomiServices {
@override
Future<GetMangaModel?> getPopularManga(
{required String source, required int page}) async {
source = source.toLowerCase();
final response = await httpGet(
url:
'https://api.comick.fun/v1.0/search?sort=follow&page=$page&tachiyomi=true',
source: source,
resDom: false) as String?;
var popularManga = jsonDecode(response!) as List;
var popularMangaList =
popularManga.map((e) => PopularMangaModelComick.fromJson(e)).toList();
for (var popular in popularMangaList) {
url.add("/comic/${popular.slug}");
name.add(popular.title);
image.add(popular.coverUrl);
}
return mangaRes();
}
@override
Future<GetMangaDetailModel?> getMangaDetail(
{required String imageUrl,
required String url,
required String title,
required String lang,
required String source}) async {
final response = await httpGet(
url: 'https://api.comick.fun$url?tachiyomi=true',
source: source,
resDom: false) as String?;
var mangaDetail = jsonDecode(response!) as Map<String, dynamic>;
var mangaDetailLMap = MangaDetailModelComick.fromJson(mangaDetail);
RegExp regExp = RegExp(r'name:\s*(.*?),');
String authorr =
regExp.firstMatch(mangaDetailLMap.authors![0].toString())?.group(1) ??
'';
String statuss = parseStatut(mangaDetailLMap.comic!.status!);
status = statuss;
author = authorr;
RegExp regExp1 = RegExp(r'name:\s*(.*?)}');
for (var ok in mangaDetailLMap.genres!) {
genre.add(regExp1.firstMatch(ok.toString())!.group(1)!);
}
description = mangaDetailLMap.comic!.desc;
String tt = await findCurrentSlug(mangaDetailLMap.comic!.slug!);
String mangaId = tt.split('":"').last.replaceAll('"}', '');
String limit = mangaDetailLMap.comic!.chapterCount.toString();
final responsee = await httpGet(
url:
'https://api.comick.fun/comic/$mangaId/chapters?lang=$lang&limit=$limit',
source: source,
resDom: false) as String?;
var chapterDetail = jsonDecode(responsee!) as Map<String, dynamic>;
var chapterDetailMap = MangaChapterModelComick.fromJson(chapterDetail);
for (var chapter in chapterDetailMap.chapters!) {
scanlators.add(chapter.groupName!.isNotEmpty
? chapter.groupName!.first.toString() != 'null'
? chapter.groupName!.first
: ""
: "");
chapterUrl.add(
"/comic/${mangaDetailLMap.comic!.slug}/${chapter.hid}-chapter-${chapter.chap}-en");
chapterDate.add(parseDate(chapter.createdAt!, source));
chapterTitle.add(beautifyChapterName(
chapter.vol ?? "", chapter.chap ?? "", chapter.title ?? "", lang));
}
return mangadetailRes(
imageUrl: imageUrl, url: url, title: title, source: source);
}
@override
Future<GetMangaModel> searchManga(
{required String source, required String query}) async {
final response = await httpGet(
url:
'https://api.comick.fun/search?q=${query.trim()}&tachiyomi=true&page=1',
source: source,
resDom: false) as String?;
var popularManga = jsonDecode(response!) as List;
var popularMangaList =
popularManga.map((e) => MangaSearchModelComick.fromJson(e)).toList();
for (var popular in popularMangaList) {
url.add("/comic/${popular.slug}");
name.add(popular.title);
image.add(popular.coverUrl);
}
return mangaRes();
}
@override
Future<List<dynamic>> getMangaChapterUrl({required Chapter chapter}) async {
String mangaId = chapter.url!.split('/').last.split('-').first;
final response = await httpGet(
url: 'https://api.comick.fun/chapter/$mangaId?tachiyomi=true',
source: 'comick',
resDom: false) as String?;
var data = jsonDecode(response!) as Map<String, dynamic>;
var page = ChapterPageComick.fromJson(data);
for (var url in page.chapter!.images!) {
pageUrls.add(url.url);
}
return pageUrls;
}
}

View file

@ -0,0 +1,38 @@
import 'package:intl/intl.dart';
import 'package:mangayomi/sources/utils/utils.dart';
import 'package:mangayomi/utils/headers.dart';
import 'package:http/http.dart' as http;
parseStatut(int i) {
if (i == 1) {
return 'Ongoing';
} else if (i == 2) {
return 'Completed';
} else if (i == 3) {
return 'Canceled';
} else if (i == 4) {
return '';
} else {
return 'Unknown';
}
}
Future findCurrentSlug(String oldSlug) async {
var request = http.Request('GET',
Uri.parse('https://api.comick.fun/tachiyomi/mapping?slugs=$oldSlug'));
request.headers.addAll(headers("comick"));
http.StreamedResponse response = await request.send();
if (response.statusCode == 200) {
return await response.stream.bytesToString();
} else {
return response.reasonPhrase;
}
}
beautifyChapterName(String? vol, String? chap, String? title, String? lang) {
return "${vol!.isNotEmpty ? chap!.isEmpty ? "Volume $vol " : "Vol. $vol " : ""}${chap!.isNotEmpty ? vol.isEmpty ? lang == "fr" ? "Chapitre $chap" : "Chapter $chap" : "Ch. $chap " : ""}${title!.isNotEmpty ? chap.isEmpty ? title : " : $title" : ""}";
}

View file

@ -0,0 +1,12 @@
import 'package:mangayomi/models/source_model.dart';
SourceModel mangahereSource = SourceModel(
sourceName: "MangaHere",
url: "http://www.mangahere.cc",
lang: "en",
typeSource: TypeSource.single,
logoUrl: 'http://static.mangahere.cc/v20210106/mangahere/images/logo.png',
isFullData: true,
dateFormat: "MMM dd,yyyy",
dateFormatLocale: "en",
);

View file

@ -0,0 +1,295 @@
import 'package:flutter_js/flutter_js.dart';
import 'package:html/dom.dart';
import 'package:mangayomi/models/chapter.dart';
import 'package:mangayomi/services/http_service/http_service.dart';
import 'package:mangayomi/sources/service/service.dart';
import 'package:http/http.dart' as http;
import 'package:html/dom.dart' as dom;
import 'package:mangayomi/sources/utils/utils.dart';
class Mangahere extends MangaYomiServices {
@override
Future<GetMangaDetailModel?> getMangaDetail(
{required String imageUrl,
required String url,
required String title,
required String lang,
required String source}) async {
final dom = await httpGet(
url: "http://www.mangahere.cc$url",
source: source,
resDom: true) as Document?;
if (dom!
.querySelectorAll(
' body > div > div > div.detail-info-right > p.detail-info-right-title > span.detail-info-right-title-tip')
.isNotEmpty) {
final tt = dom
.querySelectorAll(
' body > div > div > div.detail-info-right > p.detail-info-right-title > span.detail-info-right-title-tip')
.map((e) => e.text.trim())
.toList();
status = tt[0];
} else {
status = "";
}
if (dom
.querySelectorAll(
' body > div > div > div.detail-info-right > p.detail-info-right-say > a')
.isNotEmpty) {
final tt = dom
.querySelectorAll(
' body > div > div > div.detail-info-right > p.detail-info-right-say > a')
.map((e) => e.text.trim())
.toList();
author = tt[0];
} else {
author = "";
}
if (dom
.querySelectorAll(
'body > div > div > div.detail-info-right > p.detail-info-right-content')
.isNotEmpty) {
final tt = dom
.querySelectorAll(
'body > div > div > div.detail-info-right > p.detail-info-right-content')
.map((e) => e.text.trim())
.toList();
description = tt.first;
}
if (dom.querySelectorAll('ul > li > a').isNotEmpty) {
final udl = dom
.querySelectorAll('ul > li > a ')
.where((e) => e.attributes.containsKey('href'))
.map((e) => e.attributes['href'])
.toList();
for (var ok in udl) {
chapterUrl.add(ok!);
}
}
if (dom.querySelectorAll('ul > li > a > div > p.title3').isNotEmpty) {
final tt = dom
.querySelectorAll('ul > li > a > div > p.title3')
.map((e) => e.text.trim())
.toList();
for (var ok in tt) {
chapterTitle.add(ok);
}
}
if (dom.querySelectorAll('ul > li > a > div > p.title2').isNotEmpty) {
final tt = dom
.querySelectorAll('ul > li > a > div > p.title2')
.map((e) => e.text.trim())
.toList();
for (var ok in tt) {
chapterDate.add(parseDate(ok, source));
}
}
if (dom
.querySelectorAll(
' body > div > div > div.detail-info-right > p.detail-info-right-tag-list > a')
.isNotEmpty) {
final tt = dom
.querySelectorAll(
' body > div > div > div.detail-info-right > p.detail-info-right-tag-list > a')
.map((e) => e.text.trim())
.toList();
for (var ok in tt) {
genre.add(ok);
}
}
return mangadetailRes(
imageUrl: imageUrl, url: url, title: title, source: source);
}
@override
Future<GetMangaModel?> getPopularManga(
{required String source, required int page}) async {
final dom = await httpGet(
url: 'https://www.mangahere.cc/ranking/',
source: source,
resDom: true) as Document?;
if (dom!
.querySelectorAll(
'body > div.container.weekrank.ranking > div > div > ul > li > a')
.isNotEmpty) {
url = dom
.querySelectorAll(
'body > div.container.weekrank.ranking > div > div > ul > li > a ')
.where((e) => e.attributes.containsKey('href'))
.map((e) => e.attributes['href'])
.toList();
image = dom
.querySelectorAll(
' body > div.container.weekrank.ranking > div > div > ul > li > a > img')
.where((e) => e.attributes.containsKey('src'))
.where((e) => e.attributes['src']!.contains("cover"))
.map((e) => e.attributes['src'])
.toList();
name = dom
.querySelectorAll(
'body > div.container.weekrank.ranking > div > div > ul > li > a ')
.where((e) => e.attributes.containsKey('title'))
.map((e) => e.attributes['title'])
.toList();
}
return mangaRes();
}
@override
Future<GetMangaModel?> searchManga(
{required String source, required String query}) async {
final dom = await httpGet(
url:
'${getWpMangaUrl(source)}/search?title=${query.trim()}&genres=&nogenres=&sort=&stype=1&name=&type=0&author_method=cw&author=&artist_method=cw&artist=&rating_method=eq&rating=&released_method=eq&released=&st=0',
source: source,
resDom: true) as Document?;
if (dom!
.querySelectorAll(
'body > div.container > div > div > ul > li > p.manga-list-4-item-title > a')
.isNotEmpty) {
url = dom
.querySelectorAll(
'body > div.container > div > div > ul > li > p.manga-list-4-item-title > a')
.where((e) => e.attributes.containsKey('href'))
.map((e) => e.attributes['href'])
.toList();
image = dom
.querySelectorAll(
'body > div.container > div > div > ul > li > a > img')
.where((e) => e.attributes.containsKey('src'))
.where((e) => e.attributes['src']!.contains("cover"))
.map((e) => e.attributes['src'])
.toList();
name = dom
.querySelectorAll(
'body > div.container > div > div > ul > li > p.manga-list-4-item-title > a')
.where((e) => e.attributes.containsKey('title'))
.map((e) => e.attributes['title'])
.toList();
}
return mangaRes();
}
@override
Future<List<dynamic>> getMangaChapterUrl({required Chapter chapter}) async {
JavascriptRuntime? flutterJs;
flutterJs = getJavascriptRuntime();
extractSecretKey(String response, JavascriptRuntime? flutterJs) {
var secretKeyScriptLocation =
response.indexOf("eval(function(p,a,c,k,e,d)");
var secretKeyScriptEndLocation =
response.indexOf("</script>", secretKeyScriptLocation);
var secretKeyScript = response
.substring(secretKeyScriptLocation, secretKeyScriptEndLocation)
.replaceAll("eval", "");
var secretKeyDeobfuscatedScript =
flutterJs!.evaluate(secretKeyScript).toString();
var secretKeyStartLoc = secretKeyDeobfuscatedScript.indexOf("'");
var secretKeyEndLoc = secretKeyDeobfuscatedScript.indexOf(";");
var secretKeyResultScript = secretKeyDeobfuscatedScript.substring(
secretKeyStartLoc, secretKeyEndLoc);
return secretKeyResultScript;
}
var link = "http://www.mangahere.cc${chapter.url!}";
final response =
await httpGet(url: link, source: "managhere", resDom: false) as String?;
dom.Document htmll = dom.Document.html(response!);
int? pagesNumber = -1;
if (htmll.querySelectorAll('body > div > div > span > a:').isNotEmpty) {
final ta = htmll
.querySelectorAll('body > div > div > span > a:')
.map((e) => e.text.trim())
.toList();
ta.removeLast();
pagesNumber = int.parse(ta.last);
}
if (pagesNumber == -1) {
final script = htmll
.getElementsByTagName("script")
.firstWhere((e) => e.innerHtml.contains(
"function(p,a,c,k,e,d)",
))
.innerHtml
.replaceAll("eval", "");
String deobfuscatedScript = flutterJs.evaluate(script).toString();
List<String> urlss = deobfuscatedScript
.substring(
deobfuscatedScript.indexOf("newImgs=['") + "newImgs=['".length,
deobfuscatedScript.indexOf("'];"))
.split("','");
for (var tt in urlss) {
pageUrls.add("https:$tt");
}
flutterJs.dispose();
} else {
var secretKey = extractSecretKey(response, flutterJs);
var chapterIdStartLoc = response.indexOf("chapterid");
var chapterId = response
.substring(
chapterIdStartLoc + 11, response.indexOf(";", chapterIdStartLoc))
.trim();
var pageBase = link.substring(0, link.lastIndexOf("/"));
for (int i = 1; i <= pagesNumber; i++) {
var pageLink =
"$pageBase/chapterfun.ashx?cid=$chapterId&page=$i&key=$secretKey";
var responseText = "";
for (int tr = 1; tr <= 3; tr++) {
var response = await http.get(Uri.parse(pageLink), headers: {
"Referer": link,
"Accept": "*/*",
"Accept-Language": "en-US,en;q=0.9",
"Connection": "keep-alive",
"Host": "www.mangahere.cc",
"X-Requested-With": "XMLHttpRequest"
});
responseText = response.body;
if (responseText.isNotEmpty) {
break;
} else {
secretKey = "";
}
}
var deobfuscatedScript =
flutterJs.evaluate(responseText.replaceAll("eval", "")).toString();
var baseLinkStartPos = deobfuscatedScript.indexOf("pix=") + 5;
var baseLinkEndPos =
deobfuscatedScript.indexOf(";", baseLinkStartPos) - 1;
var baseLink =
deobfuscatedScript.substring(baseLinkStartPos, baseLinkEndPos);
var imageLinkStartPos = deobfuscatedScript.indexOf("pvalue=") + 9;
var imageLinkEndPos =
deobfuscatedScript.indexOf("\"", imageLinkStartPos);
var imageLink =
deobfuscatedScript.substring(imageLinkStartPos, imageLinkEndPos);
pageUrls.add("https:$baseLink$imageLink");
}
flutterJs.dispose();
}
return pageUrls;
}
}

View file

@ -0,0 +1,11 @@
import 'package:mangayomi/models/source_model.dart';
SourceModel japscanSource = SourceModel(
sourceName: "Japscan",
url: "https://japscan.lol",
lang: "fr",
typeSource: TypeSource.single,
logoUrl: '',
isCloudflare: true,
dateFormat: "d MMM yyyy",
dateFormatLocale: "en_US");

View file

@ -0,0 +1,238 @@
// ignore_for_file: depend_on_referenced_packages
import 'dart:convert';
import 'dart:developer';
import 'package:html/dom.dart' as dom;
import 'package:html/dom.dart';
import 'package:mangayomi/models/chapter.dart';
import 'package:mangayomi/services/http_service/cloudflare/cloudflare_bypass.dart';
import 'package:mangayomi/services/http_service/http_service.dart';
import 'package:mangayomi/sources/service/service.dart';
import 'package:mangayomi/sources/utils/utils.dart';
import 'package:collection/collection.dart';
class Japscan extends MangaYomiServices {
@override
Future<GetMangaDetailModel?> getMangaDetail(
{required String imageUrl,
required String url,
required String title,
required String lang,
required String source}) async {
final dom =
await httpGet(url: url, source: source, resDom: true) as Document?;
if (dom!.querySelectorAll('.col-7 > p').isNotEmpty) {
final images =
dom.querySelectorAll('.col-5 ').map((e) => e.outerHtml).toList();
RegExp exp = RegExp(r'src="([^"]+)"');
String? srcValue = exp.firstMatch(images[0])?.group(1);
imageUrl = 'https://www.japscan.lol$srcValue';
if (dom.querySelectorAll('.col-7 > p').isNotEmpty) {
final stat = dom
.querySelectorAll('.col-7 > p')
.where((element) => element.innerHtml.contains('Statut:'))
.map((e) => e.text)
.toList();
if (stat.isNotEmpty) {
status = stat[0].replaceAll('Statut:', '').trim();
}
final auth = dom
.querySelectorAll('.col-7 > p')
.where((element) => element.innerHtml.contains('Auteur(s):'))
.map((e) => e.text)
.toList();
if (auth.isNotEmpty) {
author = auth[0].replaceAll('Auteur(s):', '').trim();
}
} else {
author = "";
status = "";
}
final genres = dom
.querySelectorAll('.col-7 > p')
.where((element) => element.innerHtml.contains('Genre(s):'))
.map((e) => e.text.replaceAll('Genre(s):', '').trim())
.toList();
if (genres.isNotEmpty) {
for (var ok in genres[0].split(',')) {
genre.add(ok);
}
}
final synop = dom
.querySelectorAll('p.list-group-item ')
.map((e) => e.text.trim())
.toList();
if (synop.isNotEmpty) {
description = synop[0];
}
}
final urls =
dom.querySelectorAll('.col-8 ').map((e) => e.outerHtml).toList();
for (var ok in urls) {
RegExp exp = RegExp(r'href="([^"]+)"');
String? srcValue = exp.firstMatch(ok)?.group(1);
chapterUrl.add('https://www.japscan.lol$srcValue');
}
final chapterTitlee =
dom.querySelectorAll('.col-8').map((e) => e.text.trim()).toList();
if (chapterTitlee.isNotEmpty) {
for (var ok in chapterTitlee) {
chapterTitle.add(ok);
}
}
final chapterDatee =
dom.querySelectorAll('.col-4').map((e) => e.text.trim()).toList();
if (chapterDatee.isNotEmpty) {
for (var ok in chapterDatee) {
chapterDate.add(parseDate(ok, source));
}
}
return mangadetailRes(
imageUrl: imageUrl, url: url, title: title, source: source);
}
@override
Future<GetMangaModel?> getPopularManga(
{required String source, required int page}) async {
final dom = await httpGet(
url: "https://www.japscan.lol/",
source: source,
resDom: true) as Document?;
if (dom!.querySelectorAll('#top_mangas_week > ul > li ').isNotEmpty) {
final urls = dom
.querySelectorAll('#top_mangas_week > ul > li > a')
.where((e) => e.attributes['href'].toString().contains('manga'))
.map((e) => e.attributes['href'])
.toList();
for (var ok in urls) {
url.add("https://www.japscan.lol$ok");
}
name = dom
.querySelectorAll(
'#top_mangas_week > ul > li > a.text-dark.font-weight-bold')
.map((e) => e.innerHtml)
.toList();
for (var ia in name) {
image.add("");
}
}
return mangaRes();
}
@override
Future<GetMangaModel?> searchManga(
{required String source, required String query}) async {
final dom = await httpGet(
url: "https://www.google.com/search?q=${query.toLowerCase()}+japscan",
source: source,
resDom: true) as Document?;
if (dom!.querySelectorAll("div > div > div > div > div > a").isNotEmpty) {
final urls = dom
.querySelectorAll("div > div > div > div > div > a")
.where((e) => e.attributes.containsKey('href'))
.where((element) =>
element.text.toLowerCase().contains("https://www.japscan."))
.map((e) => e.attributes['href']
.toString()
.replaceAll('lecture-en-ligne', 'manga')
.split("/"))
.toList();
List<String?> tt = [];
List<String?> ta = [];
for (var ok in urls) {
tt.add("${ok[0]}//${ok[2]}/${ok[3]}/${ok[4]}/");
ta.add(ok[4]
.replaceAll('-', " ")
.toString()
.split(' ')
.map((word) =>
word.substring(0, 1).toUpperCase() + word.substring(1))
.join(' '));
}
name = ta.toSet().toList();
url = tt.toSet().toList();
for (var a in url) {
image.add("");
}
}
return mangaRes();
}
@override
Future<List<dynamic>> getMangaChapterUrl({required Chapter chapter}) async {
final response = await httpGet(
useUserAgent: true,
url: chapter.url!,
source: "japscan",
resDom: false) as String?;
RegExp regex = RegExp(r'<script src="/zjs/(.*?)"');
Match? match = regex.firstMatch(response!);
String zjsurl = match!.group(1)!;
baseUrl = response;
zjsUrl = "https://www.japscan.lol/zjs/$zjsurl";
zjs();
await Future.doWhile(() async {
await Future.delayed(const Duration(seconds: 1));
if (isOk == true) {
return false;
}
return true;
});
return pageUrls;
}
bool isOk = false;
String? baseUrl;
String? zjsUrl;
zjs() async {
final html = await cloudflareBypassHtml(
url: zjsUrl!, source: "japscan", useUserAgent: true);
dom.Document htmll = dom.Document.html(baseUrl!);
final strings = html
.replaceAll(RegExp(r'\\[(.*?)\\]'), '')
.split(",")
.map((s) => s.trim().replaceAll("'", "").split('').reversed.join());
final stringLookupTables = strings
.where((s) =>
s.length == 62 &&
s.split('').toSet().toList().sorted().join() ==
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz")
.toList();
if (stringLookupTables.length != 2) {
throw Exception("Expected only two lookup tables in ZJS");
}
final scrambledData =
htmll.getElementById("data")!.attributes['data-data']!;
for (var i = 0; i <= 1; i++) {
final otherIndex = i == 0 ? 1 : 0;
final lookupTable = Map.fromIterables(stringLookupTables[i].split(''),
stringLookupTables[otherIndex].split(''));
try {
final unscrambledData = scrambledData
.split('')
.map((char) => lookupTable[char] ?? char)
.join();
final decoded = utf8.decode(base64.decode(unscrambledData));
final data = jsonDecode(decoded);
pageUrls = data["imagesLink"].map((it) => it).toList();
} catch (_) {}
}
isOk = true;
}
}

View file

@ -0,0 +1,10 @@
import 'package:mangayomi/models/source_model.dart';
SourceModel mangakawaiiSource = SourceModel(
sourceName: "MangaKawaii",
url: "https://www.mangakawaii.io",
lang: "fr",
typeSource: TypeSource.single,
logoUrl: 'https://www.mangakawaii.io/assets/img/logo.png',
dateFormat: "dd.MM.yyyy",
dateFormatLocale: "en_US");

View file

@ -0,0 +1,206 @@
import 'package:html/dom.dart';
import 'package:mangayomi/models/chapter.dart';
import 'package:mangayomi/services/http_service/http_res_to_dom_html.dart';
import 'package:mangayomi/services/http_service/http_service.dart';
import 'package:mangayomi/sources/service/service.dart';
import 'package:mangayomi/sources/src/all/comick/src/utils/utils.dart';
import 'package:mangayomi/sources/utils/utils.dart';
class MangaKawaii extends MangaYomiServices {
@override
Future<GetMangaDetailModel?> getMangaDetail(
{required String imageUrl,
required String url,
required String title,
required String lang,
required String source}) async {
final dom = await httpGet(
url: 'https://www.mangakawaii.io$url',
source: source,
resDom: true) as Document?;
List detail = [];
imageUrl =
"https://cdn.mangakawaii.pics/uploads$url/cover/cover_250x350.jpg";
if (dom!.querySelectorAll('dd.text-justify.text-break').isNotEmpty) {
final tt = dom
.querySelectorAll('dd.text-justify.text-break')
.map((e) => e.text.trim())
.toList();
description = tt[0];
}
if (dom
.querySelectorAll('span.badge.bg-success.text-uppercase')
.isNotEmpty) {
final tt = dom
.querySelectorAll('span.badge.bg-success.text-uppercase')
.map((e) => e.text.trim())
.toList();
detail.add(tt[0]);
} else {
detail.add("");
}
if (dom.querySelectorAll('a[href*=author]').isNotEmpty) {
final tt = dom
.querySelectorAll('a[href*=author]')
.map((e) => e.text.trim())
.toList();
detail.add(tt[0]);
} else {
detail.add("");
}
if (dom.querySelectorAll('a[href*=category]').isNotEmpty) {
final tt = dom
.querySelectorAll('a[href*=category]')
.map((e) => e.text.trim())
.toList();
for (var ok in tt) {
genre.add(ok);
}
}
detail = detail.toSet().toList();
status = detail[0];
author = detail[1];
if (dom.querySelectorAll("tr[class*='volume-']").isNotEmpty) {
final url = dom.querySelectorAll("tr[class*='volume-']").map((e) {
RegExp exp = RegExp(r'<a href="([^"]+)"');
Iterable<Match> matches = exp.allMatches(e.outerHtml);
String? firstMatch = matches.first.group(1);
return firstMatch;
}).toList();
final htm = await httpResToDom(
url: 'https://www.mangakawaii.io${url[0]}',
headers: {"Accept-Language": "fr"});
if (htm
.querySelectorAll(
'#bottom_nav_reader > div > div > ul.chapter-pager.navbar-nav > li.nav-item.dropup.d-inline-block > ul > li > a')
.isNotEmpty) {
final tt = htm
.querySelectorAll(
'#bottom_nav_reader > div > div > ul.chapter-pager.navbar-nav > li.nav-item.dropup.d-inline-block > ul > li > a')
.map((e) => e.innerHtml)
.toList();
final urlz = htm
.querySelectorAll(
"#bottom_nav_reader > div > div > ul.chapter-pager.navbar-nav > li.nav-item.dropup.d-inline-block > ul > li ")
.map((e) => e.innerHtml)
.toList();
for (var ok in urlz) {
chapterUrl.add(ok.split('href="')[1].split('"').first);
}
for (var ok in tt) {
chapterTitle.add(ok);
}
if (dom.querySelectorAll("tr[class*='volume-']").isNotEmpty) {
final url = dom
.querySelectorAll("tr[class*='volume-']")
.map((e) => e
.querySelectorAll('td.table__date')
.map((e) => e.text.trim())
.toList()[0])
.toList();
if (urlz.length > url.length) {
for (var _ in urlz) {
chapterDate.add(parseDate(
"${DateTime.now().day}.${DateTime.now().month}.${DateTime.now().year}",
source));
}
} else {
for (var ok in url) {
chapterDate.add(parseDate(
ok.split(" ").first.toString().substring(0, 10), source));
}
}
}
}
}
return mangadetailRes(
imageUrl: imageUrl, url: url, title: title, source: source);
}
@override
Future<GetMangaModel?> getPopularManga(
{required String source, required int page}) async {
final dom = await httpGet(
url: 'https://www.mangakawaii.io/',
source: source,
resDom: true) as Document?;
if (dom!.querySelectorAll('a.hot-manga__item').isNotEmpty) {
url = dom
.querySelectorAll('a.hot-manga__item ')
.where((e) => e.attributes.containsKey('href'))
.map((e) => e.attributes['href'])
.toList();
name = dom
.querySelectorAll('a > div > div.hot-manga__item-name')
.map((e) => e.innerHtml)
.toList();
for (var ia in name) {
image.add("");
}
}
return mangaRes();
}
@override
Future<GetMangaModel?> searchManga(
{required String source, required String query}) async {
final dom = await httpGet(
url:
'https://www.mangakawaii.io/search?query=${query.trim()}&search_type=manga',
source: source,
resDom: true) as Document?;
if (dom!
.querySelectorAll(
'#page-content > div > div > ul > li > div.section__list-group-right > div.section__list-group-header > div > h4 > a')
.isNotEmpty) {
final ur = dom
.querySelectorAll(
'#page-content > div > div > ul > li > div.section__list-group-right > div.section__list-group-header > div > h4 > a')
.where((e) => e.attributes.containsKey('href'))
.map((e) => e.attributes['href'])
.toList();
for (var a in ur) {
url.add(a!);
}
final nam = dom
.querySelectorAll(
'#page-content > div > div > ul > li > div.section__list-group-right > div.section__list-group-header > div > h4 > a')
.map((e) => e.text)
.toList();
for (var a in nam) {
name.add(a);
image.add('');
}
}
return mangaRes();
}
@override
Future<List<dynamic>> getMangaChapterUrl({required Chapter chapter}) async {
final response =
await httpGet(url: chapter.url!, source: "mangakawaii", resDom: false)
as String?;
var chapterSlug = RegExp("""var chapter_slug = "([^"]*)";""")
.allMatches(response!)
.last
.group(1);
var mangaSlug = RegExp("""var oeuvre_slug = "([^"]*)";""")
.allMatches(response)
.last
.group(1);
var pages = RegExp('''"page_image":"([^"]*)"''')
.allMatches(response)
.map((e) => e.group(1));
for (var tt in pages) {
pageUrls.add(
'https://cdn.mangakawaii.pics/uploads/manga/$mangaSlug/chapters_fr/$chapterSlug/$tt');
}
return pageUrls;
}
}

View file

@ -1,191 +1,13 @@
import 'package:mangayomi/source/source_model.dart';
List<SourceModel> sourcesList = [
SourceModel(
sourceName: "MangaHere",
url: "http://www.mangahere.cc",
lang: "en",
typeSource: TypeSource.single,
logoUrl: 'http://static.mangahere.cc/v20210106/mangahere/images/logo.png',
isFullData: true,
dateFormat: "MMM dd,yyyy",
dateFormatLocale: "en",
),
// SourceModel(
// sourceName: "MangaSee",
// url: "https://mangasee123.com",
// lang: "en",
// typeSource: TypeSource.single,
// logoUrl: 'https://mangasee123.com/media/navbar.brand.png',
// dateFormat: "MMM dd,yyyy",
// dateFormatLocale: "en"),
SourceModel(
sourceName: "MangaKawaii",
url: "https://www.mangakawaii.io",
lang: "fr",
typeSource: TypeSource.single,
logoUrl: 'https://www.mangakawaii.io/assets/img/logo.png',
dateFormat: "dd.MM.yyyy",
dateFormatLocale: "en_US"),
SourceModel(
sourceName: 'Comick',
url: 'https://api.comick.fun/',
lang: 'en',
typeSource: TypeSource.comick,
logoUrl:
'https://comick.app/_next/image?url=%2Fstatic%2Ficons%2Funicorn-64.png&w=144&q=75',
dateFormat: "yyyy-MM-dd'T'HH:mm:ss'Z'",
dateFormatLocale: "en"),
SourceModel(
sourceName: 'Comick',
url: 'https://api.comick.fun/',
lang: 'ar',
typeSource: TypeSource.comick,
logoUrl:
'https://comick.app/_next/image?url=%2Fstatic%2Ficons%2Funicorn-64.png&w=144&q=75',
dateFormat: "yyyy-MM-dd'T'HH:mm:ss'Z'",
dateFormatLocale: "en"),
SourceModel(
sourceName: 'Comick',
url: 'https://api.comick.fun/',
lang: 'pt',
typeSource: TypeSource.comick,
logoUrl:
'https://comick.app/_next/image?url=%2Fstatic%2Ficons%2Funicorn-64.png&w=144&q=75',
dateFormat: "yyyy-MM-dd'T'HH:mm:ss'Z'",
dateFormatLocale: "en"),
SourceModel(
sourceName: 'Comick',
url: 'https://api.comick.fun/',
lang: 'pt-br',
typeSource: TypeSource.comick,
logoUrl:
'https://comick.app/_next/image?url=%2Fstatic%2Ficons%2Funicorn-64.png&w=144&q=75',
dateFormat: "yyyy-MM-dd'T'HH:mm:ss'Z'",
dateFormatLocale: "en"),
SourceModel(
sourceName: 'Comick',
url: 'https://api.comick.fun/',
lang: 'it',
typeSource: TypeSource.comick,
logoUrl:
'https://comick.app/_next/image?url=%2Fstatic%2Ficons%2Funicorn-64.png&w=144&q=75',
dateFormat: "yyyy-MM-dd'T'HH:mm:ss'Z'",
dateFormatLocale: "en"),
SourceModel(
sourceName: 'Comick',
url: 'https://api.comick.fun/',
lang: 'ru',
typeSource: TypeSource.comick,
logoUrl:
'https://comick.app/_next/image?url=%2Fstatic%2Ficons%2Funicorn-64.png&w=144&q=75',
dateFormat: "yyyy-MM-dd'T'HH:mm:ss'Z'",
dateFormatLocale: "en"),
SourceModel(
sourceName: 'Comick',
url: 'https://api.comick.fun/',
lang: 'es',
typeSource: TypeSource.comick,
logoUrl:
'https://comick.app/_next/image?url=%2Fstatic%2Ficons%2Funicorn-64.png&w=144&q=75',
dateFormat: "yyyy-MM-dd'T'HH:mm:ss'Z'",
dateFormatLocale: "en"),
SourceModel(
sourceName: 'Comick',
url: 'https://api.comick.fun/',
lang: 'es-419',
typeSource: TypeSource.comick,
logoUrl:
'https://comick.app/_next/image?url=%2Fstatic%2Ficons%2Funicorn-64.png&w=144&q=75',
dateFormat: "yyyy-MM-dd'T'HH:mm:ss'Z'",
dateFormatLocale: "en"),
SourceModel(
sourceName: 'Comick',
url: 'https://api.comick.fun/',
lang: 'id',
typeSource: TypeSource.comick,
logoUrl:
'https://comick.app/_next/image?url=%2Fstatic%2Ficons%2Funicorn-64.png&w=144&q=75',
dateFormat: "yyyy-MM-dd'T'HH:mm:ss'Z'",
dateFormatLocale: "en"),
SourceModel(
sourceName: 'Comick',
url: 'https://api.comick.fun/',
lang: 'hi',
typeSource: TypeSource.comick,
logoUrl:
'https://comick.app/_next/image?url=%2Fstatic%2Ficons%2Funicorn-64.png&w=144&q=75',
dateFormat: "yyyy-MM-dd'T'HH:mm:ss'Z'",
dateFormatLocale: "en"),
SourceModel(
sourceName: 'Comick',
url: 'https://api.comick.fun/',
lang: 'de',
typeSource: TypeSource.comick,
logoUrl:
'https://comick.app/_next/image?url=%2Fstatic%2Ficons%2Funicorn-64.png&w=144&q=75',
dateFormat: "yyyy-MM-dd'T'HH:mm:ss'Z'",
dateFormatLocale: "en"),
SourceModel(
sourceName: 'Comick',
url: 'https://api.comick.fun/',
lang: 'ja',
typeSource: TypeSource.comick,
logoUrl:
'https://comick.app/_next/image?url=%2Fstatic%2Ficons%2Funicorn-64.png&w=144&q=75',
dateFormat: "yyyy-MM-dd'T'HH:mm:ss'Z'",
dateFormatLocale: "en"),
SourceModel(
sourceName: 'Comick',
url: 'https://api.comick.fun/',
lang: 'tr',
typeSource: TypeSource.comick,
logoUrl:
'https://comick.app/_next/image?url=%2Fstatic%2Ficons%2Funicorn-64.png&w=144&q=75',
dateFormat: "yyyy-MM-dd'T'HH:mm:ss'Z'",
dateFormatLocale: "en"),
SourceModel(
sourceName: 'Comick',
url: 'https://api.comick.fun/',
lang: 'pl',
typeSource: TypeSource.comick,
logoUrl:
'https://comick.app/_next/image?url=%2Fstatic%2Ficons%2Funicorn-64.png&w=144&q=75',
dateFormat: "yyyy-MM-dd'T'HH:mm:ss'Z'",
dateFormatLocale: "en"),
SourceModel(
sourceName: 'Comick',
url: 'https://api.comick.fun/',
lang: 'zh',
typeSource: TypeSource.comick,
logoUrl:
'https://comick.app/_next/image?url=%2Fstatic%2Ficons%2Funicorn-64.png&w=144&q=75',
dateFormat: "yyyy-MM-dd'T'HH:mm:ss'Z'",
dateFormatLocale: "en"),
SourceModel(
sourceName: 'Comick',
url: 'https://api.comick.fun/',
lang: 'zh-hk',
typeSource: TypeSource.comick,
logoUrl:
'https://comick.app/_next/image?url=%2Fstatic%2Ficons%2Funicorn-64.png&w=144&q=75',
dateFormat: "yyyy-MM-dd'T'HH:mm:ss'Z'",
dateFormatLocale: "en"),
SourceModel(
sourceName: 'Comick',
url: 'https://api.comick.fun/',
lang: 'fr',
typeSource: TypeSource.comick,
logoUrl:
'https://comick.app/_next/image?url=%2Fstatic%2Ficons%2Funicorn-64.png&w=144&q=75',
dateFormat: "yyyy-MM-dd'T'HH:mm:ss'Z'",
dateFormatLocale: "en"),
import 'package:mangayomi/models/source_model.dart';
List<SourceModel> mangathemesiaSourcesList = [
SourceModel(
sourceName: "KomikLab",
url: "https://komiklab.com",
lang: "en",
typeSource: TypeSource.mangathemesia,
logoUrl: '',
dateFormat: "MMMM dd, yyyy",
dateFormat: "MMMM dd, yyyy",
dateFormatLocale: "en"),
SourceModel(
sourceName: "AnimatedGlitchedScans",
@ -193,7 +15,7 @@ List<SourceModel> sourcesList = [
lang: "en",
typeSource: TypeSource.mangathemesia,
logoUrl: '',
dateFormat: "MMMM dd, yyyy",
dateFormat: "MMMM dd, yyyy",
dateFormatLocale: "en"),
// SourceModel(
// sourceName: "ArenaScans",
@ -234,7 +56,7 @@ List<SourceModel> sourcesList = [
lang: "en",
typeSource: TypeSource.mangathemesia,
logoUrl: '',
dateFormat: "MMMM dd, yyyy",
dateFormat: "MMMM dd, yyyy",
dateFormatLocale: "en"),
SourceModel(
sourceName: "DiskusScan",
@ -272,7 +94,7 @@ List<SourceModel> sourcesList = [
typeSource: TypeSource.mangathemesia,
logoUrl: '',
isCloudflare: true,
dateFormat: "MMMM dd, yyyy",
dateFormat: "MMMM dd, yyyy",
dateFormatLocale: "en"),
// SourceModel(
@ -487,70 +309,4 @@ List<SourceModel> sourcesList = [
'https://xcalibrscans.com/wp-content/uploads/2021/06/xcalibr-dark-v3.png',
dateFormat: "MMMM dd, yyyy",
dateFormatLocale: "en"),
// SourceModel(
// sourceName: "Fallen Angels",
// url: "https://manga.fascans.com",
// lang: "en",
// typeSource: TypeSource.mmrcms,
// logoUrl: '',
// dateFormat: "d MMM. yyyy",
// dateFormatLocale: "en_US"),
SourceModel(
sourceName: "Scan FR",
url: "https://www.scan-fr.org",
lang: "fr",
typeSource: TypeSource.mmrcms,
logoUrl: '',
dateFormat: "d MMM. yyyy",
dateFormatLocale: "en_US"),
SourceModel(
sourceName: "Scan VF",
url: "https://www.scan-vf.net",
lang: "fr",
typeSource: TypeSource.mmrcms,
logoUrl: '',
dateFormat: "d MMM. yyyy",
dateFormatLocale: "en_US"), //
SourceModel(
sourceName: "Komikid",
url: "https://www.komikid.com",
lang: "id",
typeSource: TypeSource.mmrcms,
logoUrl: '',
dateFormat: "d MMM. yyyy",
dateFormatLocale: "en_US"),
// SourceModel(
// sourceName: "MangaHanta",
// url: "http://mangahanta.com",
// lang: "tr",
// typeSource: TypeSource.mmrcms,
// logoUrl: '',
// dateFormat: "d MMM. yyyy",
// dateFormatLocale: "en_US"),
SourceModel(
sourceName: "MangaID",
url: "https://mangaid.click",
lang: "id",
typeSource: TypeSource.mmrcms,
logoUrl: '',
dateFormat: "d MMM. yyyy",
dateFormatLocale: "en_US"),
SourceModel(
sourceName: "Jpmangas",
url: "https://jpmangas.cc",
lang: "fr",
typeSource: TypeSource.mmrcms,
logoUrl: '',
dateFormat: "d MMM. yyyy",
dateFormatLocale: "en_US"),
SourceModel(
sourceName: "Japscan",
url: "https://japscan.lol",
lang: "fr",
typeSource: TypeSource.single,
logoUrl: '',
isCloudflare: true,
dateFormat: "d MMM. yyyy",
dateFormatLocale: "en_US"),
];
];

View file

@ -0,0 +1,272 @@
import 'package:html/dom.dart';
import 'package:mangayomi/models/chapter.dart';
import 'package:mangayomi/services/http_service/http_service.dart';
import 'package:mangayomi/sources/service/service.dart';
import 'package:mangayomi/sources/utils/utils.dart';
class MangaThemeSia extends MangaYomiServices {
@override
Future<GetMangaDetailModel?> getMangaDetail(
{required String imageUrl,
required String url,
required String title,
required String lang,
required String source}) async {
final dom = await httpGet(
url: url,
source: source,
resDom: true,
useUserAgent: true) as Document?;
if (dom!
.querySelectorAll(
'div.bigcontent, div.animefull, div.main-info, div.postbody')
.isNotEmpty) {
final resHtml = dom.querySelector(
'div.bigcontent, div.animefull, div.main-info, div.postbody');
if (resHtml!.querySelectorAll('.tsinfo .imptdt').isNotEmpty) {
status = resHtml
.querySelectorAll('.tsinfo .imptdt')
.where((e) =>
e.innerHtml.contains("Status") ||
e.innerHtml.contains("Situação"))
.map((e) => e.innerHtml.contains("Situação")
? e.text.replaceAll('Situação', '').trim()
: e.text.replaceAll('Status', '').trim())
.toList()
.last;
} else if (resHtml.querySelectorAll('.infotable tr').isNotEmpty) {
status = resHtml
.querySelectorAll('.infotable tr')
.where((e) =>
e.innerHtml.toLowerCase().contains('statut') ||
e.innerHtml.toLowerCase().contains('status'))
.map((e) => e.querySelector('td:last-child')!.text)
.toList()
.first;
} else if (resHtml.querySelectorAll('.fmed').isNotEmpty) {
status = resHtml
.querySelectorAll('.tsinfo .imptdt')
.map((e) => e.text.replaceAll('Status', '').trim())
.toList()
.first;
} else {
status = "";
}
//2
if (resHtml.querySelectorAll('.fmed').isNotEmpty) {
author = resHtml
.querySelectorAll('.fmed')
.where((e) => e.innerHtml.contains("Author"))
.map((e) => e.text.replaceAll('Author', '').trim())
.toList()
.first;
} else if (resHtml.querySelectorAll('.tsinfo .imptdt').isNotEmpty) {
author = resHtml
.querySelectorAll('.tsinfo .imptdt')
.where((e) =>
e.innerHtml.contains("Author") ||
e.innerHtml.contains("Auteur") ||
e.innerHtml.contains("Autor"))
.map((e) => e.innerHtml.contains("Autor")
? e.text.replaceAll('Autor', '').trim()
: e.innerHtml.contains("Autheur")
? e.text.replaceAll('Auteur', '').trim()
: e.text.replaceAll('Author', '').trim())
.toList()
.first;
} else if (resHtml.querySelectorAll('.infotable tr').isNotEmpty) {
author = resHtml
.querySelectorAll('.infotable tr')
.where((e) =>
e.innerHtml.toLowerCase().contains('auteur') ||
e.innerHtml.toLowerCase().contains('author'))
.map((e) => e.querySelector('td:last-child')!.text)
.toList()
.first;
} else {
author = "";
}
description = resHtml
.querySelector(".desc, .entry-content[itemprop=description]")!
.text;
if (resHtml
.querySelectorAll('div.gnr a, .mgen a, .seriestugenre a')
.isNotEmpty) {
final tt = resHtml
.querySelectorAll('div.gnr a, .mgen a, .seriestugenre a')
.map((e) => e.text.trim())
.toList();
for (var ok in tt) {
genre.add(ok);
}
} else {
genre.add('');
}
if (resHtml.querySelectorAll('#chapterlist a').isNotEmpty) {
final udl = resHtml
.querySelectorAll('#chapterlist a ')
.where((e) => e.attributes.containsKey('href'))
.map((e) => e.attributes['href'])
.toList();
for (var ok in udl) {
chapterUrl.add(ok!);
}
}
if (resHtml.querySelectorAll('.lch a, .chapternum').isNotEmpty) {
final tt = resHtml
.querySelectorAll('.lch a, .chapternum')
.map((e) => e.text.trim())
.toList();
tt.removeWhere((element) => element.contains('{{number}}'));
for (var ok in tt) {
chapterTitle.add(ok.trimLeft());
}
}
if (resHtml.querySelectorAll('.chapterdate').isNotEmpty) {
final tt = resHtml
.querySelectorAll('.chapterdate')
.map((e) => e.text.trim())
.toList();
tt.removeWhere((element) => element.contains('{{date}}'));
for (var ok in tt) {
chapterDate.add(parseDate(ok, source));
}
}
}
return mangadetailRes(
imageUrl: imageUrl, url: url, title: title, source: source);
}
@override
Future<GetMangaModel?> getPopularManga(
{required String source, required int page}) async {
source = source.toLowerCase();
final dom = await httpGet(
useUserAgent: true,
url: '${getWpMangaUrl(source)}/manga/?title=&page=$page&order=popular',
source: source,
resDom: true) as Document?;
if (dom!
.querySelectorAll(
'.utao .uta .imgu, .listupd .bs .bsx, .listo .bs .bsx')
.isNotEmpty) {
url = dom
.querySelectorAll(
'.utao .uta .imgu, .listupd .bs .bsx, .listo .bs .bsx')
.map((e) {
RegExp exp = RegExp(r'href="([^"]+)"');
Iterable<Match> matches = exp.allMatches(e.innerHtml);
String? firstMatch = matches.first.group(1);
return firstMatch;
}).toList();
image = dom
.querySelectorAll(
'.utao .uta .imgu, .listupd .bs .bsx, .listo .bs .bsx')
.map((e) {
RegExp exp = RegExp(r'src="([^"]+)"');
Iterable<Match> matches = exp.allMatches(e.innerHtml);
String? firstMatch = matches.first.group(1);
return firstMatch;
}).toList();
name = dom
.querySelectorAll(
'.utao .uta .imgu, .listupd .bs .bsx, .listo .bs .bsx ')
.map((e) {
RegExp exp = RegExp(r'title="([^"]+)"');
Iterable<Match> matches = exp.allMatches(e.innerHtml);
String? firstMatch = matches.first.group(1);
return firstMatch;
}).toList();
}
return mangaRes();
}
@override
Future<GetMangaModel?> searchManga(
{required String source, required String query}) async {
final dom = await httpGet(
url: '${getWpMangaUrl(source)}/?s=${query.trim()}',
source: source,
resDom: true) as Document?;
if (dom!
.querySelectorAll(
'#content > div > div.postbody > div > div.listupd > div > div > a')
.isNotEmpty) {
url = dom
.querySelectorAll(
'#content > div > div.postbody > div > div.listupd > div > div > a ')
.where((e) => e.attributes.containsKey('href'))
.map((e) => e.attributes['href'])
.toList();
image = dom
.querySelectorAll(
' #content > div > div.postbody > div > div.listupd > div > div > a > div > img')
.where((e) => e.attributes.containsKey('src'))
.map((e) => e.attributes['src'])
.toList();
name = dom
.querySelectorAll(
'#content > div > div.postbody > div > div.listupd > div > div > a ')
.where((e) => e.attributes.containsKey('title'))
.map((e) => e.attributes['title'])
.toList();
}
return mangaRes();
}
@override
Future<List<dynamic>> getMangaChapterUrl({required Chapter chapter}) async {
final dom = await httpGet(
useUserAgent: true,
url: chapter.url!,
source: "mangathemesia",
resDom: true) as Document?;
if (dom!.querySelectorAll('#readerarea').isNotEmpty) {
final ta =
dom.querySelectorAll('#readerarea').map((e) => e.outerHtml).toList();
final RegExp regex = RegExp(r'<img[^>]+src="([^"]+)"');
final Iterable<Match> matches = regex.allMatches(ta.first);
final List<String?> urls = matches.map((m) => m.group(1)).toList();
Iterable<Match> matchess = [];
if (dom.querySelectorAll(' #select-paged ').isNotEmpty) {
final ee = dom
.querySelectorAll(' #select-paged ')
.map((e) => e.outerHtml)
.toList();
final RegExp regexx = RegExp(r'value="([^"]+)"');
matchess = regexx.allMatches(ee.first);
}
final List<String?> urlss = matchess.map((m) => m.group(1)).toList();
if (urls.length == 1 && urls.isNotEmpty) {
for (var i = 0; i < urlss.length; i++) {
if (urlss[i]!.length == 1) {
pageUrls.add(
urls.first!.replaceAll("001", '00${int.parse(urlss[i]!) + 1}'));
} else if (urlss[i]!.length == 2) {
pageUrls.add(
urls.first!.replaceAll("001", '0${int.parse(urlss[i]!) + 1}'));
} else if (urlss[i]!.length == 3) {
pageUrls.add(
urls.first!.replaceAll("001", '${int.parse(urlss[i]!) + 1}'));
}
}
} else if (urls.length > 1 && urls.isNotEmpty) {
for (var tt in urls) {
pageUrls.add(tt);
}
}
}
return pageUrls;
}
}

View file

@ -0,0 +1,62 @@
import 'package:mangayomi/models/source_model.dart';
List<SourceModel> mmrcmsSourcesList = [
// SourceModel(
// sourceName: "Fallen Angels",
// url: "https://manga.fascans.com",
// lang: "en",
// typeSource: TypeSource.mmrcms,
// logoUrl: '',
// dateFormat: "d MMM. yyyy",
// dateFormatLocale: "en_US"),
SourceModel(
sourceName: "Scan FR",
url: "https://www.scan-fr.org",
lang: "fr",
typeSource: TypeSource.mmrcms,
logoUrl: '',
dateFormat: "d MMM. yyyy",
dateFormatLocale: "en_US"),
SourceModel(
sourceName: "Scan VF",
url: "https://www.scan-vf.net",
lang: "fr",
typeSource: TypeSource.mmrcms,
logoUrl: '',
dateFormat: "d MMM. yyyy",
dateFormatLocale: "en_US"), //
SourceModel(
sourceName: "Komikid",
url: "https://www.komikid.com",
lang: "id",
typeSource: TypeSource.mmrcms,
logoUrl: '',
dateFormat: "d MMM. yyyy",
dateFormatLocale: "en_US"),
// SourceModel(
// sourceName: "MangaHanta",
// url: "http://mangahanta.com",
// lang: "tr",
// typeSource: TypeSource.mmrcms,
// logoUrl: '',
// dateFormat: "d MMM. yyyy",
// dateFormatLocale: "en_US"),
SourceModel(
sourceName: "MangaID",
url: "https://mangaid.click",
lang: "id",
typeSource: TypeSource.mmrcms,
logoUrl: '',
dateFormat: "d MMM. yyyy",
dateFormatLocale: "en_US"),
SourceModel(
sourceName: "Jpmangas",
url: "https://jpmangas.cc",
lang: "fr",
typeSource: TypeSource.mmrcms,
logoUrl: '',
dateFormat: "d MMM. yyyy",
dateFormatLocale: "en_US"),
];

View file

@ -0,0 +1,183 @@
import 'dart:convert';
import 'package:html/dom.dart';
import 'package:mangayomi/models/chapter.dart';
import 'package:mangayomi/services/http_service/http_service.dart';
import 'package:mangayomi/sources/service/service.dart';
import 'package:mangayomi/sources/utils/utils.dart';
import 'package:mangayomi/utils/reg_exp_matcher.dart';
class Mmrcms extends MangaYomiServices {
@override
Future<GetMangaDetailModel?> getMangaDetail(
{required String imageUrl,
required String url,
required String title,
required String lang,
required String source}) async {
final dom =
await httpGet(url: url, source: source, resDom: true) as Document?;
description = dom!
.querySelectorAll('.row .well p')
.map((e) => e.text.trim())
.toList()
.first;
status = dom
.querySelectorAll('.row .dl-horizontal dt')
.where((e) =>
e.innerHtml.toString().toLowerCase().contains("status") ||
e.innerHtml.toString().toLowerCase().contains("statut") ||
e.innerHtml.toString().toLowerCase().contains("estado") ||
e.innerHtml.toString().toLowerCase().contains("durum"))
.map((e) => e.nextElementSibling!.text.trim())
.toList()
.first;
if (dom.querySelectorAll(".row .dl-horizontal dt").isNotEmpty) {
author = dom
.querySelectorAll('.row .dl-horizontal dt')
.where((e) =>
e.innerHtml.toString().toLowerCase().contains("auteur(s)") ||
e.innerHtml.toString().toLowerCase().contains("author(s)") ||
e.innerHtml.toString().toLowerCase().contains("autor(es)") ||
e.innerHtml.toString().toLowerCase().contains("yazar(lar)") ||
e.innerHtml.toString().toLowerCase().contains("mangaka(lar)") ||
e.innerHtml
.toString()
.toLowerCase()
.contains("pengarang/penulis") ||
e.innerHtml.toString().toLowerCase().contains("autor") ||
e.innerHtml.toString().toLowerCase().contains("penulis"))
.map((e) => e.nextElementSibling!.text
.trim()
.replaceAll(RegExp(r"\s+\b|\b\s"), ""))
.toList()
.first;
final genr = dom
.querySelectorAll('.row .dl-horizontal dt')
.where((e) =>
e.innerHtml.toString().toLowerCase().contains("categories") ||
e.innerHtml.toString().toLowerCase().contains("categorías") ||
e.innerHtml.toString().toLowerCase().contains("catégories") ||
e.innerHtml.toString().toLowerCase().contains("kategoriler") ||
e.innerHtml.toString().toLowerCase().contains("categorias") ||
e.innerHtml.toString().toLowerCase().contains("kategorie") ||
e.innerHtml.toString().toLowerCase().contains("kategori") ||
e.innerHtml.toString().toLowerCase().contains("tagi"))
.map((e) => e.nextElementSibling!.text.trim())
.toList();
if (genr.isNotEmpty) {
genre = genr.first.replaceAll(RegExp(r"\s+\b|\b\s"), "").split(',');
}
}
final rrr = dom.querySelectorAll(".row [class^=img-responsive]");
final data = rrr.map((e) => e.outerHtml).toList();
if (source.toLowerCase() == 'jpmangas' ||
source.toLowerCase() == 'fr scan') {
imageUrl = regSrcMatcher(data.first).replaceAll('//', 'https://');
} else {
imageUrl = regSrcMatcher(data.first);
}
final ttt = dom
.querySelectorAll("ul[class^=chapters] > li:not(.btn), table.table tr");
if (ttt.isNotEmpty) {
final data = ttt
.map((e) => e.querySelector("[class^=chapter-title-rtl]")!)
.toList();
var name = data;
for (var iaa in name) {
chapterTitle.add(iaa.getElementsByTagName("a").first.text);
chapterUrl
.add(regHrefMatcher(iaa.getElementsByTagName("a").first.outerHtml));
}
final date = ttt
.map((e) => e
.getElementsByClassName("date-chapter-title-rtl")
.map((e) => e.text.trim())
.first)
.toList();
for (var da in date) {
chapterDate.add(parseDate(da, source));
}
}
return mangadetailRes(
imageUrl: imageUrl, url: url, title: title, source: source);
}
@override
Future<GetMangaModel?> getPopularManga(
{required String source, required int page}) async {
final dom = await httpGet(
url:
'${getWpMangaUrl(source)}/filterList?page=$page&sortBy=views&asc=false',
source: source,
resDom: true) as Document?;
final urlElement = dom!.getElementsByClassName('chart-title');
for (var e in urlElement) {
RegExp exp = RegExp(r'href="([^"]+)"');
Iterable<Match> matches = exp.allMatches(e.outerHtml);
String? firstMatch = matches.first.group(1);
url.add(firstMatch);
name.add(e.text);
}
final imgElement = dom.getElementsByTagName('img');
for (var e in imgElement) {
RegExp exp = RegExp(r'src="([^"]+)"');
Iterable<Match> matches = exp.allMatches(e.outerHtml);
String? firstMatch = matches.first.group(1);
image.add(firstMatch);
}
return mangaRes();
}
@override
Future<GetMangaModel?> searchManga(
{required String source, required String query}) async {
final response = await httpGet(
url: '${getWpMangaUrl(source)}/search?query=${query.trim()}',
source: source,
resDom: false) as String?;
final rep = jsonDecode(response!);
for (var ok in rep['suggestions']) {
if (source == 'Read Comics Online') {
url.add('${getWpMangaUrl(source)}/comic/${ok['data']}');
} else if (source == 'Scan VF') {
url.add('${getWpMangaUrl(source)}/${ok['data']}');
} else {
url.add('${getWpMangaUrl(source)}/manga/${ok['data']}');
}
name.add(ok["value"]);
image.add('');
}
return mangaRes();
}
@override
Future<List<dynamic>> getMangaChapterUrl({required Chapter chapter}) async {
final dom = await httpGet(
useUserAgent: true,
url: chapter.url!,
source: chapter.manga.value!.source!.toLowerCase(),
resDom: true) as Document?;
if (dom!.querySelectorAll('#all > .img-responsive').isNotEmpty) {
pageUrls = dom.querySelectorAll('#all > .img-responsive').map((e) {
final RegExp regexx = RegExp(r'data-src="([^"]+)"');
if (chapter.manga.value!.source!.toLowerCase() == 'fr scan') {
return regexx
.allMatches(e.outerHtml)
.first
.group(1)!
.replaceAll('//', 'https://')
.replaceAll(RegExp(r"\s+\b|\b\s"), "");
}
return regexx
.allMatches(e.outerHtml)
.first
.group(1)!
.replaceAll(RegExp(r"\s+\b|\b\s"), "");
}).toList();
}
return pageUrls;
}
}

View file

@ -0,0 +1,65 @@
import 'package:intl/intl.dart';
import 'package:mangayomi/models/source_model.dart';
import 'package:mangayomi/sources/source_list.dart';
String getWpMangaUrl(String source) {
String url = "";
for (var i = 0; i < sourcesList.length; i++) {
if (sourcesList[i].sourceName.toLowerCase() == source.toLowerCase()) {
url = sourcesList[i].url;
}
}
return url;
}
TypeSource getWpMangTypeSource(String source) {
TypeSource? typeSource;
for (var i = 0; i < sourcesList.length; i++) {
if (sourcesList[i].sourceName.toLowerCase() == source.toLowerCase()) {
typeSource = sourcesList[i].typeSource;
}
}
return typeSource!;
}
String getFormatDate(String source) {
String? dateFormat;
for (var i = 0; i < sourcesList.length; i++) {
if (sourcesList[i].sourceName.toLowerCase() == source.toLowerCase()) {
dateFormat = sourcesList[i].dateFormat;
}
}
return dateFormat!;
}
String getFormatDateLocale(String source) {
String? dateFormatLocale;
for (var i = 0; i < sourcesList.length; i++) {
if (sourcesList[i].sourceName.toLowerCase() == source.toLowerCase()) {
dateFormatLocale = sourcesList[i].dateFormatLocale;
}
}
return dateFormatLocale!;
}
bool isCloudflare(String source) {
bool? isCloudflare;
for (var i = 0; i < sourcesList.length; i++) {
if (sourcesList[i].sourceName.toLowerCase() == source.toLowerCase()) {
isCloudflare = sourcesList[i].isCloudflare;
}
}
return isCloudflare!;
}
String utilDate(String data) {
DateTime date = DateTime.parse(data);
return date.millisecondsSinceEpoch.toString();
}
parseDate(String data, String source) {
source = source.toLowerCase();
DateTime date = DateFormat(getFormatDate(source), getFormatDateLocale(source))
.parse(data);
return date.millisecondsSinceEpoch.toString();
}

View file

@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:mangayomi/providers/storage_provider.dart';
import 'package:mangayomi/source/source_model.dart';
import 'package:mangayomi/models/source_model.dart';
import 'package:mangayomi/views/browse/extension/extension_screen.dart';
import 'package:mangayomi/views/browse/migrate_screen.dart';
import 'package:mangayomi/views/browse/sources_screen.dart';

View file

@ -2,8 +2,8 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:mangayomi/providers/hive_provider.dart';
import 'package:mangayomi/source/source_list.dart';
import 'package:mangayomi/source/source_model.dart';
import 'package:mangayomi/models/source_model.dart';
import 'package:mangayomi/sources/source_list.dart';
import 'package:mangayomi/utils/lang.dart';
import 'package:mangayomi/views/browse/extension/widgets/extension_lang_list_tile_widget.dart';

View file

@ -5,7 +5,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:grouped_list/grouped_list.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:mangayomi/providers/hive_provider.dart';
import 'package:mangayomi/source/source_model.dart';
import 'package:mangayomi/models/source_model.dart';
import 'package:mangayomi/utils/lang.dart';
import 'package:mangayomi/views/browse/extension/refresh_filter_data.dart';
import 'package:mangayomi/views/browse/extension/widgets/extension_list_tile_widget.dart';

View file

@ -1,6 +1,6 @@
import 'package:mangayomi/providers/hive_provider.dart';
import 'package:mangayomi/source/source_list.dart';
import 'package:mangayomi/source/source_model.dart';
import 'package:mangayomi/models/source_model.dart';
import 'package:mangayomi/sources/source_list.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'refresh_filter_data.g.dart';

View file

@ -8,7 +8,7 @@ import 'package:mangayomi/models/manga.dart';
import 'package:mangayomi/providers/hive_provider.dart';
import 'package:mangayomi/services/get_manga_detail.dart';
import 'package:mangayomi/services/search_manga.dart';
import 'package:mangayomi/source/source_model.dart';
import 'package:mangayomi/models/source_model.dart';
import 'package:mangayomi/utils/cached_network.dart';
import 'package:mangayomi/utils/headers.dart';
import 'package:mangayomi/utils/lang.dart';

View file

@ -6,7 +6,7 @@ import 'package:grouped_list/grouped_list.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:mangayomi/models/manga_type.dart';
import 'package:mangayomi/providers/hive_provider.dart';
import 'package:mangayomi/source/source_model.dart';
import 'package:mangayomi/models/source_model.dart';
import 'package:mangayomi/utils/headers.dart';
import 'package:mangayomi/utils/lang.dart';
import 'package:mangayomi/views/browse/extension/refresh_filter_data.dart';

View file

@ -866,7 +866,7 @@ class ChapterSetDownloadStateProvider
}
String _$chaptersListttStateHash() =>
r'2f81698d88c8087360e33c19884bfbb018604269';
r'5f1b0d2be32fcb904c12c5735f1340c8b33400a9';
/// See also [ChaptersListttState].
@ProviderFor(ChaptersListttState)

View file

@ -1,12 +1,8 @@
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:intl/intl.dart';
import 'package:mangayomi/models/chapter.dart';
import 'package:mangayomi/utils/date.dart';
import 'package:mangayomi/views/manga/reader/providers/push_router.dart';
import 'package:mangayomi/models/manga.dart';
import 'package:mangayomi/utils/colors.dart';
import 'package:mangayomi/utils/utils.dart';
import 'package:mangayomi/views/manga/detail/providers/state_providers.dart';

View file

@ -18,7 +18,7 @@ Future<List<dynamic>> downloadChapter(
DownloadChapterRef ref, {
required Chapter chapter,
}) async {
List urll = [];
List pageUrls = [];
List<DownloadTask> tasks = [];
final StorageProvider storageProvider = StorageProvider();
await storageProvider.requestPermission();
@ -39,8 +39,8 @@ Future<List<dynamic>> downloadChapter(
chapter: chapter,
).future)
.then((value) {
if (value.urll.isNotEmpty) {
urll = value.urll;
if (value.pageUrls.isNotEmpty) {
pageUrls = value.pageUrls;
isOk = true;
}
});
@ -52,8 +52,8 @@ Future<List<dynamic>> downloadChapter(
return true;
});
if (urll.isNotEmpty) {
for (var index = 0; index < urll.length; index++) {
if (pageUrls.isNotEmpty) {
for (var index = 0; index < pageUrls.length; index++) {
final path2 = Directory("${path1.path}downloads/");
final path4 = Directory(
"${path2.path}${manga.source} (${manga.lang!.toUpperCase()})/");
@ -83,9 +83,9 @@ Future<List<dynamic>> downloadChapter(
if (await File("${path.path}" "${padIndex(index + 1)}.jpg").exists()) {
} else {
tasks.add(DownloadTask(
taskId: urll[index],
taskId: pageUrls[index],
headers: headers(manga.source!),
url: urll[index],
url: pageUrls[index],
filename: "${padIndex(index + 1)}.jpg",
baseDirectory:
Platform.isWindows || Platform.isMacOS || Platform.isLinux
@ -104,9 +104,9 @@ Future<List<dynamic>> downloadChapter(
if (await File("${path.path}" "${padIndex(index + 1)}.jpg").exists()) {
} else {
tasks.add(DownloadTask(
taskId: urll[index],
taskId: pageUrls[index],
headers: headers(manga.source!),
url: urll[index],
url: pageUrls[index],
filename: "${padIndex(index + 1)}.jpg",
baseDirectory:
Platform.isWindows || Platform.isMacOS || Platform.isLinux
@ -122,7 +122,7 @@ Future<List<dynamic>> downloadChapter(
}
}
}
if (tasks.isEmpty && urll.isNotEmpty) {
if (tasks.isEmpty && pageUrls.isNotEmpty) {
final model = DownloadModel(
chapterId: chapter.id,
mangaName: manga.name,
@ -133,7 +133,7 @@ Future<List<dynamic>> downloadChapter(
total: 0,
isDownload: true,
mangaId: manga.id!,
taskIds: urll,
taskIds: pageUrls,
isStartDownload: false);
ref
@ -150,7 +150,7 @@ Future<List<dynamic>> downloadChapter(
chapterId: chapter.id,
total: tasks.length,
isDownload: (succeeded == tasks.length) ? true : false,
taskIds: urll,
taskIds: pageUrls,
isStartDownload: true,
chapterName: chapter.name!,
mangaSource: manga.source,
@ -182,5 +182,5 @@ Future<List<dynamic>> downloadChapter(
);
}
}
return urll;
return pageUrls;
}

View file

@ -6,7 +6,7 @@ part of 'download_provider.dart';
// RiverpodGenerator
// **************************************************************************
String _$downloadChapterHash() => r'240d09a7cf3726a4b75c2a45ef12a50de20d5a0e';
String _$downloadChapterHash() => r'd750be212254a2aefbbb7825b98235dafe41ace5';
/// Copied from Dart SDK
class _SystemHash {

View file

@ -5,6 +5,7 @@ import 'package:go_router/go_router.dart';
import 'package:mangayomi/models/manga_type.dart';
import 'package:mangayomi/services/get_manga_detail.dart';
import 'package:mangayomi/services/get_popular_manga.dart';
import 'package:mangayomi/sources/utils/utils.dart';
import 'package:mangayomi/utils/colors.dart';
import 'package:mangayomi/views/manga/home/manga_search_screen.dart';
import 'package:mangayomi/views/widgets/bottom_text_widget.dart';

View file

@ -40,12 +40,12 @@ class MangaReaderView extends ConsumerWidget {
ref.read(readerControllerProvider(chapter: chapter).notifier);
return chapterData.when(
data: (data) {
if (data.urll.isEmpty) {
if (data.pageUrls.isEmpty) {
Navigator.pop(context);
}
return MangaChapterPageGallery(
path: data.path!,
url: data.urll,
url: data.pageUrls,
readerController: readerController,
isLocaleList: data.isLocaleList,
chapter: chapter,

View file

@ -1,6 +1,8 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:mangayomi/providers/hive_provider.dart';
import 'package:mangayomi/utils/colors.dart';
import 'package:mangayomi/views/more/settings/appearance/providers/flex_scheme_color_state_provider.dart';
import 'package:mangayomi/views/more/settings/appearance/providers/theme_mode_state_provider.dart';
// import 'package:rive/rive.dart';
@ -17,13 +19,23 @@ class _DarkModeButtonState extends ConsumerState<DarkModeButton> {
@override
Widget build(BuildContext context) {
bool isLight = ref.watch(themeModeStateProvider);
return ListTile(
onTap: () {
int flexColorIndex = ref
.watch(hiveBoxSettingsProvider)
.get('FlexColorIndex', defaultValue: 7);
if (!isLight == true) {
ref.read(themeModeStateProvider.notifier).setLightTheme();
} else {
ref.read(themeModeStateProvider.notifier).setDarkTheme();
}
!isLight
? ref.read(flexSchemeColorStateProvider.notifier).setTheme(
ThemeAA.schemes[flexColorIndex].light, flexColorIndex)
: ref.read(flexSchemeColorStateProvider.notifier).setTheme(
ThemeAA.schemes[flexColorIndex].dark, flexColorIndex);
},
title: const Text("Dark mode"),
subtitle: Text(

View file

@ -5,6 +5,7 @@ import 'package:mangayomi/main.dart';
import 'package:mangayomi/models/chapter.dart';
import 'package:mangayomi/models/manga.dart';
import 'package:mangayomi/services/get_manga_detail.dart';
import 'package:mangayomi/sources/service/service.dart';
import 'package:mangayomi/utils/cached_network.dart';
import 'package:mangayomi/utils/headers.dart';
import 'package:mangayomi/views/widgets/bottom_text_widget.dart';