import 'package:hgdj/hj_model/drama_media_info.dart'; import 'package:hgdj/hj_model/media_content.dart'; /// 一条短剧观看记录:续播位置 + 列表展示用的剧信息快照。本地记录,不走后端,见 [DramaResumeStore]。 /// 落库时整条存成 model_data,所以 [mediaId] 必须写进 json—— /// 基类只回 model_data 一列,读盘时全靠它认领是哪部剧 class DramaResume { final String mediaId; //哪部剧,同时是行主键 final String contentId; //上次看的分集。定位只认它,别再存「第几集」——分页深度靠边翻边找就够了 final int progressSeconds; //看到第几秒 /// 剧信息快照(封面/剧名/集数)。续播定位用不到,是历史记录页拿它画卡片—— /// 只存 id 的话列表除了 id 什么都渲染不出来 final DramaMediaInfo? drama; /// 本条写入时刻(毫秒)。历史记录页按它倒序,不看 db 的 create_time—— /// 老数据没这个字段,读出来是 0,排在最后 final int watchedAt; const DramaResume({ required this.mediaId, required this.contentId, required this.progressSeconds, this.drama, this.watchedAt = 0, }); DramaResume.fromJson(dynamic json) : mediaId = json?['mediaId'] ?? '', contentId = json?['contentId'] ?? '', progressSeconds = json?['progressSeconds'] ?? 0, drama = json?['drama'] == null ? null : DramaMediaInfo.fromJson(json['drama']), watchedAt = json?['watchedAt'] ?? 0; Map toJson() => { 'mediaId': mediaId, 'contentId': contentId, 'progressSeconds': progressSeconds, 'drama': drama?.toJson(), 'watchedAt': watchedAt, }; } /// 付费墙:文案/价格/余额全部服务端下发,前端不自己拼 class DramaPaywall { String? checkoutContextId; //本次付费墙上下文,充值/开卡归因和下单都要带 String? title; int? coinBalance; int? unlockCoin; String? coinButtonText; String? cardButtonText; DramaPaywall.fromJson(dynamic json) { json ??= {}; checkoutContextId = json['checkoutContextId']; title = json['title']; coinBalance = json['coinBalance']; unlockCoin = json['unlockCoin']; coinButtonText = json['coinButtonText']; cardButtonText = json['cardButtonText']; } String get titleUI => title?.isNotEmpty == true ? title! : '更多精彩解锁即享'; String get coinButtonUI => coinButtonText?.isNotEmpty == true ? coinButtonText! : '${unlockCoin ?? 0}金币解锁'; String get cardButtonUI => cardButtonText?.isNotEmpty == true ? cardButtonText! : '开通短剧卡免费看'; } /// AI短剧 Feed 的一条:某部剧 + 该剧第一集。整页走 ListBaseModel class DramaFeedItem { DramaMediaInfo? media; MediaContent? content; DramaFeedItem.fromJson(dynamic json) { json ??= {}; //缺 media 就不造空模型,上层按 null 过滤掉这条,别让信息流多出一个没剧名没地址的空位 if (json['media'] != null) media = DramaMediaInfo.fromJson(json['media']); if (json['content'] != null) content = MediaContent.fromJson(json['content']); } } /// 短剧专题(热门短剧 Tab) class DramaTopic { String? topicId; String? name; String? topicType; String? systemKey; int? sort; int? workCount; DramaTopic({ this.topicId, this.name, this.topicType, this.systemKey, this.sort, this.workCount, }); DramaTopic.fromJson(dynamic json) { json ??= {}; topicId = json['topicId']?.toString(); name = json['name']?.toString(); topicType = json['topicType']?.toString(); systemKey = json['systemKey']?.toString(); sort = json['sort'] is int ? json['sort'] as int : int.tryParse('${json['sort'] ?? ''}'); workCount = json['workCount'] is int ? json['workCount'] as int : int.tryParse('${json['workCount'] ?? ''}'); } } /// 短剧搜索结果(`/media/search` kind=4) class DramaSearchResult { List list = []; List tagMediaList = []; String? tagID; bool hasNext = false; DramaSearchResult.fromJson(dynamic json) { json ??= {}; hasNext = json['hasNext'] == true; tagID = json['tagID']?.toString(); list = (json['list'] as List?) ?.map((e) => DramaMediaInfo.fromJson(e)) .toList() ?? []; tagMediaList = (json['tagMediaList'] as List?) ?.map((e) => DramaMediaInfo.fromJson(e)) .toList() ?? []; } } /// 单集金币解锁的下单结果 class DramaUnlockResult { String? orderId; String? mediaId; String? contentId; int? paidCoin; int? coinBalance; DramaUnlockResult.fromJson(Map? json) { json ??= {}; orderId = json['orderId']; mediaId = json['mediaId']; contentId = json['contentId']; paidCoin = json['paidCoin']; coinBalance = json['coinBalance']; } } /// 短剧单集下载授权结果。权益/上下架/次数校验和扣次都在服务端一次做完, /// 客户端只负责拿地址开下载,不再自己算次数 class DramaDownloadAuth { String? mediaId; String? contentId; int? episodeNumber; String? name; String? cover; String? downloadUrl; // H.264 m3u8,可能是相对地址 String? h265DownloadUrl; // H.265 m3u8,没有时为空串 int? mediaSize; // 字节 String? expiresAt; // 本次地址失效时间(签发后 6 小时) int? remainingDownloadCount; // 扣完之后钱包剩余下载次数,直接用它顶掉本地值 bool? charged; // false = 幂等重试命中,没有重复扣次 DramaDownloadAuth.fromJson(Map? json) { json ??= {}; mediaId = json['mediaId']; contentId = json['contentId']; episodeNumber = json['episodeNumber']; name = json['name']; cover = json['cover']; downloadUrl = json['downloadUrl']; h265DownloadUrl = json['h265DownloadUrl']; mediaSize = json['mediaSize']; expiresAt = json['expiresAt']; remainingDownloadCount = json['remainingDownloadCount']; charged = json['charged'] == true; } }