diff --git a/lib/common/widgets/progress_bar/video_progress_indicator.dart b/lib/common/widgets/progress_bar/video_progress_indicator.dart index 6bd3d4995..0057eb41a 100644 --- a/lib/common/widgets/progress_bar/video_progress_indicator.dart +++ b/lib/common/widgets/progress_bar/video_progress_indicator.dart @@ -25,7 +25,7 @@ class VideoProgressIndicator extends LeafRenderObjectWidget { this.radius = 10, this.height = 4, required this.progress, - }) : assert(progress >= 0 && progress <= 1); + }); final Color color; final Color backgroundColor; @@ -136,9 +136,9 @@ class RenderProgressBar extends RenderBox { bottomRight: radius, ); - if (progress == 0) { + if (progress <= 0) { canvas.drawRRect(rrect, paint..color = _backgroundColor); - } else if (progress == 1) { + } else if (progress >= 1) { canvas.drawRRect(rrect, paint..color = _color); } else { final w = size.width * progress; diff --git a/lib/common/widgets/video_card/video_card_h.dart b/lib/common/widgets/video_card/video_card_h.dart index 017f7079e..5cd1f01ce 100644 --- a/lib/common/widgets/video_card/video_card_h.dart +++ b/lib/common/widgets/video_card/video_card_h.dart @@ -7,18 +7,13 @@ import 'package:PiliPlus/common/widgets/progress_bar/video_progress_indicator.da import 'package:PiliPlus/common/widgets/stat/stat.dart'; import 'package:PiliPlus/common/widgets/video_popup_menu.dart'; import 'package:PiliPlus/http/search.dart'; -import 'package:PiliPlus/models/common/badge_type.dart'; -import 'package:PiliPlus/models/common/stat_type.dart'; -import 'package:PiliPlus/models/model_hot_video_item.dart'; -import 'package:PiliPlus/models/model_video.dart'; -import 'package:PiliPlus/models/search/result.dart'; +import 'package:PiliPlus/models/horizontal_video_model.dart'; import 'package:PiliPlus/models_new/video/video_detail/dimension.dart'; import 'package:PiliPlus/utils/date_utils.dart'; import 'package:PiliPlus/utils/duration_utils.dart'; import 'package:PiliPlus/utils/page_utils.dart'; import 'package:PiliPlus/utils/platform_utils.dart'; import 'package:flutter/material.dart' hide LayoutBuilder; -import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; // 视频卡片 - 水平布局 class VideoCardH extends StatelessWidget { @@ -29,37 +24,13 @@ class VideoCardH extends StatelessWidget { this.onViewLater, this.onRemove, }); - final BaseVideoItemModel videoItem; + final HorizontalVideoModel videoItem; final VoidCallback? onTap; final ValueChanged? onViewLater; final VoidCallback? onRemove; @override Widget build(BuildContext context) { - String type = 'video'; - String? badge; - if (videoItem case final SearchVideoItemModel item) { - final typeOrNull = item.type; - if (typeOrNull != null && typeOrNull.isNotEmpty) { - type = typeOrNull; - if (type == 'ketang') { - badge = '课堂'; - } else if (type == 'live_room') { - badge = '直播'; - } - } - if (item.isUnionVideo == 1) { - badge = '合作'; - } - } else if (videoItem case final HotVideoItemModel item) { - if (item.isCharging == true) { - badge = '充电专属'; - } else if (item.isCooperation == 1) { - badge = '合作'; - } else { - badge = item.pgcLabel; - } - } void onLongPress() => imageSaveDialog( bvid: videoItem.bvid, title: videoItem.title, @@ -67,9 +38,9 @@ class VideoCardH extends StatelessWidget { ); final theme = Theme.of(context); return Material( - type: MaterialType.transparency, + type: .transparency, child: Stack( - clipBehavior: Clip.none, + clipBehavior: .none, children: [ InkWell( onLongPress: onLongPress, @@ -77,33 +48,25 @@ class VideoCardH extends StatelessWidget { onTap: onTap ?? () async { - if (type == 'ketang') { - PageUtils.viewPugv(seasonId: videoItem.aid); + if (videoItem.isPugv ?? false) { + PageUtils.viewPugv(seasonId: videoItem.seasonId); return; - } else if (type == 'live_room') { - if (videoItem case final SearchVideoItemModel item) { - int? roomId = item.id; - if (roomId != null) { - PageUtils.toLiveRoom(roomId); - } - } else { - SmartDialog.showToast( - 'err: live_room : ${videoItem.runtimeType}', - ); + } + + if (videoItem.isLive ?? false) { + if (videoItem.roomId case final roomId?) { + PageUtils.toLiveRoom(roomId); } return; } - Dimension? dimension; - if (videoItem case final HotVideoItemModel item) { - if (item.redirectUrl?.isNotEmpty == true && - PageUtils.viewPgcFromUri(item.redirectUrl!)) { - return; - } - dimension = item.dimension; + if (videoItem.redirectUrl?.isNotEmpty == true && + PageUtils.viewPgcFromUri(videoItem.redirectUrl!)) { + return; } int? cid = videoItem.cid; + Dimension? dimension = videoItem.dimension; if (cid == null) { if (await SearchHttp.ab2cWithDimension( aid: videoItem.aid, @@ -125,40 +88,38 @@ class VideoCardH extends StatelessWidget { } }, child: Padding( - padding: const EdgeInsets.symmetric( + padding: const .symmetric( horizontal: Style.safeSpace, vertical: 5, ), child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ + crossAxisAlignment: .start, + children: [ AspectRatio( aspectRatio: Style.aspectRatio, child: LayoutBuilder( builder: (context, boxConstraints) { final double maxWidth = boxConstraints.maxWidth; final double maxHeight = boxConstraints.maxHeight; - num? progress; - if (videoItem case final HotVideoItemModel item) { - progress = item.progress; - } + + final progress = videoItem.progress; return Stack( - clipBehavior: Clip.none, + clipBehavior: .none, children: [ NetworkImgLayer( src: videoItem.cover, width: maxWidth, height: maxHeight, ), - if (badge != null) + if (videoItem.badge case final badge?) PBadge( text: badge, top: 6.0, right: 6.0, type: switch (badge) { - '充电专属' => PBadgeType.error, - _ => PBadgeType.primary, + '充电专属' => .error, + _ => .primary, }, ), if (progress != null && progress != 0) ...[ @@ -168,7 +129,7 @@ class VideoCardH extends StatelessWidget { : '${DurationUtils.formatDuration(progress)}/${DurationUtils.formatDuration(videoItem.duration)}', right: 6, bottom: 8, - type: PBadgeType.gray, + type: .gray, ), Positioned( left: 0, @@ -190,7 +151,7 @@ class VideoCardH extends StatelessWidget { ), right: 6.0, bottom: 6.0, - type: PBadgeType.gray, + type: .gray, ), ], ); @@ -224,45 +185,44 @@ class VideoCardH extends StatelessWidget { if (pubdate != '') pubdate += ' '; return Expanded( child: Column( - crossAxisAlignment: CrossAxisAlignment.start, + crossAxisAlignment: .start, children: [ - if (videoItem case final SearchVideoItemModel item) ...[ - if (item.titleList?.isNotEmpty == true) - Expanded( - child: Text.rich( - overflow: TextOverflow.ellipsis, - maxLines: 2, - TextSpan( - children: item.titleList! - .map( - (e) => TextSpan( - text: e.text, - style: TextStyle( - fontSize: theme.textTheme.bodyMedium!.fontSize, - height: 1.42, - letterSpacing: 0.3, - color: e.isEm - ? theme.colorScheme.primary - : theme.colorScheme.onSurface, - ), + if (videoItem.titleList?.isNotEmpty == true) + Expanded( + child: Text.rich( + overflow: .ellipsis, + maxLines: 2, + TextSpan( + children: videoItem.titleList! + .map( + (e) => TextSpan( + text: e.text, + style: TextStyle( + fontSize: theme.textTheme.bodyMedium!.fontSize, + height: 1.42, + letterSpacing: 0.3, + color: e.isEm + ? theme.colorScheme.primary + : theme.colorScheme.onSurface, ), - ) - .toList(), - ), + ), + ) + .toList(), ), ), - ] else + ) + else Expanded( child: Text( videoItem.title, - textAlign: TextAlign.start, + textAlign: .start, style: TextStyle( fontSize: theme.textTheme.bodyMedium!.fontSize, height: 1.42, letterSpacing: 0.3, ), maxLines: 2, - overflow: TextOverflow.ellipsis, + overflow: .ellipsis, ), ), Text( @@ -272,7 +232,7 @@ class VideoCardH extends StatelessWidget { fontSize: 12, height: 1, color: theme.colorScheme.outline, - overflow: TextOverflow.clip, + overflow: .clip, ), ), const SizedBox(height: 3), @@ -280,11 +240,11 @@ class VideoCardH extends StatelessWidget { spacing: 8, children: [ StatWidget( - type: StatType.play, + type: .play, value: videoItem.stat.view, ), StatWidget( - type: StatType.danmaku, + type: .danmaku, value: videoItem.stat.danmu, ), ], diff --git a/lib/models/horizontal_video_model.dart b/lib/models/horizontal_video_model.dart new file mode 100644 index 000000000..c99bceafa --- /dev/null +++ b/lib/models/horizontal_video_model.dart @@ -0,0 +1,21 @@ +import 'package:PiliPlus/models/model_video.dart'; +import 'package:PiliPlus/models_new/video/video_detail/dimension.dart'; + +abstract class HorizontalVideoModel extends BaseVideoItemModel { + bool? isPugv; + int? seasonId; + + int? roomId; + bool? isLive; + + Dimension? dimension; + + String? badge; + + num? progress; + + String? redirectUrl; + + // search + List<({bool isEm, String text})>? titleList; +} diff --git a/lib/models/model_hot_video_item.dart b/lib/models/model_hot_video_item.dart index ddb685ae6..8722947c6 100644 --- a/lib/models/model_hot_video_item.dart +++ b/lib/models/model_hot_video_item.dart @@ -1,25 +1,19 @@ +import 'package:PiliPlus/models/horizontal_video_model.dart'; import 'package:PiliPlus/models/model_owner.dart'; -import 'package:PiliPlus/models/model_rec_video_item.dart'; import 'package:PiliPlus/models/model_video.dart'; import 'package:PiliPlus/models_new/video/video_detail/dimension.dart'; import 'package:PiliPlus/pages/common/multi_select/base.dart'; // 稍后再看, 排行榜等网页返回也使用该类 -class HotVideoItemModel extends BaseRcmdVideoItemModel with MultiSelectData { +class HotVideoItemModel extends HorizontalVideoModel with MultiSelectData { int? videos; int? tid; String? tname; int? copyright; int? ctime; int? state; - Dimension? dimension; String? firstFrame; String? pubLocation; - String? pgcLabel; - String? redirectUrl; - num? progress; - int? isCooperation; - bool? isCharging; HotVideoItemModel.fromJson(Map json) { aid = json["aid"]; @@ -43,23 +37,16 @@ class HotVideoItemModel extends BaseRcmdVideoItemModel with MultiSelectData { : Dimension.fromJson(json['dimension']); firstFrame = json["first_frame"]; pubLocation = json["pub_location"]; - dynamic rcmd = json['rcmd_reason']; - rcmdReason = rcmd is Map ? rcmd['content'] : rcmd; // 相关视频里rcmd为String, - if (rcmdReason?.isEmpty == true) rcmdReason = null; - pgcLabel = json['pgc_label']; redirectUrl = json['redirect_url']; - // uri = json['uri']; // 仅在稍后再看存在 progress = json['progress']; - isCooperation = json['rights']?['is_cooperation']; - isCharging = json['charging_pay']?['level'] != null; + if (json['charging_pay']?['level'] != null) { + badge = '充电专属'; + } else if (json['rights']?['is_cooperation'] == 1) { + badge = '合作'; + } else { + badge = json['pgc_label']; + } } - - // @override - // get isFollowed => false; - // @override - // get goto => 'av'; - // @override - // get uri => 'bilibili://video/$aid'; } class HotStat extends Stat { diff --git a/lib/models/search/result.dart b/lib/models/search/result.dart index 4aa50c4fb..129e19b7a 100644 --- a/lib/models/search/result.dart +++ b/lib/models/search/result.dart @@ -1,3 +1,4 @@ +import 'package:PiliPlus/models/horizontal_video_model.dart'; import 'package:PiliPlus/models/model_avatar.dart'; import 'package:PiliPlus/models/model_owner.dart'; import 'package:PiliPlus/models/model_video.dart'; @@ -64,18 +65,16 @@ class SearchVideoData extends SearchNumData { } } -class SearchVideoItemModel extends BaseVideoItemModel { - String? type; +class SearchVideoItemModel extends HorizontalVideoModel { int? id; String? arcurl; String? tag; int? ctime; - int? isUnionVideo; - List<({bool isEm, String text})>? titleList; + @override + int? get seasonId => aid; SearchVideoItemModel.fromJson(Map json) { - type = json['type']; id = json['id']; arcurl = json['arcurl']; aid = json['aid']; @@ -89,7 +88,19 @@ class SearchVideoItemModel extends BaseVideoItemModel { duration = DurationUtils.parseDuration(json['duration']); owner = SearchOwner.fromJson(json); stat = SearchStat.fromJson(json); - isUnionVideo = json['is_union_video']; + switch (json['type']) { + case 'ketang': + badge = '课堂'; + isPugv = true; + case 'live_room': + badge = '直播'; + isLive = true; + roomId = json['roomid']; + default: + if (json['is_union_video'] == 1) { + badge = '合作'; + } + } } } diff --git a/lib/models_new/member/search_archive/vlist.dart b/lib/models_new/member/search_archive/vlist.dart index 95f93d1ff..a7018313c 100644 --- a/lib/models_new/member/search_archive/vlist.dart +++ b/lib/models_new/member/search_archive/vlist.dart @@ -1,7 +1,8 @@ +import 'package:PiliPlus/models/horizontal_video_model.dart'; import 'package:PiliPlus/models/model_video.dart'; import 'package:PiliPlus/utils/duration_utils.dart'; -class VListItemModel extends BaseVideoItemModel { +class VListItemModel extends HorizontalVideoModel { VListItemModel.fromJson(Map json) { cover = json['pic']; desc = json['description']; @@ -14,6 +15,24 @@ class VListItemModel extends BaseVideoItemModel { bvid = json['bvid']; stat = VListStat.fromJson(json); owner = VListOwner.fromJson(json); + if (json['is_lesson_video'] == 1) { + isPugv = true; + badge = '课堂'; + } else if (json['is_charging_arc'] == true) { + badge = '充电专属'; + } else if (json['is_union_video'] == 1) { + badge = '合作'; + } + seasonId = json['season_id']; + redirectUrl = json['jump_url']; + final position = json['playback_position'] as num?; // percent + if (position != null) { + if (position == 100) { + progress = -1; + } else { + progress = ((position / 100) * duration).round(); + } + } } } diff --git a/lib/models_new/member/season_web/archive.dart b/lib/models_new/member/season_web/archive.dart index 4d11ab98a..79e353b16 100644 --- a/lib/models_new/member/season_web/archive.dart +++ b/lib/models_new/member/season_web/archive.dart @@ -1,6 +1,7 @@ +import 'package:PiliPlus/models/horizontal_video_model.dart'; import 'package:PiliPlus/models/model_video.dart'; -class SeasonArchive extends BaseVideoItemModel { +class SeasonArchive extends HorizontalVideoModel { SeasonArchive.fromJson(Map json) { aid = json['aid']; bvid = json['bvid']; diff --git a/lib/pages/member_video_web/base/view.dart b/lib/pages/member_video_web/base/view.dart index fc9aebbac..24552e1ae 100644 --- a/lib/pages/member_video_web/base/view.dart +++ b/lib/pages/member_video_web/base/view.dart @@ -7,7 +7,7 @@ import 'package:PiliPlus/common/widgets/sliver/sliver_pinned_header.dart'; import 'package:PiliPlus/common/widgets/video_card/video_card_h.dart'; import 'package:PiliPlus/http/loading_state.dart'; import 'package:PiliPlus/models/common/enum_with_label.dart'; -import 'package:PiliPlus/models/model_video.dart'; +import 'package:PiliPlus/models/horizontal_video_model.dart'; import 'package:PiliPlus/pages/member_video_web/base/controller.dart'; import 'package:PiliPlus/pages/search/widgets/search_text.dart'; import 'package:PiliPlus/utils/grid.dart'; @@ -19,7 +19,7 @@ import 'package:get/get.dart'; abstract class BaseVideoWebState< S extends StatefulWidget, R, - T extends BaseVideoItemModel, + T extends HorizontalVideoModel, V extends EnumWithLabel > extends State