mirror of
https://github.com/venera-app/venera.git
synced 2025-09-27 07:47:24 +00:00
add star rating, network cache, advanced search option, loginWithCookies, loadNext; fix some minor issues
This commit is contained in:
@@ -27,6 +27,10 @@ part 'models.dart';
|
||||
/// build comic list, [Res.subData] should be maxPage or null if there is no limit.
|
||||
typedef ComicListBuilder = Future<Res<List<Comic>>> Function(int page);
|
||||
|
||||
/// build comic list with next param, [Res.subData] should be next page param or null if there is no next page.
|
||||
typedef ComicListBuilderWithNext = Future<Res<List<Comic>>> Function(
|
||||
String? next);
|
||||
|
||||
typedef LoginFunction = Future<Res<bool>> Function(String, String);
|
||||
|
||||
typedef LoadComicFunc = Future<Res<ComicDetails>> Function(String id);
|
||||
@@ -40,7 +44,7 @@ typedef CommentsLoader = Future<Res<List<Comment>>> Function(
|
||||
typedef SendCommentFunc = Future<Res<bool>> Function(
|
||||
String id, String? subId, String content, String? replyTo);
|
||||
|
||||
typedef GetImageLoadingConfigFunc = Map<String, dynamic> Function(
|
||||
typedef GetImageLoadingConfigFunc = Future<Map<String, dynamic>> Function(
|
||||
String imageKey, String comicId, String epId)?;
|
||||
typedef GetThumbnailLoadingConfigFunc = Map<String, dynamic> Function(
|
||||
String imageKey)?;
|
||||
@@ -64,6 +68,9 @@ typedef VoteCommentFunc = Future<Res<int?>> Function(
|
||||
typedef HandleClickTagEvent = Map<String, String> Function(
|
||||
String namespace, String tag);
|
||||
|
||||
/// [rating] is the rating value, 0-10. 1 represents 0.5 star.
|
||||
typedef StarRatingFunc = Future<Res<bool>> Function(String comicId, int rating);
|
||||
|
||||
class ComicSource {
|
||||
static final List<ComicSource> _sources = [];
|
||||
|
||||
@@ -163,8 +170,7 @@ class ComicSource {
|
||||
/// Load comic pages.
|
||||
final LoadComicPagesFunc? loadComicPages;
|
||||
|
||||
final Map<String, dynamic> Function(
|
||||
String imageKey, String comicId, String epId)? getImageLoadingConfig;
|
||||
final GetImageLoadingConfigFunc? getImageLoadingConfig;
|
||||
|
||||
final Map<String, dynamic> Function(String imageKey)?
|
||||
getThumbnailLoadingConfig;
|
||||
@@ -203,6 +209,8 @@ class ComicSource {
|
||||
|
||||
final bool enableTagsTranslate;
|
||||
|
||||
final StarRatingFunc? starRatingFunc;
|
||||
|
||||
Future<void> loadData() async {
|
||||
var file = File("${App.dataPath}/comic_source/$key.data");
|
||||
if (await file.exists()) {
|
||||
@@ -270,6 +278,7 @@ class ComicSource {
|
||||
this.linkHandler,
|
||||
this.enableTagsSuggestions,
|
||||
this.enableTagsTranslate,
|
||||
this.starRatingFunc,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -288,12 +297,21 @@ class AccountConfig {
|
||||
|
||||
final bool Function(String url, String title)? checkLoginStatus;
|
||||
|
||||
final void Function()? onLoginWithWebviewSuccess;
|
||||
|
||||
final List<String>? cookieFields;
|
||||
|
||||
final Future<bool> Function(List<String>)? validateCookies;
|
||||
|
||||
const AccountConfig(
|
||||
this.login,
|
||||
this.loginWebsite,
|
||||
this.registerWebsite,
|
||||
this.logout,
|
||||
this.checkLoginStatus,
|
||||
this.onLoginWithWebviewSuccess,
|
||||
this.cookieFields,
|
||||
this.validateCookies,
|
||||
) : allowReLogin = true,
|
||||
infoItems = const [];
|
||||
}
|
||||
@@ -322,6 +340,8 @@ class ExplorePageData {
|
||||
|
||||
final ComicListBuilder? loadPage;
|
||||
|
||||
final ComicListBuilderWithNext? loadNext;
|
||||
|
||||
final Future<Res<List<ExplorePagePart>>> Function()? loadMultiPart;
|
||||
|
||||
/// return a `List` contains `List<Comic>` or `ExplorePagePart`
|
||||
@@ -331,6 +351,7 @@ class ExplorePageData {
|
||||
this.title,
|
||||
this.type,
|
||||
this.loadPage,
|
||||
this.loadNext,
|
||||
this.loadMultiPart,
|
||||
this.loadMixed,
|
||||
);
|
||||
@@ -388,9 +409,13 @@ class SearchOptions {
|
||||
|
||||
final String label;
|
||||
|
||||
const SearchOptions(this.options, this.label);
|
||||
final String type;
|
||||
|
||||
String get defaultValue => options.keys.first;
|
||||
final String? defaultVal;
|
||||
|
||||
const SearchOptions(this.options, this.label, this.type, this.defaultVal);
|
||||
|
||||
String get defaultValue => defaultVal ?? options.keys.first;
|
||||
}
|
||||
|
||||
typedef CategoryComicsLoader = Future<Res<List<Comic>>> Function(
|
||||
@@ -401,7 +426,7 @@ class CategoryComicsData {
|
||||
final List<CategoryComicsOptions> options;
|
||||
|
||||
/// [category] is the one clicked by the user on the category page.
|
||||
|
||||
///
|
||||
/// if [BaseCategoryPart.categoryParams] is not null, [param] will be not null.
|
||||
///
|
||||
/// [Res.subData] should be maxPage or null if there is no limit.
|
||||
@@ -415,9 +440,12 @@ class CategoryComicsData {
|
||||
class RankingData {
|
||||
final Map<String, String> options;
|
||||
|
||||
final Future<Res<List<Comic>>> Function(String option, int page) load;
|
||||
final Future<Res<List<Comic>>> Function(String option, int page)? load;
|
||||
|
||||
const RankingData(this.options, this.load);
|
||||
final Future<Res<List<Comic>>> Function(String option, String? next)?
|
||||
loadWithNext;
|
||||
|
||||
const RankingData(this.options, this.load, this.loadWithNext);
|
||||
}
|
||||
|
||||
class CategoryComicsOptions {
|
||||
@@ -434,11 +462,10 @@ class CategoryComicsOptions {
|
||||
const CategoryComicsOptions(this.options, this.notShowWhen, this.showWhen);
|
||||
}
|
||||
|
||||
|
||||
class LinkHandler {
|
||||
final List<String> domains;
|
||||
|
||||
final String? Function(String url) linkToId;
|
||||
|
||||
const LinkHandler(this.domains, this.linkToId);
|
||||
}
|
||||
}
|
||||
|
@@ -1,20 +1,26 @@
|
||||
part of 'comic_source.dart';
|
||||
|
||||
typedef AddOrDelFavFunc = Future<Res<bool>> Function(String comicId, String folderId, bool isAdding, String? favId);
|
||||
typedef AddOrDelFavFunc = Future<Res<bool>> Function(
|
||||
String comicId, String folderId, bool isAdding, String? favId);
|
||||
|
||||
class FavoriteData{
|
||||
class FavoriteData {
|
||||
final String key;
|
||||
|
||||
final String title;
|
||||
|
||||
final bool multiFolder;
|
||||
|
||||
final Future<Res<List<Comic>>> Function(int page, [String? folder]) loadComic;
|
||||
final Future<Res<List<Comic>>> Function(int page, [String? folder])?
|
||||
loadComic;
|
||||
|
||||
final Future<Res<List<Comic>>> Function(String? next, [String? folder])?
|
||||
loadNext;
|
||||
|
||||
/// key-id, value-name
|
||||
///
|
||||
/// if comicId is not null, Res.subData is the folders that the comic is in
|
||||
final Future<Res<Map<String, String>>> Function([String? comicId])? loadFolders;
|
||||
final Future<Res<Map<String, String>>> Function([String? comicId])?
|
||||
loadFolders;
|
||||
|
||||
/// A value of null disables this feature
|
||||
final Future<Res<bool>> Function(String key)? deleteFolder;
|
||||
@@ -32,19 +38,21 @@ class FavoriteData{
|
||||
required this.title,
|
||||
required this.multiFolder,
|
||||
required this.loadComic,
|
||||
required this.loadNext,
|
||||
this.loadFolders,
|
||||
this.deleteFolder,
|
||||
this.addFolder,
|
||||
this.allFavoritesId,
|
||||
this.addOrDelFavorite});
|
||||
this.addOrDelFavorite,
|
||||
});
|
||||
}
|
||||
|
||||
FavoriteData getFavoriteData(String key){
|
||||
FavoriteData getFavoriteData(String key) {
|
||||
var source = ComicSource.find(key) ?? (throw "Unknown source key: $key");
|
||||
return source.favoriteData!;
|
||||
}
|
||||
|
||||
FavoriteData? getFavoriteDataOrNull(String key){
|
||||
FavoriteData? getFavoriteDataOrNull(String key) {
|
||||
var source = ComicSource.find(key);
|
||||
return source?.favoriteData;
|
||||
}
|
||||
}
|
||||
|
@@ -7,9 +7,9 @@ class Comment {
|
||||
final String? time;
|
||||
final int? replyCount;
|
||||
final String? id;
|
||||
final int? score;
|
||||
int? score;
|
||||
final bool? isLiked;
|
||||
final int? voteStatus; // 1: upvote, -1: downvote, 0: none
|
||||
int? voteStatus; // 1: upvote, -1: downvote, 0: none
|
||||
|
||||
static String? parseTime(dynamic value) {
|
||||
if (value == null) return null;
|
||||
@@ -60,6 +60,9 @@ class Comic {
|
||||
|
||||
final String? favoriteId;
|
||||
|
||||
/// 0-5
|
||||
final double? stars;
|
||||
|
||||
const Comic(
|
||||
this.title,
|
||||
this.cover,
|
||||
@@ -70,7 +73,7 @@ class Comic {
|
||||
this.sourceKey,
|
||||
this.maxPage,
|
||||
this.language,
|
||||
): favoriteId = null;
|
||||
): favoriteId = null, stars = null;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
@@ -96,7 +99,8 @@ class Comic {
|
||||
description = json["description"] ?? "",
|
||||
maxPage = json["maxPage"],
|
||||
language = json["language"],
|
||||
favoriteId = json["favoriteId"];
|
||||
favoriteId = json["favoriteId"],
|
||||
stars = (json["stars"] as num?)?.toDouble();
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
@@ -151,6 +155,11 @@ class ComicDetails with HistoryMixin {
|
||||
|
||||
final String? url;
|
||||
|
||||
final double? stars;
|
||||
|
||||
@override
|
||||
final int? maxPage;
|
||||
|
||||
static Map<String, List<String>> _generateMap(Map<dynamic, dynamic> map) {
|
||||
var res = <String, List<String>>{};
|
||||
map.forEach((key, value) {
|
||||
@@ -182,7 +191,9 @@ class ComicDetails with HistoryMixin {
|
||||
uploader = json["uploader"],
|
||||
uploadTime = json["uploadTime"],
|
||||
updateTime = json["updateTime"],
|
||||
url = json["url"];
|
||||
url = json["url"],
|
||||
stars = (json["stars"] as num?)?.toDouble(),
|
||||
maxPage = json["maxPage"];
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
|
@@ -150,6 +150,7 @@ class ComicSourceParser {
|
||||
_parseLinkHandler(),
|
||||
_getValue("search.enableTagsSuggestions") ?? false,
|
||||
_getValue("comic.enableTagsTranslate") ?? false,
|
||||
_parseStarRatingFunc(),
|
||||
);
|
||||
|
||||
await source.loadData();
|
||||
@@ -182,48 +183,77 @@ class ComicSourceParser {
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<Res<bool>> login(account, pwd) async {
|
||||
try {
|
||||
await JsEngine().runCode("""
|
||||
Future<Res<bool>> Function(String account, String pwd)? login;
|
||||
|
||||
if(_checkExists("account.login")) {
|
||||
login = (account, pwd) async {
|
||||
try {
|
||||
await JsEngine().runCode("""
|
||||
ComicSource.sources.$_key.account.login(${jsonEncode(account)},
|
||||
${jsonEncode(pwd)})
|
||||
""");
|
||||
var source = ComicSource.find(_key!)!;
|
||||
source.data["account"] = <String>[account, pwd];
|
||||
source.saveData();
|
||||
return const Res(true);
|
||||
} catch (e, s) {
|
||||
Log.error("Network", "$e\n$s");
|
||||
return Res.error(e.toString());
|
||||
}
|
||||
var source = ComicSource.find(_key!)!;
|
||||
source.data["account"] = <String>[account, pwd];
|
||||
source.saveData();
|
||||
return const Res(true);
|
||||
} catch (e, s) {
|
||||
Log.error("Network", "$e\n$s");
|
||||
return Res.error(e.toString());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void logout() {
|
||||
JsEngine().runCode("ComicSource.sources.$_key.account.logout()");
|
||||
}
|
||||
|
||||
if (!_checkExists('account.loginWithWebview')) {
|
||||
return AccountConfig(
|
||||
login,
|
||||
null,
|
||||
_getValue("account.registerWebsite"),
|
||||
logout,
|
||||
null,
|
||||
);
|
||||
} else {
|
||||
return AccountConfig(
|
||||
null,
|
||||
_getValue("account.loginWithWebview.url"),
|
||||
_getValue("account.registerWebsite"),
|
||||
logout,
|
||||
(url, title) {
|
||||
return JsEngine().runCode("""
|
||||
bool Function(String url, String title)? checkLoginStatus;
|
||||
|
||||
void Function()? onLoginSuccess;
|
||||
|
||||
if (_checkExists('account.loginWithWebview')) {
|
||||
checkLoginStatus = (url, title) {
|
||||
return JsEngine().runCode("""
|
||||
ComicSource.sources.$_key.account.loginWithWebview.checkStatus(
|
||||
${jsonEncode(url)}, ${jsonEncode(title)})
|
||||
""");
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
if (_checkExists('account.loginWithWebview.onLoginSuccess')) {
|
||||
onLoginSuccess = () {
|
||||
JsEngine().runCode("""
|
||||
ComicSource.sources.$_key.account.loginWithWebview.onLoginSuccess()
|
||||
""");
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> Function(List<String>)? validateCookies;
|
||||
|
||||
if (_checkExists('account.loginWithCookies?.validate')) {
|
||||
validateCookies = (cookies) async {
|
||||
try {
|
||||
var res = await JsEngine().runCode("""
|
||||
ComicSource.sources.$_key.account.loginWithCookies.validate(${jsonEncode(cookies)})
|
||||
""");
|
||||
return res;
|
||||
} catch (e, s) {
|
||||
Log.error("Network", "$e\n$s");
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return AccountConfig(
|
||||
login,
|
||||
_getValue("account.loginWithWebview?.url"),
|
||||
_getValue("account.registerWebsite"),
|
||||
logout,
|
||||
checkLoginStatus,
|
||||
onLoginSuccess,
|
||||
ListOrNull.from(_getValue("account.loginWithCookies?.fields")),
|
||||
validateCookies,
|
||||
);
|
||||
}
|
||||
|
||||
List<ExplorePageData> _loadExploreData() {
|
||||
@@ -237,6 +267,7 @@ class ComicSourceParser {
|
||||
final String type = _getValue("explore[$i].type");
|
||||
Future<Res<List<ExplorePagePart>>> Function()? loadMultiPart;
|
||||
Future<Res<List<Comic>>> Function(int page)? loadPage;
|
||||
Future<Res<List<Comic>>> Function(String? next)? loadNext;
|
||||
Future<Res<List<Object>>> Function(int index)? loadMixed;
|
||||
if (type == "singlePageWithMultiPart") {
|
||||
loadMultiPart = () async {
|
||||
@@ -257,19 +288,36 @@ class ComicSourceParser {
|
||||
}
|
||||
};
|
||||
} else if (type == "multiPageComicList") {
|
||||
loadPage = (int page) async {
|
||||
try {
|
||||
var res = await JsEngine().runCode(
|
||||
"ComicSource.sources.$_key.explore[$i].load(${jsonEncode(page)})");
|
||||
return Res(
|
||||
if (_checkExists("explore[$i].load")) {
|
||||
loadPage = (int page) async {
|
||||
try {
|
||||
var res = await JsEngine().runCode(
|
||||
"ComicSource.sources.$_key.explore[$i].load(${jsonEncode(page)})");
|
||||
return Res(
|
||||
List.generate(res["comics"].length,
|
||||
(index) => Comic.fromJson(res["comics"][index], _key!)),
|
||||
subData: res["maxPage"]);
|
||||
} catch (e, s) {
|
||||
Log.error("Network", "$e\n$s");
|
||||
return Res.error(e.toString());
|
||||
}
|
||||
};
|
||||
} else {
|
||||
loadNext = (next) async {
|
||||
try {
|
||||
var res = await JsEngine().runCode(
|
||||
"ComicSource.sources.$_key.explore[$i].loadNext(${jsonEncode(next)})");
|
||||
return Res(
|
||||
List.generate(res["comics"].length,
|
||||
(index) => Comic.fromJson(res["comics"][index], _key!)),
|
||||
subData: res["maxPage"]);
|
||||
} catch (e, s) {
|
||||
Log.error("Network", "$e\n$s");
|
||||
return Res.error(e.toString());
|
||||
}
|
||||
};
|
||||
subData: res["next"],
|
||||
);
|
||||
} catch (e, s) {
|
||||
Log.error("Network", "$e\n$s");
|
||||
return Res.error(e.toString());
|
||||
}
|
||||
};
|
||||
}
|
||||
} else if (type == "multiPartPage") {
|
||||
loadMultiPart = () async {
|
||||
try {
|
||||
@@ -330,6 +378,7 @@ class ComicSourceParser {
|
||||
throw ComicSourceParseException("Unknown explore page type $type")
|
||||
},
|
||||
loadPage,
|
||||
loadNext,
|
||||
loadMultiPart,
|
||||
loadMixed,
|
||||
));
|
||||
@@ -406,21 +455,44 @@ class ComicSourceParser {
|
||||
var value = split.join("-");
|
||||
options[key] = value;
|
||||
}
|
||||
rankingData = RankingData(options, (option, page) async {
|
||||
try {
|
||||
var res = await JsEngine().runCode("""
|
||||
Future<Res<List<Comic>>> Function(String option, int page)? load;
|
||||
Future<Res<List<Comic>>> Function(String option, String? next)?
|
||||
loadWithNext;
|
||||
if (_checkExists("categoryComics.ranking.load")) {
|
||||
load = (option, page) async {
|
||||
try {
|
||||
var res = await JsEngine().runCode("""
|
||||
ComicSource.sources.$_key.categoryComics.ranking.load(
|
||||
${jsonEncode(option)}, ${jsonEncode(page)})
|
||||
""");
|
||||
return Res(
|
||||
return Res(
|
||||
List.generate(res["comics"].length,
|
||||
(index) => Comic.fromJson(res["comics"][index], _key!)),
|
||||
subData: res["maxPage"]);
|
||||
} catch (e, s) {
|
||||
Log.error("Network", "$e\n$s");
|
||||
return Res.error(e.toString());
|
||||
}
|
||||
};
|
||||
} else {
|
||||
loadWithNext = (option, next) async {
|
||||
try {
|
||||
var res = await JsEngine().runCode("""
|
||||
ComicSource.sources.$_key.categoryComics.ranking.loadWithNext(
|
||||
${jsonEncode(option)}, ${jsonEncode(next)})
|
||||
""");
|
||||
return Res(
|
||||
List.generate(res["comics"].length,
|
||||
(index) => Comic.fromJson(res["comics"][index], _key!)),
|
||||
subData: res["maxPage"]);
|
||||
} catch (e, s) {
|
||||
Log.error("Network", "$e\n$s");
|
||||
return Res.error(e.toString());
|
||||
}
|
||||
});
|
||||
subData: res["next"],
|
||||
);
|
||||
} catch (e, s) {
|
||||
Log.error("Network", "$e\n$s");
|
||||
return Res.error(e.toString());
|
||||
}
|
||||
};
|
||||
}
|
||||
rankingData = RankingData(options, load, loadWithNext);
|
||||
}
|
||||
return CategoryComicsData(options, (category, param, options, page) async {
|
||||
try {
|
||||
@@ -457,7 +529,12 @@ class ComicSourceParser {
|
||||
var value = split.join("-");
|
||||
map[key] = value;
|
||||
}
|
||||
options.add(SearchOptions(map, element["label"]));
|
||||
options.add(SearchOptions(
|
||||
map,
|
||||
element["label"],
|
||||
element['type'] ?? 'select',
|
||||
element['default'] == null ? null : jsonEncode(element['default']),
|
||||
));
|
||||
}
|
||||
return SearchPageData(options, (keyword, page, searchOption) async {
|
||||
try {
|
||||
@@ -550,24 +627,53 @@ class ComicSourceParser {
|
||||
return retryZone(func);
|
||||
}
|
||||
|
||||
Future<Res<List<Comic>>> loadComic(int page, [String? folder]) async {
|
||||
Future<Res<List<Comic>>> func() async {
|
||||
try {
|
||||
var res = await JsEngine().runCode("""
|
||||
Future<Res<List<Comic>>> Function(int page, [String? folder])? loadComic;
|
||||
|
||||
Future<Res<List<Comic>>> Function(String? next, [String? folder])? loadNext;
|
||||
|
||||
if (_checkExists("favorites.loadComic")) {
|
||||
loadComic = (int page, [String? folder]) async {
|
||||
Future<Res<List<Comic>>> func() async {
|
||||
try {
|
||||
var res = await JsEngine().runCode("""
|
||||
ComicSource.sources.$_key.favorites.loadComics(
|
||||
${jsonEncode(page)}, ${jsonEncode(folder)})
|
||||
""");
|
||||
return Res(
|
||||
return Res(
|
||||
List.generate(res["comics"].length,
|
||||
(index) => Comic.fromJson(res["comics"][index], _key!)),
|
||||
subData: res["maxPage"]);
|
||||
} catch (e, s) {
|
||||
Log.error("Network", "$e\n$s");
|
||||
return Res.error(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
return retryZone(func);
|
||||
};
|
||||
}
|
||||
|
||||
if (_checkExists("favorites.loadNext")) {
|
||||
loadNext = (String? next, [String? folder]) async {
|
||||
Future<Res<List<Comic>>> func() async {
|
||||
try {
|
||||
var res = await JsEngine().runCode("""
|
||||
ComicSource.sources.$_key.favorites.loadNext(
|
||||
${jsonEncode(next)}, ${jsonEncode(folder)})
|
||||
""");
|
||||
return Res(
|
||||
List.generate(res["comics"].length,
|
||||
(index) => Comic.fromJson(res["comics"][index], _key!)),
|
||||
subData: res["maxPage"]);
|
||||
} catch (e, s) {
|
||||
Log.error("Network", "$e\n$s");
|
||||
return Res.error(e.toString());
|
||||
subData: res["next"],
|
||||
);
|
||||
} catch (e, s) {
|
||||
Log.error("Network", "$e\n$s");
|
||||
return Res.error(e.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return retryZone(func);
|
||||
return retryZone(func);
|
||||
};
|
||||
}
|
||||
|
||||
Future<Res<Map<String, String>>> Function([String? comicId])? loadFolders;
|
||||
@@ -625,6 +731,7 @@ class ComicSourceParser {
|
||||
title: _name!,
|
||||
multiFolder: multiFolder,
|
||||
loadComic: loadComic,
|
||||
loadNext: loadNext,
|
||||
loadFolders: loadFolders,
|
||||
addFolder: addFolder,
|
||||
deleteFolder: deleteFolder,
|
||||
@@ -683,11 +790,15 @@ class ComicSourceParser {
|
||||
if (!_checkExists("comic.onImageLoad")) {
|
||||
return null;
|
||||
}
|
||||
return (imageKey, comicId, ep) {
|
||||
return JsEngine().runCode("""
|
||||
return (imageKey, comicId, ep) async {
|
||||
var res = JsEngine().runCode("""
|
||||
ComicSource.sources.$_key.comic.onImageLoad(
|
||||
${jsonEncode(imageKey)}, ${jsonEncode(comicId)}, ${jsonEncode(ep)})
|
||||
""") as Map<String, dynamic>;
|
||||
""");
|
||||
if (res is Future) {
|
||||
return await res;
|
||||
}
|
||||
return res;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -826,4 +937,21 @@ class ComicSourceParser {
|
||||
|
||||
return LinkHandler(domains, linkToId);
|
||||
}
|
||||
|
||||
StarRatingFunc? _parseStarRatingFunc() {
|
||||
if (!_checkExists("comic.starRating")) {
|
||||
return null;
|
||||
}
|
||||
return (id, rating) async {
|
||||
try {
|
||||
await JsEngine().runCode("""
|
||||
ComicSource.sources.$_key.comic.starRating(${jsonEncode(id)}, ${jsonEncode(rating)})
|
||||
""");
|
||||
return const Res(true);
|
||||
} catch (e, s) {
|
||||
Log.error("Network", "$e\n$s");
|
||||
return Res.error(e.toString());
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user