rewrite: Copied some source files

This commit is contained in:
Kevin Rodrigues Borges
2025-07-30 06:39:19 +01:00
parent b0622078f2
commit ddc16ae6ad
68 changed files with 13147 additions and 1 deletions

View 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',
);
}
}

View 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',
);
}
}

View File

@@ -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',
);
}
}

View File

@@ -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',
);
}
}

View File

@@ -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',
);
}
}

View File

@@ -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',
);
}
}

View File

@@ -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',
);
}
}

View File

@@ -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,
),
);
}
}

View File

@@ -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',
);
}
}

View File

@@ -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',
);
}
}

View File

@@ -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',
);
}
}

View File

@@ -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',
);
}
}

View File

@@ -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);
}
}

View File

@@ -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',
);
}
}

View 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;
}
}

View 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;
}
}

View 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();
}

View 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);
}

View 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,
};
}

View 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,
}

View 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,
};
}

View 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();
}

View 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,
};
}

View 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});
}

View 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};
}

View 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

View 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};
}

View 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 [];
}
}
}

View 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])
);
}
}
''');
}
}

View 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());
}
}

View 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()),
);
}
}

View 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])
);
}
}
''');
}
}

View 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;
}
}
}

View 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;
}
}

View 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();
}
}

View 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 [];
}
}
}

View 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 [];
}
}
}

View File

@@ -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 [];
}
}
}

View File

@@ -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();
}
}
}

View 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 [];
}
}
}

View 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 [];
}
}

View 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]}';
}
}

View File

@@ -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 [];
}
}
}

View File

@@ -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 [];
}
}
}

View File

@@ -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);
}
}

View 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 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 [];
}
}
}

View File

@@ -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 [];
}
}
}

View File

@@ -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 [];
}
}
}

View 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 [];
}
}
}

View File

@@ -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 [];
}
}
}

View 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;
}
}

View 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
}
}

View 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;
}
}
}

View 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;
}
}

View 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;
}
}
}

View File

@@ -0,0 +1 @@
//TODO implement

View 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");
}

View 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));
}
}

View 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);
}

View File

@@ -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';

View File

@@ -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);

View File

@@ -4,6 +4,7 @@
list(APPEND FLUTTER_PLUGIN_LIST
dynamic_color
flutter_js
fvp
url_launcher_linux
)

View File

@@ -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"))

View File

@@ -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:

View File

@@ -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(

View File

@@ -5,6 +5,7 @@
list(APPEND FLUTTER_PLUGIN_LIST
bonsoir_windows
dynamic_color
flutter_js
fvp
url_launcher_windows
)