初始化

This commit is contained in:
谢宇宁
2026-09-15 15:44:13 +07:00
commit a23ba685e9
1065 changed files with 101122 additions and 0 deletions
+369
View File
@@ -0,0 +1,369 @@
import 'package:flutter/material.dart';
import 'package:hgdj/assets_tool/app_colors.dart';
import 'package:hgdj/hj_model/splash/ads_model.dart';
import 'package:hgdj/hj_model/video_model.dart';
import 'package:hgdj/tools_base/global_store/store.dart';
import 'acg/comic_chapters_model.dart';
import 'actress_info.dart';
import 'media_content.dart';
class CartoonMediaInfo {
// —— 基础信息 ——
String? id; //文档id
String? title; //标题
String? author; //作者
String? summary; //简介
String? mediaType; //媒体类型 "video":动漫,"image":漫画 "text":小说
int? mediaSubType; //子类型 ,暂时只有小说使用 0-默认文本小说 1-有声小说
int? kind; //种类(1、哩番,2、3D,3、同人动画,4、单行本,5、连载漫画,6、韩漫 )
int? status; //状态
VidStatus? mediaStatus; //媒体状态
String? lsjId; // 老司机媒体资源ID
// —— 封面 ——
String? horizontalCover; //横版封面
String? verticalCover; //竖版封面
// —— 集数 / 更新 ——
int? totalEpisode; //总集数
int? currentEpisode; //当前集数
int? freeEpisode; //免费集数
bool isCartoonFreeEpisode =
false; //当前播放集是否属于免费集(前 freeEpisode 集):运行时按当前集号计算,命中则不拦播、进度可全程拖
int? updateStatus; //更新状态 0 默认 1、更新中 2、已完结
String? updateTime; //文档更新时间
String? createdAt; //文档创建时间
// —— 价格 / 权限 ——
int? price; //价格(整部购买的类型 小说 cos 写真)
int? discountPrice; // 折扣价(后端字段,App 未使用)
int? contentsPrice; //所有子集的价格
int? permission; // 0:会员 1:金币购买 2:免费
bool? permissionIconHide; //售卖类型标识隐藏
// —— 专题 / 模块 ——
String? mId; //模块ID
String? moduleName; //模块名称
String? sId; //专题id
String? sectionName; //专题名称
int? sectionSort; //专题排序
int? sortCode; //排序
// —— 统计 ——
int? countBrowse; //浏览数
int? countCollect; //收藏数
int? countDisLike; //不喜欢数
int? countComment; //评论数
int? countLike; //喜欢数
int? countPurchases; //购买数
int? countView; //展现数
bool? hasFollow; //是否关注
// —— 标签 / 演员 / 内容 ——
List<TagsBean>? tagDetails; //标签对象
List<ActressInfo>? allActress; // 演员列表
ActressInfo? actress; // 主演员
List<MediaContent>? contents; // 子集内容列表
AdsInfoModel? randomAdsInfo; // 随机广告
// —— 其他后端字段 ——
int? direction; // 方向(横/竖)
int? freeTime; // 免费试看时长
bool? isDelete; // 是否已删除
int? choiceSort; // 后端字段,App 未使用
int? countPurchasesRate; // 后端字段,App 未使用
String? updatedAct; // 后端字段,App 未使用
// —— 本地 / UI 字段(非后端返回) ——
List<ComicChapterInfo>? episodeList; // 通过接口获取,外部赋值(改版了,不是同一个接口返回)
int? episodeCurPage; // 当前页码
int episodePageSize = 40; // 每页集数
int isUpSort = 0; //0-正序 1-倒序
bool haxNextEpisode = true; // 是否还有下一页
int? episodeNumber; //本地添加字段。用于判断,跳转到播放详情的index
bool isSelected = false; // 是否选中
String? heroTag; //本地字段,用于传递hero动画tag 随机数
String? get coverH =>
horizontalCover?.isNotEmpty == true ? horizontalCover : verticalCover;
String? get coverV =>
verticalCover?.isNotEmpty == true ? verticalCover : horizontalCover;
String get unit => mediaType == 'image' ? '' : '';
//更新状态
String get updateDesc => updateStatus == 1 ? '连载中' : '已完结';
String get tagIds => tagDetails?.map((e) => e.id).toList().join(',') ?? '';
String get tagNames =>
tagDetails?.map((e) => e.name).toList().join(',') ?? '';
//更新状态
String get getDetailUpdateString {
if (updateStatus == 2 || updateStatus == 0) {
return '已完结';
} else {
return '连载中';
}
}
Color get getDetailUpdateColor {
if (updateStatus == 2 || updateStatus == 0) {
return Color(0xff989898);
} else {
return AppColors.actionRed;
}
}
//是否是有声小说
bool get isAudiobooks => mediaType == 'text' && mediaSubType == 1;
//集数状态描述
String get episodeNumberStatus {
if (updateStatus == 1) {
if (currentEpisode == 0) {
return '$totalEpisode$unit';
} else {
return '更新$currentEpisode$unit';
}
} else {
return '$totalEpisode$unit';
}
}
//评论类型,传参数类型
String get commentType => 'cartoon';
/// 该集是否落在前 N 集免费区间内。
/// 集号缺失兜底为 1(与项目其他处 `episodeNumber ?? 1` 口径一致);服务端真给 0 或负数时判为非免费
bool isFreeEpisodeNumber(int? episodeNumber) {
final ep = episodeNumber ?? 1;
return ep >= 1 && (freeEpisode ?? 0) >= ep;
}
/// 转成播放器用的 [VideoModel][episode] 为当前集
/// 顺带把 [isCartoonFreeEpisode] 按当前集号刷新一次(有副作用,别当纯转换用)
VideoModel toVideoModel(ComicChapterInfo? episode) {
final model = VideoModel()
..videoType = 1
..id = id
..totalEpisode = totalEpisode
..updateStatus = updateStatus
..mediaType = mediaType
..mediaInfo = this
..title = title
..tags = tagDetails
..cover = coverH
..likeCount = countCollect // 视频likecount是收藏数
..freeTime = freeTime ?? 0
..collectCount = countCollect
..commentCount = countComment
..playCount = countBrowse
..freeArea = permission == 2
..coins = price
..originCoins = price
..vidStatus = (VidStatus()
..hasPaid = mediaStatus?.hasPaid == true
..hasCollected = mediaStatus?.hasCollected
..hasLiked = mediaStatus?.hasLiked
..hasDisliked = mediaStatus?.hasDisliked);
// 动漫免费集:当前集号在前 freeEpisode 集内 → 播放不拦、进度可全程拖(与 video_logic 免费章节判定同一口径)
isCartoonFreeEpisode = isFreeEpisodeNumber(episode?.episodeNumber);
final list = episodeList ?? [];
if (list.isNotEmpty) {
//在列表里找不到当前集就退回第一集,别让播放信息整块空掉
final index = list.indexWhere((e) => e.id == episode?.id);
final content = list[index < 0 ? 0 : index].mediaContent;
model
..subid = content?.id
..name = content?.name
..sourceURL = content?.videoUrl
..playTime = content?.playTime;
}
return model;
}
//acg子集角标显示逻辑,0:会员 1:金币购买 2:免费。为 false(无任何权限)时才展示角标
bool get hasPermission {
return (mediaStatus?.hasPaid == true && permission == 1) ||
permission == 2 ||
(permission == 0 && globalStore.isVIP) ||
globalStore.isSuperUp;
}
CartoonMediaInfo(
{this.contents,
this.choiceSort,
this.countBrowse,
this.countCollect,
this.countComment,
this.countLike,
this.countPurchases,
this.countPurchasesRate,
this.countView,
this.createdAt,
this.direction,
this.freeTime,
this.horizontalCover,
this.id,
this.isDelete,
this.kind,
this.mId,
this.mediaType,
this.moduleName,
this.permission,
this.permissionIconHide,
this.price,
this.sId,
this.discountPrice,
this.sectionName,
this.sectionSort,
this.sortCode,
this.status,
this.summary,
this.tagDetails,
this.title,
this.totalEpisode,
this.currentEpisode,
this.updateStatus,
this.updateTime,
this.updatedAct,
this.verticalCover,
this.author,
this.freeEpisode,
this.contentsPrice,
this.mediaSubType,
this.actress});
CartoonMediaInfo.fromJson(dynamic json) {
freeEpisode = json['freeEpisode'];
mediaSubType = json['mediaSubType'];
author = json['author'];
choiceSort = json['choiceSort'];
totalEpisode = json['totalEpisode'];
currentEpisode = json['currentEpisode'];
countBrowse = json['countBrowse'];
countCollect = json['countCollect'];
countComment = json['countComment'];
countLike = json['countLike'];
countDisLike = json['countDisLike'];
countPurchases = json['countPurchases'];
countPurchasesRate = json['countPurchasesRate'];
discountPrice = json['discountPrice'];
countView = json['countView'];
createdAt = json['createdAt'];
direction = json['direction'];
freeTime = json['freeTime'];
horizontalCover = json['horizontalCover'];
id = json['id'];
isDelete = json['isDelete'];
kind = json['kind'];
mId = json['mId'];
mediaType = json['mediaType'];
moduleName = json['moduleName'];
permission = json['permission'];
permissionIconHide = json['permissionIconHide'];
price = json['price'];
sId = json['sId'];
sectionName = json['sectionName'];
sectionSort = json['sectionSort'];
sortCode = json['sortCode'];
status = json['status'];
summary = json['summary'];
lsjId = json['lsjId'];
actress =
json["actress"] == null ? null : ActressInfo.fromJson(json["actress"]);
allActress = json["actresses"] == null
? []
: List<ActressInfo>.from(
json["actresses"]!.map((x) => ActressInfo.fromJson(x)));
hasFollow = json['hasFollow'];
if (json['tagDetails'] != null) {
tagDetails = [];
json['tagDetails'].forEach((v) {
tagDetails?.add(TagsBean.fromMap(v));
});
}
if (json['contents'] != null) {
contents = [];
json['contents'].forEach((v) {
contents?.add(MediaContent.fromJson(v));
});
}
if (json['title'] == null) {
title = json['name'];
} else {
title = json['title'];
}
updateStatus = json['updateStatus'];
updateTime = json['updateTime'];
updatedAct = json['updatedAct'];
verticalCover = json['verticalCover'];
mediaStatus = VidStatus.fromMap(json['mediaStatus']);
contentsPrice = json['contentsPrice'];
}
///videoModel是否随机广告
bool isRandomAd() {
return randomAdsInfo != null;
}
Map<String, dynamic> toJson() {
final map = <String, dynamic>{};
map['mediaSubType'] = mediaSubType;
map['choiceSort'] = choiceSort;
map['countBrowse'] = countBrowse;
map['countCollect'] = countCollect;
map['countComment'] = countComment;
map['countLike'] = countLike;
map['countDisLike'] = countDisLike;
map['countPurchases'] = countPurchases;
map['countPurchasesRate'] = countPurchasesRate;
map['countView'] = countView;
map['createdAt'] = createdAt;
map['direction'] = direction;
map['freeTime'] = freeTime;
map['horizontalCover'] = horizontalCover;
map['id'] = id;
map['isDelete'] = isDelete;
map['kind'] = kind;
map['mId'] = mId;
map['mediaType'] = mediaType;
map['moduleName'] = moduleName;
map['permission'] = permission;
map['permissionIconHide'] = permissionIconHide;
map['price'] = price;
map['sId'] = sId;
map['sectionName'] = sectionName;
map['sectionSort'] = sectionSort;
map['sortCode'] = sortCode;
map['status'] = status;
map['summary'] = summary;
map['lsjId'] = lsjId;
if (tagDetails != null) {
map['tagDetails'] = tagDetails?.map((v) => v.toJson()).toList();
}
if (contents != null) {
map['contents'] = contents?.map((v) => v.toJson()).toList();
}
map['hasFollow'] = hasFollow;
map['title'] = title;
map['totalEpisode'] = totalEpisode;
map['currentEpisode'] = currentEpisode;
map['updateTime'] = updateTime;
map['updateStatus'] = updateStatus;
map['updatedAct'] = updatedAct;
map['verticalCover'] = verticalCover;
map['mediaStatus'] = mediaStatus;
return map;
}
}