From 26d8ab5b432ba86144b9d25db55cf49df8e25e71 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 26 Sep 2023 22:51:21 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=92=AD=E6=94=BE=E9=A1=BA=E5=BA=8F?= =?UTF-8?q?=E3=80=81=E8=A7=86=E9=A2=91=E8=AF=A6=E6=83=85=E6=93=8D=E4=BD=9C?= =?UTF-8?q?=E6=A0=8F=E6=A0=B7=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bangumi/introduction/controller.dart | 32 ++++++++- lib/pages/bangumi/introduction/view.dart | 25 ++++++- lib/pages/bangumi/widgets/bangumi_panel.dart | 16 ++++- lib/pages/video/detail/controller.dart | 8 +-- .../video/detail/introduction/controller.dart | 63 ++++++++++++++-- lib/pages/video/detail/introduction/view.dart | 48 +++++++------ .../introduction/widgets/action_item.dart | 5 ++ .../detail/introduction/widgets/page.dart | 13 +++- .../detail/introduction/widgets/season.dart | 16 ++++- lib/pages/video/detail/view.dart | 24 ++++++- .../video/detail/widgets/header_control.dart | 72 ++++++++++++++++--- lib/plugin/pl_player/controller.dart | 15 ++++ lib/plugin/pl_player/models/play_repeat.dart | 25 +++++++ lib/utils/storage.dart | 2 + 14 files changed, 314 insertions(+), 50 deletions(-) create mode 100644 lib/plugin/pl_player/models/play_repeat.dart diff --git a/lib/pages/bangumi/introduction/controller.dart b/lib/pages/bangumi/introduction/controller.dart index c027f8af..f37a3310 100644 --- a/lib/pages/bangumi/introduction/controller.dart +++ b/lib/pages/bangumi/introduction/controller.dart @@ -9,6 +9,7 @@ import 'package:pilipala/models/bangumi/info.dart'; import 'package:pilipala/models/user/fav_folder.dart'; import 'package:pilipala/pages/video/detail/index.dart'; import 'package:pilipala/pages/video/detail/reply/index.dart'; +import 'package:pilipala/plugin/pl_player/models/play_repeat.dart'; import 'package:pilipala/utils/feed_back.dart'; import 'package:pilipala/utils/id_utils.dart'; import 'package:pilipala/utils/storage.dart'; @@ -21,7 +22,7 @@ class BangumiIntroController extends GetxController { ? int.parse(Get.parameters['seasonId']!) : null; var epId = Get.parameters['epId'] != null - ? int.parse(Get.parameters['epId']!) + ? int.tryParse(Get.parameters['epId']!) : null; // 是否预渲染 骨架屏 @@ -257,7 +258,7 @@ class BangumiIntroController extends GetxController { VideoDetailController videoDetailCtr = Get.find(tag: Get.arguments['heroTag']); videoDetailCtr.bvid = bvid; - videoDetailCtr.cid = cid; + videoDetailCtr.cid.value = cid; videoDetailCtr.danmakuCid.value = cid; videoDetailCtr.queryVideoUrl(); // 重新请求评论 @@ -292,4 +293,31 @@ class BangumiIntroController extends GetxController { } return result; } + + /// 列表循环或者顺序播放时,自动播放下一个 + void nextPlay() { + late List episodes; + if (bangumiDetail.value.episodes != null) { + episodes = bangumiDetail.value.episodes!; + } + VideoDetailController videoDetailCtr = + Get.find(tag: Get.arguments['heroTag']); + int currentIndex = + episodes.indexWhere((e) => e.cid == videoDetailCtr.cid.value); + int nextIndex = currentIndex + 1; + PlayRepeat platRepeat = videoDetailCtr.plPlayerController.playRepeat; + // 列表循环 + if (platRepeat == PlayRepeat.listCycle) { + if (nextIndex == episodes.length - 1) { + nextIndex = 0; + } + } + if (nextIndex <= episodes.length - 1 && + platRepeat == PlayRepeat.listOrder) {} + + int cid = episodes[nextIndex].cid!; + String bvid = episodes[nextIndex].bvid!; + int aid = episodes[nextIndex].aid!; + changeSeasonOrbangu(bvid, cid, aid); + } } diff --git a/lib/pages/bangumi/introduction/view.dart b/lib/pages/bangumi/introduction/view.dart index 9ebd2558..af47d7da 100644 --- a/lib/pages/bangumi/introduction/view.dart +++ b/lib/pages/bangumi/introduction/view.dart @@ -34,10 +34,12 @@ class BangumiIntroPanel extends StatefulWidget { class _BangumiIntroPanelState extends State with AutomaticKeepAliveClientMixin { - final BangumiIntroController bangumiIntroController = - Get.put(BangumiIntroController(), tag: Get.arguments['heroTag']); + late BangumiIntroController bangumiIntroController; + late VideoDetailController videoDetailCtr; BangumiInfoModel? bangumiDetail; late Future _futureBuilderFuture; + late int cid; + late String heroTag; // 添加页面缓存 @override @@ -46,10 +48,19 @@ class _BangumiIntroPanelState extends State @override void initState() { super.initState(); + heroTag = Get.arguments['heroTag']; + cid = widget.cid!; + bangumiIntroController = Get.put(BangumiIntroController(), tag: heroTag); + videoDetailCtr = Get.find(tag: heroTag); bangumiIntroController.bangumiDetail.listen((value) { bangumiDetail = value; }); _futureBuilderFuture = bangumiIntroController.queryBangumiIntro(); + videoDetailCtr.cid.listen((p0) { + print('🐶🐶$p0'); + cid = p0; + setState(() {}); + }); } @override @@ -61,9 +72,11 @@ class _BangumiIntroPanelState extends State if (snapshot.connectionState == ConnectionState.done) { if (snapshot.data['status']) { // 请求成功 + return BangumiInfo( loadingStatus: false, bangumiDetail: bangumiDetail, + cid: cid, ); } else { // 请求错误 @@ -77,7 +90,7 @@ class _BangumiIntroPanelState extends State return BangumiInfo( loadingStatus: true, bangumiDetail: bangumiDetail, - cid: widget.cid, + cid: cid, ); } }, @@ -118,6 +131,12 @@ class _BangumiInfoState extends State { bangumiItem = bangumiIntroController.bangumiItem; sheetHeight = localCache.get('sheetHeight'); cid = widget.cid!; + print('cid: $cid'); + videoDetailCtr.cid.listen((p0) { + cid = p0; + print('cid: $cid'); + setState(() {}); + }); } // 收藏 diff --git a/lib/pages/bangumi/widgets/bangumi_panel.dart b/lib/pages/bangumi/widgets/bangumi_panel.dart index 9c55448d..bb27a38a 100644 --- a/lib/pages/bangumi/widgets/bangumi_panel.dart +++ b/lib/pages/bangumi/widgets/bangumi_panel.dart @@ -1,7 +1,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; +import 'package:get/get.dart'; import 'package:hive/hive.dart'; import 'package:pilipala/models/bangumi/info.dart'; +import 'package:pilipala/pages/video/detail/index.dart'; import 'package:pilipala/utils/storage.dart'; class BangumiPanel extends StatefulWidget { @@ -30,16 +32,28 @@ class _BangumiPanelState extends State { dynamic userInfo; // 默认未开通 int vipStatus = 0; + late int cid; + String heroTag = Get.arguments['heroTag']; + late final VideoDetailController videoDetailCtr; @override void initState() { super.initState(); - currentIndex = widget.pages.indexWhere((e) => e.cid == widget.cid!); + cid = widget.cid!; + currentIndex = widget.pages.indexWhere((e) => e.cid == cid); scrollToIndex(); userInfo = userInfoCache.get('userInfoCache'); if (userInfo != null) { vipStatus = userInfo.vipStatus; } + videoDetailCtr = Get.find(tag: heroTag); + + videoDetailCtr.cid.listen((p0) { + cid = p0; + setState(() {}); + currentIndex = widget.pages.indexWhere((e) => e.cid == cid); + scrollToIndex(); + }); } @override diff --git a/lib/pages/video/detail/controller.dart b/lib/pages/video/detail/controller.dart index d6795c52..bc525fa3 100644 --- a/lib/pages/video/detail/controller.dart +++ b/lib/pages/video/detail/controller.dart @@ -24,7 +24,7 @@ class VideoDetailController extends GetxController with GetSingleTickerProviderStateMixin { /// 路由传参 String bvid = Get.parameters['bvid']!; - int cid = int.parse(Get.parameters['cid']!); + RxInt cid = int.parse(Get.parameters['cid']!).obs; RxInt danmakuCid = 0.obs; String heroTag = Get.arguments['heroTag']; // 视频详情 @@ -109,7 +109,7 @@ class VideoDetailController extends GetxController localCache.get(LocalCacheKey.historyPause) == true) { enableHeart = false; } - danmakuCid.value = cid; + danmakuCid.value = cid.value; /// if (Platform.isAndroid) { @@ -218,7 +218,7 @@ class VideoDetailController extends GetxController // 默认1倍速 speed: 1.0, bvid: bvid, - cid: cid, + cid: cid.value, enableHeart: enableHeart, isFirstTime: isFirstTime, autoplay: autoplay, @@ -230,7 +230,7 @@ class VideoDetailController extends GetxController // 视频链接 Future queryVideoUrl() async { - var result = await VideoHttp.videoUrl(cid: cid, bvid: bvid); + var result = await VideoHttp.videoUrl(cid: cid.value, bvid: bvid); if (result['status']) { data = result['data']; diff --git a/lib/pages/video/detail/introduction/controller.dart b/lib/pages/video/detail/introduction/controller.dart index 71b63c71..04ece4a0 100644 --- a/lib/pages/video/detail/introduction/controller.dart +++ b/lib/pages/video/detail/introduction/controller.dart @@ -11,6 +11,7 @@ import 'package:pilipala/models/user/fav_folder.dart'; import 'package:pilipala/models/video_detail_res.dart'; import 'package:pilipala/pages/video/detail/controller.dart'; import 'package:pilipala/pages/video/detail/reply/index.dart'; +import 'package:pilipala/plugin/pl_player/models/play_repeat.dart'; import 'package:pilipala/utils/feed_back.dart'; import 'package:pilipala/utils/id_utils.dart'; import 'package:pilipala/utils/storage.dart'; @@ -58,6 +59,7 @@ class VideoIntroController extends GetxController { RxString total = '1'.obs; Timer? timer; bool isPaused = false; + String heroTag = Get.arguments['heroTag']; @override void onInit() { @@ -102,9 +104,10 @@ class VideoIntroController extends GetxController { if (videoDetail.value.pages!.isNotEmpty && lastPlayCid.value == 0) { lastPlayCid.value = videoDetail.value.pages!.first.cid!; } - Get.find(tag: Get.arguments['heroTag']) - .tabs - .value = ['简介', '评论 ${result['data']!.stat!.reply}']; + // Get.find(tag: heroTag).tabs.value = [ + // '简介', + // '评论 ${result['data']!.stat!.reply}' + // ]; // 获取到粉丝数再返回 await queryUserStat(); } @@ -440,16 +443,16 @@ class VideoIntroController extends GetxController { Future changeSeasonOrbangu(bvid, cid, aid) async { // 重新获取视频资源 VideoDetailController videoDetailCtr = - Get.find(tag: Get.arguments['heroTag']); + Get.find(tag: heroTag); videoDetailCtr.bvid = bvid; - videoDetailCtr.cid = cid; + videoDetailCtr.cid.value = cid; videoDetailCtr.danmakuCid.value = cid; videoDetailCtr.queryVideoUrl(); // 重新请求评论 try { /// 未渲染回复组件时可能异常 VideoReplyController videoReplyCtr = - Get.find(tag: Get.arguments['heroTag']); + Get.find(tag: heroTag); videoReplyCtr.aid = aid; videoReplyCtr.queryReplyList(type: 'init'); } catch (_) {} @@ -486,4 +489,52 @@ class VideoIntroController extends GetxController { } super.onClose(); } + + /// 列表循环或者顺序播放时,自动播放下一个 + void nextPlay() { + late List episodes; + // if (videoDetail.value.ugcSeason != null) { + // UgcSeason ugcSeason = videoDetail.value.ugcSeason!; + // List sections = ugcSeason.sections!; + // for (int i = 0; i < sections.length; i++) { + // List episodesList = sections[i].episodes!; + // for (int j = 0; j < episodesList.length; j++) { + // if (episodesList[j].cid == lastPlayCid.value) { + // episodes = episodesList; + // continue; + // } + // } + // } + // } + if (videoDetail.value.ugcSeason != null) { + UgcSeason ugcSeason = videoDetail.value.ugcSeason!; + List sections = ugcSeason.sections!; + episodes = []; + + for (int i = 0; i < sections.length; i++) { + List episodesList = sections[i].episodes!; + episodes.addAll(episodesList); + } + } + + int currentIndex = episodes.indexWhere((e) => e.cid == lastPlayCid.value); + int nextIndex = currentIndex + 1; + VideoDetailController videoDetailCtr = + Get.find(tag: heroTag); + PlayRepeat platRepeat = videoDetailCtr.plPlayerController.playRepeat; + + // 列表循环 + if (nextIndex >= episodes.length) { + if (platRepeat == PlayRepeat.listCycle) { + nextIndex = 0; + } + if (platRepeat == PlayRepeat.listOrder) { + return; + } + } + int cid = episodes[nextIndex].cid!; + String bvid = episodes[nextIndex].bvid!; + int aid = episodes[nextIndex].aid!; + changeSeasonOrbangu(bvid, cid, aid); + } } diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index e961f135..1c9520a8 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -330,17 +330,17 @@ class _VideoInfoState extends State with TickerProviderStateMixin { ), const SizedBox(height: 7), // 点赞收藏转发 布局样式1 - SingleChildScrollView( - padding: const EdgeInsets.only(top: 7, bottom: 7), - scrollDirection: Axis.horizontal, - child: actionRow( - context, - videoIntroController, - videoDetailCtr, - ), - ), + // SingleChildScrollView( + // padding: const EdgeInsets.only(top: 7, bottom: 7), + // scrollDirection: Axis.horizontal, + // child: actionRow( + // context, + // videoIntroController, + // videoDetailCtr, + // ), + // ), // 点赞收藏转发 布局样式2 - // actionGrid(context, videoIntroController), + actionGrid(context, videoIntroController), // 合集 if (!loadingStatus && widget.videoDetail!.ugcSeason != null) ...[ @@ -458,7 +458,7 @@ class _VideoInfoState extends State with TickerProviderStateMixin { Widget actionGrid(BuildContext context, videoIntroController) { return LayoutBuilder(builder: (context, constraints) { return Container( - padding: const EdgeInsets.only(top: 6, bottom: 10), + margin: const EdgeInsets.only(top: 6, bottom: 4), height: constraints.maxWidth / 5 * 0.8, child: GridView.count( primary: false, @@ -477,12 +477,12 @@ class _VideoInfoState extends State with TickerProviderStateMixin { ? widget.videoDetail!.stat!.like!.toString() : '-'), ), - ActionItem( - icon: const Icon(FontAwesomeIcons.clock), - onTap: () => videoIntroController.actionShareVideo(), - selectStatus: false, - loadingStatus: loadingStatus, - text: '稍后再看'), + // ActionItem( + // icon: const Icon(FontAwesomeIcons.clock), + // onTap: () => videoIntroController.actionShareVideo(), + // selectStatus: false, + // loadingStatus: loadingStatus, + // text: '稍后再看'), Obx( () => ActionItem( icon: const Icon(FontAwesomeIcons.b), @@ -498,22 +498,28 @@ class _VideoInfoState extends State with TickerProviderStateMixin { () => ActionItem( icon: const Icon(FontAwesomeIcons.star), selectIcon: const Icon(FontAwesomeIcons.solidStar), - // onTap: () => videoIntroController.actionFavVideo(), onTap: () => showFavBottomSheet(), + onLongPress: () => showFavBottomSheet(type: 'longPress'), selectStatus: videoIntroController.hasFav.value, loadingStatus: loadingStatus, text: !loadingStatus ? widget.videoDetail!.stat!.favorite!.toString() : '-'), ), + ActionItem( + icon: const Icon(FontAwesomeIcons.comment), + onTap: () => videoDetailCtr.tabCtr.animateTo(1), + selectStatus: false, + loadingStatus: loadingStatus, + text: !loadingStatus + ? widget.videoDetail!.stat!.reply!.toString() + : '评论'), ActionItem( icon: const Icon(FontAwesomeIcons.shareFromSquare), onTap: () => videoIntroController.actionShareVideo(), selectStatus: false, loadingStatus: loadingStatus, - text: !loadingStatus - ? widget.videoDetail!.stat!.share!.toString() - : '-'), + text: '分享'), ], ), ); diff --git a/lib/pages/video/detail/introduction/widgets/action_item.dart b/lib/pages/video/detail/introduction/widgets/action_item.dart index a2e54b33..95ac103b 100644 --- a/lib/pages/video/detail/introduction/widgets/action_item.dart +++ b/lib/pages/video/detail/introduction/widgets/action_item.dart @@ -6,6 +6,7 @@ class ActionItem extends StatelessWidget { final Icon? icon; final Icon? selectIcon; final Function? onTap; + final Function? onLongPress; final bool? loadingStatus; final String? text; final bool selectStatus; @@ -15,6 +16,7 @@ class ActionItem extends StatelessWidget { this.icon, this.selectIcon, this.onTap, + this.onLongPress, this.loadingStatus, this.text, this.selectStatus = false, @@ -27,6 +29,9 @@ class ActionItem extends StatelessWidget { feedBack(), onTap!(), }, + onLongPress: () => { + if (onLongPress != null) {onLongPress!()} + }, borderRadius: StyleString.mdRadius, child: Column( mainAxisAlignment: MainAxisAlignment.center, diff --git a/lib/pages/video/detail/introduction/widgets/page.dart b/lib/pages/video/detail/introduction/widgets/page.dart index 261b6227..b3f1f7a0 100644 --- a/lib/pages/video/detail/introduction/widgets/page.dart +++ b/lib/pages/video/detail/introduction/widgets/page.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:pilipala/models/video_detail_res.dart'; +import 'package:pilipala/pages/video/detail/index.dart'; class PagesPanel extends StatefulWidget { final List pages; @@ -22,13 +23,23 @@ class PagesPanel extends StatefulWidget { class _PagesPanelState extends State { late List episodes; + late int cid; late int currentIndex; + String heroTag = Get.arguments['heroTag']; + late VideoDetailController _videoDetailController; @override void initState() { super.initState(); + cid = widget.cid!; episodes = widget.pages; - currentIndex = episodes.indexWhere((e) => e.cid == widget.cid); + _videoDetailController = Get.find(tag: heroTag); + currentIndex = episodes.indexWhere((e) => e.cid == cid); + _videoDetailController.cid.listen((p0) { + cid = p0; + setState(() {}); + currentIndex = episodes.indexWhere((e) => e.cid == cid); + }); } void changeFucCall(item, i) async { diff --git a/lib/pages/video/detail/introduction/widgets/season.dart b/lib/pages/video/detail/introduction/widgets/season.dart index 3f3a1475..0be22757 100644 --- a/lib/pages/video/detail/introduction/widgets/season.dart +++ b/lib/pages/video/detail/introduction/widgets/season.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:pilipala/models/video_detail_res.dart'; +import 'package:pilipala/pages/video/detail/index.dart'; import 'package:pilipala/utils/id_utils.dart'; class SeasonPanel extends StatefulWidget { @@ -23,11 +24,16 @@ class SeasonPanel extends StatefulWidget { class _SeasonPanelState extends State { late List episodes; + late int cid; late int currentIndex; + String heroTag = Get.arguments['heroTag']; + late VideoDetailController _videoDetailController; @override void initState() { super.initState(); + cid = widget.cid!; + _videoDetailController = Get.find(tag: heroTag); /// 根据 cid 找到对应集,找到对应 episodes /// 有多个episodes时,只显示其中一个 @@ -36,7 +42,7 @@ class _SeasonPanelState extends State { for (int i = 0; i < sections.length; i++) { List episodesList = sections[i].episodes!; for (int j = 0; j < episodesList.length; j++) { - if (episodesList[j].cid == widget.cid) { + if (episodesList[j].cid == cid) { episodes = episodesList; continue; } @@ -47,7 +53,12 @@ class _SeasonPanelState extends State { // episodes = widget.ugcSeason.sections! // .firstWhere((e) => e.seasonId == widget.ugcSeason.id) // .episodes!; - currentIndex = episodes.indexWhere((e) => e.cid == widget.cid); + currentIndex = episodes.indexWhere((e) => e.cid == cid); + _videoDetailController.cid.listen((p0) { + cid = p0; + setState(() {}); + currentIndex = episodes.indexWhere((e) => e.cid == cid); + }); } void changeFucCall(item, i) async { @@ -57,6 +68,7 @@ class _SeasonPanelState extends State { item.aid, ); currentIndex = i; + setState(() {}); Get.back(); } diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 048876d6..3902a682 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -20,6 +20,7 @@ import 'package:pilipala/pages/video/detail/controller.dart'; import 'package:pilipala/pages/video/detail/introduction/index.dart'; import 'package:pilipala/pages/video/detail/related/index.dart'; import 'package:pilipala/plugin/pl_player/index.dart'; +import 'package:pilipala/plugin/pl_player/models/play_repeat.dart'; import 'package:pilipala/utils/storage.dart'; import 'widgets/app_bar.dart'; @@ -41,6 +42,7 @@ class _VideoDetailPageState extends State final ScrollController _extendNestCtr = ScrollController(); late StreamController appbarStream; late VideoIntroController videoIntroController; + late BangumiIntroController bangumiIntroController; late String heroTag; PlayerStatus playerStatus = PlayerStatus.playing; @@ -61,6 +63,7 @@ class _VideoDetailPageState extends State heroTag = Get.arguments['heroTag']; videoDetailController = Get.put(VideoDetailController(), tag: heroTag); videoIntroController = Get.put(VideoIntroController(), tag: heroTag); + bangumiIntroController = Get.put(BangumiIntroController(), tag: heroTag); statusBarHeight = localCache.get('statusBarHeight'); autoExitFullcreen = setting.get(SettingBoxKey.enableAutoExit, defaultValue: false); @@ -98,6 +101,23 @@ class _VideoDetailPageState extends State if (autoExitFullcreen) { plPlayerController!.triggerFullScreen(status: false); } + + /// 顺序播放 列表循环 + if (plPlayerController!.playRepeat != PlayRepeat.pause && + plPlayerController!.playRepeat != PlayRepeat.singleCycle) { + if (videoDetailController.videoType == SearchType.video) { + videoIntroController.nextPlay(); + } + if (videoDetailController.videoType == SearchType.media_bangumi) { + bangumiIntroController.nextPlay(); + } + } + + /// 单个循环 + if (plPlayerController!.playRepeat == PlayRepeat.singleCycle) { + plPlayerController!.seekTo(Duration.zero); + plPlayerController!.play(); + } // 播放完展示控制栏 try { PiPStatus currentStatus = @@ -385,8 +405,8 @@ class _VideoDetailPageState extends State const VideoIntroPanel(), ] else if (videoDetailController.videoType == SearchType.media_bangumi) ...[ - BangumiIntroPanel( - cid: videoDetailController.cid) + Obx(() => BangumiIntroPanel( + cid: videoDetailController.cid.value)), ], // if (videoDetailController.videoType == // SearchType.video) ...[ diff --git a/lib/pages/video/detail/widgets/header_control.dart b/lib/pages/video/detail/widgets/header_control.dart index 1697b4d4..35c54b07 100644 --- a/lib/pages/video/detail/widgets/header_control.dart +++ b/lib/pages/video/detail/widgets/header_control.dart @@ -13,6 +13,7 @@ import 'package:pilipala/models/video/play/url.dart'; import 'package:pilipala/pages/video/detail/index.dart'; import 'package:pilipala/pages/video/detail/introduction/widgets/menu_row.dart'; import 'package:pilipala/plugin/pl_player/index.dart'; +import 'package:pilipala/plugin/pl_player/models/play_repeat.dart'; import 'package:pilipala/utils/storage.dart'; class HeaderControl extends StatefulWidget implements PreferredSizeWidget { @@ -56,7 +57,7 @@ class _HeaderControlState extends State { builder: (_) { return Container( width: double.infinity, - height: 400, + height: 440, clipBehavior: Clip.hardEdge, decoration: BoxDecoration( color: Theme.of(context).colorScheme.background, @@ -149,13 +150,14 @@ class _HeaderControlState extends State { '当前解码格式 ${widget.videoDetailCtr!.currentDecodeFormats.description}', style: subTitleStyle), ), - // ListTile( - // onTap: () {}, - // dense: true, - // enabled: false, - // leading: const Icon(Icons.play_circle_outline, size: 20), - // title: Text('播放设置', style: titleStyle), - // ), + ListTile( + onTap: () => {Get.back(), showSetRepeat()}, + dense: true, + leading: const Icon(Icons.repeat, size: 20), + title: Text('播放顺序', style: titleStyle), + subtitle: Text(widget.controller!.playRepeat.description, + style: subTitleStyle), + ), ListTile( onTap: () => {Get.back(), showSetDanmaku()}, dense: true, @@ -704,6 +706,60 @@ class _HeaderControlState extends State { ); } + /// 播放顺序 + void showSetRepeat() async { + showModalBottomSheet( + context: context, + elevation: 0, + backgroundColor: Colors.transparent, + builder: (BuildContext context) { + return Container( + width: double.infinity, + height: 250, + clipBehavior: Clip.hardEdge, + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.background, + borderRadius: const BorderRadius.all(Radius.circular(12)), + ), + margin: const EdgeInsets.all(12), + child: Column( + children: [ + SizedBox( + height: 45, + child: Center(child: Text('选择播放顺序', style: titleStyle))), + Expanded( + child: Material( + child: ListView( + children: [ + for (var i in PlayRepeat.values) ...[ + ListTile( + onTap: () { + widget.controller!.setPlayRepeat(i); + Get.back(); + }, + dense: true, + contentPadding: + const EdgeInsets.only(left: 20, right: 20), + title: Text(i.description), + trailing: widget.controller!.playRepeat == i + ? Icon( + Icons.done, + color: Theme.of(context).colorScheme.primary, + ) + : const SizedBox(), + ) + ], + ], + ), + ), + ), + ], + ), + ); + }, + ); + } + @override Widget build(BuildContext context) { final _ = widget.controller!; diff --git a/lib/plugin/pl_player/controller.dart b/lib/plugin/pl_player/controller.dart index 01476ade..5d7a8332 100644 --- a/lib/plugin/pl_player/controller.dart +++ b/lib/plugin/pl_player/controller.dart @@ -13,6 +13,7 @@ import 'package:media_kit_video/media_kit_video.dart'; import 'package:ns_danmaku/ns_danmaku.dart'; import 'package:pilipala/http/video.dart'; import 'package:pilipala/plugin/pl_player/index.dart'; +import 'package:pilipala/plugin/pl_player/models/play_repeat.dart'; import 'package:pilipala/utils/feed_back.dart'; import 'package:pilipala/utils/storage.dart'; import 'package:screen_brightness/screen_brightness.dart'; @@ -209,6 +210,9 @@ class PlPlayerController { late double fontSizeVal; late double danmakuSpeedVal; + // 播放顺序相关 + PlayRepeat playRepeat = PlayRepeat.pause; + // 添加一个私有构造函数 PlPlayerController._() { _videoType = videoType; @@ -226,6 +230,12 @@ class PlPlayerController { // 弹幕速度 danmakuSpeedVal = localCache.get(LocalCacheKey.danmakuSpeed, defaultValue: 4.0); + playRepeat = PlayRepeat.values.toList().firstWhere( + (e) => + e.value == + videoStorage.get(VideoBoxKey.playRepeat, + defaultValue: PlayRepeat.pause.value), + ); // _playerEventSubs = onPlayerStatusChanged.listen((PlayerStatus status) { // if (status == PlayerStatus.playing) { // WakelockPlus.enable(); @@ -910,6 +920,11 @@ class PlPlayerController { } } + setPlayRepeat(PlayRepeat type) { + playRepeat = type; + videoStorage.put(VideoBoxKey.playRepeat, type.value); + } + Future dispose({String type = 'single'}) async { // 每次减1,最后销毁 if (type == 'single' && playerCount.value > 1) { diff --git a/lib/plugin/pl_player/models/play_repeat.dart b/lib/plugin/pl_player/models/play_repeat.dart new file mode 100644 index 00000000..e68196c7 --- /dev/null +++ b/lib/plugin/pl_player/models/play_repeat.dart @@ -0,0 +1,25 @@ +enum PlayRepeat { + pause, + listOrder, + singleCycle, + listCycle, +} + +extension PlayRepeatExtension on PlayRepeat { + static final List _descList = [ + '播完暂停', + '顺序播放', + '单个循环', + '列表循环', + ]; + get description => _descList[index]; + + static final List _valueList = [ + 1, + 2, + 3, + 4, + ]; + get value => _valueList[index]; + get defaultValue => _valueList[1]; +} diff --git a/lib/utils/storage.dart b/lib/utils/storage.dart index fdf0fb0a..c8b1d59d 100644 --- a/lib/utils/storage.dart +++ b/lib/utils/storage.dart @@ -157,4 +157,6 @@ class VideoBoxKey { static const String videoBrightness = 'videoBrightness'; // 倍速 static const String videoSpeed = 'videoSpeed'; + // 播放顺序 + static const String playRepeat = 'playRepeat'; }