feat: 合集封面展示、封面切换

This commit is contained in:
guozhigq
2024-04-27 17:44:19 +08:00
parent 13ca6f3aed
commit f24291134c
12 changed files with 78 additions and 35 deletions

View File

@ -1,5 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:pilipala/common/widgets/network_img_layer.dart';
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
import '../models/common/video_episode_type.dart'; import '../models/common/video_episode_type.dart';
@ -44,25 +45,38 @@ class EpisodeBottomSheet {
title = '${episode.title}${episode.longTitle!}'; title = '${episode.title}${episode.longTitle!}';
break; break;
} }
return ListTile( return InkWell(
onTap: () { onTap: () {
SmartDialog.showToast('切换至「$title'); SmartDialog.showToast('切换至「$title');
changeFucCall.call(episode, index); changeFucCall.call(episode, index);
}, },
dense: false, child: Padding(
leading: isCurrentIndex padding: const EdgeInsets.only(left: 14, right: 14, top: 8, bottom: 8),
? Image.asset( child: isFullScreen
'assets/images/live.gif', ? Text(
color: primary, title,
height: 12, maxLines: 1,
) style: TextStyle(
: null, fontSize: 14,
title: Text( color: isCurrentIndex ? primary : onSurface,
title, ),
style: TextStyle( )
fontSize: 14, : Row(
color: isCurrentIndex ? primary : onSurface, children: [
), NetworkImgLayer(width: 130, height: 75, src: episode.cover),
const SizedBox(width: 10),
Expanded(
child: Text(
title,
maxLines: 2,
style: TextStyle(
fontSize: 14,
color: isCurrentIndex ? primary : onSurface,
),
),
),
],
),
), ),
); );
} }

View File

