mirror of
https://github.com/K3vinb5/Unyo.git
synced 2026-06-13 13:49:43 +00:00
rewrite: Copied some source files
This commit is contained in:
0
lib/core/services/extensions/extension.dart
Normal file
0
lib/core/services/extensions/extension.dart
Normal file
55
lib/core/services/extensions/mangayomi/Eval/dart/bridge/document.dart
Executable file
55
lib/core/services/extensions/mangayomi/Eval/dart/bridge/document.dart
Executable file
@@ -0,0 +1,55 @@
|
||||
import 'package:d4rt/d4rt.dart';
|
||||
import 'package:html/dom.dart';
|
||||
|
||||
import '../model/document.dart';
|
||||
|
||||
class MDocumentBridge {
|
||||
final documentBridgedClass = BridgedClassDefinition(
|
||||
nativeType: MDocument,
|
||||
name: 'MDocument',
|
||||
constructors: {
|
||||
'': (visitor, positionalArgs, namedArgs) {
|
||||
return MDocument(positionalArgs[0] as Document);
|
||||
},
|
||||
},
|
||||
getters: {
|
||||
'body': (visitor, target) => (target as MDocument).body,
|
||||
'documentElement': (visitor, target) =>
|
||||
(target as MDocument).documentElement,
|
||||
'head': (visitor, target) => (target as MDocument).head,
|
||||
'parent': (visitor, target) => (target as MDocument).parent,
|
||||
'outerHtml': (visitor, target) => (target as MDocument).outerHtml,
|
||||
'text': (visitor, target) => (target as MDocument).text,
|
||||
'children': (visitor, target) => (target as MDocument).children,
|
||||
},
|
||||
methods: {
|
||||
'select': (visitor, target, positionalArgs, namedArgs) =>
|
||||
(target as MDocument).select(positionalArgs[0] as String),
|
||||
'selectFirst': (visitor, target, positionalArgs, namedArgs) =>
|
||||
(target as MDocument).selectFirst(positionalArgs[0] as String),
|
||||
'getElementsByClassName': (visitor, target, positionalArgs, namedArgs) =>
|
||||
(target as MDocument)
|
||||
.getElementsByClassName(positionalArgs[0] as String),
|
||||
'getElementsByTagName': (visitor, target, positionalArgs, namedArgs) =>
|
||||
(target as MDocument)
|
||||
.getElementsByTagName(positionalArgs[0] as String),
|
||||
'getElementById': (visitor, target, positionalArgs, namedArgs) =>
|
||||
(target as MDocument).getElementById(positionalArgs[0] as String),
|
||||
'attr': (visitor, target, positionalArgs, namedArgs) =>
|
||||
(target as MDocument).attr(positionalArgs[0] as String),
|
||||
'hasAttr': (visitor, target, positionalArgs, namedArgs) =>
|
||||
(target as MDocument).hasAttr(positionalArgs[0] as String),
|
||||
'xpath': (visitor, target, positionalArgs, namedArgs) =>
|
||||
(target as MDocument).xpath(positionalArgs[0] as String),
|
||||
'xpathFirst': (visitor, target, positionalArgs, namedArgs) =>
|
||||
(target as MDocument).xpathFirst(positionalArgs[0] as String),
|
||||
},
|
||||
);
|
||||
|
||||
void registerBridgedClasses(D4rt interpreter) {
|
||||
interpreter.registerBridgedClass(
|
||||
documentBridgedClass,
|
||||
'package:mangayomi/bridge_lib.dart',
|
||||
);
|
||||
}
|
||||
}
|
||||
63
lib/core/services/extensions/mangayomi/Eval/dart/bridge/element.dart
Executable file
63
lib/core/services/extensions/mangayomi/Eval/dart/bridge/element.dart
Executable file
@@ -0,0 +1,63 @@
|
||||
import 'package:d4rt/d4rt.dart';
|
||||
import 'package:html/dom.dart';
|
||||
|
||||
import '../model/element.dart';
|
||||
|
||||
class MElementBridge {
|
||||
final elementBridgedClass = BridgedClassDefinition(
|
||||
nativeType: MElement,
|
||||
name: 'MElement',
|
||||
constructors: {
|
||||
'': (visitor, positionalArgs, namedArgs) {
|
||||
return MElement(positionalArgs[0] as Element);
|
||||
},
|
||||
},
|
||||
getters: {
|
||||
'outerHtml': (visitor, target) => (target as MElement).outerHtml,
|
||||
'innerHtml': (visitor, target) => (target as MElement).innerHtml,
|
||||
'text': (visitor, target) => (target as MElement).text,
|
||||
'className': (visitor, target) => (target as MElement).className,
|
||||
'localName': (visitor, target) => (target as MElement).localName,
|
||||
'namespaceUri': (visitor, target) => (target as MElement).namespaceUri,
|
||||
'getSrc': (visitor, target) => (target as MElement).getSrc,
|
||||
'getImg': (visitor, target) => (target as MElement).getImg,
|
||||
'getHref': (visitor, target) => (target as MElement).getHref,
|
||||
'getDataSrc': (visitor, target) => (target as MElement).getDataSrc,
|
||||
'children': (visitor, target) => (target as MElement).children,
|
||||
'parent': (visitor, target) => (target as MElement).parent,
|
||||
'nextElementSibling': (visitor, target) =>
|
||||
(target as MElement).nextElementSibling,
|
||||
'previousElementSibling': (visitor, target) =>
|
||||
(target as MElement).previousElementSibling,
|
||||
},
|
||||
methods: {
|
||||
'attr': (visitor, target, positionalArgs, namedArgs) =>
|
||||
(target as MElement).attr(positionalArgs[0] as String),
|
||||
'text': (visitor, target, positionalArgs, namedArgs) =>
|
||||
(target as MElement).text,
|
||||
'select': (visitor, target, positionalArgs, namedArgs) =>
|
||||
(target as MElement).select(positionalArgs[0] as String),
|
||||
'selectFirst': (visitor, target, positionalArgs, namedArgs) =>
|
||||
(target as MElement).selectFirst(positionalArgs[0] as String),
|
||||
'getElementsByClassName': (visitor, target, positionalArgs, namedArgs) =>
|
||||
(target as MElement)
|
||||
.getElementsByClassName(positionalArgs[0] as String),
|
||||
'getElementsByTagName': (visitor, target, positionalArgs, namedArgs) =>
|
||||
(target as MElement)
|
||||
.getElementsByTagName(positionalArgs[0] as String),
|
||||
'xpath': (visitor, target, positionalArgs, namedArgs) =>
|
||||
(target as MElement).xpath(positionalArgs[0] as String),
|
||||
'xpathFirst': (visitor, target, positionalArgs, namedArgs) =>
|
||||
(target as MElement).xpathFirst(positionalArgs[0] as String),
|
||||
'hasAttr': (visitor, target, positionalArgs, namedArgs) =>
|
||||
(target as MElement).hasAttr(positionalArgs[0] as String),
|
||||
},
|
||||
);
|
||||
|
||||
void registerBridgedClasses(D4rt interpreter) {
|
||||
interpreter.registerBridgedClass(
|
||||
elementBridgedClass,
|
||||
'package:mangayomi/bridge_lib.dart',
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,361 @@
|
||||
import 'package:d4rt/d4rt.dart';
|
||||
|
||||
import '../model/filter.dart';
|
||||
|
||||
class FilterBridge {
|
||||
final filterBridgedClass = BridgedClassDefinition(
|
||||
nativeType: FilterList,
|
||||
name: 'FilterList',
|
||||
constructors: {
|
||||
'': (visitor, positionalArgs, namedArgs) {
|
||||
return FilterList(positionalArgs[0] as List);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
'filters': (visitor, target, positionalArgs, namedArgs) =>
|
||||
(target as FilterList).filters,
|
||||
},
|
||||
setters: {
|
||||
'filters': (visitor, target, value) =>
|
||||
(target as FilterList).filters = value as List,
|
||||
},
|
||||
);
|
||||
final selectFilterBridgedClass = BridgedClassDefinition(
|
||||
nativeType: SelectFilter,
|
||||
name: 'SelectFilter',
|
||||
constructors: {
|
||||
'': (visitor, positionalArgs, namedArgs) {
|
||||
return SelectFilter(
|
||||
positionalArgs.get<String?>(0),
|
||||
positionalArgs.get<String>(1)!,
|
||||
positionalArgs.get<int>(2)!,
|
||||
positionalArgs.get<List>(3)!,
|
||||
positionalArgs.get<String?>(4),
|
||||
);
|
||||
},
|
||||
},
|
||||
getters: {
|
||||
'type': (visitor, target) => (target as SelectFilter).type,
|
||||
'name': (visitor, target) => (target as SelectFilter).name,
|
||||
'state': (visitor, target) => (target as SelectFilter).state,
|
||||
'values': (visitor, target) => (target as SelectFilter).values,
|
||||
'typeName': (visitor, target) => (target as SelectFilter).typeName,
|
||||
},
|
||||
setters: {
|
||||
'state': (visitor, target, value) =>
|
||||
(target as SelectFilter).state = value as int,
|
||||
'values': (visitor, target, value) =>
|
||||
(target as SelectFilter).values = value as List,
|
||||
'type': (visitor, target, value) =>
|
||||
(target as SelectFilter).type = value as String,
|
||||
'name': (visitor, target, value) =>
|
||||
(target as SelectFilter).name = value as String,
|
||||
'typeName': (visitor, target, value) =>
|
||||
(target as SelectFilter).typeName = value as String?,
|
||||
},
|
||||
);
|
||||
final selectFilterOptionBridgedClass = BridgedClassDefinition(
|
||||
nativeType: SelectFilterOption,
|
||||
name: 'SelectFilterOption',
|
||||
constructors: {
|
||||
'': (visitor, positionalArgs, namedArgs) {
|
||||
return SelectFilterOption(
|
||||
positionalArgs.get<String>(0)!,
|
||||
positionalArgs.get<String>(1)!,
|
||||
positionalArgs.get<String?>(2),
|
||||
);
|
||||
},
|
||||
},
|
||||
getters: {
|
||||
'name': (visitor, target) => (target as SelectFilterOption).name,
|
||||
'value': (visitor, target) => (target as SelectFilterOption).value,
|
||||
'typeName': (visitor, target) => (target as SelectFilterOption).typeName,
|
||||
},
|
||||
setters: {
|
||||
'name': (visitor, target, value) =>
|
||||
(target as SelectFilterOption).name = value as String,
|
||||
'value': (visitor, target, value) =>
|
||||
(target as SelectFilterOption).value = value as String,
|
||||
'typeName': (visitor, target, value) =>
|
||||
(target as SelectFilterOption).typeName = value as String?,
|
||||
},
|
||||
);
|
||||
|
||||
final separatorFilterBridgedClass = BridgedClassDefinition(
|
||||
nativeType: SeparatorFilter,
|
||||
name: 'SeparatorFilter',
|
||||
constructors: {
|
||||
'': (visitor, positionalArgs, namedArgs) {
|
||||
return SeparatorFilter(
|
||||
null,
|
||||
type: positionalArgs.get<String?>(0) ?? '',
|
||||
);
|
||||
},
|
||||
},
|
||||
getters: {
|
||||
'type': (visitor, target) => (target as SeparatorFilter),
|
||||
'typeName': (visitor, target) => (target as SeparatorFilter).typeName,
|
||||
},
|
||||
setters: {
|
||||
'type': (visitor, target, value) =>
|
||||
(target as SeparatorFilter).type = value as String?,
|
||||
'typeName': (visitor, target, value) =>
|
||||
(target as SeparatorFilter).typeName = value as String?,
|
||||
},
|
||||
);
|
||||
|
||||
final headerFilterBridgedClass = BridgedClassDefinition(
|
||||
nativeType: HeaderFilter,
|
||||
name: 'HeaderFilter',
|
||||
constructors: {
|
||||
'': (visitor, positionalArgs, namedArgs) {
|
||||
return HeaderFilter(
|
||||
positionalArgs.get<String>(0)!,
|
||||
null,
|
||||
type: positionalArgs.get<String?>(1) ?? '',
|
||||
);
|
||||
},
|
||||
},
|
||||
getters: {
|
||||
'type': (visitor, target) => (target as HeaderFilter).type,
|
||||
'name': (visitor, target) => (target as HeaderFilter).name,
|
||||
'typeName': (visitor, target) => (target as HeaderFilter).typeName,
|
||||
},
|
||||
setters: {
|
||||
'type': (visitor, target, value) =>
|
||||
(target as HeaderFilter).type = value as String?,
|
||||
'name': (visitor, target, value) =>
|
||||
(target as HeaderFilter).name = value as String,
|
||||
'typeName': (visitor, target, value) =>
|
||||
(target as HeaderFilter).typeName = value as String?,
|
||||
},
|
||||
);
|
||||
|
||||
final textFilterBridgedClass = BridgedClassDefinition(
|
||||
nativeType: TextFilter,
|
||||
name: 'TextFilter',
|
||||
constructors: {
|
||||
'': (visitor, positionalArgs, namedArgs) {
|
||||
return TextFilter(
|
||||
positionalArgs.get<String>(0),
|
||||
positionalArgs.get<String>(1)!,
|
||||
positionalArgs.get<String>(2),
|
||||
state: namedArgs.get<String?>('state') ?? '',
|
||||
);
|
||||
},
|
||||
},
|
||||
getters: {
|
||||
'type': (visitor, target) => (target as TextFilter).type,
|
||||
'name': (visitor, target) => (target as TextFilter).name,
|
||||
'state': (visitor, target) => (target as TextFilter).state,
|
||||
'typeName': (visitor, target) => (target as TextFilter).typeName,
|
||||
},
|
||||
setters: {
|
||||
'state': (visitor, target, value) =>
|
||||
(target as TextFilter).state = value as String,
|
||||
'type': (visitor, target, value) =>
|
||||
(target as TextFilter).type = value as String?,
|
||||
'name': (visitor, target, value) =>
|
||||
(target as TextFilter).name = value as String,
|
||||
'typeName': (visitor, target, value) =>
|
||||
(target as TextFilter).typeName = value as String?,
|
||||
},
|
||||
);
|
||||
|
||||
final sortFilterBridgedClass = BridgedClassDefinition(
|
||||
nativeType: SortFilter,
|
||||
name: 'SortFilter',
|
||||
constructors: {
|
||||
'': (visitor, positionalArgs, namedArgs) {
|
||||
return SortFilter(
|
||||
positionalArgs.get<String>(0),
|
||||
positionalArgs.get<String>(1)!,
|
||||
positionalArgs.get<SortState>(2)!,
|
||||
positionalArgs.get<List>(3)!,
|
||||
positionalArgs.get<String?>(4),
|
||||
);
|
||||
},
|
||||
},
|
||||
getters: {
|
||||
'type': (visitor, target) => (target as SortFilter).type,
|
||||
'name': (visitor, target) => (target as SortFilter).name,
|
||||
'state': (visitor, target) => (target as SortFilter).state,
|
||||
'typeName': (visitor, target) => (target as SortFilter).typeName,
|
||||
'values': (visitor, target) => (target as SortFilter).values,
|
||||
},
|
||||
setters: {
|
||||
'type': (visitor, target, value) =>
|
||||
(target as SortFilter).type = value as String?,
|
||||
'name': (visitor, target, value) =>
|
||||
(target as SortFilter).name = value as String,
|
||||
'typeName': (visitor, target, value) =>
|
||||
(target as SortFilter).typeName = value as String?,
|
||||
'values': (visitor, target, value) =>
|
||||
(target as SortFilter).values = value as List,
|
||||
},
|
||||
);
|
||||
final sortStateBridgedClass = BridgedClassDefinition(
|
||||
nativeType: SortState,
|
||||
name: 'SortState',
|
||||
constructors: {
|
||||
'': (visitor, positionalArgs, namedArgs) {
|
||||
return SortState(
|
||||
positionalArgs.get<int>(0)!,
|
||||
positionalArgs.get<bool>(1)!,
|
||||
positionalArgs.get<String?>(2),
|
||||
);
|
||||
},
|
||||
},
|
||||
getters: {
|
||||
'index': (visitor, target) => (target as SortState).index,
|
||||
'ascending': (visitor, target) => (target as SortState).ascending,
|
||||
'typeName': (visitor, target) => (target as SortState).typeName,
|
||||
},
|
||||
setters: {
|
||||
'index': (visitor, target, value) =>
|
||||
(target as SortState).index = value as int,
|
||||
'ascending': (visitor, target, value) =>
|
||||
(target as SortState).ascending = value as bool,
|
||||
'typeName': (visitor, target, value) =>
|
||||
(target as SortState).typeName = value as String?,
|
||||
},
|
||||
);
|
||||
|
||||
final triStateFilterBridgedClass = BridgedClassDefinition(
|
||||
nativeType: TriStateFilter,
|
||||
name: 'TriStateFilter',
|
||||
constructors: {
|
||||
'': (visitor, positionalArgs, namedArgs) {
|
||||
return TriStateFilter(
|
||||
positionalArgs.get<String?>(2),
|
||||
positionalArgs.get<String>(0)!,
|
||||
positionalArgs.get<String>(1)!,
|
||||
positionalArgs.get<String?>(3),
|
||||
state: positionalArgs.get<int?>(3) ?? 0,
|
||||
);
|
||||
},
|
||||
},
|
||||
getters: {
|
||||
'type': (visitor, target) => (target as TriStateFilter).type,
|
||||
'name': (visitor, target) => (target as TriStateFilter).name,
|
||||
'state': (visitor, target) => (target as TriStateFilter).state,
|
||||
'typeName': (visitor, target) => (target as TriStateFilter).typeName,
|
||||
'value': (visitor, target) => (target as TriStateFilter).value,
|
||||
},
|
||||
setters: {
|
||||
'state': (visitor, target, value) =>
|
||||
(target as TriStateFilter).state = value as int,
|
||||
'type': (visitor, target, value) =>
|
||||
(target as TriStateFilter).type = value as String?,
|
||||
'name': (visitor, target, value) =>
|
||||
(target as TriStateFilter).name = value as String,
|
||||
'typeName': (visitor, target, value) =>
|
||||
(target as TriStateFilter).typeName = value as String?,
|
||||
},
|
||||
);
|
||||
|
||||
final groupFilterBridgedClass = BridgedClassDefinition(
|
||||
nativeType: GroupFilter,
|
||||
name: 'GroupFilter',
|
||||
constructors: {
|
||||
'': (visitor, positionalArgs, namedArgs) {
|
||||
return GroupFilter(
|
||||
positionalArgs.get<String?>(0),
|
||||
positionalArgs.get<String>(1)!,
|
||||
positionalArgs.get<List>(2)!,
|
||||
positionalArgs.get<String?>(3),
|
||||
);
|
||||
},
|
||||
},
|
||||
getters: {
|
||||
'type': (visitor, target) => (target as GroupFilter).type,
|
||||
'name': (visitor, target) => (target as GroupFilter).name,
|
||||
'state': (visitor, target) => (target as GroupFilter).state,
|
||||
'typeName': (visitor, target) => (target as GroupFilter).typeName,
|
||||
},
|
||||
setters: {
|
||||
'type': (visitor, target, value) =>
|
||||
(target as GroupFilter).type = value as String?,
|
||||
'name': (visitor, target, value) =>
|
||||
(target as GroupFilter).name = value as String,
|
||||
'typeName': (visitor, target, value) =>
|
||||
(target as GroupFilter).typeName = value as String?,
|
||||
'state': (visitor, target, value) =>
|
||||
(target as GroupFilter).state = value as List,
|
||||
},
|
||||
);
|
||||
|
||||
final checkBoxFilterBridgedClass = BridgedClassDefinition(
|
||||
nativeType: CheckBoxFilter,
|
||||
name: 'CheckBoxFilter',
|
||||
constructors: {
|
||||
'': (visitor, positionalArgs, namedArgs) {
|
||||
return CheckBoxFilter(
|
||||
positionalArgs.get<String?>(2) ?? '',
|
||||
positionalArgs.get<String>(0)!,
|
||||
positionalArgs.get<String>(1)!,
|
||||
null,
|
||||
state: positionalArgs.get<bool?>(3) ?? false,
|
||||
);
|
||||
},
|
||||
},
|
||||
getters: {
|
||||
'type': (visitor, target) => (target as CheckBoxFilter).type,
|
||||
'name': (visitor, target) => (target as CheckBoxFilter).name,
|
||||
'state': (visitor, target) => (target as CheckBoxFilter).state,
|
||||
'typeName': (visitor, target) => (target as CheckBoxFilter).typeName,
|
||||
'value': (visitor, target) => (target as CheckBoxFilter).value,
|
||||
},
|
||||
setters: {
|
||||
'state': (visitor, target, value) =>
|
||||
(target as CheckBoxFilter).state = value as bool,
|
||||
'type': (visitor, target, value) =>
|
||||
(target as CheckBoxFilter).type = value as String?,
|
||||
'name': (visitor, target, value) =>
|
||||
(target as CheckBoxFilter).name = value as String,
|
||||
'typeName': (visitor, target, value) =>
|
||||
(target as CheckBoxFilter).typeName = value as String?,
|
||||
'value': (visitor, target, value) =>
|
||||
(target as CheckBoxFilter).value = value as String,
|
||||
},
|
||||
);
|
||||
|
||||
void registerBridgedClasses(D4rt interpreter) {
|
||||
interpreter.registerBridgedClass(
|
||||
filterBridgedClass,
|
||||
'package:mangayomi/bridge_lib.dart',
|
||||
);
|
||||
interpreter.registerBridgedClass(
|
||||
selectFilterBridgedClass,
|
||||
'package:mangayomi/bridge_lib.dart',
|
||||
);
|
||||
interpreter.registerBridgedClass(
|
||||
selectFilterOptionBridgedClass,
|
||||
'package:mangayomi/bridge_lib.dart',
|
||||
);
|
||||
interpreter.registerBridgedClass(
|
||||
separatorFilterBridgedClass,
|
||||
'package:mangayomi/bridge_lib.dart',
|
||||
);
|
||||
interpreter.registerBridgedClass(
|
||||
textFilterBridgedClass,
|
||||
'package:mangayomi/bridge_lib.dart',
|
||||
);
|
||||
interpreter.registerBridgedClass(
|
||||
sortFilterBridgedClass,
|
||||
'package:mangayomi/bridge_lib.dart',
|
||||
);
|
||||
interpreter.registerBridgedClass(
|
||||
triStateFilterBridgedClass,
|
||||
'package:mangayomi/bridge_lib.dart',
|
||||
);
|
||||
interpreter.registerBridgedClass(
|
||||
groupFilterBridgedClass,
|
||||
'package:mangayomi/bridge_lib.dart',
|
||||
);
|
||||
interpreter.registerBridgedClass(
|
||||
checkBoxFilterBridgedClass,
|
||||
'package:mangayomi/bridge_lib.dart',
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:d4rt/d4rt.dart';
|
||||
import 'package:http_interceptor/http_interceptor.dart';
|
||||
|
||||
import '../../../http/m_client.dart';
|
||||
import '../model/m_source.dart';
|
||||
|
||||
class HttpBridge {
|
||||
final clientBridgedClass = BridgedClassDefinition(
|
||||
nativeType: InterceptedClient,
|
||||
name: 'Client',
|
||||
constructors: {
|
||||
'': (visitor, positionalArgs, namedArgs) {
|
||||
return MClient.init(
|
||||
source:
|
||||
positionalArgs.isNotEmpty ? positionalArgs[0] as MSource : null,
|
||||
reqcopyWith: positionalArgs.length > 1
|
||||
? (jsonDecode(positionalArgs[1] as String) as Map)
|
||||
.cast<String, dynamic>()
|
||||
: null,
|
||||
);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
'get': (visitor, target, positionalArgs, namedArgs) =>
|
||||
(target as Client).get(
|
||||
positionalArgs[0] as Uri,
|
||||
headers: namedArgs.get<Map?>('headers')?.cast(),
|
||||
),
|
||||
'post': (visitor, target, positionalArgs, namedArgs) =>
|
||||
(target as Client).post(
|
||||
positionalArgs[0] as Uri,
|
||||
headers: namedArgs.get<Map?>('headers')?.cast(),
|
||||
body: namedArgs.get<Object?>('body'),
|
||||
encoding: namedArgs.get<Encoding?>('encoding'),
|
||||
),
|
||||
'put': (visitor, target, positionalArgs, namedArgs) =>
|
||||
(target as Client).put(
|
||||
positionalArgs[0] as Uri,
|
||||
headers: namedArgs.get<Map?>('headers')?.cast(),
|
||||
body: namedArgs.get<Object?>('body'),
|
||||
encoding: namedArgs.get<Encoding?>('encoding'),
|
||||
),
|
||||
'delete': (visitor, target, positionalArgs, namedArgs) =>
|
||||
(target as Client).delete(
|
||||
positionalArgs[0] as Uri,
|
||||
headers: namedArgs.get<Map?>('headers')?.cast(),
|
||||
body: namedArgs.get<Object?>('body'),
|
||||
encoding: namedArgs.get<Encoding?>('encoding'),
|
||||
),
|
||||
'head': (visitor, target, positionalArgs, namedArgs) =>
|
||||
(target as Client).head(
|
||||
positionalArgs[0] as Uri,
|
||||
headers: namedArgs.get<Map?>('headers')?.cast(),
|
||||
),
|
||||
'patch': (visitor, target, positionalArgs, namedArgs) =>
|
||||
(target as Client).patch(
|
||||
positionalArgs[0] as Uri,
|
||||
headers: namedArgs.get<Map?>('headers')?.cast(),
|
||||
body: namedArgs.get<Object?>('body'),
|
||||
encoding: namedArgs.get<Encoding?>('encoding'),
|
||||
),
|
||||
'read': (visitor, target, positionalArgs, namedArgs) =>
|
||||
(target as Client).read(
|
||||
positionalArgs[0] as Uri,
|
||||
headers: namedArgs.get<Map?>('headers')?.cast(),
|
||||
),
|
||||
'readBytes': (visitor, target, positionalArgs, namedArgs) =>
|
||||
(target as Client).readBytes(
|
||||
positionalArgs[0] as Uri,
|
||||
headers: namedArgs.get<Map?>('headers')?.cast(),
|
||||
),
|
||||
'close': (visitor, target, positionalArgs, namedArgs) =>
|
||||
(target as Client).close(),
|
||||
'send': (visitor, target, positionalArgs, namedArgs) =>
|
||||
(target as Client).send(positionalArgs[0] as BaseRequest),
|
||||
},
|
||||
);
|
||||
final baseRequestBridgedClass = BridgedClassDefinition(
|
||||
nativeType: BaseRequest,
|
||||
name: 'BaseRequest',
|
||||
constructors: {
|
||||
'': (visitor, positionalArgs, namedArgs) {
|
||||
return BaseRequest;
|
||||
},
|
||||
},
|
||||
getters: {
|
||||
'contentLength': (visitor, target) =>
|
||||
(target as BaseRequest).contentLength,
|
||||
'headers': (visitor, target) => (target as BaseRequest).headers,
|
||||
'url': (visitor, target) => (target as BaseRequest).url,
|
||||
'persistentConnection': (visitor, target) =>
|
||||
(target as BaseRequest).persistentConnection,
|
||||
'method': (visitor, target) => (target as BaseRequest).method,
|
||||
'followRedirects': (visitor, target) =>
|
||||
(target as BaseRequest).followRedirects,
|
||||
'maxRedirects': (visitor, target) => (target as BaseRequest).maxRedirects,
|
||||
'finalized': (visitor, target) => (target as BaseRequest).finalized,
|
||||
},
|
||||
);
|
||||
final responseBridgedClass = BridgedClassDefinition(
|
||||
nativeType: Response,
|
||||
name: 'Response',
|
||||
constructors: {
|
||||
'': (visitor, positionalArgs, namedArgs) {
|
||||
return Response;
|
||||
},
|
||||
},
|
||||
getters: {
|
||||
'statusCode': (visitor, target) => (target as Response).statusCode,
|
||||
'body': (visitor, target) => (target as Response).body,
|
||||
'headers': (visitor, target) => (target as Response).headers,
|
||||
'isRedirect': (visitor, target) => (target as Response).isRedirect,
|
||||
'reasonPhrase': (visitor, target) => (target as Response).reasonPhrase,
|
||||
'contentLength': (visitor, target) => (target as Response).contentLength,
|
||||
'bodyBytes': (visitor, target) => (target as Response).bodyBytes,
|
||||
'persistentConnection': (visitor, target) =>
|
||||
(target as Response).persistentConnection,
|
||||
'request': (visitor, target) => (target as Response).request,
|
||||
},
|
||||
);
|
||||
final streamedResponseBridgedClass = BridgedClassDefinition(
|
||||
nativeType: StreamedResponse,
|
||||
name: 'StreamedResponse',
|
||||
constructors: {
|
||||
'': (visitor, positionalArgs, namedArgs) {
|
||||
return StreamedResponse;
|
||||
},
|
||||
},
|
||||
getters: {
|
||||
'statusCode': (visitor, target) =>
|
||||
(target as StreamedResponse).statusCode,
|
||||
'headers': (visitor, target) => (target as StreamedResponse).headers,
|
||||
'isRedirect': (visitor, target) =>
|
||||
(target as StreamedResponse).isRedirect,
|
||||
'reasonPhrase': (visitor, target) =>
|
||||
(target as StreamedResponse).reasonPhrase,
|
||||
'stream': (visitor, target) => (target as StreamedResponse).stream,
|
||||
'contentLength': (visitor, target) =>
|
||||
(target as StreamedResponse).contentLength,
|
||||
'persistentConnection': (visitor, target) =>
|
||||
(target as StreamedResponse).persistentConnection,
|
||||
'request': (visitor, target) => (target as StreamedResponse).request,
|
||||
},
|
||||
);
|
||||
final byteStreamBridgedClass = BridgedClassDefinition(
|
||||
nativeType: ByteStream,
|
||||
name: 'ByteStream',
|
||||
constructors: {
|
||||
'': (visitor, positionalArgs, namedArgs) {
|
||||
return ByteStream;
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
void registerBridgedClasses(D4rt interpreter) {
|
||||
interpreter.registerBridgedClass(
|
||||
baseRequestBridgedClass,
|
||||
'package:mangayomi/bridge_lib.dart',
|
||||
);
|
||||
interpreter.registerBridgedClass(
|
||||
clientBridgedClass,
|
||||
'package:mangayomi/bridge_lib.dart',
|
||||
);
|
||||
interpreter.registerBridgedClass(
|
||||
responseBridgedClass,
|
||||
'package:mangayomi/bridge_lib.dart',
|
||||
);
|
||||
interpreter.registerBridgedClass(
|
||||
streamedResponseBridgedClass,
|
||||
'package:mangayomi/bridge_lib.dart',
|
||||
);
|
||||
interpreter.registerBridgedClass(
|
||||
byteStreamBridgedClass,
|
||||
'package:mangayomi/bridge_lib.dart',
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import 'package:d4rt/d4rt.dart';
|
||||
|
||||
import '../model/m_chapter.dart';
|
||||
|
||||
class MChapterBridge {
|
||||
final mChapterBridgedClass = BridgedClassDefinition(
|
||||
nativeType: MChapter,
|
||||
name: 'MChapter',
|
||||
constructors: {
|
||||
'': (visitor, positionalArgs, namedArgs) {
|
||||
return MChapter(
|
||||
name: namedArgs.get<String?>('name'),
|
||||
url: namedArgs.get<String?>('url'),
|
||||
dateUpload: namedArgs.get<String?>('dateUpload'),
|
||||
scanlator: namedArgs.get<String?>('scanlator'),
|
||||
);
|
||||
},
|
||||
},
|
||||
getters: {
|
||||
'name': (visitor, target) => (target as MChapter).name,
|
||||
'url': (visitor, target) => (target as MChapter).url,
|
||||
'dateUpload': (visitor, target) => (target as MChapter).dateUpload,
|
||||
'scanlator': (visitor, target) => (target as MChapter).scanlator,
|
||||
},
|
||||
setters: {
|
||||
'name': (visitor, target, value) =>
|
||||
(target as MChapter).name = value as String?,
|
||||
'url': (visitor, target, value) =>
|
||||
(target as MChapter).url = value as String?,
|
||||
'dateUpload': (visitor, target, value) =>
|
||||
(target as MChapter).dateUpload = value as String?,
|
||||
'scanlator': (visitor, target, value) =>
|
||||
(target as MChapter).scanlator = value as String?,
|
||||
},
|
||||
);
|
||||
|
||||
void registerBridgedClasses(D4rt interpreter) {
|
||||
interpreter.registerBridgedClass(
|
||||
mChapterBridgedClass,
|
||||
'package:mangayomi/bridge_lib.dart',
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
import 'package:d4rt/d4rt.dart';
|
||||
|
||||
import '../model/m_manga.dart';
|
||||
|
||||
class MMangaBridge {
|
||||
final mMangaBridgedClass = BridgedClassDefinition(
|
||||
nativeType: MManga,
|
||||
name: 'MManga',
|
||||
constructors: {
|
||||
'': (visitor, positionalArgs, namedArgs) {
|
||||
return MManga(
|
||||
artist: namedArgs.get<String?>('artist'),
|
||||
author: namedArgs.get<String?>('author'),
|
||||
description: namedArgs.get<String?>('description'),
|
||||
genre: namedArgs.get<List?>('genre')?.cast(),
|
||||
status: namedArgs.get<Status?>('status') ?? Status.unknown,
|
||||
imageUrl: namedArgs.get<String?>('imageUrl'),
|
||||
link: namedArgs.get<String?>('link'),
|
||||
chapters: namedArgs.get<List?>('chapters')?.cast() ?? [],
|
||||
name: namedArgs.get<String?>('name'),
|
||||
);
|
||||
},
|
||||
},
|
||||
getters: {
|
||||
'name': (visitor, target) => (target as MManga).name,
|
||||
'artist': (visitor, target) => (target as MManga).artist,
|
||||
'author': (visitor, target) => (target as MManga).author,
|
||||
'description': (visitor, target) => (target as MManga).description,
|
||||
'genre': (visitor, target) => (target as MManga).genre,
|
||||
'status': (visitor, target) => (target as MManga).status,
|
||||
'imageUrl': (visitor, target) => (target as MManga).imageUrl,
|
||||
'link': (visitor, target) => (target as MManga).link,
|
||||
'chapters': (visitor, target) => (target as MManga).chapters,
|
||||
},
|
||||
setters: {
|
||||
'name': (visitor, target, value) =>
|
||||
(target as MManga).name = value as String?,
|
||||
'artist': (visitor, target, value) =>
|
||||
(target as MManga).artist = value as String?,
|
||||
'author': (visitor, target, value) =>
|
||||
(target as MManga).author = value as String?,
|
||||
'description': (visitor, target, value) =>
|
||||
(target as MManga).description = value as String?,
|
||||
'genre': (visitor, target, value) =>
|
||||
(target as MManga).genre = (value as List?)?.cast(),
|
||||
'status': (visitor, target, value) =>
|
||||
(target as MManga).status = value as Status?,
|
||||
'imageUrl': (visitor, target, value) =>
|
||||
(target as MManga).imageUrl = value as String?,
|
||||
'link': (visitor, target, value) =>
|
||||
(target as MManga).link = value as String,
|
||||
'chapters': (visitor, target, value) =>
|
||||
(target as MManga).chapters = (value as List?)?.cast(),
|
||||
},
|
||||
);
|
||||
|
||||
void registerBridgedClasses(D4rt interpreter) {
|
||||
interpreter.registerBridgedClass(
|
||||
mMangaBridgedClass,
|
||||
'package:mangayomi/bridge_lib.dart',
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import 'package:d4rt/d4rt.dart';
|
||||
|
||||
import '../model/m_manga.dart';
|
||||
import '../model/m_pages.dart';
|
||||
|
||||
class MPagesBridge {
|
||||
final mPageBridgedClass = BridgedClassDefinition(
|
||||
nativeType: MPages,
|
||||
name: 'MPages',
|
||||
constructors: {
|
||||
'': (visitor, positionalArgs, namedArgs) {
|
||||
return MPages(
|
||||
list: (positionalArgs[0] as List).map((e) => e as MManga).toList(),
|
||||
hasNextPage: positionalArgs[1] as bool,
|
||||
);
|
||||
},
|
||||
},
|
||||
getters: {
|
||||
'list': (visitor, target) => (target as MPages).list,
|
||||
'hasNextPage': (visitor, target) => (target as MPages).hasNextPage,
|
||||
},
|
||||
setters: {
|
||||
'list': (visitor, target, value) =>
|
||||
(target as MPages).list = (value as List).cast<MManga>(),
|
||||
'hasNextPage': (visitor, target, value) =>
|
||||
(target as MPages).hasNextPage = value as bool,
|
||||
},
|
||||
);
|
||||
|
||||
void registerBridgedClasses(D4rt interpreter) {
|
||||
interpreter.registerBridgedClass(
|
||||
mPageBridgedClass,
|
||||
'package:mangayomi/bridge_lib.dart',
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,345 @@
|
||||
import 'package:d4rt/d4rt.dart';
|
||||
import '../../../util/string_extensions.dart';
|
||||
import 'package:flutter_js/flutter_js.dart';
|
||||
|
||||
import '../model/filter.dart';
|
||||
import '../model/m_bridge.dart';
|
||||
import '../model/m_provider.dart';
|
||||
|
||||
class MProviderBridged {
|
||||
final mProviderBridged = BridgedClassDefinition(
|
||||
nativeType: MProvider,
|
||||
name: 'MProvider',
|
||||
constructors: {
|
||||
'': (visitor, positionalArgs, namedArgs) {
|
||||
return MProvider;
|
||||
},
|
||||
},
|
||||
getters: {
|
||||
'supportsLatest': (visitor, target) =>
|
||||
(target as MProvider).supportsLatest,
|
||||
'baseUrl': (visitor, target) => (target as MProvider).baseUrl,
|
||||
'headers': (visitor, target) => (target as MProvider).headers,
|
||||
},
|
||||
methods: {
|
||||
'getLatestUpdates': (visitor, target, positionalArgs, namedArgs) =>
|
||||
(target as MProvider).getLatestUpdates(positionalArgs[0] as int),
|
||||
'getPopular': (visitor, target, positionalArgs, namedArgs) =>
|
||||
(target as MProvider).getPopular(positionalArgs[0] as int),
|
||||
'getVideoList': (visitor, target, positionalArgs, namedArgs) =>
|
||||
(target as MProvider).getVideoList(positionalArgs[0] as String),
|
||||
'search': (visitor, target, positionalArgs, namedArgs) =>
|
||||
(target as MProvider).search(
|
||||
positionalArgs[0] as String,
|
||||
positionalArgs[1] as int,
|
||||
positionalArgs[2] as FilterList,
|
||||
),
|
||||
'getDetail': (visitor, target, positionalArgs, namedArgs) =>
|
||||
(target as MProvider).getDetail(positionalArgs[0] as String),
|
||||
'getPageList': (visitor, target, positionalArgs, namedArgs) =>
|
||||
(target as MProvider).getPageList(positionalArgs[0] as String),
|
||||
'cleanHtmlContent': (visitor, target, positionalArgs, namedArgs) =>
|
||||
(target as MProvider).cleanHtmlContent(positionalArgs[0] as String),
|
||||
'getHtmlContent': (visitor, target, positionalArgs, namedArgs) =>
|
||||
(target as MProvider).getHtmlContent(
|
||||
positionalArgs[0] as String,
|
||||
positionalArgs[1] as String,
|
||||
),
|
||||
'getFilterList': (visitor, target, positionalArgs, namedArgs) =>
|
||||
(target as MProvider).getFilterList(),
|
||||
'getSourcePreferences': (visitor, target, positionalArgs, namedArgs) =>
|
||||
(target as MProvider).getSourcePreferences(),
|
||||
},
|
||||
);
|
||||
|
||||
void registerBridgedClasses(D4rt interpreter) {
|
||||
interpreter.registerBridgedClass(
|
||||
mProviderBridged,
|
||||
'package:mangayomi/bridge_lib.dart',
|
||||
);
|
||||
interpreter.registertopLevelFunction(
|
||||
'getPreferenceValue',
|
||||
(visitor, positionalArgs, namedArgs, _) => getPreferenceValue(
|
||||
positionalArgs[0] as int,
|
||||
positionalArgs[1] as String,
|
||||
),
|
||||
);
|
||||
interpreter.registertopLevelFunction(
|
||||
'getPrefStringValue',
|
||||
(visitor, positionalArgs, namedArgs, _) => getSourcePreferenceStringValue(
|
||||
positionalArgs[0] as int,
|
||||
positionalArgs[1] as String,
|
||||
positionalArgs[2] as String,
|
||||
),
|
||||
);
|
||||
interpreter.registertopLevelFunction(
|
||||
'cryptoHandler',
|
||||
(visitor, positionalArgs, namedArgs, _) => MBridge.cryptoHandler(
|
||||
positionalArgs[0] as String,
|
||||
positionalArgs[1] as String,
|
||||
positionalArgs[2] as String,
|
||||
positionalArgs[3] as bool,
|
||||
),
|
||||
);
|
||||
interpreter.registertopLevelFunction(
|
||||
'encryptAESCryptoJS',
|
||||
(visitor, positionalArgs, namedArgs, _) => MBridge.encryptAESCryptoJS(
|
||||
positionalArgs[0] as String,
|
||||
positionalArgs[1] as String,
|
||||
),
|
||||
);
|
||||
interpreter.registertopLevelFunction(
|
||||
'decryptAESCryptoJS',
|
||||
(visitor, positionalArgs, namedArgs, _) => MBridge.decryptAESCryptoJS(
|
||||
positionalArgs[0] as String,
|
||||
positionalArgs[1] as String,
|
||||
),
|
||||
);
|
||||
interpreter.registertopLevelFunction(
|
||||
'deobfuscateJsPassword',
|
||||
(visitor, positionalArgs, namedArgs, _) =>
|
||||
MBridge.deobfuscateJsPassword(positionalArgs[0] as String),
|
||||
);
|
||||
interpreter.registertopLevelFunction(
|
||||
'sibnetExtractor',
|
||||
(visitor, positionalArgs, namedArgs, _) => MBridge.sibnetExtractor(
|
||||
positionalArgs[0] as String,
|
||||
positionalArgs[1] as String,
|
||||
),
|
||||
);
|
||||
interpreter.registertopLevelFunction(
|
||||
'myTvExtractor',
|
||||
(visitor, positionalArgs, namedArgs, _) =>
|
||||
MBridge.myTvExtractor(positionalArgs[0] as String),
|
||||
);
|
||||
interpreter.registertopLevelFunction(
|
||||
'okruExtractor',
|
||||
(visitor, positionalArgs, namedArgs, _) =>
|
||||
MBridge.okruExtractor(positionalArgs[0] as String),
|
||||
);
|
||||
interpreter.registertopLevelFunction(
|
||||
'voeExtractor',
|
||||
(visitor, positionalArgs, namedArgs, _) => MBridge.voeExtractor(
|
||||
positionalArgs[0] as String,
|
||||
positionalArgs[1] as String?,
|
||||
),
|
||||
);
|
||||
interpreter.registertopLevelFunction(
|
||||
'vidBomExtractor',
|
||||
(visitor, positionalArgs, namedArgs, _) =>
|
||||
MBridge.vidBomExtractor(positionalArgs[0] as String),
|
||||
);
|
||||
interpreter.registertopLevelFunction(
|
||||
'streamlareExtractor',
|
||||
(visitor, positionalArgs, namedArgs, _) => MBridge.streamlareExtractor(
|
||||
positionalArgs[0] as String,
|
||||
positionalArgs[1] as String,
|
||||
positionalArgs[2] as String,
|
||||
),
|
||||
);
|
||||
interpreter.registertopLevelFunction(
|
||||
'sendVidExtractor',
|
||||
(visitor, positionalArgs, namedArgs, _) => MBridge.sendVidExtractor(
|
||||
positionalArgs[0] as String,
|
||||
positionalArgs[1] as String?,
|
||||
positionalArgs[2] as String,
|
||||
),
|
||||
);
|
||||
interpreter.registertopLevelFunction(
|
||||
'yourUploadExtractor',
|
||||
(visitor, positionalArgs, namedArgs, _) => MBridge.yourUploadExtractor(
|
||||
positionalArgs[0] as String,
|
||||
positionalArgs[1] as String?,
|
||||
positionalArgs[2] as String?,
|
||||
positionalArgs[3] as String,
|
||||
),
|
||||
);
|
||||
interpreter.registertopLevelFunction(
|
||||
'quarkVideosExtractor',
|
||||
(visitor, positionalArgs, namedArgs, _) => MBridge.quarkVideosExtractor(
|
||||
positionalArgs[0] as String,
|
||||
positionalArgs[1] as String,
|
||||
),
|
||||
);
|
||||
interpreter.registertopLevelFunction(
|
||||
'ucVideosExtractor',
|
||||
(visitor, positionalArgs, namedArgs, _) => MBridge.ucVideosExtractor(
|
||||
positionalArgs[0] as String,
|
||||
positionalArgs[1] as String,
|
||||
),
|
||||
);
|
||||
interpreter.registertopLevelFunction(
|
||||
'quarkFilesExtractor',
|
||||
(visitor, positionalArgs, namedArgs, _) => MBridge.quarkFilesExtractor(
|
||||
(positionalArgs[0] as List).cast(),
|
||||
positionalArgs[1] as String,
|
||||
),
|
||||
);
|
||||
interpreter.registertopLevelFunction(
|
||||
'ucFilesExtractor',
|
||||
(visitor, positionalArgs, namedArgs, _) => MBridge.ucFilesExtractor(
|
||||
(positionalArgs[0] as List).cast(),
|
||||
positionalArgs[1] as String,
|
||||
),
|
||||
);
|
||||
interpreter.registertopLevelFunction(
|
||||
'substringAfter',
|
||||
(visitor, positionalArgs, namedArgs, _) => MBridge.substringAfter(
|
||||
positionalArgs[0] as String,
|
||||
positionalArgs[1] as String,
|
||||
),
|
||||
);
|
||||
interpreter.registertopLevelFunction(
|
||||
'substringBefore',
|
||||
(visitor, positionalArgs, namedArgs, _) => MBridge.substringBefore(
|
||||
positionalArgs[0] as String,
|
||||
positionalArgs[1] as String,
|
||||
),
|
||||
);
|
||||
interpreter.registertopLevelFunction(
|
||||
'substringBeforeLast',
|
||||
(visitor, positionalArgs, namedArgs, _) => MBridge.substringBeforeLast(
|
||||
positionalArgs[0] as String,
|
||||
positionalArgs[1] as String,
|
||||
),
|
||||
);
|
||||
interpreter.registertopLevelFunction(
|
||||
'substringAfterLast',
|
||||
(visitor, positionalArgs, namedArgs, _) => MBridge.substringAfterLast(
|
||||
positionalArgs[0] as String,
|
||||
positionalArgs[1] as String,
|
||||
),
|
||||
);
|
||||
interpreter.registertopLevelFunction(
|
||||
'getMapValue',
|
||||
(visitor, positionalArgs, namedArgs, _) => MBridge.getMapValue(
|
||||
positionalArgs[0] as String,
|
||||
positionalArgs[1] as String,
|
||||
namedArgs.get<bool?>('encode') ?? false,
|
||||
),
|
||||
);
|
||||
interpreter.registertopLevelFunction(
|
||||
'parseStatus',
|
||||
(visitor, positionalArgs, namedArgs, _) => MBridge.parseStatus(
|
||||
positionalArgs[0] as String,
|
||||
positionalArgs[1] as List,
|
||||
),
|
||||
);
|
||||
interpreter.registertopLevelFunction(
|
||||
'parseDates',
|
||||
(visitor, positionalArgs, namedArgs, _) => MBridge.parseDates(
|
||||
positionalArgs[0] as List,
|
||||
positionalArgs[1] as String,
|
||||
positionalArgs[2] as String,
|
||||
),
|
||||
);
|
||||
interpreter.registertopLevelFunction(
|
||||
'xpath',
|
||||
(visitor, positionalArgs, namedArgs, _) => MBridge.xpath(
|
||||
positionalArgs[0] as String,
|
||||
positionalArgs[1] as String,
|
||||
),
|
||||
);
|
||||
interpreter.registertopLevelFunction(
|
||||
'gogoCdnExtractor',
|
||||
(visitor, positionalArgs, namedArgs, _) =>
|
||||
MBridge.gogoCdnExtractor(positionalArgs[0] as String),
|
||||
);
|
||||
interpreter.registertopLevelFunction(
|
||||
'doodExtractor',
|
||||
(visitor, positionalArgs, namedArgs, _) => MBridge.doodExtractor(
|
||||
positionalArgs[0] as String,
|
||||
positionalArgs[1] as String?,
|
||||
),
|
||||
);
|
||||
interpreter.registertopLevelFunction(
|
||||
'streamTapeExtractor',
|
||||
(visitor, positionalArgs, namedArgs, _) => MBridge.streamTapeExtractor(
|
||||
positionalArgs[0] as String,
|
||||
positionalArgs[1] as String?,
|
||||
),
|
||||
);
|
||||
interpreter.registertopLevelFunction(
|
||||
'mp4UploadExtractor',
|
||||
(visitor, positionalArgs, namedArgs, _) => MBridge.mp4UploadExtractor(
|
||||
positionalArgs[0] as String,
|
||||
positionalArgs[1] as String?,
|
||||
positionalArgs[2] as String,
|
||||
positionalArgs[3] as String,
|
||||
),
|
||||
);
|
||||
interpreter.registertopLevelFunction(
|
||||
'streamWishExtractor',
|
||||
(visitor, positionalArgs, namedArgs, _) => MBridge.streamWishExtractor(
|
||||
positionalArgs[0] as String,
|
||||
positionalArgs[1] as String,
|
||||
),
|
||||
);
|
||||
interpreter.registertopLevelFunction(
|
||||
'filemoonExtractor',
|
||||
(visitor, positionalArgs, namedArgs, _) => MBridge.filemoonExtractor(
|
||||
positionalArgs[0] as String,
|
||||
positionalArgs[1] as String,
|
||||
positionalArgs[2] as String,
|
||||
),
|
||||
);
|
||||
interpreter.registertopLevelFunction(
|
||||
'unpackJs',
|
||||
(visitor, positionalArgs, namedArgs, _) =>
|
||||
MBridge.unpackJs(positionalArgs[0] as String),
|
||||
);
|
||||
interpreter.registertopLevelFunction(
|
||||
'unpackJsAndCombine',
|
||||
(visitor, positionalArgs, namedArgs, _) =>
|
||||
MBridge.unpackJsAndCombine(positionalArgs[0] as String),
|
||||
);
|
||||
interpreter.registertopLevelFunction(
|
||||
'evalJs',
|
||||
(visitor, positionalArgs, namedArgs, _) =>
|
||||
getJavascriptRuntime().evaluateAsync(positionalArgs[0] as String),
|
||||
);
|
||||
interpreter.registertopLevelFunction(
|
||||
'evalJsSync',
|
||||
(visitor, positionalArgs, namedArgs, _) =>
|
||||
getJavascriptRuntime().evaluate(positionalArgs[0] as String),
|
||||
);
|
||||
interpreter.registertopLevelFunction(
|
||||
'regExp',
|
||||
(visitor, positionalArgs, namedArgs, _) => MBridge.regExp(
|
||||
positionalArgs[0] as String,
|
||||
positionalArgs[1] as String,
|
||||
positionalArgs[2] as String,
|
||||
positionalArgs[3] as int,
|
||||
positionalArgs[4] as int,
|
||||
),
|
||||
);
|
||||
interpreter.registertopLevelFunction(
|
||||
'sortMapList',
|
||||
(visitor, positionalArgs, namedArgs, _) => MBridge.sortMapList(
|
||||
positionalArgs[0] as List,
|
||||
positionalArgs[1] as String,
|
||||
positionalArgs[2] as int,
|
||||
),
|
||||
);
|
||||
interpreter.registertopLevelFunction(
|
||||
'parseHtml',
|
||||
(visitor, positionalArgs, namedArgs, _) =>
|
||||
MBridge.parsHtml(positionalArgs[0] as String),
|
||||
);
|
||||
interpreter.registertopLevelFunction(
|
||||
'getUrlWithoutDomain',
|
||||
(visitor, positionalArgs, namedArgs, _) =>
|
||||
(positionalArgs[0] as String).getUrlWithoutDomain,
|
||||
);
|
||||
interpreter.registertopLevelFunction(
|
||||
'evaluateJavascriptViaWebview',
|
||||
(visitor, positionalArgs, namedArgs, _) =>
|
||||
MBridge.evaluateJavascriptViaWebview(
|
||||
positionalArgs[0] as String,
|
||||
(positionalArgs[1] as Map).cast(),
|
||||
(positionalArgs[2] as List).cast(),
|
||||
time: namedArgs.get<int?>('time') ?? 30,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import 'package:d4rt/d4rt.dart';
|
||||
|
||||
import '../model/m_source.dart';
|
||||
|
||||
class MSourceBridge {
|
||||
final mSourceBridgedClass = BridgedClassDefinition(
|
||||
nativeType: MSource,
|
||||
name: 'MSource',
|
||||
constructors: {
|
||||
'': (visitor, positionalArgs, namedArgs) {
|
||||
return MSource(
|
||||
id: namedArgs.get<int?>('id'),
|
||||
name: namedArgs.get<String?>('name'),
|
||||
baseUrl: namedArgs.get<String?>('baseUrl'),
|
||||
lang: namedArgs.get<String?>('lang'),
|
||||
isFullData: namedArgs.get<bool?>('isFullData'),
|
||||
hasCloudflare: namedArgs.get<bool?>('hasCloudflare'),
|
||||
dateFormat: namedArgs.get<String?>('dateFormat'),
|
||||
dateFormatLocale: namedArgs.get<String?>('dateFormatLocale'),
|
||||
apiUrl: namedArgs.get<String?>('apiUrl'),
|
||||
additionalParams: namedArgs.get<String?>('additionalParams'),
|
||||
notes: namedArgs.get<String?>('notes'),
|
||||
);
|
||||
},
|
||||
},
|
||||
getters: {
|
||||
'id': (visitor, target) => (target as MSource).id,
|
||||
'name': (visitor, target) => (target as MSource).name,
|
||||
'baseUrl': (visitor, target) => (target as MSource).baseUrl,
|
||||
'lang': (visitor, target) => (target as MSource).lang,
|
||||
'isFullData': (visitor, target) => (target as MSource).isFullData,
|
||||
'hasCloudflare': (visitor, target) => (target as MSource).hasCloudflare,
|
||||
'dateFormat': (visitor, target) => (target as MSource).dateFormat,
|
||||
'dateFormatLocale': (visitor, target) =>
|
||||
(target as MSource).dateFormatLocale,
|
||||
'apiUrl': (visitor, target) => (target as MSource).apiUrl,
|
||||
'additionalParams': (visitor, target) =>
|
||||
(target as MSource).additionalParams,
|
||||
'notes': (visitor, target) => (target as MSource).notes,
|
||||
},
|
||||
);
|
||||
|
||||
void registerBridgedClasses(D4rt interpreter) {
|
||||
interpreter.registerBridgedClass(
|
||||
mSourceBridgedClass,
|
||||
'package:mangayomi/bridge_lib.dart',
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import 'package:d4rt/d4rt.dart';
|
||||
|
||||
import '../model/m_manga.dart';
|
||||
|
||||
class MStatusBridge {
|
||||
final statusDefinition = BridgedEnumDefinition<Status>(
|
||||
name: 'MStatus',
|
||||
values: Status.values,
|
||||
);
|
||||
|
||||
void registerBridgedEnum(D4rt interpreter) {
|
||||
interpreter.registerBridgedEnum(
|
||||
statusDefinition,
|
||||
'package:mangayomi/bridge_lib.dart',
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import 'package:d4rt/d4rt.dart';
|
||||
|
||||
import '../model/video.dart';
|
||||
|
||||
class MTrackBridge {
|
||||
final mTrackBridgedClass = BridgedClassDefinition(
|
||||
nativeType: Track,
|
||||
name: 'MTrack',
|
||||
constructors: {
|
||||
'': (visitor, positionalArgs, namedArgs) {
|
||||
return Track(
|
||||
file: namedArgs.get<String?>('file'),
|
||||
label: namedArgs.get<String?>('label'),
|
||||
);
|
||||
},
|
||||
},
|
||||
getters: {
|
||||
'file': (visitor, target) => (target as Track).file,
|
||||
'label': (visitor, target) => (target as Track).label,
|
||||
},
|
||||
setters: {
|
||||
'file': (visitor, target, value) =>
|
||||
(target as Track).file = value as String?,
|
||||
'label': (visitor, target, value) =>
|
||||
(target as Track).label = value as String?,
|
||||
},
|
||||
);
|
||||
|
||||
void registerBridgedClasses(D4rt interpreter) {
|
||||
interpreter.registerBridgedClass(
|
||||
mTrackBridgedClass,
|
||||
'package:mangayomi/bridge_lib.dart',
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
import 'package:d4rt/d4rt.dart';
|
||||
|
||||
import '../model/video.dart';
|
||||
|
||||
class MVideoBridge {
|
||||
final mVideoBridgedClass = BridgedClassDefinition(
|
||||
nativeType: Video,
|
||||
name: 'MVideo',
|
||||
constructors: {
|
||||
'': (visitor, positionalArgs, namedArgs) {
|
||||
return Video(
|
||||
positionalArgs.get<String?>(0) ?? '',
|
||||
positionalArgs.get<String?>(1) ?? '',
|
||||
positionalArgs.get<String?>(2) ?? '',
|
||||
headers: namedArgs.get<Map?>('headers')?.cast(),
|
||||
subtitles: namedArgs.get<List<Track>?>('subtitles'),
|
||||
audios: namedArgs.get<List<Track>?>('audios'),
|
||||
);
|
||||
},
|
||||
},
|
||||
getters: {
|
||||
'url': (visitor, target) => (target as Video).url,
|
||||
'quality': (visitor, target) => (target as Video).quality,
|
||||
'originalUrl': (visitor, target) => (target as Video).originalUrl,
|
||||
'headers': (visitor, target) => (target as Video).headers,
|
||||
'subtitles': (visitor, target) => (target as Video).subtitles,
|
||||
'audios': (visitor, target) => (target as Video).audios,
|
||||
},
|
||||
setters: {
|
||||
'url': (visitor, target, value) =>
|
||||
(target as Video).url = value as String,
|
||||
'quality': (visitor, target, value) =>
|
||||
(target as Video).quality = value as String,
|
||||
'originalUrl': (visitor, target, value) =>
|
||||
(target as Video).originalUrl = value as String,
|
||||
'headers': (visitor, target, value) =>
|
||||
(target as Video).headers = (value as Map?)?.cast(),
|
||||
'subtitles': (visitor, target, value) =>
|
||||
(target as Video).subtitles = (value as List?)?.cast(),
|
||||
'audios': (visitor, target, value) =>
|
||||
(target as Video).audios = (value as List?)?.cast(),
|
||||
},
|
||||
);
|
||||
|
||||
void registerBridgedClasses(D4rt interpreter) {
|
||||
interpreter.registerBridgedClass(
|
||||
mVideoBridgedClass,
|
||||
'package:mangayomi/bridge_lib.dart',
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import 'package:d4rt/d4rt.dart';
|
||||
|
||||
import 'document.dart';
|
||||
import 'element.dart';
|
||||
import 'filter.dart';
|
||||
import 'http.dart';
|
||||
import 'm_chapter.dart';
|
||||
import 'm_manga.dart';
|
||||
import 'm_pages.dart';
|
||||
import 'm_provider.dart';
|
||||
import 'm_source.dart';
|
||||
import 'm_status.dart';
|
||||
import 'm_track.dart';
|
||||
import 'm_video.dart';
|
||||
import 'source_preference.dart';
|
||||
|
||||
class RegistrerBridge {
|
||||
static void registerBridge(D4rt interpreter) {
|
||||
MDocumentBridge().registerBridgedClasses(interpreter);
|
||||
MElementBridge().registerBridgedClasses(interpreter);
|
||||
FilterBridge().registerBridgedClasses(interpreter);
|
||||
HttpBridge().registerBridgedClasses(interpreter);
|
||||
MMangaBridge().registerBridgedClasses(interpreter);
|
||||
MChapterBridge().registerBridgedClasses(interpreter);
|
||||
MPagesBridge().registerBridgedClasses(interpreter);
|
||||
MProviderBridged().registerBridgedClasses(interpreter);
|
||||
MSourceBridge().registerBridgedClasses(interpreter);
|
||||
MStatusBridge().registerBridgedEnum(interpreter);
|
||||
MTrackBridge().registerBridgedClasses(interpreter);
|
||||
MVideoBridge().registerBridgedClasses(interpreter);
|
||||
SourcePreferenceBridge().registerBridgedClasses(interpreter);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
import 'package:d4rt/d4rt.dart';
|
||||
|
||||
import '../model/source_preference.dart';
|
||||
|
||||
// EditTextPreference
|
||||
class SourcePreferenceBridge {
|
||||
final checkBoxPreferenceBridgedClass = BridgedClassDefinition(
|
||||
nativeType: CheckBoxPreference,
|
||||
name: 'CheckBoxPreference',
|
||||
constructors: {
|
||||
'': (visitor, positionalArgs, namedArgs) {
|
||||
return SourcePreference(
|
||||
key: namedArgs.get<String?>('key'),
|
||||
checkBoxPreference: CheckBoxPreference(
|
||||
title: namedArgs.get<String?>('title'),
|
||||
summary: namedArgs.get<String?>('summary'),
|
||||
value: namedArgs.get<bool?>('value'),
|
||||
),
|
||||
);
|
||||
},
|
||||
},
|
||||
getters: {
|
||||
'key': (visitor, target) => (target as SourcePreference).key,
|
||||
'title': (visitor, target) =>
|
||||
(target as SourcePreference).checkBoxPreference?.title,
|
||||
'summary': (visitor, target) =>
|
||||
(target as SourcePreference).checkBoxPreference?.summary,
|
||||
'value': (visitor, target) =>
|
||||
(target as SourcePreference).checkBoxPreference?.value,
|
||||
},
|
||||
);
|
||||
final switchPreferenceCompatBridgedClass = BridgedClassDefinition(
|
||||
nativeType: SwitchPreferenceCompat,
|
||||
name: 'SwitchPreferenceCompat',
|
||||
constructors: {
|
||||
'': (visitor, positionalArgs, namedArgs) {
|
||||
return SourcePreference(
|
||||
key: namedArgs.get<String?>('key'),
|
||||
switchPreferenceCompat: SwitchPreferenceCompat(
|
||||
title: namedArgs.get<String?>('title'),
|
||||
summary: namedArgs.get<String?>('summary'),
|
||||
value: namedArgs.get<bool?>('value'),
|
||||
),
|
||||
);
|
||||
},
|
||||
},
|
||||
getters: {
|
||||
'key': (visitor, target) => (target as SourcePreference).key,
|
||||
'title': (visitor, target) =>
|
||||
(target as SourcePreference).switchPreferenceCompat?.title,
|
||||
'summary': (visitor, target) =>
|
||||
(target as SourcePreference).switchPreferenceCompat?.summary,
|
||||
'value': (visitor, target) =>
|
||||
(target as SourcePreference).switchPreferenceCompat?.value,
|
||||
},
|
||||
);
|
||||
final listPreferenceBridgedClass = BridgedClassDefinition(
|
||||
nativeType: ListPreference,
|
||||
name: 'ListPreference',
|
||||
constructors: {
|
||||
'': (visitor, positionalArgs, namedArgs) {
|
||||
return SourcePreference(
|
||||
key: namedArgs.get<String?>('key'),
|
||||
listPreference: ListPreference(
|
||||
title: namedArgs.get<String?>('title'),
|
||||
summary: namedArgs.get<String?>('summary'),
|
||||
valueIndex: namedArgs.get<int?>('valueIndex'),
|
||||
entries: namedArgs.get<List?>('entries')?.cast<String>(),
|
||||
entryValues: namedArgs.get<List?>('entryValues')?.cast<String>(),
|
||||
),
|
||||
);
|
||||
},
|
||||
},
|
||||
getters: {
|
||||
'key': (visitor, target) => (target as SourcePreference).key,
|
||||
'title': (visitor, target) =>
|
||||
(target as SourcePreference).listPreference?.title,
|
||||
'summary': (visitor, target) =>
|
||||
(target as SourcePreference).listPreference?.summary,
|
||||
'value': (visitor, target) =>
|
||||
(target as SourcePreference).listPreference?.valueIndex,
|
||||
'entries': (visitor, target) =>
|
||||
(target as SourcePreference).listPreference?.entries,
|
||||
'entryValues': (visitor, target) =>
|
||||
(target as SourcePreference).listPreference?.entryValues,
|
||||
},
|
||||
);
|
||||
|
||||
final multiSelectListPreferenceBridgedClass = BridgedClassDefinition(
|
||||
nativeType: MultiSelectListPreference,
|
||||
name: 'MultiSelectListPreference',
|
||||
constructors: {
|
||||
'': (visitor, positionalArgs, namedArgs) {
|
||||
return SourcePreference(
|
||||
key: namedArgs.get<String?>('key'),
|
||||
multiSelectListPreference: MultiSelectListPreference(
|
||||
title: namedArgs.get<String?>('title'),
|
||||
summary: namedArgs.get<String?>('summary'),
|
||||
entries: namedArgs.get<List?>('entries')?.cast<String>(),
|
||||
entryValues: namedArgs.get<List?>('entryValues')?.cast<String>(),
|
||||
values: namedArgs.get<List?>('values')?.cast<String>(),
|
||||
),
|
||||
);
|
||||
},
|
||||
},
|
||||
getters: {
|
||||
'key': (visitor, target) => (target as SourcePreference).key,
|
||||
'title': (visitor, target) =>
|
||||
(target as SourcePreference).multiSelectListPreference?.title,
|
||||
'summary': (visitor, target) =>
|
||||
(target as SourcePreference).multiSelectListPreference?.summary,
|
||||
'values': (visitor, target) =>
|
||||
(target as SourcePreference).multiSelectListPreference?.values,
|
||||
'entries': (visitor, target) =>
|
||||
(target as SourcePreference).multiSelectListPreference?.entries,
|
||||
'entryValues': (visitor, target) =>
|
||||
(target as SourcePreference).multiSelectListPreference?.entryValues,
|
||||
},
|
||||
);
|
||||
final editTextPreferenceBridgedClass = BridgedClassDefinition(
|
||||
nativeType: EditTextPreference,
|
||||
name: 'EditTextPreference',
|
||||
constructors: {
|
||||
'': (visitor, positionalArgs, namedArgs) {
|
||||
return SourcePreference(
|
||||
key: namedArgs.get<String?>('key'),
|
||||
editTextPreference: EditTextPreference(
|
||||
title: namedArgs.get<String?>('title'),
|
||||
summary: namedArgs.get<String?>('summary'),
|
||||
value: namedArgs.get<String?>('value'),
|
||||
dialogTitle: namedArgs.get<String?>('dialogTitle'),
|
||||
dialogMessage: namedArgs.get<String?>('dialogMessage'),
|
||||
text: namedArgs.get<String?>('text'),
|
||||
),
|
||||
);
|
||||
},
|
||||
},
|
||||
getters: {
|
||||
'key': (visitor, target) => (target as SourcePreference).key,
|
||||
'title': (visitor, target) =>
|
||||
(target as SourcePreference).editTextPreference?.title,
|
||||
'summary': (visitor, target) =>
|
||||
(target as SourcePreference).editTextPreference?.summary,
|
||||
'value': (visitor, target) =>
|
||||
(target as SourcePreference).editTextPreference?.value,
|
||||
'dialogTitle': (visitor, target) =>
|
||||
(target as SourcePreference).editTextPreference?.dialogTitle,
|
||||
'dialogMessage': (visitor, target) =>
|
||||
(target as SourcePreference).editTextPreference?.dialogMessage,
|
||||
'text': (visitor, target) =>
|
||||
(target as SourcePreference).editTextPreference?.text,
|
||||
},
|
||||
);
|
||||
|
||||
void registerBridgedClasses(D4rt interpreter) {
|
||||
interpreter.registerBridgedClass(
|
||||
checkBoxPreferenceBridgedClass,
|
||||
'package:mangayomi/bridge_lib.dart',
|
||||
);
|
||||
interpreter.registerBridgedClass(
|
||||
switchPreferenceCompatBridgedClass,
|
||||
'package:mangayomi/bridge_lib.dart',
|
||||
);
|
||||
interpreter.registerBridgedClass(
|
||||
listPreferenceBridgedClass,
|
||||
'package:mangayomi/bridge_lib.dart',
|
||||
);
|
||||
interpreter.registerBridgedClass(
|
||||
multiSelectListPreferenceBridgedClass,
|
||||
'package:mangayomi/bridge_lib.dart',
|
||||
);
|
||||
interpreter.registerBridgedClass(
|
||||
editTextPreferenceBridgedClass,
|
||||
'package:mangayomi/bridge_lib.dart',
|
||||
);
|
||||
}
|
||||
}
|
||||
67
lib/core/services/extensions/mangayomi/Eval/dart/model/document.dart
Executable file
67
lib/core/services/extensions/mangayomi/Eval/dart/model/document.dart
Executable file
@@ -0,0 +1,67 @@
|
||||
import '../../../util/dom_extensions.dart';
|
||||
import 'package:html/dom.dart';
|
||||
|
||||
import 'element.dart';
|
||||
|
||||
class MDocument {
|
||||
const MDocument(this._document);
|
||||
|
||||
final Document? _document;
|
||||
|
||||
MElement? get body => MElement(_document?.body);
|
||||
|
||||
MElement? get documentElement => MElement(_document?.documentElement);
|
||||
|
||||
MElement? get head => MElement(_document?.head);
|
||||
|
||||
MElement? get parent => MElement(_document?.parent);
|
||||
|
||||
String? get outerHtml => _document?.outerHtml;
|
||||
|
||||
String? get text => _document?.text?.trim();
|
||||
|
||||
List<MElement>? get children =>
|
||||
_document?.children.map((e) => MElement(e)).toList();
|
||||
|
||||
List<MElement>? select(String selector) {
|
||||
return _document?.select(selector)?.map((e) => MElement(e)).toList();
|
||||
}
|
||||
|
||||
String? xpathFirst(String xpath) {
|
||||
return _document?.xpathFirst(xpath);
|
||||
}
|
||||
|
||||
List<String> xpath(String xpath) {
|
||||
return _document?.xpath(xpath) ?? [];
|
||||
}
|
||||
|
||||
List<MElement>? getElementsByClassName(String classNames) {
|
||||
return _document
|
||||
?.getElementsByClassName(classNames)
|
||||
.map((e) => MElement(e))
|
||||
.toList();
|
||||
}
|
||||
|
||||
List<MElement>? getElementsByTagName(String localNames) {
|
||||
return _document
|
||||
?.getElementsByTagName(localNames)
|
||||
.map((e) => MElement(e))
|
||||
.toList();
|
||||
}
|
||||
|
||||
MElement? getElementById(String id) {
|
||||
return MElement(_document?.getElementById(id));
|
||||
}
|
||||
|
||||
MElement? selectFirst(String selector) {
|
||||
return MElement(_document?.selectFirst(selector));
|
||||
}
|
||||
|
||||
String? attr(String attr) {
|
||||
return _document?.attr(attr)?.trim();
|
||||
}
|
||||
|
||||
bool hasAttr(String attr) {
|
||||
return _document?.hasAtr(attr) ?? false;
|
||||
}
|
||||
}
|
||||
76
lib/core/services/extensions/mangayomi/Eval/dart/model/element.dart
Executable file
76
lib/core/services/extensions/mangayomi/Eval/dart/model/element.dart
Executable file
@@ -0,0 +1,76 @@
|
||||
import '../../../util/dom_extensions.dart';
|
||||
import 'package:html/dom.dart';
|
||||
|
||||
class MElement {
|
||||
MElement(this._element);
|
||||
|
||||
final Element? _element;
|
||||
|
||||
String? get outerHtml => _element?.outerHtml;
|
||||
|
||||
String? get innerHtml => _element?.innerHtml;
|
||||
|
||||
String? get text => _element?.text.trim();
|
||||
|
||||
String? get className => _element?.className;
|
||||
|
||||
String? get localName => _element?.localName;
|
||||
|
||||
String? get namespaceUri => _element?.namespaceUri;
|
||||
|
||||
String? get getSrc => _element?.getSrc;
|
||||
|
||||
String? get getImg => _element?.getImg;
|
||||
|
||||
String? get getHref => _element?.getHref;
|
||||
|
||||
String? get getDataSrc => _element?.getDataSrc;
|
||||
|
||||
List<MElement>? get children =>
|
||||
_element?.children.map((e) => MElement(e)).toList();
|
||||
|
||||
MElement? get parent => MElement(_element?.parent);
|
||||
|
||||
MElement? get nextElementSibling => MElement(_element?.nextElementSibling);
|
||||
|
||||
MElement? get previousElementSibling =>
|
||||
MElement(_element?.previousElementSibling);
|
||||
|
||||
String? xpathFirst(String xpath) {
|
||||
return _element?.outerHtml == null ? null : _element?.xpathFirst(xpath);
|
||||
}
|
||||
|
||||
List<String>? xpath(String xpath) {
|
||||
return _element?.outerHtml == null ? null : _element?.xpath(xpath);
|
||||
}
|
||||
|
||||
List<MElement>? getElementsByClassName(String classNames) {
|
||||
return _element
|
||||
?.getElementsByClassName(classNames)
|
||||
.map((e) => MElement(e))
|
||||
.toList();
|
||||
}
|
||||
|
||||
List<MElement>? getElementsByTagName(String localNames) {
|
||||
return _element
|
||||
?.getElementsByTagName(localNames)
|
||||
.map((e) => MElement(e))
|
||||
.toList();
|
||||
}
|
||||
|
||||
List<MElement>? select(String selector) {
|
||||
return _element?.select(selector)?.map((e) => MElement(e)).toList();
|
||||
}
|
||||
|
||||
MElement? selectFirst(String selector) {
|
||||
return MElement(_element?.selectFirst(selector));
|
||||
}
|
||||
|
||||
String? attr(String attribute) {
|
||||
return _element?.attr(attribute);
|
||||
}
|
||||
|
||||
bool hasAttr(String attr) {
|
||||
return _element?.hasAtr(attr) ?? false;
|
||||
}
|
||||
}
|
||||
311
lib/core/services/extensions/mangayomi/Eval/dart/model/filter.dart
Executable file
311
lib/core/services/extensions/mangayomi/Eval/dart/model/filter.dart
Executable file
@@ -0,0 +1,311 @@
|
||||
import '../../javascript/http.dart';
|
||||
|
||||
class FilterList {
|
||||
List<dynamic> filters;
|
||||
|
||||
FilterList(this.filters);
|
||||
|
||||
factory FilterList.fromJson(Map<String, dynamic> json) {
|
||||
return FilterList(fromJsonFilterValuesToList(json['filters']));
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() => {'filters': filterValuesListToJson(filters)};
|
||||
}
|
||||
|
||||
class SelectFilter {
|
||||
String? type;
|
||||
String name;
|
||||
int state;
|
||||
List<dynamic> values;
|
||||
String? typeName;
|
||||
|
||||
SelectFilter(this.type, this.name, this.state, this.values, this.typeName);
|
||||
|
||||
factory SelectFilter.fromJson(Map<String, dynamic> json) {
|
||||
return SelectFilter(
|
||||
json['type'],
|
||||
json['name'],
|
||||
json['state'] ?? 0,
|
||||
fromJsonFilterValuesToList(json['values']),
|
||||
json['type_name'],
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'type': type,
|
||||
'name': name,
|
||||
'state': state,
|
||||
'values': filterValuesListToJson(values),
|
||||
'type_name': "SelectFilter",
|
||||
};
|
||||
}
|
||||
|
||||
class SelectFilterOption {
|
||||
String name;
|
||||
String value;
|
||||
String? typeName;
|
||||
|
||||
SelectFilterOption(this.name, this.value, this.typeName);
|
||||
|
||||
factory SelectFilterOption.fromJson(Map<String, dynamic> json) {
|
||||
return SelectFilterOption(json['name'], json['value'], json['type_name']);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'value': value,
|
||||
'name': name,
|
||||
'type_name': "SelectOption",
|
||||
};
|
||||
}
|
||||
|
||||
class SeparatorFilter {
|
||||
String? type;
|
||||
String? typeName;
|
||||
|
||||
SeparatorFilter(this.typeName, {this.type = ''});
|
||||
|
||||
factory SeparatorFilter.fromJson(Map<String, dynamic> json) {
|
||||
return SeparatorFilter(type: json['type'], json['type_name']);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'type': type,
|
||||
'type_name': "SeparatorFilter",
|
||||
};
|
||||
}
|
||||
|
||||
class HeaderFilter {
|
||||
String? type;
|
||||
String name;
|
||||
String? typeName;
|
||||
|
||||
HeaderFilter(this.name, this.typeName, {this.type = ''});
|
||||
|
||||
factory HeaderFilter.fromJson(Map<String, dynamic> json) {
|
||||
return HeaderFilter(json['name'], json['type_name'], type: json['value']);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'type': type,
|
||||
'name': name,
|
||||
'type_name': "HeaderFilter",
|
||||
};
|
||||
}
|
||||
|
||||
class TextFilter {
|
||||
String? type;
|
||||
String name;
|
||||
String state;
|
||||
String? typeName;
|
||||
|
||||
TextFilter(this.type, this.name, this.typeName, {this.state = ""});
|
||||
|
||||
factory TextFilter.fromJson(Map<String, dynamic> json) {
|
||||
return TextFilter(
|
||||
json['type'],
|
||||
json['name'],
|
||||
json['type_name'],
|
||||
state: json['state'] ?? "",
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'type': type,
|
||||
'name': name,
|
||||
'state': state,
|
||||
'type_name': "TextFilter",
|
||||
};
|
||||
}
|
||||
|
||||
class SortFilter {
|
||||
String? type;
|
||||
String name;
|
||||
SortState state;
|
||||
List<dynamic> values;
|
||||
String? typeName;
|
||||
|
||||
SortFilter(this.type, this.name, this.state, this.values, this.typeName);
|
||||
|
||||
factory SortFilter.fromJson(Map<String, dynamic> json) {
|
||||
return SortFilter(
|
||||
json['type'],
|
||||
json['name'],
|
||||
json['state'] == null
|
||||
? SortState(0, false, "")
|
||||
: SortState.fromJson(json['state']),
|
||||
fromJsonFilterValuesToList(json['values']),
|
||||
json['type_name'],
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'type': type,
|
||||
'name': name,
|
||||
'state': state.toJson(),
|
||||
'values': filterValuesListToJson(values),
|
||||
'type_name': "SortFilter",
|
||||
};
|
||||
}
|
||||
|
||||
class SortState {
|
||||
int index;
|
||||
bool ascending;
|
||||
String? typeName;
|
||||
|
||||
SortState(this.index, this.ascending, this.typeName);
|
||||
|
||||
factory SortState.fromJson(Map<String, dynamic> json) {
|
||||
return SortState(json['index'], json['ascending'], json['type_name']);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'index': index,
|
||||
'ascending': ascending,
|
||||
'type_name': "SortState",
|
||||
};
|
||||
}
|
||||
|
||||
class TriStateFilter {
|
||||
String? type;
|
||||
String name;
|
||||
String value;
|
||||
int state;
|
||||
String? typeName;
|
||||
|
||||
factory TriStateFilter.fromJson(Map<String, dynamic> json) {
|
||||
return TriStateFilter(
|
||||
json['type'],
|
||||
json['name'],
|
||||
json['value'],
|
||||
json['type_name'],
|
||||
state: json['state'] ?? 0,
|
||||
);
|
||||
}
|
||||
|
||||
TriStateFilter(
|
||||
this.type,
|
||||
this.name,
|
||||
this.value,
|
||||
this.typeName, {
|
||||
this.state = 0,
|
||||
});
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'type': type,
|
||||
'name': name,
|
||||
'value': value,
|
||||
'state': state,
|
||||
'type_name': "TriState",
|
||||
};
|
||||
}
|
||||
|
||||
class GroupFilter {
|
||||
String? type;
|
||||
String name;
|
||||
List<dynamic> state;
|
||||
String? typeName;
|
||||
|
||||
GroupFilter(this.type, this.name, this.state, this.typeName);
|
||||
|
||||
factory GroupFilter.fromJson(Map<String, dynamic> json) {
|
||||
return GroupFilter(
|
||||
json['type'],
|
||||
json['name'],
|
||||
fromJsonFilterValuesToList(json['state']),
|
||||
json['type_name'],
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'type': type,
|
||||
'name': name,
|
||||
'state': filterValuesListToJson(state),
|
||||
'type_name': "GroupFilter",
|
||||
};
|
||||
}
|
||||
|
||||
class CheckBoxFilter {
|
||||
String? type;
|
||||
String name;
|
||||
String value;
|
||||
bool state;
|
||||
String? typeName;
|
||||
|
||||
CheckBoxFilter(
|
||||
this.type,
|
||||
this.name,
|
||||
this.value,
|
||||
this.typeName, {
|
||||
this.state = false,
|
||||
});
|
||||
|
||||
factory CheckBoxFilter.fromJson(Map<String, dynamic> json) {
|
||||
return CheckBoxFilter(
|
||||
json['type'],
|
||||
json['name'],
|
||||
json['value'],
|
||||
json['type_name'],
|
||||
state: json['state'] ?? false,
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'type': type,
|
||||
'name': name,
|
||||
'value': value,
|
||||
'state': state,
|
||||
'type_name': "CheckBox",
|
||||
};
|
||||
}
|
||||
|
||||
List<dynamic> fromJsonFilterValuesToList(List list) {
|
||||
return list
|
||||
.map((e) {
|
||||
final map = (e as Map).toMapStringDynamic!;
|
||||
|
||||
return switch (map['type_name']) {
|
||||
'TriState' => TriStateFilter.fromJson(map.toMapStringDynamic!),
|
||||
'CheckBox' => CheckBoxFilter.fromJson(map.toMapStringDynamic!),
|
||||
'SelectOption' => SelectFilterOption.fromJson(
|
||||
map.toMapStringDynamic!,
|
||||
),
|
||||
'SelectFilter' => SelectFilter.fromJson(map.toMapStringDynamic!),
|
||||
'SeparatorFilter' => SeparatorFilter.fromJson(
|
||||
map.toMapStringDynamic!,
|
||||
),
|
||||
'HeaderFilter' => HeaderFilter.fromJson(map.toMapStringDynamic!),
|
||||
'TextFilter' => TextFilter.fromJson(map.toMapStringDynamic!),
|
||||
'SortFilter' => SortFilter.fromJson(map.toMapStringDynamic!),
|
||||
'SortState' => SortState.fromJson(map.toMapStringDynamic!),
|
||||
'GroupFilter' => GroupFilter.fromJson(map.toMapStringDynamic!),
|
||||
_ => null,
|
||||
};
|
||||
})
|
||||
.where((filter) => filter != null)
|
||||
.toList();
|
||||
}
|
||||
|
||||
List<Map<String, dynamic>?> filterValuesListToJson(List<dynamic> values) {
|
||||
return values.map((e) {
|
||||
if (e is SelectFilter) {
|
||||
return e.toJson();
|
||||
} else if (e is SelectFilterOption) {
|
||||
return e.toJson();
|
||||
} else if (e is SeparatorFilter) {
|
||||
return e.toJson();
|
||||
} else if (e is HeaderFilter) {
|
||||
return e.toJson();
|
||||
} else if (e is TextFilter) {
|
||||
return e.toJson();
|
||||
} else if (e is SortFilter) {
|
||||
return e.toJson();
|
||||
} else if (e is SortState) {
|
||||
return e.toJson();
|
||||
} else if (e is TriStateFilter) {
|
||||
return e.toJson();
|
||||
} else if (e is GroupFilter) {
|
||||
return e.toJson();
|
||||
}
|
||||
return (e.toJson() as Map<String, dynamic>?);
|
||||
}).toList();
|
||||
}
|
||||
682
lib/core/services/extensions/mangayomi/Eval/dart/model/m_bridge.dart
Executable file
682
lib/core/services/extensions/mangayomi/Eval/dart/model/m_bridge.dart
Executable file
@@ -0,0 +1,682 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import '../../../../extension_bridge.dart';
|
||||
import 'video.dart';
|
||||
import '../../javascript/http.dart';
|
||||
import '../../../util/string_extensions.dart';
|
||||
import 'package:encrypt/encrypt.dart' as encrypt;
|
||||
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
|
||||
import 'package:html/dom.dart' hide Text;
|
||||
import 'package:intl/date_symbol_data_local.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:js_packer/js_packer.dart';
|
||||
import 'package:xpath_selector_html_parser/xpath_selector_html_parser.dart';
|
||||
|
||||
import '../../../anime_extractors/dood_extractor.dart';
|
||||
import '../../../anime_extractors/filemoon.dart';
|
||||
import '../../../anime_extractors/gogocdn_extractor.dart';
|
||||
import '../../../anime_extractors/mp4upload_extractor.dart';
|
||||
import '../../../anime_extractors/mytv_extractor.dart';
|
||||
import '../../../anime_extractors/okru_extractor.dart';
|
||||
import '../../../anime_extractors/quarkuc_extractor.dart';
|
||||
import '../../../anime_extractors/sendvid_extractor.dart';
|
||||
import '../../../anime_extractors/sibnet_extractor.dart';
|
||||
import '../../../anime_extractors/streamlare_extractor.dart';
|
||||
import '../../../anime_extractors/streamtape_extractor.dart';
|
||||
import '../../../anime_extractors/streamwish_extractor.dart';
|
||||
import '../../../anime_extractors/vidbom_extractor.dart';
|
||||
import '../../../anime_extractors/voe_extractor.dart';
|
||||
import '../../../anime_extractors/your_upload_extractor.dart';
|
||||
import '../../../cryptoaes/crypto_aes.dart';
|
||||
import '../../../cryptoaes/deobfuscator.dart';
|
||||
import '../../../cryptoaes/js_unpacker.dart';
|
||||
import '../../../util/reg_exp_matcher.dart';
|
||||
import 'document.dart';
|
||||
import 'm_manga.dart';
|
||||
|
||||
class WordSet {
|
||||
final List<String> words;
|
||||
|
||||
WordSet(this.words);
|
||||
|
||||
bool anyWordIn(String dateString) {
|
||||
return words.any(
|
||||
(word) => dateString.toLowerCase().contains(word.toLowerCase()),
|
||||
);
|
||||
}
|
||||
|
||||
bool startsWith(String dateString) {
|
||||
return words.any(
|
||||
(word) => dateString.toLowerCase().startsWith(word.toLowerCase()),
|
||||
);
|
||||
}
|
||||
|
||||
bool endsWith(String dateString) {
|
||||
return words.any(
|
||||
(word) => dateString.toLowerCase().endsWith(word.toLowerCase()),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class MBridge {
|
||||
static MDocument parsHtml(String html) {
|
||||
return MDocument(Document.html(html));
|
||||
}
|
||||
|
||||
///Create query by html string
|
||||
|
||||
static List<String>? xpath(String html, String xpath) {
|
||||
List<String> attrs = [];
|
||||
try {
|
||||
var htmlXPath = HtmlXPath.html(html);
|
||||
var query = htmlXPath.query(xpath);
|
||||
if (query.nodes.length > 1) {
|
||||
for (var element in query.attrs) {
|
||||
attrs.add(element!.trim().trimLeft().trimRight());
|
||||
}
|
||||
}
|
||||
//Return one attr
|
||||
else if (query.nodes.length == 1) {
|
||||
String attr = query.attr != null
|
||||
? query.attr!.trim().trimLeft().trimRight()
|
||||
: "";
|
||||
if (attr.isNotEmpty) {
|
||||
attrs = [attr];
|
||||
}
|
||||
}
|
||||
return attrs;
|
||||
} catch (_) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
///Convert serie status to int
|
||||
///[status] contains the current status of the serie
|
||||
///[statusList] contains a list of map of many static status
|
||||
static Status parseStatus(String status, List statusList) {
|
||||
for (var element in statusList) {
|
||||
Map statusMap = {};
|
||||
statusMap = element;
|
||||
for (var element in statusMap.entries) {
|
||||
if (element.key.toString().toLowerCase().contains(
|
||||
status.toLowerCase().trim().trimLeft().trimRight(),
|
||||
)) {
|
||||
return switch (element.value as int) {
|
||||
0 => Status.ongoing,
|
||||
1 => Status.completed,
|
||||
2 => Status.onHiatus,
|
||||
3 => Status.canceled,
|
||||
4 => Status.publishingFinished,
|
||||
_ => Status.unknown,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
return Status.unknown;
|
||||
}
|
||||
|
||||
///Unpack a JS code
|
||||
|
||||
static String? unpackJs(String code) {
|
||||
try {
|
||||
final jsPacker = JSPacker(code);
|
||||
return jsPacker.unpack() ?? "";
|
||||
} catch (_) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
///Unpack a JS code
|
||||
static String? unpackJsAndCombine(String code) {
|
||||
try {
|
||||
return JsUnpacker.unpackAndCombine(code) ?? "";
|
||||
} catch (_) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
///GetMapValue
|
||||
static String getMapValue(String source, String attr, bool encode) {
|
||||
try {
|
||||
var map = json.decode(source) as Map<String, dynamic>;
|
||||
if (!encode) {
|
||||
return map[attr] != null ? map[attr].toString() : "";
|
||||
}
|
||||
return map[attr] != null ? jsonEncode(map[attr]) : "";
|
||||
} catch (_) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
//Parse a list of dates to millisecondsSinceEpoch
|
||||
static List parseDates(
|
||||
List value,
|
||||
String dateFormat,
|
||||
String dateFormatLocale,
|
||||
) {
|
||||
List<dynamic> val = [];
|
||||
for (var element in value) {
|
||||
element = element.toString().trim();
|
||||
if (element.isNotEmpty) {
|
||||
val.add(element);
|
||||
}
|
||||
}
|
||||
bool error = false;
|
||||
List<dynamic> valD = [];
|
||||
for (var date in val) {
|
||||
String dateStr = "";
|
||||
if (error) {
|
||||
dateStr = DateTime.now().millisecondsSinceEpoch.toString();
|
||||
} else {
|
||||
dateStr = parseChapterDate(date, dateFormat, dateFormatLocale, (val) {
|
||||
dateFormat = val.$1;
|
||||
dateFormatLocale = val.$2;
|
||||
error = val.$3;
|
||||
});
|
||||
}
|
||||
valD.add(dateStr);
|
||||
}
|
||||
return valD;
|
||||
}
|
||||
|
||||
static List sortMapList(List list, String value, int type) {
|
||||
if (type == 0) {
|
||||
list.sort((a, b) => a[value].compareTo(b[value]));
|
||||
} else if (type == 1) {
|
||||
list.sort((a, b) => b[value].compareTo(a[value]));
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
//Utility to use RegExp
|
||||
static String regExp(
|
||||
String expression,
|
||||
String source,
|
||||
String replace,
|
||||
int type,
|
||||
int group,
|
||||
) {
|
||||
if (type == 0) {
|
||||
return expression.replaceAll(RegExp(source), replace);
|
||||
}
|
||||
return regCustomMatcher(expression, source, group);
|
||||
}
|
||||
|
||||
static Future<List<Video>> gogoCdnExtractor(String url) async {
|
||||
return await GogoCdnExtractor().videosFromUrl(url);
|
||||
}
|
||||
|
||||
static Future<List<Video>> doodExtractor(String url, String? quality) async {
|
||||
return await DoodExtractor().videosFromUrl(url, quality: quality);
|
||||
}
|
||||
|
||||
static Future<List<Video>> streamWishExtractor(
|
||||
String url,
|
||||
String prefix,
|
||||
) async {
|
||||
return await StreamWishExtractor().videosFromUrl(url, prefix);
|
||||
}
|
||||
|
||||
static Future<List<Video>> filemoonExtractor(
|
||||
String url,
|
||||
String prefix,
|
||||
String suffix,
|
||||
) async {
|
||||
return await FilemoonExtractor().videosFromUrl(url, prefix, suffix);
|
||||
}
|
||||
|
||||
static Map<String, String> decodeHeaders(String? headers) =>
|
||||
headers == null ? {} : (jsonDecode(headers) as Map).toMapStringString!;
|
||||
|
||||
static Future<List<Video>> mp4UploadExtractor(
|
||||
String url,
|
||||
String? headers,
|
||||
String prefix,
|
||||
String suffix,
|
||||
) async {
|
||||
return await Mp4uploadExtractor().videosFromUrl(
|
||||
url,
|
||||
decodeHeaders(headers),
|
||||
prefix: prefix,
|
||||
suffix: suffix,
|
||||
);
|
||||
}
|
||||
|
||||
static final Map<CloudDriveType, QuarkUcExtractor> _extractorCache = {};
|
||||
|
||||
static QuarkUcExtractor _getExtractor(String cookie, CloudDriveType type) {
|
||||
if (!_extractorCache.containsKey(type)) {
|
||||
QuarkUcExtractor extractor = QuarkUcExtractor();
|
||||
extractor.initCloudDrive(cookie, type);
|
||||
_extractorCache[type] = extractor;
|
||||
}
|
||||
return _extractorCache[type]!;
|
||||
}
|
||||
|
||||
static Future<List<Map<String, String>>> quarkFilesExtractor(
|
||||
List<String> url,
|
||||
String cookie,
|
||||
) async {
|
||||
var quark = _getExtractor(cookie, CloudDriveType.quark);
|
||||
return await quark.videoFilesFromUrl(url);
|
||||
}
|
||||
|
||||
static Future<List<Video>> quarkVideosExtractor(
|
||||
String url,
|
||||
String cookie,
|
||||
) async {
|
||||
var quark = _getExtractor(cookie, CloudDriveType.quark);
|
||||
return await quark.videosFromUrl(url);
|
||||
}
|
||||
|
||||
static Future<List<Map<String, String>>> ucFilesExtractor(
|
||||
List<String> url,
|
||||
String cookie,
|
||||
) async {
|
||||
var uc = _getExtractor(cookie, CloudDriveType.uc);
|
||||
return await uc.videoFilesFromUrl(url);
|
||||
}
|
||||
|
||||
static Future<List<Video>> ucVideosExtractor(
|
||||
String url,
|
||||
String cookie,
|
||||
) async {
|
||||
var uc = _getExtractor(cookie, CloudDriveType.uc);
|
||||
return await uc.videosFromUrl(url);
|
||||
}
|
||||
|
||||
static Future<List<Video>> streamTapeExtractor(
|
||||
String url,
|
||||
String? quality,
|
||||
) async {
|
||||
return await StreamTapeExtractor().videosFromUrl(
|
||||
url,
|
||||
quality: quality ?? "StreamTape",
|
||||
);
|
||||
}
|
||||
|
||||
//Utility to use substring
|
||||
static String substringAfter(String text, String pattern) {
|
||||
return text.substringAfter(pattern);
|
||||
}
|
||||
|
||||
//Utility to use substring
|
||||
static String substringBefore(String text, String pattern) {
|
||||
return text.substringBefore(pattern);
|
||||
}
|
||||
|
||||
//Utility to use substring
|
||||
static String substringBeforeLast(String text, String pattern) {
|
||||
return text.substringBeforeLast(pattern);
|
||||
}
|
||||
|
||||
static String substringAfterLast(String text, String pattern) {
|
||||
return text.split(pattern).last;
|
||||
}
|
||||
|
||||
//Parse a chapter date to millisecondsSinceEpoch
|
||||
static String parseChapterDate(
|
||||
String date,
|
||||
String dateFormat,
|
||||
String dateFormatLocale,
|
||||
Function((String, String, bool)) newLocale,
|
||||
) {
|
||||
int parseRelativeDate(String date) {
|
||||
final number = int.tryParse(RegExp(r"(\d+)").firstMatch(date)!.group(0)!);
|
||||
if (number == null) return 0;
|
||||
final cal = DateTime.now();
|
||||
|
||||
if (WordSet([
|
||||
"hari",
|
||||
"gün",
|
||||
"jour",
|
||||
"día",
|
||||
"dia",
|
||||
"day",
|
||||
"วัน",
|
||||
"ngày",
|
||||
"giorni",
|
||||
"أيام",
|
||||
"天",
|
||||
]).anyWordIn(date)) {
|
||||
return cal.subtract(Duration(days: number)).millisecondsSinceEpoch;
|
||||
} else if (WordSet([
|
||||
"jam",
|
||||
"saat",
|
||||
"heure",
|
||||
"hora",
|
||||
"hour",
|
||||
"ชั่วโมง",
|
||||
"giờ",
|
||||
"ore",
|
||||
"ساعة",
|
||||
"小时",
|
||||
]).anyWordIn(date)) {
|
||||
return cal.subtract(Duration(hours: number)).millisecondsSinceEpoch;
|
||||
} else if (WordSet([
|
||||
"menit",
|
||||
"dakika",
|
||||
"min",
|
||||
"minute",
|
||||
"minuto",
|
||||
"นาที",
|
||||
"دقائق",
|
||||
]).anyWordIn(date)) {
|
||||
return cal.subtract(Duration(minutes: number)).millisecondsSinceEpoch;
|
||||
} else if (WordSet([
|
||||
"detik",
|
||||
"segundo",
|
||||
"second",
|
||||
"วินาที",
|
||||
"sec",
|
||||
]).anyWordIn(date)) {
|
||||
return cal.subtract(Duration(seconds: number)).millisecondsSinceEpoch;
|
||||
} else if (WordSet(["week", "semana"]).anyWordIn(date)) {
|
||||
return cal.subtract(Duration(days: number * 7)).millisecondsSinceEpoch;
|
||||
} else if (WordSet(["month", "mes"]).anyWordIn(date)) {
|
||||
return cal.subtract(Duration(days: number * 30)).millisecondsSinceEpoch;
|
||||
} else if (WordSet(["year", "año"]).anyWordIn(date)) {
|
||||
return cal
|
||||
.subtract(Duration(days: number * 365))
|
||||
.millisecondsSinceEpoch;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
if (WordSet(["yesterday", "يوم واحد"]).startsWith(date)) {
|
||||
DateTime cal = DateTime.now().subtract(const Duration(days: 1));
|
||||
cal = DateTime(cal.year, cal.month, cal.day);
|
||||
return cal.millisecondsSinceEpoch.toString();
|
||||
} else if (WordSet(["today"]).startsWith(date)) {
|
||||
DateTime cal = DateTime.now();
|
||||
cal = DateTime(cal.year, cal.month, cal.day);
|
||||
return cal.millisecondsSinceEpoch.toString();
|
||||
} else if (WordSet(["يومين"]).startsWith(date)) {
|
||||
DateTime cal = DateTime.now().subtract(const Duration(days: 2));
|
||||
cal = DateTime(cal.year, cal.month, cal.day);
|
||||
return cal.millisecondsSinceEpoch.toString();
|
||||
} else if (WordSet(["ago", "atrás", "önce", "قبل"]).endsWith(date)) {
|
||||
return parseRelativeDate(date).toString();
|
||||
} else if (WordSet(["hace"]).startsWith(date)) {
|
||||
return parseRelativeDate(date).toString();
|
||||
} else if (date.contains(RegExp(r"\d(st|nd|rd|th)"))) {
|
||||
final cleanedDate = date
|
||||
.split(" ")
|
||||
.map(
|
||||
(it) => it.contains(RegExp(r"\d\D\D"))
|
||||
? it.replaceAll(RegExp(r"\D"), "")
|
||||
: it,
|
||||
)
|
||||
.join(" ");
|
||||
return DateFormat(
|
||||
dateFormat,
|
||||
dateFormatLocale,
|
||||
).parse(cleanedDate).millisecondsSinceEpoch.toString();
|
||||
} else {
|
||||
return DateFormat(
|
||||
dateFormat,
|
||||
dateFormatLocale,
|
||||
).parse(date).millisecondsSinceEpoch.toString();
|
||||
}
|
||||
} catch (e) {
|
||||
final supportedLocales = DateFormat.allLocalesWithSymbols();
|
||||
|
||||
for (var locale in supportedLocales) {
|
||||
for (var dateFormat in _dateFormats) {
|
||||
newLocale((dateFormat, locale, false));
|
||||
try {
|
||||
initializeDateFormatting(locale);
|
||||
if (WordSet(["yesterday", "يوم واحد"]).startsWith(date)) {
|
||||
DateTime cal = DateTime.now().subtract(const Duration(days: 1));
|
||||
cal = DateTime(cal.year, cal.month, cal.day);
|
||||
return cal.millisecondsSinceEpoch.toString();
|
||||
} else if (WordSet(["today"]).startsWith(date)) {
|
||||
DateTime cal = DateTime.now();
|
||||
cal = DateTime(cal.year, cal.month, cal.day);
|
||||
return cal.millisecondsSinceEpoch.toString();
|
||||
} else if (WordSet(["يومين"]).startsWith(date)) {
|
||||
DateTime cal = DateTime.now().subtract(const Duration(days: 2));
|
||||
cal = DateTime(cal.year, cal.month, cal.day);
|
||||
return cal.millisecondsSinceEpoch.toString();
|
||||
} else if (WordSet([
|
||||
"ago",
|
||||
"atrás",
|
||||
"önce",
|
||||
"قبل",
|
||||
]).endsWith(date)) {
|
||||
return parseRelativeDate(date).toString();
|
||||
} else if (WordSet(["hace"]).startsWith(date)) {
|
||||
return parseRelativeDate(date).toString();
|
||||
} else if (date.contains(RegExp(r"\d(st|nd|rd|th)"))) {
|
||||
final cleanedDate = date
|
||||
.split(" ")
|
||||
.map(
|
||||
(it) => it.contains(RegExp(r"\d\D\D"))
|
||||
? it.replaceAll(RegExp(r"\D"), "")
|
||||
: it,
|
||||
)
|
||||
.join(" ");
|
||||
return DateFormat(
|
||||
dateFormat,
|
||||
locale,
|
||||
).parse(cleanedDate).millisecondsSinceEpoch.toString();
|
||||
} else {
|
||||
return DateFormat(
|
||||
dateFormat,
|
||||
locale,
|
||||
).parse(date).millisecondsSinceEpoch.toString();
|
||||
}
|
||||
} catch (_) {}
|
||||
}
|
||||
}
|
||||
newLocale((dateFormat, dateFormatLocale, true));
|
||||
return DateTime.now().millisecondsSinceEpoch.toString();
|
||||
}
|
||||
}
|
||||
|
||||
static String deobfuscateJsPassword(String inputString) {
|
||||
return Deobfuscator.deobfuscateJsPassword(inputString);
|
||||
}
|
||||
|
||||
static Future<List<Video>> sibnetExtractor(String url, String prefix) async {
|
||||
return await SibnetExtractor().videosFromUrl(url, prefix: prefix);
|
||||
}
|
||||
|
||||
static Future<List<Video>> sendVidExtractor(
|
||||
String url,
|
||||
String? headers,
|
||||
String prefix,
|
||||
) async {
|
||||
return await SendvidExtractor(
|
||||
decodeHeaders(headers),
|
||||
).videosFromUrl(url, prefix: prefix);
|
||||
}
|
||||
|
||||
static Future<List<Video>> myTvExtractor(String url) async {
|
||||
return await MytvExtractor().videosFromUrl(url);
|
||||
}
|
||||
|
||||
static Future<List<Video>> okruExtractor(String url) async {
|
||||
return await OkruExtractor().videosFromUrl(url);
|
||||
}
|
||||
|
||||
static Future<List<Video>> yourUploadExtractor(
|
||||
String url,
|
||||
String? headers,
|
||||
String? name,
|
||||
String prefix,
|
||||
) async {
|
||||
return await YourUploadExtractor().videosFromUrl(
|
||||
url,
|
||||
decodeHeaders(headers),
|
||||
prefix: prefix,
|
||||
name: name ?? "YourUpload",
|
||||
);
|
||||
}
|
||||
|
||||
static Future<List<Video>> voeExtractor(String url, String? quality) async {
|
||||
return await VoeExtractor().videosFromUrl(url, quality);
|
||||
}
|
||||
|
||||
static Future<List<Video>> vidBomExtractor(String url) async {
|
||||
return await VidBomExtractor().videosFromUrl(url);
|
||||
}
|
||||
|
||||
static Future<List<Video>> streamlareExtractor(
|
||||
String url,
|
||||
String prefix,
|
||||
String suffix,
|
||||
) async {
|
||||
return await StreamlareExtractor().videosFromUrl(
|
||||
url,
|
||||
prefix: prefix,
|
||||
suffix: suffix,
|
||||
);
|
||||
}
|
||||
|
||||
static String encryptAESCryptoJS(String plainText, String passphrase) {
|
||||
return CryptoAES.encryptAESCryptoJS(plainText, passphrase);
|
||||
}
|
||||
|
||||
static String decryptAESCryptoJS(String encrypted, String passphrase) {
|
||||
return CryptoAES.decryptAESCryptoJS(encrypted, passphrase);
|
||||
}
|
||||
|
||||
static Video toVideo(
|
||||
String url,
|
||||
String quality,
|
||||
String originalUrl,
|
||||
String? headers,
|
||||
List<Track>? subtitles,
|
||||
List<Track>? audios,
|
||||
) {
|
||||
return Video(
|
||||
url,
|
||||
quality,
|
||||
originalUrl,
|
||||
headers: decodeHeaders(headers),
|
||||
subtitles: subtitles ?? [],
|
||||
audios: audios ?? [],
|
||||
);
|
||||
}
|
||||
|
||||
static String cryptoHandler(
|
||||
String text,
|
||||
String iv,
|
||||
String secretKeyString,
|
||||
bool encrypt,
|
||||
) {
|
||||
try {
|
||||
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;
|
||||
}
|
||||
} catch (_) {
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
static Future<String> evaluateJavascriptViaWebview(
|
||||
String url,
|
||||
Map<String, String> headers,
|
||||
List<String> scripts, {
|
||||
int time = 30,
|
||||
}) async {
|
||||
int t = 0;
|
||||
bool timeOut = false;
|
||||
bool isOk = false;
|
||||
String response = "";
|
||||
HeadlessInAppWebView? headlessWebView;
|
||||
headlessWebView = HeadlessInAppWebView(
|
||||
webViewEnvironment: webViewEnvironment,
|
||||
onWebViewCreated: (controller) {
|
||||
controller.addJavaScriptHandler(
|
||||
handlerName: 'setResponse',
|
||||
callback: (args) {
|
||||
response = args[0] as String;
|
||||
isOk = true;
|
||||
},
|
||||
);
|
||||
},
|
||||
initialUrlRequest: URLRequest(url: WebUri(url), headers: headers),
|
||||
onLoadStop: (controller, url) async {
|
||||
for (var script in scripts) {
|
||||
await controller.platform.evaluateJavascript(source: script);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
headlessWebView.run();
|
||||
|
||||
await Future.doWhile(() async {
|
||||
timeOut = time == t;
|
||||
if (timeOut || isOk) {
|
||||
return false;
|
||||
}
|
||||
await Future.delayed(const Duration(seconds: 1));
|
||||
t++;
|
||||
return true;
|
||||
});
|
||||
try {
|
||||
headlessWebView.dispose();
|
||||
} catch (_) {}
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
final List<String> _dateFormats = [
|
||||
'dd/MM/yyyy',
|
||||
'MM/dd/yyyy',
|
||||
'yyyy/MM/dd',
|
||||
'dd-MM-yyyy',
|
||||
'MM-dd-yyyy',
|
||||
'yyyy-MM-dd',
|
||||
'dd.MM.yyyy',
|
||||
'MM.dd.yyyy',
|
||||
'yyyy.MM.dd',
|
||||
'dd MMMM yyyy',
|
||||
'MMMM dd, yyyy',
|
||||
'yyyy MMMM dd',
|
||||
'dd MMM yyyy',
|
||||
'MMM dd yyyy',
|
||||
'yyyy MMM dd',
|
||||
'dd MMMM, yyyy',
|
||||
'yyyy, MMMM dd',
|
||||
'MMMM dd yyyy',
|
||||
'MMM dd, yyyy',
|
||||
'dd LLLL yyyy',
|
||||
'LLLL dd, yyyy',
|
||||
'yyyy LLLL dd',
|
||||
'LLLL dd yyyy',
|
||||
"MMMMM dd, yyyy",
|
||||
"MMM d, yyy",
|
||||
"MMM d, yyyy",
|
||||
"dd/mm/yyyy",
|
||||
"d MMMM yyyy",
|
||||
"dd 'de' MMMM 'de' yyyy",
|
||||
"d MMMM'،' yyyy",
|
||||
"yyyy'年'M'月'd",
|
||||
"d MMMM, yyyy",
|
||||
"dd 'de' MMMMM 'de' yyyy",
|
||||
"dd MMMMM, yyyy",
|
||||
"MMMM d, yyyy",
|
||||
"MMM dd,yyyy",
|
||||
];
|
||||
|
||||
(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);
|
||||
}
|
||||
27
lib/core/services/extensions/mangayomi/Eval/dart/model/m_chapter.dart
Executable file
27
lib/core/services/extensions/mangayomi/Eval/dart/model/m_chapter.dart
Executable file
@@ -0,0 +1,27 @@
|
||||
class MChapter {
|
||||
String? name;
|
||||
|
||||
String? url;
|
||||
|
||||
String? dateUpload;
|
||||
|
||||
String? scanlator;
|
||||
|
||||
MChapter({this.name, this.url, this.dateUpload, this.scanlator});
|
||||
|
||||
factory MChapter.fromJson(Map<String, dynamic> json) {
|
||||
return MChapter(
|
||||
name: json['name'],
|
||||
url: json['url'],
|
||||
dateUpload: json['dateUpload'],
|
||||
scanlator: json['scanlator'],
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'name': name,
|
||||
'url': url,
|
||||
'dateUpload': dateUpload,
|
||||
'scanlator': scanlator,
|
||||
};
|
||||
}
|
||||
83
lib/core/services/extensions/mangayomi/Eval/dart/model/m_manga.dart
Executable file
83
lib/core/services/extensions/mangayomi/Eval/dart/model/m_manga.dart
Executable file
@@ -0,0 +1,83 @@
|
||||
import '../../../util/string_extensions.dart';
|
||||
|
||||
import 'm_chapter.dart';
|
||||
|
||||
class MManga {
|
||||
String? name;
|
||||
|
||||
String? link;
|
||||
|
||||
String? imageUrl;
|
||||
|
||||
String? description;
|
||||
|
||||
String? author;
|
||||
|
||||
String? artist;
|
||||
|
||||
Status? status;
|
||||
|
||||
List<String>? genre;
|
||||
|
||||
List<MChapter>? chapters;
|
||||
|
||||
MManga({
|
||||
this.author,
|
||||
this.artist,
|
||||
this.genre,
|
||||
this.imageUrl,
|
||||
this.link,
|
||||
this.name,
|
||||
this.status = Status.unknown,
|
||||
this.description,
|
||||
this.chapters,
|
||||
});
|
||||
|
||||
factory MManga.fromJson(Map<String, dynamic> json) {
|
||||
return MManga(
|
||||
name: json['name'],
|
||||
link: json['link'],
|
||||
imageUrl: json['imageUrl'],
|
||||
description: json['description'],
|
||||
author: json['author'],
|
||||
artist: json['artist'],
|
||||
status: switch (json['status'] as int?) {
|
||||
0 => Status.ongoing,
|
||||
1 => Status.completed,
|
||||
2 => Status.onHiatus,
|
||||
3 => Status.canceled,
|
||||
4 => Status.publishingFinished,
|
||||
_ => Status.unknown,
|
||||
},
|
||||
genre: (json['genre'] as List?)?.map((e) => e.toString()).toList() ?? [],
|
||||
chapters: json['chapters'] != null
|
||||
? (json['chapters'] as List).map((e) => MChapter.fromJson(e)).toList()
|
||||
: json['episodes'] != null
|
||||
? (json['episodes'] as List).map((e) => MChapter.fromJson(e)).toList()
|
||||
: [],
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'name': name,
|
||||
'link': link,
|
||||
'imageUrl': imageUrl,
|
||||
'description': description,
|
||||
'author': author,
|
||||
'artist': artist,
|
||||
'status': status.toString().substringAfter("."),
|
||||
'genre': genre,
|
||||
'chapters': chapters!.map((e) => e.toJson()).toList(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
enum Status {
|
||||
ongoing,
|
||||
completed,
|
||||
canceled,
|
||||
unknown,
|
||||
onHiatus,
|
||||
publishingFinished,
|
||||
}
|
||||
22
lib/core/services/extensions/mangayomi/Eval/dart/model/m_pages.dart
Executable file
22
lib/core/services/extensions/mangayomi/Eval/dart/model/m_pages.dart
Executable file
@@ -0,0 +1,22 @@
|
||||
import 'm_manga.dart';
|
||||
|
||||
class MPages {
|
||||
List<MManga> list;
|
||||
bool hasNextPage;
|
||||
|
||||
MPages({required this.list, this.hasNextPage = false});
|
||||
|
||||
factory MPages.fromJson(Map<String, dynamic> json) {
|
||||
return MPages(
|
||||
list: json['list'] != null
|
||||
? (json['list'] as List).map((e) => MManga.fromJson(e)).toList()
|
||||
: [],
|
||||
hasNextPage: json['hasNextPage'],
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'list': list.map((v) => v.toJson()).toList(),
|
||||
'hasNextPage': hasNextPage,
|
||||
};
|
||||
}
|
||||
35
lib/core/services/extensions/mangayomi/Eval/dart/model/m_provider.dart
Executable file
35
lib/core/services/extensions/mangayomi/Eval/dart/model/m_provider.dart
Executable file
@@ -0,0 +1,35 @@
|
||||
import '../model/video.dart';
|
||||
|
||||
import 'filter.dart';
|
||||
import 'm_manga.dart';
|
||||
import 'm_pages.dart';
|
||||
|
||||
abstract class MProvider {
|
||||
MProvider();
|
||||
|
||||
bool get supportsLatest => true;
|
||||
|
||||
String? get baseUrl;
|
||||
|
||||
Map<String, String> get headers;
|
||||
|
||||
Future<MPages> getLatestUpdates(int page);
|
||||
|
||||
Future<MPages> getPopular(int page);
|
||||
|
||||
Future<MPages> search(String query, int page, FilterList filterList);
|
||||
|
||||
Future<MManga> getDetail(String url);
|
||||
|
||||
Future<List<dynamic>> getPageList(String url);
|
||||
|
||||
Future<List<Video>> getVideoList(String url);
|
||||
|
||||
Future<String> getHtmlContent(String name, String url);
|
||||
|
||||
Future<String> cleanHtmlContent(String html);
|
||||
|
||||
List<dynamic> getFilterList();
|
||||
|
||||
List<dynamic> getSourcePreferences();
|
||||
}
|
||||
51
lib/core/services/extensions/mangayomi/Eval/dart/model/m_source.dart
Executable file
51
lib/core/services/extensions/mangayomi/Eval/dart/model/m_source.dart
Executable file
@@ -0,0 +1,51 @@
|
||||
class MSource {
|
||||
int? id;
|
||||
|
||||
String? name;
|
||||
|
||||
String? baseUrl;
|
||||
|
||||
String? lang;
|
||||
|
||||
bool? isFullData;
|
||||
|
||||
bool? hasCloudflare;
|
||||
|
||||
String? dateFormat;
|
||||
|
||||
String? dateFormatLocale;
|
||||
|
||||
String? apiUrl;
|
||||
|
||||
String? additionalParams;
|
||||
|
||||
String? notes;
|
||||
|
||||
MSource({
|
||||
this.id,
|
||||
this.name,
|
||||
this.baseUrl,
|
||||
this.lang,
|
||||
this.isFullData,
|
||||
this.hasCloudflare,
|
||||
this.dateFormat,
|
||||
this.dateFormatLocale,
|
||||
this.apiUrl,
|
||||
this.additionalParams,
|
||||
this.notes,
|
||||
});
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'apiUrl': apiUrl,
|
||||
'baseUrl': baseUrl,
|
||||
'dateFormat': dateFormat,
|
||||
'dateFormatLocale': dateFormatLocale,
|
||||
'hasCloudflare': hasCloudflare,
|
||||
'id': id,
|
||||
'isFullData': isFullData,
|
||||
'lang': lang,
|
||||
'name': name,
|
||||
'additionalParams': additionalParams,
|
||||
'notes': notes,
|
||||
};
|
||||
}
|
||||
23
lib/core/services/extensions/mangayomi/Eval/dart/model/m_video.dart
Executable file
23
lib/core/services/extensions/mangayomi/Eval/dart/model/m_video.dart
Executable file
@@ -0,0 +1,23 @@
|
||||
class MVideo {
|
||||
String url;
|
||||
String quality;
|
||||
String originalUrl;
|
||||
Map<String, String>? headers;
|
||||
List<MTrack>? subtitles;
|
||||
List<MTrack>? audios;
|
||||
|
||||
MVideo(this.url,
|
||||
this.quality,
|
||||
this.originalUrl, {
|
||||
this.headers,
|
||||
this.subtitles,
|
||||
this.audios,
|
||||
});
|
||||
}
|
||||
|
||||
class MTrack {
|
||||
String? file;
|
||||
String? label;
|
||||
|
||||
MTrack({this.file, this.label});
|
||||
}
|
||||
17
lib/core/services/extensions/mangayomi/Eval/dart/model/page.dart
Executable file
17
lib/core/services/extensions/mangayomi/Eval/dart/model/page.dart
Executable file
@@ -0,0 +1,17 @@
|
||||
import '../..//javascript/http.dart';
|
||||
|
||||
class PageUrl {
|
||||
String url;
|
||||
Map<String, String>? headers;
|
||||
|
||||
PageUrl(this.url, {this.headers});
|
||||
|
||||
factory PageUrl.fromJson(Map<String, dynamic> json) {
|
||||
return PageUrl(
|
||||
json['url'].toString().trim(),
|
||||
headers: (json['headers'] as Map?)?.toMapStringString,
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() => {'url': url, 'headers': headers};
|
||||
}
|
||||
209
lib/core/services/extensions/mangayomi/Eval/dart/model/source_preference.dart
Executable file
209
lib/core/services/extensions/mangayomi/Eval/dart/model/source_preference.dart
Executable file
@@ -0,0 +1,209 @@
|
||||
import 'package:hive_ce/hive.dart';
|
||||
|
||||
part 'source_preference.g.dart';
|
||||
|
||||
@collection
|
||||
@Name("SourcePreferences")
|
||||
class SourcePreference {
|
||||
Id? id;
|
||||
int? sourceId;
|
||||
String? key;
|
||||
CheckBoxPreference? checkBoxPreference;
|
||||
SwitchPreferenceCompat? switchPreferenceCompat;
|
||||
ListPreference? listPreference;
|
||||
MultiSelectListPreference? multiSelectListPreference;
|
||||
EditTextPreference? editTextPreference;
|
||||
|
||||
SourcePreference(
|
||||
{this.id = Isar.autoIncrement,
|
||||
this.sourceId,
|
||||
this.key,
|
||||
this.checkBoxPreference,
|
||||
this.switchPreferenceCompat,
|
||||
this.listPreference,
|
||||
this.multiSelectListPreference,
|
||||
this.editTextPreference});
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'id': id,
|
||||
'sourceId': sourceId,
|
||||
'key': key,
|
||||
if (checkBoxPreference != null)
|
||||
'checkBoxPreference': checkBoxPreference!.toJson(),
|
||||
if (switchPreferenceCompat != null)
|
||||
'switchPreferenceCompat': switchPreferenceCompat!.toJson(),
|
||||
if (listPreference != null) 'listPreference': listPreference!.toJson(),
|
||||
if (multiSelectListPreference != null)
|
||||
'multiSelectListPreference': multiSelectListPreference!.toJson(),
|
||||
if (editTextPreference != null)
|
||||
'editTextPreference': editTextPreference!.toJson()
|
||||
};
|
||||
|
||||
factory SourcePreference.fromJson(Map<String, dynamic> json) {
|
||||
return SourcePreference(
|
||||
id: json['id'] ?? Isar.autoIncrement,
|
||||
sourceId: json['sourceId'],
|
||||
key: json['key'],
|
||||
checkBoxPreference: json['checkBoxPreference'] != null
|
||||
? CheckBoxPreference.fromJson(json['checkBoxPreference'])
|
||||
: null,
|
||||
switchPreferenceCompat: json['switchPreferenceCompat'] != null
|
||||
? SwitchPreferenceCompat.fromJson(json['switchPreferenceCompat'])
|
||||
: null,
|
||||
listPreference: json['listPreference'] != null
|
||||
? ListPreference.fromJson(json['listPreference'])
|
||||
: null,
|
||||
multiSelectListPreference: json['multiSelectListPreference'] != null
|
||||
? MultiSelectListPreference.fromJson(
|
||||
json['multiSelectListPreference'])
|
||||
: null,
|
||||
editTextPreference: json['editTextPreference'] != null
|
||||
? EditTextPreference.fromJson(json['editTextPreference'])
|
||||
: null);
|
||||
}
|
||||
}
|
||||
|
||||
@embedded
|
||||
class CheckBoxPreference {
|
||||
String? title;
|
||||
String? summary;
|
||||
bool? value;
|
||||
|
||||
CheckBoxPreference({this.title, this.summary, this.value});
|
||||
|
||||
Map<String, dynamic> toJson() =>
|
||||
{'title': title, 'summary': summary, 'value': value};
|
||||
|
||||
factory CheckBoxPreference.fromJson(Map<String, dynamic> json) {
|
||||
return CheckBoxPreference(
|
||||
title: json['title'], summary: json['summary'], value: json['value']);
|
||||
}
|
||||
}
|
||||
|
||||
@embedded
|
||||
class SwitchPreferenceCompat {
|
||||
String? title;
|
||||
String? summary;
|
||||
bool? value;
|
||||
|
||||
SwitchPreferenceCompat({this.title, this.summary, this.value});
|
||||
|
||||
Map<String, dynamic> toJson() =>
|
||||
{'title': title, 'summary': summary, 'value': value};
|
||||
|
||||
factory SwitchPreferenceCompat.fromJson(Map<String, dynamic> json) {
|
||||
return SwitchPreferenceCompat(
|
||||
title: json['title'], summary: json['summary'], value: json['value']);
|
||||
}
|
||||
}
|
||||
|
||||
@embedded
|
||||
class ListPreference {
|
||||
String? title;
|
||||
String? summary;
|
||||
int? valueIndex;
|
||||
List<String>? entries;
|
||||
List<String>? entryValues;
|
||||
|
||||
ListPreference(
|
||||
{this.title,
|
||||
this.summary,
|
||||
this.valueIndex,
|
||||
this.entries,
|
||||
this.entryValues});
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'title': title,
|
||||
'summary': summary,
|
||||
'valueIndex': valueIndex,
|
||||
'entries': entries,
|
||||
'entryValues': entryValues
|
||||
};
|
||||
|
||||
factory ListPreference.fromJson(Map<String, dynamic> json) {
|
||||
return ListPreference(
|
||||
title: json['title'],
|
||||
summary: json['summary'],
|
||||
valueIndex: json['valueIndex'],
|
||||
entries: json['entries']?.cast<String>(),
|
||||
entryValues: json['entryValues']?.cast<String>());
|
||||
}
|
||||
}
|
||||
|
||||
@embedded
|
||||
class MultiSelectListPreference {
|
||||
String? title;
|
||||
String? summary;
|
||||
List<String>? entries;
|
||||
List<String>? entryValues;
|
||||
List<String>? values;
|
||||
|
||||
MultiSelectListPreference(
|
||||
{this.title, this.summary, this.entries, this.entryValues, this.values});
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'title': title,
|
||||
'summary': summary,
|
||||
'entries': entries?.cast<String>(),
|
||||
'entryValues': entryValues?.cast<String>(),
|
||||
'values': values?.cast<String>()
|
||||
};
|
||||
|
||||
factory MultiSelectListPreference.fromJson(Map<String, dynamic> json) {
|
||||
return MultiSelectListPreference(
|
||||
title: json['title'],
|
||||
summary: json['summary'],
|
||||
entries: json['entries']?.cast<String>(),
|
||||
entryValues: json['entryValues']?.cast<String>(),
|
||||
values: json['values']?.cast<String>());
|
||||
}
|
||||
}
|
||||
|
||||
@embedded
|
||||
class EditTextPreference {
|
||||
String? title;
|
||||
String? summary;
|
||||
String? value;
|
||||
String? dialogTitle;
|
||||
String? dialogMessage;
|
||||
String? text;
|
||||
|
||||
EditTextPreference(
|
||||
{this.title,
|
||||
this.summary,
|
||||
this.value,
|
||||
this.dialogTitle,
|
||||
this.dialogMessage,
|
||||
this.text});
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'title': title,
|
||||
'summary': summary,
|
||||
'value': value,
|
||||
'dialogTitle': dialogTitle,
|
||||
'dialogMessage': dialogMessage,
|
||||
'text': text
|
||||
};
|
||||
|
||||
factory EditTextPreference.fromJson(Map<String, dynamic> json) {
|
||||
return EditTextPreference(
|
||||
title: json['title'],
|
||||
summary: json['summary'],
|
||||
value: json['value'],
|
||||
dialogTitle: json['dialogTitle'],
|
||||
dialogMessage: json['dialogMessage'],
|
||||
text: json['text']);
|
||||
}
|
||||
}
|
||||
|
||||
@collection
|
||||
@Name("SourcePreferenceStringValue")
|
||||
class SourcePreferenceStringValue {
|
||||
Id id;
|
||||
int? sourceId;
|
||||
String? key;
|
||||
String? value;
|
||||
|
||||
SourcePreferenceStringValue(
|
||||
{this.id = Isar.autoIncrement, this.sourceId, this.key, this.value});
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
57
lib/core/services/extensions/mangayomi/Eval/dart/model/video.dart
Executable file
57
lib/core/services/extensions/mangayomi/Eval/dart/model/video.dart
Executable file
@@ -0,0 +1,57 @@
|
||||
import '../../javascript/http.dart';
|
||||
|
||||
class Video {
|
||||
String url;
|
||||
String quality;
|
||||
String originalUrl;
|
||||
Map<String, String>? headers;
|
||||
List<Track>? subtitles;
|
||||
List<Track>? audios;
|
||||
|
||||
Video(
|
||||
this.url,
|
||||
this.quality,
|
||||
this.originalUrl, {
|
||||
this.headers,
|
||||
this.subtitles,
|
||||
this.audios,
|
||||
});
|
||||
|
||||
factory Video.fromJson(Map<String, dynamic> json) {
|
||||
return Video(
|
||||
json['url'].toString().trim(),
|
||||
json['quality'].toString().trim(),
|
||||
json['originalUrl'].toString().trim(),
|
||||
headers: (json['headers'] as Map?)?.toMapStringString,
|
||||
subtitles: json['subtitles'] != null
|
||||
? (json['subtitles'] as List).map((e) => Track.fromJson(e)).toList()
|
||||
: [],
|
||||
audios: json['audios'] != null
|
||||
? (json['audios'] as List).map((e) => Track.fromJson(e)).toList()
|
||||
: [],
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'url': url,
|
||||
'quality': quality,
|
||||
'originalUrl': originalUrl,
|
||||
'headers': headers,
|
||||
'subtitles': subtitles?.map((e) => e.toJson()).toList(),
|
||||
'audios': audios?.map((e) => e.toJson()).toList(),
|
||||
};
|
||||
}
|
||||
|
||||
class Track {
|
||||
String? file;
|
||||
String? label;
|
||||
|
||||
Track({this.file, this.label});
|
||||
|
||||
Track.fromJson(Map<String, dynamic> json) {
|
||||
file = json['file']?.toString().trim();
|
||||
label = json['label']?.toString().trim();
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() => {'file': file, 'label': label};
|
||||
}
|
||||
187
lib/core/services/extensions/mangayomi/Eval/dart/service.dart
Executable file
187
lib/core/services/extensions/mangayomi/Eval/dart/service.dart
Executable file
@@ -0,0 +1,187 @@
|
||||
import 'package:d4rt/d4rt.dart';
|
||||
import '../javascript/http.dart';
|
||||
|
||||
import '../../Models/Source.dart';
|
||||
import '../../interface.dart';
|
||||
import 'bridge/registrer.dart';
|
||||
import 'model/filter.dart';
|
||||
import 'model/m_manga.dart';
|
||||
import 'model/m_pages.dart';
|
||||
import 'model/page.dart';
|
||||
import 'model/source_preference.dart';
|
||||
import 'model/video.dart';
|
||||
|
||||
class DartExtensionService implements ExtensionService {
|
||||
@override
|
||||
late MSource source;
|
||||
|
||||
DartExtensionService(this.source);
|
||||
|
||||
D4rt _executeLib() {
|
||||
final interpreter = D4rt();
|
||||
RegistrerBridge.registerBridge(interpreter);
|
||||
|
||||
interpreter.execute(source: source.sourceCode!, args: source.toMSource());
|
||||
return interpreter;
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, String> getHeaders() {
|
||||
Map<String, String> headers = {};
|
||||
try {
|
||||
headers = _executeLib().invoke('headers', []) as Map<String, String>;
|
||||
} catch (_) {
|
||||
try {
|
||||
headers =
|
||||
_executeLib().invoke('getHeader', [source.baseUrl!])
|
||||
as Map<String, String>;
|
||||
} catch (_) {}
|
||||
}
|
||||
return headers;
|
||||
}
|
||||
|
||||
@override
|
||||
String get sourceBaseUrl {
|
||||
String? baseUrl;
|
||||
try {
|
||||
final interpreter = _executeLib();
|
||||
baseUrl = interpreter.invoke('baseUrl', []) as String?;
|
||||
} catch (_) {}
|
||||
|
||||
return baseUrl == null || baseUrl.isEmpty ? source.baseUrl! : baseUrl;
|
||||
}
|
||||
|
||||
@override
|
||||
bool get supportsLatest {
|
||||
bool? supportsLatest;
|
||||
try {
|
||||
final interpreter = _executeLib();
|
||||
supportsLatest = interpreter.invoke('supportsLatest', []) as bool?;
|
||||
} catch (e) {
|
||||
supportsLatest = true;
|
||||
}
|
||||
return supportsLatest ?? true;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MPages> getPopular(int page) async {
|
||||
try {
|
||||
final interpreter = _executeLib();
|
||||
final result = await interpreter.invoke('getPopular', [page]);
|
||||
return result as MPages;
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MPages> getLatestUpdates(int page) async {
|
||||
final interpreter = _executeLib();
|
||||
final result = await interpreter.invoke('getLatestUpdates', [page]);
|
||||
return result as MPages;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MPages> search(String query, int page, List<dynamic> filters) async {
|
||||
final interpreter = _executeLib();
|
||||
final result = await interpreter.invoke('search', [
|
||||
query,
|
||||
page,
|
||||
FilterList(filters),
|
||||
]);
|
||||
return result as MPages;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MManga> getDetail(String url) async {
|
||||
final interpreter = _executeLib();
|
||||
final result = await interpreter.invoke('getDetail', [url]);
|
||||
return result as MManga;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<PageUrl>> getPageList(String url) async {
|
||||
final interpreter = _executeLib();
|
||||
final result = await interpreter.invoke('getPageList', [url]);
|
||||
return (result as List)
|
||||
.map(
|
||||
(e) => e is String
|
||||
? PageUrl(e.toString().trim())
|
||||
: PageUrl.fromJson((e as Map).toMapStringDynamic!),
|
||||
)
|
||||
.toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<Video>> getVideoList(String url) async {
|
||||
final interpreter = _executeLib();
|
||||
final result = await interpreter.invoke('getVideoList', [url]);
|
||||
return (result as List).cast<Video>();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String> getHtmlContent(String url, String? referer) async {
|
||||
final interpreter = _executeLib();
|
||||
final result = await interpreter.invoke('getHtmlContent', [url, referer]);
|
||||
return result as String;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String> cleanHtmlContent(String html) async {
|
||||
final interpreter = _executeLib();
|
||||
final result = await interpreter.invoke('cleanHtmlContent', [html]);
|
||||
return result as String;
|
||||
}
|
||||
|
||||
@override
|
||||
FilterList getFilterList() {
|
||||
List<dynamic> list;
|
||||
try {
|
||||
final interpreter = _executeLib();
|
||||
list = interpreter.invoke('getFilterList', []) as List;
|
||||
} catch (_) {
|
||||
list = [];
|
||||
}
|
||||
|
||||
return FilterList(_toValueList(list));
|
||||
}
|
||||
|
||||
List _toValueList(List filters) {
|
||||
return (filters).map((e) {
|
||||
if (e is BridgedInstance) {
|
||||
e = e.nativeObject;
|
||||
}
|
||||
if (e is SelectFilter) {
|
||||
return SelectFilter(
|
||||
e.type,
|
||||
e.name,
|
||||
e.state,
|
||||
_toValueList(e.values),
|
||||
e.typeName,
|
||||
);
|
||||
} else if (e is SortFilter) {
|
||||
return SortFilter(
|
||||
e.type,
|
||||
e.name,
|
||||
e.state,
|
||||
_toValueList(e.values),
|
||||
e.typeName,
|
||||
);
|
||||
} else if (e is GroupFilter) {
|
||||
return GroupFilter(e.type, e.name, _toValueList(e.state), e.typeName);
|
||||
}
|
||||
return e;
|
||||
}).toList();
|
||||
}
|
||||
|
||||
@override
|
||||
List<SourcePreference> getSourcePreferences() {
|
||||
final interpreter = _executeLib();
|
||||
try {
|
||||
final result = interpreter.invoke('getSourcePreferences', []);
|
||||
return (result as List).cast();
|
||||
} catch (_) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
414
lib/core/services/extensions/mangayomi/Eval/javascript/dom_selector.dart
Executable file
414
lib/core/services/extensions/mangayomi/Eval/javascript/dom_selector.dart
Executable file
@@ -0,0 +1,414 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import '../../util/dom_extensions.dart';
|
||||
import 'package:flutter_js/flutter_js.dart';
|
||||
import 'package:html/dom.dart';
|
||||
import 'package:html/parser.dart';
|
||||
|
||||
class JsDomSelector {
|
||||
late JavascriptRuntime runtime;
|
||||
|
||||
JsDomSelector(this.runtime);
|
||||
|
||||
final Map<int, Element?> _elements = {};
|
||||
int _elementKey = 0;
|
||||
|
||||
void init() {
|
||||
runtime.onMessage('get_doc_element', (dynamic args) {
|
||||
final input = args[0];
|
||||
final type = args[1];
|
||||
final doc = parse(input);
|
||||
final element = switch (type) {
|
||||
'body' => doc.body,
|
||||
'documentElement' => doc.documentElement,
|
||||
'head' => doc.head,
|
||||
_ => doc.parent,
|
||||
};
|
||||
_elementKey++;
|
||||
_elements[_elementKey] = element;
|
||||
return _elementKey;
|
||||
});
|
||||
runtime.onMessage('get_doc_string', (dynamic args) {
|
||||
final input = args[0];
|
||||
final type = args[1];
|
||||
final doc = parse(input);
|
||||
final res = switch (type) {
|
||||
'text' => doc.text,
|
||||
_ => doc.outerHtml,
|
||||
};
|
||||
return res ?? "";
|
||||
});
|
||||
runtime.onMessage('get_element_string', (dynamic args) {
|
||||
final type = args[0];
|
||||
final key = args[1];
|
||||
final element = _elements[key];
|
||||
final res = switch (type) {
|
||||
'text' => element?.text,
|
||||
'innerHtml' => element?.innerHtml,
|
||||
'outerHtml' => element?.outerHtml,
|
||||
'className' => element?.className,
|
||||
'localName' => element?.localName,
|
||||
'namespaceUri' => element?.namespaceUri,
|
||||
'getSrc' => element?.getSrc,
|
||||
'getImg' => element?.getImg,
|
||||
'getHref' => element?.getHref,
|
||||
_ => element?.getDataSrc,
|
||||
};
|
||||
return res ?? "";
|
||||
});
|
||||
runtime.onMessage('doc_select_first', (dynamic args) {
|
||||
final input = args[0];
|
||||
final selector = args[1];
|
||||
_elementKey++;
|
||||
_elements[_elementKey] = parse(input).selectFirst(selector);
|
||||
return _elementKey;
|
||||
});
|
||||
runtime.onMessage('ele_selectFirst', (dynamic args) {
|
||||
final selector = args[0];
|
||||
final key = args[1];
|
||||
_elementKey++;
|
||||
_elements[_elementKey] = _elements[key]?.selectFirst(selector);
|
||||
return _elementKey;
|
||||
});
|
||||
runtime.onMessage('ele_element_sibling', (dynamic args) {
|
||||
final type = args[0];
|
||||
final key = args[1];
|
||||
final ele = _elements[key];
|
||||
final element = switch (type) {
|
||||
'nextElementSibling' => ele?.nextElementSibling,
|
||||
_ => ele?.previousElementSibling,
|
||||
};
|
||||
_elementKey++;
|
||||
_elements[_elementKey] = element;
|
||||
return _elementKey;
|
||||
});
|
||||
runtime.onMessage('ele_attr', (dynamic args) {
|
||||
final attr = args[0];
|
||||
final key = args[1];
|
||||
return _elements[key]?.attr(attr) ?? "";
|
||||
});
|
||||
runtime.onMessage('doc_attr', (dynamic args) {
|
||||
final input = args[0];
|
||||
final attr = args[1];
|
||||
return parse(input).attr(attr) ?? "";
|
||||
});
|
||||
runtime.onMessage('ele_has_attr', (dynamic args) {
|
||||
final attr = args[0];
|
||||
final key = args[1];
|
||||
return _elements[key]?.hasAtr(attr) ?? false;
|
||||
});
|
||||
runtime.onMessage('doc_has_attr', (dynamic args) {
|
||||
final input = args[0];
|
||||
final attr = args[1];
|
||||
return parse(input).hasAtr(attr);
|
||||
});
|
||||
runtime.onMessage('doc_xpath_first', (dynamic args) {
|
||||
final input = args[0];
|
||||
final xpath = args[1];
|
||||
return parse(input).xpathFirst(xpath) ?? "";
|
||||
});
|
||||
runtime.onMessage('ele_xpathFirst', (dynamic args) {
|
||||
final xpath = args[0];
|
||||
final key = args[1];
|
||||
return _elements[key]?.xpathFirst(xpath) ?? "";
|
||||
});
|
||||
|
||||
runtime.onMessage('doc_xpath', (dynamic args) {
|
||||
final input = args[0];
|
||||
final xpath = args[1];
|
||||
return jsonEncode(parse(input).xpath(xpath));
|
||||
});
|
||||
runtime.onMessage('ele_xpath', (dynamic args) {
|
||||
final xpath = args[0];
|
||||
final key = args[1];
|
||||
return jsonEncode(_elements[key]?.xpath(xpath));
|
||||
});
|
||||
runtime.onMessage('doc_get_elements_by', (dynamic args) {
|
||||
final input = args[0];
|
||||
final type = args[1];
|
||||
final name = args[2];
|
||||
final doc = parse(input);
|
||||
final elements = switch (type) {
|
||||
'children' => doc.children,
|
||||
'getElementsByTagName' => doc.getElementsByTagName(name),
|
||||
_ => doc.getElementsByClassName(name),
|
||||
};
|
||||
List<int> elementKeys = [];
|
||||
for (var element in elements) {
|
||||
_elementKey++;
|
||||
_elements[_elementKey] = element;
|
||||
elementKeys.add(_elementKey);
|
||||
}
|
||||
return jsonEncode(elementKeys);
|
||||
});
|
||||
runtime.onMessage('ele_get_elements_by', (dynamic args) {
|
||||
final type = args[0];
|
||||
final name = args[1];
|
||||
final key = args[2];
|
||||
final element = _elements[key];
|
||||
final elements = switch (type) {
|
||||
'children' => element?.children,
|
||||
'getElementsByTagName' => element?.getElementsByTagName(name),
|
||||
_ => element?.getElementsByClassName(name),
|
||||
};
|
||||
List<int> elementKeys = [];
|
||||
for (var element in elements ?? []) {
|
||||
_elementKey++;
|
||||
_elements[_elementKey] = element;
|
||||
elementKeys.add(_elementKey);
|
||||
}
|
||||
return jsonEncode(elementKeys);
|
||||
});
|
||||
runtime.onMessage('doc_get_element_by_id', (dynamic args) {
|
||||
final input = args[0];
|
||||
final id = args[1];
|
||||
_elementKey++;
|
||||
_elements[_elementKey] = parse(input).getElementById(id);
|
||||
return _elementKey;
|
||||
});
|
||||
runtime.onMessage('doc_select', (dynamic args) {
|
||||
final input = args[0];
|
||||
final selector = args[1];
|
||||
final elements = parse(input).select(selector);
|
||||
List<int> elementKeys = [];
|
||||
for (var element in elements ?? []) {
|
||||
_elementKey++;
|
||||
_elements[_elementKey] = element;
|
||||
elementKeys.add(_elementKey);
|
||||
}
|
||||
return jsonEncode(elementKeys);
|
||||
});
|
||||
runtime.onMessage('ele_select', (dynamic args) {
|
||||
final selector = args[0];
|
||||
final key = args[1];
|
||||
final elements = _elements[key]?.select(selector);
|
||||
List<int> elementKeys = [];
|
||||
for (var element in elements ?? []) {
|
||||
_elementKey++;
|
||||
_elements[_elementKey] = element;
|
||||
elementKeys.add(_elementKey);
|
||||
}
|
||||
return jsonEncode(elementKeys);
|
||||
});
|
||||
|
||||
runtime.evaluate('''
|
||||
class Document {
|
||||
constructor(html) {
|
||||
this.html = html;
|
||||
}
|
||||
getElement(type) {
|
||||
const key = sendMessage(
|
||||
"get_doc_element",
|
||||
JSON.stringify([this.html, type])
|
||||
);
|
||||
return new Element(key);
|
||||
}
|
||||
get body() {
|
||||
return this.getElement('body');
|
||||
}
|
||||
get documentElement() {
|
||||
return this.getElement('documentElement');
|
||||
}
|
||||
get head() {
|
||||
return this.getElement('head');
|
||||
}
|
||||
get parent() {
|
||||
return this.getElement('parent');
|
||||
}
|
||||
getString(type) {
|
||||
return sendMessage(
|
||||
"get_doc_string",
|
||||
JSON.stringify([this.html, type]));
|
||||
}
|
||||
get text() {
|
||||
return this.getString('text');
|
||||
}
|
||||
get outerHtml() {
|
||||
return this.getString('outerHtml');
|
||||
}
|
||||
selectFirst(selector) {
|
||||
const key = sendMessage(
|
||||
"doc_select_first",
|
||||
JSON.stringify([this.html, selector])
|
||||
);
|
||||
return new Element(key);
|
||||
}
|
||||
select(selector) {
|
||||
let elements = [];
|
||||
JSON.parse(
|
||||
sendMessage("doc_select", JSON.stringify([this.html, selector]))
|
||||
).forEach((key) => {
|
||||
elements.push(new Element(key));
|
||||
});
|
||||
return elements;
|
||||
}
|
||||
xpathFirst(xpath) {
|
||||
return sendMessage(
|
||||
"doc_xpath_first",
|
||||
JSON.stringify([this.html, xpath])
|
||||
);
|
||||
}
|
||||
xpath(xpath) {
|
||||
return JSON.parse(sendMessage(
|
||||
"doc_xpath",
|
||||
JSON.stringify([this.html, xpath]))
|
||||
);
|
||||
}
|
||||
getElementsListBy(type, name) {
|
||||
name = name || '';
|
||||
let elements = [];
|
||||
JSON.parse(sendMessage(
|
||||
"doc_get_elements_by",
|
||||
JSON.stringify([this.html, type, name]))
|
||||
).forEach((key) => {
|
||||
elements.push(new Element(key));
|
||||
});
|
||||
return elements;
|
||||
}
|
||||
get children() {
|
||||
return this.getElementsListBy('children');
|
||||
}
|
||||
getElementsByTagName(name) {
|
||||
return this.getElementsListBy('getElementsByTagName', name);
|
||||
}
|
||||
getElementsByClassName(name) {
|
||||
return this.getElementsListBy('getElementsByClassName', name);
|
||||
}
|
||||
getElementById(id) {
|
||||
const key = sendMessage(
|
||||
"doc_get_element_by_id",
|
||||
JSON.stringify([this.html, id])
|
||||
);
|
||||
return new Element(key);
|
||||
}
|
||||
attr(attr) {
|
||||
return sendMessage(
|
||||
"doc_attr",
|
||||
JSON.stringify([this.html, attr])
|
||||
);
|
||||
}
|
||||
hasAttr(attr) {
|
||||
return sendMessage(
|
||||
"doc_has_attr",
|
||||
JSON.stringify([this.html, attr])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class Element {
|
||||
constructor(key) {
|
||||
this.key = key;
|
||||
}
|
||||
getString(type) {
|
||||
return sendMessage(
|
||||
"get_element_string",
|
||||
JSON.stringify([type, this.key])
|
||||
);
|
||||
}
|
||||
get text() {
|
||||
return this.getString("text");
|
||||
}
|
||||
get outerHtml() {
|
||||
return this.getString("outerHtml");
|
||||
}
|
||||
get innerHtml() {
|
||||
return this.getString("innerHtml");
|
||||
}
|
||||
get className() {
|
||||
return this.getString("className");
|
||||
}
|
||||
get localName() {
|
||||
return this.getString("localName");
|
||||
}
|
||||
get namespaceUri() {
|
||||
return this.getString("namespaceUri");
|
||||
}
|
||||
get getSrc() {
|
||||
return this.getString("getSrc");
|
||||
}
|
||||
get getImg() {
|
||||
return this.getString("getImg");
|
||||
}
|
||||
get getHref() {
|
||||
return this.getString("getHref");
|
||||
}
|
||||
get getDataSrc() {
|
||||
return this.getString("getDataSrc");
|
||||
}
|
||||
getElementSibling(type) {
|
||||
const key = sendMessage(
|
||||
"ele_element_sibling",
|
||||
JSON.stringify([type, this.key])
|
||||
);
|
||||
return new Element(key);
|
||||
}
|
||||
get previousElementSibling() {
|
||||
return this.getElementSibling("previousElementSibling");
|
||||
}
|
||||
get nextElementSibling() {
|
||||
return this.getElementSibling("nextElementSibling");
|
||||
}
|
||||
getElementsListBy(type, name) {
|
||||
name = name || '';
|
||||
let elements = [];
|
||||
JSON.parse(sendMessage(
|
||||
"ele_get_elements_by",
|
||||
JSON.stringify([type, name, this.key]))
|
||||
).forEach((key) => {
|
||||
elements.push(new Element(key));
|
||||
});
|
||||
return elements;
|
||||
}
|
||||
get children() {
|
||||
return this.getElementsListBy('children');
|
||||
}
|
||||
getElementsByTagName(name) {
|
||||
return this.getElementsListBy('getElementsByTagName', name);
|
||||
}
|
||||
getElementsByClassName(name) {
|
||||
return this.getElementsListBy('getElementsByClassName', name);
|
||||
}
|
||||
xpath(xpath) {
|
||||
return JSON.parse(sendMessage(
|
||||
"xpath",
|
||||
JSON.stringify([xpath, this.key]))
|
||||
);
|
||||
}
|
||||
attr(attr) {
|
||||
return sendMessage(
|
||||
"ele_attr",
|
||||
JSON.stringify([attr, this.key])
|
||||
);
|
||||
}
|
||||
xpathFirst(xpath) {
|
||||
return sendMessage(
|
||||
"xpathFirst",
|
||||
JSON.stringify([xpath, this.key])
|
||||
);
|
||||
}
|
||||
selectFirst(selector) {
|
||||
const key = sendMessage(
|
||||
"ele_selectFirst",
|
||||
JSON.stringify([selector, this.key])
|
||||
);
|
||||
return new Element(key);
|
||||
}
|
||||
select(selector) {
|
||||
let elements = [];
|
||||
JSON.parse(
|
||||
sendMessage("ele_select", JSON.stringify([selector, this.key]))
|
||||
).forEach((key) => {
|
||||
elements.push(new Element(key));
|
||||
});
|
||||
return elements;
|
||||
}
|
||||
hasAttr(attr) {
|
||||
return sendMessage(
|
||||
"ele_has_attr",
|
||||
JSON.stringify([this.html, attr])
|
||||
);
|
||||
}
|
||||
}
|
||||
''');
|
||||
}
|
||||
}
|
||||
256
lib/core/services/extensions/mangayomi/Eval/javascript/extractors.dart
Executable file
256
lib/core/services/extensions/mangayomi/Eval/javascript/extractors.dart
Executable file
@@ -0,0 +1,256 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter_js/flutter_js.dart';
|
||||
|
||||
import '../dart/model/m_bridge.dart';
|
||||
import '../dart/model/video.dart';
|
||||
import 'http.dart';
|
||||
|
||||
class JsVideosExtractors {
|
||||
late JavascriptRuntime runtime;
|
||||
|
||||
JsVideosExtractors(this.runtime);
|
||||
|
||||
void init() {
|
||||
runtime.onMessage('sibnetExtractor', (dynamic args) async {
|
||||
return (await MBridge.sibnetExtractor(
|
||||
args[0],
|
||||
args[1] ?? "",
|
||||
))
|
||||
.encodeToJson();
|
||||
});
|
||||
runtime.onMessage('myTvExtractor', (dynamic args) async {
|
||||
return (await MBridge.myTvExtractor(args[0])).encodeToJson();
|
||||
});
|
||||
runtime.onMessage('okruExtractor', (dynamic args) async {
|
||||
return (await MBridge.okruExtractor(args[0])).encodeToJson();
|
||||
});
|
||||
runtime.onMessage('voeExtractor', (dynamic args) async {
|
||||
return (await MBridge.voeExtractor(args[0], args[1])).encodeToJson();
|
||||
});
|
||||
runtime.onMessage('vidBomExtractor', (dynamic args) async {
|
||||
return (await MBridge.vidBomExtractor(args[0])).encodeToJson();
|
||||
});
|
||||
runtime.onMessage('quarkVideosExtractor', (dynamic args) async {
|
||||
return (await MBridge.quarkVideosExtractor(
|
||||
args[0],
|
||||
args[1],
|
||||
))
|
||||
.encodeToJson();
|
||||
});
|
||||
runtime.onMessage('ucVideosExtractor', (dynamic args) async {
|
||||
return (await MBridge.ucVideosExtractor(args[0], args[1])).encodeToJson();
|
||||
});
|
||||
runtime.onMessage('quarkFilesExtractor', (dynamic args) async {
|
||||
List<String> urls = (args[0] as List).cast<String>();
|
||||
return (await MBridge.quarkFilesExtractor(urls, args[1]));
|
||||
});
|
||||
runtime.onMessage('ucFilesExtractor', (dynamic args) async {
|
||||
List<String> urls = (args[0] as List).cast<String>();
|
||||
return (await MBridge.ucFilesExtractor(urls, args[1]));
|
||||
});
|
||||
runtime.onMessage('streamlareExtractor', (dynamic args) async {
|
||||
return (await MBridge.streamlareExtractor(
|
||||
args[0],
|
||||
args[1] ?? "",
|
||||
args[2] ?? "",
|
||||
))
|
||||
.encodeToJson();
|
||||
});
|
||||
runtime.onMessage('sendVidExtractor', (dynamic args) async {
|
||||
return (await MBridge.sendVidExtractor(
|
||||
args[0],
|
||||
args[1] != null
|
||||
? jsonEncode((args[1] as Map?).toMapStringString)
|
||||
: null,
|
||||
args[2] ?? "",
|
||||
))
|
||||
.encodeToJson();
|
||||
});
|
||||
runtime.onMessage('yourUploadExtractor', (dynamic args) async {
|
||||
return (await MBridge.yourUploadExtractor(
|
||||
args[0],
|
||||
args[1] != null
|
||||
? jsonEncode((args[1] as Map?).toMapStringString)
|
||||
: null,
|
||||
args[2],
|
||||
args[3] ?? "",
|
||||
))
|
||||
.encodeToJson();
|
||||
});
|
||||
runtime.onMessage('gogoCdnExtractor', (dynamic args) async {
|
||||
return (await MBridge.gogoCdnExtractor(args[0])).encodeToJson();
|
||||
});
|
||||
runtime.onMessage('doodExtractor', (dynamic args) async {
|
||||
return (await MBridge.doodExtractor(args[0], args[1])).encodeToJson();
|
||||
});
|
||||
runtime.onMessage('streamTapeExtractor', (dynamic args) async {
|
||||
return (await MBridge.streamTapeExtractor(
|
||||
args[0],
|
||||
args[1],
|
||||
))
|
||||
.encodeToJson();
|
||||
});
|
||||
runtime.onMessage('mp4UploadExtractor', (dynamic args) async {
|
||||
return (await MBridge.mp4UploadExtractor(
|
||||
args[0],
|
||||
args[1] != null
|
||||
? jsonEncode((args[1] as Map?).toMapStringString)
|
||||
: null,
|
||||
args[2] ?? "",
|
||||
args[3] ?? "",
|
||||
))
|
||||
.encodeToJson();
|
||||
});
|
||||
runtime.onMessage('streamWishExtractor', (dynamic args) async {
|
||||
return (await MBridge.streamWishExtractor(
|
||||
args[0],
|
||||
args[1] ?? "",
|
||||
))
|
||||
.encodeToJson();
|
||||
});
|
||||
runtime.onMessage('filemoonExtractor', (dynamic args) async {
|
||||
return (await MBridge.filemoonExtractor(
|
||||
args[0],
|
||||
args[1] ?? "",
|
||||
args[2] ?? "",
|
||||
))
|
||||
.encodeToJson();
|
||||
});
|
||||
|
||||
runtime.evaluate('''
|
||||
async function sibnetExtractor(url, prefix) {
|
||||
const result = await sendMessage(
|
||||
"sibnetExtractor",
|
||||
JSON.stringify([url, prefix])
|
||||
);
|
||||
return JSON.parse(result);
|
||||
}
|
||||
async function myTvExtractor(url) {
|
||||
const result = await sendMessage(
|
||||
"myTvExtractor",
|
||||
JSON.stringify([url])
|
||||
);
|
||||
return JSON.parse(result);
|
||||
}
|
||||
async function okruExtractor(url) {
|
||||
const result = await sendMessage(
|
||||
"okruExtractor",
|
||||
JSON.stringify([url])
|
||||
);
|
||||
return JSON.parse(result);
|
||||
}
|
||||
async function voeExtractor(url, quality) {
|
||||
const result = await sendMessage(
|
||||
"voeExtractor",
|
||||
JSON.stringify([url, quality])
|
||||
);
|
||||
return JSON.parse(result);
|
||||
}
|
||||
async function vidBomExtractor(url) {
|
||||
const result = await sendMessage(
|
||||
"vidBomExtractor",
|
||||
JSON.stringify([url])
|
||||
);
|
||||
return JSON.parse(result);
|
||||
}
|
||||
async function streamlareExtractor(url, prefix, suffix) {
|
||||
const result = await sendMessage(
|
||||
"streamlareExtractor",
|
||||
JSON.stringify([url, prefix, suffix])
|
||||
);
|
||||
return JSON.parse(result);
|
||||
}
|
||||
async function sendVidExtractor(url, headers, prefix) {
|
||||
const result = await sendMessage(
|
||||
"sendVidExtractor",
|
||||
JSON.stringify([url, JSON.stringify(headers), prefix])
|
||||
);
|
||||
return JSON.parse(result);
|
||||
}
|
||||
async function yourUploadExtractor(url, headers, name, prefix) {
|
||||
const result = await sendMessage(
|
||||
"yourUploadExtractor",
|
||||
JSON.stringify([url, JSON.stringify(headers), name, prefix])
|
||||
);
|
||||
return JSON.parse(result);
|
||||
}
|
||||
async function gogoCdnExtractor(url) {
|
||||
const result = await sendMessage(
|
||||
"gogoCdnExtractor",
|
||||
JSON.stringify([url])
|
||||
);
|
||||
return JSON.parse(result);
|
||||
}
|
||||
async function doodExtractor(url, quality) {
|
||||
const result = await sendMessage(
|
||||
"doodExtractor",
|
||||
JSON.stringify([url, quality])
|
||||
);
|
||||
return JSON.parse(result);
|
||||
}
|
||||
async function streamTapeExtractor(url, quality) {
|
||||
const result = await sendMessage(
|
||||
"streamTapeExtractor",
|
||||
JSON.stringify([url, quality])
|
||||
);
|
||||
return JSON.parse(result);
|
||||
}
|
||||
async function mp4UploadExtractor(url, headers, prefix, suffix) {
|
||||
const result = await sendMessage(
|
||||
"mp4UploadExtractor",
|
||||
JSON.stringify([url, JSON.stringify(headers), prefix, suffix])
|
||||
);
|
||||
return JSON.parse(result);
|
||||
}
|
||||
async function streamWishExtractor(url, prefix) {
|
||||
const result = await sendMessage(
|
||||
"streamWishExtractor",
|
||||
JSON.stringify([url, prefix])
|
||||
);
|
||||
return JSON.parse(result);
|
||||
}
|
||||
async function filemoonExtractor(url, prefix, suffix) {
|
||||
const result = await sendMessage(
|
||||
"filemoonExtractor",
|
||||
JSON.stringify([url, prefix, suffix])
|
||||
);
|
||||
return JSON.parse(result);
|
||||
}
|
||||
async function quarkVideosExtractor(url, cookie) {
|
||||
const result = await sendMessage(
|
||||
"quarkVideosExtractor",
|
||||
JSON.stringify([url, cookie])
|
||||
);
|
||||
return JSON.parse(result);
|
||||
}
|
||||
async function ucVideosExtractor(url, cookie) {
|
||||
const result = await sendMessage(
|
||||
"ucVideosExtractor",
|
||||
JSON.stringify([url, cookie])
|
||||
);
|
||||
return JSON.parse(result);
|
||||
}
|
||||
async function quarkFilesExtractor(urls, cookie) {
|
||||
const result = await sendMessage(
|
||||
"quarkFilesExtractor",
|
||||
JSON.stringify([urls, cookie])
|
||||
);
|
||||
return result;
|
||||
}
|
||||
async function ucFilesExtractor(urls, cookie) {
|
||||
const result = await sendMessage(
|
||||
"ucFilesExtractor",
|
||||
JSON.stringify([urls, cookie])
|
||||
);
|
||||
return result;
|
||||
}
|
||||
''');
|
||||
}
|
||||
}
|
||||
|
||||
extension ResponseExtexsion on List<Video> {
|
||||
String encodeToJson() {
|
||||
return jsonEncode(map((e) => e.toJson()).toList());
|
||||
}
|
||||
}
|
||||
172
lib/core/services/extensions/mangayomi/Eval/javascript/http.dart
Executable file
172
lib/core/services/extensions/mangayomi/Eval/javascript/http.dart
Executable file
@@ -0,0 +1,172 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter_js/flutter_js.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:http_interceptor/http_interceptor.dart';
|
||||
|
||||
import 'package:unyo/core/services/extensions/mangayomi//http/m_client.dart';
|
||||
|
||||
class JsHttpClient {
|
||||
late JavascriptRuntime runtime;
|
||||
|
||||
JsHttpClient(this.runtime);
|
||||
|
||||
void init() {
|
||||
InterceptedClient client(dynamic reqcopyWith) {
|
||||
return MClient.init(
|
||||
reqcopyWith: (reqcopyWith as Map?)?.toMapStringDynamic,
|
||||
);
|
||||
}
|
||||
|
||||
runtime.onMessage('http_head', (dynamic args) async {
|
||||
return await _toHttpResponse(client(args[1]), "HEAD", args);
|
||||
});
|
||||
runtime.onMessage('http_get', (dynamic args) async {
|
||||
return await _toHttpResponse(client(args[1]), "GET", args);
|
||||
});
|
||||
runtime.onMessage('http_post', (dynamic args) async {
|
||||
return await _toHttpResponse(client(args[1]), "POST", args);
|
||||
});
|
||||
runtime.onMessage('http_put', (dynamic args) async {
|
||||
return await _toHttpResponse(client(args[1]), "PUT", args);
|
||||
});
|
||||
runtime.onMessage('http_delete', (dynamic args) async {
|
||||
return await _toHttpResponse(client(args[1]), "DELETE", args);
|
||||
});
|
||||
runtime.onMessage('http_patch', (dynamic args) async {
|
||||
return await _toHttpResponse(client(args[1]), "PATCH", args);
|
||||
});
|
||||
runtime.evaluate('''
|
||||
class Client {
|
||||
constructor(reqcopyWith) {
|
||||
this.reqcopyWith = reqcopyWith;
|
||||
}
|
||||
async head(url, headers) {
|
||||
headers = headers;
|
||||
const result = await sendMessage(
|
||||
"http_head",
|
||||
JSON.stringify([null, this.reqcopyWith, url, headers])
|
||||
);
|
||||
return JSON.parse(result);
|
||||
}
|
||||
async get(url, headers) {
|
||||
headers = headers;
|
||||
const result = await sendMessage(
|
||||
"http_get",
|
||||
JSON.stringify([null, this.reqcopyWith, url, headers])
|
||||
);
|
||||
return JSON.parse(result);
|
||||
}
|
||||
async post(url, headers, body) {
|
||||
headers = headers;
|
||||
const result = await sendMessage(
|
||||
"http_post",
|
||||
JSON.stringify([null, this.reqcopyWith, url, headers, body])
|
||||
);
|
||||
return JSON.parse(result);
|
||||
}
|
||||
async put(url, headers, body) {
|
||||
headers = headers;
|
||||
const result = await sendMessage(
|
||||
"http_post",
|
||||
JSON.stringify([null, this.reqcopyWith, url, headers, body])
|
||||
);
|
||||
return JSON.parse(result);
|
||||
}
|
||||
async delete(url, headers, body) {
|
||||
headers = headers;
|
||||
const result = await sendMessage(
|
||||
"http_post",
|
||||
JSON.stringify([null, this.reqcopyWith, url, headers, body])
|
||||
);
|
||||
return JSON.parse(result);
|
||||
}
|
||||
async patch(url, headers, body) {
|
||||
headers = headers;
|
||||
const result = await sendMessage(
|
||||
"http_post",
|
||||
JSON.stringify([null, this.reqcopyWith, url, headers, body])
|
||||
);
|
||||
return JSON.parse(result);
|
||||
}
|
||||
}
|
||||
''');
|
||||
}
|
||||
}
|
||||
|
||||
Future<String> _toHttpResponse(Client client, String method, List args) async {
|
||||
final url = args[2] as String;
|
||||
final headers = (args[3] as Map?)?.toMapStringString;
|
||||
final body = args.length >= 5
|
||||
? args[4] is List
|
||||
? args[4] as List
|
||||
: args[4] is String
|
||||
? args[4] as String
|
||||
: (args[4] as Map?)?.toMapStringDynamic
|
||||
: null;
|
||||
var request = http.Request(method, Uri.parse(url));
|
||||
request.headers.addAll(headers ?? {});
|
||||
if ((request.headers[HttpHeaders.contentTypeHeader]?.contains(
|
||||
"application/json",
|
||||
)) ??
|
||||
false) {
|
||||
request.body = json.encode(body);
|
||||
request.headers.addAll(headers ?? {});
|
||||
http.StreamedResponse response = await client.send(request);
|
||||
final res = Response(
|
||||
"",
|
||||
response.statusCode,
|
||||
request: response.request,
|
||||
headers: response.headers,
|
||||
isRedirect: response.isRedirect,
|
||||
persistentConnection: response.persistentConnection,
|
||||
reasonPhrase: response.reasonPhrase,
|
||||
);
|
||||
Map<String, dynamic> resMap = res.toJson();
|
||||
resMap["body"] = await response.stream.bytesToString();
|
||||
return jsonEncode(resMap);
|
||||
}
|
||||
final future = switch (method) {
|
||||
"HEAD" => client.head(Uri.parse(url), headers: headers),
|
||||
"GET" => client.get(Uri.parse(url), headers: headers),
|
||||
"POST" => client.post(Uri.parse(url), headers: headers, body: body),
|
||||
"PUT" => client.put(Uri.parse(url), headers: headers, body: body),
|
||||
"DELETE" => client.delete(Uri.parse(url), headers: headers, body: body),
|
||||
_ => client.patch(Uri.parse(url), headers: headers, body: body),
|
||||
};
|
||||
return jsonEncode((await future).toJson());
|
||||
}
|
||||
|
||||
extension ResponseExtexsion on Response {
|
||||
Map<String, dynamic> toJson() => {
|
||||
'body': body,
|
||||
'headers': headers,
|
||||
'isRedirect': isRedirect,
|
||||
'persistentConnection': persistentConnection,
|
||||
'reasonPhrase': reasonPhrase,
|
||||
'statusCode': statusCode,
|
||||
'request': {
|
||||
'contentLength': request?.contentLength,
|
||||
'finalized': request?.finalized,
|
||||
'followRedirects': request?.followRedirects,
|
||||
'headers': request?.headers,
|
||||
'maxRedirects': request?.maxRedirects,
|
||||
'method': request?.method,
|
||||
'persistentConnection': request?.persistentConnection,
|
||||
'url': request?.url.toString(),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
extension ToMapExtension on Map? {
|
||||
Map<String, dynamic>? get toMapStringDynamic {
|
||||
return this?.map((key, value) => MapEntry(key.toString(), value));
|
||||
}
|
||||
|
||||
Map<String, String>? get toMapStringString {
|
||||
return this?.map(
|
||||
(key, value) => MapEntry(key.toString(), value.toString()),
|
||||
);
|
||||
}
|
||||
}
|
||||
46
lib/core/services/extensions/mangayomi/Eval/javascript/preferences.dart
Executable file
46
lib/core/services/extensions/mangayomi/Eval/javascript/preferences.dart
Executable file
@@ -0,0 +1,46 @@
|
||||
import 'package:flutter_js/flutter_js.dart';
|
||||
|
||||
import '../../Models/Source.dart';
|
||||
import '../../util/extension_preferences_providers.dart';
|
||||
|
||||
class JsPreferences {
|
||||
late JavascriptRuntime runtime;
|
||||
late MSource? source;
|
||||
|
||||
JsPreferences(this.runtime, this.source);
|
||||
|
||||
void init() {
|
||||
runtime.onMessage('get', (dynamic args) {
|
||||
return getPreferenceValue(source!.id!, args[0]);
|
||||
});
|
||||
runtime.onMessage('getString', (dynamic args) {
|
||||
return getSourcePreferenceStringValue(source!.id!, args[0], args[1]);
|
||||
});
|
||||
runtime.onMessage('setString', (dynamic args) {
|
||||
return setSourcePreferenceStringValue(source!.id!, args[0], args[1]);
|
||||
});
|
||||
|
||||
runtime.evaluate('''
|
||||
class SharedPreferences {
|
||||
get(key) {
|
||||
return sendMessage(
|
||||
"get",
|
||||
JSON.stringify([key])
|
||||
);
|
||||
}
|
||||
getString(key, defaultValue) {
|
||||
return sendMessage(
|
||||
"getString",
|
||||
JSON.stringify([key, defaultValue])
|
||||
);
|
||||
}
|
||||
setString(key, defaultValue) {
|
||||
return sendMessage(
|
||||
"setString",
|
||||
JSON.stringify([key, defaultValue])
|
||||
);
|
||||
}
|
||||
}
|
||||
''');
|
||||
}
|
||||
}
|
||||
226
lib/core/services/extensions/mangayomi/Eval/javascript/service.dart
Executable file
226
lib/core/services/extensions/mangayomi/Eval/javascript/service.dart
Executable file
@@ -0,0 +1,226 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'preferences.dart';
|
||||
import 'utils.dart';
|
||||
import 'package:flutter_js/flutter_js.dart';
|
||||
|
||||
import '../../Models/Source.dart';
|
||||
import '../../interface.dart';
|
||||
import '../dart/model/filter.dart';
|
||||
import '../dart/model/m_manga.dart';
|
||||
import '../dart/model/m_pages.dart';
|
||||
import '../dart/model/page.dart';
|
||||
import '../dart/model/source_preference.dart';
|
||||
import '../dart/model/video.dart';
|
||||
import 'dom_selector.dart';
|
||||
import 'extractors.dart';
|
||||
import 'http.dart';
|
||||
|
||||
class JsExtensionService implements ExtensionService {
|
||||
late JavascriptRuntime runtime;
|
||||
@override
|
||||
late MSource source;
|
||||
|
||||
JsExtensionService(this.source);
|
||||
|
||||
void _init() {
|
||||
runtime = getJavascriptRuntime();
|
||||
JsHttpClient(runtime).init();
|
||||
JsDomSelector(runtime).init();
|
||||
JsVideosExtractors(runtime).init();
|
||||
JsUtils(runtime).init();
|
||||
JsPreferences(runtime, source).init();
|
||||
|
||||
runtime.evaluate('''
|
||||
class MProvider {
|
||||
get source() {
|
||||
return JSON.parse('${jsonEncode(source.toMSource().toJson())}');
|
||||
}
|
||||
get supportsLatest() {
|
||||
throw new Error("supportsLatest not implemented");
|
||||
}
|
||||
getHeaders(url) {
|
||||
throw new Error("getHeaders not implemented");
|
||||
}
|
||||
async getPopular(page) {
|
||||
throw new Error("getPopular not implemented");
|
||||
}
|
||||
async getLatestUpdates(page) {
|
||||
throw new Error("getLatestUpdates not implemented");
|
||||
}
|
||||
async search(query, page, filters) {
|
||||
throw new Error("search not implemented");
|
||||
}
|
||||
async getDetail(url) {
|
||||
throw new Error("getDetail not implemented");
|
||||
}
|
||||
async getPageList() {
|
||||
throw new Error("getPageList not implemented");
|
||||
}
|
||||
async getVideoList(url) {
|
||||
throw new Error("getVideoList not implemented");
|
||||
}
|
||||
async getHtmlContent(name, url) {
|
||||
throw new Error("getHtmlContent not implemented");
|
||||
}
|
||||
async cleanHtmlContent(html) {
|
||||
throw new Error("cleanHtmlContent not implemented");
|
||||
}
|
||||
getFilterList() {
|
||||
throw new Error("getFilterList not implemented");
|
||||
}
|
||||
getSourcePreferences() {
|
||||
throw new Error("getSourcePreferences not implemented");
|
||||
}
|
||||
}
|
||||
async function jsonStringify(fn) {
|
||||
return JSON.stringify(await fn());
|
||||
}
|
||||
''');
|
||||
runtime.evaluate('''${source.sourceCode}
|
||||
var extention = new DefaultExtension();
|
||||
''');
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, String> getHeaders() {
|
||||
return _extensionCall<Map>(
|
||||
'getHeaders(`${source.baseUrl ?? ''}`)',
|
||||
{},
|
||||
).toMapStringString!;
|
||||
}
|
||||
|
||||
@override
|
||||
bool get supportsLatest {
|
||||
return _extensionCall<bool>('supportsLatest', true);
|
||||
}
|
||||
|
||||
@override
|
||||
String get sourceBaseUrl {
|
||||
return source.baseUrl!;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MPages> getPopular(int page) async {
|
||||
return MPages.fromJson(await _extensionCallAsync('getPopular($page)'));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MPages> getLatestUpdates(int page) async {
|
||||
return MPages.fromJson(
|
||||
await _extensionCallAsync('getLatestUpdates($page)'),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MPages> search(String query, int page, List<dynamic> filters) async {
|
||||
return MPages.fromJson(
|
||||
await _extensionCallAsync(
|
||||
'search("$query",$page,${jsonEncode(filterValuesListToJson(filters))})',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MManga> getDetail(String url) async {
|
||||
return MManga.fromJson(await _extensionCallAsync('getDetail(`$url`)'));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<PageUrl>> getPageList(String url) async {
|
||||
return (await _extensionCallAsync<List>('getPageList(`$url`)'))
|
||||
.map(
|
||||
(e) => e is String
|
||||
? PageUrl(e.trim())
|
||||
: PageUrl.fromJson((e as Map).toMapStringDynamic!),
|
||||
)
|
||||
.toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<Video>> getVideoList(String url) async {
|
||||
return (await _extensionCallAsync<List>('getVideoList(`$url`)'))
|
||||
.where(
|
||||
(element) => element['url'] != null && element['originalUrl'] != null,
|
||||
)
|
||||
.map((e) => Video.fromJson(e))
|
||||
.toList()
|
||||
.toSet()
|
||||
.toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String> getHtmlContent(String name, String url) async {
|
||||
_init();
|
||||
final res = (await runtime.handlePromise(
|
||||
await runtime.evaluateAsync(
|
||||
'jsonStringify(() => extention.getHtmlContent(`$name`, `$url`))',
|
||||
),
|
||||
))
|
||||
.stringResult;
|
||||
return res;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String> cleanHtmlContent(String html) async {
|
||||
_init();
|
||||
final res = (await runtime.handlePromise(
|
||||
await runtime.evaluateAsync(
|
||||
'jsonStringify(() => extention.cleanHtmlContent(`$html`))',
|
||||
),
|
||||
))
|
||||
.stringResult;
|
||||
return res;
|
||||
}
|
||||
|
||||
@override
|
||||
FilterList getFilterList() {
|
||||
List<dynamic> list;
|
||||
|
||||
try {
|
||||
list = fromJsonFilterValuesToList(_extensionCall('getFilterList()', []));
|
||||
} catch (_) {
|
||||
list = [];
|
||||
}
|
||||
|
||||
return FilterList(list);
|
||||
}
|
||||
|
||||
@override
|
||||
List<SourcePreference> getSourcePreferences() {
|
||||
return _extensionCall(
|
||||
'getSourcePreferences()',
|
||||
[],
|
||||
).map((e) => SourcePreference.fromJson(e)..sourceId = source.id).toList();
|
||||
}
|
||||
|
||||
T _extensionCall<T>(String call, T def) {
|
||||
_init();
|
||||
|
||||
try {
|
||||
final res = runtime.evaluate('JSON.stringify(extention.$call)');
|
||||
|
||||
return jsonDecode(res.stringResult) as T;
|
||||
} catch (_) {
|
||||
if (def != null) {
|
||||
return def;
|
||||
}
|
||||
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
Future<T> _extensionCallAsync<T>(String call) async {
|
||||
_init();
|
||||
|
||||
try {
|
||||
final promised = await runtime.handlePromise(
|
||||
await runtime.evaluateAsync('jsonStringify(() => extention.$call)'),
|
||||
);
|
||||
|
||||
return jsonDecode(promised.stringResult) as T;
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
}
|
||||
241
lib/core/services/extensions/mangayomi/Eval/javascript/utils.dart
Executable file
241
lib/core/services/extensions/mangayomi/Eval/javascript/utils.dart
Executable file
@@ -0,0 +1,241 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
|
||||
import 'http.dart';
|
||||
// import 'package:epubx/epubx.dart';
|
||||
import 'package:flutter_js/flutter_js.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:http_interceptor/http/intercepted_client.dart';
|
||||
import 'package:js_packer/js_packer.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
|
||||
import '../../cryptoaes/js_unpacker.dart';
|
||||
import '../../http/m_client.dart';
|
||||
import '../dart/model/m_bridge.dart';
|
||||
|
||||
class JsUtils {
|
||||
late JavascriptRuntime runtime;
|
||||
|
||||
JsUtils(this.runtime);
|
||||
|
||||
void init() {
|
||||
InterceptedClient client() {
|
||||
return MClient.init();
|
||||
}
|
||||
|
||||
runtime.onMessage('log', (dynamic args) {
|
||||
debugPrint(args.toString());
|
||||
return null;
|
||||
});
|
||||
runtime.onMessage('cryptoHandler', (dynamic args) {
|
||||
return MBridge.cryptoHandler(args[0], args[1], args[2], args[3]);
|
||||
});
|
||||
runtime.onMessage('encryptAESCryptoJS', (dynamic args) {
|
||||
return MBridge.encryptAESCryptoJS(args[0], args[1]);
|
||||
});
|
||||
runtime.onMessage('decryptAESCryptoJS', (dynamic args) {
|
||||
return MBridge.decryptAESCryptoJS(args[0], args[1]);
|
||||
});
|
||||
runtime.onMessage('deobfuscateJsPassword', (dynamic args) {
|
||||
return MBridge.deobfuscateJsPassword(args[0]);
|
||||
});
|
||||
runtime.onMessage('unpackJsAndCombine', (dynamic args) {
|
||||
return JsUnpacker.unpackAndCombine(args[0]) ?? "";
|
||||
});
|
||||
runtime.onMessage('unpackJs', (dynamic args) {
|
||||
return JSPacker(args[0]).unpack() ?? "";
|
||||
});
|
||||
runtime.onMessage('evaluateJavascriptViaWebview', (dynamic args) async {
|
||||
return await MBridge.evaluateJavascriptViaWebview(
|
||||
args[0]!,
|
||||
(args[1]! as Map).toMapStringString!,
|
||||
(args[2]! as List).map((e) => e.toString()).toList(),
|
||||
);
|
||||
});
|
||||
// TODO resolve version issue with EpubReader
|
||||
// runtime.onMessage('parseEpub', (dynamic args) async {
|
||||
// final bytes = await _toBytesResponse(client(), "GET", args);
|
||||
// final book = await EpubReader.readBook(bytes);
|
||||
// final List<String> chapters = [];
|
||||
// for (var chapter in book.Chapters ?? []) {
|
||||
// final chapterTitle = chapter.Title;
|
||||
// chapters.add(chapterTitle);
|
||||
// }
|
||||
// return jsonEncode({
|
||||
// "title": book.Title,
|
||||
// "author": book.Author,
|
||||
// "chapters": chapters,
|
||||
// });
|
||||
// });
|
||||
// runtime.onMessage('parseEpubChapter', (dynamic args) async {
|
||||
// final bytes = await _toBytesResponse(client(), "GET", args);
|
||||
// final book = await EpubReader.readBook(bytes);
|
||||
// final chapter = book.Chapters?.where(
|
||||
// (element) => element.Title == args[3],
|
||||
// ).firstOrNull;
|
||||
// return chapter?.HtmlContent;
|
||||
// });
|
||||
|
||||
runtime.evaluate('''
|
||||
console.log = function (message) {
|
||||
if (typeof message === "object") {
|
||||
message = JSON.stringify(message);
|
||||
}
|
||||
sendMessage("log", JSON.stringify([message.toString()]));
|
||||
};
|
||||
console.warn = function (message) {
|
||||
if (typeof message === "object") {
|
||||
message = JSON.stringify(message);
|
||||
}
|
||||
sendMessage("log", JSON.stringify([message.toString()]));
|
||||
};
|
||||
console.error = function (message) {
|
||||
if (typeof message === "object") {
|
||||
message = JSON.stringify(message);
|
||||
}
|
||||
sendMessage("log", JSON.stringify([message.toString()]));
|
||||
};
|
||||
String.prototype.substringAfter = function(pattern) {
|
||||
const startIndex = this.indexOf(pattern);
|
||||
if (startIndex === -1) return this.substring(0);
|
||||
|
||||
const start = startIndex + pattern.length;
|
||||
return this.substring(start);
|
||||
}
|
||||
|
||||
String.prototype.substringAfterLast = function(pattern) {
|
||||
return this.split(pattern).pop();
|
||||
}
|
||||
|
||||
String.prototype.substringBefore = function(pattern) {
|
||||
const endIndex = this.indexOf(pattern);
|
||||
if (endIndex === -1) return this.substring(0);
|
||||
|
||||
return this.substring(0, endIndex);
|
||||
}
|
||||
|
||||
String.prototype.substringBeforeLast = function(pattern) {
|
||||
const endIndex = this.lastIndexOf(pattern);
|
||||
if (endIndex === -1) return this.substring(0);
|
||||
return this.substring(0, endIndex);
|
||||
}
|
||||
|
||||
String.prototype.substringBetween = function(left, right) {
|
||||
let startIndex = 0;
|
||||
let index = this.indexOf(left, startIndex);
|
||||
if (index === -1) return "";
|
||||
let leftIndex = index + left.length;
|
||||
let rightIndex = this.indexOf(right, leftIndex);
|
||||
if (rightIndex === -1) return "";
|
||||
startIndex = rightIndex + right.length;
|
||||
return this.substring(leftIndex, rightIndex);
|
||||
}
|
||||
|
||||
function cryptoHandler(text, iv, secretKeyString, encrypt) {
|
||||
return sendMessage(
|
||||
"cryptoHandler",
|
||||
JSON.stringify([text, iv, secretKeyString, encrypt])
|
||||
);
|
||||
}
|
||||
function encryptAESCryptoJS(plainText, passphrase) {
|
||||
return sendMessage(
|
||||
"encryptAESCryptoJS",
|
||||
JSON.stringify([plainText, passphrase])
|
||||
);
|
||||
}
|
||||
function decryptAESCryptoJS(encrypted, passphrase) {
|
||||
return sendMessage(
|
||||
"decryptAESCryptoJS",
|
||||
JSON.stringify([encrypted, passphrase])
|
||||
);
|
||||
}
|
||||
function deobfuscateJsPassword(inputString) {
|
||||
return sendMessage(
|
||||
"deobfuscateJsPassword",
|
||||
JSON.stringify([inputString])
|
||||
);
|
||||
}
|
||||
function unpackJsAndCombine(scriptBlock) {
|
||||
return sendMessage(
|
||||
"unpackJsAndCombine",
|
||||
JSON.stringify([scriptBlock])
|
||||
);
|
||||
}
|
||||
function unpackJs(packedJS) {
|
||||
return sendMessage(
|
||||
"unpackJs",
|
||||
JSON.stringify([packedJS])
|
||||
);
|
||||
}
|
||||
function parseDates(value, dateFormat, dateFormatLocale) {
|
||||
return sendMessage(
|
||||
"parseDates",
|
||||
JSON.stringify([value, dateFormat, dateFormatLocale])
|
||||
);
|
||||
}
|
||||
async function evaluateJavascriptViaWebview(url, headers, scripts) {
|
||||
return await sendMessage(
|
||||
"evaluateJavascriptViaWebview",
|
||||
JSON.stringify([url, headers, scripts])
|
||||
);
|
||||
}
|
||||
async function parseEpub(bookName, url, headers) {
|
||||
return JSON.parse(await sendMessage(
|
||||
"parseEpub",
|
||||
JSON.stringify([bookName, url, headers])
|
||||
));
|
||||
}
|
||||
async function parseEpubChapter(bookName, url, headers, chapterTitle) {
|
||||
return await sendMessage(
|
||||
"parseEpubChapter",
|
||||
JSON.stringify([bookName, url, headers, chapterTitle])
|
||||
);
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
Future<Uint8List> _toBytesResponse(
|
||||
http.Client client,
|
||||
String method,
|
||||
List args,
|
||||
) async {
|
||||
final bookName = args[0] as String;
|
||||
final url = args[1] as String;
|
||||
final headers = (args[2] as Map?)?.toMapStringString;
|
||||
final body = args.length >= 4
|
||||
? args[3] is List
|
||||
? args[3] as List
|
||||
: args[3] is String
|
||||
? args[3] as String
|
||||
: (args[3] as Map?)?.toMapStringDynamic
|
||||
: null;
|
||||
|
||||
final tmpDirectory = (await getTemporaryDirectory());
|
||||
if (Platform.isAndroid) {
|
||||
if (!(await File(p.join(tmpDirectory.path, ".nomedia")).exists())) {
|
||||
await File(p.join(tmpDirectory.path, ".nomedia")).create();
|
||||
}
|
||||
}
|
||||
final file = File(p.join(tmpDirectory.path, "$bookName.epub"));
|
||||
if (await file.exists()) {
|
||||
return await file.readAsBytes();
|
||||
}
|
||||
|
||||
var request = http.Request(method, Uri.parse(url));
|
||||
request.headers.addAll(headers ?? {});
|
||||
final future = switch (method) {
|
||||
"GET" => client.get(Uri.parse(url), headers: headers),
|
||||
"POST" => client.post(Uri.parse(url), headers: headers, body: body),
|
||||
"PUT" => client.put(Uri.parse(url), headers: headers, body: body),
|
||||
"DELETE" => client.delete(Uri.parse(url), headers: headers, body: body),
|
||||
_ => client.patch(Uri.parse(url), headers: headers, body: body),
|
||||
};
|
||||
final bytes = (await future).bodyBytes;
|
||||
await file.writeAsBytes(bytes);
|
||||
return bytes;
|
||||
}
|
||||
}
|
||||
60
lib/core/services/extensions/mangayomi/anime_extractors/dood_extractor.dart
Executable file
60
lib/core/services/extensions/mangayomi/anime_extractors/dood_extractor.dart
Executable file
@@ -0,0 +1,60 @@
|
||||
import 'dart:math';
|
||||
|
||||
import '../util/string_extensions.dart';
|
||||
import 'package:http_interceptor/http_interceptor.dart';
|
||||
|
||||
import '../Eval/dart/model/video.dart';
|
||||
import '../http/m_client.dart';
|
||||
|
||||
class DoodExtractor {
|
||||
Future<List<Video>> videosFromUrl(
|
||||
String url, {
|
||||
String? quality,
|
||||
bool redirect = true,
|
||||
}) async {
|
||||
final InterceptedClient client = MClient.init(
|
||||
reqcopyWith: {'useDartHttpClient': true},
|
||||
);
|
||||
final newQuality = quality ?? ('Doodstream ${redirect ? ' mirror' : ''}');
|
||||
|
||||
try {
|
||||
final response = await client.get(Uri.parse(url));
|
||||
final newUrl = redirect ? response.request!.url.toString() : url;
|
||||
|
||||
final doodHost = RegExp('https://(.*?)/').firstMatch(newUrl)!.group(1)!;
|
||||
final content = response.body;
|
||||
if (!content.contains("'/pass_md5/")) return [];
|
||||
final md5 = content.substringAfter("'/pass_md5/").substringBefore("',");
|
||||
final token = md5.substring(md5.lastIndexOf('/') + 1);
|
||||
final randomString = getRandomString();
|
||||
final expiry = DateTime.now().millisecondsSinceEpoch;
|
||||
final videoUrlStart = await client.get(
|
||||
Uri.parse('https://$doodHost/pass_md5/$md5'),
|
||||
headers: {'referer': newUrl},
|
||||
);
|
||||
|
||||
final videoUrl =
|
||||
'${videoUrlStart.body}$randomString?token=$token&expiry=$expiry';
|
||||
return [
|
||||
Video(
|
||||
newUrl,
|
||||
newQuality,
|
||||
videoUrl,
|
||||
headers: {'User-Agent': 'Mangayomi', 'Referer': 'https://$doodHost/'},
|
||||
),
|
||||
];
|
||||
} catch (_) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
String getRandomString({int length = 10}) {
|
||||
const allowedChars =
|
||||
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||
return List.generate(
|
||||
length,
|
||||
(index) =>
|
||||
allowedChars.runes.elementAt(Random().nextInt(allowedChars.length)),
|
||||
).join();
|
||||
}
|
||||
}
|
||||
82
lib/core/services/extensions/mangayomi/anime_extractors/filemoon.dart
Executable file
82
lib/core/services/extensions/mangayomi/anime_extractors/filemoon.dart
Executable file
@@ -0,0 +1,82 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import '../util/string_extensions.dart';
|
||||
import 'package:http_interceptor/http_interceptor.dart';
|
||||
import 'package:js_packer/js_packer.dart';
|
||||
|
||||
import '../Eval/dart/model/video.dart';
|
||||
import '../http/m_client.dart';
|
||||
import '../util/xpath_selector.dart';
|
||||
|
||||
class FilemoonExtractor {
|
||||
final InterceptedClient client = MClient.init(
|
||||
reqcopyWith: {'useDartHttpClient': true},
|
||||
);
|
||||
|
||||
Future<List<Video>> videosFromUrl(
|
||||
String url,
|
||||
String prefix,
|
||||
String suffix,
|
||||
) async {
|
||||
prefix = prefix.isEmpty ? "Filemoon " : prefix;
|
||||
try {
|
||||
final videoHeaders = {
|
||||
'Referer': url,
|
||||
'Origin': 'https://${Uri.parse(url).host}',
|
||||
};
|
||||
final response = await client.get(Uri.parse(url));
|
||||
|
||||
final jsEval = xpathSelector(
|
||||
response.body,
|
||||
).queryXPath('//script[contains(text(), "eval")]/text()').attr;
|
||||
|
||||
final unpacked = JSPacker(jsEval!).unpack() ?? "";
|
||||
|
||||
final masterUrl = unpacked.isNotEmpty
|
||||
? unpacked.substringAfter('{file:"').substringBefore('"}')
|
||||
: '';
|
||||
|
||||
if (masterUrl.isEmpty) {
|
||||
return [];
|
||||
}
|
||||
List<Track> subtitleTracks = [];
|
||||
final subUrl =
|
||||
Uri.parse(url).queryParameters["sub.info"] ??
|
||||
unpacked.substringAfter("""fetch('", """).substringBefore("""').""");
|
||||
if (subUrl.isNotEmpty) {
|
||||
try {
|
||||
final subResponse = await client.get(
|
||||
Uri.parse(subUrl),
|
||||
headers: videoHeaders,
|
||||
);
|
||||
final subList = jsonDecode(subResponse.body) as List;
|
||||
for (var item in subList) {
|
||||
subtitleTracks.add(Track(file: item["file"], label: item["label"]));
|
||||
}
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
final masterPlaylistResponse = await client.get(Uri.parse(masterUrl));
|
||||
final masterPlaylist = masterPlaylistResponse.body;
|
||||
|
||||
const separator = '#EXT-X-STREAM-INF:';
|
||||
final playlists = masterPlaylist.split(separator).sublist(1);
|
||||
|
||||
return playlists.map((playlist) {
|
||||
final resolution =
|
||||
'${playlist.substringAfter('RESOLUTION=').substringAfter('x').substringBefore(',').trim()}p';
|
||||
final videoUrl = playlist.split('\n')[1].trim();
|
||||
|
||||
return Video(
|
||||
videoUrl,
|
||||
"$prefix - $resolution $suffix",
|
||||
videoUrl,
|
||||
headers: videoHeaders,
|
||||
subtitles: subtitleTracks,
|
||||
);
|
||||
}).toList();
|
||||
} catch (_) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
131
lib/core/services/extensions/mangayomi/anime_extractors/gogocdn_extractor.dart
Executable file
131
lib/core/services/extensions/mangayomi/anime_extractors/gogocdn_extractor.dart
Executable file
@@ -0,0 +1,131 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import '../util/string_extensions.dart';
|
||||
import 'package:html/dom.dart';
|
||||
import 'package:html/parser.dart' as parser;
|
||||
import 'package:http_interceptor/http_interceptor.dart';
|
||||
|
||||
import '../Eval/dart/model/m_bridge.dart';
|
||||
import '../Eval/dart/model/video.dart';
|
||||
import '../http/m_client.dart';
|
||||
|
||||
class GogoCdnExtractor {
|
||||
final InterceptedClient client = MClient.init(
|
||||
reqcopyWith: {'useDartHttpClient': true},
|
||||
);
|
||||
final JsonCodec json = const JsonCodec();
|
||||
|
||||
Future<List<Video>> videosFromUrl(String serverUrl) async {
|
||||
try {
|
||||
final response = await client.get(Uri.parse(serverUrl));
|
||||
final document = response.body;
|
||||
|
||||
Document parsedResponse = parser.parse(response.body);
|
||||
final iv = parsedResponse
|
||||
.querySelector('div.wrapper')!
|
||||
.attributes["class"]!
|
||||
.split('container-')
|
||||
.last;
|
||||
|
||||
final secretKey = parsedResponse
|
||||
.querySelector('body[class]')!
|
||||
.attributes["class"]!
|
||||
.split('container-')
|
||||
.last;
|
||||
RegExp(r'container-(\d+)').firstMatch(document)?.group(1);
|
||||
final decryptionKey = RegExp(
|
||||
r'videocontent-(\d+)',
|
||||
).firstMatch(document)?.group(1);
|
||||
final encryptAjaxParams = MBridge.cryptoHandler(
|
||||
RegExp(r'data-value="([^"]+)').firstMatch(document)?.group(1) ?? "",
|
||||
iv,
|
||||
secretKey,
|
||||
false,
|
||||
).substringAfter("&");
|
||||
|
||||
final httpUrl = Uri.parse(serverUrl);
|
||||
final host = "https://${httpUrl.host}/";
|
||||
final id = httpUrl.queryParameters['id'];
|
||||
final encryptedId = MBridge.cryptoHandler(id ?? "", iv, secretKey, true);
|
||||
|
||||
final token = httpUrl.queryParameters['token'];
|
||||
final qualityPrefix = token != null ? "Gogostream - " : "Vidstreaming - ";
|
||||
|
||||
final encryptAjaxUrl =
|
||||
"${host}encrypt-ajax.php?id=$encryptedId&$encryptAjaxParams&alias=$id";
|
||||
|
||||
final encryptAjaxResponse = await client.get(
|
||||
Uri.parse(encryptAjaxUrl),
|
||||
headers: {"X-Requested-With": "XMLHttpRequest"},
|
||||
);
|
||||
final jsonResponse = encryptAjaxResponse.body;
|
||||
final data = json.decode(jsonResponse)["data"];
|
||||
final decryptedData = MBridge.cryptoHandler(
|
||||
data ?? "",
|
||||
iv,
|
||||
decryptionKey!,
|
||||
false,
|
||||
);
|
||||
final videoList = <Video>[];
|
||||
final autoList = <Video>[];
|
||||
final array = json.decode(decryptedData)["source"];
|
||||
if (array != null &&
|
||||
array is List &&
|
||||
array.length == 1 &&
|
||||
array[0]["type"] == "hls") {
|
||||
final fileURL = array[0]["file"].toString().trim();
|
||||
const separator = "#EXT-X-STREAM-INF:";
|
||||
final masterPlaylistResponse = await client.get(Uri.parse(fileURL));
|
||||
final masterPlaylist = masterPlaylistResponse.body;
|
||||
if (masterPlaylist.contains(separator)) {
|
||||
for (var it
|
||||
in masterPlaylist.substringAfter(separator).split(separator)) {
|
||||
final quality =
|
||||
"${it.substringAfter("RESOLUTION=").substringAfter("x").substringBefore(",").substringBefore("\n")}p";
|
||||
|
||||
var videoUrl = it.substringAfter("\n").substringBefore("\n");
|
||||
|
||||
if (!videoUrl.startsWith("http")) {
|
||||
videoUrl =
|
||||
"${fileURL.split("/").sublist(0, fileURL.split("/").length - 1).join("/")}/$videoUrl";
|
||||
}
|
||||
videoList.add(Video(videoUrl, "$qualityPrefix$quality", videoUrl));
|
||||
}
|
||||
} else {
|
||||
videoList.add(Video(fileURL, "${qualityPrefix}Original", fileURL));
|
||||
}
|
||||
} else if (array != null && array is List) {
|
||||
for (var it in array) {
|
||||
final label = it["label"].toString().toLowerCase().trim().replaceAll(
|
||||
" ",
|
||||
"",
|
||||
);
|
||||
final fileURL = it["file"].toString().trim();
|
||||
final videoHeaders = {"Referer": serverUrl};
|
||||
if (label == "auto") {
|
||||
autoList.add(
|
||||
Video(
|
||||
fileURL,
|
||||
"$qualityPrefix$label",
|
||||
fileURL,
|
||||
headers: videoHeaders,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
videoList.add(
|
||||
Video(
|
||||
fileURL,
|
||||
"$qualityPrefix$label",
|
||||
fileURL,
|
||||
headers: videoHeaders,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
return videoList + autoList;
|
||||
} catch (e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
import '../util/string_extensions.dart';
|
||||
import 'package:http_interceptor/http_interceptor.dart';
|
||||
import 'package:js_packer/js_packer.dart';
|
||||
|
||||
import '../Eval/dart/model/video.dart';
|
||||
import '../http/m_client.dart';
|
||||
import '../util/xpath_selector.dart';
|
||||
|
||||
class Mp4uploadExtractor {
|
||||
static final RegExp qualityRegex = RegExp(r'\WHEIGHT=(\d+)');
|
||||
static const String referer = "https://mp4upload.com/";
|
||||
final InterceptedClient client = MClient.init(
|
||||
reqcopyWith: {'useDartHttpClient': true},
|
||||
);
|
||||
Future<List<Video>> videosFromUrl(
|
||||
String url,
|
||||
Map<String, String> headers, {
|
||||
String prefix = '',
|
||||
String suffix = '',
|
||||
}) async {
|
||||
final newHeaders = Map<String, String>.from(headers)
|
||||
..addAll({'referer': referer});
|
||||
try {
|
||||
final response = await client.get(Uri.parse(url), headers: newHeaders);
|
||||
String script = "";
|
||||
|
||||
final scriptElementWithEval = xpathSelector(response.body)
|
||||
.queryXPath(
|
||||
'//script[contains(text(), "eval") and contains(text(), "p,a,c,k,e,d")]/text()',
|
||||
)
|
||||
.attrs;
|
||||
|
||||
if (scriptElementWithEval.isNotEmpty) {
|
||||
script = JSPacker(script).unpack() ?? "";
|
||||
} else {
|
||||
final scriptElementWithSrc = xpathSelector(
|
||||
response.body,
|
||||
).queryXPath('//script[contains(text(), "player.src")]/text()').attrs;
|
||||
if (scriptElementWithSrc.isNotEmpty) {
|
||||
script = scriptElementWithSrc.first!;
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
final videoUrl = script
|
||||
.substringAfter('.src(')
|
||||
.substringBefore(')')
|
||||
.substringAfter('src:')
|
||||
.substringAfter('"')
|
||||
.substringBefore('"');
|
||||
final resolutionMatch = qualityRegex.firstMatch(script);
|
||||
final resolution = resolutionMatch?.group(1) ?? 'Unknown resolution';
|
||||
final quality = '$prefix Mp4Upload - ${resolution}p $suffix';
|
||||
|
||||
return [Video(videoUrl, quality, videoUrl, headers: newHeaders)];
|
||||
} catch (_) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
import 'dart:async';
|
||||
|
||||
import '../util/string_extensions.dart';
|
||||
import 'package:http_interceptor/http_interceptor.dart';
|
||||
|
||||
import '../Eval/dart/model/video.dart';
|
||||
import '../http/m_client.dart';
|
||||
|
||||
class MyStreamExtractor {
|
||||
Future<List<Video>> videosFromUrl(
|
||||
String url,
|
||||
Map<String, String> headers,
|
||||
) async {
|
||||
final host = url.substringBefore("/watch");
|
||||
|
||||
final InterceptedClient client = MClient.init(
|
||||
reqcopyWith: {'useDartHttpClient': true},
|
||||
);
|
||||
|
||||
try {
|
||||
final response = await client.get(Uri.parse(url), headers: headers);
|
||||
|
||||
final document = response.body;
|
||||
final streamCode = document
|
||||
.substringAfter("${url.substringAfter("?v=")}\", \"")
|
||||
.substringBefore("\",null,null");
|
||||
final streamUrl = "$host/m3u8/$streamCode/master.txt?s=1&cache=1";
|
||||
|
||||
final cookie = response.headers.entries
|
||||
.firstWhere(
|
||||
(entry) =>
|
||||
entry.key.toLowerCase() == "set-cookie" &&
|
||||
entry.value.startsWith("PHPSESSID", 0),
|
||||
orElse: () => const MapEntry("set-cookie", ""),
|
||||
)
|
||||
.value
|
||||
.split(";")
|
||||
.first;
|
||||
|
||||
final newHeaders = {...headers, "cookie": cookie, "accept": "*/*"};
|
||||
|
||||
final masterPlaylistResponse = await client.get(
|
||||
Uri.parse(streamUrl),
|
||||
headers: newHeaders,
|
||||
);
|
||||
final masterPlaylist = masterPlaylistResponse.body;
|
||||
|
||||
const separator = "#EXT-X-STREAM-INF";
|
||||
return masterPlaylist.substringAfter(separator).split(separator).map((
|
||||
it,
|
||||
) {
|
||||
final resolution =
|
||||
"${it.substringAfter("RESOLUTION=").substringBefore("\n").substringAfter("x").substringBefore(",")}p";
|
||||
final quality = "MyStream - $resolution";
|
||||
final videoUrl = it.substringAfter("\n").substringBefore("\n");
|
||||
return Video(videoUrl, quality, videoUrl, headers: newHeaders);
|
||||
}).toList();
|
||||
} catch (_) {
|
||||
return [];
|
||||
} finally {
|
||||
client.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
44
lib/core/services/extensions/mangayomi/anime_extractors/mytv_extractor.dart
Executable file
44
lib/core/services/extensions/mangayomi/anime_extractors/mytv_extractor.dart
Executable file
@@ -0,0 +1,44 @@
|
||||
import '../util/string_extensions.dart';
|
||||
import 'package:html/parser.dart' show parse;
|
||||
import 'package:http_interceptor/http_interceptor.dart';
|
||||
|
||||
import '../Eval/dart/model/video.dart';
|
||||
import '../http/m_client.dart';
|
||||
|
||||
class MytvExtractor {
|
||||
final InterceptedClient client = MClient.init(
|
||||
reqcopyWith: {'useDartHttpClient': true},
|
||||
);
|
||||
|
||||
Future<List<Video>> videosFromUrl(String url) async {
|
||||
try {
|
||||
final response = await client.get(Uri.parse(url));
|
||||
final document = parse(response.body);
|
||||
final videoList = <Video>[];
|
||||
|
||||
document.querySelectorAll("script").forEach((script) {
|
||||
if (script.text.contains("CreatePlayer(\"v")) {
|
||||
final videosString = script.text;
|
||||
final videoUrl = videosString
|
||||
.substringAfter("\"v=")
|
||||
.substringBefore("\\u0026tp=video")
|
||||
.replaceAll("%26", "&")
|
||||
.replaceAll("%3a", ":")
|
||||
.replaceAll("%2f", "/")
|
||||
.replaceAll("%3f", "?")
|
||||
.replaceAll("%3d", "=");
|
||||
|
||||
if (!videoUrl.contains("https:")) {
|
||||
videoList.add(Video(videoUrl, "Stream", videoUrl));
|
||||
} else {
|
||||
videoList.add(Video(videoUrl, "Mytv", videoUrl));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return videoList;
|
||||
} catch (_) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
61
lib/core/services/extensions/mangayomi/anime_extractors/okru_extractor.dart
Executable file
61
lib/core/services/extensions/mangayomi/anime_extractors/okru_extractor.dart
Executable file
@@ -0,0 +1,61 @@
|
||||
import '../util/dom_extensions.dart';
|
||||
import '../util/string_extensions.dart';
|
||||
import 'package:html/parser.dart' show parse;
|
||||
import 'package:http_interceptor/http_interceptor.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
|
||||
import '../Eval/dart/model/video.dart';
|
||||
import '../http/m_client.dart';
|
||||
|
||||
class OkruExtractor {
|
||||
final InterceptedClient client = MClient.init(
|
||||
reqcopyWith: {'useDartHttpClient': true},
|
||||
);
|
||||
|
||||
Future<List<Video>> videosFromUrl(
|
||||
String url, {
|
||||
String prefix = "",
|
||||
bool fixQualities = true,
|
||||
}) async {
|
||||
final response = await client.get(Uri.parse(url));
|
||||
final document = parse(response.body);
|
||||
final videoString = document
|
||||
.selectFirst('div[data-options]')
|
||||
?.attr("data-options");
|
||||
|
||||
if (videoString == null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (videoString.contains('ondemandHls')) {
|
||||
final playlistUrl = Uri.parse(
|
||||
videoString
|
||||
.substringAfter("ondemandHls\\\":\\\"")
|
||||
.substringBefore("\\\"")
|
||||
.replaceAll("\\\\u0026", "&"),
|
||||
);
|
||||
|
||||
final masterPlaylistResponse = await client.get(playlistUrl);
|
||||
final masterPlaylist = masterPlaylistResponse.body;
|
||||
|
||||
const separator = "#EXT-X-STREAM-INF";
|
||||
return masterPlaylist.substringAfter(separator).split(separator).map((
|
||||
it,
|
||||
) {
|
||||
final resolution =
|
||||
"${it.substringAfter("RESOLUTION=").substringBefore("\n").substringAfter("x").substringBefore(",")}p";
|
||||
final m3u8Host =
|
||||
"${playlistUrl.scheme}://${playlistUrl.host}${path.dirname(playlistUrl.path)}";
|
||||
final videoUrl =
|
||||
"$m3u8Host/${it.substringAfter("\n").substringBefore("\n")}";
|
||||
return Video(
|
||||
videoUrl,
|
||||
"${prefix.isNotEmpty ? prefix : ""}Okru:$resolution",
|
||||
videoUrl,
|
||||
);
|
||||
}).toList();
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
||||
697
lib/core/services/extensions/mangayomi/anime_extractors/quarkuc_extractor.dart
Executable file
697
lib/core/services/extensions/mangayomi/anime_extractors/quarkuc_extractor.dart
Executable file
@@ -0,0 +1,697 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:http_interceptor/http_interceptor.dart';
|
||||
|
||||
import '../Eval/dart/model/video.dart';
|
||||
import '../http/m_client.dart';
|
||||
|
||||
enum CloudDriveType { quark, uc }
|
||||
|
||||
class QuarkUcExtractor {
|
||||
late CloudDriveType cloudDriveType;
|
||||
String apiUrl = "";
|
||||
// String cookie = "";
|
||||
String refererUrl = "";
|
||||
String ua = "";
|
||||
String host = "";
|
||||
Map<String, dynamic> shareTokenCache = {};
|
||||
String pr = "";
|
||||
final List<String> subtitleExts = ['.srt', '.ass', '.scc', '.stl', '.ttml'];
|
||||
Map<String, String> saveFileIdCaches = {};
|
||||
String? saveDirId;
|
||||
final String saveDirName = 'TV';
|
||||
String _lastCookie = "";
|
||||
|
||||
Future<void> initCloudDrive(
|
||||
String cookie,
|
||||
CloudDriveType cloudDriveType,
|
||||
) async {
|
||||
this.cloudDriveType = cloudDriveType;
|
||||
if (cloudDriveType == CloudDriveType.quark) {
|
||||
apiUrl = "https://drive-pc.quark.cn/1/clouddrive/";
|
||||
pr = "pr=ucpro&fr=pc";
|
||||
ua =
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) quark-cloud-drive/2.5.20 Chrome/100.0.4896.160 Electron/18.3.5.4-b478491100 Safari/537.36 Channel/pckk_other_ch";
|
||||
refererUrl = "https://pan.quark.cn/";
|
||||
host = "https://quark.cn";
|
||||
_lastCookie = "https://quarkcookie.last";
|
||||
} else {
|
||||
apiUrl = "https://pc-api.uc.cn/1/clouddrive/";
|
||||
pr = "pr=UCBrowser&fr=pc";
|
||||
ua =
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) uc-cloud-drive/2.5.20 Chrome/100.0.4896.160 Electron/18.3.5.4-b478491100 Safari/537.36 Channel/pckk_other_ch";
|
||||
refererUrl = "https://drive.uc.cn/";
|
||||
host = "https://uc.cn";
|
||||
_lastCookie = "https://uccookie.last";
|
||||
}
|
||||
if (cookie.isNotEmpty && getLastCookie() != cookie) {
|
||||
MClient.setCookie(host, ua, null, cookie: cookie);
|
||||
MClient.setCookie(_lastCookie, ua, null, cookie: cookie);
|
||||
}
|
||||
}
|
||||
|
||||
Future<String> getLastCookie() async {
|
||||
var cookie = await MClient.getCookiesPref(_lastCookie);
|
||||
return cookie.isNotEmpty ? cookie.values.first : "";
|
||||
}
|
||||
|
||||
Future<String> getCurrentCookie() async {
|
||||
var cookie = await MClient.getCookiesPref(host);
|
||||
return cookie.isNotEmpty ? cookie.values.first : "";
|
||||
}
|
||||
|
||||
Future<Map<String, String>> getHeaders() async{
|
||||
return {
|
||||
'User-Agent': ua,
|
||||
'Referer': refererUrl,
|
||||
"Content-Type": "application/json",
|
||||
"Cookie": await getCurrentCookie(),
|
||||
};
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> api(
|
||||
String url,
|
||||
dynamic data,
|
||||
String method,
|
||||
) async {
|
||||
InterceptedClient client = MClient.init(
|
||||
reqcopyWith: {'useDartHttpClient': true},
|
||||
);
|
||||
late Response resp;
|
||||
if (method != "get") {
|
||||
resp = await client.post(
|
||||
Uri.parse(apiUrl + url),
|
||||
body: jsonEncode(data),
|
||||
headers: await getHeaders(),
|
||||
);
|
||||
} else {
|
||||
resp = await client.get(Uri.parse(apiUrl + url), headers: await getHeaders());
|
||||
}
|
||||
// if (resp.headers['set-cookie'] != null) {
|
||||
// print('headers: ${resp.headers}');
|
||||
// final puus = resp.headers['set-cookie']!
|
||||
// .split(';;;')
|
||||
// .join()
|
||||
// .split(';')
|
||||
// .firstWhere((element) => element.startsWith('__puus='),
|
||||
// orElse: () => '');
|
||||
// if (puus.isNotEmpty) {
|
||||
// final newPuus = puus.split('=')[1];
|
||||
// var cookie = getCurrentCookie();
|
||||
// if (cookie != null && cookie.contains('__puus=')) {
|
||||
// cookie =
|
||||
// cookie.replaceFirst(RegExp(r'__puus=[^;]+'), '__puus=$newPuus');
|
||||
// }
|
||||
// MClient.setCookie(host, ua, cookie: cookie);
|
||||
// }
|
||||
// }
|
||||
// 处理 set-cookie
|
||||
if (resp.headers['set-cookie'] != null) {
|
||||
final cookies = resp.headers['set-cookie']!.split(';;;');
|
||||
for (var cookie in cookies) {
|
||||
if (cookie.contains('__puus=')) {
|
||||
final newPuus = cookie.split(';')[0]; // 获取新的 __puus
|
||||
var currentCookie = await getCurrentCookie();
|
||||
if (currentCookie.isNotEmpty) {
|
||||
// 更新 __puus
|
||||
if (currentCookie.contains('__puus=')) {
|
||||
currentCookie = currentCookie.replaceFirst(
|
||||
RegExp(r'__puus=[^;]+'),
|
||||
newPuus,
|
||||
);
|
||||
} else {
|
||||
currentCookie = '$currentCookie; $newPuus';
|
||||
}
|
||||
MClient.setCookie(host, ua, null, cookie: currentCookie);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return jsonDecode(resp.body);
|
||||
}
|
||||
|
||||
Map<String, String>? getShareData(String url) {
|
||||
RegExp regex;
|
||||
if (cloudDriveType == CloudDriveType.quark) {
|
||||
regex = RegExp(r'https://pan\.quark\.cn/s/([^\\|#/]+)');
|
||||
} else {
|
||||
regex = RegExp(r'https://drive\.uc\.cn/s/([^?]+)');
|
||||
}
|
||||
final matches = regex.firstMatch(url);
|
||||
if (matches != null) {
|
||||
return {'shareId': matches.group(1)!, 'folderId': '0'};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<void> getShareToken(Map<String, String> shareData) async {
|
||||
if (!shareTokenCache.containsKey(shareData['shareId'])) {
|
||||
shareTokenCache.remove(shareData['shareId']);
|
||||
final shareToken = await api('share/sharepage/token?$pr', {
|
||||
'pwd_id': shareData['shareId'],
|
||||
'passcode': shareData['sharePwd'] ?? '',
|
||||
}, 'post');
|
||||
if (shareToken['data'] != null && shareToken['data']['stoken'] != null) {
|
||||
shareTokenCache[shareData['shareId']!] = shareToken['data'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<dynamic>> listFile(
|
||||
int shareIndex,
|
||||
Map<String, String> shareData,
|
||||
List<dynamic> videos,
|
||||
List<dynamic> subtitles,
|
||||
String shareId,
|
||||
String folderId, {
|
||||
int page = 1,
|
||||
}) async {
|
||||
const int prePage = 200;
|
||||
final listData = await api(
|
||||
'share/sharepage/detail?$pr&pwd_id=$shareId&stoken=${Uri.encodeComponent(shareTokenCache[shareId]['stoken'])}&pdir_fid=$folderId&force=0&_page=$page&_size=$prePage&_sort=file_type:asc,file_name:desc',
|
||||
null,
|
||||
'get',
|
||||
);
|
||||
if (listData['data'] == null) return [];
|
||||
final items = listData['data']['list'];
|
||||
if (items == null) return [];
|
||||
final subDir = [];
|
||||
for (final item in items) {
|
||||
if (item['dir'] == true) {
|
||||
subDir.add(item);
|
||||
} else if (item['file'] == true && item['obj_category'] == 'video') {
|
||||
if (item['size'] < 1024 * 1024 * 5) continue;
|
||||
item['stoken'] = shareTokenCache[shareData['shareId']]['stoken'];
|
||||
videos.add(
|
||||
Item.objectFrom(
|
||||
item,
|
||||
shareData['shareId']!,
|
||||
shareIndex,
|
||||
cloudDriveType,
|
||||
),
|
||||
);
|
||||
} else if (item['type'] == 'file' &&
|
||||
subtitleExts.any((x) => item['file_name'].endsWith(x))) {
|
||||
subtitles.add(
|
||||
Item.objectFrom(
|
||||
item,
|
||||
shareData['shareId']!,
|
||||
shareIndex,
|
||||
cloudDriveType,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
if (page < (listData['metadata']['_total'] / prePage).ceil()) {
|
||||
final nextItems = await listFile(
|
||||
shareIndex,
|
||||
shareData,
|
||||
videos,
|
||||
subtitles,
|
||||
shareId,
|
||||
folderId,
|
||||
page: page + 1,
|
||||
);
|
||||
items.addAll(nextItems);
|
||||
}
|
||||
for (final dir in subDir) {
|
||||
final subItems = await listFile(
|
||||
shareIndex,
|
||||
shareData,
|
||||
videos,
|
||||
subtitles,
|
||||
shareId,
|
||||
dir['fid'],
|
||||
);
|
||||
items.addAll(subItems);
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
Map<String, dynamic> findBestLCS(Item mainItem, List<Item> targetItems) {
|
||||
final results = [];
|
||||
var bestMatchIndex = 0;
|
||||
for (var i = 0; i < targetItems.length; i++) {
|
||||
final currentLCS = lcs(mainItem.name, targetItems[i].name);
|
||||
results.add({'target': targetItems[i], 'lcs': currentLCS});
|
||||
if (currentLCS['length'] > results[bestMatchIndex]['lcs']['length']) {
|
||||
bestMatchIndex = i;
|
||||
}
|
||||
}
|
||||
final bestMatch = results[bestMatchIndex];
|
||||
return {
|
||||
'allLCS': results,
|
||||
'bestMatch': bestMatch,
|
||||
'bestMatchIndex': bestMatchIndex,
|
||||
};
|
||||
}
|
||||
|
||||
Future<void> getFilesByShareUrl(
|
||||
int shareIndex,
|
||||
dynamic shareInfo,
|
||||
List<dynamic> videos,
|
||||
List<dynamic> subtitles,
|
||||
) async {
|
||||
final shareData = shareInfo is String ? getShareData(shareInfo) : shareInfo;
|
||||
if (shareData == null) return;
|
||||
await getShareToken(shareData);
|
||||
if (!shareTokenCache.containsKey(shareData['shareId'])) return;
|
||||
await listFile(
|
||||
shareIndex,
|
||||
shareData,
|
||||
videos,
|
||||
subtitles,
|
||||
shareData['shareId']!,
|
||||
shareData['folderId']!,
|
||||
);
|
||||
if (subtitles.isNotEmpty) {
|
||||
for (var item in videos) {
|
||||
var matchSubtitle = findBestLCS(item, subtitles as List<Item>);
|
||||
if (matchSubtitle['bestMatch'] != null) {
|
||||
item.subtitle = matchSubtitle['bestMatch']['target'];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void clean() {
|
||||
saveFileIdCaches.clear();
|
||||
}
|
||||
|
||||
Future<void> clearSaveDir() async {
|
||||
final listData = await api(
|
||||
'file/sort?$pr&pdir_fid=$saveDirId&_page=1&_size=200&_sort=file_type:asc,updated_at:desc',
|
||||
{},
|
||||
'get',
|
||||
);
|
||||
if (listData['data'] != null &&
|
||||
listData['data']['list'] != null &&
|
||||
listData['data']['list'].isNotEmpty) {
|
||||
await api('file/delete?$pr', {
|
||||
'action_type': 2,
|
||||
'filelist': listData['data']['list'].map((v) => v['fid']).toList(),
|
||||
'exclude_fids': [],
|
||||
}, 'post');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> createSaveDir(bool clean) async {
|
||||
if (saveDirId != null) {
|
||||
if (clean) await clearSaveDir();
|
||||
return;
|
||||
}
|
||||
final listData = await api(
|
||||
'file/sort?$pr&pdir_fid=0&_page=1&_size=200&_sort=file_type:asc,updated_at:desc',
|
||||
{},
|
||||
'get',
|
||||
);
|
||||
if (listData['data'] != null && listData['data']['list'] != null) {
|
||||
for (final item in listData['data']['list']) {
|
||||
if (item['file_name'] == saveDirName) {
|
||||
saveDirId = item['fid'];
|
||||
await clearSaveDir();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (saveDirId == null) {
|
||||
final create = await api('file?$pr', {
|
||||
'pdir_fid': '0',
|
||||
'file_name': saveDirName,
|
||||
'dir_path': '',
|
||||
'dir_init_lock': false,
|
||||
}, 'post');
|
||||
if (create['data'] != null && create['data']['fid'] != null) {
|
||||
saveDirId = create['data']['fid'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<String?> save(
|
||||
String shareId,
|
||||
String stoken,
|
||||
String fileId,
|
||||
String fileToken,
|
||||
bool clean,
|
||||
) async {
|
||||
await createSaveDir(clean);
|
||||
if (clean) {
|
||||
this.clean();
|
||||
}
|
||||
if (saveDirId == null) return null;
|
||||
if (stoken.isEmpty) {
|
||||
await getShareToken({'shareId': shareId});
|
||||
if (!shareTokenCache.containsKey(shareId)) return null;
|
||||
}
|
||||
final saveResult = await api('share/sharepage/save?$pr', {
|
||||
'fid_list': [fileId],
|
||||
'fid_token_list': [fileToken],
|
||||
'to_pdir_fid': saveDirId,
|
||||
'pwd_id': shareId,
|
||||
'stoken': stoken.isNotEmpty ? stoken : shareTokenCache[shareId]['stoken'],
|
||||
'pdir_fid': '0',
|
||||
'scene': 'link',
|
||||
}, 'post');
|
||||
if (saveResult['data'] != null && saveResult['data']['task_id'] != null) {
|
||||
var retry = 0;
|
||||
while (true) {
|
||||
final taskResult = await api(
|
||||
'task?$pr&task_id=${saveResult['data']['task_id']}&retry_index=$retry',
|
||||
{},
|
||||
'get',
|
||||
);
|
||||
if (taskResult['data'] != null &&
|
||||
taskResult['data']['save_as'] != null &&
|
||||
taskResult['data']['save_as']['save_as_top_fids'] != null &&
|
||||
taskResult['data']['save_as']['save_as_top_fids'].isNotEmpty) {
|
||||
return taskResult['data']['save_as']['save_as_top_fids'][0];
|
||||
}
|
||||
retry++;
|
||||
if (retry > 2) break;
|
||||
await Future.delayed(const Duration(seconds: 1));
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<List<Map<String, String>>?> getLiveTranscoding(
|
||||
String shareId,
|
||||
String stoken,
|
||||
String fileId,
|
||||
String fileToken,
|
||||
) async {
|
||||
if (!saveFileIdCaches.containsKey(fileId)) {
|
||||
final saveFileId = await save(shareId, stoken, fileId, fileToken, true);
|
||||
if (saveFileId == null) return null;
|
||||
saveFileIdCaches[fileId] = saveFileId;
|
||||
}
|
||||
final transcoding = await api('file/v2/play?$pr', {
|
||||
'fid': saveFileIdCaches[fileId],
|
||||
'resolutions': 'normal,low,high,super,2k,4k',
|
||||
'supports': 'fmp4',
|
||||
}, 'post');
|
||||
if (transcoding['data'] != null &&
|
||||
transcoding['data']['video_list'] != null) {
|
||||
List<Map<String, String>> qualityOptions = [];
|
||||
for (final video in transcoding['data']['video_list']) {
|
||||
qualityOptions.add({
|
||||
'url': video['video_info']['url'],
|
||||
'quality': video['resolution'],
|
||||
});
|
||||
}
|
||||
return qualityOptions;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>?> getDownload(
|
||||
String shareId,
|
||||
String stoken,
|
||||
String fileId,
|
||||
String fileToken,
|
||||
bool clean,
|
||||
) async {
|
||||
if (!saveFileIdCaches.containsKey(fileId)) {
|
||||
final saveFileId = await save(shareId, stoken, fileId, fileToken, clean);
|
||||
if (saveFileId == null) return null;
|
||||
saveFileIdCaches[fileId] = saveFileId;
|
||||
}
|
||||
final down = await api('file/download?$pr&uc_param_str=', {
|
||||
'fids': [saveFileIdCaches[fileId]],
|
||||
}, 'post');
|
||||
if (down['data'] != null) {
|
||||
return down['data'][0];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<List<Map<String, String>>> videoFilesFromUrl(
|
||||
List<String> shareUrlList, {
|
||||
String typeName = "电影",
|
||||
}) async {
|
||||
List<dynamic> videoItems = [];
|
||||
List<dynamic> subItems = [];
|
||||
|
||||
for (int i = 0; i < shareUrlList.length; i++) {
|
||||
String shareUrl = shareUrlList[i];
|
||||
await getFilesByShareUrl(i + 1, shareUrl, videoItems, subItems);
|
||||
}
|
||||
|
||||
return await getVodFile(videoItems, subItems, typeName);
|
||||
}
|
||||
|
||||
Future<List<Map<String, String>>> getVodFile(
|
||||
List<dynamic> videoItemList,
|
||||
List<dynamic> subItemList,
|
||||
String typeName,
|
||||
) async {
|
||||
if (videoItemList.isEmpty) {
|
||||
return [];
|
||||
}
|
||||
List<Map<String, String>> vodItems = [];
|
||||
for (var videoItem in videoItemList) {
|
||||
String episodeUrl = videoItem.getEpisodeUrl(typeName);
|
||||
String subtitles = findSubs(videoItem.getName(), subItemList);
|
||||
String fullUrl = episodeUrl + subtitles;
|
||||
List<String> parts = fullUrl.split('\$');
|
||||
String name = parts[0].trim();
|
||||
String url = parts[1];
|
||||
vodItems.add({"name": name, "url": url});
|
||||
}
|
||||
// print(vodItems);
|
||||
return vodItems;
|
||||
}
|
||||
|
||||
Future<List<Video>> videosFromUrl(String url) async {
|
||||
List<String> parts = url.split('++');
|
||||
String fileId = parts[1];
|
||||
String fileToken = parts[2];
|
||||
String shareId = parts[3];
|
||||
String stoken = parts[4];
|
||||
// String type = parts[0];
|
||||
List<String> subtitleParts = parts.length > 5 ? parts[5].split('+') : [];
|
||||
// 获取可用的质量列表
|
||||
//List<String> qualities = getPlayFormtList();
|
||||
List<Video> videos = [];
|
||||
|
||||
String? originalUrl;
|
||||
List<Map<String, String>>? qualityOptions = await getLiveTranscoding(
|
||||
shareId,
|
||||
stoken,
|
||||
fileId,
|
||||
fileToken,
|
||||
);
|
||||
originalUrl = qualityOptions?[0]['url'];
|
||||
var headers = await getHeaders();
|
||||
headers.remove('Content-Type');
|
||||
if (qualityOptions != null) {
|
||||
for (Map<String, String> qualityOption in qualityOptions) {
|
||||
videos.add(
|
||||
Video(
|
||||
qualityOption['url'] ?? '',
|
||||
qualityOption['quality'] ?? '',
|
||||
originalUrl ?? '',
|
||||
headers: headers,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 处理字幕
|
||||
List<Track> subtitles = [];
|
||||
for (String subtitleInfo in subtitleParts) {
|
||||
if (subtitleInfo.isNotEmpty) {
|
||||
List<String> subParts = subtitleInfo.split('@@@');
|
||||
if (subParts.length == 3) {
|
||||
String subName = subParts[0];
|
||||
String subFileId = subParts[2];
|
||||
var subDownload = await getDownload(
|
||||
shareId,
|
||||
stoken,
|
||||
subFileId,
|
||||
'',
|
||||
false,
|
||||
);
|
||||
String? subUrl = subDownload?['download_url'];
|
||||
if (subUrl != null) {
|
||||
subtitles.add(Track(file: subUrl, label: subName));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 为所有视频添加字幕
|
||||
for (var video in videos) {
|
||||
video.subtitles = subtitles;
|
||||
}
|
||||
return videos;
|
||||
}
|
||||
|
||||
String findSubs(String name, List<dynamic> itemList) {
|
||||
List<dynamic> subItemList = [];
|
||||
pair(removeExt(name).toLowerCase(), itemList, subItemList);
|
||||
if (subItemList.isEmpty) {
|
||||
subItemList.addAll(itemList);
|
||||
}
|
||||
String subStr = "";
|
||||
for (var item in subItemList) {
|
||||
subStr +=
|
||||
"+${removeExt(item.getName())}@@@${item.getFileExtension()}@@@${item.getFileId()}";
|
||||
}
|
||||
return subStr;
|
||||
}
|
||||
|
||||
void pair(String name, List<dynamic> itemList, List<dynamic> subItemList) {
|
||||
for (var item in itemList) {
|
||||
final subName = removeExt(item.getName()).toLowerCase();
|
||||
if (name.contains(subName) || subName.contains(name)) {
|
||||
subItemList.add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String removeExt(String text) {
|
||||
return text.contains('.') ? text.substring(0, text.lastIndexOf(".")) : text;
|
||||
}
|
||||
|
||||
Map<String, dynamic> lcs(String str1, String str2) {
|
||||
if (str1.isEmpty || str2.isEmpty) {
|
||||
return {'length': 0, 'sequence': '', 'offset': 0};
|
||||
}
|
||||
var sequence = '';
|
||||
var str1Length = str1.length;
|
||||
var str2Length = str2.length;
|
||||
var num = List.generate(str1Length, (_) => List<int>.filled(str2Length, 0));
|
||||
var maxlen = 0;
|
||||
var lastSubsBegin = 0;
|
||||
var thisSubsBegin = 0;
|
||||
for (var i = 0; i < str1Length; i++) {
|
||||
for (var j = 0; j < str2Length; j++) {
|
||||
if (str1[i] != str2[j]) {
|
||||
num[i][j] = 0;
|
||||
} else {
|
||||
if (i == 0 || j == 0) {
|
||||
num[i][j] = 1;
|
||||
} else {
|
||||
num[i][j] = 1 + num[i - 1][j - 1];
|
||||
}
|
||||
if (num[i][j] > maxlen) {
|
||||
maxlen = num[i][j];
|
||||
thisSubsBegin = i - num[i][j] + 1;
|
||||
if (lastSubsBegin == thisSubsBegin) {
|
||||
sequence += str1[i];
|
||||
} else {
|
||||
lastSubsBegin = thisSubsBegin;
|
||||
sequence = str1.substring(lastSubsBegin, i + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return {'length': maxlen, 'sequence': sequence, 'offset': thisSubsBegin};
|
||||
}
|
||||
}
|
||||
|
||||
class Item {
|
||||
String fileId = "";
|
||||
String shareId = "";
|
||||
String shareToken = "";
|
||||
String shareFileToken = "";
|
||||
String seriesId = "";
|
||||
String name = "";
|
||||
String type = "";
|
||||
String formatType = "";
|
||||
String size = "";
|
||||
String parent = "";
|
||||
dynamic shareData;
|
||||
int shareIndex = 0;
|
||||
int lastUpdateAt = 0;
|
||||
dynamic subtitle;
|
||||
late CloudDriveType cloudDriveType;
|
||||
|
||||
static Item objectFrom(
|
||||
Map<String, dynamic> itemJson,
|
||||
String shareId,
|
||||
int shareIndex,
|
||||
CloudDriveType cloudDriveType,
|
||||
) {
|
||||
Item item = Item();
|
||||
item.fileId = itemJson['fid'] ?? "";
|
||||
item.shareId = shareId;
|
||||
item.shareToken = itemJson['stoken'] ?? "";
|
||||
item.shareFileToken = itemJson['share_fid_token'] ?? "";
|
||||
item.seriesId = itemJson['series_id'] ?? "";
|
||||
item.name = itemJson['file_name'] ?? "";
|
||||
item.type = itemJson['obj_category'] ?? "";
|
||||
item.formatType = itemJson['format_type'] ?? "";
|
||||
item.size = (itemJson['size'] ?? 0).toString();
|
||||
item.parent = itemJson['pdir_fid'] ?? "";
|
||||
item.lastUpdateAt = itemJson['last_update_at'] ?? 0;
|
||||
item.shareIndex = shareIndex;
|
||||
item.cloudDriveType = cloudDriveType;
|
||||
return item;
|
||||
}
|
||||
|
||||
String getFileExtension() {
|
||||
return name.split(".").last;
|
||||
}
|
||||
|
||||
String getFileId() {
|
||||
return fileId;
|
||||
}
|
||||
|
||||
String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
String getParent() {
|
||||
return "[$parent]";
|
||||
}
|
||||
|
||||
String getSize() {
|
||||
return size == "0" ? "" : "[${getHumanReadableSize(int.parse(size))}]";
|
||||
}
|
||||
|
||||
int getShareIndex() {
|
||||
return shareIndex;
|
||||
}
|
||||
|
||||
String getDisplayName(String typeName) {
|
||||
String drivePrefix =
|
||||
cloudDriveType == CloudDriveType.quark ? '[quark]' : '[uc]';
|
||||
String displayName = getName();
|
||||
if (typeName == "电视剧") {
|
||||
List<String> replaceNameList = ["4k", "4K"];
|
||||
displayName = displayName.replaceAll(".$getFileExtension()", "");
|
||||
displayName = displayName.replaceAll(" ", "").replaceAll(" ", "");
|
||||
for (String replaceName in replaceNameList) {
|
||||
displayName = displayName.replaceAll(replaceName, "");
|
||||
}
|
||||
displayName =
|
||||
RegExp(r'\.S01E(.*?)\.').firstMatch(displayName)?.group(1) ??
|
||||
displayName;
|
||||
final numbers =
|
||||
RegExp(
|
||||
r'\d+',
|
||||
).allMatches(displayName).map((m) => m.group(0)).toList();
|
||||
if (numbers.isNotEmpty) {
|
||||
displayName = numbers[0]!;
|
||||
}
|
||||
}
|
||||
return "$drivePrefix $displayName ${getSize()}";
|
||||
}
|
||||
|
||||
String getEpisodeUrl(String typeName) {
|
||||
return "${getDisplayName(typeName)}\$${cloudDriveType == CloudDriveType.quark ? "quark" : "uc"}++${getFileId()}++$shareFileToken++$shareId++$shareToken";
|
||||
}
|
||||
|
||||
String getHumanReadableSize(int bytes) {
|
||||
if (bytes <= 0) return "";
|
||||
final units = ['B', 'KB', 'MB', 'GB', 'TB'];
|
||||
int digitGroups = (log(bytes) / log(1024)).floor();
|
||||
return '${(bytes / pow(1024, digitGroups)).toStringAsFixed(2)} ${units[digitGroups]}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
import '../util/string_extensions.dart';
|
||||
import 'package:html/parser.dart' as parser;
|
||||
import 'package:http_interceptor/http_interceptor.dart';
|
||||
|
||||
import '../Eval/dart/model/video.dart';
|
||||
import '../http/m_client.dart';
|
||||
|
||||
class SendvidExtractor {
|
||||
final InterceptedClient client = MClient.init(
|
||||
reqcopyWith: {'useDartHttpClient': true},
|
||||
);
|
||||
final Map<String, String> headers;
|
||||
|
||||
SendvidExtractor(this.headers);
|
||||
|
||||
Future<List<Video>> videosFromUrl(String url, {String prefix = ""}) async {
|
||||
try {
|
||||
final videoList = <Video>[];
|
||||
final response = await client.get(Uri.parse(url));
|
||||
final document = parser.parse(response.body);
|
||||
final masterUrl =
|
||||
document.querySelector("source#video_source")?.attributes["src"];
|
||||
|
||||
if (masterUrl == null) return videoList;
|
||||
|
||||
final masterHeaders = Map<String, String>.from(headers)..addAll({
|
||||
"Accept": "*/*",
|
||||
"Host": Uri.parse(masterUrl).host,
|
||||
"Origin": "https://${Uri.parse(url).host}",
|
||||
"Referer": "https://${Uri.parse(url).host}/",
|
||||
});
|
||||
|
||||
final masterPlaylistResponse = await client.get(
|
||||
Uri.parse(masterUrl),
|
||||
headers: masterHeaders,
|
||||
);
|
||||
final masterPlaylist = masterPlaylistResponse.body;
|
||||
|
||||
final masterBase =
|
||||
"${"https://${Uri.parse(masterUrl).host}${Uri.parse(masterUrl).path}".substringBeforeLast("/")}/";
|
||||
|
||||
masterPlaylist
|
||||
.substringAfter("#EXT-X-STREAM-INF:")
|
||||
.split("#EXT-X-STREAM-INF:")
|
||||
.forEach((it) {
|
||||
final quality =
|
||||
"Sendvid:${it.substringAfter("RESOLUTION=").substringAfter("x").substringBefore(",")}p ";
|
||||
final videoUrl =
|
||||
masterBase + it.substringAfter("\n").substringBefore("\n");
|
||||
|
||||
final videoHeaders = Map<String, String>.from(headers)..addAll({
|
||||
"Accept": "*/*",
|
||||
"Host": Uri.parse(videoUrl).host,
|
||||
"Origin": "https://${Uri.parse(url).host}",
|
||||
"Referer": "https://${Uri.parse(url).host}/",
|
||||
});
|
||||
videoList.add(
|
||||
Video(
|
||||
videoUrl,
|
||||
"$prefix - $quality",
|
||||
videoUrl,
|
||||
headers: videoHeaders,
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
return videoList;
|
||||
} catch (_) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
import '../util/string_extensions.dart';
|
||||
import 'package:http_interceptor/http_interceptor.dart';
|
||||
|
||||
import '../Eval/dart/model/video.dart';
|
||||
import '../http/m_client.dart';
|
||||
|
||||
class SibnetExtractor {
|
||||
final InterceptedClient client = MClient.init(
|
||||
reqcopyWith: {'useDartHttpClient': true},
|
||||
);
|
||||
|
||||
Future<List<Video>> videosFromUrl(String url, {String prefix = ""}) async {
|
||||
List<Video> videoList = [];
|
||||
try {
|
||||
final response = await client.get(Uri.parse(url));
|
||||
if (response.statusCode != 200) {
|
||||
return [];
|
||||
}
|
||||
|
||||
String script = response.body;
|
||||
String slug = script
|
||||
.substringAfter("player.src")
|
||||
.substringAfter("src:")
|
||||
.substringAfter("\"")
|
||||
.substringBefore("\"");
|
||||
|
||||
String videoUrl = slug.contains("http")
|
||||
? slug
|
||||
: "https://${Uri.parse(url).host}$slug";
|
||||
|
||||
Map<String, String> videoHeaders = {"Referer": url};
|
||||
|
||||
videoList.add(
|
||||
Video(videoUrl, "$prefix - Sibnet", videoUrl, headers: videoHeaders),
|
||||
);
|
||||
|
||||
return videoList;
|
||||
} catch (_) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
import '../util/string_extensions.dart';
|
||||
import 'package:http_interceptor/http_interceptor.dart';
|
||||
|
||||
import '../Eval/dart/model/video.dart';
|
||||
import '../http/m_client.dart';
|
||||
|
||||
class StreamlareExtractor {
|
||||
final InterceptedClient client = MClient.init(
|
||||
reqcopyWith: {'useDartHttpClient': true},
|
||||
);
|
||||
|
||||
Future<List<Video>> videosFromUrl(
|
||||
String url, {
|
||||
String prefix = "",
|
||||
String suffix = "",
|
||||
}) async {
|
||||
try {
|
||||
final id = url.split('/').last;
|
||||
final playlistResponse = await client.post(
|
||||
Uri.parse('https://slwatch.co/api/video/stream/get'),
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: '{"id":"$id"}',
|
||||
);
|
||||
|
||||
final playlist = playlistResponse.body;
|
||||
final type = playlist.substringAfter('"type":"').substringBefore('"');
|
||||
|
||||
if (type == 'hls') {
|
||||
final masterPlaylistUrl = playlist
|
||||
.substringAfter('"file":"')
|
||||
.substringBefore('"')
|
||||
.replaceAll(r'\/', '/');
|
||||
final masterPlaylistResponse = await client.get(
|
||||
Uri.parse(masterPlaylistUrl),
|
||||
);
|
||||
final masterPlaylist = masterPlaylistResponse.body;
|
||||
|
||||
const separator = '#EXT-X-STREAM-INF';
|
||||
return masterPlaylist.substringAfter(separator).split(separator).map((
|
||||
value,
|
||||
) {
|
||||
final quality =
|
||||
'${value.substringAfter('RESOLUTION=').substringAfter('x').substringBefore(',')}p';
|
||||
final videoUrl = value.substringAfter('\n').substringBefore('\n').let(
|
||||
(urlPart) {
|
||||
return !urlPart.startsWith('http')
|
||||
? masterPlaylistUrl.substringBefore('master.m3u8') + urlPart
|
||||
: urlPart;
|
||||
},
|
||||
);
|
||||
|
||||
return Video(
|
||||
videoUrl,
|
||||
_buildQuality(quality, prefix, suffix),
|
||||
videoUrl,
|
||||
);
|
||||
}).toList();
|
||||
} else {
|
||||
const separator = '"label":"';
|
||||
List<Video> videoList = [];
|
||||
List<String> values = playlist
|
||||
.substringAfter(separator)
|
||||
.split(separator);
|
||||
for (var value in values) {
|
||||
final quality = value.substringAfter(separator).substringBefore('",');
|
||||
final apiUrl = value
|
||||
.substringAfter('"file":"')
|
||||
.substringBefore('",')
|
||||
.replaceAll('\\', '');
|
||||
final response = await client.post(Uri.parse(apiUrl));
|
||||
final videoUrl = response.request!.url.toString();
|
||||
|
||||
videoList.add(
|
||||
Video(videoUrl, _buildQuality(quality, prefix, suffix), videoUrl),
|
||||
);
|
||||
}
|
||||
return videoList;
|
||||
}
|
||||
} catch (_) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
String _buildQuality(
|
||||
String resolution, [
|
||||
String prefix = '',
|
||||
String suffix = '',
|
||||
]) {
|
||||
final buffer = StringBuffer();
|
||||
if (prefix.isNotEmpty) buffer.write('$prefix ');
|
||||
buffer.write('Streamlare:$resolution');
|
||||
if (suffix.isNotEmpty) buffer.write(' $suffix');
|
||||
|
||||
return buffer.toString();
|
||||
}
|
||||
}
|
||||
|
||||
extension LetExtension<T> on T {
|
||||
R let<R>(R Function(T) block) {
|
||||
return block(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
import '../util/string_extensions.dart';
|
||||
import 'package:html/parser.dart' show parse;
|
||||
import 'package:http_interceptor/http_interceptor.dart';
|
||||
|
||||
import '../Eval/dart/model/video.dart';
|
||||
import '../http/m_client.dart';
|
||||
|
||||
class StreamTapeExtractor {
|
||||
Future<List<Video>> videosFromUrl(
|
||||
String url, {
|
||||
String quality = "StreamTape",
|
||||
}) async {
|
||||
final InterceptedClient client = MClient.init(
|
||||
reqcopyWith: {'useDartHttpClient': true},
|
||||
);
|
||||
try {
|
||||
const baseUrl = "https://streamtape.com/e/";
|
||||
final newUrl = !url.startsWith(baseUrl)
|
||||
? "$baseUrl${url.split("/")[4]}"
|
||||
: url;
|
||||
|
||||
final response = await client.get(Uri.parse(newUrl));
|
||||
final document = parse(response.body);
|
||||
|
||||
const targetLine = "document.getElementById('robotlink')";
|
||||
String script = "";
|
||||
final scri = document
|
||||
.querySelectorAll("script")
|
||||
.where((element) => element.innerHtml.contains(targetLine))
|
||||
.map((e) => e.innerHtml)
|
||||
.toList();
|
||||
if (scri.isEmpty) {
|
||||
return [];
|
||||
}
|
||||
script = scri.first.split("$targetLine.innerHTML = '").last;
|
||||
final videoUrl =
|
||||
"https:${script.substringBefore("'")}${script.substringAfter("+ ('xcd").substringBefore("'")}";
|
||||
|
||||
return [Video(videoUrl, quality, videoUrl)];
|
||||
} catch (_) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
import 'streamlare_extractor.dart';
|
||||
import '../util/string_extensions.dart';
|
||||
import 'package:http_interceptor/http_interceptor.dart';
|
||||
import 'package:js_packer/js_packer.dart';
|
||||
|
||||
import '../Eval/dart/model/video.dart';
|
||||
import '../http/m_client.dart';
|
||||
import '../util/xpath_selector.dart';
|
||||
|
||||
class StreamWishExtractor {
|
||||
final InterceptedClient client = MClient.init(
|
||||
reqcopyWith: {'useDartHttpClient': true},
|
||||
);
|
||||
final Map<String, String> headers = {};
|
||||
|
||||
Future<List<Video>> videosFromUrl(String url, String prefix) async {
|
||||
final videoList = <Video>[];
|
||||
try {
|
||||
final response = await client.get(Uri.parse(url), headers: headers);
|
||||
|
||||
final jsEval = xpathSelector(
|
||||
response.body,
|
||||
).queryXPath('//script[contains(text(), "m3u8")]/text()').attrs;
|
||||
if (jsEval.isEmpty) {
|
||||
return [];
|
||||
}
|
||||
|
||||
String? masterUrl = jsEval.first!
|
||||
.let((script) {
|
||||
if (script.contains("function(p,a,c")) {
|
||||
return JSPacker(script).unpack() ?? "";
|
||||
}
|
||||
return script;
|
||||
})
|
||||
.substringAfter('source')
|
||||
.substringAfter('file:"')
|
||||
.substringBefore('"');
|
||||
|
||||
if (masterUrl.isEmpty) return [];
|
||||
|
||||
final playlistHeaders = Map<String, String>.from(headers)
|
||||
..addAll({
|
||||
'Accept': '*/*',
|
||||
'Host': Uri.parse(masterUrl).host,
|
||||
'Origin': 'https://${Uri.parse(url).host}',
|
||||
'Referer': 'https://${Uri.parse(url).host}/',
|
||||
});
|
||||
|
||||
final masterBase =
|
||||
'${'https://${Uri.parse(masterUrl).host}${Uri.parse(masterUrl).path}'.substringBeforeLast('/')}/';
|
||||
|
||||
final masterPlaylistResponse = await client.get(
|
||||
Uri.parse(masterUrl),
|
||||
headers: playlistHeaders,
|
||||
);
|
||||
final masterPlaylist = masterPlaylistResponse.body;
|
||||
|
||||
const separator = '#EXT-X-STREAM-INF:';
|
||||
masterPlaylist.substringAfter(separator).split(separator).forEach((it) {
|
||||
final quality =
|
||||
'$prefix - ${it.substringAfter('RESOLUTION=').substringAfter('x').substringBefore(',')}p ';
|
||||
final videoUrl =
|
||||
masterBase + it.substringAfter('\n').substringBefore('\n');
|
||||
videoList.add(
|
||||
Video(videoUrl, quality, videoUrl, headers: playlistHeaders),
|
||||
);
|
||||
});
|
||||
|
||||
return videoList;
|
||||
} catch (_) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
import '../util/string_extensions.dart';
|
||||
import 'package:http_interceptor/http_interceptor.dart';
|
||||
|
||||
import '../Eval/dart/model/video.dart';
|
||||
import '../http/m_client.dart';
|
||||
import '../util/xpath_selector.dart';
|
||||
|
||||
class VidBomExtractor {
|
||||
final InterceptedClient client = MClient.init(
|
||||
reqcopyWith: {'useDartHttpClient': true},
|
||||
);
|
||||
|
||||
Future<List<Video>> videosFromUrl(String url) async {
|
||||
try {
|
||||
final response = await client.get(Uri.parse(url));
|
||||
final script = xpathSelector(
|
||||
response.body,
|
||||
).queryXPath('//script[contains(text(), "sources")]/text()').attrs;
|
||||
|
||||
final data = script.first!
|
||||
.substringAfter('sources: [')
|
||||
.substringBefore('],');
|
||||
|
||||
return data.split('file:"').skip(1).map((source) {
|
||||
final src = source.substringBefore('"');
|
||||
var quality =
|
||||
'Vidbom - ${source.substringAfter('label:"').substringBefore('"')}';
|
||||
if (quality.length > 15) {
|
||||
quality = 'Vidshare - 480p';
|
||||
}
|
||||
return Video(src, quality, src);
|
||||
}).toList();
|
||||
} catch (_) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
99
lib/core/services/extensions/mangayomi/anime_extractors/voe_extractor.dart
Executable file
99
lib/core/services/extensions/mangayomi/anime_extractors/voe_extractor.dart
Executable file
@@ -0,0 +1,99 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import '../util/dom_extensions.dart';
|
||||
import '../util/string_extensions.dart';
|
||||
import 'package:html/dom.dart';
|
||||
import 'package:html/parser.dart';
|
||||
import 'package:http_interceptor/http_interceptor.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
|
||||
import '../Eval/dart/model/video.dart';
|
||||
import '../http/m_client.dart';
|
||||
|
||||
class VoeExtractor {
|
||||
final InterceptedClient client = MClient.init(
|
||||
reqcopyWith: {'useDartHttpClient': true},
|
||||
);
|
||||
final linkRegex = RegExp(
|
||||
r'(http|https)://([\w_-]+(?:\.[\w_-]+)+)([\w.,@?^=%&:/~+#-]*[\w@?^=%&/~+#-])',
|
||||
);
|
||||
|
||||
final base64Regex = RegExp(r"'.*'");
|
||||
final RegExp scriptBase64Regex = RegExp(
|
||||
r"(let|var)\s+\w+\s*=\s*'(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)';",
|
||||
);
|
||||
|
||||
Future<List<Video>> videosFromUrl(String url, String? prefix) async {
|
||||
try {
|
||||
Document document = parse((await client.get(Uri.parse(url))).body);
|
||||
var scriptElement = document.selectFirst("script");
|
||||
if (scriptElement?.text.contains(
|
||||
"if (typeof localStorage !== 'undefined')",
|
||||
) ??
|
||||
false) {
|
||||
var originalUrl = scriptElement?.text
|
||||
.substringAfter("window.location.href = '")
|
||||
.substringBefore("';");
|
||||
if (originalUrl == null) {
|
||||
return [];
|
||||
}
|
||||
document = parse((await client.get(Uri.parse(originalUrl))).body);
|
||||
}
|
||||
var alternativeScript = document
|
||||
.select('script')
|
||||
?.where((script) => scriptBase64Regex.hasMatch(script.text))
|
||||
.toList();
|
||||
|
||||
Element? script = document.selectFirst(
|
||||
"script:contains(const sources), script:contains(var sources), script:contains(wc0)",
|
||||
);
|
||||
if (script == null) {
|
||||
if (alternativeScript?.isNotEmpty ?? false) {
|
||||
script = alternativeScript!.first;
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
final scriptContent = script.text;
|
||||
String playlistUrl = "";
|
||||
if (scriptContent.contains('sources')) {
|
||||
final link = scriptContent
|
||||
.substringAfter("hls': '")
|
||||
.substringBefore("'");
|
||||
|
||||
playlistUrl = linkRegex.hasMatch(link)
|
||||
? link
|
||||
: utf8.decode(base64.decode(link));
|
||||
} else if (scriptContent.contains('wc0') || alternativeScript != null) {
|
||||
final base64Match = base64Regex.firstMatch(scriptContent)!.group(0)!;
|
||||
final decoded = utf8.decode(base64.decode(base64Match));
|
||||
playlistUrl = json.decode(
|
||||
alternativeScript != null
|
||||
? String.fromCharCodes(decoded.runes.toList().reversed)
|
||||
: decoded,
|
||||
)['file'];
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
final uri = Uri.parse(playlistUrl);
|
||||
final m3u8Host = "${uri.scheme}://${uri.host}${path.dirname(uri.path)}";
|
||||
final masterPlaylistResponse = await client.get(uri);
|
||||
final masterPlaylist = masterPlaylistResponse.body;
|
||||
|
||||
const separator = "#EXT-X-STREAM-INF";
|
||||
return masterPlaylist.substringAfter(separator).split(separator).map((
|
||||
it,
|
||||
) {
|
||||
final resolution =
|
||||
"${it.substringAfter("RESOLUTION=").substringBefore("\n").substringAfter("x").substringBefore(",")}p";
|
||||
final line = it.substringAfter("\n").substringBefore("\n");
|
||||
final videoUrl = line.startsWith("http")
|
||||
? line
|
||||
: "$m3u8Host/${line.replaceFirst("/", "")}";
|
||||
return Video(videoUrl, '${prefix ?? ""}Voe: $resolution', videoUrl);
|
||||
}).toList();
|
||||
} catch (_) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
import '../util/string_extensions.dart';
|
||||
import 'package:http_interceptor/http_interceptor.dart';
|
||||
|
||||
import '../Eval/dart/model/video.dart';
|
||||
import '../http/m_client.dart';
|
||||
import '../util/xpath_selector.dart';
|
||||
|
||||
class YourUploadExtractor {
|
||||
final InterceptedClient client = MClient.init(
|
||||
reqcopyWith: {'useDartHttpClient': true},
|
||||
);
|
||||
|
||||
Future<List<Video>> videosFromUrl(
|
||||
String url,
|
||||
Map<String, String> headers, {
|
||||
String name = "YourUpload",
|
||||
String prefix = "",
|
||||
}) async {
|
||||
final newHeaders = Map<String, String>.from(headers);
|
||||
newHeaders["referer"] = "https://www.yourupload.com/";
|
||||
|
||||
try {
|
||||
final response = await client.get(Uri.parse(url), headers: newHeaders);
|
||||
final baseData = xpathSelector(response.body)
|
||||
.queryXPath('//script[contains(text(), "jwplayerOptions")]/text()')
|
||||
.attrs;
|
||||
if (baseData.isNotEmpty) {
|
||||
final basicUrl = baseData.first!
|
||||
.substringAfter("file: '")
|
||||
.substringBefore("',");
|
||||
final quality = prefix + name;
|
||||
return [Video(basicUrl, quality, basicUrl, headers: newHeaders)];
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
} catch (_) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
91
lib/core/services/extensions/mangayomi/cryptoaes/crypto_aes.dart
Executable file
91
lib/core/services/extensions/mangayomi/cryptoaes/crypto_aes.dart
Executable file
@@ -0,0 +1,91 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:math';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:crypto/crypto.dart';
|
||||
import 'package:encrypt/encrypt.dart' as encrypt;
|
||||
|
||||
class CryptoAES {
|
||||
static String encryptAESCryptoJS(String plainText, String passphrase) {
|
||||
try {
|
||||
final salt = genRandomWithNonZero(8);
|
||||
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.trim(), iv: iv);
|
||||
Uint8List encryptedBytesWithSalt = Uint8List.fromList(
|
||||
createUint8ListFromString("Salted__") + salt + encrypted.bytes);
|
||||
return base64.encode(encryptedBytesWithSalt);
|
||||
} catch (error) {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
static String decryptAESCryptoJS(String encrypted, String passphrase) {
|
||||
try {
|
||||
Uint8List encryptedBytesWithSalt = base64.decode(encrypted.trim());
|
||||
|
||||
Uint8List encryptedBytes =
|
||||
encryptedBytesWithSalt.sublist(16, encryptedBytesWithSalt.length);
|
||||
final salt = encryptedBytesWithSalt.sublist(8, 16);
|
||||
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 decrypted =
|
||||
encrypter.decrypt64(base64.encode(encryptedBytes), iv: iv);
|
||||
return decrypted;
|
||||
} catch (error) {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
static (Uint8List, Uint8List) deriveKeyAndIV(
|
||||
String passphrase, Uint8List salt) {
|
||||
var password = createUint8ListFromString(passphrase);
|
||||
Uint8List concatenatedHashes = Uint8List(0);
|
||||
Uint8List currentHash = Uint8List(0);
|
||||
bool enoughBytesForKey = false;
|
||||
Uint8List preHash = Uint8List(0);
|
||||
|
||||
while (!enoughBytesForKey) {
|
||||
// int preHashLength = currentHash.length + password.length + salt.length;
|
||||
if (currentHash.isNotEmpty) {
|
||||
preHash = Uint8List.fromList(currentHash + password + salt);
|
||||
} else {
|
||||
preHash = Uint8List.fromList(password + salt);
|
||||
}
|
||||
|
||||
currentHash = md5.convert(preHash).bytes as Uint8List;
|
||||
concatenatedHashes = Uint8List.fromList(concatenatedHashes + currentHash);
|
||||
if (concatenatedHashes.length >= 48) enoughBytesForKey = true;
|
||||
}
|
||||
|
||||
var keyBtyes = concatenatedHashes.sublist(0, 32);
|
||||
var ivBtyes = concatenatedHashes.sublist(32, 48);
|
||||
return (keyBtyes, ivBtyes);
|
||||
}
|
||||
|
||||
static Uint8List createUint8ListFromString(String s) {
|
||||
var ret = Uint8List(s.length);
|
||||
for (var i = 0; i < s.length; i++) {
|
||||
ret[i] = s.codeUnitAt(i);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static Uint8List genRandomWithNonZero(int seedLength) {
|
||||
final random = Random.secure();
|
||||
const int randomMax = 245;
|
||||
final Uint8List uint8list = Uint8List(seedLength);
|
||||
for (int i = 0; i < seedLength; i++) {
|
||||
uint8list[i] = random.nextInt(randomMax) + 1;
|
||||
}
|
||||
return uint8list;
|
||||
}
|
||||
}
|
||||
66
lib/core/services/extensions/mangayomi/cryptoaes/deobfuscator.dart
Executable file
66
lib/core/services/extensions/mangayomi/cryptoaes/deobfuscator.dart
Executable file
@@ -0,0 +1,66 @@
|
||||
class Deobfuscator {
|
||||
static String deobfuscateJsPassword(String inputString) {
|
||||
int idx = 0;
|
||||
final brackets = ['[', '('];
|
||||
final evaluatedString = StringBuffer();
|
||||
|
||||
while (idx < inputString.length) {
|
||||
final chr = inputString[idx];
|
||||
|
||||
if (!brackets.contains(chr)) {
|
||||
idx++;
|
||||
continue;
|
||||
}
|
||||
|
||||
final closingIndex = getMatchingBracketIndex(idx, inputString);
|
||||
|
||||
if (chr == '[') {
|
||||
final digit = calculateDigit(inputString.substring(idx, closingIndex));
|
||||
evaluatedString.write(digit);
|
||||
} else {
|
||||
evaluatedString.write('.');
|
||||
|
||||
if (inputString[closingIndex + 1] == '[') {
|
||||
final skippingIndex =
|
||||
getMatchingBracketIndex(closingIndex + 1, inputString);
|
||||
idx = skippingIndex + 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
idx = closingIndex + 1;
|
||||
}
|
||||
|
||||
return evaluatedString.toString();
|
||||
}
|
||||
|
||||
static int getMatchingBracketIndex(int openingIndex, String inputString) {
|
||||
final openingBracket = inputString[openingIndex];
|
||||
final closingBracket = openingBracket == '[' ? ']' : ')';
|
||||
var counter = 0;
|
||||
|
||||
for (var idx = openingIndex; idx < inputString.length; idx++) {
|
||||
if (inputString[idx] == openingBracket) counter++;
|
||||
if (inputString[idx] == closingBracket) counter--;
|
||||
|
||||
if (counter == 0) return idx; // found matching bracket
|
||||
if (counter < 0) return -1; // unbalanced brackets
|
||||
}
|
||||
|
||||
return -1; // matching bracket not found
|
||||
}
|
||||
|
||||
static String calculateDigit(String inputSubString) {
|
||||
final digit = RegExp(r"\!\+\[\]").allMatches(inputSubString).length;
|
||||
|
||||
if (digit == 0) {
|
||||
if (RegExp(r"\+\[\]").allMatches(inputSubString).length == 1) {
|
||||
return '0';
|
||||
}
|
||||
} else if (digit >= 1 && digit <= 9) {
|
||||
return digit.toString();
|
||||
}
|
||||
|
||||
return '-'; // Illegal digit
|
||||
}
|
||||
}
|
||||
85
lib/core/services/extensions/mangayomi/cryptoaes/js_unpacker.dart
Executable file
85
lib/core/services/extensions/mangayomi/cryptoaes/js_unpacker.dart
Executable file
@@ -0,0 +1,85 @@
|
||||
import 'dart:math';
|
||||
|
||||
class JsUnpacker {
|
||||
static final RegExp _packedRegex = RegExp(
|
||||
r"eval[(]function[(]p,a,c,k,e,[r|d]?",
|
||||
caseSensitive: false,
|
||||
multiLine: true);
|
||||
|
||||
static final RegExp _packedExtractRegex = RegExp(
|
||||
r"[}][(]'(.*)', *(\d+), *(\d+), *'(.*?)'[.]split[(]'[|]'[)]",
|
||||
caseSensitive: false,
|
||||
multiLine: true);
|
||||
|
||||
static final RegExp _unpackReplaceRegex =
|
||||
RegExp(r"\b\w+\b", caseSensitive: false, multiLine: true);
|
||||
|
||||
static bool detect(String scriptBlock) {
|
||||
return _packedRegex.hasMatch(scriptBlock);
|
||||
}
|
||||
|
||||
static List<String> detectMultiple(List<String> scriptBlocks) {
|
||||
return scriptBlocks.where(detect).toList();
|
||||
}
|
||||
|
||||
static List<String> unpack(String scriptBlock) {
|
||||
return detect(scriptBlock) ? _unpacking(scriptBlock).toList() : <String>[];
|
||||
}
|
||||
|
||||
static String? unpackAndCombine(String scriptBlock) {
|
||||
final unpacked = unpack(scriptBlock);
|
||||
return unpacked.isEmpty ? null : unpacked.join(' ');
|
||||
}
|
||||
|
||||
static Iterable<String> _unpacking(String scriptBlock) sync* {
|
||||
final matches = _packedExtractRegex.allMatches(scriptBlock);
|
||||
for (final match in matches) {
|
||||
final payload = match.group(1);
|
||||
final symtab = match.group(4)?.split('|');
|
||||
final radix = int.tryParse(match.group(2)!) ?? 10;
|
||||
final count = int.tryParse(match.group(3)!) ?? 0;
|
||||
final unbaser = Unbaser(radix);
|
||||
|
||||
if (symtab != null && symtab.length == count) {
|
||||
final unpackedPayload =
|
||||
payload!.replaceAllMapped(_unpackReplaceRegex, (match) {
|
||||
final word = match.group(0)!;
|
||||
final unbased = symtab[unbaser.unbase(word)];
|
||||
return unbased.isEmpty ? word : unbased;
|
||||
});
|
||||
yield unpackedPayload;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Unbaser {
|
||||
final int base;
|
||||
static const Map<int, String> _alphabet = {
|
||||
52: "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOP",
|
||||
54: "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQR",
|
||||
62: "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",
|
||||
95: " !\"#\$%&\\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
|
||||
};
|
||||
|
||||
Unbaser(this.base);
|
||||
|
||||
int unbase(String value) {
|
||||
if (base >= 2 && base <= 36) {
|
||||
return int.tryParse(value, radix: base) ?? 0;
|
||||
} else {
|
||||
final dict = _alphabet[base]
|
||||
?.split('')
|
||||
.asMap()
|
||||
.map((index, c) => MapEntry(c, index));
|
||||
var returnVal = 0;
|
||||
|
||||
final valArray = value.runes.toList().reversed.toList();
|
||||
for (var i = 0; i < valArray.length; i++) {
|
||||
final cipher = String.fromCharCode(valArray[i]);
|
||||
returnVal += pow(base, i).toInt() * (dict?[cipher] ?? 0).toInt();
|
||||
}
|
||||
return returnVal;
|
||||
}
|
||||
}
|
||||
}
|
||||
202
lib/core/services/extensions/mangayomi/http/m_client.dart
Executable file
202
lib/core/services/extensions/mangayomi/http/m_client.dart
Executable file
@@ -0,0 +1,202 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:cookie_jar/cookie_jar.dart';
|
||||
import 'package:http/io_client.dart';
|
||||
import 'package:http_interceptor/http_interceptor.dart';
|
||||
import '../Eval/dart/model/m_source.dart';
|
||||
|
||||
class MClient {
|
||||
MClient();
|
||||
|
||||
static final CookieJar cookieJar = CookieJar();
|
||||
|
||||
static InterceptedClient init({
|
||||
MSource? source,
|
||||
Map<String, dynamic>? reqcopyWith,
|
||||
}) {
|
||||
return InterceptedClient.build(
|
||||
client: IOClient(HttpClient()),
|
||||
interceptors: [MCookieManager(reqcopyWith), LoggerInterceptor()],
|
||||
);
|
||||
}
|
||||
|
||||
static Future<Map<String, String>> getCookiesPref(String url) async {
|
||||
final uri = Uri.parse(url);
|
||||
final cookies = await cookieJar.loadForRequest(uri);
|
||||
|
||||
if (cookies.isEmpty) return {};
|
||||
|
||||
final cookieString = cookies.map((c) => '${c.name}=${c.value}').join('; ');
|
||||
return {HttpHeaders.cookieHeader: cookieString};
|
||||
}
|
||||
|
||||
static Future<void> setCookie(
|
||||
String url,
|
||||
String ua,
|
||||
dynamic controller, { // No specific controller type needed
|
||||
String? cookie,
|
||||
}) async {
|
||||
final Uri uri = Uri.parse(url);
|
||||
List<Cookie> cookieList = [];
|
||||
|
||||
if (cookie != null && cookie.isNotEmpty) {
|
||||
final List<String> cookieStrings = cookie
|
||||
.split(RegExp('(?<=)(,)(?=[^;]+?=)'))
|
||||
.where((cookie) => cookie.isNotEmpty)
|
||||
.toList();
|
||||
|
||||
for (final cookieStr in cookieStrings) {
|
||||
try {
|
||||
final parts = cookieStr.split('=');
|
||||
if (parts.length >= 2) {
|
||||
final name = parts[0].trim();
|
||||
final value = parts.sublist(1).join('=').split(';')[0].trim();
|
||||
cookieList.add(Cookie(name, value));
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint('Error parsing cookie: $e');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (cookieList.isNotEmpty) {
|
||||
// Store cookies in cookie jar
|
||||
await cookieJar.saveFromResponse(uri, cookieList);
|
||||
|
||||
// For compatibility with existing code, also store as string
|
||||
final host = uri.host;
|
||||
final cookiesMap = {}; //loadData(PrefName.cookies);
|
||||
cookiesMap.removeWhere((key, value) => key == host || host.contains(key));
|
||||
cookiesMap[host] = cookieList.map((c) => '${c.name}=${c.value}').join('; ');
|
||||
//saveData(PrefName.cookies, cookiesMap);
|
||||
}
|
||||
|
||||
if (ua.isNotEmpty) {
|
||||
//saveData(PrefName.userAgent, ua);
|
||||
}
|
||||
}
|
||||
|
||||
static void deleteAllCookies(String url) {
|
||||
final uri = Uri.parse(url);
|
||||
cookieJar.deleteAll();
|
||||
|
||||
// For compatibility with existing code
|
||||
final cookiesMap = {}; //loadData(PrefName.cookies);
|
||||
final urlHost = uri.host;
|
||||
cookiesMap.removeWhere(
|
||||
(host, cookie) => host == urlHost || urlHost.contains(host),
|
||||
);
|
||||
//saveData(PrefName.cookies, cookiesMap);
|
||||
}
|
||||
|
||||
// static Future<void> setCookie(
|
||||
// String url,
|
||||
// String ua,
|
||||
// flutter_inappwebview.InAppWebViewController? webViewController, {
|
||||
// String? cookie,
|
||||
// }) async {
|
||||
// List<String> cookies = [];
|
||||
// if (Platform.isLinux) {
|
||||
// cookies =
|
||||
// cookie
|
||||
// ?.split(RegExp('(?<=)(,)(?=[^;]+?=)'))
|
||||
// .where((cookie) => cookie.isNotEmpty)
|
||||
// .toList() ??
|
||||
// [];
|
||||
// } else {
|
||||
// cookies =
|
||||
// (await flutter_inappwebview.CookieManager.instance(
|
||||
// webViewEnvironment: webViewEnvironment,
|
||||
// ).getCookies(
|
||||
// url: flutter_inappwebview.WebUri(url),
|
||||
// webViewController: webViewController,
|
||||
// ))
|
||||
// .map((e) => "${e.name}=${e.value}")
|
||||
// .toList();
|
||||
// }
|
||||
// if (cookies.isNotEmpty) {
|
||||
// final host = Uri.parse(url).host;
|
||||
// final newCookie = cookies.join("; ");
|
||||
// final cookiesMap = {}; //loadData(PrefName.cookies);
|
||||
// cookiesMap.removeWhere((key, value) => key == host || host.contains(key));
|
||||
// cookiesMap[host] = newCookie;
|
||||
// // saveData(PrefName.cookies, cookiesMap);
|
||||
// }
|
||||
// if (ua.isNotEmpty) {
|
||||
// //saveData(PrefName.userAgent, ua);
|
||||
// }
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
class MCookieManager extends InterceptorContract {
|
||||
MCookieManager(this.reqcopyWith);
|
||||
|
||||
Map<String, dynamic>? reqcopyWith;
|
||||
|
||||
@override
|
||||
Future<BaseRequest> interceptRequest({required BaseRequest request}) async {
|
||||
final cookie = await MClient.getCookiesPref(request.url.toString());
|
||||
if (cookie.isNotEmpty) {
|
||||
final userAgent = ''; //loadData(PrefName.userAgent);
|
||||
if (request.headers[HttpHeaders.cookieHeader] == null) {
|
||||
request.headers.addAll(cookie);
|
||||
}
|
||||
if (request.headers[HttpHeaders.userAgentHeader] == null) {
|
||||
request.headers[HttpHeaders.userAgentHeader] = userAgent;
|
||||
}
|
||||
}
|
||||
try {
|
||||
if (reqcopyWith != null) {
|
||||
if (reqcopyWith!["followRedirects"] != null) {
|
||||
request.followRedirects = reqcopyWith!["followRedirects"];
|
||||
}
|
||||
if (reqcopyWith!["maxRedirects"] != null) {
|
||||
request.maxRedirects = reqcopyWith!["maxRedirects"];
|
||||
}
|
||||
if (reqcopyWith!["contentLength"] != null) {
|
||||
request.contentLength = reqcopyWith!["contentLength"];
|
||||
}
|
||||
if (reqcopyWith!["persistentConnection"] != null) {
|
||||
request.persistentConnection = reqcopyWith!["persistentConnection"];
|
||||
}
|
||||
}
|
||||
} catch (_) {}
|
||||
return request;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<BaseResponse> interceptResponse({
|
||||
required BaseResponse response,
|
||||
}) async {
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
class LoggerInterceptor extends InterceptorContract {
|
||||
@override
|
||||
Future<BaseRequest> interceptRequest({required BaseRequest request}) async {
|
||||
debugPrint(
|
||||
'----- Request -----\n${request.toString()}\nheader: ${request.headers.toString()}',
|
||||
);
|
||||
return request;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<BaseResponse> interceptResponse({
|
||||
required BaseResponse response,
|
||||
}) async {
|
||||
final cloudflare =
|
||||
[403, 503].contains(response.statusCode) &&
|
||||
["cloudflare-nginx", "cloudflare"].contains(response.headers["server"]);
|
||||
debugPrint(
|
||||
"----- Response -----\n${response.request?.method}: ${response.request?.url}, statusCode: ${response.statusCode} ${cloudflare ? "Failed to bypass Cloudflare" : ""}",
|
||||
);
|
||||
if (cloudflare) {
|
||||
debugPrint("${response.statusCode} Failed to bypass Cloudflare");
|
||||
}
|
||||
return response;
|
||||
}
|
||||
}
|
||||
204
lib/core/services/extensions/mangayomi/util/dom_extensions.dart
Executable file
204
lib/core/services/extensions/mangayomi/util/dom_extensions.dart
Executable file
@@ -0,0 +1,204 @@
|
||||
import 'reg_exp_matcher.dart';
|
||||
import 'package:html/dom.dart';
|
||||
import 'package:pseudom/pseudom.dart' as pseudom;
|
||||
import 'package:xpath_selector_html_parser/xpath_selector_html_parser.dart';
|
||||
|
||||
void _initPseudoSelector() {
|
||||
bool nthChild(Element element, String? args) {
|
||||
if (int.tryParse(args!) != null) {
|
||||
final parent = element.parentNode;
|
||||
return parent != null &&
|
||||
(int.parse(args) as num) > 0 &&
|
||||
parent.nodes.indexOf(element) == int.parse(args);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool has(Element element, String? args) {
|
||||
if (args == null) return false;
|
||||
final parent = element.parent;
|
||||
final res = parent == null
|
||||
? false
|
||||
: pseudom.parse(args).selectFirst(parent) == element;
|
||||
return res ? res : pseudom.parse(args).selectFirst(element) != null;
|
||||
}
|
||||
|
||||
bool inot(Element element, String? args) {
|
||||
if (args == null) return false;
|
||||
final parent = element.parent;
|
||||
final res = parent == null
|
||||
? false
|
||||
: pseudom.parse(args).selectFirst(parent) != element;
|
||||
return res ? res : pseudom.parse(args).selectFirst(element) == null;
|
||||
}
|
||||
|
||||
bool contains(Element element, String? args) {
|
||||
final text = args ?? '';
|
||||
return element.text.contains(text);
|
||||
}
|
||||
|
||||
bool firstChild(Element element, String? args) {
|
||||
return element.previousElementSibling == null;
|
||||
}
|
||||
|
||||
bool lastChild(Element element, String? args) {
|
||||
return element.nextElementSibling == null;
|
||||
}
|
||||
|
||||
bool onlyChild(Element element, String? args) {
|
||||
return element.nextElementSibling == null;
|
||||
}
|
||||
|
||||
pseudom.PseudoSelector.handlers['nth-child'] = nthChild;
|
||||
pseudom.PseudoSelector.handlers['has'] = has;
|
||||
pseudom.PseudoSelector.handlers['inot'] = inot;
|
||||
pseudom.PseudoSelector.handlers['contains'] = contains;
|
||||
pseudom.PseudoSelector.handlers['last-child'] = lastChild;
|
||||
pseudom.PseudoSelector.handlers['first-child'] = firstChild;
|
||||
pseudom.PseudoSelector.handlers['only-child'] = onlyChild;
|
||||
}
|
||||
|
||||
String _fixSelector(String selector) {
|
||||
return selector.replaceAll(':not', ':inot');
|
||||
}
|
||||
|
||||
extension DocumentExtension on Document? {
|
||||
List<Element>? select(String selector) {
|
||||
try {
|
||||
_initPseudoSelector();
|
||||
final dom = this?.documentElement;
|
||||
return pseudom.parse(_fixSelector(selector)).select(dom!).toList();
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Element? selectFirst(String selector) {
|
||||
try {
|
||||
_initPseudoSelector();
|
||||
final dom = this?.documentElement;
|
||||
return pseudom.parse(_fixSelector(selector)).selectFirst(dom!);
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
bool hasAtr(String attribute) {
|
||||
return attr(attribute) != null;
|
||||
}
|
||||
|
||||
String? xpathFirst(String xpath) {
|
||||
final dom = this?.documentElement;
|
||||
if (dom == null) return null;
|
||||
var htmlXPath = HtmlXPath.node(dom);
|
||||
var query = htmlXPath.query(xpath);
|
||||
return query.attr;
|
||||
}
|
||||
|
||||
List<String> xpath(String xpath) {
|
||||
final dom = this?.documentElement;
|
||||
if (dom == null) return [];
|
||||
var htmlXPath = HtmlXPath.node(dom);
|
||||
var query = htmlXPath.query(xpath);
|
||||
if (query.nodes.length > 1) {
|
||||
return query.attrs.map((e) => e!.trim().trimLeft().trimRight()).toList();
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
String? attr(String attribute) {
|
||||
try {
|
||||
return this?.attributes[attribute];
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension ElementtExtension on Element {
|
||||
List<Element>? select(String selector) {
|
||||
try {
|
||||
_initPseudoSelector();
|
||||
return pseudom
|
||||
.parse(_fixSelector(selector))
|
||||
.select(
|
||||
parent!.nodes.firstWhere((element) => element == this) as Element,
|
||||
)
|
||||
.toList();
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
String? xpathFirst(String xpath) {
|
||||
var htmlXPath = HtmlXPath.node(this);
|
||||
var query = htmlXPath.query(xpath);
|
||||
return query.attr;
|
||||
}
|
||||
|
||||
List<String> xpath(String xpath) {
|
||||
var htmlXPath = HtmlXPath.node(this);
|
||||
var query = htmlXPath.query(xpath);
|
||||
if (query.nodes.length > 1) {
|
||||
return query.attrs.map((e) => e!.trim().trimLeft().trimRight()).toList();
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
Element? selectFirst(String selector) {
|
||||
try {
|
||||
_initPseudoSelector();
|
||||
return pseudom
|
||||
.parse(_fixSelector(selector))
|
||||
.selectFirst(
|
||||
parent!.nodes.firstWhere((element) => element == this) as Element,
|
||||
);
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
String? attr(String attribute) {
|
||||
try {
|
||||
return attributes[attribute];
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
bool hasAtr(String attribute) {
|
||||
return attr(attribute) != null;
|
||||
}
|
||||
|
||||
String? get getSrc {
|
||||
try {
|
||||
return regSrcMatcher(outerHtml);
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
String? get getImg {
|
||||
try {
|
||||
return regImgMatcher(outerHtml);
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
String? get getHref {
|
||||
try {
|
||||
return regHrefMatcher(outerHtml);
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
String? get getDataSrc {
|
||||
try {
|
||||
return regDataSrcMatcher(outerHtml);
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
//TODO implement
|
||||
46
lib/core/services/extensions/mangayomi/util/reg_exp_matcher.dart
Executable file
46
lib/core/services/extensions/mangayomi/util/reg_exp_matcher.dart
Executable file
@@ -0,0 +1,46 @@
|
||||
String regHrefMatcher(String input) {
|
||||
RegExp exp = RegExp(r'href="([^"]+)"');
|
||||
Iterable<Match> matches = exp.allMatches(input);
|
||||
String? firstMatch = matches.first.group(1);
|
||||
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);
|
||||
String? firstMatch = matches.first.group(1);
|
||||
return firstMatch!;
|
||||
}
|
||||
|
||||
String regImgMatcher(String input) {
|
||||
RegExp exp = RegExp(r'img="([^"]+)"');
|
||||
Iterable<Match> matches = exp.allMatches(input);
|
||||
String? firstMatch = matches.first.group(1);
|
||||
return firstMatch!;
|
||||
}
|
||||
|
||||
String regCustomMatcher(
|
||||
String input,
|
||||
String source,
|
||||
int group,
|
||||
) {
|
||||
try {
|
||||
RegExp exp = RegExp(source);
|
||||
Iterable<Match> matches = exp.allMatches(input);
|
||||
String? firstMatch = matches.first.group(group);
|
||||
return firstMatch!;
|
||||
} catch (_) {
|
||||
return input;
|
||||
}
|
||||
}
|
||||
|
||||
String padIndex(int index) {
|
||||
return index.toString().padLeft(3, "0");
|
||||
}
|
||||
91
lib/core/services/extensions/mangayomi/util/string_extensions.dart
Executable file
91
lib/core/services/extensions/mangayomi/util/string_extensions.dart
Executable file
@@ -0,0 +1,91 @@
|
||||
extension StringExtensions on String {
|
||||
String substringAfter(String pattern) {
|
||||
final startIndex = indexOf(pattern);
|
||||
if (startIndex == -1) return substring(0);
|
||||
|
||||
final start = startIndex + pattern.length;
|
||||
return substring(start);
|
||||
}
|
||||
|
||||
String substringAfterLast(String pattern) {
|
||||
return split(pattern).last;
|
||||
}
|
||||
|
||||
double toDouble() {
|
||||
return double.parse(this);
|
||||
}
|
||||
|
||||
int toInt() {
|
||||
return int.parse(this);
|
||||
}
|
||||
|
||||
int? toNullInt() {
|
||||
return int.tryParse(this);
|
||||
}
|
||||
|
||||
bool isEqualTo(String? other) {
|
||||
return this == other;
|
||||
}
|
||||
|
||||
String substringBefore(String pattern) {
|
||||
final endIndex = indexOf(pattern);
|
||||
if (endIndex == -1) return substring(0);
|
||||
|
||||
return substring(0, endIndex);
|
||||
}
|
||||
|
||||
String substringBeforeLast(String pattern) {
|
||||
final endIndex = lastIndexOf(pattern);
|
||||
if (endIndex == -1) return substring(0);
|
||||
|
||||
return substring(0, endIndex);
|
||||
}
|
||||
|
||||
String substringBetween(String left, String right) {
|
||||
int startIndex = 0;
|
||||
int index = indexOf(left, startIndex);
|
||||
if (index == -1) return "";
|
||||
int leftIndex = index + left.length;
|
||||
int rightIndex = indexOf(right, leftIndex);
|
||||
if (rightIndex == -1) return "";
|
||||
startIndex = rightIndex + right.length;
|
||||
return substring(leftIndex, rightIndex);
|
||||
}
|
||||
|
||||
String replaceForbiddenCharacters(String source) {
|
||||
return replaceAll(
|
||||
RegExp(
|
||||
r'[\\/:*?"<>|\0]|(^CON$|^PRN$|^AUX$|^NUL$|^COM[1-9]$|^LPT[1-9]$)'),
|
||||
source);
|
||||
}
|
||||
|
||||
String get getUrlWithoutDomain {
|
||||
final uri = Uri.parse(replaceAll(' ', '%20'));
|
||||
String out = uri.path;
|
||||
if (uri.query.isNotEmpty) {
|
||||
out += '?${uri.query}';
|
||||
}
|
||||
if (uri.fragment.isNotEmpty) {
|
||||
out += '#${uri.fragment}';
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
bool isMediaVideo() {
|
||||
return [
|
||||
"3gp",
|
||||
"avi",
|
||||
"mpg",
|
||||
"mpeg",
|
||||
"webm",
|
||||
"ogg",
|
||||
"flv",
|
||||
"m4v",
|
||||
"mvp",
|
||||
"mp4",
|
||||
"wmv",
|
||||
"mkv",
|
||||
"mov"
|
||||
].any((extension) => toLowerCase().endsWith(extension));
|
||||
}
|
||||
}
|
||||
7
lib/core/services/extensions/mangayomi/util/xpath_selector.dart
Executable file
7
lib/core/services/extensions/mangayomi/util/xpath_selector.dart
Executable 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);
|
||||
}
|
||||
@@ -4,10 +4,10 @@ import 'package:logger/logger.dart';
|
||||
import 'package:unyo/core/di/locator.dart';
|
||||
// Internal dependencies
|
||||
import 'package:unyo/config/config.dart' as config;
|
||||
import 'package:unyo/core/services/api/graphql/queries/queries.dart' as queries;
|
||||
import 'package:unyo/core/services/api/dto/media_collection_recently_completed_graphql_dto_entity.dart';
|
||||
import 'package:unyo/core/services/api/dto/media_collection_trendingOrPopular_graphql_dto_entity.dart';
|
||||
import 'package:unyo/core/services/api/dto/media_collection_upcoming_graphql_dto_entity.dart';
|
||||
import 'package:unyo/core/services/api/graphql/queries/queries.dart' as queries;
|
||||
import 'package:unyo/core/services/api/dto/media_collection_recently_released_graphql_dto_entity.dart';
|
||||
import 'package:unyo/core/services/api/graphql/graphql_response.dart';
|
||||
import 'package:unyo/core/services/api/graphql/graphql_service.dart';
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "generated_plugin_registrant.h"
|
||||
|
||||
#include <dynamic_color/dynamic_color_plugin.h>
|
||||
#include <flutter_js/flutter_js_plugin.h>
|
||||
#include <fvp/fvp_plugin.h>
|
||||
#include <url_launcher_linux/url_launcher_plugin.h>
|
||||
|
||||
@@ -14,6 +15,9 @@ void fl_register_plugins(FlPluginRegistry* registry) {
|
||||
g_autoptr(FlPluginRegistrar) dynamic_color_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "DynamicColorPlugin");
|
||||
dynamic_color_plugin_register_with_registrar(dynamic_color_registrar);
|
||||
g_autoptr(FlPluginRegistrar) flutter_js_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterJsPlugin");
|
||||
flutter_js_plugin_register_with_registrar(flutter_js_registrar);
|
||||
g_autoptr(FlPluginRegistrar) fvp_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "FvpPlugin");
|
||||
fvp_plugin_register_with_registrar(fvp_registrar);
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
dynamic_color
|
||||
flutter_js
|
||||
fvp
|
||||
url_launcher_linux
|
||||
)
|
||||
|
||||
@@ -7,6 +7,7 @@ import Foundation
|
||||
|
||||
import bonsoir_darwin
|
||||
import dynamic_color
|
||||
import flutter_js
|
||||
import fvp
|
||||
import path_provider_foundation
|
||||
import shared_preferences_foundation
|
||||
@@ -16,6 +17,7 @@ import video_player_avfoundation
|
||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||
SwiftBonsoirPlugin.register(with: registry.registrar(forPlugin: "SwiftBonsoirPlugin"))
|
||||
DynamicColorPlugin.register(with: registry.registrar(forPlugin: "DynamicColorPlugin"))
|
||||
FlutterJsPlugin.register(with: registry.registrar(forPlugin: "FlutterJsPlugin"))
|
||||
FvpPlugin.register(with: registry.registrar(forPlugin: "FvpPlugin"))
|
||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||
|
||||
10
pubspec.yaml
10
pubspec.yaml
@@ -68,6 +68,16 @@ dependencies:
|
||||
ffigen: ^19.0.0
|
||||
meta: ^1.16.0
|
||||
jwt_decoder: ^2.0.1
|
||||
http_interceptor: ^2.0.0
|
||||
flutter_js: ^0.8.5
|
||||
js_packer: ^0.0.6
|
||||
intl: ^0.20.2
|
||||
crypto: ^3.0.6
|
||||
encrypt: ^5.0.3
|
||||
d4rt: ^0.1.0
|
||||
xpath_selector_html_parser: ^3.0.1
|
||||
pseudom: ^1.0.1
|
||||
cookie_jar: ^4.0.8
|
||||
# Dev dependencies
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include <bonsoir_windows/bonsoir_windows_plugin_c_api.h>
|
||||
#include <dynamic_color/dynamic_color_plugin_c_api.h>
|
||||
#include <flutter_js/flutter_js_plugin.h>
|
||||
#include <fvp/fvp_plugin_c_api.h>
|
||||
#include <url_launcher_windows/url_launcher_windows.h>
|
||||
|
||||
@@ -16,6 +17,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||
registry->GetRegistrarForPlugin("BonsoirWindowsPluginCApi"));
|
||||
DynamicColorPluginCApiRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("DynamicColorPluginCApi"));
|
||||
FlutterJsPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("FlutterJsPlugin"));
|
||||
FvpPluginCApiRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("FvpPluginCApi"));
|
||||
UrlLauncherWindowsRegisterWithRegistrar(
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
bonsoir_windows
|
||||
dynamic_color
|
||||
flutter_js
|
||||
fvp
|
||||
url_launcher_windows
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user