156 lines
4.9 KiB
Dart
156 lines
4.9 KiB
Dart
/// Feed 视频项(GET /videos/feed 列表项,对应服务端 video_service 输出)
|
||
class VideoItem {
|
||
VideoItem({
|
||
required this.videoId,
|
||
this.title = '',
|
||
this.coverUrl = '',
|
||
this.videoUrl = '',
|
||
this.duration = 0,
|
||
required this.author,
|
||
this.categoryId = 0,
|
||
this.sourceId = 0,
|
||
this.sourceName = '',
|
||
this.viewCount = 0,
|
||
this.likeCount = 0,
|
||
this.commentCount = 0,
|
||
this.favoriteCount = 0,
|
||
this.tags = const [],
|
||
this.isLiked = false,
|
||
this.isFavorited = false,
|
||
this.description = '',
|
||
this.status = -1,
|
||
this.statusText = '',
|
||
this.rejectReason = '',
|
||
this.seriesId = 0,
|
||
this.totalEpisodes = 0,
|
||
});
|
||
|
||
final int videoId;
|
||
final String title;
|
||
final String coverUrl;
|
||
final String videoUrl; // m3u8
|
||
final int duration; // 秒
|
||
final VideoAuthor author;
|
||
final int categoryId;
|
||
|
||
/// 内容来源(麻豆 / 91 ...)
|
||
final int sourceId;
|
||
final String sourceName;
|
||
|
||
final int viewCount;
|
||
final int likeCount;
|
||
final int commentCount;
|
||
final int favoriteCount;
|
||
final List<String> tags;
|
||
final bool isLiked;
|
||
final bool isFavorited;
|
||
final String description;
|
||
|
||
/// 作品状态(仅「我的作品」接口返回):-1 未提供;0转码中 1待审核 2已发布 3拒绝 4下架 5转码失败
|
||
final int status;
|
||
final String statusText;
|
||
final String rejectReason;
|
||
|
||
/// 所属系列 id(0 = 非系列视频);系列聚合 Feed 中为首集 video_id
|
||
final int seriesId;
|
||
|
||
/// 系列总集数(系列聚合 Feed 返回,用于卡片「全N集」角标)
|
||
final int totalEpisodes;
|
||
|
||
/// 拷贝更新(点赞/收藏乐观更新用)
|
||
VideoItem copyWith({
|
||
bool? isLiked,
|
||
bool? isFavorited,
|
||
int? likeCount,
|
||
int? favoriteCount,
|
||
int? commentCount,
|
||
}) =>
|
||
VideoItem(
|
||
videoId: videoId,
|
||
title: title,
|
||
coverUrl: coverUrl,
|
||
videoUrl: videoUrl,
|
||
duration: duration,
|
||
author: author,
|
||
categoryId: categoryId,
|
||
sourceId: sourceId,
|
||
sourceName: sourceName,
|
||
viewCount: viewCount,
|
||
likeCount: likeCount ?? this.likeCount,
|
||
commentCount: commentCount ?? this.commentCount,
|
||
favoriteCount: favoriteCount ?? this.favoriteCount,
|
||
tags: tags,
|
||
isLiked: isLiked ?? this.isLiked,
|
||
isFavorited: isFavorited ?? this.isFavorited,
|
||
description: description,
|
||
status: status,
|
||
statusText: statusText,
|
||
rejectReason: rejectReason,
|
||
seriesId: seriesId,
|
||
totalEpisodes: totalEpisodes,
|
||
);
|
||
|
||
factory VideoItem.fromJson(Map<String, dynamic> json) => VideoItem(
|
||
videoId: json['video_id'] as int,
|
||
title: (json['title'] as String?) ?? '',
|
||
coverUrl: (json['cover_url'] as String?) ?? '',
|
||
videoUrl: (json['video_url'] as String?) ?? '',
|
||
duration: (json['duration'] as num?)?.toInt() ?? 0,
|
||
categoryId: (json['category_id'] as num?)?.toInt() ?? 0,
|
||
sourceId: (json['source_id'] as num?)?.toInt() ?? 0,
|
||
sourceName: (json['source_name'] as String?) ?? '',
|
||
author: VideoAuthor.fromJson(
|
||
(json['author'] as Map<String, dynamic>?) ?? const {},
|
||
),
|
||
viewCount: (json['view_count'] as num?)?.toInt() ?? 0,
|
||
likeCount: (json['like_count'] as num?)?.toInt() ?? 0,
|
||
commentCount: (json['comment_count'] as num?)?.toInt() ?? 0,
|
||
favoriteCount: (json['favorite_count'] as num?)?.toInt() ?? 0,
|
||
tags: ((json['tags'] as List?) ?? const [])
|
||
.map((e) => e.toString())
|
||
.toList(),
|
||
isLiked: (json['is_liked'] as bool?) ?? false,
|
||
isFavorited: (json['is_favorited'] as bool?) ?? false,
|
||
description: (json['description'] as String?) ?? '',
|
||
status: (json['status'] as num?)?.toInt() ?? -1,
|
||
statusText: (json['status_text'] as String?) ?? '',
|
||
rejectReason: (json['reject_reason'] as String?) ?? '',
|
||
seriesId: (json['series_id'] as num?)?.toInt() ?? 0,
|
||
totalEpisodes: (json['total_episodes'] as num?)?.toInt() ?? 0,
|
||
);
|
||
}
|
||
|
||
/// 内容来源(片商):麻豆 / 91 ...
|
||
class VideoSource {
|
||
VideoSource({required this.sourceId, required this.name, this.code = ''});
|
||
|
||
final int sourceId;
|
||
final String name;
|
||
final String code;
|
||
|
||
factory VideoSource.fromJson(Map<String, dynamic> json) => VideoSource(
|
||
sourceId: (json['source_id'] as num?)?.toInt() ?? 0,
|
||
name: (json['name'] as String?) ?? '',
|
||
code: (json['code'] as String?) ?? '',
|
||
);
|
||
}
|
||
|
||
/// 作者简要信息
|
||
class VideoAuthor {
|
||
VideoAuthor({
|
||
required this.userId,
|
||
this.nickname = '',
|
||
this.avatarUrl = '',
|
||
});
|
||
|
||
final int userId;
|
||
final String nickname;
|
||
final String avatarUrl;
|
||
|
||
factory VideoAuthor.fromJson(Map<String, dynamic> json) => VideoAuthor(
|
||
userId: (json['user_id'] as num?)?.toInt() ?? 0,
|
||
nickname: (json['nickname'] as String?) ?? '',
|
||
avatarUrl: (json['avatar_url'] as String?) ?? '',
|
||
);
|
||
}
|