From f7e9fbaea7be723f2dc9e1a98a22a713b3f26e75 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 28 Aug 2023 17:15:30 +0800 Subject: [PATCH 1/9] =?UTF-8?q?fix:=20Hero=E5=AF=BC=E8=87=B4videoPlayer?= =?UTF-8?q?=E6=B8=B2=E6=9F=93=E4=B8=A4=E6=AC=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/view.dart | 205 +++++++++++++++---------------- 1 file changed, 100 insertions(+), 105 deletions(-) diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index b51510dc..d193de42 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -187,120 +187,115 @@ class _VideoDetailPageState extends State builder: (context, boxConstraints) { double maxWidth = boxConstraints.maxWidth; double maxHeight = boxConstraints.maxHeight; - return Hero( - tag: videoDetailController.heroTag, - child: Stack( - children: [ - FutureBuilder( - future: _futureBuilderFuture, - builder: ((context, snapshot) { - if (snapshot.hasData && - snapshot.data['status']) { - return Obx( - () => videoDetailController - .autoPlay.value - ? PLVideoPlayer( + return Stack( + children: [ + FutureBuilder( + future: _futureBuilderFuture, + builder: ((context, snapshot) { + if (snapshot.hasData && + snapshot.data['status']) { + return Obx( + () => !videoDetailController + .autoPlay.value + ? const SizedBox() + : PLVideoPlayer( + controller: plPlayerController!, + headerControl: HeaderControl( controller: - plPlayerController!, - headerControl: HeaderControl( - controller: - plPlayerController, - videoDetailCtr: - videoDetailController, - ), - ) - : const SizedBox(), - ); - } else { - return const SizedBox(); - } - }), - ), - Obx( - () => Visibility( - visible: videoDetailController - .isShowCover.value, - child: Positioned( - top: 0, - left: 0, - right: 0, - child: NetworkImgLayer( - type: 'emote', - src: videoDetailController - .videoItem['pic'], - width: maxWidth, - height: maxHeight, - ), + plPlayerController, + videoDetailCtr: + videoDetailController, + ), + ), + ); + } else { + return const SizedBox(); + } + }), + ), + + Obx( + () => Visibility( + visible: + videoDetailController.isShowCover.value, + child: Positioned( + top: 0, + left: 0, + right: 0, + child: NetworkImgLayer( + type: 'emote', + src: videoDetailController + .videoItem['pic'], + width: maxWidth, + height: maxHeight, ), ), ), + ), - /// 关闭自动播放时 手动播放 - Obx( - () => Visibility( - visible: videoDetailController - .isShowCover.value && - videoDetailController - .isEffective.value && - !videoDetailController - .autoPlay.value, - child: Stack( - children: [ - Positioned( - top: 0, - left: 0, - right: 0, - child: AppBar( - primary: false, - foregroundColor: Colors.white, + /// 关闭自动播放时 手动播放 + Obx( + () => Visibility( + visible: videoDetailController + .isShowCover.value && + videoDetailController + .isEffective.value && + !videoDetailController.autoPlay.value, + child: Stack( + children: [ + Positioned( + top: 0, + left: 0, + right: 0, + child: AppBar( + primary: false, + foregroundColor: Colors.white, + backgroundColor: + Colors.transparent, + actions: [ + IconButton( + tooltip: '稍后再看', + onPressed: () async { + var res = await UserHttp + .toViewLater( + bvid: + videoDetailController + .bvid); + SmartDialog.showToast( + res['msg']); + }, + icon: const Icon( + Icons.history_outlined), + ), + const SizedBox(width: 14) + ], + ), + ), + Positioned( + right: 12, + bottom: 10, + child: TextButton.icon( + style: ButtonStyle( backgroundColor: - Colors.transparent, - actions: [ - IconButton( - tooltip: '稍后再看', - onPressed: () async { - var res = await UserHttp - .toViewLater( - bvid: - videoDetailController - .bvid); - SmartDialog.showToast( - res['msg']); - }, - icon: const Icon( - Icons.history_outlined), - ), - const SizedBox(width: 14) - ], + MaterialStateProperty + .resolveWith((states) { + return Theme.of(context) + .colorScheme + .primaryContainer; + }), ), - ), - Positioned( - right: 12, - bottom: 10, - child: TextButton.icon( - style: ButtonStyle( - backgroundColor: - MaterialStateProperty - .resolveWith( - (states) { - return Theme.of(context) - .colorScheme - .primaryContainer; - }), - ), - onPressed: () => handlePlay(), - icon: const Icon( - Icons.play_circle_outline, - size: 20, - ), - label: const Text('Play'), + onPressed: () => handlePlay(), + icon: const Icon( + Icons.play_circle_outline, + size: 20, ), + label: const Text('Play'), ), - ], - )), - ), - ], - ), + ), + ], + )), + ), + ], ); }, ), From c15646867cbbd8d7233cd683099059d7d94d1e27 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 28 Aug 2023 18:21:09 +0800 Subject: [PATCH 2/9] =?UTF-8?q?fix:=20issues=20#42=20=E6=94=B6=E8=97=8F?= =?UTF-8?q?=E5=A4=B9=E5=8F=AA=E6=98=BE=E7=A4=BA=E5=89=8D50=E4=B8=AA?= =?UTF-8?q?=E8=A7=86=E9=A2=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/favDetail/controller.dart | 16 +++++++++-- lib/pages/favDetail/view.dart | 41 ++++++++++++++++++----------- 2 files changed, 39 insertions(+), 18 deletions(-) diff --git a/lib/pages/favDetail/controller.dart b/lib/pages/favDetail/controller.dart index 5229ee87..8b772716 100644 --- a/lib/pages/favDetail/controller.dart +++ b/lib/pages/favDetail/controller.dart @@ -15,6 +15,8 @@ class FavDetailController extends GetxController { bool isLoadingMore = false; RxMap favInfo = {}.obs; RxList favList = [FavDetailItemData()].obs; + RxString loadingText = '加载中...'.obs; + int mediaCount = 0; @override void onInit() { @@ -27,6 +29,11 @@ class FavDetailController extends GetxController { } Future queryUserFavFolderDetail({type = 'init'}) async { + if (type == 'onLoad' && favList.length >= mediaCount) { + loadingText.value = '没有更多了'; + return; + } + isLoadingMore = true; var res = await await UserHttp.userFavFolderDetail( pn: currentPage, ps: 20, @@ -36,11 +43,16 @@ class FavDetailController extends GetxController { favInfo.value = res['data'].info; if (currentPage == 1 && type == 'init') { favList.value = res['data'].medias; - } else if (type == 'onload') { + mediaCount = res['data'].info['media_count']; + } else if (type == 'onLoad') { favList.addAll(res['data'].medias); } + if (favList.length >= mediaCount) { + loadingText.value = '没有更多了'; + } } currentPage += 1; + isLoadingMore = false; return res; } @@ -64,6 +76,6 @@ class FavDetailController extends GetxController { } onLoad() { - queryUserFavFolderDetail(type: 'onload'); + queryUserFavFolderDetail(type: 'onLoad'); } } diff --git a/lib/pages/favDetail/view.dart b/lib/pages/favDetail/view.dart index a99d1c03..426bfa8f 100644 --- a/lib/pages/favDetail/view.dart +++ b/lib/pages/favDetail/view.dart @@ -1,9 +1,12 @@ import 'dart:async'; +import 'package:easy_debounce/easy_throttle.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:pilipala/common/skeleton/video_card_h.dart'; import 'package:pilipala/common/widgets/http_error.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; +import 'package:pilipala/common/widgets/no_data.dart'; import 'package:pilipala/pages/favDetail/index.dart'; import 'widget/fav_video_card.dart'; @@ -37,10 +40,9 @@ class _FavDetailPageState extends State { if (_controller.position.pixels >= _controller.position.maxScrollExtent - 200) { - if (!_favDetailController.isLoadingMore) { - _favDetailController.isLoadingMore = true; + EasyThrottle.throttle('favDetail', const Duration(seconds: 1), () { _favDetailController.onLoad(); - } + }); } }, ); @@ -183,12 +185,7 @@ class _FavDetailPageState extends State { Map data = snapshot.data; if (data['status']) { if (_favDetailController.item!.mediaCount == 0) { - return const SliverToBoxAdapter( - child: SizedBox( - height: 300, - child: Center(child: Text('没有内容')), - ), - ); + return const NoData(); } else { return Obx( () => SliverList( @@ -207,18 +204,30 @@ class _FavDetailPageState extends State { ); } } else { - return const SliverToBoxAdapter( - child: SizedBox( - height: 300, - child: Center(child: Text('加载中')), - ), + // 骨架屏 + return SliverList( + delegate: SliverChildBuilderDelegate((context, index) { + return const VideoCardHSkeleton(); + }, childCount: 10), ); } }, ), SliverToBoxAdapter( - child: SizedBox( - height: MediaQuery.of(context).padding.bottom + 20, + child: Container( + height: MediaQuery.of(context).padding.bottom + 60, + padding: EdgeInsets.only( + bottom: MediaQuery.of(context).padding.bottom), + child: Center( + child: Obx( + () => Text( + _favDetailController.loadingText.value, + style: TextStyle( + color: Theme.of(context).colorScheme.outline, + fontSize: 13), + ), + ), + ), ), ) ], From 73e8972a762facb4d7de099b2e32e4cc60c0ae10 Mon Sep 17 00:00:00 2001 From: Infinite Date: Mon, 28 Aug 2023 23:40:04 +0800 Subject: [PATCH 3/9] Update README.md --- README.md | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index fac2a885..6d2c1d56 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,14 @@ Xcode 13.4 不支持**auto_orientation**,请注释相关代码 ``` +
+ + +## 技术交流 + +Telegram: https://t.me/+lm_oOVmF0RJiODk1 + +
## 功能 @@ -100,6 +108,7 @@ Xcode 13.4 不支持**auto_orientation**,请注释相关代码 - [x] 主题模式:亮色/暗色/跟随系统 - [x] 震动反馈(可选) - [x] 高帧率 + - [x] 自动全屏 - [ ] 等等
@@ -117,11 +126,6 @@ Xcode 13.4 不支持**auto_orientation**,请注释相关代码 感谢使用 -
- -## 技术交流 - -Telegram https://t.me/+lm_oOVmF0RJiODk1
From 184088f96d83e3baf4e9c8373dd5c49597b415bd Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 29 Aug 2023 11:23:30 +0800 Subject: [PATCH 4/9] =?UTF-8?q?feat:=20=E5=8F=AF=E5=90=A6=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E6=89=93=E5=BC=80=E8=A7=86=E9=A2=91=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E5=85=A8=E5=B1=8F=E7=9A=84=E5=8A=9F=E8=83=BD=20issues=20#37?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/setting/play_setting.dart | 12 ++ lib/pages/video/detail/controller.dart | 4 +- lib/pages/video/detail/introduction/view.dart | 36 ++++-- lib/pages/video/detail/view.dart | 38 +++---- lib/plugin/pl_player/controller.dart | 106 +++++++++++++++++- lib/plugin/pl_player/view.dart | 67 +---------- lib/utils/storage.dart | 2 + 7 files changed, 166 insertions(+), 99 deletions(-) diff --git a/lib/pages/setting/play_setting.dart b/lib/pages/setting/play_setting.dart index 8f9d8226..6d74c5b3 100644 --- a/lib/pages/setting/play_setting.dart +++ b/lib/pages/setting/play_setting.dart @@ -78,6 +78,18 @@ class _PlaySettingState extends State { setKey: SettingBoxKey.enableAutoBrightness, defaultVal: false, ), + const SetSwitchItem( + title: '自动全屏', + subTitle: '视频开始播放时进入全屏', + setKey: SettingBoxKey.enableAutoEnter, + defaultVal: false, + ), + const SetSwitchItem( + title: '自动退出', + subTitle: '视频结束播放时退出全屏', + setKey: SettingBoxKey.enableAutoExit, + defaultVal: false, + ), ListTile( dense: false, title: Text('默认画质', style: titleStyle), diff --git a/lib/pages/video/detail/controller.dart b/lib/pages/video/detail/controller.dart index 53f0e16d..27cf35c2 100644 --- a/lib/pages/video/detail/controller.dart +++ b/lib/pages/video/detail/controller.dart @@ -73,6 +73,7 @@ class VideoDetailController extends GetxController // 默认记录历史记录 bool enableHeart = true; var userInfo; + late bool isFirstTime = true; @override void onInit() { @@ -193,6 +194,7 @@ class VideoDetailController extends GetxController bvid: bvid, cid: cid, enableHeart: enableHeart, + isFirstTime: isFirstTime, ); } @@ -233,7 +235,6 @@ class VideoDetailController extends GetxController currentDecodeFormats = VideoDecodeFormatsCode.fromString(setting.get( SettingBoxKey.defaultDecode, defaultValue: VideoDecodeFormats.values.last.code))!; - print(currentDecodeFormats.description); try { // 当前视频没有对应格式返回第一个 bool flag = false; @@ -287,6 +288,7 @@ class VideoDetailController extends GetxController defaultST = Duration(milliseconds: data.lastPlayTime!); if (autoPlay.value) { await playerInit(); + isShowCover.value = false; } } else { if (result['code'] == -404) { diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index c993fda9..75eef280 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -31,9 +31,10 @@ class VideoIntroPanel extends StatefulWidget { class _VideoIntroPanelState extends State with AutomaticKeepAliveClientMixin { - final VideoIntroController videoIntroController = - Get.put(VideoIntroController(), tag: Get.arguments['heroTag']); + late String heroTag; + late VideoIntroController videoIntroController; VideoDetailData? videoDetail; + late Future? _futureBuilderFuture; // 添加页面缓存 @override @@ -42,6 +43,11 @@ class _VideoIntroPanelState extends State @override void initState() { super.initState(); + + /// fix 全屏时参数丢失 + heroTag = Get.arguments['heroTag']; + videoIntroController = Get.put(VideoIntroController(), tag: heroTag); + _futureBuilderFuture = videoIntroController.queryVideoIntro(); videoIntroController.videoDetail.listen((value) { videoDetail = value; }); @@ -57,15 +63,20 @@ class _VideoIntroPanelState extends State Widget build(BuildContext context) { super.build(context); return FutureBuilder( - future: videoIntroController.queryVideoIntro(), + future: _futureBuilderFuture, builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.done) { + if (snapshot.data == null) { + return const SliverToBoxAdapter(child: SizedBox()); + } if (snapshot.data['status']) { // 请求成功 return Obx( () => VideoInfo( - loadingStatus: false, - videoDetail: videoIntroController.videoDetail.value), + loadingStatus: false, + videoDetail: videoIntroController.videoDetail.value, + heroTag: heroTag, + ), ); } else { // 请求错误 @@ -79,7 +90,11 @@ class _VideoIntroPanelState extends State ); } } else { - return VideoInfo(loadingStatus: true, videoDetail: videoDetail); + return VideoInfo( + loadingStatus: true, + videoDetail: videoDetail, + heroTag: heroTag, + ); } }, ); @@ -89,8 +104,10 @@ class _VideoIntroPanelState extends State class VideoInfo extends StatefulWidget { final bool loadingStatus; final VideoDetailData? videoDetail; + final String? heroTag; - const VideoInfo({Key? key, this.loadingStatus = false, this.videoDetail}) + const VideoInfo( + {Key? key, this.loadingStatus = false, this.videoDetail, this.heroTag}) : super(key: key); @override @@ -98,7 +115,8 @@ class VideoInfo extends StatefulWidget { } class _VideoInfoState extends State with TickerProviderStateMixin { - final String heroTag = Get.arguments['heroTag']; + // final String heroTag = Get.arguments['heroTag']; + late String heroTag; late final VideoIntroController videoIntroController; late final VideoDetailController videoDetailCtr; late final Map videoItem; @@ -117,7 +135,7 @@ class _VideoInfoState extends State with TickerProviderStateMixin { @override void initState() { super.initState(); - + heroTag = widget.heroTag!; videoIntroController = Get.put(VideoIntroController(), tag: heroTag); videoDetailCtr = Get.find(tag: heroTag); videoItem = videoIntroController.videoItem!; diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index d193de42..e144918f 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -41,7 +41,6 @@ class _VideoDetailPageState extends State Get.put(VideoIntroController(), tag: Get.arguments['heroTag']); PlayerStatus playerStatus = PlayerStatus.playing; - // bool isShowCover = true; double doubleOffset = 0; Box localCache = GStrorage.localCache; @@ -49,11 +48,15 @@ class _VideoDetailPageState extends State late double statusBarHeight; final videoHeight = Get.size.width * 9 / 16; late Future _futureBuilderFuture; + // 自动退出全屏 + late bool autoExitFullcreen; @override void initState() { super.initState(); statusBarHeight = localCache.get('statusBarHeight'); + autoExitFullcreen = + setting.get(SettingBoxKey.enableAutoExit, defaultValue: false); videoSourceInit(); appbarStreamListen(); } @@ -63,7 +66,7 @@ class _VideoDetailPageState extends State _futureBuilderFuture = videoDetailController.queryVideoUrl(); if (videoDetailController.autoPlay.value) { plPlayerController = videoDetailController.plPlayerController; - playerListener(); + plPlayerController!.addStatusLister(playerListener); } } @@ -79,23 +82,15 @@ class _VideoDetailPageState extends State } // 播放器状态监听 - void playerListener() { - plPlayerController!.onPlayerStatusChanged.listen( - (PlayerStatus status) async { - playerStatus = status; - if (status == PlayerStatus.playing) { - videoDetailController.isShowCover.value = false; - } else { - // 播放完成停止 or 切换下一个 - if (status == PlayerStatus.completed) { - // 当只有1p或多p未打开自动播放时,播放完成还原进度条,展示控制栏 - plPlayerController!.seekTo(Duration.zero); - plPlayerController!.onLockControl(false); - plPlayerController!.videoPlayerController!.pause(); - } - } - }, - ); + void playerListener(PlayerStatus? status) { + if (status == PlayerStatus.completed) { + // 结束播放退出全屏 + if (autoExitFullcreen) { + plPlayerController!.triggerFullScreen(status: false); + } + // 播放完展示控制栏 + plPlayerController!.onLockControl(false); + } } // 继续播放或重新播放 @@ -110,11 +105,11 @@ class _VideoDetailPageState extends State plPlayerController = videoDetailController.plPlayerController; videoDetailController.autoPlay.value = true; videoDetailController.isShowCover.value = false; - playerListener(); } @override void dispose() { + plPlayerController!.removeStatusLister(playerListener); plPlayerController!.dispose(); super.dispose(); } @@ -128,6 +123,7 @@ class _VideoDetailPageState extends State } videoDetailController.defaultST = plPlayerController!.position.value; videoIntroController.isPaused = true; + plPlayerController!.removeStatusLister(playerListener); plPlayerController!.pause(); super.didPushNext(); } @@ -135,12 +131,14 @@ class _VideoDetailPageState extends State @override // 返回当前页面时 void didPopNext() async { + videoDetailController.isFirstTime = false; videoDetailController.playerInit(); videoIntroController.isPaused = false; if (_extendNestCtr.position.pixels == 0) { await Future.delayed(const Duration(milliseconds: 300)); plPlayerController!.play(); } + plPlayerController!.addStatusLister(playerListener); super.didPopNext(); } diff --git a/lib/plugin/pl_player/controller.dart b/lib/plugin/pl_player/controller.dart index 861c7898..648c059a 100644 --- a/lib/plugin/pl_player/controller.dart +++ b/lib/plugin/pl_player/controller.dart @@ -11,18 +11,15 @@ import 'package:hive/hive.dart'; import 'package:media_kit/media_kit.dart'; import 'package:media_kit_video/media_kit_video.dart'; import 'package:pilipala/http/video.dart'; -import 'package:pilipala/plugin/pl_player/models/data_source.dart'; +import 'package:pilipala/plugin/pl_player/index.dart'; import 'package:pilipala/utils/feed_back.dart'; import 'package:pilipala/utils/storage.dart'; import 'package:screen_brightness/screen_brightness.dart'; import 'package:universal_platform/universal_platform.dart'; // import 'package:wakelock_plus/wakelock_plus.dart'; -import 'models/data_status.dart'; -import 'models/play_speed.dart'; -import 'models/play_status.dart'; - Box videoStorage = GStrorage.video; +Box setting = GStrorage.setting; class PlPlayerController { Player? _videoPlayerController; @@ -84,6 +81,7 @@ class PlPlayerController { int _cid = 0; int _heartDuration = 0; bool _enableHeart = true; + bool _isFirstTime = true; Timer? _timer; Timer? _timerForSeek; @@ -248,6 +246,8 @@ class PlPlayerController { int cid = 0, // 历史记录开关 bool enableHeart = true, + // 是否首次加载 + bool isFirstTime = true, }) async { try { _autoPlay = autoplay; @@ -261,6 +261,7 @@ class PlPlayerController { _bvid = bvid; _cid = cid; _enableHeart = enableHeart; + _isFirstTime = isFirstTime; if (_videoPlayerController != null && _videoPlayerController!.state.playing) { @@ -281,6 +282,12 @@ class PlPlayerController { if (!_listenersInitialized) { startListeners(); } + bool autoEnterFullcreen = + setting.get(SettingBoxKey.enableAutoEnter, defaultValue: false); + if (autoEnterFullcreen && _isFirstTime) { + await Future.delayed(const Duration(milliseconds: 100)); + triggerFullScreen(); + } } catch (err) { dataStatus.status.value = DataStatus.error; print('plPlayer err: $err'); @@ -397,6 +404,8 @@ class PlPlayerController { } List subscriptions = []; + final List _positionListeners = []; + final List _statusListeners = []; /// 播放事件监听 void startListeners() { @@ -408,11 +417,21 @@ class PlPlayerController { } else { // playerStatus.status.value = PlayerStatus.paused; } + + /// 触发回调事件 + for (var element in _statusListeners) { + element(event ? PlayerStatus.playing : PlayerStatus.paused); + } makeHeartBeat(_position.value.inSeconds, type: 'status'); }), videoPlayerController!.stream.completed.listen((event) { if (event) { playerStatus.status.value = PlayerStatus.completed; + + /// 触发回调事件 + for (var element in _statusListeners) { + element(PlayerStatus.completed); + } } else { // playerStatus.status.value = PlayerStatus.playing; } @@ -423,6 +442,11 @@ class PlPlayerController { if (!isSliderMoving.value) { _sliderPosition.value = event; } + + /// 触发回调事件 + for (var element in _positionListeners) { + element(); + } makeHeartBeat(event.inSeconds); }), videoPlayerController!.stream.duration.listen((event) { @@ -714,6 +738,78 @@ class PlPlayerController { _isFullScreen.value = val; } + // 全屏 + Future triggerFullScreen({bool status = true}) async { + FullScreenMode mode = FullScreenModeCode.fromCode( + setting.get(SettingBoxKey.fullScreenMode, defaultValue: 0))!; + + if (!isFullScreen.value && status) { + /// 按照视频宽高比决定全屏方向 + switch (mode) { + case FullScreenMode.auto: + if (direction.value == 'horizontal') { + /// 进入全屏 + await enterFullScreen(); + // 横屏 + await landScape(); + } else { + // 竖屏 + await verticalScreen(); + } + break; + case FullScreenMode.vertical: + + /// 进入全屏 + await enterFullScreen(); + // 横屏 + await verticalScreen(); + break; + case FullScreenMode.horizontal: + + /// 进入全屏 + await enterFullScreen(); + // 横屏 + await landScape(); + break; + } + + toggleFullScreen(true); + var result = await showDialog( + context: Get.context!, + useSafeArea: false, + builder: (context) => Dialog.fullscreen( + backgroundColor: Colors.black, + child: PLVideoPlayer( + controller: this, + headerControl: headerControl, + ), + ), + ); + if (result == null) { + // 退出全屏 + exitFullScreen(); + await verticalScreen(); + toggleFullScreen(false); + } + } else if (isFullScreen.value) { + Get.back(); + exitFullScreen(); + await verticalScreen(); + toggleFullScreen(false); + } + } + + void addPositionListener(VoidCallback listener) => + _positionListeners.add(listener); + void removePositionListener(VoidCallback listener) => + _positionListeners.remove(listener); + void addStatusLister(Function(PlayerStatus status) listener) { + _statusListeners.add(listener); + } + + void removeStatusLister(Function(PlayerStatus status) listener) => + _statusListeners.remove(listener); + /// 截屏 Future screenshot() async { final Uint8List? screenshot = diff --git a/lib/plugin/pl_player/view.dart b/lib/plugin/pl_player/view.dart index 077b5a1b..ea7315dd 100644 --- a/lib/plugin/pl_player/view.dart +++ b/lib/plugin/pl_player/view.dart @@ -159,67 +159,6 @@ class _PLVideoPlayerState extends State widget.controller.brightness.value = value; } - Future triggerFullScreen() async { - PlPlayerController _ = widget.controller; - mode = FullScreenModeCode.fromCode( - setting.get(SettingBoxKey.fullScreenMode, defaultValue: 0))!; - - if (!_.isFullScreen.value) { - /// 按照视频宽高比决定全屏方向 - switch (mode) { - case FullScreenMode.auto: - if (_.direction.value == 'horizontal') { - /// 进入全屏 - await enterFullScreen(); - // 横屏 - await landScape(); - } else { - // 竖屏 - await verticalScreen(); - } - break; - case FullScreenMode.vertical: - - /// 进入全屏 - await enterFullScreen(); - // 横屏 - await verticalScreen(); - break; - case FullScreenMode.horizontal: - - /// 进入全屏 - await enterFullScreen(); - // 横屏 - await landScape(); - break; - } - - _.toggleFullScreen(true); - var result = await showDialog( - context: Get.context!, - useSafeArea: false, - builder: (context) => Dialog.fullscreen( - backgroundColor: Colors.black, - child: PLVideoPlayer( - controller: _, - headerControl: _.headerControl, - ), - ), - ); - if (result == null) { - // 退出全屏 - exitFullScreen(); - await verticalScreen(); - _.toggleFullScreen(false); - } - } else { - Get.back(); - exitFullScreen(); - await verticalScreen(); - _.toggleFullScreen(false); - } - } - @override void dispose() { animationController.dispose(); @@ -559,13 +498,13 @@ class _PLVideoPlayerState extends State if (dy > _distance && dy > threshold) { if (_.isFullScreen.value) { // 下滑退出全屏 - await triggerFullScreen(); + await widget.controller.triggerFullScreen(status: false); } _distance = 0.0; } else if (dy < _distance && dy < -threshold) { if (!_.isFullScreen.value) { // 上滑进入全屏 - await triggerFullScreen(); + await widget.controller.triggerFullScreen(); } _distance = 0.0; } @@ -606,7 +545,7 @@ class _PLVideoPlayerState extends State position: 'bottom', child: BottomControl( controller: widget.controller, - triggerFullScreen: triggerFullScreen), + triggerFullScreen: widget.controller.triggerFullScreen), ), ), ], diff --git a/lib/utils/storage.dart b/lib/utils/storage.dart index 21a8060b..91094a39 100644 --- a/lib/utils/storage.dart +++ b/lib/utils/storage.dart @@ -97,6 +97,8 @@ class SettingBoxKey { static const String enableHA = 'enableHA'; static const String enableOnlineTotal = 'enableOnlineTotal'; static const String enableAutoBrightness = 'enableAutoBrightness'; + static const String enableAutoEnter = 'enableAutoEnter'; + static const String enableAutoExit = 'enableAutoExit'; /// 隐私 static const String blackMidsList = 'blackMidsList'; From 52e44fb95bc8fdf38905352fc6cb843609fa7da8 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 30 Aug 2023 09:01:21 +0800 Subject: [PATCH 5/9] =?UTF-8?q?mod:=20=E8=A7=86=E9=A2=91=E9=94=81=E5=AE=9A?= =?UTF-8?q?=E9=80=BB=E8=BE=91=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/plugin/pl_player/view.dart | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/lib/plugin/pl_player/view.dart b/lib/plugin/pl_player/view.dart index ea7315dd..e6ba2e05 100644 --- a/lib/plugin/pl_player/view.dart +++ b/lib/plugin/pl_player/view.dart @@ -422,8 +422,8 @@ class _PLVideoPlayerState extends State _.controls = !_.showControls.value; }, onDoubleTapDown: (details) { - // live模式下禁用 - if (_.videoType.value == 'live') { + // live模式下禁用 锁定时🔒禁用 + if (_.videoType.value == 'live' || _.controlsLock.value) { return; } final totalWidth = MediaQuery.of(context).size.width; @@ -453,7 +453,8 @@ class _PLVideoPlayerState extends State /// 水平位置 快进 live模式下禁用 onHorizontalDragUpdate: (DragUpdateDetails details) { - if (_.videoType.value == 'live') { + // live模式下禁用 锁定时🔒禁用 + if (_.videoType.value == 'live' || _.controlsLock.value) { return; } final tapPosition = details.localPosition.dx; @@ -474,7 +475,7 @@ class _PLVideoPlayerState extends State _initTapPositoin = tapPosition; }, onHorizontalDragEnd: (DragEndDetails details) { - if (_.videoType.value == 'live') { + if (_.videoType.value == 'live' || _.controlsLock.value) { return; } _.onChangedSliderEnd(); @@ -486,6 +487,11 @@ class _PLVideoPlayerState extends State final tapPosition = details.localPosition.dx; final sectionWidth = totalWidth / 3; final delta = details.delta.dy; + + /// 锁定时禁用 + if (_.controlsLock.value) { + return; + } if (tapPosition < sectionWidth) { // 左边区域 👈 final brightness = _brightnessValue - delta / 100.0; @@ -621,7 +627,7 @@ class _PLVideoPlayerState extends State child: Align( alignment: Alignment.centerLeft, child: FractionalTranslation( - translation: const Offset(0.5, 0.0), + translation: const Offset(1, 0.0), child: Visibility( visible: _.showControls.value, child: ComBtn( From fceb55aaa325d1a42e33462bb8712d4878d678a4 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 30 Aug 2023 09:18:34 +0800 Subject: [PATCH 6/9] =?UTF-8?q?feat:=20=E4=B8=A4=E6=AC=A1=E9=80=80?= =?UTF-8?q?=E5=87=BA=E7=A1=AE=E8=AE=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/main/controller.dart | 14 +++++ lib/pages/main/view.dart | 95 ++++++++++++++++++---------------- 2 files changed, 63 insertions(+), 46 deletions(-) diff --git a/lib/pages/main/controller.dart b/lib/pages/main/controller.dart index 85150349..1d296105 100644 --- a/lib/pages/main/controller.dart +++ b/lib/pages/main/controller.dart @@ -1,5 +1,6 @@ import 'dart:async'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:flutter/material.dart'; import 'package:hive/hive.dart'; @@ -53,6 +54,7 @@ class MainController extends GetxController { final StreamController bottomBarStream = StreamController.broadcast(); Box setting = GStrorage.setting; + DateTime? _lastPressedAt; @override void onInit() { @@ -61,4 +63,16 @@ class MainController extends GetxController { Utils.checkUpdata(); } } + + Future onBackPressed(BuildContext context) { + if (_lastPressedAt == null || + DateTime.now().difference(_lastPressedAt!) > + const Duration(seconds: 2)) { + // 两次点击时间间隔超过2秒,重新记录时间戳 + _lastPressedAt = DateTime.now(); + SmartDialog.showToast("再按一次退出Pili"); + return Future.value(false); // 不退出应用 + } + return Future.value(true); // 退出应用 + } } diff --git a/lib/pages/main/view.dart b/lib/pages/main/view.dart index b771ab0f..c744098e 100644 --- a/lib/pages/main/view.dart +++ b/lib/pages/main/view.dart @@ -110,55 +110,58 @@ class _MainAppState extends State with SingleTickerProviderStateMixin { MediaQuery.of(context).size.width * 9 / 16; localCache.put('sheetHeight', sheetHeight); localCache.put('statusBarHeight', statusBarHeight); - return Scaffold( - extendBody: true, - body: FadeTransition( - opacity: _fadeAnimation!, - child: SlideTransition( - position: Tween( - begin: const Offset(0, 0.5), - end: Offset.zero, - ).animate( - CurvedAnimation( - parent: _slideAnimation!, - curve: Curves.fastOutSlowIn, - reverseCurve: Curves.linear, + return WillPopScope( + onWillPop: () => _mainController.onBackPressed(context), + child: Scaffold( + extendBody: true, + body: FadeTransition( + opacity: _fadeAnimation!, + child: SlideTransition( + position: Tween( + begin: const Offset(0, 0.5), + end: Offset.zero, + ).animate( + CurvedAnimation( + parent: _slideAnimation!, + curve: Curves.fastOutSlowIn, + reverseCurve: Curves.linear, + ), + ), + child: PageView( + physics: const NeverScrollableScrollPhysics(), + controller: _pageController, + onPageChanged: (index) { + selectedIndex = index; + setState(() {}); + }, + children: _mainController.pages, ), - ), - child: PageView( - physics: const NeverScrollableScrollPhysics(), - controller: _pageController, - onPageChanged: (index) { - selectedIndex = index; - setState(() {}); - }, - children: _mainController.pages, ), ), - ), - bottomNavigationBar: StreamBuilder( - stream: _mainController.bottomBarStream.stream, - initialData: true, - builder: (context, AsyncSnapshot snapshot) { - return AnimatedSlide( - curve: Curves.easeInOutCubicEmphasized, - duration: const Duration(milliseconds: 1000), - offset: Offset(0, snapshot.data ? 0 : 1), - child: NavigationBar( - onDestinationSelected: (value) => setIndex(value), - selectedIndex: selectedIndex, - destinations: [ - ..._mainController.navigationBars.map((e) { - return NavigationDestination( - icon: e['icon'], - selectedIcon: e['selectIcon'], - label: e['label'], - ); - }).toList(), - ], - ), - ); - }, + bottomNavigationBar: StreamBuilder( + stream: _mainController.bottomBarStream.stream, + initialData: true, + builder: (context, AsyncSnapshot snapshot) { + return AnimatedSlide( + curve: Curves.easeInOutCubicEmphasized, + duration: const Duration(milliseconds: 1000), + offset: Offset(0, snapshot.data ? 0 : 1), + child: NavigationBar( + onDestinationSelected: (value) => setIndex(value), + selectedIndex: selectedIndex, + destinations: [ + ..._mainController.navigationBars.map((e) { + return NavigationDestination( + icon: e['icon'], + selectedIcon: e['selectIcon'], + label: e['label'], + ); + }).toList(), + ], + ), + ); + }, + ), ), ); } From 108c37f0d7147330ff44ff3a72abe2f6d5925e6f Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 30 Aug 2023 09:36:19 +0800 Subject: [PATCH 7/9] =?UTF-8?q?feat:=20=E7=A7=BB=E9=99=A4=E5=8D=95?= =?UTF-8?q?=E4=B8=AA=E7=A8=8D=E5=90=8E=E5=86=8D=E7=9C=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/user.dart | 14 ++++++++------ lib/pages/later/controller.dart | 33 +++++++++++++++++++++++---------- lib/pages/later/view.dart | 8 +++++--- 3 files changed, 36 insertions(+), 19 deletions(-) diff --git a/lib/http/user.dart b/lib/http/user.dart index 8fceea41..404502b3 100644 --- a/lib/http/user.dart +++ b/lib/http/user.dart @@ -179,14 +179,16 @@ class UserHttp { } // 移除已观看 - static Future toViewDel() async { + static Future toViewDel({int? aid}) async { + final Map params = { + 'jsonp': 'jsonp', + 'csrf': await Request.getCsrf(), + }; + + params[aid != null ? 'aid' : 'viewed'] = aid ?? true; var res = await Request().post( Api.toViewDel, - queryParameters: { - 'jsonp': 'jsonp', - 'viewed': true, - 'csrf': await Request.getCsrf(), - }, + queryParameters: params, ); if (res.data['code'] == 0) { return {'status': true, 'msg': 'yeah!成功移除'}; diff --git a/lib/pages/later/controller.dart b/lib/pages/later/controller.dart index 6de9254c..3de51901 100644 --- a/lib/pages/later/controller.dart +++ b/lib/pages/later/controller.dart @@ -23,29 +23,38 @@ class LaterController extends GetxController { return res; } - Future toViewDel() async { + Future toViewDel({int? aid}) async { SmartDialog.show( useSystem: true, animationType: SmartAnimationType.centerFade_otherSlide, builder: (BuildContext context) { return AlertDialog( title: const Text('提示'), - content: const Text('即将删除所有已观看视频,此操作不可恢复。确定是否删除?'), + content: Text( + aid != null ? '即将移除该视频,确定是否移除' : '即将删除所有已观看视频,此操作不可恢复。确定是否删除?'), actions: [ TextButton( - onPressed: () => SmartDialog.dismiss(), - child: const Text('取消')), + onPressed: () => SmartDialog.dismiss(), + child: Text( + '取消', + style: TextStyle(color: Theme.of(context).colorScheme.outline), + ), + ), TextButton( onPressed: () async { - var res = await UserHttp.toViewDel(); + var res = await UserHttp.toViewDel(aid: aid); if (res['status']) { - laterList.clear(); - queryLaterList(); + if (aid != null) { + laterList.removeWhere((e) => e.aid == aid); + } else { + laterList.clear(); + queryLaterList(); + } } SmartDialog.dismiss(); SmartDialog.showToast(res['msg']); }, - child: const Text('确认删除'), + child: Text(aid != null ? '确认移除' : '确认删除'), ) ], ); @@ -64,8 +73,12 @@ class LaterController extends GetxController { content: const Text('确定要清空你的稍后再看列表吗?'), actions: [ TextButton( - onPressed: () => SmartDialog.dismiss(), - child: const Text('取消')), + onPressed: () => SmartDialog.dismiss(), + child: Text( + '取消', + style: TextStyle(color: Theme.of(context).colorScheme.outline), + ), + ), TextButton( onPressed: () async { var res = await UserHttp.toViewClear(); diff --git a/lib/pages/later/view.dart b/lib/pages/later/view.dart index 7c04f8dc..76527ca1 100644 --- a/lib/pages/later/view.dart +++ b/lib/pages/later/view.dart @@ -80,10 +80,12 @@ class _LaterPageState extends State { ? SliverList( delegate: SliverChildBuilderDelegate((context, index) { + var videoItem = _laterController.laterList[index]; return VideoCardH( - videoItem: _laterController.laterList[index], - source: 'later', - ); + videoItem: videoItem, + source: 'later', + longPress: () => _laterController.toViewDel( + aid: videoItem.aid)); }, childCount: _laterController.laterList.length), ) : _laterController.isLoading.value From b485399517aa948074028618ead9f59cd73b3e34 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 30 Aug 2023 11:04:01 +0800 Subject: [PATCH 8/9] =?UTF-8?q?feat:=20=E5=BF=AB=E9=80=9F=E6=94=B6?= =?UTF-8?q?=E8=97=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/setting/extra_setting.dart | 6 +++ .../video/detail/introduction/controller.dart | 28 +++++++++- lib/pages/video/detail/introduction/view.dart | 51 +++++++++++++++---- .../introduction/widgets/action_row_item.dart | 8 +++ lib/utils/storage.dart | 1 + 5 files changed, 83 insertions(+), 11 deletions(-) diff --git a/lib/pages/setting/extra_setting.dart b/lib/pages/setting/extra_setting.dart index 4ed55f96..bb262b57 100644 --- a/lib/pages/setting/extra_setting.dart +++ b/lib/pages/setting/extra_setting.dart @@ -55,6 +55,12 @@ class _ExtraSettingState extends State { defaultVal: true, callFn: (val) => {SmartDialog.showToast('下次启动时生效')}, ), + const SetSwitchItem( + title: '快速收藏', + subTitle: '点按收藏至默认,长按选择文件夹', + setKey: SettingBoxKey.enableQuickFav, + defaultVal: false, + ), ListTile( dense: false, title: Text('评论展示', style: titleStyle), diff --git a/lib/pages/video/detail/introduction/controller.dart b/lib/pages/video/detail/introduction/controller.dart index ea63c14d..5a548607 100644 --- a/lib/pages/video/detail/introduction/controller.dart +++ b/lib/pages/video/detail/introduction/controller.dart @@ -144,6 +144,8 @@ class VideoIntroController extends GetxController { // 获取收藏状态 Future queryHasFavVideo() async { + /// fix 延迟查询 + await Future.delayed(const Duration(milliseconds: 200)); var result = await VideoHttp.hasFavVideo(aid: IdUtils.bv2av(bvid)); if (result['status']) { hasFav.value = result["data"]['favoured']; @@ -275,7 +277,27 @@ class VideoIntroController extends GetxController { } // (取消)收藏 - Future actionFavVideo() async { + Future actionFavVideo({type = 'choose'}) async { + // 收藏至默认文件夹 + if (type == 'default') { + await queryVideoInFolder(); + int defaultFolderId = favFolderData.value.list!.first.id!; + int favStatus = favFolderData.value.list!.first.favState!; + print('favStatus: $favStatus'); + var result = await VideoHttp.favVideo( + aid: IdUtils.bv2av(bvid), + addIds: favStatus == 0 ? '$defaultFolderId' : '', + delIds: favStatus == 1 ? '$defaultFolderId' : '', + ); + if (result['status']) { + if (result['data']['prompt']) { + // 重新获取收藏状态 + await queryHasFavVideo(); + SmartDialog.showToast('✅ 操作成功'); + } + } + return; + } try { for (var i in favFolderData.value.list!.toList()) { if (i.favState == 1) { @@ -288,17 +310,19 @@ class VideoIntroController extends GetxController { // ignore: avoid_print print(e); } + SmartDialog.showLoading(msg: '请求中'); var result = await VideoHttp.favVideo( aid: IdUtils.bv2av(bvid), addIds: addMediaIdsNew.join(','), delIds: delMediaIdsNew.join(',')); + SmartDialog.dismiss(); if (result['status']) { if (result['data']['prompt']) { addMediaIdsNew = []; delMediaIdsNew = []; Get.back(); // 重新获取收藏状态 - queryHasFavVideo(); + await queryHasFavVideo(); SmartDialog.showToast('✅ 操作成功'); } } diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index 75eef280..a0fa0b9e 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -122,6 +122,7 @@ class _VideoInfoState extends State with TickerProviderStateMixin { late final Map videoItem; Box localCache = GStrorage.localCache; + Box setting = GStrorage.setting; late double sheetHeight; late final bool loadingStatus; // 加载状态 @@ -150,19 +151,50 @@ class _VideoInfoState extends State with TickerProviderStateMixin { } // 收藏 - showFavBottomSheet() { + showFavBottomSheet({type = 'tap'}) { if (videoIntroController.userInfo == null) { SmartDialog.showToast('账号未登录'); return; } - showModalBottomSheet( - context: context, - useRootNavigator: true, - isScrollControlled: true, - builder: (context) { - return FavPanel(ctr: videoIntroController); - }, - ); + bool enableDragQuickFav = + setting.get(SettingBoxKey.enableQuickFav, defaultValue: false); + // 快速收藏 & + // 点按 收藏至默认文件夹 + // 长按选择文件夹 + if (enableDragQuickFav) { + if (type == 'tap') { + if (!videoIntroController.hasFav.value) { + videoIntroController.actionFavVideo(type: 'default'); + } else { + showModalBottomSheet( + context: context, + useRootNavigator: true, + isScrollControlled: true, + builder: (context) { + return FavPanel(ctr: videoIntroController); + }, + ); + } + } else { + showModalBottomSheet( + context: context, + useRootNavigator: true, + isScrollControlled: true, + builder: (context) { + return FavPanel(ctr: videoIntroController); + }, + ); + } + } else if (type != 'longPress') { + showModalBottomSheet( + context: context, + useRootNavigator: true, + isScrollControlled: true, + builder: (context) { + return FavPanel(ctr: videoIntroController); + }, + ); + } } // 视频介绍 @@ -510,6 +542,7 @@ class _VideoInfoState extends State with TickerProviderStateMixin { () => ActionRowItem( icon: const Icon(FontAwesomeIcons.heart), onTap: () => showFavBottomSheet(), + onLongPress: () => showFavBottomSheet(type: 'longPress'), selectStatus: videoIntroController.hasFav.value, loadingStatus: loadingStatus, text: !loadingStatus diff --git a/lib/pages/video/detail/introduction/widgets/action_row_item.dart b/lib/pages/video/detail/introduction/widgets/action_row_item.dart index 3aabe337..890a3a97 100644 --- a/lib/pages/video/detail/introduction/widgets/action_row_item.dart +++ b/lib/pages/video/detail/introduction/widgets/action_row_item.dart @@ -8,6 +8,7 @@ class ActionRowItem extends StatelessWidget { final bool? loadingStatus; final String? text; final bool selectStatus; + final Function? onLongPress; const ActionRowItem({ Key? key, @@ -17,6 +18,7 @@ class ActionRowItem extends StatelessWidget { this.loadingStatus, this.text, this.selectStatus = false, + this.onLongPress, }) : super(key: key); @override @@ -32,6 +34,12 @@ class ActionRowItem extends StatelessWidget { feedBack(), onTap!(), }, + onLongPress: () { + feedBack(); + if (onLongPress != null) { + onLongPress!(); + } + }, child: Padding( padding: const EdgeInsets.fromLTRB(15, 7, 15, 7), child: Row( diff --git a/lib/utils/storage.dart b/lib/utils/storage.dart index 91094a39..8c3b981c 100644 --- a/lib/utils/storage.dart +++ b/lib/utils/storage.dart @@ -108,6 +108,7 @@ class SettingBoxKey { static const String replySortType = 'replySortType'; static const String defaultDynamicType = 'defaultDynamicType'; static const String enableHotKey = 'enableHotKey'; + static const String enableQuickFav = 'enableQuickFav'; /// 外观 static const String themeMode = 'themeMode'; From f6c7143d2d5b0eb345f046695c418e65b6d23cdd Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 30 Aug 2023 11:45:28 +0800 Subject: [PATCH 9/9] =?UTF-8?q?mod:=20=E5=8F=8C=E5=87=BB=E5=BF=AB=E8=BF=9B?= =?UTF-8?q?/=E5=BF=AB=E9=80=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/setting/play_setting.dart | 26 ++++++++++------ lib/plugin/pl_player/view.dart | 47 +++++++++++++++++++++++------ lib/utils/storage.dart | 2 ++ 3 files changed, 56 insertions(+), 19 deletions(-) diff --git a/lib/pages/setting/play_setting.dart b/lib/pages/setting/play_setting.dart index 6d74c5b3..f093a740 100644 --- a/lib/pages/setting/play_setting.dart +++ b/lib/pages/setting/play_setting.dart @@ -60,6 +60,18 @@ class _PlaySettingState extends State { setKey: SettingBoxKey.autoPlayEnable, defaultVal: true, ), + const SetSwitchItem( + title: '自动全屏', + subTitle: '视频开始播放时进入全屏', + setKey: SettingBoxKey.enableAutoEnter, + defaultVal: false, + ), + const SetSwitchItem( + title: '自动退出', + subTitle: '视频结束播放时退出全屏', + setKey: SettingBoxKey.enableAutoExit, + defaultVal: false, + ), const SetSwitchItem( title: '开启硬解', subTitle: '以较低功耗播放视频', @@ -79,16 +91,10 @@ class _PlaySettingState extends State { defaultVal: false, ), const SetSwitchItem( - title: '自动全屏', - subTitle: '视频开始播放时进入全屏', - setKey: SettingBoxKey.enableAutoEnter, - defaultVal: false, - ), - const SetSwitchItem( - title: '自动退出', - subTitle: '视频结束播放时退出全屏', - setKey: SettingBoxKey.enableAutoExit, - defaultVal: false, + title: '双击快退/快进', + subTitle: '左侧双击快退,右侧双击快进', + setKey: SettingBoxKey.enableQuickDouble, + defaultVal: true, ), ListTile( dense: false, diff --git a/lib/plugin/pl_player/view.dart b/lib/plugin/pl_player/view.dart index e6ba2e05..35a277b4 100644 --- a/lib/plugin/pl_player/view.dart +++ b/lib/plugin/pl_player/view.dart @@ -69,6 +69,7 @@ class _PLVideoPlayerState extends State Box setting = GStrorage.setting; late FullScreenMode mode; late int defaultBtmProgressBehavior; + late bool enableQuickDouble; void onDoubleTapSeekBackward() { setState(() { @@ -82,6 +83,36 @@ class _PLVideoPlayerState extends State }); } + // 双击播放、暂停 + void onDoubleTapCenter() { + final _ = widget.controller; + if (_.playerStatus.status.value == PlayerStatus.playing) { + _.togglePlay(); + } else { + _.play(); + } + } + + doubleTapFuc(String type) { + if (!enableQuickDouble) { + onDoubleTapCenter(); + return; + } + switch (type) { + case 'left': + // 双击左边区域 👈 + onDoubleTapSeekBackward(); + break; + case 'center': + onDoubleTapCenter(); + break; + case 'right': + // 双击右边区域 👈 + onDoubleTapSeekForward(); + break; + } + } + @override void initState() { super.initState(); @@ -91,6 +122,8 @@ class _PLVideoPlayerState extends State widget.controller.headerControl = widget.headerControl; defaultBtmProgressBehavior = setting.get(SettingBoxKey.btmProgressBehavior, defaultValue: BtmProgresBehavior.values.first.code); + enableQuickDouble = + setting.get(SettingBoxKey.enableQuickDouble, defaultValue: true); Future.microtask(() async { try { @@ -429,19 +462,15 @@ class _PLVideoPlayerState extends State final totalWidth = MediaQuery.of(context).size.width; final tapPosition = details.localPosition.dx; final sectionWidth = totalWidth / 3; + String type = 'left'; if (tapPosition < sectionWidth) { - // 双击左边区域 👈 - onDoubleTapSeekBackward(); + type = 'left'; } else if (tapPosition < sectionWidth * 2) { - if (_.playerStatus.status.value == PlayerStatus.playing) { - _.togglePlay(); - } else { - _.play(); - } + type = 'center'; } else { - // 双击右边区域 👈 - onDoubleTapSeekForward(); + type = 'right'; } + doubleTapFuc(type); }, onLongPressStart: (detail) { feedBack(); diff --git a/lib/utils/storage.dart b/lib/utils/storage.dart index 8c3b981c..de7ffd04 100644 --- a/lib/utils/storage.dart +++ b/lib/utils/storage.dart @@ -99,6 +99,8 @@ class SettingBoxKey { static const String enableAutoBrightness = 'enableAutoBrightness'; static const String enableAutoEnter = 'enableAutoEnter'; static const String enableAutoExit = 'enableAutoExit'; + // youtube 双击快进快退 + static const String enableQuickDouble = 'enableQuickDouble'; /// 隐私 static const String blackMidsList = 'blackMidsList';