This commit is contained in:
kodjomoustapha 2024-02-05 19:56:44 +01:00
parent 2af65f09ff
commit e33b1b55f0
19 changed files with 867 additions and 338 deletions

View file

@ -897,11 +897,6 @@ class $MProvider extends MProvider with $Bridge<MProvider> {
///////////////////////////////////////////////////////////////////////
"getHtmlViaWebview" => $Function((_, __, List<$Value?> args) {
return $Future.wrap(
MBridge.getHtmlViaWebview(args[0]!.$value, args[1]!.$value)
.then((value) => $String(value)));
}),
"unpackJs" => MBridge.unpackJs,
"regExp" => $Function((_, __, List<$Value?> args) {
return $String(MBridge.regExp(args[0]!.$value, args[1]!.$value,

View file

@ -1,12 +1,9 @@
import 'dart:convert';
import 'dart:developer';
import 'dart:io';
import 'package:bot_toast/bot_toast.dart';
import 'package:dart_eval/dart_eval_bridge.dart';
import 'package:dart_eval/stdlib/core.dart';
import 'package:desktop_webview_window/desktop_webview_window.dart';
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'package:html/dom.dart';
import 'package:intl/date_symbol_data_local.dart';
import 'package:intl/intl.dart';
@ -27,18 +24,15 @@ import 'package:mangayomi/services/anime_extractors/streamtape_extractor.dart';
import 'package:mangayomi/main.dart';
import 'package:mangayomi/models/source.dart';
import 'package:mangayomi/models/video.dart';
import 'package:mangayomi/modules/webview/webview.dart';
import 'package:mangayomi/services/anime_extractors/streamwish_extractor.dart';
import 'package:mangayomi/services/anime_extractors/vidbom_extractor.dart';
import 'package:mangayomi/services/anime_extractors/voe_extractor.dart';
import 'package:mangayomi/services/anime_extractors/your_upload_extractor.dart';
import 'package:mangayomi/services/http/cloudflare.dart';
import 'package:mangayomi/utils/constant.dart';
import 'package:mangayomi/utils/cryptoaes/crypto_aes.dart';
import 'package:mangayomi/utils/cryptoaes/deobfuscator.dart';
import 'package:mangayomi/utils/extensions/string_extensions.dart';
import 'package:mangayomi/utils/reg_exp_matcher.dart';
import 'package:mangayomi/utils/xpath_selector.dart';
import 'package:xpath_selector_html_parser/xpath_selector_html_parser.dart';
import 'package:http/http.dart' as hp;
import 'package:encrypt/encrypt.dart' as encrypt;
@ -129,75 +123,6 @@ class MBridge {
return Status.unknown;
}
///Get Html content via webview when http request not working
static Future<String> getHtmlViaWebview(String url, String rule) async {
bool isOk = false;
String? html;
if (Platform.isWindows || Platform.isLinux || Platform.isMacOS) {
final webview = await WebviewWindow.create(
configuration: CreateConfiguration(
windowHeight: 500,
windowWidth: 500,
userDataFolderWindows: await getWebViewPath(),
),
);
webview
..setApplicationNameForUserAgent(defaultUserAgent)
..launch(url);
await Future.doWhile(() async {
await Future.delayed(const Duration(seconds: 3));
html = await decodeHtml(
webview,
);
if (html == null || xpathSelector(html!).query(rule).attrs.isEmpty) {
html = await decodeHtml(webview);
return true;
}
return false;
});
html = await decodeHtml(webview);
webview.close();
} else {
HeadlessInAppWebView? headlessWebView;
headlessWebView = 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(rule).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;
headlessWebView!.dispose();
},
initialUrlRequest: URLRequest(
url: Uri.parse(url),
),
);
headlessWebView.run();
await Future.doWhile(() async {
await Future.delayed(const Duration(seconds: 1));
if (isOk == true) {
return false;
}
return true;
});
}
return html!;
}
///Unpack a JS code
static const $Function unpackJs = $Function(_unpackJs);

View file

@ -61,6 +61,8 @@ class Settings {
String? userAgent;
List<MCookie>? cookiesList;
@enumerated
late ReaderMode defaultReaderMode;
@ -190,6 +192,7 @@ class Settings {
this.showPagesNumber = true,
this.chapterPageIndexList,
this.userAgent = defaultUserAgent,
this.cookiesList,
this.defaultReaderMode = ReaderMode.vertical,
this.personalReaderModeList,
this.animatePageTransitions = true,
@ -279,6 +282,11 @@ class Settings {
.toList();
}
checkForExtensionUpdates = json['checkForExtensionUpdates'];
if (json['cookiesList'] != null) {
cookiesList = (json['cookiesList'] as List)
.map((e) => MCookie.fromJson(e))
.toList();
}
cropBorders = json['cropBorders'];
dateFormat = json['dateFormat'];
defaultReaderMode = ReaderMode.values[json['defaultReaderMode']];
@ -393,6 +401,7 @@ class Settings {
'chapterPageUrlsList':
chapterPageUrlsList!.map((v) => v.toJson()).toList(),
'checkForExtensionUpdates': checkForExtensionUpdates,
'cookiesList': cookiesList,
'cropBorders': cropBorders,
'dateFormat': dateFormat,
'defaultReaderMode': defaultReaderMode.index,
@ -481,6 +490,20 @@ enum ScaleType {
enum BackgroundColor { black, grey, white, automatic }
@embedded
class MCookie {
String? host;
String? cookie;
MCookie({this.host, this.cookie});
MCookie.fromJson(Map<String, dynamic> json) {
host = json['host'];
cookie = json['cookie'];
}
Map<String, dynamic> toJson() => {'host': host, 'cookie': cookie};
}
@embedded
class SortLibraryManga {
bool? reverse;

File diff suppressed because it is too large Load diff

View file

@ -54,7 +54,7 @@ class Source {
String? additionalParams;
Source(
{this.id = 0,
{this.id = 1,
this.name = '',
this.baseUrl = '',
this.lang = '',

View file

@ -7,7 +7,7 @@ part of 'fetch_anime_sources.dart';
// **************************************************************************
String _$fetchAnimeSourcesListHash() =>
r'fce156ad85528a46bf28390f783a04ca2768e92c';
r'3f9cf47efce8e6e207c59e0d936727959c1fe982';
/// Copied from Dart SDK
class _SystemHash {

View file

@ -7,7 +7,7 @@ part of 'fetch_manga_sources.dart';
// **************************************************************************
String _$fetchMangaSourcesListHash() =>
r'ec5b2fa05b41e73485460fbe1456f33d40c8fda8';
r'a74557098c49877942a296abe110fb6ef1dce580';
/// Copied from Dart SDK
class _SystemHash {

View file

@ -1,6 +1,4 @@
// import 'package:mangayomi/eval/model/m_bridge.dart';
import 'package:mangayomi/eval/model/m_bridge.dart';
import 'package:mangayomi/eval/model/m_chapter.dart';
import 'package:mangayomi/eval/model/m_manga.dart';
import 'package:mangayomi/main.dart';
import 'package:mangayomi/models/chapter.dart';
@ -86,15 +84,14 @@ Future<dynamic> updateMangaDetail(UpdateMangaDetailRef ref,
final oldChapers =
isar.mangas.getSync(mangaId)!.chapters.toList().reversed.toList();
if (oldChapers.length == chaps.length) {
for (var oldChap in oldChapers) {
final newChap = chaps.firstWhere(
(e) => e.name == oldChap.name,
orElse: () => MChapter(),
);
for (var i = 0; i < oldChapers.length; i++) {
final oldChap = oldChapers[i];
final newChap = chaps[i];
if (newChap.url != null &&
newChap.url!.isNotEmpty &&
newChap.url != oldChap.url) {
oldChap.url = newChap.url;
oldChap.scanlator = newChap.scanlator;
isar.chapters.putSync(oldChap);
oldChap.manga.saveSync();
}

View file

@ -55,9 +55,10 @@ class _MangaWebViewState extends ConsumerState<MangaWebView> {
..setBrightness(Brightness.dark)
..launch(widget.url)
..addOnUrlRequestCallback((url) async {
final newCookie =
await webview!.evaluateJavaScript("window.document.cookie;");
log(newCookie.toString());
decodeHtml(webview!, url);
// final newCookie =
// await webview!.evaluateJavaScript("window.document.cookie;");
// log(newCookie.toString());
})
..onClose.whenComplete(() {
Navigator.pop(context);
@ -251,16 +252,17 @@ Future<String> getWebViewPath() async {
);
}
Future<String?> decodeHtml(Webview webview) async {
Future<String?> decodeHtml(Webview webview, String url) async {
try {
final html = await webview
.evaluateJavaScript("window.document.documentElement.outerHTML;");
final ua = await webview.evaluateJavaScript("navigator.userAgent") ?? "";
final newCookie =
await webview.evaluateJavaScript("window.document.cookie;");
log(ua);
if (newCookie != null) {
await MInterceptor.setCookie(
jsonDecode(newCookie), ua.isNotEmpty ? jsonDecode(ua) : "");
await MInterceptor.setCookie(url, jsonDecode(ua),
cookie: jsonDecode(newCookie));
}
final res = jsonDecode(html!) as String;

View file

@ -6,7 +6,7 @@ part of 'get_chapter_pages.dart';
// RiverpodGenerator
// **************************************************************************
String _$getChapterPagesHash() => r'843f224e365aafe2f3ca124a07254a1ff1d153ae';
String _$getChapterPagesHash() => r'aa95d95504a7f52e22bd3886782a9ef4443453e9';
/// Copied from Dart SDK
class _SystemHash {

View file

@ -1,13 +1,10 @@
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:mangayomi/eval/bridge/m_source.dart';
import 'package:mangayomi/eval/model/m_manga.dart';
import 'package:mangayomi/eval/compiler/compiler.dart';
import 'package:mangayomi/eval/model/m_provider.dart';
import 'package:mangayomi/models/source.dart';
import 'package:mangayomi/eval/runtime/runtime.dart';
import 'package:mangayomi/services/isolate.dart';
import 'package:mangayomi/sources/source_test.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'get_detail.g.dart';
@ -18,21 +15,18 @@ Future<MManga> getDetail(
required String url,
required Source source,
}) async {
return await compute<RootIsolateToken, MManga>((token) async {
await initInIsolate(token);
MManga? mangadetail;
final bytecode =
compilerEval(useTestSourceCode ? testSourceCode : source.sourceCode!);
MManga? mangadetail;
final bytecode =
compilerEval(useTestSourceCode ? testSourceCode : source.sourceCode!);
final runtime = runtimeEval(bytecode);
final runtime = runtimeEval(bytecode);
var res = await runtime.executeLib('package:mangayomi/main.dart', 'main',
[$MSource.wrap(source.toMSource())]);
try {
mangadetail = await (res as MProvider).getDetail(url);
} catch (e) {
throw Exception(e);
}
return mangadetail;
}, RootIsolateToken.instance!);
var res = await runtime.executeLib('package:mangayomi/main.dart', 'main',
[$MSource.wrap(source.toMSource())]);
try {
mangadetail = await (res as MProvider).getDetail(url);
} catch (e) {
throw Exception(e);
}
return mangadetail;
}

View file

@ -6,7 +6,7 @@ part of 'get_video_list.dart';
// RiverpodGenerator
// **************************************************************************
String _$getVideoListHash() => r'c7e36c244ebbac9dad01945be0e2ed5712236855';
String _$getVideoListHash() => r'dd1908b1174efac883d05862ea55febf09ad976c';
/// Copied from Dart SDK
class _SystemHash {

View file

@ -24,7 +24,7 @@ Future<String> cloudflareBypass(
await Future.doWhile(() async {
await Future.delayed(const Duration(seconds: 1));
html = await decodeHtml(webview);
html = await decodeHtml(webview, url);
if (html == null ||
html!.contains("Just a moment") ||
html!.contains("challenges.cloudflare.com")) {

View file

@ -15,7 +15,10 @@ class MInterceptor {
ignoreExpires: true,
storage: cookie_jar.FileStorage(
"${isar.settings.getSync(227)!.defaultAppStoragePath}.cookies/"));
MInterceptor({required String idSource});
static final flutter_inappwebview.CookieManager _cookieManager =
flutter_inappwebview.CookieManager.instance();
MInterceptor();
static InterceptedClient init({MSource? source}) {
return InterceptedClient.build(interceptors: [
@ -24,13 +27,50 @@ class MInterceptor {
]);
}
static Future<void> setCookie(String url, String ua) async {
flutter_inappwebview.CookieManager cookieManager =
flutter_inappwebview.CookieManager.instance();
static Map<String, String> getCookiesPref(String url) {
final cookiesList = isar.settings.getSync(227)!.cookiesList ?? [];
if (cookiesList.isEmpty) return {};
final cookies = cookiesList
.firstWhere(
(element) => element.host == Uri.parse(url).host,
orElse: () => MCookie(cookie: ""),
)
.cookie!;
if (cookies.isEmpty) return {};
return {HttpHeaders.cookieHeader: cookies};
}
final cookies = (await cookieManager.getCookies(url: Uri.parse(url)))
.map((e) => "${e.name}=${e.value}")
.toList();
static Future<void> setCookiesPref(String url) async {
final host = Uri.parse(url).host;
final newCookie = (await _cookieJar.loadForRequest(Uri.parse(url)))
.map((e) => e.toString())
.join(";");
final settings = isar.settings.getSync(227);
List<MCookie>? cookieList = [];
for (var cookie in settings!.cookiesList ?? []) {
if (cookie.host != host) {
cookieList.add(cookie);
}
}
cookieList.add(MCookie()
..host = host
..cookie = newCookie);
isar.writeTxnSync(
() => isar.settings.putSync(settings..cookiesList = cookieList));
}
static Future<void> setCookie(String url, String ua, {String? cookie}) async {
List<String> cookies = [];
if (Platform.isWindows || Platform.isMacOS || Platform.isLinux) {
cookies = cookie!
.split(RegExp('(?<=)(,)(?=[^;]+?=)'))
.where((cookie) => cookie.isNotEmpty)
.toList();
} else {
cookies = (await _cookieManager.getCookies(url: Uri.parse(url)))
.map((e) => "${e.name}=${e.value}")
.toList();
}
if (cookies.isNotEmpty) {
for (final cookie in cookies) {
@ -39,6 +79,7 @@ class MInterceptor {
[Cookie.fromSetCookieValue(cookie)],
);
}
await setCookiesPref(url);
final settings = isar.settings.getSync(227);
isar.writeTxnSync(() => isar.settings.putSync(settings!..userAgent = ua));
@ -98,23 +139,30 @@ class MCookieManager extends InterceptorContract {
if (setCookies == null || setCookies.isEmpty) {
return;
}
final List<Cookie> cookies = setCookies
.split(RegExp('(?<=)(,)(?=[^;]+?=)'))
.where((cookie) => cookie.isNotEmpty)
.map((str) => Cookie.fromSetCookieValue(str))
.toList();
final statusCode = response.statusCode;
final isRedirectRequest = statusCode >= 300 && statusCode < 400;
final location = response.headers[HttpHeaders.locationHeader] ?? "";
final originalUri = response.request!.url;
final realUri = originalUri
.resolveUri(isRedirectRequest ? Uri.parse(location) : originalUri);
await cookieJar.saveFromResponse(realUri, cookies);
if (isRedirectRequest && location.isNotEmpty) {
await Future.wait(
[cookieJar.saveFromResponse(originalUri.resolve(location), cookies)],
);
}
try {
final List<Cookie> cookies = setCookies
.split(RegExp('(?<=)(,)(?=[^;]+?=)'))
.where((cookie) => cookie.isNotEmpty)
.map((str) => Cookie.fromSetCookieValue(str))
.toList();
final statusCode = response.statusCode;
final isRedirectRequest = statusCode >= 300 && statusCode < 400;
final location = response.headers[HttpHeaders.locationHeader] ?? "";
final originalUri = response.request!.url;
final realUri = originalUri
.resolveUri(isRedirectRequest ? Uri.parse(location) : originalUri);
await cookieJar.saveFromResponse(realUri, cookies);
await MInterceptor.setCookiesPref(realUri.toString());
if (isRedirectRequest && location.isNotEmpty) {
await Future.wait(
[cookieJar.saveFromResponse(originalUri.resolve(location), cookies)],
);
await MInterceptor.setCookiesPref(
originalUri.resolve(location).toString());
}
} catch (_) {}
}
}

View file

@ -58,7 +58,7 @@ class TestSource extends MProvider {
// For manga chapter pages
@override
Future<List<String>> getPageList(String url) {
Future<List<String>> getPageList(String url) async{
// TODO: implement
}

View file

@ -1,4 +1,5 @@
import 'dart:convert';
import 'package:mangayomi/services/http/interceptor.dart';
import 'package:mangayomi/sources/utils/utils.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'headers.g.dart';
@ -14,5 +15,8 @@ Map<String, String> headers(HeadersRef ref,
.map((key, value) => MapEntry(key.toString(), value.toString()));
}
final cookies = MInterceptor.getCookiesPref(mSource!.baseUrl!);
headers.addAll(cookies);
return headers;
}

View file

@ -6,7 +6,7 @@ part of 'headers.dart';
// RiverpodGenerator
// **************************************************************************
String _$headersHash() => r'cabe2e8d0241df2a9741f4f738c52a0f6ce2ab10';
String _$headersHash() => r'a84518937e2a324c8463089e85db23b426d81a91';
/// Copied from Dart SDK
class _SystemHash {

View file

@ -623,7 +623,7 @@ packages:
source: hosted
version: "0.15.4"
http:
dependency: "direct overridden"
dependency: "direct main"
description:
name: http
sha256: a2bbf9d017fcced29139daa8ed2bba4ece450ab222871df93ca9eec6f80c34ba

View file

@ -67,6 +67,7 @@ dependencies:
ffigen: ^11.0.0
http_interceptor: ^2.0.0-beta.7
cookie_jar: ^4.0.0
http: ^1.2.0
dependency_overrides: