Files
2026-09-15 15:44:13 +07:00

370 lines
12 KiB
Dart
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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;
}
}