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

192 lines
6.1 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: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<String, dynamic> 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<DramaFeedItem>
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<DramaMediaInfo> list = [];
List<DramaMediaInfo> 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<String, dynamic>? 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<String, dynamic>? 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;
}
}