mirror of
https://github.com/kodjodevf/mangayomi.git
synced 2026-01-11 22:40:36 +00:00
initial adding madara multi source
This commit is contained in:
parent
c936eda540
commit
33eb2458a4
13 changed files with 300 additions and 32 deletions
|
|
@ -60,5 +60,7 @@ enum TypeSource {
|
|||
|
||||
mmrcms,
|
||||
|
||||
heancms
|
||||
heancms,
|
||||
|
||||
madara
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import 'package:mangayomi/models/settings.dart';
|
|||
import 'package:mangayomi/models/source.dart';
|
||||
import 'package:mangayomi/providers/storage_provider.dart';
|
||||
import 'package:mangayomi/sources/multisrc/heancms/heancms.dart';
|
||||
import 'package:mangayomi/sources/multisrc/madara/src/madara.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';
|
||||
|
|
@ -104,6 +105,13 @@ Future<GetChapterUrlModel> getChapterUrl(
|
|||
else if (getMangaTypeSource(source) == TypeSource.heancms) {
|
||||
pageUrls = await HeanCms().getChapterUrl(chapter: chapter, ref: ref);
|
||||
}
|
||||
/***********/
|
||||
/*madara*/
|
||||
/***********/
|
||||
|
||||
else if (getMangaTypeSource(source) == TypeSource.madara) {
|
||||
pageUrls = await Madara().getChapterUrl(chapter: chapter, ref: ref);
|
||||
}
|
||||
|
||||
if (pageUrls.isNotEmpty) {
|
||||
if (!incognitoMode) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import 'dart:async';
|
||||
import 'package:mangayomi/models/source.dart';
|
||||
import 'package:mangayomi/sources/multisrc/heancms/heancms.dart';
|
||||
import 'package:mangayomi/sources/multisrc/madara/src/madara.dart';
|
||||
import 'package:mangayomi/sources/service.dart';
|
||||
import 'package:mangayomi/sources/src/all/comick/src/comick.dart';
|
||||
import 'package:mangayomi/sources/src/en/mangahere/src/mangahere.dart';
|
||||
|
|
@ -77,5 +78,14 @@ Future<GetManga> getMangaDetail(GetMangaDetailRef ref,
|
|||
mangadetail = await HeanCms()
|
||||
.getMangaDetail(manga: manga, lang: lang, source: source, ref: ref);
|
||||
}
|
||||
|
||||
/***********/
|
||||
/*madara*/
|
||||
/***********/
|
||||
|
||||
else if (getMangaTypeSource(source.toLowerCase()) == TypeSource.madara) {
|
||||
mangadetail = await Madara()
|
||||
.getMangaDetail(manga: manga, lang: lang, source: source, ref: ref);
|
||||
}
|
||||
return mangadetail!;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import 'dart:async';
|
||||
import 'package:mangayomi/models/source.dart';
|
||||
import 'package:mangayomi/sources/multisrc/heancms/heancms.dart';
|
||||
import 'package:mangayomi/sources/multisrc/madara/src/madara.dart';
|
||||
import 'package:mangayomi/sources/service.dart';
|
||||
import 'package:mangayomi/sources/src/all/comick/src/comick.dart';
|
||||
import 'package:mangayomi/sources/src/en/mangahere/src/mangahere.dart';
|
||||
|
|
@ -67,11 +68,18 @@ Future<List<GetManga?>> getPopularManga(GetPopularMangaRef ref,
|
|||
}
|
||||
|
||||
/***********/
|
||||
/*japscan*/
|
||||
/*heancms*/
|
||||
/***********/
|
||||
else if (getMangaTypeSource(source) == TypeSource.heancms) {
|
||||
popularManga =
|
||||
await HeanCms().getPopularManga(source: source, page: page, ref: ref);
|
||||
}
|
||||
/***********/
|
||||
/*madara*/
|
||||
/***********/
|
||||
else if (getMangaTypeSource(source) == TypeSource.madara) {
|
||||
popularManga =
|
||||
await Madara().getPopularManga(source: source, page: page, ref: ref);
|
||||
}
|
||||
return popularManga!;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -82,6 +82,7 @@ Future<String> cloudflareBypassHtml(CloudflareBypassHtmlRef ref,
|
|||
}
|
||||
return false;
|
||||
});
|
||||
await Future.delayed(Duration(seconds: 10));
|
||||
html = await controller.evaluateJavascript(
|
||||
source: "window.document.getElementsByTagName('html')[0].outerHTML;");
|
||||
isOk = true;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,29 @@
|
|||
import 'package:mangayomi/models/source.dart';
|
||||
|
||||
List<Source> get madaraSourcesList => _madaraSourcesList;
|
||||
List<Source> _madaraSourcesList = [
|
||||
Source(
|
||||
sourceName: "FR-Scan",
|
||||
baseUrl: "https://fr-scan.com",
|
||||
lang: "fr",
|
||||
typeSource: TypeSource.madara,
|
||||
logoUrl: '',
|
||||
dateFormat: "MMMM d, yyyy",
|
||||
dateFormatLocale: "fr"),
|
||||
Source(
|
||||
sourceName: "AstralManga",
|
||||
baseUrl: "https://astral-manga.fr",
|
||||
lang: "fr",
|
||||
typeSource: TypeSource.madara,
|
||||
logoUrl: '',
|
||||
dateFormat: "dd/mm/yyyy",
|
||||
dateFormatLocale: "fr"),
|
||||
Source(
|
||||
sourceName: "arabtoons",
|
||||
baseUrl: "https://arabtoons.net",
|
||||
lang: "ar",
|
||||
typeSource: TypeSource.madara,
|
||||
logoUrl: '',
|
||||
dateFormat: "MMM d",
|
||||
dateFormatLocale: "ar"),
|
||||
];
|
||||
|
|
@ -1,31 +1,193 @@
|
|||
// import 'package:mangayomi/models/chapter.dart';
|
||||
// import 'package:mangayomi/sources/service.dart';
|
||||
import 'dart:convert';
|
||||
import 'dart:developer';
|
||||
|
||||
// class Madara extends MangaYomiServices {
|
||||
// @override
|
||||
// Future<List?> getChapterUrl({required Chapter chapter}) {
|
||||
// // TODO: implement getChapterUrl
|
||||
// throw UnimplementedError();
|
||||
// }
|
||||
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.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.dart';
|
||||
import 'package:mangayomi/sources/utils/utils.dart';
|
||||
import 'package:mangayomi/utils/reg_exp_matcher.dart';
|
||||
import 'package:mangayomi/utils/xpath_selector.dart';
|
||||
|
||||
// @override
|
||||
// Future<GetManga?> getMangaDetail(
|
||||
// {required GetManga manga, required String lang, required String source}) {
|
||||
// // TODO: implement getMangaDetail
|
||||
// throw UnimplementedError();
|
||||
// }
|
||||
class Madara extends MangaYomiServices {
|
||||
@override
|
||||
Future<List<String>> getChapterUrl(
|
||||
{required Chapter chapter,
|
||||
required AutoDisposeFutureProviderRef ref}) async {
|
||||
final dom = await await ref.watch(httpGetProvider(
|
||||
useUserAgent: true,
|
||||
url: chapter.url!,
|
||||
source: chapter.manga.value!.source!.toLowerCase(),
|
||||
resDom: true)
|
||||
.future) as Document?;
|
||||
final res = dom!.querySelector(
|
||||
"div.page-break, li.blocks-gallery-item, .reading-content, .text-left img");
|
||||
final imgs = res!
|
||||
.querySelectorAll('img')
|
||||
.map((i) => regSrcMatcher(i.outerHtml).trim().trimLeft().trimRight())
|
||||
.toList();
|
||||
if (imgs.isNotEmpty && imgs.length == 1) {
|
||||
final pagesNumber = dom
|
||||
.querySelector("#single-pager")!
|
||||
.querySelectorAll("option")
|
||||
.map((e) => e.outerHtml)
|
||||
.toList();
|
||||
for (var i = 0; i < pagesNumber.length; i++) {
|
||||
if (i.toString().length == 1) {
|
||||
pageUrls.add(
|
||||
imgs.first.replaceAll("01", '0${int.parse(i.toString()) + 1}'));
|
||||
} else if (i.toString().length == 2) {
|
||||
pageUrls.add(
|
||||
imgs.first.replaceAll("01", '${int.parse(i.toString()) + 1}'));
|
||||
} else if (i.toString().length == 3) {
|
||||
pageUrls.add(
|
||||
imgs.first.replaceAll("01", '${int.parse(i.toString()) + 1}'));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
pageUrls = imgs;
|
||||
}
|
||||
log(pageUrls.toString());
|
||||
|
||||
// @override
|
||||
// Future<List<GetManga?>> getPopularManga(
|
||||
// {required String source, required int page}) {
|
||||
// // TODO: implement getPopularManga
|
||||
// throw UnimplementedError();
|
||||
// }
|
||||
return pageUrls;
|
||||
}
|
||||
|
||||
// @override
|
||||
// Future<List<GetManga?>> searchManga(
|
||||
// {required String source, required String query}) {
|
||||
// // TODO: implement searchManga
|
||||
// throw UnimplementedError();
|
||||
// }
|
||||
// }
|
||||
@override
|
||||
Future<GetManga?> getMangaDetail(
|
||||
{required GetManga manga,
|
||||
required String lang,
|
||||
required String source,
|
||||
required AutoDisposeFutureProviderRef ref}) async {
|
||||
final dom = await ref.watch(
|
||||
httpGetProvider(url: manga.url!, source: source, resDom: true)
|
||||
.future) as Document?;
|
||||
author = dom!
|
||||
.querySelectorAll("div.author-content > a")
|
||||
.map((e) => e.text)
|
||||
.toList()
|
||||
.join(', ');
|
||||
description = dom
|
||||
.querySelectorAll(
|
||||
"div.description-summary div.summary__content, div.summary_content div.post-content_item > h5 + div, div.summary_content div.manga-excerpt")
|
||||
.map((e) => e.text)
|
||||
.toList()
|
||||
.first;
|
||||
status = dom
|
||||
.querySelectorAll("div.summary-content")
|
||||
.map((e) => e.text.trim().trimLeft().trimRight())
|
||||
.toList()
|
||||
.last;
|
||||
|
||||
manga.imageUrl = dom
|
||||
.querySelectorAll("div.summary_image img")
|
||||
.map((e) => regSrcMatcher(e.outerHtml))
|
||||
.toList()
|
||||
.first;
|
||||
genre = dom
|
||||
.querySelectorAll("div.genres-content a")
|
||||
.map((e) => e.text)
|
||||
.toList();
|
||||
bool isOk = false;
|
||||
String? html;
|
||||
HeadlessInAppWebView? headlessWebViewJapScan;
|
||||
headlessWebViewJapScan = HeadlessInAppWebView(
|
||||
onLoadStop: (controller, u) async {
|
||||
html = await controller.evaluateJavascript(
|
||||
source:
|
||||
"window.document.getElementsByTagName('html')[0].outerHTML;");
|
||||
await Future.doWhile(() async {
|
||||
html = await controller.evaluateJavascript(
|
||||
source:
|
||||
"window.document.getElementsByTagName('html')[0].outerHTML;");
|
||||
if (xpathSelector(html!)
|
||||
.query(
|
||||
"//*[@id='manga-chapters-holder']/div[2]/div/ul/li/a/@href")
|
||||
.attrs
|
||||
.isEmpty) {
|
||||
html = await controller.evaluateJavascript(
|
||||
source:
|
||||
"window.document.getElementsByTagName('html')[0].outerHTML;");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
html = await controller.evaluateJavascript(
|
||||
source:
|
||||
"window.document.getElementsByTagName('html')[0].outerHTML;");
|
||||
isOk = true;
|
||||
headlessWebViewJapScan!.dispose();
|
||||
},
|
||||
initialUrlRequest: URLRequest(
|
||||
url: WebUri.uri(Uri.parse(manga.url!)),
|
||||
),
|
||||
);
|
||||
|
||||
headlessWebViewJapScan.run();
|
||||
await Future.doWhile(() async {
|
||||
await Future.delayed(const Duration(seconds: 1));
|
||||
if (isOk == true) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
final xpath = xpathSelector(html!);
|
||||
for (var url in xpath
|
||||
.query("//*[@id='manga-chapters-holder']/div[2]/div/ul/li/a/@href")
|
||||
.attrs) {
|
||||
chapterUrl.add(url!);
|
||||
}
|
||||
for (var title in xpath
|
||||
.query("//*[@id='manga-chapters-holder']/div[2]/div/ul/li/a/text()")
|
||||
.attrs) {
|
||||
chapterTitle.add(title!.trim().trimLeft().trimRight());
|
||||
}
|
||||
final dateF = xpath
|
||||
.query(
|
||||
"//*[@id='manga-chapters-holder']/div[2]/div/ul/li/span/i/text()")
|
||||
.attrs;
|
||||
|
||||
if (dateF.length == chapterUrl.length) {
|
||||
for (var date in dateF) {
|
||||
chapterDate.add(parseDate(date!, source));
|
||||
}
|
||||
} else if (dateF.length < chapterUrl.length) {
|
||||
final length = chapterUrl.length - dateF.length;
|
||||
for (var i = 0; i < length; i++) {
|
||||
chapterDate.add(DateTime.now().millisecondsSinceEpoch.toString());
|
||||
}
|
||||
for (var date in dateF) {
|
||||
chapterDate.add(parseDate(date!, source));
|
||||
}
|
||||
}
|
||||
|
||||
return mangadetailRes(manga: manga, source: source);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<GetManga?>> getPopularManga(
|
||||
{required String source,
|
||||
required int page,
|
||||
required AutoDisposeFutureProviderRef ref}) async {
|
||||
final html = await ref.watch(httpGetProvider(
|
||||
url: '${getMangaBaseUrl(source)}/manga/page/$page/?m_orderby=views',
|
||||
source: source,
|
||||
resDom: false)
|
||||
.future) as String?;
|
||||
final xpath = xpathSelector(html!);
|
||||
name = xpath.query('//*[@id^="manga-item"]/a/@title').attrs;
|
||||
url = xpath.query('//*[@class^="post-title"]/h3/a/@href').attrs;
|
||||
image = xpath.query('//*[@id^="manga-item"]/a/img/@data-src=').attrs;
|
||||
return mangaRes();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<GetManga?>> searchManga(
|
||||
{required String source,
|
||||
required String query,
|
||||
required AutoDisposeFutureProviderRef ref}) {
|
||||
// TODO: implement searchManga
|
||||
throw UnimplementedError();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -255,7 +255,7 @@ class MangaThemeSia extends MangaYomiServices {
|
|||
}
|
||||
|
||||
final List<String?> urlss = matchess.map((m) => m.group(1)).toList();
|
||||
if (urls.length == 1 && urls.isNotEmpty) {
|
||||
if (urls.isNotEmpty && urls.length == 1) {
|
||||
for (var i = 0; i < urlss.length; i++) {
|
||||
if (urlss[i]!.length == 1) {
|
||||
pageUrls.add(
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import 'package:mangayomi/sources/src/fr/japscan/japscan_source.dart';
|
|||
import 'package:mangayomi/sources/src/fr/mangakawaii/mangakawaii_source.dart';
|
||||
import 'package:mangayomi/sources/multisrc/mangathemesia/mangathemesia_source_list.dart';
|
||||
import 'package:mangayomi/sources/multisrc/mmrcms/mmrcms_source_list.dart';
|
||||
|
||||
import 'package:mangayomi/sources/multisrc/madara/madara_source_list.dart';
|
||||
List<Source> get sourcesList => _sourcesList;
|
||||
List<Source> _sourcesList = [
|
||||
mangahereSource,
|
||||
|
|
@ -15,5 +15,6 @@ List<Source> _sourcesList = [
|
|||
...comickSourcesList,
|
||||
...mmrcmsSourcesList,
|
||||
japscanSource,
|
||||
...heanCmsSourcesList
|
||||
...heanCmsSourcesList,
|
||||
...madaraSourcesList
|
||||
];
|
||||
|
|
|
|||
|
|
@ -5,6 +5,13 @@ String regHrefMatcher(String input) {
|
|||
return firstMatch!;
|
||||
}
|
||||
|
||||
String regDataSrcMatcher(String input) {
|
||||
RegExp exp = RegExp(r'data-src="([^"]+)"');
|
||||
Iterable<Match> matches = exp.allMatches(input);
|
||||
String? firstMatch = matches.first.group(1);
|
||||
return firstMatch!;
|
||||
}
|
||||
|
||||
String regSrcMatcher(String input) {
|
||||
RegExp exp = RegExp(r'src="([^"]+)"');
|
||||
Iterable<Match> matches = exp.allMatches(input);
|
||||
|
|
|
|||
7
lib/utils/xpath_selector.dart
Normal file
7
lib/utils/xpath_selector.dart
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
import 'package:html/parser.dart';
|
||||
import 'package:xpath_selector_html_parser/xpath_selector_html_parser.dart';
|
||||
|
||||
HtmlXPath xpathSelector(String html) {
|
||||
final html1 = parse(html).documentElement!;
|
||||
return HtmlXPath.node(html1);
|
||||
}
|
||||
32
pubspec.lock
32
pubspec.lock
|
|
@ -306,6 +306,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.0"
|
||||
expressions:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: expressions
|
||||
sha256: "75ca4343f9f8a38087bea130cf51395d737d87c6947cc19cbb8fb2732cae1a27"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.5"
|
||||
extended_image:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
@ -878,6 +886,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.2"
|
||||
quiver:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: quiver
|
||||
sha256: b1c1ac5ce6688d77f65f3375a9abb9319b3cb32486bdc7a1e0fdf004d7ba4e47
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.2.1"
|
||||
riverpod:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -1211,6 +1227,22 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.2.2"
|
||||
xpath_selector:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: xpath_selector
|
||||
sha256: "6d8295565a34a6e2821a0721592e584c70421e52a0f54955e0c8e41963db7c90"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.2"
|
||||
xpath_selector_html_parser:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: xpath_selector_html_parser
|
||||
sha256: ebc562a07832a4062a2bc1c238fd59a81d31d841f15460caeed412ce078af9e0
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.1"
|
||||
xxh3:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ dependencies:
|
|||
isar: 3.1.0+1
|
||||
isar_flutter_libs: 3.1.0+1
|
||||
share_plus: ^7.0.0
|
||||
xpath_selector_html_parser: ^3.0.1
|
||||
|
||||
|
||||
# The following adds the Cupertino Icons font to your application.
|
||||
|
|
|
|||
Loading…
Reference in a new issue