diff --git a/android/app/src/main/kotlin/com/orz12/PiliPalaX/MainActivity.kt b/android/app/src/main/kotlin/com/orz12/PiliPalaX/MainActivity.kt index 97964c7c1..c713ac171 100644 --- a/android/app/src/main/kotlin/com/orz12/PiliPalaX/MainActivity.kt +++ b/android/app/src/main/kotlin/com/orz12/PiliPalaX/MainActivity.kt @@ -2,19 +2,19 @@ package com.orz12.PiliPalaX import io.flutter.embedding.android.FlutterActivity import io.flutter.embedding.engine.FlutterEngine -import io.flutter.plugin.common.MethodChannel +//import io.flutter.plugin.common.MethodChannel import com.ryanheise.audioservice.AudioServiceActivity import android.os.Build import android.os.Bundle import android.view.WindowManager.LayoutParams class MainActivity : AudioServiceActivity() { - private lateinit var methodChannel: MethodChannel +// private lateinit var methodChannel: MethodChannel - override fun configureFlutterEngine(flutterEngine: FlutterEngine) { - super.configureFlutterEngine(flutterEngine) - methodChannel = MethodChannel(flutterEngine!!.getDartExecutor()!!.getBinaryMessenger(), CHANNEL) - } +// override fun configureFlutterEngine(flutterEngine: FlutterEngine) { +// super.configureFlutterEngine(flutterEngine) +// methodChannel = MethodChannel(flutterEngine!!.getDartExecutor()!!.getBinaryMessenger(), CHANNEL) +// } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -24,12 +24,12 @@ class MainActivity : AudioServiceActivity() { } } - override fun onUserLeaveHint() { - super.onUserLeaveHint() - methodChannel.invokeMethod("onUserLeaveHint", null) - } +// override fun onUserLeaveHint() { +// super.onUserLeaveHint() +// methodChannel.invokeMethod("onUserLeaveHint", null) +// } - companion object { - private const val CHANNEL = "onUserLeaveHint" - } +// companion object { +// private const val CHANNEL = "onUserLeaveHint" +// } } diff --git a/lib/pages/bangumi/introduction/controller.dart b/lib/pages/bangumi/introduction/controller.dart index 5b2793cec..b40870702 100644 --- a/lib/pages/bangumi/introduction/controller.dart +++ b/lib/pages/bangumi/introduction/controller.dart @@ -329,7 +329,7 @@ class BangumiIntroController extends GetxController { int currentIndex = episodes.indexWhere((e) => e.cid == videoDetailCtr.cid.value); int prevIndex = currentIndex - 1; - PlayRepeat platRepeat = videoDetailCtr.plPlayerController.playRepeat; + PlayRepeat platRepeat = videoDetailCtr.plPlayerController!.playRepeat; if (prevIndex < 0) { if (platRepeat == PlayRepeat.listCycle) { prevIndex = episodes.length - 1; @@ -349,7 +349,7 @@ class BangumiIntroController extends GetxController { late List episodes; VideoDetailController videoDetailCtr = Get.find(tag: Get.arguments['heroTag']); - PlayRepeat platRepeat = videoDetailCtr.plPlayerController.playRepeat; + PlayRepeat platRepeat = videoDetailCtr.plPlayerController!.playRepeat; if (bangumiDetail.value.episodes != null) { episodes = bangumiDetail.value.episodes!; diff --git a/lib/pages/danmaku/view.dart b/lib/pages/danmaku/view.dart index b45bfe4fa..1b72fa915 100644 --- a/lib/pages/danmaku/view.dart +++ b/lib/pages/danmaku/view.dart @@ -77,11 +77,10 @@ class _PlDanmakuState extends State { // 播放器状态监听 void playerListener(PlayerStatus? status) { - if (status == PlayerStatus.paused) { - _controller?.pause(); - } if (status == PlayerStatus.playing) { _controller?.onResume(); + } else { + _controller?.pause(); } } diff --git a/lib/pages/live_room/view.dart b/lib/pages/live_room/view.dart index 2c3b91197..82e7493ad 100644 --- a/lib/pages/live_room/view.dart +++ b/lib/pages/live_room/view.dart @@ -44,7 +44,7 @@ class _LiveRoomPageState extends State { @override void dispose() { - floating?.dispose(); + // floating?.dispose(); plPlayerController!.dispose(); super.dispose(); } diff --git a/lib/pages/live_room/widgets/bottom_control.dart b/lib/pages/live_room/widgets/bottom_control.dart index 50459ec4e..72cbacb5f 100644 --- a/lib/pages/live_room/widgets/bottom_control.dart +++ b/lib/pages/live_room/widgets/bottom_control.dart @@ -102,7 +102,7 @@ class _BottomControlState extends State { canUsePiP = false; } if (canUsePiP) { - await widget.floating!.enable(const EnableManual()); + await widget.floating!.enable(const ImmediatePiP()); } else {} }, icon: const Icon( diff --git a/lib/pages/video/detail/controller.dart b/lib/pages/video/detail/controller.dart index 2d1ff29e9..1643ba150 100644 --- a/lib/pages/video/detail/controller.dart +++ b/lib/pages/video/detail/controller.dart @@ -70,7 +70,7 @@ class VideoDetailController extends GetxController ReplyItemModel? firstFloor; final scaffoldKey = GlobalKey(); RxString bgCover = ''.obs; - PlPlayerController plPlayerController = PlPlayerController.getInstance(); + PlPlayerController? plPlayerController; late VideoItem firstVideo; late AudioItem firstAudio; @@ -84,7 +84,7 @@ class VideoDetailController extends GetxController var userInfo; late bool isFirstTime = true; Floating? floating; - late PreferredSizeWidget headerControl; + PreferredSizeWidget? headerControl; // late bool enableCDN; late int? cacheVideoQa; @@ -95,7 +95,7 @@ class VideoDetailController extends GetxController PersistentBottomSheetController? replyReplyBottomSheetCtr; @override - void onInit() { + void onInit() async { super.onInit(); final Map argMap = Get.arguments; userInfo = userInfoCache.get('userInfoCache'); @@ -108,7 +108,9 @@ class VideoDetailController extends GetxController } } if (keys.contains('pic')) { - videoItem['pic'] = argMap['pic']; + if (argMap['pic'] != null && argMap['pic'] != '') { + videoItem['pic'] = argMap['pic']; + } } } bool defaultShowComment = @@ -117,7 +119,28 @@ class VideoDetailController extends GetxController length: 2, vsync: this, initialIndex: defaultShowComment ? 1 : 0); autoPlay.value = setting.get(SettingBoxKey.autoPlayEnable, defaultValue: true); - if (autoPlay.value) isShowCover.value = false; + if (autoPlay.value) { + isShowCover.value = false; + plPlayerController = PlPlayerController.getInstance(); + headerControl = HeaderControl( + controller: plPlayerController, + videoDetailCtr: this, + floating: floating, + heroTag: heroTag, + ); + } + if (videoItem['pic']?.isEmpty != false) { + VideoHttp.videoIntro(bvid: bvid).then( + (value) { + if (value['status']) { + videoItem['pic'] = value['data'].pic; + isShowCover.refresh(); + } else { + SmartDialog.showToast("视频封面获取失败:${value['msg']}"); + } + }, + ); + } enableHA.value = setting.get(SettingBoxKey.enableHA, defaultValue: true); hwdec.value = setting.get(SettingBoxKey.hardwareDecoding, defaultValue: Platform.isAndroid ? 'auto-safe' : 'auto'); @@ -131,12 +154,6 @@ class VideoDetailController extends GetxController if (Platform.isAndroid) { floating = Floating(); } - headerControl = HeaderControl( - controller: plPlayerController, - videoDetailCtr: this, - floating: floating, - heroTag: heroTag, - ); // CDN优化 // enableCDN = setting.get(SettingBoxKey.enableCDN, defaultValue: true); @@ -150,7 +167,12 @@ class VideoDetailController extends GetxController defaultValue: VideoDecodeFormats.values[1].code); cacheAudioQa = setting.get(SettingBoxKey.defaultAudioQa, defaultValue: AudioQuality.hiRes.code); - oid.value = IdUtils.bv2av(Get.parameters['bvid']!); + if (Get.parameters['bvid'] != null && Get.parameters['bvid']!.isNotEmpty) { + oid.value = IdUtils.bv2av(Get.parameters['bvid']!); + } else { + SmartDialog.showToast('视频信息获取失败,可能为充电视频等特殊情况'); + oid.value = 0; + } } showReplyReplyPanel() { @@ -178,11 +200,12 @@ class VideoDetailController extends GetxController /// 更新画质、音质 /// TODO 继续进度播放 updatePlayer() { + if (plPlayerController == null) return; isShowCover.value = false; - defaultST = plPlayerController.position.value; - plPlayerController.removeListeners(); - plPlayerController.isBuffering.value = false; - plPlayerController.buffered.value = Duration.zero; + defaultST = plPlayerController!.position.value; + plPlayerController!.removeListeners(); + plPlayerController!.isBuffering.value = false; + plPlayerController!.buffered.value = Duration.zero; /// 根据currentVideoQa和currentDecodeFormats 重新设置videoUrl List videoList = @@ -266,11 +289,15 @@ class VideoDetailController extends GetxController /// 设置/恢复 屏幕亮度 if (brightness != null) { ScreenBrightness().setScreenBrightness(brightness!); - } else if (setting.get(SettingBoxKey.enableAutoBrightness, - defaultValue: false) as bool) { - ScreenBrightness().resetScreenBrightness(); } - await plPlayerController.setDataSource( + plPlayerController ??= PlPlayerController.getInstance(); + headerControl ??= HeaderControl( + controller: plPlayerController, + videoDetailCtr: this, + floating: floating, + heroTag: heroTag, + ); + await plPlayerController!.setDataSource( DataSource( videoSource: video ?? videoUrl, audioSource: audio ?? audioUrl, @@ -301,7 +328,7 @@ class VideoDetailController extends GetxController ); /// 开启自动全屏时,在player初始化完成后立即传入headerControl - plPlayerController.headerControl = headerControl; + plPlayerController!.headerControl = headerControl; } // 视频链接 @@ -310,9 +337,10 @@ class VideoDetailController extends GetxController if (result['status']) { data = result['data']; if (data.acceptDesc!.isNotEmpty && data.acceptDesc!.contains('试看')) { - SmartDialog.showToast( - '该视频为专属视频,仅提供试看', + SmartDialog.showNotify( + msg: '该视频为专属视频,仅提供试看', displayTime: const Duration(seconds: 3), + notifyType: NotifyType.warning, ); } if (data.dash == null && data.durl != null) { @@ -334,7 +362,11 @@ class VideoDetailController extends GetxController return result; } if (data.dash == null) { - SmartDialog.showToast('视频资源不存在'); + SmartDialog.showNotify( + msg: '视频资源不存在', + displayTime: const Duration(seconds: 3), + notifyType: NotifyType.error, + ); isShowCover.value = false; return result; } @@ -438,12 +470,20 @@ class VideoDetailController extends GetxController } else { if (result['code'] == -404) { isShowCover.value = false; - SmartDialog.showToast('视频不存在或已被删除'); + SmartDialog.showNotify( + msg: '视频不存在或已被删除', + displayTime: const Duration(seconds: 3), + notifyType: NotifyType.error, + ); } if (result['code'] == 87008) { SmartDialog.showToast("当前视频可能是专属视频,可能需包月充电观看(${result['msg']})"); } else { - SmartDialog.showToast("错误(${result['code']}):${result['msg']}"); + SmartDialog.showNotify( + msg: '错误(${result['code']}):${result['msg']}', + displayTime: const Duration(seconds: 3), + notifyType: NotifyType.warning, + ); } } return result; diff --git a/lib/pages/video/detail/introduction/controller.dart b/lib/pages/video/detail/introduction/controller.dart index eeba42611..81190da31 100644 --- a/lib/pages/video/detail/introduction/controller.dart +++ b/lib/pages/video/detail/introduction/controller.dart @@ -525,7 +525,7 @@ class VideoIntroController extends GetxController { int prevIndex = currentIndex - 1; final VideoDetailController videoDetailCtr = Get.find(tag: heroTag); - final PlayRepeat platRepeat = videoDetailCtr.plPlayerController.playRepeat; + final PlayRepeat platRepeat = videoDetailCtr.plPlayerController!.playRepeat; // 列表循环 if (prevIndex < 0) { @@ -558,12 +558,17 @@ class VideoIntroController extends GetxController { final List pages = videoDetail.value.pages!; episodes.addAll(pages); } - final VideoDetailController videoDetailCtr = - Get.find(tag: heroTag); - final PlayRepeat platRepeat = videoDetailCtr.plPlayerController.playRepeat; + late VideoDetailController videoDetailCtr; + PlayRepeat playRepeat = PlayRepeat.listCycle; + try { + videoDetailCtr = Get.find(tag: heroTag); + playRepeat = videoDetailCtr.plPlayerController!.playRepeat; + } catch (_) { + return false; + } if (episodes.isEmpty) { - if (platRepeat == PlayRepeat.autoPlayRelated) { + if (playRepeat == PlayRepeat.autoPlayRelated) { return playRelated(); } return false; @@ -575,9 +580,9 @@ class VideoIntroController extends GetxController { // 列表循环 if (nextIndex >= episodes.length) { - if (platRepeat == PlayRepeat.listCycle) { + if (playRepeat == PlayRepeat.listCycle) { nextIndex = 0; - } else if (platRepeat == PlayRepeat.autoPlayRelated) { + } else if (playRepeat == PlayRepeat.autoPlayRelated) { return playRelated(); } else { return false; diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index 5e029d823..15464f66e 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -673,7 +673,7 @@ InlineSpan buildContent( SmartDialog.showToast('跳转至:$matchStr'); Get.find( tag: Get.arguments['heroTag']) - .plPlayerController + .plPlayerController! .seekTo(Duration(seconds: Utils.duration(matchStr)), type: 'slider'); } catch (e) { diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index bc41ef119..15c29ee49 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -67,7 +67,7 @@ class _VideoDetailPageState extends State bool isShowing = true; RxBool isFullScreen = false.obs; late StreamSubscription fullScreenStatusListener; - late final MethodChannel onUserLeaveHintListener; + // late final MethodChannel onUserLeaveHintListener; // StreamSubscription? _bufferedListener; @override @@ -78,22 +78,22 @@ class _VideoDetailPageState extends State } videoDetailController = Get.put(VideoDetailController(), tag: heroTag); videoIntroController = Get.put(VideoIntroController(), tag: heroTag); - videoIntroController.videoDetail.listen((value) { - if (!context.mounted) return; - videoPlayerServiceHandler.onVideoDetailChange( - value, videoDetailController.cid.value); - }); + // videoIntroController.videoDetail.listen((value) { + // if (!context.mounted) return; + // videoPlayerServiceHandler.onVideoDetailChange( + // value, videoDetailController.cid.value); + // }); bangumiIntroController = Get.put(BangumiIntroController(), tag: heroTag); - bangumiIntroController.bangumiDetail.listen((value) { - if (!context.mounted) return; - videoPlayerServiceHandler.onVideoDetailChange( - value, videoDetailController.cid.value); - }); - videoDetailController.cid.listen((p0) { - if (!context.mounted) return; - videoPlayerServiceHandler.onVideoDetailChange( - bangumiIntroController.bangumiDetail.value, p0); - }); + // bangumiIntroController.bangumiDetail.listen((value) { + // if (!context.mounted) return; + // videoPlayerServiceHandler.onVideoDetailChange( + // value, videoDetailController.cid.value); + // }); + // videoDetailController.cid.listen((p0) { + // if (!context.mounted) return; + // videoPlayerServiceHandler.onVideoDetailChange( + // bangumiIntroController.bangumiDetail.value, p0); + // }); autoExitFullscreen = setting.get(SettingBoxKey.enableAutoExit, defaultValue: true); horizontalScreen = @@ -114,16 +114,16 @@ class _VideoDetailPageState extends State appbarStreamListen(); // lifecycleListener(); autoScreen(); - onUserLeaveHintListener = const MethodChannel("onUserLeaveHint"); - onUserLeaveHintListener.setMethodCallHandler((call) async { - if (call.method == 'onUserLeaveHint') { - if (autoPiP && - plPlayerController != null && - playerStatus == PlayerStatus.playing) { - autoEnterPip(); - } - } - }); + // onUserLeaveHintListener = const MethodChannel("onUserLeaveHint"); + // onUserLeaveHintListener.setMethodCallHandler((call) async { + // if (call.method == 'onUserLeaveHint') { + // if (autoPiP && + // plPlayerController != null && + // playerStatus == PlayerStatus.playing) { + // autoEnterPip(); + // } + // } + // }); // _animationController = AnimationController( // vsync: this, // duration: const Duration(milliseconds: 300), @@ -147,6 +147,7 @@ class _VideoDetailPageState extends State plPlayerController!.addStatusLister(playerListener); listenFullScreenStatus(); await plPlayerController!.autoEnterFullscreen(); + // autoEnterPip(); } } @@ -208,6 +209,7 @@ class _VideoDetailPageState extends State listenFullScreenStatus(); await plPlayerController!.autoEnterFullscreen(); videoDetailController.autoPlay.value = true; + // autoEnterPip(); } // // 生命周期监听 @@ -255,8 +257,8 @@ class _VideoDetailPageState extends State @override void dispose() { - floating.dispose(); - videoDetailController.floating?.dispose(); + // floating.dispose(); + // videoDetailController.floating?.dispose(); videoIntroController.videoDetail.close(); bangumiIntroController.bangumiDetail.close(); videoDetailController.cid.close(); @@ -268,9 +270,10 @@ class _VideoDetailPageState extends State if (plPlayerController != null) { plPlayerController!.removeStatusLister(playerListener); fullScreenStatusListener.cancel(); + plPlayerController!.disable(); plPlayerController!.dispose(); } - videoPlayerServiceHandler.onVideoDetailDispose(); + // videoPlayerServiceHandler.onVideoDetailDispose(); VideoDetailPage.routeObserver.unsubscribe(this); // _lifecycleListener.dispose(); showStatusBar(); @@ -284,16 +287,12 @@ class _VideoDetailPageState extends State // _bufferedListener?.cancel(); /// 开启 - if (setting.get(SettingBoxKey.enableAutoBrightness, defaultValue: false) - as bool) { - videoDetailController.brightness = plPlayerController!.brightness.value; - } if (plPlayerController != null) { videoDetailController.defaultST = plPlayerController!.position.value; videoIntroController.isPaused = true; plPlayerController!.removeStatusLister(playerListener); fullScreenStatusListener.cancel(); - plPlayerController!.pause(); + plPlayerController!.disable(); } isShowing = false; if (mounted) { @@ -311,13 +310,15 @@ class _VideoDetailPageState extends State } super.didPopNext(); videoDetailController.isFirstTime = false; - final bool autoplay = autoPlayEnable; + // final bool autoplay = autoPlayEnable; videoDetailController.autoPlay.value = !videoDetailController.isShowCover.value; - await videoDetailController.playerInit(autoplay: autoplay); + print("autoplay:${videoDetailController.autoPlay.value}"); + await videoDetailController.playerInit( + autoplay: videoDetailController.autoPlay.value); /// 未开启自动播放时,未播放跳转下一页返回/播放后跳转下一页返回 - videoIntroController.isPaused = false; + videoIntroController.isPaused = videoDetailController.autoPlay.value; // if (autoplay) { // // await Future.delayed(const Duration(milliseconds: 300)); // print(plPlayerController); @@ -367,11 +368,13 @@ class _VideoDetailPageState extends State final String routePath = Get.currentRoute; if (autoPiP && routePath.startsWith('/video')) { - floating.enable(EnableManual( - aspectRatio: Rational( - videoDetailController.data.dash!.video!.first.width!, - videoDetailController.data.dash!.video!.first.height!, - ), + floating.enable(OnLeavePiP( + aspectRatio: plPlayerController != null + ? Rational( + videoDetailController.data.dash!.video!.first.width!, + videoDetailController.data.dash!.video!.first.height!, + ) + : const Rational.landscape(), sourceRectHint: Rectangle( 0, 0, @@ -379,382 +382,374 @@ class _VideoDetailPageState extends State context.height.toInt(), ), )); + print("enabled"); } } - @override - Widget build(BuildContext context) { - Widget plPlayer = FutureBuilder( - future: _futureBuilderFuture, - builder: (BuildContext context, AsyncSnapshot snapshot) { - if (snapshot.hasData && snapshot.data['status']) { - return Obx( - () => !videoDetailController.autoPlay.value || - plPlayerController == null || - plPlayerController!.videoController == null - ? nil - : PLVideoPlayer( - controller: plPlayerController!, - videoIntroController: - videoDetailController.videoType == SearchType.video - ? videoIntroController - : null, - bangumiIntroController: videoDetailController.videoType == - SearchType.media_bangumi - ? bangumiIntroController - : null, - headerControl: videoDetailController.headerControl, - danmuWidget: Obx( - () => PlDanmaku( - key: Key(videoDetailController.danmakuCid.value - .toString()), - cid: videoDetailController.danmakuCid.value, - playerController: plPlayerController!, - ), + Widget get plPlayer => FutureBuilder( + future: _futureBuilderFuture, + builder: (BuildContext context, AsyncSnapshot snapshot) { + if (snapshot.hasData && snapshot.data['status']) { + return Obx( + () => !videoDetailController.autoPlay.value || + plPlayerController == null || + plPlayerController!.videoController == null + ? nil + : PLVideoPlayer( + controller: plPlayerController!, + videoIntroController: + videoDetailController.videoType == SearchType.video + ? videoIntroController + : null, + bangumiIntroController: videoDetailController.videoType == + SearchType.media_bangumi + ? bangumiIntroController + : null, + headerControl: videoDetailController.headerControl, + danmuWidget: Obx( + () => PlDanmaku( + key: Key( + videoDetailController.danmakuCid.value.toString()), + cid: videoDetailController.danmakuCid.value, + playerController: plPlayerController!, ), ), - ); - } else { - return const SizedBox(); - } - }); - Widget manualPlayerWidget = Obx( - () => Visibility( - visible: videoDetailController.isShowCover.value && - videoDetailController.isEffective.value, - child: Stack( - children: [ - Positioned( - top: 0, - left: 0, - right: 0, - child: AppBar( - primary: false, - foregroundColor: Colors.white, - elevation: 0, - scrolledUnderElevation: 0, - 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: IconButton( - tooltip: '播放', - onPressed: handlePlay, - icon: Image.asset( - 'assets/images/play.png', - width: 60, - height: 60, - )), + ), + ); + } else { + return const SizedBox(); + } + }); + + Widget get manualPlayerWidget => Obx(() => Visibility( + visible: videoDetailController.isShowCover.value && + videoDetailController.isEffective.value, + child: Stack(children: [ + Positioned( + top: 0, + left: 0, + right: 0, + child: AppBar( + primary: false, + foregroundColor: Colors.white, + elevation: 0, + scrolledUnderElevation: 0, + 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) ], - )), - ); - Widget childWhenDisabled = SafeArea( - top: !removeSafeArea && - MediaQuery.of(context).orientation == Orientation.portrait && - isFullScreen.value == true, - bottom: !removeSafeArea && - MediaQuery.of(context).orientation == Orientation.portrait && - isFullScreen.value == true, - left: false, //isFullScreen != true, - right: false, //isFullScreen != true, - child: Stack( - children: [ - Scaffold( - resizeToAvoidBottomInset: false, - key: videoDetailController.scaffoldKey, - // backgroundColor: Colors.black, - appBar: removeSafeArea - ? null - : AppBar( - backgroundColor: - showStatusBarBackgroundColor ? null : Colors.black, - elevation: 0, - toolbarHeight: 0, - systemOverlayStyle: SystemUiOverlayStyle( - statusBarIconBrightness: - Theme.of(context).brightness == Brightness.dark || - !showStatusBarBackgroundColor - ? Brightness.light - : Brightness.dark, - systemNavigationBarColor: Colors.transparent), - ), - body: Column( - children: [ - Obx( - () { - double videoHeight = context.width * 9 / 16; - final double videoWidth = context.width; - // print(videoDetailController.tabCtr.index); - if (enableVerticalExpand && - plPlayerController?.direction.value == 'vertical') { - videoHeight = context.width; - } - if (MediaQuery.of(context).orientation == - Orientation.landscape && - !horizontalScreen && - !isFullScreen.value && - isShowing && - mounted) { - hideStatusBar(); - } - if (MediaQuery.of(context).orientation == - Orientation.portrait && - !isFullScreen.value && - isShowing && - mounted) { - if (!removeSafeArea) showStatusBar(); - } - return Container( - color: - showStatusBarBackgroundColor ? null : Colors.black, - height: MediaQuery.of(context).orientation == - Orientation.landscape || - isFullScreen.value == true - ? MediaQuery.sizeOf(context).height - - (MediaQuery.of(context).orientation == - Orientation.landscape || - removeSafeArea - ? 0 - : MediaQuery.of(context).padding.top) - : videoHeight, - width: context.width, - child: PopScope( - canPop: isFullScreen.value != true && - (horizontalScreen || - MediaQuery.of(context).orientation == - Orientation.portrait), - onPopInvokedWithResult: (bool didPop, Object? result) { - if (isFullScreen.value == true) { - plPlayerController! - .triggerFullScreen(status: false); - } - if (MediaQuery.of(context).orientation == - Orientation.landscape && - !horizontalScreen) { - verticalScreenForTwoSeconds(); - } - }, - child: Stack( - children: [ - if (isShowing) plPlayer, + ), + ), + Positioned( + right: 12, + bottom: 10, + child: IconButton( + tooltip: '播放', + onPressed: handlePlay, + icon: Image.asset( + 'assets/images/play.png', + width: 60, + height: 60, + )), + ), + ]))); - /// 关闭自动播放时 手动播放 - if (!videoDetailController - .autoPlay.value) ...[ - Obx( - () => Visibility( - visible: videoDetailController - .isShowCover.value, - child: Positioned( - top: 0, - left: 0, - right: 0, - child: GestureDetector( - onTap: handlePlay, - child: NetworkImgLayer( - type: 'emote', - src: videoDetailController - .videoItem['pic'], - width: videoWidth, - height: videoHeight, - ), - ), - ), - ), - ), - manualPlayerWidget, - ] - ], - )), - ); - }, + Widget get childWhenDisabled => SafeArea( + top: !removeSafeArea && + MediaQuery.of(context).orientation == Orientation.portrait && + isFullScreen.value == true, + bottom: !removeSafeArea && + MediaQuery.of(context).orientation == Orientation.portrait && + isFullScreen.value == true, + left: false, //isFullScreen != true, + right: false, //isFullScreen != true, + child: Scaffold( + resizeToAvoidBottomInset: false, + key: videoDetailController.scaffoldKey, + // backgroundColor: Colors.black, + appBar: removeSafeArea + ? null + : AppBar( + backgroundColor: + showStatusBarBackgroundColor ? null : Colors.black, + elevation: 0, + toolbarHeight: 0, + systemOverlayStyle: SystemUiOverlayStyle( + statusBarIconBrightness: + Theme.of(context).brightness == Brightness.dark || + !showStatusBarBackgroundColor + ? Brightness.light + : Brightness.dark, + systemNavigationBarColor: Colors.transparent, ), - Expanded( - child: ColoredBox( - key: Key(heroTag), - color: Theme.of(context).colorScheme.background, - child: Column( - children: [ - // Opacity( - // opacity: 0, - // child: SizedBox( - // width: context.width, - // height: 0, - // child: Obx( - // () => TabBar( - // controller: videoDetailController.tabCtr, - // dividerColor: Colors.transparent, - // indicatorColor: - // Theme.of(context).colorScheme.background, - // tabs: videoDetailController.tabs - // .map((String name) => Tab(text: name)) - // .toList(), - // ), - // ), - // ), - // ), - Expanded( - child: TabBarView( - physics: const BouncingScrollPhysics(), - controller: videoDetailController.tabCtr, - children: [ - CustomScrollView( - key: const PageStorageKey('简介'), - slivers: [ - if (videoDetailController.videoType == - SearchType.video) ...[ - VideoIntroPanel(heroTag: heroTag), - ] else if (videoDetailController - .videoType == - SearchType.media_bangumi) ...[ - Obx(() => BangumiIntroPanel( - heroTag: heroTag, - cid: - videoDetailController.cid.value)), - ], - SliverToBoxAdapter( - child: Divider( - indent: 12, - endIndent: 12, - color: Theme.of(context) - .dividerColor - .withOpacity(0.06), + ), + body: Column( + children: [ + Obx( + () { + double videoHeight = context.width * 9 / 16; + final double videoWidth = context.width; + // print(videoDetailController.tabCtr.index); + if (enableVerticalExpand && + plPlayerController?.direction.value == 'vertical') { + videoHeight = context.width; + } + if (MediaQuery.of(context).orientation == + Orientation.landscape && + !horizontalScreen && + !isFullScreen.value && + isShowing && + mounted) { + hideStatusBar(); + } + if (MediaQuery.of(context).orientation == + Orientation.portrait && + !isFullScreen.value && + isShowing && + mounted) { + if (!removeSafeArea) showStatusBar(); + } + return Container( + color: showStatusBarBackgroundColor ? null : Colors.black, + height: MediaQuery.of(context).orientation == + Orientation.landscape || + isFullScreen.value == true + ? MediaQuery.sizeOf(context).height - + (MediaQuery.of(context).orientation == + Orientation.landscape || + removeSafeArea + ? 0 + : MediaQuery.of(context).padding.top) + : videoHeight, + width: context.width, + child: PopScope( + canPop: isFullScreen.value != true && + (horizontalScreen || + MediaQuery.of(context).orientation == + Orientation.portrait), + onPopInvokedWithResult: (bool didPop, Object? result) { + if (isFullScreen.value == true) { + plPlayerController! + .triggerFullScreen(status: false); + } + if (MediaQuery.of(context).orientation == + Orientation.landscape && + !horizontalScreen) { + verticalScreenForTwoSeconds(); + } + }, + child: Stack( + children: [ + if (isShowing) plPlayer, + + /// 关闭自动播放时 手动播放 + if (!videoDetailController + .autoPlay.value) ...[ + Obx( + () => Visibility( + visible: + videoDetailController.isShowCover.value, + child: Positioned( + top: 0, + left: 0, + right: 0, + child: GestureDetector( + onTap: handlePlay, + child: NetworkImgLayer( + type: 'emote', + src: videoDetailController + .videoItem['pic'], + width: videoWidth, + height: videoHeight, ), ), - RelatedVideoPanel(heroTag: heroTag), - ], + ), ), - Obx( - () => VideoReplyPanel( - bvid: videoDetailController.bvid, - oid: videoDetailController.oid.value, - heroTag: heroTag, + ), + manualPlayerWidget, + ] + ], + )), + ); + }, + ), + Expanded( + child: ColoredBox( + key: Key(heroTag), + color: Theme.of(context).colorScheme.background, + child: Column( + children: [ + // Opacity( + // opacity: 0, + // child: SizedBox( + // width: context.width, + // height: 0, + // child: Obx( + // () => TabBar( + // controller: videoDetailController.tabCtr, + // dividerColor: Colors.transparent, + // indicatorColor: + // Theme.of(context).colorScheme.background, + // tabs: videoDetailController.tabs + // .map((String name) => Tab(text: name)) + // .toList(), + // ), + // ), + // ), + // ), + Expanded( + child: TabBarView( + physics: const BouncingScrollPhysics(), + controller: videoDetailController.tabCtr, + children: [ + CustomScrollView( + key: const PageStorageKey('简介'), + slivers: [ + if (videoDetailController.videoType == + SearchType.video) ...[ + VideoIntroPanel(heroTag: heroTag), + ] else if (videoDetailController.videoType == + SearchType.media_bangumi) ...[ + Obx(() => BangumiIntroPanel( + heroTag: heroTag, + cid: videoDetailController.cid.value)), + ], + SliverToBoxAdapter( + child: Divider( + indent: 12, + endIndent: 12, + color: Theme.of(context) + .dividerColor + .withOpacity(0.06), ), - ) + ), + RelatedVideoPanel(heroTag: heroTag), ], ), - ), - ], + Obx( + () => VideoReplyPanel( + bvid: videoDetailController.bvid, + oid: videoDetailController.oid.value, + heroTag: heroTag, + ), + ) + ], + ), ), - ), + ], ), - ], - )), - ], - ), - ); - Widget childWhenDisabledAlmostSquareInner = Obx(() { - if (enableVerticalExpand && - plPlayerController?.direction.value == 'vertical') { - final double videoHeight = context.height - - (removeSafeArea - ? 0 - : (MediaQuery.of(context).padding.top + - MediaQuery.of(context).padding.bottom)); - final double videoWidth = videoHeight * 9 / 16; - return Row(children: [ - SizedBox( - height: videoHeight, - width: isFullScreen.value == true ? context.width : videoWidth, - child: PopScope( - canPop: isFullScreen.value != true, - onPopInvokedWithResult: (bool didPop, Object? result) { - if (isFullScreen.value == true) { - plPlayerController!.triggerFullScreen(status: false); - } - if (MediaQuery.of(context).orientation == - Orientation.landscape && - !horizontalScreen) { - verticalScreenForTwoSeconds(); - } - }, - child: Stack(children: [ - if (isShowing) plPlayer, + ), + ), + ], + ), + ), + ); + Widget get childWhenDisabledAlmostSquareInner => Obx(() { + if (enableVerticalExpand && + plPlayerController?.direction.value == 'vertical') { + final double videoHeight = context.height - + (removeSafeArea + ? 0 + : (MediaQuery.of(context).padding.top + + MediaQuery.of(context).padding.bottom)); + final double videoWidth = videoHeight * 9 / 16; + return Row(children: [ + SizedBox( + height: videoHeight, + width: isFullScreen.value == true ? context.width : videoWidth, + child: PopScope( + canPop: isFullScreen.value != true, + onPopInvokedWithResult: (bool didPop, Object? result) { + if (isFullScreen.value == true) { + plPlayerController!.triggerFullScreen(status: false); + } + if (MediaQuery.of(context).orientation == + Orientation.landscape && + !horizontalScreen) { + verticalScreenForTwoSeconds(); + } + }, + child: Stack(children: [ + if (isShowing) plPlayer, - /// 关闭自动播放时 手动播放 - if (!videoDetailController.autoPlay.value) ...[ - Obx( - () => Visibility( - visible: videoDetailController.isShowCover.value, - child: Positioned( - top: 0, - left: 0, - right: 0, - child: GestureDetector( - onTap: handlePlay, - child: NetworkImgLayer( - type: 'emote', - src: videoDetailController.videoItem['pic'], - width: videoWidth, - height: videoHeight, + /// 关闭自动播放时 手动播放 + if (!videoDetailController.autoPlay.value) ...[ + Obx( + () => Visibility( + visible: videoDetailController.isShowCover.value, + child: Positioned( + top: 0, + left: 0, + right: 0, + child: GestureDetector( + onTap: handlePlay, + child: NetworkImgLayer( + type: 'emote', + src: videoDetailController.videoItem['pic'], + width: videoWidth, + height: videoHeight, + ), ), ), ), ), - ), - manualPlayerWidget, - ] - ]), + manualPlayerWidget, + ] + ]), + ), ), - ), - Expanded( - child: TabBarView( - physics: const BouncingScrollPhysics(), - controller: videoDetailController.tabCtr, - children: [ - CustomScrollView( - key: const PageStorageKey('简介'), - slivers: [ - if (videoDetailController.videoType == - SearchType.video) ...[ - VideoIntroPanel(heroTag: heroTag), - ] else if (videoDetailController.videoType == - SearchType.media_bangumi) ...[ - Obx(() => BangumiIntroPanel( - heroTag: heroTag, - cid: videoDetailController.cid.value)), - ], - SliverToBoxAdapter( - child: Divider( - indent: 12, - endIndent: 12, - color: Theme.of(context).dividerColor.withOpacity(0.06), + Expanded( + child: TabBarView( + physics: const BouncingScrollPhysics(), + controller: videoDetailController.tabCtr, + children: [ + CustomScrollView( + key: const PageStorageKey('简介'), + slivers: [ + if (videoDetailController.videoType == + SearchType.video) ...[ + VideoIntroPanel(heroTag: heroTag), + ] else if (videoDetailController.videoType == + SearchType.media_bangumi) ...[ + Obx(() => BangumiIntroPanel( + heroTag: heroTag, + cid: videoDetailController.cid.value)), + ], + SliverToBoxAdapter( + child: Divider( + indent: 12, + endIndent: 12, + color: + Theme.of(context).dividerColor.withOpacity(0.06), + ), ), - ), - RelatedVideoPanel(heroTag: heroTag), - ], - ), - Obx( - () => VideoReplyPanel( - bvid: videoDetailController.bvid, - oid: videoDetailController.oid.value, - heroTag: heroTag, + RelatedVideoPanel(heroTag: heroTag), + ], ), - ) - ], + Obx( + () => VideoReplyPanel( + bvid: videoDetailController.bvid, + oid: videoDetailController.oid.value, + heroTag: heroTag, + ), + ) + ], + ), ), - ), - ]); - } - final double videoHeight = context.height / 2.5; - final double videoWidth = context.width; - return Column( - children: [ + ]); + } + final double videoHeight = context.height / 2.5; + final double videoWidth = context.width; + return Column(children: [ SizedBox( width: videoWidth, height: isFullScreen.value == true @@ -831,17 +826,15 @@ class _VideoDetailPageState extends State ), ) ])) - ], - ); - }); - Widget childWhenDisabledLandscapeInner = Obx(() { - if (enableVerticalExpand && - plPlayerController?.direction.value == 'vertical') { - final double videoHeight = context.height - - (removeSafeArea ? 0 : MediaQuery.of(context).padding.top); - final double videoWidth = videoHeight * 9 / 16; - return Row( - children: [ + ]); + }); + Widget get childWhenDisabledLandscapeInner => Obx(() { + if (enableVerticalExpand && + plPlayerController?.direction.value == 'vertical') { + final double videoHeight = context.height - + (removeSafeArea ? 0 : MediaQuery.of(context).padding.top); + final double videoWidth = videoHeight * 9 / 16; + return Row(children: [ Expanded( child: CustomScrollView( key: PageStorageKey('简介${videoDetailController.bvid}'), @@ -946,65 +939,61 @@ class _VideoDetailPageState extends State // ], // ), // ), - ], - ); - } - final double videoWidth = - max(context.height / context.width * 1.04, 1 / 2) * context.width; - final double videoHeight = videoWidth * 9 / 16; - return Row( - children: [ + ]); + } + final double videoWidth = + max(context.height / context.width * 1.04, 1 / 2) * context.width; + final double videoHeight = videoWidth * 9 / 16; + return Row(children: [ Column( children: [ SizedBox( - width: - isFullScreen.value == true ? context.width : videoWidth, - height: - isFullScreen.value == true ? context.height : videoHeight, - child: PopScope( - canPop: isFullScreen.value != true, - onPopInvokedWithResult: (bool didPop, Object? result) { - if (isFullScreen.value == true) { - plPlayerController!.triggerFullScreen(status: false); - } - if (MediaQuery.of(context).orientation == - Orientation.landscape && - !horizontalScreen) { - verticalScreenForTwoSeconds(); - } - }, - child: Stack( - children: [ - if (isShowing) plPlayer, + width: isFullScreen.value == true ? context.width : videoWidth, + height: + isFullScreen.value == true ? context.height : videoHeight, + child: PopScope( + canPop: isFullScreen.value != true, + onPopInvokedWithResult: (bool didPop, Object? result) { + if (isFullScreen.value == true) { + plPlayerController!.triggerFullScreen(status: false); + } + if (MediaQuery.of(context).orientation == + Orientation.landscape && + !horizontalScreen) { + verticalScreenForTwoSeconds(); + } + }, + child: Stack( + children: [ + if (isShowing) plPlayer, - /// 关闭自动播放时 手动播放 - if (!videoDetailController - .autoPlay.value) ...[ - Obx( - () => Visibility( - visible: - videoDetailController.isShowCover.value, - child: Positioned( - top: 0, - left: 0, - right: 0, - child: GestureDetector( - onTap: handlePlay, - child: NetworkImgLayer( - type: 'emote', - src: videoDetailController - .videoItem['pic'], - width: videoWidth, - height: videoHeight, - ), - ), + /// 关闭自动播放时 手动播放 + if (!videoDetailController.autoPlay.value) ...[ + Obx( + () => Visibility( + visible: videoDetailController.isShowCover.value, + child: Positioned( + top: 0, + left: 0, + right: 0, + child: GestureDetector( + onTap: handlePlay, + child: NetworkImgLayer( + type: 'emote', + src: videoDetailController.videoItem['pic'], + width: videoWidth, + height: videoHeight, ), ), ), - manualPlayerWidget, - ] - ], - ))), + ), + ), + manualPlayerWidget, + ] + ], + ), + ), + ), Offstage( offstage: isFullScreen.value == true, child: SizedBox( @@ -1064,108 +1053,109 @@ class _VideoDetailPageState extends State ), ), ) - ], + ]); + }); + Widget get childWhenDisabledLandscape => Scaffold( + resizeToAvoidBottomInset: false, + key: videoDetailController.scaffoldKey, + // backgroundColor: Colors.black, + appBar: removeSafeArea + ? null + : AppBar( + backgroundColor: + showStatusBarBackgroundColor ? null : Colors.black, + elevation: 0, + toolbarHeight: 0, + systemOverlayStyle: SystemUiOverlayStyle( + statusBarIconBrightness: + Theme.of(context).brightness == Brightness.dark || + !showStatusBarBackgroundColor + ? Brightness.light + : Brightness.dark, + systemNavigationBarColor: Colors.transparent), + ), + body: Container( + color: Theme.of(context).colorScheme.background, + child: SafeArea( + left: !removeSafeArea && isFullScreen.value != true, + right: !removeSafeArea && isFullScreen.value != true, + top: !removeSafeArea, + bottom: false, //!removeSafeArea, + child: childWhenDisabledLandscapeInner), + ), ); - }); - Widget childWhenDisabledLandscape = Stack(children: [ - Scaffold( - resizeToAvoidBottomInset: false, - key: videoDetailController.scaffoldKey, - // backgroundColor: Colors.black, - appBar: removeSafeArea - ? null - : AppBar( - backgroundColor: - showStatusBarBackgroundColor ? null : Colors.black, - elevation: 0, - toolbarHeight: 0, - systemOverlayStyle: SystemUiOverlayStyle( - statusBarIconBrightness: - Theme.of(context).brightness == Brightness.dark || - !showStatusBarBackgroundColor - ? Brightness.light - : Brightness.dark, - systemNavigationBarColor: Colors.transparent), - ), - body: Container( - color: Theme.of(context).colorScheme.background, - child: SafeArea( - left: !removeSafeArea && isFullScreen.value != true, - right: !removeSafeArea && isFullScreen.value != true, - top: !removeSafeArea, - bottom: false, //!removeSafeArea, - child: childWhenDisabledLandscapeInner))) - ]); - Widget childWhenDisabledAlmostSquare = Stack(children: [ - Scaffold( - resizeToAvoidBottomInset: false, - key: videoDetailController.scaffoldKey, - // backgroundColor: Colors.black, - appBar: removeSafeArea - ? null - : AppBar( - backgroundColor: - showStatusBarBackgroundColor ? null : Colors.black, - elevation: 0, - toolbarHeight: 0, - systemOverlayStyle: SystemUiOverlayStyle( - statusBarIconBrightness: - Theme.of(context).brightness == Brightness.dark || - !showStatusBarBackgroundColor - ? Brightness.light - : Brightness.dark, - systemNavigationBarColor: Colors.transparent), - ), - body: Container( - color: Theme.of(context).colorScheme.background, - child: SafeArea( - left: !removeSafeArea && isFullScreen.value != true, - right: !removeSafeArea && isFullScreen.value != true, - top: !removeSafeArea, - bottom: false, //!removeSafeArea, - child: childWhenDisabledAlmostSquareInner))) - ]); - Widget childWhenEnabled = Obx( - () => !videoDetailController.autoPlay.value - ? const SizedBox() - : PLVideoPlayer( - controller: plPlayerController!, - videoIntroController: - videoDetailController.videoType == SearchType.video - ? videoIntroController - : null, - bangumiIntroController: - videoDetailController.videoType == SearchType.media_bangumi - ? bangumiIntroController - : null, - headerControl: HeaderControl( - controller: plPlayerController, - videoDetailCtr: videoDetailController, - heroTag: heroTag, + Widget get childWhenDisabledAlmostSquare => Scaffold( + resizeToAvoidBottomInset: false, + key: videoDetailController.scaffoldKey, + // backgroundColor: Colors.black, + appBar: removeSafeArea + ? null + : AppBar( + backgroundColor: + showStatusBarBackgroundColor ? null : Colors.black, + elevation: 0, + toolbarHeight: 0, + systemOverlayStyle: SystemUiOverlayStyle( + statusBarIconBrightness: + Theme.of(context).brightness == Brightness.dark || + !showStatusBarBackgroundColor + ? Brightness.light + : Brightness.dark, + systemNavigationBarColor: Colors.transparent), ), - danmuWidget: pipNoDanmaku - ? null - : Obx( - () => PlDanmaku( - key: Key( - videoDetailController.danmakuCid.value.toString()), - cid: videoDetailController.danmakuCid.value, - playerController: plPlayerController!, + body: Container( + color: Theme.of(context).colorScheme.background, + child: SafeArea( + left: !removeSafeArea && isFullScreen.value != true, + right: !removeSafeArea && isFullScreen.value != true, + top: !removeSafeArea, + bottom: false, //!removeSafeArea, + child: childWhenDisabledAlmostSquareInner), + ), + ); + Widget get childWhenEnabled => Obx( + () => !videoDetailController.autoPlay.value + ? const SizedBox() + : PLVideoPlayer( + controller: plPlayerController!, + videoIntroController: + videoDetailController.videoType == SearchType.video + ? videoIntroController + : null, + bangumiIntroController: + videoDetailController.videoType == SearchType.media_bangumi + ? bangumiIntroController + : null, + headerControl: HeaderControl( + controller: plPlayerController, + videoDetailCtr: videoDetailController, + heroTag: heroTag, + ), + danmuWidget: pipNoDanmaku + ? null + : Obx( + () => PlDanmaku( + key: Key(videoDetailController.danmakuCid.value + .toString()), + cid: videoDetailController.danmakuCid.value, + playerController: plPlayerController!, + ), ), - ), - ), - ); - Widget autoChoose(Widget childWhenDisabled) { - if (Platform.isAndroid) { - return PiPSwitcher( - childWhenDisabled: childWhenDisabled, - childWhenEnabled: childWhenEnabled, - floating: floating, - ); - } - return childWhenDisabled; + ), + ); + Widget autoChoose(Widget childWhenDisabled) { + if (Platform.isAndroid) { + return PiPSwitcher( + childWhenDisabled: childWhenDisabled, + childWhenEnabled: childWhenEnabled, + floating: floating, + ); } + return childWhenDisabled; + } + @override + Widget build(BuildContext context) { if (!horizontalScreen) { return autoChoose(childWhenDisabled); } diff --git a/lib/pages/video/detail/widgets/ai_detail.dart b/lib/pages/video/detail/widgets/ai_detail.dart index 1afbfdc9a..264ff2bc5 100644 --- a/lib/pages/video/detail/widgets/ai_detail.dart +++ b/lib/pages/video/detail/widgets/ai_detail.dart @@ -108,7 +108,7 @@ class AiDetail extends StatelessWidget { Get.find( tag: Get.arguments[ 'heroTag']) - .plPlayerController + .plPlayerController! .seekTo( Duration( seconds: diff --git a/lib/pages/video/detail/widgets/header_control.dart b/lib/pages/video/detail/widgets/header_control.dart index b4a2305de..37ebe0c32 100644 --- a/lib/pages/video/detail/widgets/header_control.dart +++ b/lib/pages/video/detail/widgets/header_control.dart @@ -86,7 +86,7 @@ class _HeaderControlState extends State { void listenFullScreenStatus() { fullScreenStatusListener = widget - .videoDetailCtr!.plPlayerController.isFullScreen + .videoDetailCtr!.plPlayerController!.isFullScreen .listen((bool status) { isFullScreen = status; @@ -99,7 +99,7 @@ class _HeaderControlState extends State { @override void dispose() { - widget.floating?.dispose(); + // widget.floating?.dispose(); fullScreenStatusListener.cancel(); clock.cancel(); super.dispose(); @@ -1569,109 +1569,109 @@ class _HeaderControlState extends State { ), ), ), - if (Platform.isAndroid) - SizedBox( - width: 42, - height: 34, - child: IconButton( - tooltip: '画中画', - style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), - ), - onPressed: () async { - bool canUsePiP = widget.floating != null && - await widget.floating!.isPipAvailable; - widget.controller!.hiddenControls(false); - if (canUsePiP) { - bool enableBackgroundPlay = setting.get( - SettingBoxKey.enableBackgroundPlay, - defaultValue: true); - if (!enableBackgroundPlay) { - // SmartDialog.showToast('建议开启【后台播放】功能\n避免画中画没有暂停按钮'); - // await Future.delayed(const Duration(seconds: 2), () { - // }); - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Column(children: [ - const Row( - children: [ - Icon( - Icons.check, - color: Colors.green, - ), - SizedBox(width: 10), - Text('画中画', - style: - TextStyle(fontSize: 15, height: 1.5)) - ], - ), - const SizedBox(height: 10), - const Text( - '建议开启【后台音频服务】\n' - '避免画中画没有暂停按钮', - style: - TextStyle(fontSize: 12.5, height: 1.5)), - Row(children: [ - TextButton( - style: ButtonStyle( - foregroundColor: - MaterialStateProperty.resolveWith( - (states) { - return Theme.of(context) - .snackBarTheme - .actionTextColor; - }), - ), - onPressed: () async { - _.setBackgroundPlay(true); - SmartDialog.showToast("请重新载入本页面刷新"); - // Get.back(); - }, - child: const Text('启用后台音频服务')), - const SizedBox(width: 10), - TextButton( - style: ButtonStyle( - foregroundColor: - MaterialStateProperty.resolveWith( - (states) { - return Theme.of(context) - .snackBarTheme - .actionTextColor; - }), - ), - onPressed: () {}, - child: const Text('不启用')) - ]) - ]), - duration: const Duration(seconds: 2), - showCloseIcon: true, - ), - ); - await Future.delayed(const Duration(seconds: 3), () {}); - } - final Rational aspectRatio = Rational( - widget.videoDetailCtr!.data.dash!.video!.first.width!, - widget.videoDetailCtr!.data.dash!.video!.first.height!, - ); - if (!context.mounted) return; - await widget.floating!.enable(EnableManual( - aspectRatio: aspectRatio, - sourceRectHint: Rectangle( - 0, - 0, - context.width.toInt(), - context.height.toInt(), - ), - )); - } else {} - }, - icon: const Icon( - Icons.picture_in_picture_outlined, - size: 19, - color: Colors.white, - ), - ), - ), + // if (Platform.isAndroid) + // SizedBox( + // width: 42, + // height: 34, + // child: IconButton( + // tooltip: '画中画', + // style: ButtonStyle( + // padding: MaterialStateProperty.all(EdgeInsets.zero), + // ), + // onPressed: () async { + // bool canUsePiP = widget.floating != null && + // await widget.floating!.isPipAvailable; + // widget.controller!.hiddenControls(false); + // if (canUsePiP) { + // bool enableBackgroundPlay = setting.get( + // SettingBoxKey.enableBackgroundPlay, + // defaultValue: true); + // if (!enableBackgroundPlay) { + // // SmartDialog.showToast('建议开启【后台播放】功能\n避免画中画没有暂停按钮'); + // // await Future.delayed(const Duration(seconds: 2), () { + // // }); + // ScaffoldMessenger.of(context).showSnackBar( + // SnackBar( + // content: Column(children: [ + // const Row( + // children: [ + // Icon( + // Icons.check, + // color: Colors.green, + // ), + // SizedBox(width: 10), + // Text('画中画', + // style: + // TextStyle(fontSize: 15, height: 1.5)) + // ], + // ), + // const SizedBox(height: 10), + // const Text( + // '建议开启【后台音频服务】\n' + // '避免画中画没有暂停按钮', + // style: + // TextStyle(fontSize: 12.5, height: 1.5)), + // Row(children: [ + // TextButton( + // style: ButtonStyle( + // foregroundColor: + // MaterialStateProperty.resolveWith( + // (states) { + // return Theme.of(context) + // .snackBarTheme + // .actionTextColor; + // }), + // ), + // onPressed: () async { + // _.setBackgroundPlay(true); + // SmartDialog.showToast("请重新载入本页面刷新"); + // // Get.back(); + // }, + // child: const Text('启用后台音频服务')), + // const SizedBox(width: 10), + // TextButton( + // style: ButtonStyle( + // foregroundColor: + // MaterialStateProperty.resolveWith( + // (states) { + // return Theme.of(context) + // .snackBarTheme + // .actionTextColor; + // }), + // ), + // onPressed: () {}, + // child: const Text('不启用')) + // ]) + // ]), + // duration: const Duration(seconds: 2), + // showCloseIcon: true, + // ), + // ); + // await Future.delayed(const Duration(seconds: 3), () {}); + // } + // final Rational aspectRatio = Rational( + // widget.videoDetailCtr!.data.dash!.video!.first.width!, + // widget.videoDetailCtr!.data.dash!.video!.first.height!, + // ); + // if (!context.mounted) return; + // await widget.floating!.enable(ImmediatePiP( + // aspectRatio: aspectRatio, + // sourceRectHint: Rectangle( + // 0, + // 0, + // context.width.toInt(), + // context.height.toInt(), + // ), + // )); + // } else {} + // }, + // icon: const Icon( + // Icons.picture_in_picture_outlined, + // size: 19, + // color: Colors.white, + // ), + // ), + // ), SizedBox( width: 42, height: 34, diff --git a/lib/plugin/pl_player/controller.dart b/lib/plugin/pl_player/controller.dart index 754bdcca9..11e9d4eeb 100644 --- a/lib/plugin/pl_player/controller.dart +++ b/lib/plugin/pl_player/controller.dart @@ -32,7 +32,7 @@ Box setting = GStorage.setting; Box localCache = GStorage.localCache; class PlPlayerController { - Player? _videoPlayerController; + static Player? _videoPlayerController; VideoController? _videoController; // 添加一个私有静态变量来保存实例 @@ -386,7 +386,11 @@ class PlPlayerController { }) { // 如果实例尚未创建,则创建一个新实例 _instance ??= PlPlayerController._(); + // print('getInstance'); + // print(StackTrace.current); _instance!._playerCount.value += 1; + print("_playerCount"); + print(_instance!._playerCount.value); _videoType.value = videoType; return _instance!; } @@ -887,6 +891,7 @@ class PlPlayerController { /// TODO _duration.value丢失 Future play({bool repeat = false, bool hideControls = true}) async { if (_playerCount.value == 0) return; + if (playerStatus.status.value == PlayerStatus.disabled) return; // 播放时自动隐藏控制条 controls = !hideControls; // repeat为true,将从头播放 @@ -918,6 +923,11 @@ class PlPlayerController { } } + Future disable() async { + playerStatus.status.value = PlayerStatus.disabled; + await _videoPlayerController?.stop(); + } + /// 更改播放状态 Future togglePlay() async { feedBack(); diff --git a/lib/plugin/pl_player/models/play_status.dart b/lib/plugin/pl_player/models/play_status.dart index 2af09a61d..1ce70c2ff 100644 --- a/lib/plugin/pl_player/models/play_status.dart +++ b/lib/plugin/pl_player/models/play_status.dart @@ -1,6 +1,6 @@ import 'package:get/get.dart'; -enum PlayerStatus { completed, playing, paused } +enum PlayerStatus { completed, playing, paused, disabled } class PlPlayerStatus { Rx status = Rx(PlayerStatus.paused); @@ -16,4 +16,8 @@ class PlPlayerStatus { bool get completed { return status.value == PlayerStatus.completed; } + + bool get disabled { + return status.value == PlayerStatus.disabled; + } } diff --git a/lib/services/audio_handler.dart b/lib/services/audio_handler.dart index 7ee587bdc..e5ccd21e1 100644 --- a/lib/services/audio_handler.dart +++ b/lib/services/audio_handler.dart @@ -23,7 +23,7 @@ Future initAudioService() async { } class VideoPlayerServiceHandler extends BaseAudioHandler with SeekHandler { - static final List _item = []; + // static final List _item = []; Box setting = GStorage.setting; bool enableBackgroundPlay = true; // PlPlayerController player = PlPlayerController.getInstance(); @@ -60,11 +60,11 @@ class VideoPlayerServiceHandler extends BaseAudioHandler with SeekHandler { Future setMediaItem(MediaItem newMediaItem) async { if (!enableBackgroundPlay) return; - // print("此时调用栈为:"); - // print(newMediaItem); - // print(newMediaItem.title); - // debugPrint(StackTrace.current.toString()); - if(!mediaItem.isClosed) mediaItem.add(newMediaItem); + print("此时调用栈为:"); + print(newMediaItem); + print(newMediaItem.title); + debugPrint(StackTrace.current.toString()); + if (!mediaItem.isClosed) mediaItem.add(newMediaItem); } Future setPlaybackState(PlayerStatus status, bool isBuffering) async { @@ -72,7 +72,9 @@ class VideoPlayerServiceHandler extends BaseAudioHandler with SeekHandler { final AudioProcessingState processingState; final playing = status == PlayerStatus.playing; - if (status == PlayerStatus.completed) { + if (status == PlayerStatus.disabled) { + processingState = AudioProcessingState.idle; + } else if (status == PlayerStatus.completed) { processingState = AudioProcessingState.completed; } else if (isBuffering) { processingState = AudioProcessingState.buffering; @@ -84,11 +86,11 @@ class VideoPlayerServiceHandler extends BaseAudioHandler with SeekHandler { processingState: isBuffering ? AudioProcessingState.buffering : processingState, controls: [ - MediaControl.rewind - .copyWith(androidIcon: 'drawable/ic_baseline_replay_10_24'), + MediaControl.rewind, + // .copyWith(androidIcon: 'drawable/ic_baseline_replay_10_24'), if (playing) MediaControl.pause else MediaControl.play, - MediaControl.fastForward - .copyWith(androidIcon: 'drawable/ic_baseline_forward_10_24'), + MediaControl.fastForward, + // .copyWith(androidIcon: 'drawable/ic_baseline_forward_10_24'), ], playing: playing, systemActions: const { @@ -99,74 +101,89 @@ class VideoPlayerServiceHandler extends BaseAudioHandler with SeekHandler { onStatusChange(PlayerStatus status, bool isBuffering) { if (!enableBackgroundPlay) return; - - if (_item.isEmpty) return; + // if (_item.isEmpty) return; setPlaybackState(status, isBuffering); } - onVideoDetailChange(dynamic data, int cid) { + onVideoDetailChange( + String? title, String? artist, Duration? duration, String? artUri) { if (!enableBackgroundPlay) return; - // print('当前调用栈为:'); - // print(StackTrace.current); - if (!PlPlayerController.instanceExists()) return; - if (data == null) return; - - late MediaItem? mediaItem; - if (data is VideoDetailData) { - if ((data.pages?.length ?? 0) > 1) { - final current = data.pages?.firstWhere((element) => element.cid == cid); - mediaItem = MediaItem( - id: UniqueKey().toString(), - title: current?.pagePart ?? "", - artist: data.title ?? "", - album: data.title ?? "", - duration: Duration(seconds: current?.duration ?? 0), - artUri: Uri.parse(data.pic ?? ""), - ); - } else { - mediaItem = MediaItem( - id: UniqueKey().toString(), - title: data.title ?? "", - artist: data.owner?.name ?? "", - duration: Duration(seconds: data.duration ?? 0), - artUri: Uri.parse(data.pic ?? ""), - ); - } - } else if (data is BangumiInfoModel) { - final current = - data.episodes?.firstWhere((element) => element.cid == cid); - mediaItem = MediaItem( - id: UniqueKey().toString(), - title: current?.longTitle ?? "", - artist: data.title ?? "", - duration: Duration(milliseconds: current?.duration ?? 0), - artUri: Uri.parse(data.cover ?? ""), - ); - } - if (mediaItem == null) return; - // print("exist: ${PlPlayerController.instanceExists()}"); + print('当前调用栈为:'); + print(StackTrace.current); if (!PlPlayerController.instanceExists()) return; - _item.add(mediaItem); + MediaItem mediaItem = MediaItem( + id: UniqueKey().toString(), + title: title ?? "", + artist: artist ?? "", + duration: duration ?? Duration.zero, + artUri: Uri.parse(artUri ?? ""), + ); setMediaItem(mediaItem); } - onVideoDetailDispose() { - if (!enableBackgroundPlay) return; - - playbackState.add(playbackState.value.copyWith( - processingState: AudioProcessingState.idle, - playing: false, - )); - if (_item.isNotEmpty) { - _item.removeLast(); - } - if (_item.isNotEmpty) { - setMediaItem(_item.last); - stop(); - } else { - clear(); - } - } + // onVideoDetailChange2(dynamic data, int cid) { + // if (!enableBackgroundPlay) return; + // print('当前调用栈为:'); + // print(StackTrace.current); + // if (!PlPlayerController.instanceExists()) return; + // if (data == null) return; + // + // late MediaItem? mediaItem; + // if (data is VideoDetailData) { + // if ((data.pages?.length ?? 0) > 1) { + // final current = data.pages?.firstWhere((element) => element.cid == cid); + // mediaItem = MediaItem( + // id: UniqueKey().toString(), + // title: current?.pagePart ?? "", + // artist: data.title ?? "", + // album: data.title ?? "", + // duration: Duration(seconds: current?.duration ?? 0), + // artUri: Uri.parse(data.pic ?? ""), + // ); + // } else { + // mediaItem = MediaItem( + // id: UniqueKey().toString(), + // title: data.title ?? "", + // artist: data.owner?.name ?? "", + // duration: Duration(seconds: data.duration ?? 0), + // artUri: Uri.parse(data.pic ?? ""), + // ); + // } + // } else if (data is BangumiInfoModel) { + // final current = + // data.episodes?.firstWhere((element) => element.cid == cid); + // mediaItem = MediaItem( + // id: UniqueKey().toString(), + // title: current?.longTitle ?? "", + // artist: data.title ?? "", + // duration: Duration(milliseconds: current?.duration ?? 0), + // artUri: Uri.parse(data.cover ?? ""), + // ); + // } + // if (mediaItem == null) return; + // print("exist: ${PlPlayerController.instanceExists()}"); + // if (!PlPlayerController.instanceExists()) return; + // _item.add(mediaItem); + // setMediaItem(mediaItem); + // } + + // onVideoDetailDispose() { + // if (!enableBackgroundPlay) return; + // + // playbackState.add(playbackState.value.copyWith( + // processingState: AudioProcessingState.idle, + // playing: false, + // )); + // if (_item.isNotEmpty) { + // _item.removeLast(); + // } + // if (_item.isNotEmpty) { + // setMediaItem(_item.last); + // stop(); + // } else { + // clear(); + // } + // } clear() { if (!enableBackgroundPlay) return; @@ -175,7 +192,7 @@ class VideoPlayerServiceHandler extends BaseAudioHandler with SeekHandler { processingState: AudioProcessingState.idle, playing: false, )); - _item.clear(); + // _item.clear(); stop(); } diff --git a/pubspec.lock b/pubspec.lock index 512514b31..34d25395d 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -563,10 +563,10 @@ packages: dependency: "direct main" description: name: floating - sha256: ddcd7f28247746dbb62997c48c89d1824118676796df47fdc6f864f8d02849bc + sha256: "62924c96915823ce955da32b26766e11583ce437990a601032191d6d4b0b0764" url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted - version: "3.0.0" + version: "4.0.1" flutter: dependency: "direct main" description: flutter diff --git a/pubspec.yaml b/pubspec.yaml index 7ccdfdf15..2cc5a31bc 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # In Windows, build-name is used as the major, minor, and patch parts # of the product and file versions while build-number is used as the build suffix. -version: 1.0.22+114514 +version: 1.0.22-beta.12+1743 environment: sdk: ">=2.19.6 <3.0.0" @@ -129,7 +129,7 @@ dependencies: # 代理 system_proxy: ^0.1.0 # pip - floating: ^3.0.0 + floating: ^4.0.1 # html解析 html: ^0.15.4 # html渲染