@ -23,6 +23,7 @@ class HotVideoItemModel {
this.dimension, this.dimension,
this.shortLinkV2, this.shortLinkV2,
this.firstFrame, this.firstFrame,
this.cover,
this.pubLocation, this.pubLocation,
this.seasontype, this.seasontype,
this.isOgv, this.isOgv,
@ -50,6 +51,7 @@ class HotVideoItemModel {
Dimension? dimension; Dimension? dimension;
String? shortLinkV2; String? shortLinkV2;
String? firstFrame; String? firstFrame;
String? cover;
String? pubLocation; String? pubLocation;
int? seasontype; int? seasontype;
bool? isOgv; bool? isOgv;
@ -77,6 +79,7 @@ class HotVideoItemModel {
dimension = Dimension.fromMap(json['dimension']); dimension = Dimension.fromMap(json['dimension']);
shortLinkV2 = json["short_link_v2"]; shortLinkV2 = json["short_link_v2"];
firstFrame = json["first_frame"]; firstFrame = json["first_frame"];
cover = json["first_frame"];
pubLocation = json["pub_location"]; pubLocation = json["pub_location"];
seasontype = json["seasontype"]; seasontype = json["seasontype"];
isOgv = json["isOgv"]; isOgv = json["isOgv"];

View File

@ -382,6 +382,7 @@ class Part {
String? weblink; String? weblink;
Dimension? dimension; Dimension? dimension;
String? firstFrame; String? firstFrame;
String? cover;
Part({ Part({
this.cid, this.cid,
@ -393,6 +394,7 @@ class Part {
this.weblink, this.weblink,
this.dimension, this.dimension,
this.firstFrame, this.firstFrame,
this.cover,
}); });
fromRawJson(String str) => Part.fromJson(json.decode(str)); fromRawJson(String str) => Part.fromJson(json.decode(str));
@ -411,6 +413,7 @@ class Part {
? null ? null
: Dimension.fromJson(json["dimension"]); : Dimension.fromJson(json["dimension"]);
firstFrame = json["first_frame"]; firstFrame = json["first_frame"];
cover = json["first_frame"];
} }
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
@ -634,6 +637,7 @@ class EpisodeItem {
this.attribute, this.attribute,
this.page, this.page,
this.bvid, this.bvid,
this.cover,
}); });
int? seasonId; int? seasonId;
int? sectionId; int? sectionId;
@ -644,6 +648,7 @@ class EpisodeItem {
int? attribute; int? attribute;
Part? page; Part? page;
String? bvid; String? bvid;
String? cover;
EpisodeItem.fromJson(Map<String, dynamic> json) { EpisodeItem.fromJson(Map<String, dynamic> json) {
seasonId = json['season_id']; seasonId = json['season_id'];
@ -655,6 +660,7 @@ class EpisodeItem {
attribute = json['attribute']; attribute = json['attribute'];
page = Part.fromJson(json['page']); page = Part.fromJson(json['page']);
bvid = json['bvid']; bvid = json['bvid'];
cover = json['arc']['pic'];
} }
} }

View File

@ -215,7 +215,7 @@ class BangumiIntroController extends GetxController {
} }
// 修改分P或番剧分集 // 修改分P或番剧分集
Future changeSeasonOrbangu(bvid, cid, aid) async { Future changeSeasonOrbangu(bvid, cid, aid, cover) async {
// 重新获取视频资源 // 重新获取视频资源
VideoDetailController videoDetailCtr = VideoDetailController videoDetailCtr =
Get.find<VideoDetailController>(tag: Get.arguments['heroTag']); Get.find<VideoDetailController>(tag: Get.arguments['heroTag']);
@ -223,6 +223,7 @@ class BangumiIntroController extends GetxController {
videoDetailCtr.cid.value = cid; videoDetailCtr.cid.value = cid;
videoDetailCtr.danmakuCid.value = cid; videoDetailCtr.danmakuCid.value = cid;
videoDetailCtr.oid.value = aid; videoDetailCtr.oid.value = aid;
videoDetailCtr.cover.value = cover;
videoDetailCtr.queryVideoUrl(); videoDetailCtr.queryVideoUrl();
// 重新请求评论 // 重新请求评论
try { try {
@ -281,7 +282,8 @@ class BangumiIntroController extends GetxController {
int cid = episodes[nextIndex].cid!; int cid = episodes[nextIndex].cid!;
String bvid = episodes[nextIndex].bvid!; String bvid = episodes[nextIndex].bvid!;
int aid = episodes[nextIndex].aid!; int aid = episodes[nextIndex].aid!;
changeSeasonOrbangu(bvid, cid, aid); String cover = episodes[nextIndex].cover!;
changeSeasonOrbangu(bvid, cid, aid, cover);
} }
// 播放器底栏 选集 回调 // 播放器底栏 选集 回调
@ -302,7 +304,7 @@ class BangumiIntroController extends GetxController {
sheetHeight: Get.size.height, sheetHeight: Get.size.height,
isFullScreen: true, isFullScreen: true,
changeFucCall: (item, index) { changeFucCall: (item, index) {
changeSeasonOrbangu(item.bvid, item.cid, item.aid); changeSeasonOrbangu(item.bvid, item.cid, item.aid, item.cover);
SmartDialog.dismiss(); SmartDialog.dismiss();
}, },
).buildShowContent(Get.context!), ).buildShowContent(Get.context!),

View File

@ -322,8 +322,8 @@ class _BangumiInfoState extends State<BangumiInfo> {
pages: widget.bangumiDetail!.episodes!, pages: widget.bangumiDetail!.episodes!,
cid: cid! ?? widget.bangumiDetail!.episodes!.first.cid!, cid: cid! ?? widget.bangumiDetail!.episodes!.first.cid!,
sheetHeight: sheetHeight, sheetHeight: sheetHeight,
changeFuc: (bvid, cid, aid) => changeFuc: (bvid, cid, aid, cover) => bangumiIntroController
bangumiIntroController.changeSeasonOrbangu(bvid, cid, aid), .changeSeasonOrbangu(bvid, cid, aid, cover),
bangumiDetail: bangumiIntroController.bangumiDetail.value, bangumiDetail: bangumiIntroController.bangumiDetail.value,
bangumiIntroController: bangumiIntroController, bangumiIntroController: bangumiIntroController,
) )

View File

@ -84,11 +84,12 @@ class _BangumiPanelState extends State<BangumiPanel> {
item.bvid, item.bvid,
item.cid, item.cid,
item.aid, item.aid,
item.cover,
); );
if (_bottomSheetController != null) { if (_bottomSheetController != null) {
_bottomSheetController?.close(); _bottomSheetController?.close();
} }
currentIndex = i; currentIndex.value = i;
scrollToIndex(); scrollToIndex();
} }

View File

