mirror of
https://github.com/kodjodevf/mangayomi.git
synced 2026-04-20 23:22:07 +00:00
code refactor, added sources languages
This commit is contained in:
parent
499002751c
commit
385677f67d
15 changed files with 169 additions and 87 deletions
|
|
@ -43,6 +43,7 @@ import 'package:mangayomi/utils/xpath_selector.dart';
|
|||
import 'package:xpath_selector_html_parser/xpath_selector_html_parser.dart';
|
||||
import 'package:html/parser.dart' as parser;
|
||||
import 'package:http/http.dart' as hp;
|
||||
import 'package:encrypt/encrypt.dart' as encrypt;
|
||||
|
||||
class WordSet {
|
||||
final List<String> words;
|
||||
|
|
@ -981,6 +982,19 @@ class MBridge {
|
|||
|
||||
return type == 0 ? value.isEmpty : value.isNotEmpty;
|
||||
}
|
||||
|
||||
static String cryptoHandler(
|
||||
String text, String iv, String secretKeyString, bool encrypt) {
|
||||
if (encrypt) {
|
||||
final encryptt = _encrypt(secretKeyString, iv);
|
||||
final en = encryptt.$1.encrypt(text, iv: encryptt.$2);
|
||||
return en.base64;
|
||||
} else {
|
||||
final encryptt = _encrypt(secretKeyString, iv);
|
||||
final en = encryptt.$1.decrypt64(text, iv: encryptt.$2);
|
||||
return en;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final List<String> _dateFormats = [
|
||||
|
|
@ -1050,6 +1064,34 @@ class $MBridge extends MBridge with $Bridge {
|
|||
],
|
||||
namedParams: []),
|
||||
isStatic: true),
|
||||
'cryptoHandler': BridgeMethodDef(
|
||||
BridgeFunctionDef(
|
||||
returns: BridgeTypeAnnotation(
|
||||
BridgeTypeRef.type(RuntimeTypes.stringType)),
|
||||
params: [
|
||||
BridgeParameter(
|
||||
'text',
|
||||
BridgeTypeAnnotation(
|
||||
BridgeTypeRef.type(RuntimeTypes.stringType)),
|
||||
false),
|
||||
BridgeParameter(
|
||||
'iv',
|
||||
BridgeTypeAnnotation(
|
||||
BridgeTypeRef.type(RuntimeTypes.stringType)),
|
||||
false),
|
||||
BridgeParameter(
|
||||
'secretKeyString',
|
||||
BridgeTypeAnnotation(
|
||||
BridgeTypeRef.type(RuntimeTypes.stringType)),
|
||||
false),
|
||||
BridgeParameter(
|
||||
'encrypt',
|
||||
BridgeTypeAnnotation(
|
||||
BridgeTypeRef.type(RuntimeTypes.boolType)),
|
||||
false),
|
||||
],
|
||||
namedParams: []),
|
||||
isStatic: true),
|
||||
'encryptAESCryptoJS': BridgeMethodDef(
|
||||
BridgeFunctionDef(
|
||||
returns: BridgeTypeAnnotation(
|
||||
|
|
@ -1997,6 +2039,11 @@ class $MBridge extends MBridge with $Bridge {
|
|||
static $String $bAse64(Runtime runtime, $Value? target, List<$Value?> args) =>
|
||||
$String(MBridge.bAse64(args[0]!.$value, args[1]!.$value));
|
||||
|
||||
static $String $cryptoHandler(
|
||||
Runtime runtime, $Value? target, List<$Value?> args) =>
|
||||
$String(MBridge.cryptoHandler(
|
||||
args[0]!.$value, args[1]!.$value, args[2]!.$value, args[3]!.$value));
|
||||
|
||||
static $String $encryptAESCryptoJS(
|
||||
Runtime runtime, $Value? target, List<$Value?> args) =>
|
||||
$String(MBridge.encryptAESCryptoJS(args[0]!.$value, args[1]!.$value));
|
||||
|
|
@ -2212,3 +2259,11 @@ $VideoModel _toVideoModel(Video e) => $VideoModel.wrap(VideoModel()
|
|||
..file = t.file
|
||||
..label = t.label))
|
||||
.toList()));
|
||||
|
||||
(encrypt.Encrypter, encrypt.IV) _encrypt(String keyy, String ivv) {
|
||||
final key = encrypt.Key.fromUtf8(keyy);
|
||||
final iv = encrypt.IV.fromUtf8(ivv);
|
||||
final encrypter = encrypt.Encrypter(
|
||||
encrypt.AES(key, mode: encrypt.AESMode.cbc, padding: 'PKCS7'));
|
||||
return (encrypter, iv);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -103,6 +103,8 @@ Runtime runtimeEval(Uint8List bytecode) {
|
|||
'MBridge.deobfuscateJsPassword', $MBridge.$deobfuscateJsPassword);
|
||||
runtime.registerBridgeFunc('package:bridge_lib/bridge_lib.dart',
|
||||
'MBridge.querySelectorAll', $MBridge.$querySelectorAll);
|
||||
runtime.registerBridgeFunc('package:bridge_lib/bridge_lib.dart',
|
||||
'MBridge.cryptoHandler', $MBridge.$cryptoHandler);
|
||||
runtime.setup();
|
||||
return runtime;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -225,5 +225,6 @@
|
|||
"white":"White",
|
||||
"black":"Black",
|
||||
"grey":"Grey",
|
||||
"automaic":"Automatic"
|
||||
"automaic":"Automatic",
|
||||
"preferred_domain":"Preferred Domain"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -225,5 +225,6 @@
|
|||
"white":"Blanc",
|
||||
"black":"Noir",
|
||||
"grey":"Gris",
|
||||
"automaic":"Automatique"
|
||||
"automaic":"Automatique",
|
||||
"preferred_domain":"Domaine préféré"
|
||||
}
|
||||
|
|
@ -187,7 +187,7 @@ class _ExtensionDetailState extends ConsumerState<ExtensionDetail> {
|
|||
builder: (context) {
|
||||
return AlertDialog(
|
||||
title: Text(
|
||||
l10n.relative_timestamp,
|
||||
l10n.preferred_domain,
|
||||
),
|
||||
content: SizedBox(
|
||||
width: mediaWidth(context, 0.8),
|
||||
|
|
@ -246,7 +246,7 @@ class _ExtensionDetailState extends ConsumerState<ExtensionDetail> {
|
|||
);
|
||||
});
|
||||
},
|
||||
title: Text(l10n.relative_timestamp),
|
||||
title: Text(l10n.preferred_domain),
|
||||
subtitle: Text(
|
||||
widget.source.baseUrl!,
|
||||
style: TextStyle(
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import 'package:isar/isar.dart';
|
|||
import 'package:mangayomi/main.dart';
|
||||
import 'package:mangayomi/models/source.dart';
|
||||
import 'package:mangayomi/providers/l10n_providers.dart';
|
||||
import 'package:mangayomi/utils/language.dart';
|
||||
import 'package:mangayomi/modules/browse/extension/widgets/extension_lang_list_tile_widget.dart';
|
||||
|
||||
class ExtensionsLang extends ConsumerWidget {
|
||||
|
|
@ -14,8 +13,6 @@ class ExtensionsLang extends ConsumerWidget {
|
|||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final l10n = l10nLocalizations(context)!;
|
||||
final languages = languagesMap.entries.map((e) => e.value).toList();
|
||||
languages.sort((a, b) => a.compareTo(b));
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(l10n.extensions),
|
||||
|
|
@ -29,6 +26,9 @@ class ExtensionsLang extends ConsumerWidget {
|
|||
.watch(fireImmediately: true),
|
||||
builder: (context, snapshot) {
|
||||
List<Source>? entries = snapshot.hasData ? snapshot.data : [];
|
||||
final languages = entries!.map((e) => e.lang!).toSet().toList();
|
||||
|
||||
languages.sort((a, b) => a.compareTo(b));
|
||||
return ListView.builder(
|
||||
itemCount: languages.length,
|
||||
itemBuilder: (context, index) {
|
||||
|
|
@ -44,7 +44,7 @@ class ExtensionsLang extends ConsumerWidget {
|
|||
}
|
||||
});
|
||||
},
|
||||
value: entries!
|
||||
value: entries
|
||||
.where((element) =>
|
||||
element.lang!.toLowerCase() == lang.toLowerCase() &&
|
||||
element.isActive!)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:grouped_list/grouped_list.dart';
|
||||
|
|
@ -83,7 +84,23 @@ class SourcesFilterScreen extends ConsumerWidget {
|
|||
.secondaryHeaderColor
|
||||
.withOpacity(0.5),
|
||||
borderRadius: BorderRadius.circular(5)),
|
||||
child: const Icon(Icons.source_outlined),
|
||||
child: element.iconUrl!.isEmpty
|
||||
? const Icon(Icons.source_outlined)
|
||||
: CachedNetworkImage(
|
||||
imageUrl: element.iconUrl!,
|
||||
fit: BoxFit.contain,
|
||||
width: 37,
|
||||
height: 37,
|
||||
errorWidget: (context, url, error) {
|
||||
return const SizedBox(
|
||||
width: 37,
|
||||
height: 37,
|
||||
child: Center(
|
||||
child: Icon(Icons.source_outlined),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
onChanged: (bool? value) {
|
||||
isar.writeTxnSync(() {
|
||||
|
|
|
|||
|
|
@ -1401,10 +1401,10 @@ class _MangaDetailViewState extends ConsumerState<MangaDetailView>
|
|||
onPressed: () {
|
||||
final manga = widget.manga!;
|
||||
final source =
|
||||
getSource(widget.manga!.lang!, widget.manga!.source!);
|
||||
String url = source!.apiUrl!.isEmpty
|
||||
? widget.manga!.link!
|
||||
: "${source.baseUrl}${widget.manga!.link!}";
|
||||
getSource(widget.manga!.lang!, widget.manga!.source!)!;
|
||||
String url = widget.manga!.link!.startsWith('/')
|
||||
? "${source.baseUrl}${widget.manga!.link!}"
|
||||
: widget.manga!.link!;
|
||||
|
||||
Map<String, String> data = {
|
||||
'url': url,
|
||||
|
|
|
|||
|
|
@ -376,6 +376,9 @@ class _MangaChapterPageGalleryState
|
|||
final double _imageDetailY = 0;
|
||||
|
||||
void _onBtnTapped(int index, bool isPrev, {bool isSlide = false}) {
|
||||
if (_isView) {
|
||||
_isViewFunction();
|
||||
}
|
||||
final readerMode = ref.watch(_selectedValue);
|
||||
final animatePageTransitions =
|
||||
ref.watch(animatePageTransitionsStateProvider);
|
||||
|
|
@ -637,10 +640,10 @@ class _MangaChapterPageGalleryState
|
|||
IconButton(
|
||||
onPressed: () {
|
||||
final manga = chapter.manga.value!;
|
||||
final source = getSource(manga.lang!, manga.source!);
|
||||
String url = source!.apiUrl!.isEmpty
|
||||
? chapter.url!
|
||||
: "${source.baseUrl}/${chapter.url!}";
|
||||
final source = getSource(manga.lang!, manga.source!)!;
|
||||
String url = chapter.url!.startsWith('/')
|
||||
? "${source.baseUrl}/${chapter.url!}"
|
||||
: chapter.url!;
|
||||
Map<String, String> data = {
|
||||
'url': url,
|
||||
'sourceId': source.id.toString(),
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import 'package:http/http.dart' as http;
|
||||
import 'package:js_packer/js_packer.dart';
|
||||
import 'package:mangayomi/eval/m_bridge.dart';
|
||||
import 'package:mangayomi/models/video.dart';
|
||||
import 'package:mangayomi/utils/extensions.dart';
|
||||
import 'package:mangayomi/utils/xpath_selector.dart';
|
||||
|
|
@ -16,7 +16,7 @@ class FilemoonExtractor {
|
|||
.queryXPath('//script[contains(text(), "eval")]/text()')
|
||||
.attr;
|
||||
|
||||
final unpacked = _evalJs(jsEval!) ?? '';
|
||||
final unpacked = MBridge.evalJs(jsEval!);
|
||||
|
||||
final masterUrl = unpacked.isNotEmpty
|
||||
? unpacked.substringAfter('{file:"').substringBefore('"}')
|
||||
|
|
@ -54,8 +54,3 @@ class FilemoonExtractor {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
String? _evalJs(String script) {
|
||||
final jsPacker = JSPacker(script);
|
||||
return jsPacker.unpack();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,8 @@
|
|||
import 'dart:convert';
|
||||
import 'package:encrypt/encrypt.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:html/dom.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:mangayomi/eval/m_bridge.dart';
|
||||
import 'package:mangayomi/models/video.dart';
|
||||
import 'package:encrypt/encrypt.dart' as encrypt;
|
||||
import 'package:html/parser.dart' as parser;
|
||||
import 'package:mangayomi/utils/extensions.dart';
|
||||
|
||||
|
|
@ -32,21 +30,17 @@ class GogoCdnExtractor {
|
|||
RegExp(r'container-(\d+)').firstMatch(document)?.group(1);
|
||||
final decryptionKey =
|
||||
RegExp(r'videocontent-(\d+)').firstMatch(document)?.group(1);
|
||||
final encryptAjaxParams = _cryptoHandler(
|
||||
final encryptAjaxParams = MBridge.cryptoHandler(
|
||||
RegExp(r'data-value="([^"]+)').firstMatch(document)?.group(1) ?? "",
|
||||
Uint8List.fromList(iv.codeUnits),
|
||||
Uint8List.fromList(secretKey.codeUnits),
|
||||
encrypt: false,
|
||||
iv,
|
||||
secretKey,
|
||||
false,
|
||||
).substringAfter("&");
|
||||
|
||||
final httpUrl = Uri.parse(serverUrl);
|
||||
final host = "https://${httpUrl.host}/";
|
||||
final id = httpUrl.queryParameters['id'];
|
||||
final encryptedId = _cryptoHandler(
|
||||
id ?? "",
|
||||
Uint8List.fromList(iv.codeUnits),
|
||||
Uint8List.fromList(secretKey.codeUnits),
|
||||
);
|
||||
final encryptedId = MBridge.cryptoHandler(id ?? "", iv, secretKey, true);
|
||||
|
||||
final token = httpUrl.queryParameters['token'];
|
||||
final qualityPrefix = token != null ? "Gogostream - " : "Vidstreaming - ";
|
||||
|
|
@ -58,11 +52,8 @@ class GogoCdnExtractor {
|
|||
headers: {"X-Requested-With": "XMLHttpRequest"});
|
||||
final jsonResponse = encryptAjaxResponse.body;
|
||||
final data = json.decode(jsonResponse)["data"];
|
||||
final decryptedData = _cryptoHandler(
|
||||
data ?? "",
|
||||
Uint8List.fromList(iv.codeUnits),
|
||||
Uint8List.fromList(decryptionKey!.codeUnits),
|
||||
encrypt: false);
|
||||
final decryptedData =
|
||||
MBridge.cryptoHandler(data ?? "", iv, decryptionKey!, false);
|
||||
final videoList = <Video>[];
|
||||
final autoList = <Video>[];
|
||||
final array = json.decode(decryptedData)["source"];
|
||||
|
|
@ -111,31 +102,4 @@ class GogoCdnExtractor {
|
|||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
String _cryptoHandler(
|
||||
String string,
|
||||
Uint8List iv,
|
||||
Uint8List secretKeyString, {
|
||||
bool encrypt = true,
|
||||
}) {
|
||||
if (encrypt) {
|
||||
final encryptt = _encrypt(
|
||||
String.fromCharCodes(secretKeyString), String.fromCharCodes(iv));
|
||||
final aa = encryptt.$1.encrypt(string, iv: encryptt.$2);
|
||||
return aa.base64;
|
||||
} else {
|
||||
final encryptt = _encrypt(
|
||||
String.fromCharCodes(secretKeyString), String.fromCharCodes(iv));
|
||||
final aa = encryptt.$1.decrypt64(string, iv: encryptt.$2);
|
||||
return aa;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(encrypt.Encrypter, encrypt.IV) _encrypt(String keyy, String ivv) {
|
||||
final key = encrypt.Key.fromUtf8(keyy);
|
||||
final iv = encrypt.IV.fromUtf8(ivv);
|
||||
final encrypter =
|
||||
encrypt.Encrypter(encrypt.AES(key, mode: AESMode.cbc, padding: 'PKCS7'));
|
||||
return (encrypter, iv);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import 'package:http/http.dart' as http;
|
||||
import 'package:js_packer/js_packer.dart';
|
||||
import 'package:mangayomi/eval/m_bridge.dart';
|
||||
import 'package:mangayomi/models/video.dart';
|
||||
import 'package:mangayomi/utils/extensions.dart';
|
||||
import 'package:mangayomi/utils/xpath_selector.dart';
|
||||
|
|
@ -22,7 +22,7 @@ class Mp4uploadExtractor {
|
|||
.attrs;
|
||||
|
||||
if (scriptElementWithEval.isNotEmpty) {
|
||||
script = _evalJs(script)!;
|
||||
script = MBridge.evalJs(script);
|
||||
} else {
|
||||
final scriptElementWithSrc = xpathSelector(response.body)
|
||||
.queryXPath('//script[contains(text(), "player.src")]/text()')
|
||||
|
|
@ -52,8 +52,3 @@ class Mp4uploadExtractor {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
String? _evalJs(String script) {
|
||||
final jsPacker = JSPacker(script);
|
||||
return jsPacker.unpack();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import 'package:js_packer/js_packer.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:mangayomi/eval/m_bridge.dart';
|
||||
import 'package:mangayomi/models/video.dart';
|
||||
import 'package:mangayomi/utils/extensions.dart';
|
||||
import 'package:mangayomi/utils/xpath_selector.dart';
|
||||
|
|
@ -20,12 +20,12 @@ class StreamWishExtractor {
|
|||
return [];
|
||||
}
|
||||
|
||||
String? masterUrl = _evalJs(jsEval.first!)
|
||||
?.substringAfter('source')
|
||||
String? masterUrl = MBridge.evalJs(jsEval.first!)
|
||||
.substringAfter('source')
|
||||
.substringAfter('file:"')
|
||||
.substringBefore('"');
|
||||
|
||||
if (masterUrl == null) return [];
|
||||
if (masterUrl.isEmpty) return [];
|
||||
|
||||
final playlistHeaders = Map<String, String>.from(headers)
|
||||
..addAll({
|
||||
|
|
@ -58,8 +58,3 @@ class StreamWishExtractor {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
String? _evalJs(String script) {
|
||||
final jsPacker = JSPacker(script);
|
||||
return jsPacker.unpack();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,13 +8,13 @@ class CryptoAES {
|
|||
static String encryptAESCryptoJS(String plainText, String passphrase) {
|
||||
try {
|
||||
final salt = genRandomWithNonZero(8);
|
||||
var keyndIV = deriveKeyAndIV(passphrase, salt);
|
||||
var keyndIV = deriveKeyAndIV(passphrase.trim(), salt);
|
||||
final key = encrypt.Key(keyndIV.$1);
|
||||
final iv = encrypt.IV(keyndIV.$2);
|
||||
|
||||
final encrypter = encrypt.Encrypter(
|
||||
encrypt.AES(key, mode: encrypt.AESMode.cbc, padding: "PKCS7"));
|
||||
final encrypted = encrypter.encrypt(plainText, iv: iv);
|
||||
final encrypted = encrypter.encrypt(plainText.trim(), iv: iv);
|
||||
Uint8List encryptedBytesWithSalt = Uint8List.fromList(
|
||||
createUint8ListFromString("Salted__") + salt + encrypted.bytes);
|
||||
return base64.encode(encryptedBytesWithSalt);
|
||||
|
|
@ -30,7 +30,7 @@ class CryptoAES {
|
|||
Uint8List encryptedBytes =
|
||||
encryptedBytesWithSalt.sublist(16, encryptedBytesWithSalt.length);
|
||||
final salt = encryptedBytesWithSalt.sublist(8, 16);
|
||||
var keyndIV = deriveKeyAndIV(passphrase, salt);
|
||||
var keyndIV = deriveKeyAndIV(passphrase.trim(), salt);
|
||||
final key = encrypt.Key(keyndIV.$1);
|
||||
final iv = encrypt.IV(keyndIV.$2);
|
||||
|
||||
|
|
|
|||
|
|
@ -64,4 +64,58 @@ final languagesMap = {
|
|||
"čeština": "cs",
|
||||
"Kurdî": "ku",
|
||||
"Magyar": "hu",
|
||||
"Cebuano": "ceb",
|
||||
"English (United States)": "en-us",
|
||||
"Esperanto": "eo",
|
||||
"Estonian": "et",
|
||||
"Faroese": "fo",
|
||||
"Irish": "ga",
|
||||
"Guarani": "gn",
|
||||
"Gujarati": "gu",
|
||||
"Hausa": "ha",
|
||||
"Haitian Creole": "ht",
|
||||
"Armenian": "hy",
|
||||
"Igbo": "ig",
|
||||
"Icelandic": "is",
|
||||
"Georgian": "ka",
|
||||
"Javanese": "jv",
|
||||
"Kazakh": "kk",
|
||||
"Cambodian": "km",
|
||||
"Kannada": "kn",
|
||||
"Kyrgyz": "ky",
|
||||
"Luxembourgish": "lb",
|
||||
"Laothian": "lo",
|
||||
"Latvian": "lv",
|
||||
"Malagasy": "mg",
|
||||
"Maori": "mi",
|
||||
"Macedonian": "mk",
|
||||
"Marathi": "mr",
|
||||
"Maltese": "mt",
|
||||
"Nepali": "ne",
|
||||
"Nyanja": "ny",
|
||||
"Pashto": "ps",
|
||||
"Portuguese (Portugal)": "pt-pt",
|
||||
"Romansh": "rm",
|
||||
"Sindhi": "sd",
|
||||
"Sinhalese": "si",
|
||||
"Slovak": "sk",
|
||||
"Slovenian": "sl",
|
||||
"Samoan": "sm",
|
||||
"Shona": "sn",
|
||||
"Somali": "so",
|
||||
"Albanian": "sq",
|
||||
"Serbian": "sr",
|
||||
"Sesotho": "st",
|
||||
"Swahili": "sw",
|
||||
"Tamil": "ta",
|
||||
"Tajik": "tg",
|
||||
"Tigrinya": "ti",
|
||||
"Turkmen": "tk",
|
||||
"Tonga": "to",
|
||||
"Urdu": "ur",
|
||||
"Yoruba": "yo",
|
||||
"Chinese (Traditional)": "zh-tw",
|
||||
"Latin": "la",
|
||||
"Uzbek": "uz",
|
||||
"Tagalog": "tl"
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in a new issue