This commit is contained in:
nyne
2024-10-06 15:31:53 +08:00
parent 3a0fbee7bc
commit 5ccd0af2d8
6 changed files with 763 additions and 235 deletions

View File

@@ -17,10 +17,9 @@ import '../js_engine.dart';
import '../log.dart';
part 'category.dart';
part 'favorites.dart';
part 'parser.dart';
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);
@@ -43,9 +42,19 @@ typedef GetImageLoadingConfigFunc = Map<String, dynamic> Function(
typedef GetThumbnailLoadingConfigFunc = Map<String, dynamic> Function(
String imageKey)?;
typedef ComicThumbnailLoader = Future<Res<List<String>>> Function(String comicId, String? next);
typedef ComicThumbnailLoader = Future<Res<List<String>>> Function(
String comicId, String? next);
typedef LikeOrUnlikeComicFunc = Future<Res<bool>> Function(String comicId, bool isLiking);
typedef LikeOrUnlikeComicFunc = Future<Res<bool>> Function(
String comicId, bool isLiking);
/// [isLiking] is true if the user is liking the comment, false if unliking.
/// return the new likes count or null.
typedef LikeCommentFunc = Future<Res<int?>> Function(String comicId, String? subId, String commentId, bool isLiking);
/// [isUp] is true if the user is upvoting the comment, false if downvoting.
/// return the new vote count or null.
typedef VoteCommentFunc = Future<Res<int?>> Function(String comicId, String? subId, String commentId, bool isUp, bool isCancel);
class ComicSource {
static final List<ComicSource> _sources = [];
@@ -155,8 +164,6 @@ class ComicSource {
final Map<String, dynamic> Function(String imageKey)?
getThumbnailLoadingConfig;
final String? matchBriefIdReg;
var data = <String, dynamic>{};
bool get isLogged => data["account"] != null;
@@ -175,6 +182,10 @@ class ComicSource {
final LikeOrUnlikeComicFunc? likeOrUnlikeComic;
final VoteCommentFunc? voteCommentFunc;
final LikeCommentFunc? likeCommentFunc;
Future<void> loadData() async {
var file = File("${App.dataPath}/comic_source/$key.data");
if (await file.exists()) {
@@ -228,37 +239,15 @@ class ComicSource {
this.loadComicPages,
this.getImageLoadingConfig,
this.getThumbnailLoadingConfig,
this.matchBriefIdReg,
this.filePath,
this.url,
this.version,
this.commentsLoader,
this.sendCommentFunc,
this.likeOrUnlikeComic)
this.likeOrUnlikeComic,
this.voteCommentFunc,
this.likeCommentFunc,)
: idMatcher = null;
ComicSource.unknown(this.key)
: name = "Unknown",
account = null,
categoryData = null,
categoryComicsData = null,
favoriteData = null,
explorePages = [],
searchPageData = null,
settings = [],
loadComicInfo = null,
loadComicThumbnail = null,
loadComicPages = null,
getImageLoadingConfig = null,
getThumbnailLoadingConfig = null,
matchBriefIdReg = null,
filePath = "",
url = "",
version = "",
commentsLoader = null,
sendCommentFunc = null,
idMatcher = null,
likeOrUnlikeComic = null;
}
class AccountConfig {
@@ -394,131 +383,6 @@ enum SettingType {
input,
}
class Comic {
final String title;
final String cover;
final String id;
final String? subtitle;
final List<String>? tags;
final String description;
final String sourceKey;
final int? maxPage;
const Comic(this.title, this.cover, this.id, this.subtitle, this.tags,
this.description, this.sourceKey, this.maxPage);
Map<String, dynamic> toJson() {
return {
"title": title,
"cover": cover,
"id": id,
"subTitle": subtitle,
"tags": tags,
"description": description,
"sourceKey": sourceKey,
"maxPage": maxPage,
};
}
Comic.fromJson(Map<String, dynamic> json, this.sourceKey)
: title = json["title"],
subtitle = json["subTitle"] ?? "",
cover = json["cover"],
id = json["id"],
tags = List<String>.from(json["tags"] ?? []),
description = json["description"] ?? "",
maxPage = json["maxPage"];
}
class ComicDetails with HistoryMixin {
@override
final String title;
@override
final String? subTitle;
@override
final String cover;
final String? description;
final Map<String, List<String>> tags;
/// id-name
final Map<String, String>? chapters;
final List<String>? thumbnails;
final List<Comic>? recommend;
final String sourceKey;
final String comicId;
final bool? isFavorite;
final String? subId;
final bool? isLiked;
final int? likesCount;
final int? commentsCount;
final String? uploader;
final String? uploadTime;
final String? updateTime;
static Map<String, List<String>> _generateMap(Map<String, dynamic> map) {
var res = <String, List<String>>{};
map.forEach((key, value) {
res[key] = List<String>.from(value);
});
return res;
}
ComicDetails.fromJson(Map<String, dynamic> json)
: title = json["title"],
subTitle = json["subTitle"],
cover = json["cover"],
description = json["description"],
tags = _generateMap(json["tags"]),
chapters = json["chapters"] == null
? null
: Map<String, String>.from(json["chapters"]),
sourceKey = json["sourceKey"],
comicId = json["comicId"],
thumbnails = ListOrNull.from(json["thumbnails"]),
recommend = (json["recommend"] as List?)
?.map((e) => Comic.fromJson(e, json["sourceKey"]))
.toList(),
isFavorite = json["isFavorite"],
subId = json["subId"],
likesCount = json["likesCount"],
isLiked = json["isLiked"],
commentsCount = json["commentsCount"],
uploader = json["uploader"],
uploadTime = json["uploadTime"],
updateTime = json["updateTime"];
@override
HistoryType get historyType => HistoryType(sourceKey.hashCode);
@override
String get id => comicId;
ComicType get comicType => ComicType(sourceKey.hashCode);
}
typedef CategoryComicsLoader = Future<Res<List<Comic>>> Function(
String category, String? param, List<String> options, int page);
@@ -560,14 +424,3 @@ class CategoryComicsOptions {
const CategoryComicsOptions(this.options, this.notShowWhen, this.showWhen);
}
class Comment {
final String userName;
final String? avatar;
final String content;
final String? time;
final int? replyCount;
final String? id;
const Comment(this.userName, this.avatar, this.content, this.time,
this.replyCount, this.id);
}

View File

@@ -0,0 +1,149 @@
part of 'comic_source.dart';
class Comment {
final String userName;
final String? avatar;
final String content;
final String? time;
final int? replyCount;
final String? id;
final int? score;
final bool? isLiked;
final int? voteStatus; // 1: upvote, -1: downvote, 0: none
Comment.fromJson(Map<String, dynamic> json)
: userName = json["userName"],
avatar = json["avatar"],
content = json["content"],
time = json["time"],
replyCount = json["replyCount"],
id = json["id"].toString(),
score = json["score"],
isLiked = json["isLiked"],
voteStatus = json["voteStatus"];
}
class Comic {
final String title;
final String cover;
final String id;
final String? subtitle;
final List<String>? tags;
final String description;
final String sourceKey;
final int? maxPage;
const Comic(this.title, this.cover, this.id, this.subtitle, this.tags,
this.description, this.sourceKey, this.maxPage);
Map<String, dynamic> toJson() {
return {
"title": title,
"cover": cover,
"id": id,
"subTitle": subtitle,
"tags": tags,
"description": description,
"sourceKey": sourceKey,
"maxPage": maxPage,
};
}
Comic.fromJson(Map<String, dynamic> json, this.sourceKey)
: title = json["title"],
subtitle = json["subTitle"] ?? "",
cover = json["cover"],
id = json["id"],
tags = List<String>.from(json["tags"] ?? []),
description = json["description"] ?? "",
maxPage = json["maxPage"];
}
class ComicDetails with HistoryMixin {
@override
final String title;
@override
final String? subTitle;
@override
final String cover;
final String? description;
final Map<String, List<String>> tags;
/// id-name
final Map<String, String>? chapters;
final List<String>? thumbnails;
final List<Comic>? recommend;
final String sourceKey;
final String comicId;
final bool? isFavorite;
final String? subId;
final bool? isLiked;
final int? likesCount;
final int? commentsCount;
final String? uploader;
final String? uploadTime;
final String? updateTime;
static Map<String, List<String>> _generateMap(Map<String, dynamic> map) {
var res = <String, List<String>>{};
map.forEach((key, value) {
res[key] = List<String>.from(value);
});
return res;
}
ComicDetails.fromJson(Map<String, dynamic> json)
: title = json["title"],
subTitle = json["subTitle"],
cover = json["cover"],
description = json["description"],
tags = _generateMap(json["tags"]),
chapters = json["chapters"] == null
? null
: Map<String, String>.from(json["chapters"]),
sourceKey = json["sourceKey"],
comicId = json["comicId"],
thumbnails = ListOrNull.from(json["thumbnails"]),
recommend = (json["recommend"] as List?)
?.map((e) => Comic.fromJson(e, json["sourceKey"]))
.toList(),
isFavorite = json["isFavorite"],
subId = json["subId"],
likesCount = json["likesCount"],
isLiked = json["isLiked"],
commentsCount = json["commentsCount"],
uploader = json["uploader"],
uploadTime = json["uploadTime"],
updateTime = json["updateTime"];
@override
HistoryType get historyType => HistoryType(sourceKey.hashCode);
@override
String get id => comicId;
ComicType get comicType => ComicType(sourceKey.hashCode);
}

View File

@@ -103,8 +103,6 @@ class ComicSourceParser {
(throw ComicSourceParseException('version is required'));
var minAppVersion = JsEngine().runCode("this['temp'].minAppVersion");
var url = JsEngine().runCode("this['temp'].url");
var matchBriefIdRegex =
JsEngine().runCode("this['temp'].comic.matchBriefIdRegex");
if (minAppVersion != null) {
if (compareSemVer(minAppVersion, App.version.split('-').first)) {
throw ComicSourceParseException(
@@ -123,43 +121,29 @@ class ComicSourceParser {
ComicSource.sources.$_key = this['temp'];
""");
final account = _loadAccountConfig();
final explorePageData = _loadExploreData();
final categoryPageData = _loadCategoryData();
final categoryComicsData = _loadCategoryComicsData();
final searchData = _loadSearchData();
final loadComicFunc = _parseLoadComicFunc();
final loadComicThumbnailFunc = _parseThumbnailLoader();
final loadComicPagesFunc = _parseLoadComicPagesFunc();
final getImageLoadingConfigFunc = _parseImageLoadingConfigFunc();
final getThumbnailLoadingConfigFunc = _parseThumbnailLoadingConfigFunc();
final favoriteData = _loadFavoriteData();
final commentsLoader = _parseCommentsLoader();
final sendCommentFunc = _parseSendCommentFunc();
final likeFunc = _parseLikeFunc();
var source = ComicSource(
_name!,
key,
account,
categoryPageData,
categoryComicsData,
favoriteData,
explorePageData,
searchData,
_loadAccountConfig(),
_loadCategoryData(),
_loadCategoryComicsData(),
_loadFavoriteData(),
_loadExploreData(),
_loadSearchData(),
[],
loadComicFunc,
loadComicThumbnailFunc,
loadComicPagesFunc,
getImageLoadingConfigFunc,
getThumbnailLoadingConfigFunc,
matchBriefIdRegex,
_parseLoadComicFunc(),
_parseThumbnailLoader(),
_parseLoadComicPagesFunc(),
_parseImageLoadingConfigFunc(),
_parseThumbnailLoadingConfigFunc(),
filePath,
url ?? "",
version ?? "1.0.0",
commentsLoader,
sendCommentFunc,
likeFunc,
_parseCommentsLoader(),
_parseSendCommentFunc(),
_parseLikeFunc(),
_parseVoteCommentFunc(),
_parseLikeCommentFunc(),
);
await source.loadData();
@@ -571,10 +555,7 @@ class ComicSourceParser {
${jsonEncode(id)}, ${jsonEncode(subId)}, ${jsonEncode(page)}, ${jsonEncode(replyTo)})
""");
return Res(
(res["comments"] as List)
.map((e) => Comment(e["userName"], e["avatar"], e["content"],
e["time"], e["replyCount"], e["id"].toString()))
.toList(),
(res["comments"] as List).map((e) => Comment.fromJson(e)).toList(),
subData: res["maxPage"]);
} catch (e, s) {
Log.error("Network", "$e\n$s");
@@ -673,4 +654,38 @@ class ComicSourceParser {
}
};
}
VoteCommentFunc? _parseVoteCommentFunc() {
if (!_checkExists("comic.voteComment")) {
return null;
}
return (id, subId, commentId, isUp, isCancel) async {
try {
var res = await JsEngine().runCode("""
ComicSource.sources.$_key.comic.voteComment(${jsonEncode(id)}, ${jsonEncode(subId)}, ${jsonEncode(commentId)}, ${jsonEncode(isUp)}, ${jsonEncode(isCancel)})
""");
return Res(res is num ? res.toInt() : 0);
} catch (e, s) {
Log.error("Network", "$e\n$s");
return Res.error(e.toString());
}
};
}
LikeCommentFunc? _parseLikeCommentFunc() {
if (!_checkExists("comic.likeComment")) {
return null;
}
return (id, subId, commentId, isLiking) async {
try {
var res = await JsEngine().runCode("""
ComicSource.sources.$_key.comic.likeComment(${jsonEncode(id)}, ${jsonEncode(subId)}, ${jsonEncode(commentId)}, ${jsonEncode(isLiking)})
""");
return Res(res is num ? res.toInt() : 0);
} catch (e, s) {
Log.error("Network", "$e\n$s");
return Res.error(e.toString());
}
};
}
}