@ -73,6 +73,7 @@ class VideoDetailController extends GetxController
ReplyItemModel? firstFloor; ReplyItemModel? firstFloor;
final scaffoldKey = GlobalKey<ScaffoldState>(); final scaffoldKey = GlobalKey<ScaffoldState>();
RxString bgCover = ''.obs; RxString bgCover = ''.obs;
RxString cover = ''.obs;
PlPlayerController plPlayerController = PlPlayerController.getInstance(); PlPlayerController plPlayerController = PlPlayerController.getInstance();
late VideoItem firstVideo; late VideoItem firstVideo;
@ -120,10 +121,12 @@ class VideoDetailController extends GetxController
var args = argMap['videoItem']; var args = argMap['videoItem'];
if (args.pic != null && args.pic != '') { if (args.pic != null && args.pic != '') {
videoItem['pic'] = args.pic; videoItem['pic'] = args.pic;
cover.value = args.pic;
} }
} }
if (keys.contains('pic')) { if (keys.contains('pic')) {
videoItem['pic'] = argMap['pic']; videoItem['pic'] = argMap['pic'];
cover.value = argMap['pic'];
} }
} }
tabCtr = TabController(length: 2, vsync: this); tabCtr = TabController(length: 2, vsync: this);

View File

@ -432,7 +432,7 @@ class VideoIntroController extends GetxController {
} }
// 修改分P或番剧分集 // 修改分P或番剧分集
Future changeSeasonOrbangu(bvid, cid, aid) async { Future changeSeasonOrbangu(bvid, cid, aid, cover) async {
// 重新获取视频资源 // 重新获取视频资源
final VideoDetailController videoDetailCtr = final VideoDetailController videoDetailCtr =
Get.find<VideoDetailController>(tag: heroTag); Get.find<VideoDetailController>(tag: heroTag);
@ -447,6 +447,7 @@ class VideoIntroController extends GetxController {
videoDetailCtr.oid.value = aid ?? IdUtils.bv2av(bvid); videoDetailCtr.oid.value = aid ?? IdUtils.bv2av(bvid);
videoDetailCtr.cid.value = cid; videoDetailCtr.cid.value = cid;
videoDetailCtr.danmakuCid.value = cid; videoDetailCtr.danmakuCid.value = cid;
videoDetailCtr.cover.value = cover;
videoDetailCtr.queryVideoUrl(); videoDetailCtr.queryVideoUrl();
// 重新请求评论 // 重新请求评论
try { try {
@ -494,6 +495,7 @@ class VideoIntroController extends GetxController {
void nextPlay() { void nextPlay() {
final List episodes = []; final List episodes = [];
bool isPages = false; bool isPages = false;
late String cover;
if (videoDetail.value.ugcSeason != null) { if (videoDetail.value.ugcSeason != null) {
final UgcSeason ugcSeason = videoDetail.value.ugcSeason!; final UgcSeason ugcSeason = videoDetail.value.ugcSeason!;
final List<SectionItem> sections = ugcSeason.sections!; final List<SectionItem> sections = ugcSeason.sections!;
@ -510,6 +512,7 @@ class VideoIntroController extends GetxController {
final int currentIndex = final int currentIndex =
episodes.indexWhere((e) => e.cid == lastPlayCid.value); episodes.indexWhere((e) => e.cid == lastPlayCid.value);
int nextIndex = currentIndex + 1; int nextIndex = currentIndex + 1;
cover = episodes[nextIndex].cover;
final VideoDetailController videoDetailCtr = final VideoDetailController videoDetailCtr =
Get.find<VideoDetailController>(tag: heroTag); Get.find<VideoDetailController>(tag: heroTag);
final PlayRepeat platRepeat = videoDetailCtr.plPlayerController.playRepeat; final PlayRepeat platRepeat = videoDetailCtr.plPlayerController.playRepeat;
@ -526,7 +529,7 @@ class VideoIntroController extends GetxController {
final int cid = episodes[nextIndex].cid!; final int cid = episodes[nextIndex].cid!;
final String rBvid = isPages ? bvid : episodes[nextIndex].bvid; final String rBvid = isPages ? bvid : episodes[nextIndex].bvid;
final int rAid = isPages ? IdUtils.bv2av(bvid) : episodes[nextIndex].aid!; final int rAid = isPages ? IdUtils.bv2av(bvid) : episodes[nextIndex].aid!;
changeSeasonOrbangu(rBvid, cid, rAid); changeSeasonOrbangu(rBvid, cid, rAid, cover);
} }
// 设置关注分组 // 设置关注分组
@ -591,10 +594,11 @@ class VideoIntroController extends GetxController {
isFullScreen: true, isFullScreen: true,
changeFucCall: (item, index) { changeFucCall: (item, index) {
if (dataType == VideoEpidoesType.videoEpisode) { if (dataType == VideoEpidoesType.videoEpisode) {
changeSeasonOrbangu(IdUtils.av2bv(item.aid), item.cid, item.aid); changeSeasonOrbangu(
IdUtils.av2bv(item.aid), item.cid, item.aid, item.cover);
} }
if (dataType == VideoEpidoesType.videoPart) { if (dataType == VideoEpidoesType.videoPart) {
changeSeasonOrbangu(bvid, item.cid, null); changeSeasonOrbangu(bvid, item.cid, null, item.cover);
} }
SmartDialog.dismiss(); SmartDialog.dismiss();
}, },

View File

@ -383,11 +383,12 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
? videoIntroController.lastPlayCid.value ? videoIntroController.lastPlayCid.value
: widget.videoDetail!.pages!.first.cid, : widget.videoDetail!.pages!.first.cid,
sheetHeight: videoDetailCtr.sheetHeight.value, sheetHeight: videoDetailCtr.sheetHeight.value,
changeFuc: (bvid, cid, aid) => changeFuc: (bvid, cid, aid, cover) =>
videoIntroController.changeSeasonOrbangu( videoIntroController.changeSeasonOrbangu(
bvid, bvid,
cid, cid,
aid, aid,
cover,
), ),
videoIntroCtr: videoIntroController, videoIntroCtr: videoIntroController,
), ),
@ -401,10 +402,12 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
pages: widget.videoDetail!.pages!, pages: widget.videoDetail!.pages!,
cid: videoIntroController.lastPlayCid.value, cid: videoIntroController.lastPlayCid.value,
sheetHeight: videoDetailCtr.sheetHeight.value, sheetHeight: videoDetailCtr.sheetHeight.value,
changeFuc: (cid) => videoIntroController.changeSeasonOrbangu( changeFuc: (cid, cover) =>
videoIntroController.changeSeasonOrbangu(
videoIntroController.bvid, videoIntroController.bvid,
cid, cid,
null, null,
cover,
), ),
videoIntroCtr: videoIntroController, videoIntroCtr: videoIntroController,
), ),

View File

@ -58,7 +58,7 @@ class _PagesPanelState extends State<PagesPanel> {
} }
void changeFucCall(item, i) async { void changeFucCall(item, i) async {
widget.changeFuc?.call(item.cid); widget.changeFuc?.call(item.cid, item.cover);
currentIndex.value = i; currentIndex.value = i;
_bottomSheetController?.close(); _bottomSheetController?.close();
scrollToIndex(); scrollToIndex();
@ -129,7 +129,7 @@ class _PagesPanelState extends State<PagesPanel> {
), ),
), ),
Container( Container(
height: 35, height: 55,
margin: const EdgeInsets.only(bottom: 8), margin: const EdgeInsets.only(bottom: 8),
child: ListView.builder( child: ListView.builder(
scrollDirection: Axis.horizontal, scrollDirection: Axis.horizontal,
@ -163,7 +163,7 @@ class _PagesPanelState extends State<PagesPanel> {
Expanded( Expanded(
child: Text( child: Text(
widget.pages[i].pagePart!, widget.pages[i].pagePart!,
maxLines: 1, maxLines: 2,
style: TextStyle( style: TextStyle(
fontSize: 13, fontSize: 13,
color: isCurrentIndex color: isCurrentIndex

View File

@ -67,6 +67,7 @@ class _SeasonPanelState extends State<SeasonPanel> {
IdUtils.av2bv(item.aid), IdUtils.av2bv(item.aid),
item.cid, item.cid,
item.aid, item.aid,
item.cover,
); );
currentIndex.value = i; currentIndex.value = i;
_bottomSheetController?.close(); _bottomSheetController?.close();

View File

@ -458,11 +458,17 @@ class _VideoDetailPageState extends State<VideoDetailPage>
onTap: () { onTap: () {
handlePlay(); handlePlay();
}, },
child: NetworkImgLayer( child: Obx(
type: 'emote', () => AnimatedOpacity(
src: vdCtr.videoItem['pic'], duration: const Duration(milliseconds: 100), // 渐变动画的持续时间
width: Get.width, opacity: 1, // 设置不透明度
height: videoHeight.value, child: NetworkImgLayer(
type: 'emote',
src: vdCtr.cover.value,
width: Get.width,
height: videoHeight.value,
),
),
), ),
), ),
Positioned( Positioned(