diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 00000000..82ebaeaf --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,25 @@ +{ + // 使用 IntelliSense 了解相关属性。 + // 悬停以查看现有属性的描述。 + // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "pilipala", + "request": "launch", + "type": "dart" + }, + { + "name": "pilipala (profile mode)", + "request": "launch", + "type": "dart", + "flutterMode": "profile" + }, + { + "name": "pilipala (release mode)", + "request": "launch", + "type": "dart", + "flutterMode": "release" + } + ] +} \ No newline at end of file diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 9e4b7bef..0faef731 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -62,4 +62,11 @@ + + + diff --git a/android/app/src/main/kotlin/com/guozhigq/pilipala/MainActivity.kt b/android/app/src/main/kotlin/com/guozhigq/pilipala/MainActivity.kt index 2cb48442..117c85ef 100644 --- a/android/app/src/main/kotlin/com/guozhigq/pilipala/MainActivity.kt +++ b/android/app/src/main/kotlin/com/guozhigq/pilipala/MainActivity.kt @@ -1,9 +1,6 @@ package com.guozhigq.pilipala import io.flutter.embedding.android.FlutterActivity -import com.zezo357.flutter_meedu_media_kit.MeeduPlayerFlutterActivity; - -class MainActivity: MeeduPlayerFlutterActivity() { +class MainActivity: FlutterActivity() { } - diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 3e950c72..48a1aa63 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -5,8 +5,6 @@ PODS: - device_info_plus (0.0.1): - Flutter - Flutter (1.0.0) - - flutter_meedu_media_kit (0.0.1): - - Flutter - FMDB (2.7.5): - FMDB/standard (= 2.7.5) - FMDB/standard (2.7.5) @@ -30,9 +28,6 @@ PODS: - Flutter - share_plus (0.0.1): - Flutter - - shared_preferences_foundation (0.0.1): - - Flutter - - FlutterMacOS - sqflite (0.0.3): - Flutter - FMDB (>= 2.7.5) @@ -49,7 +44,6 @@ DEPENDENCIES: - connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`) - device_info_plus (from `.symlinks/plugins/device_info_plus/ios`) - Flutter (from `Flutter`) - - flutter_meedu_media_kit (from `.symlinks/plugins/flutter_meedu_media_kit/ios`) - image_gallery_saver (from `.symlinks/plugins/image_gallery_saver/ios`) - media_kit_libs_ios_video (from `.symlinks/plugins/media_kit_libs_ios_video/ios`) - media_kit_native_event_loop (from `.symlinks/plugins/media_kit_native_event_loop/ios`) @@ -59,7 +53,6 @@ DEPENDENCIES: - permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`) - screen_brightness_ios (from `.symlinks/plugins/screen_brightness_ios/ios`) - share_plus (from `.symlinks/plugins/share_plus/ios`) - - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) - sqflite (from `.symlinks/plugins/sqflite/ios`) - volume_controller (from `.symlinks/plugins/volume_controller/ios`) - wakelock_plus (from `.symlinks/plugins/wakelock_plus/ios`) @@ -78,8 +71,6 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/device_info_plus/ios" Flutter: :path: Flutter - flutter_meedu_media_kit: - :path: ".symlinks/plugins/flutter_meedu_media_kit/ios" image_gallery_saver: :path: ".symlinks/plugins/image_gallery_saver/ios" media_kit_libs_ios_video: @@ -98,8 +89,6 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/screen_brightness_ios/ios" share_plus: :path: ".symlinks/plugins/share_plus/ios" - shared_preferences_foundation: - :path: ".symlinks/plugins/shared_preferences_foundation/darwin" sqflite: :path: ".symlinks/plugins/sqflite/ios" volume_controller: @@ -115,19 +104,17 @@ SPEC CHECKSUMS: connectivity_plus: 413a8857dd5d9f1c399a39130850d02fe0feaf7e device_info_plus: 7545d84d8d1b896cb16a4ff98c19f07ec4b298ea Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 - flutter_meedu_media_kit: 5059e8719e3fd4a65fe5312b0febc75491e553f9 FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a image_gallery_saver: cb43cc43141711190510e92c460eb1655cd343cb media_kit_libs_ios_video: bcbf9a53dd3b60c0fcf92b0903e4706391ad0b65 media_kit_native_event_loop: 1eac6db2378101404392c80606103b42f7c2c491 - media_kit_video: 6c61501e3ab980488d80df6d705905d14b150b63 + media_kit_video: 8a750aa160f95fd6a5d6b88949c8df4c5cea6b0d package_info_plus: fd030dabf36271f146f1f3beacd48f564b0f17f7 path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943 permission_handler_apple: e76247795d700c14ea09e3a2d8855d41ee80a2e6 ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825 screen_brightness_ios: 715ca807df953bf676d339f11464e438143ee625 share_plus: 599aa54e4ea31d4b4c0e9c911bcc26c55e791028 - shared_preferences_foundation: 5b919d13b803cadd15ed2dc053125c68730e5126 sqflite: 31f7eba61e3074736dff8807a9b41581e4f7f15a volume_controller: 531ddf792994285c9b17f9d8a7e4dcdd29b3eae9 wakelock_plus: 8b09852c8876491e4b6d179e17dfe2a0b5f60d47 diff --git a/lib/common/widgets/app_bar_ani.dart b/lib/common/widgets/app_bar_ani.dart new file mode 100644 index 00000000..4fea635c --- /dev/null +++ b/lib/common/widgets/app_bar_ani.dart @@ -0,0 +1,57 @@ +import 'package:flutter/material.dart'; + +class AppBarAni extends StatelessWidget implements PreferredSizeWidget { + const AppBarAni({ + required this.child, + required this.controller, + required this.visible, + this.position, + Key? key, + }) : super(key: key); + + final PreferredSizeWidget child; + final AnimationController controller; + final bool visible; + final String? position; + + @override + Size get preferredSize => child.preferredSize; + + @override + Widget build(BuildContext context) { + visible ? controller.reverse() : controller.forward(); + return SlideTransition( + position: Tween( + begin: Offset.zero, + end: Offset(0, position! == 'top' ? -1 : 1), + ).animate(CurvedAnimation( + parent: controller, + curve: Curves.easeInOut, + )), + child: Container( + decoration: BoxDecoration( + gradient: position! == 'top' + ? const LinearGradient( + begin: Alignment.bottomCenter, + end: Alignment.topCenter, + colors: [ + Colors.transparent, + Colors.black45, + ], + tileMode: TileMode.clamp, + ) + : const LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + Colors.transparent, + Colors.black45, + ], + tileMode: TileMode.mirror, + ), + ), + child: child, + ), + ); + } +} diff --git a/lib/main.dart b/lib/main.dart index af386de1..633e41c4 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,5 +1,4 @@ import 'package:flutter_localizations/flutter_localizations.dart'; -import 'package:flutter_meedu_media_kit/meedu_player.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:flutter/material.dart'; @@ -12,6 +11,7 @@ import 'package:pilipala/router/app_pages.dart'; import 'package:pilipala/pages/main/view.dart'; import 'package:pilipala/utils/data.dart'; import 'package:pilipala/utils/storage.dart'; +import 'package:media_kit/media_kit.dart'; // Provides [Player], [Media], [Playlist] etc. void main() async { WidgetsFlutterBinding.ensureInitialized(); diff --git a/lib/pages/liveRoom/controller.dart b/lib/pages/liveRoom/controller.dart index ecd49f6f..51ffd7a2 100644 --- a/lib/pages/liveRoom/controller.dart +++ b/lib/pages/liveRoom/controller.dart @@ -1,9 +1,9 @@ import 'package:flutter/material.dart'; -import 'package:flutter_meedu_media_kit/meedu_player.dart'; import 'package:get/get.dart'; import 'package:pilipala/http/constants.dart'; import 'package:pilipala/http/live.dart'; import 'package:pilipala/models/live/room_info.dart'; +import 'package:pilipala/plugin/pl_player/index.dart'; class LiveRoomController extends GetxController { String cover = ''; @@ -13,13 +13,15 @@ class LiveRoomController extends GetxController { double volume = 0.0; // 静音状态 RxBool volumeOff = false.obs; + PlPlayerController plPlayerController = + PlPlayerController(controlsEnabled: false); - MeeduPlayerController meeduPlayerController = MeeduPlayerController( - colorTheme: Theme.of(Get.context!).colorScheme.primary, - pipEnabled: true, - controlsStyle: ControlsStyle.live, - enabledButtons: const EnabledButtons(pip: true), - ); + // MeeduPlayerController meeduPlayerController = MeeduPlayerController( + // colorTheme: Theme.of(Get.context!).colorScheme.primary, + // pipEnabled: true, + // controlsStyle: ControlsStyle.live, + // enabledButtons: const EnabledButtons(pip: true), + // ); @override void onInit() { @@ -36,19 +38,21 @@ class LiveRoomController extends GetxController { } playerInit(source) { - meeduPlayerController.setDataSource( + plPlayerController.setDataSource( DataSource( + videoSource: source, + audioSource: null, type: DataSourceType.network, - source: source, httpHeaders: { 'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 13_3_1) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.4 Safari/605.1.15', 'referer': HttpString.baseUrl }, ), + // 硬解 + enableHA: true, autoplay: true, ); - volume = meeduPlayerController.volume.value; } Future queryLiveInfo() async { @@ -68,12 +72,12 @@ class LiveRoomController extends GetxController { if (value == 0) { // 设置音量 volumeOff.value = false; - meeduPlayerController.setVolume(volume); + // meeduPlayerController.setVolume(volume); } else { // 取消音量 volume = value; volumeOff.value = true; - meeduPlayerController.setVolume(0); + // meeduPlayerController.setVolume(0); } } } diff --git a/lib/pages/liveRoom/view.dart b/lib/pages/liveRoom/view.dart index 8e73b70c..1fe31c9e 100644 --- a/lib/pages/liveRoom/view.dart +++ b/lib/pages/liveRoom/view.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; -import 'package:flutter_meedu_media_kit/meedu_player.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; +import 'package:pilipala/plugin/pl_player/index.dart'; import 'controller.dart'; @@ -14,7 +14,7 @@ class LiveRoomPage extends StatefulWidget { class _LiveRoomPageState extends State { final LiveRoomController _liveRoomController = Get.put(LiveRoomController()); - MeeduPlayerController? _meeduPlayerController; + PlPlayerController? plPlayerController; bool isShowCover = true; bool isPlay = true; @@ -22,8 +22,8 @@ class _LiveRoomPageState extends State { @override void initState() { super.initState(); - _meeduPlayerController = _liveRoomController.meeduPlayerController; - _meeduPlayerController!.onPlayerStatusChanged.listen( + plPlayerController = _liveRoomController.plPlayerController; + plPlayerController!.onPlayerStatusChanged.listen( (PlayerStatus status) { if (status == PlayerStatus.playing) { isShowCover = false; @@ -35,7 +35,7 @@ class _LiveRoomPageState extends State { @override void dispose() { - _meeduPlayerController!.dispose(); + plPlayerController!.dispose(); super.dispose(); } @@ -85,62 +85,33 @@ class _LiveRoomPageState extends State { body: Column( children: [ Hero( - tag: _liveRoomController.heroTag, - child: Stack( - children: [ - AspectRatio( - aspectRatio: 16 / 9, - child: MeeduVideoPlayer( - header: (BuildContext context, - MeeduPlayerController meeduPlayerController, - Responsive responsive) { - return AppBar( - backgroundColor: Colors.transparent, - primary: false, - elevation: 0, - scrolledUnderElevation: 0, - foregroundColor: Colors.white, - automaticallyImplyLeading: false, - centerTitle: false, - title: Text(_liveRoomController.liveItem.title, - style: const TextStyle(fontSize: 12)), - actions: [ - SizedBox( - width: 38, - height: 38, - child: IconButton( - onPressed: () => - meeduPlayerController.enterPip(context), - icon: const Icon( - Icons.branding_watermark_outlined, - size: 19, - ), - ), - ), - const SizedBox(width: 12) - ], - ); - }, - controller: _meeduPlayerController!, - ), - ), - if (_liveRoomController.liveItem.cover != null) - Visibility( - visible: isShowCover, - child: Positioned( - top: 0, - left: 0, - right: 0, - child: NetworkImgLayer( - type: 'emote', - src: _liveRoomController.liveItem.cover, - width: Get.size.width, - height: videoHeight, - ), + tag: _liveRoomController.heroTag, + child: Stack( + children: [ + AspectRatio( + aspectRatio: 16 / 9, + child: plPlayerController!.videoPlayerController != null + ? PLVideoPlayer(controller: plPlayerController!) + : const SizedBox(), + ), + if (_liveRoomController.liveItem.cover != null) + Visibility( + visible: isShowCover, + child: Positioned( + top: 0, + left: 0, + right: 0, + child: NetworkImgLayer( + type: 'emote', + src: _liveRoomController.liveItem.cover, + width: Get.size.width, + height: videoHeight, ), ), - ], - )), + ), + ], + ), + ), if (_liveRoomController.liveItem.watchedShow != null) Container( height: 45, @@ -153,35 +124,35 @@ class _LiveRoomPageState extends State { ), ), child: Row(children: [ - // SizedBox( - // width: 38, - // height: 38, - // child: IconButton( - // onPressed: () {}, - // icon: const Icon( - // Icons.subtitles_outlined, - // size: 21, - // ), - // ), - // ), + SizedBox( + width: 38, + height: 38, + child: IconButton( + onPressed: () {}, + icon: const Icon( + Icons.subtitles_outlined, + size: 21, + ), + ), + ), const Spacer(), - // SizedBox( - // width: 38, - // height: 38, - // child: IconButton( - // onPressed: () {}, - // icon: const Icon( - // Icons.hd_outlined, - // size: 20, - // ), - // ), - // ), + SizedBox( + width: 38, + height: 38, + child: IconButton( + onPressed: () {}, + icon: const Icon( + Icons.hd_outlined, + size: 20, + ), + ), + ), SizedBox( width: 38, height: 38, child: IconButton( onPressed: () => _liveRoomController - .setVolumn(_meeduPlayerController!.volume.value), + .setVolumn(plPlayerController!.volume.value), icon: Obx(() => Icon( _liveRoomController.volumeOff.value ? Icons.volume_off_outlined @@ -194,8 +165,8 @@ class _LiveRoomPageState extends State { width: 38, height: 38, child: IconButton( - onPressed: () => - _meeduPlayerController!.goToFullscreen(context), + onPressed: () => {}, + // plPlayerController!.goToFullscreen(context), icon: const Icon( Icons.fullscreen, ), diff --git a/lib/pages/video/detail/controller.dart b/lib/pages/video/detail/controller.dart index 1f7594b8..3051ba55 100644 --- a/lib/pages/video/detail/controller.dart +++ b/lib/pages/video/detail/controller.dart @@ -1,6 +1,5 @@ import 'dart:async'; import 'package:flutter/material.dart'; -import 'package:flutter_meedu_media_kit/meedu_player.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; import 'package:pilipala/http/constants.dart'; @@ -9,6 +8,7 @@ import 'package:pilipala/models/common/reply_type.dart'; import 'package:pilipala/models/video/play/url.dart'; import 'package:pilipala/models/video/reply/item.dart'; import 'package:pilipala/pages/video/detail/replyReply/index.dart'; +import 'package:pilipala/plugin/pl_player/index.dart'; import 'package:pilipala/utils/storage.dart'; class VideoDetailController extends GetxController @@ -38,21 +38,12 @@ class VideoDetailController extends GetxController int fRpid = 0; ReplyItemModel? firstFloor; - final scaffoldKey = GlobalKey(); - - MeeduPlayerController meeduPlayerController = MeeduPlayerController( - colorTheme: Theme.of(Get.context!).colorScheme.primary, - pipEnabled: true, - controlsStyle: ControlsStyle.youtube, - enabledButtons: const EnabledButtons(pip: true), - ); - Timer? timer; - RxString bgCover = ''.obs; Box user = GStrorage.user; Box localCache = GStrorage.localCache; + PlPlayerController plPlayerController = PlPlayerController(); @override void onInit() { @@ -95,38 +86,30 @@ class VideoDetailController extends GetxController }); } - playerInit(source, audioSource, {Duration defaultST = Duration.zero}) { - meeduPlayerController.onVideoFitChange(BoxFit.cover); - meeduPlayerController.setDataSource( + playerInit(source, audioSource, + {Duration defaultST = Duration.zero, int duration = 0}) async { + plPlayerController.setDataSource( DataSource( - type: DataSourceType.network, - source: source, + videoSource: source, audioSource: audioSource, + type: DataSourceType.network, httpHeaders: { 'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 13_3_1) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.4 Safari/605.1.15', 'referer': HttpString.baseUrl }, ), + // 硬解 + enableHA: true, autoplay: true, seekTo: defaultST, + duration: Duration(milliseconds: duration), ); } - // Future meeduDispose() async { - // if (meeduPlayerController != null) { - // _playerEventSubs?.cancel(); - // await meeduPlayerController!.dispose(); - // meeduPlayerController = null; - // // The next line disables the wakelock again. - // // await Wakelock.disable(); - // } - // } - // 视频链接 queryVideoUrl() async { var result = await VideoHttp.videoUrl(cid: cid, bvid: bvid); - // log('result: ${result.toString()}'); if (result['status']) { PlayUrlModel data = result['data']; // 指定质量的视频 -> 最高质量的视频 @@ -134,7 +117,8 @@ class VideoDetailController extends GetxController String audioUrl = data.dash!.audio!.isNotEmpty ? data.dash!.audio!.first.baseUrl! : ''; playerInit(videoUrl, audioUrl, - defaultST: Duration(milliseconds: data.lastPlayTime!)); + defaultST: Duration(milliseconds: data.lastPlayTime!), + duration: data.timeLength ?? 0); } } @@ -151,7 +135,7 @@ class VideoDetailController extends GetxController if (localCache.get(LocalCacheKey.historyStatus) == true) { return; } - Duration progress = meeduPlayerController.position.value; + Duration progress = plPlayerController.position.value; await VideoHttp.heartBeat( bvid: bvid, cid: cid, diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index fff3f42a..7f6a3572 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -595,11 +595,11 @@ InlineSpan buildContent( ), recognizer: TapGestureRecognizer() ..onTap = () { - Get.find(tag: Get.arguments['heroTag']) - .meeduPlayerController - .seekTo( - Duration(seconds: Utils.duration(matchStr)), - ); + // Get.find(tag: Get.arguments['heroTag']) + // .meeduPlayerController + // .seekTo( + // Duration(seconds: Utils.duration(matchStr)), + // ); }, ), ); diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 5fcaed58..056c7629 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -1,7 +1,6 @@ import 'dart:async'; import 'package:extended_nested_scroll_view/extended_nested_scroll_view.dart'; -import 'package:flutter_meedu_media_kit/meedu_player.dart'; import 'package:get/get.dart'; import 'package:flutter/material.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; @@ -11,6 +10,7 @@ import 'package:pilipala/pages/video/detail/reply/index.dart'; import 'package:pilipala/pages/video/detail/controller.dart'; import 'package:pilipala/pages/video/detail/introduction/index.dart'; import 'package:pilipala/pages/video/detail/related/index.dart'; +import 'package:pilipala/plugin/pl_player/index.dart'; import 'widgets/app_bar.dart'; @@ -27,21 +27,20 @@ class _VideoDetailPageState extends State with TickerProviderStateMixin, RouteAware { final VideoDetailController videoDetailController = Get.put(VideoDetailController(), tag: Get.arguments['heroTag']); - MeeduPlayerController? _meeduPlayerController; + PlPlayerController? plPlayerController; final ScrollController _extendNestCtr = ScrollController(); late StreamController appbarStream; - StreamSubscription? _playerEventSubs; bool isPlay = false; - PlayerStatus playerStatus = PlayerStatus.paused; + PlayerStatus playerStatus = PlayerStatus.playing; bool isShowCover = true; double doubleOffset = 0; @override void initState() { super.initState(); - _meeduPlayerController = videoDetailController.meeduPlayerController; - _playerEventSubs = _meeduPlayerController!.onPlayerStatusChanged.listen( + plPlayerController = videoDetailController.plPlayerController; + plPlayerController!.onPlayerStatusChanged.listen( (PlayerStatus status) { videoDetailController.markHeartBeat(); playerStatus = status; @@ -70,24 +69,15 @@ class _VideoDetailPageState extends State ); } - Future _meeduDispose() async { - if (_meeduPlayerController != null) { - _playerEventSubs?.cancel(); - await _meeduPlayerController!.dispose(); - _meeduPlayerController = null; - // The next line disables the wakelock again. - } - } - void continuePlay() async { await _extendNestCtr.animateTo(0, duration: const Duration(milliseconds: 500), curve: Curves.easeInOut); - _meeduPlayerController!.play(); + plPlayerController!.play(); } @override void dispose() { - videoDetailController.meeduPlayerController.dispose(); + plPlayerController!.dispose(); if (videoDetailController.timer != null) { videoDetailController.timer!.cancel(); } @@ -97,9 +87,6 @@ class _VideoDetailPageState extends State @override // 离开当前页面时 void didPushNext() async { - if (!_meeduPlayerController!.pipAvailable.value) { - _meeduPlayerController!.pause(); - } if (videoDetailController.timer!.isActive) { videoDetailController.timer!.cancel(); } @@ -111,7 +98,7 @@ class _VideoDetailPageState extends State void didPopNext() async { if (_extendNestCtr.position.pixels == 0) { await Future.delayed(const Duration(milliseconds: 300)); - _meeduPlayerController!.play(); + plPlayerController!.play(); } if (!videoDetailController.timer!.isActive) { videoDetailController.loopHeartBeat(); @@ -167,34 +154,11 @@ class _VideoDetailPageState extends State tag: videoDetailController.heroTag, child: Stack( children: [ - AspectRatio( - aspectRatio: 16 / 9, - child: MeeduVideoPlayer( - controller: _meeduPlayerController!, - header: (BuildContext context, - MeeduPlayerController - meeduPlayerController, - Responsive responsive) { - return AppBar( - toolbarHeight: 40, - backgroundColor: Colors.transparent, - primary: false, - elevation: 0, - scrolledUnderElevation: 0, - foregroundColor: Colors.white, - leading: IconButton( - onPressed: () { - Get.back(); - }, - icon: const Icon( - Icons.arrow_back_ios, - size: 19, - ), - ), - ); - }, - ), - ), + if (plPlayerController! + .videoPlayerController != + null) + PLVideoPlayer( + controller: plPlayerController!), Visibility( visible: isShowCover, child: Positioned( @@ -305,8 +269,8 @@ class _VideoDetailPageState extends State builder: ((context, snapshot) { return ScrollAppBar( snapshot.data!.toDouble(), - continuePlay, - playerStatus, + () {}, + playerStatus != PlayerStatus.playing, null, ); }), diff --git a/lib/pages/video/detail/widgets/app_bar.dart b/lib/pages/video/detail/widgets/app_bar.dart index 05730d2c..d070fdd7 100644 --- a/lib/pages/video/detail/widgets/app_bar.dart +++ b/lib/pages/video/detail/widgets/app_bar.dart @@ -1,10 +1,9 @@ import 'package:flutter/material.dart'; -import 'package:flutter_meedu_media_kit/meedu_player.dart'; class ScrollAppBar extends StatelessWidget { final double scrollVal; final Function callback; - final PlayerStatus playerStatus; + final bool playerStatus; const ScrollAppBar( this.scrollVal, @@ -34,18 +33,9 @@ class ScrollAppBar extends StatelessWidget { centerTitle: true, title: TextButton( onPressed: () => callback(), - child: Row( + child: const Row( mainAxisSize: MainAxisSize.min, - children: [ - const Icon(Icons.play_arrow_rounded), - Text( - playerStatus == PlayerStatus.paused - ? '继续播放' - : playerStatus == PlayerStatus.completed - ? '重新播放' - : '播放中', - ) - ], + children: [Icon(Icons.play_arrow_rounded), Text('继续播放')], ), ), actions: [ diff --git a/lib/plugin/pl_player/controller.dart b/lib/plugin/pl_player/controller.dart new file mode 100644 index 00000000..94b5354b --- /dev/null +++ b/lib/plugin/pl_player/controller.dart @@ -0,0 +1,645 @@ +import 'dart:async'; +import 'dart:typed_data'; + +import 'package:flutter/painting.dart'; +import 'package:get/get.dart'; +import 'package:hive/hive.dart'; +import 'package:media_kit/media_kit.dart'; +import 'package:media_kit_video/media_kit_video.dart'; +import 'package:pilipala/plugin/pl_player/models/data_source.dart'; +import 'package:pilipala/utils/storage.dart'; +import 'package:screen_brightness/screen_brightness.dart'; +import 'package:universal_platform/universal_platform.dart'; +import 'package:volume_controller/volume_controller.dart'; +import 'package:wakelock_plus/wakelock_plus.dart'; + +import 'models/data_status.dart'; +import 'models/play_status.dart'; + +Box videoStorage = GStrorage.video; + +class PlPlayerController { + Player? _videoPlayerController; + VideoController? _videoController; + + // 流事件 监听播放状态变化 + StreamSubscription? _playerEventSubs; + + /// [playerStatus] has a [status] observable + final PlPlayerStatus playerStatus = PlPlayerStatus(); + + /// + final PlPlayerDataStatus dataStatus = PlPlayerDataStatus(); + + bool controlsEnabled = true; + + /// 响应数据 + // 播放位置 + final Rx _position = Rx(Duration.zero); + final Rx _sliderPosition = Rx(Duration.zero); + final Rx _duration = Rx(Duration.zero); + final Rx _buffered = Rx(Duration.zero); + + final Rx _playbackSpeed = 1.0.obs; + final Rx _currentVolume = 1.0.obs; + final Rx _currentBrightness = 0.0.obs; + + final Rx _mute = false.obs; + final Rx _showControls = false.obs; + final Rx _showVolumeStatus = false.obs; + final Rx _showBrightnessStatus = false.obs; + final Rx _doubleSpeedStatus = false.obs; + final Rx _controlsClose = false.obs; + + Rx videoFitChanged = false.obs; + final Rx _videoFit = Rx(BoxFit.fill); + + /// + bool _isSliderMoving = false; + PlaylistMode _looping = PlaylistMode.none; + bool _autoPlay = false; + final bool _listenersInitialized = false; + + Timer? _timer; + Timer? _timerForSeek; + Timer? _timerForVolume; + Timer? _timerForShowingVolume; + Timer? _timerForGettingVolume; + Timer? timerForTrackingMouse; + Timer? videoFitChangedTimer; + + // final Durations durations; + + List fits = [ + BoxFit.contain, + BoxFit.cover, + BoxFit.fill, + BoxFit.fitHeight, + BoxFit.fitWidth, + BoxFit.scaleDown + ]; + + /// 数据加载监听 + Stream get onDataStatusChanged => dataStatus.status.stream; + + /// 播放状态监听 + Stream get onPlayerStatusChanged => playerStatus.status.stream; + + /// 视频时长 + Rx get duration => _duration; + Stream get onDurationChanged => _duration.stream; + + /// 视频当前播放位置 + Rx get position => _position; + Stream get onPositionChanged => _position.stream; + + /// 视频播放速度 + double get playbackSpeed => _playbackSpeed.value; + + /// 视频缓冲 + Rx get buffered => _buffered; + Stream get onBufferedChanged => _buffered.stream; + + // 视频静音 + Rx get mute => _mute; + Stream get onMuteChanged => _mute.stream; + + /// [videoPlayerController] instace of Player + Player? get videoPlayerController => _videoPlayerController; + + /// [videoController] instace of Player + VideoController? get videoController => _videoController; + + /// 进度条位置及监听 + Rx get sliderPosition => _sliderPosition; + Stream get onSliderPositionChanged => _sliderPosition.stream; + + /// 是否展示控制条及监听 + Rx get showControls => _showControls; + Stream get onShowControlsChanged => _showControls.stream; + + /// 音量控制条展示/隐藏 + Rx get showVolumeStatus => _showVolumeStatus; + Stream get onShowVolumeStatusChanged => _showVolumeStatus.stream; + + /// 亮度控制条展示/隐藏 + Rx get showBrightnessStatus => _showBrightnessStatus; + Stream get onShowBrightnessStatusChanged => + _showBrightnessStatus.stream; + + /// 音量控制条 + Rx get volume => _currentVolume; + Stream get onVolumeChanged => _currentVolume.stream; + + /// 亮度控制条 + Rx get brightness => _currentBrightness; + Stream get onBrightnessChanged => _currentBrightness.stream; + + /// 是否循环 + PlaylistMode get looping => _looping; + + /// 是否自动播放 + bool get autoplay => _autoPlay; + + /// 视频比例 + Rx get videoFit => _videoFit; + + /// 是否长按倍速 + Rx get doubleSpeedStatus => _doubleSpeedStatus; + + Rx isBuffering = true.obs; + + Rx get controlsClose => _controlsClose; + + PlPlayerController({ + this.controlsEnabled = true, + this.fits = const [ + BoxFit.contain, + BoxFit.cover, + BoxFit.fill, + BoxFit.fitHeight, + BoxFit.fitWidth, + BoxFit.scaleDown + ], + }) { + controlsEnabled = controlsEnabled; + _playerEventSubs = onPlayerStatusChanged.listen((PlayerStatus status) { + if (status == PlayerStatus.playing) { + WakelockPlus.enable(); + } else { + WakelockPlus.enable(); + } + }); + } + + // 初始化资源 + Future setDataSource( + DataSource dataSource, { + bool autoplay = true, + // 默认不循环 + PlaylistMode looping = PlaylistMode.single, + // 初始化播放位置 + Duration seekTo = Duration.zero, + // 初始化播放速度 + double speed = 1.0, + // 硬件加速 + bool enableHA = true, + double? width, + double? height, + Duration? duration, + }) async { + try { + _autoPlay = autoplay; + _looping = looping; + // 初始化视频时长 + _duration.value = duration ?? Duration.zero; + // 初始化视频倍速 + _playbackSpeed.value = speed; + dataStatus.status.value = DataStatus.loading; + + if (_videoPlayerController != null && + _videoPlayerController!.state.playing) { + await pause(notify: false); + } + + _videoPlayerController = await _createVideoController( + dataSource, _looping, enableHA, width, height); + + _duration.value = _videoPlayerController!.state.duration; + dataStatus.status.value = DataStatus.loaded; + + await _initializePlayer(seekTo: seekTo); + + // listen the video player events + if (!_listenersInitialized) { + startListeners(); + } + } catch (err) { + dataStatus.status.value = DataStatus.error; + print('plPlayer err: $err'); + } + } + + // 配置播放器 + Future _createVideoController( + DataSource dataSource, + PlaylistMode looping, + bool enableHA, + double? width, + double? height, + ) async { + Player player = _videoPlayerController ?? + Player( + configuration: const PlayerConfiguration( + // 默认缓存 5M 大小 + bufferSize: 5 * 1024 * 1024, + ), + ); + + var pp = player.platform as NativePlayer; + + // 音轨 + if (dataSource.audioSource != '' && dataSource.audioSource != null) { + await pp.setProperty( + 'audio-files', + UniversalPlatform.isWindows + ? dataSource.audioSource!.replaceAll(';', '\\;') + : dataSource.audioSource!.replaceAll(':', '\\:'), + ); + } + + // 字幕 + if (dataSource.subFiles != '' && dataSource.subFiles != null) { + await pp.setProperty( + 'sub-files', + UniversalPlatform.isWindows + ? dataSource.subFiles!.replaceAll(';', '\\;') + : dataSource.subFiles!.replaceAll(':', '\\:'), + ); + await pp.setProperty("subs-with-matching-audio", "no"); + await pp.setProperty("sub-forced-only", "yes"); + await pp.setProperty("blend-subtitles", "video"); + } + + _videoController = _videoController ?? + VideoController( + player, + configuration: VideoControllerConfiguration( + enableHardwareAcceleration: enableHA, + ), + ); + + player.setPlaylistMode(looping); + + if (dataSource.type == DataSourceType.asset) { + final assetUrl = dataSource.videoSource!.startsWith("asset://") + ? dataSource.videoSource! + : "asset://${dataSource.videoSource!}"; + player.open( + Media(assetUrl, httpHeaders: dataSource.httpHeaders), + play: false, + ); + } else if (dataSource.type == DataSourceType.network) { + player.open( + Media(dataSource.videoSource!, httpHeaders: dataSource.httpHeaders), + play: false, + ); + // 音轨 + // player.setAudioTrack( + // AudioTrack.uri(dataSource.audioSource!), + // ); + } else { + player.open( + Media(dataSource.file!.path, httpHeaders: dataSource.httpHeaders), + play: false, + ); + } + + return player; + } + + // 开始播放 + Future _initializePlayer({ + Duration seekTo = Duration.zero, + }) async { + // 跳转播放 + if (seekTo != Duration.zero) { + await this.seekTo(seekTo); + } + + // 设置倍速 + if (_playbackSpeed.value != 1.0) { + await setPlaybackSpeed(_playbackSpeed.value); + } + + // if (_looping) { + // await setLooping(_looping); + // } + + // 自动播放 + if (_autoPlay) { + await play(); + } + } + + List subscriptions = []; + + /// 播放事件监听 + void startListeners() { + subscriptions.addAll( + [ + videoPlayerController!.stream.playing.listen((event) { + if (event) { + playerStatus.status.value = PlayerStatus.playing; + } else { + // playerStatus.status.value = PlayerStatus.paused; + } + }), + videoPlayerController!.stream.completed.listen((event) { + if (event) { + playerStatus.status.value = PlayerStatus.completed; + } else { + // playerStatus.status.value = PlayerStatus.playing; + } + }), + videoPlayerController!.stream.position.listen((event) { + _position.value = event; + if (!_isSliderMoving) { + _sliderPosition.value = event; + } + }), + videoPlayerController!.stream.duration.listen((event) { + duration.value = event; + }), + videoPlayerController!.stream.buffer.listen((event) { + _buffered.value = event; + }), + videoPlayerController!.stream.buffering.listen((event) { + isBuffering.value = event; + }), + // videoPlayerController!.stream.volume.listen((event) { + // if (!mute.value && _volumeBeforeMute != event) { + // _volumeBeforeMute = event / 100; + // } + // }), + ], + ); + } + + /// 移除事件监听 + void removeListeners() { + for (final s in subscriptions) { + s.cancel(); + } + } + + /// 跳转至指定位置 + Future seekTo(Duration position) async { + // if (position >= duration.value) { + // position = duration.value - const Duration(milliseconds: 100); + // } + if (position < Duration.zero) { + position = Duration.zero; + } + _position.value = position; + if (duration.value.inSeconds != 0) { + // await _videoPlayerController!.stream.buffer.first; + await _videoPlayerController?.seek(position); + // if (playerStatus.stopped) { + // play(); + // } + } else { + _timerForSeek?.cancel(); + _timerForSeek = + Timer.periodic(const Duration(milliseconds: 200), (Timer t) async { + //_timerForSeek = null; + if (duration.value.inSeconds != 0) { + await _videoPlayerController?.seek(position); + // if (playerStatus.stopped) { + // play(); + // } + t.cancel(); + //_timerForSeek = null; + } + }); + } + } + + /// 设置倍速 + Future setPlaybackSpeed(double speed) async { + await _videoPlayerController?.setRate(speed); + _playbackSpeed.value = speed; + } + + /// 设置倍速 + Future togglePlaybackSpeed() async { + List allowedSpeeds = [0.25, 0.5, 0.75, 1.0, 1.25, 1.50, 1.75, 2.0]; + if (allowedSpeeds.indexOf(_playbackSpeed.value) < + allowedSpeeds.length - 1) { + setPlaybackSpeed( + allowedSpeeds[allowedSpeeds.indexOf(_playbackSpeed.value) + 1]); + } else { + setPlaybackSpeed(allowedSpeeds[0]); + } + } + + /// 播放视频 + Future play({bool repeat = false, bool hideControls = true}) async { + // repeat为true,将从头播放 + if (repeat) { + await seekTo(Duration.zero); + } + await _videoPlayerController?.play(); + + await getCurrentVolume(); + await getCurrentBrightness(); + + playerStatus.status.value = PlayerStatus.playing; + // screenManager.setOverlays(false); + + // 播放时自动隐藏控制条 + if (hideControls) { + _hideTaskControls(); + } + } + + /// 暂停播放 + Future pause({bool notify = true}) async { + await _videoPlayerController?.pause(); + playerStatus.status.value = PlayerStatus.paused; + } + + /// 更改播放状态 + Future togglePlay() async { + if (playerStatus.playing) { + pause(); + } else { + play(); + } + } + + /// 隐藏控制条 + void _hideTaskControls() { + _timer = Timer(const Duration(milliseconds: 3000), () { + if (!_isSliderMoving) { + controls = false; + } + _timer = null; + }); + } + + /// 调整播放时间 + onChangedSlider(double v) { + _sliderPosition.value = Duration(seconds: v.floor()); + } + + void onChangedSliderStart() { + _isSliderMoving = true; + } + + void onChangedSliderEnd() { + _isSliderMoving = false; + _hideTaskControls(); + } + + /// 音量 + Future getCurrentVolume() async { + _currentVolume.value = await VolumeController().getVolume(); + } + + Future setVolume(double volumeNew, + {bool videoPlayerVolume = false}) async { + if (volumeNew < 0.0) { + volumeNew = 0.0; + } else if (volumeNew > 1.0) { + volumeNew = 1.0; + } + if (volume.value == volumeNew) { + return; + } + volume.value = volumeNew; + + try { + VolumeController().setVolume(volumeNew, showSystemUI: false); + } catch (err) { + print(err); + } + } + + void volumeUpdated() { + showVolumeStatus.value = true; + _timerForShowingVolume?.cancel(); + _timerForShowingVolume = Timer(const Duration(seconds: 1), () { + showVolumeStatus.value = false; + }); + } + + /// 亮度 + Future getCurrentBrightness() async { + try { + _currentBrightness.value = await ScreenBrightness().current; + } catch (e) { + throw 'Failed to get current brightness'; + //return 0; + } + } + + Future setBrightness(double brightnes) async { + try { + brightness.value = brightnes; + ScreenBrightness().setScreenBrightness(brightnes); + setVideoBrightness(); + } catch (e) { + throw 'Failed to set brightness'; + } + } + + Future resetBrightness() async { + try { + await ScreenBrightness().resetScreenBrightness(); + } catch (e) { + throw 'Failed to reset brightness'; + } + } + + /// Toggle Change the videofit accordingly + void toggleVideoFit() { + videoFitChangedTimer?.cancel(); + videoFitChanged.value = true; + if (fits.indexOf(_videoFit.value) < fits.length - 1) { + _videoFit.value = fits[fits.indexOf(_videoFit.value) + 1]; + } else { + _videoFit.value = fits[0]; + } + videoFitChangedTimer = Timer(const Duration(seconds: 1), () { + videoFitChangedTimer = null; + videoFitChanged.value = false; + }); + print(_videoFit.value); + } + + /// Change Video Fit accordingly + void onVideoFitChange(BoxFit fit) { + _videoFit.value = fit; + } + + /// 缓存fit + Future setVideoFit() async { + videoStorage.put(VideoBoxKey.videoBrightness, _videoFit.value.name); + } + + /// 读取fit + Future getVideoFit() async { + String fitValue = videoStorage.get(VideoBoxKey.videoBrightness, + defaultValue: 'fitHeight'); + _videoFit.value = fits.firstWhere((element) => element.name == fitValue); + } + + /// 缓存亮度 + Future setVideoBrightness() async {} + + /// 读取亮度 + Future getVideoBrightness() async { + double brightnessValue = + videoStorage.get(VideoBoxKey.videoBrightness, defaultValue: 0.5); + setBrightness(brightnessValue); + } + + set controls(bool visible) { + _showControls.value = visible; + _timer?.cancel(); + if (visible) { + _hideTaskControls(); + } + } + + /// 设置长按倍速状态 + void setDoubleSpeedStatus(bool val) { + _doubleSpeedStatus.value = val; + } + + /// 关闭控制栏 + void onCloseControl(bool val) { + _controlsClose.value = val; + showControls.value = !val; + } + + /// 截屏 + Future screenshot() async { + final Uint8List? screenshot = + await _videoPlayerController!.screenshot(format: 'image/png'); + return screenshot; + } + + Future videoPlayerClosed() async { + _timer?.cancel(); + _timerForVolume?.cancel(); + _timerForGettingVolume?.cancel(); + timerForTrackingMouse?.cancel(); + _timerForSeek?.cancel(); + videoFitChangedTimer?.cancel(); + } + + Future dispose() async { + _timer?.cancel(); + _timerForVolume?.cancel(); + _timerForGettingVolume?.cancel(); + timerForTrackingMouse?.cancel(); + _timerForSeek?.cancel(); + videoFitChangedTimer?.cancel(); + _position.close(); + _playerEventSubs?.cancel(); + _sliderPosition.close(); + _duration.close(); + _buffered.close(); + _showControls.close(); + _controlsClose.close(); + + playerStatus.status.close(); + dataStatus.status.close(); + + removeListeners(); + await _videoPlayerController?.dispose(); + _videoPlayerController = null; + } +} diff --git a/lib/plugin/pl_player/index.dart b/lib/plugin/pl_player/index.dart new file mode 100644 index 00000000..cab7264e --- /dev/null +++ b/lib/plugin/pl_player/index.dart @@ -0,0 +1,7 @@ +library pl_player; + +export './controller.dart'; +export './view.dart'; +export './models/data_source.dart'; +export './models/play_status.dart'; +export './models/data_status.dart'; diff --git a/lib/plugin/pl_player/models/data_source.dart b/lib/plugin/pl_player/models/data_source.dart new file mode 100644 index 00000000..2fbc65b3 --- /dev/null +++ b/lib/plugin/pl_player/models/data_source.dart @@ -0,0 +1,55 @@ +import 'dart:io'; + +/// The way in which the video was originally loaded. +/// +/// This has nothing to do with the video's file type. It's just the place +/// from which the video is fetched from. +enum DataSourceType { + /// The video was included in the app's asset files. + asset, + + /// The video was downloaded from the internet. + network, + + /// The video was loaded off of the local filesystem. + file, + + /// The video is available via contentUri. Android only. + contentUri, +} + +class DataSource { + File? file; + String? videoSource; + String? audioSource; + String? subFiles; + DataSourceType type; + Map? httpHeaders; // for headers + DataSource({ + this.file, + this.videoSource, + this.audioSource, + this.subFiles, + required this.type, + this.httpHeaders, + }) : assert((type == DataSourceType.file && file != null) || + videoSource != null); + + DataSource copyWith({ + File? file, + String? videoSource, + String? audioSource, + String? subFiles, + DataSourceType? type, + Map? httpHeaders, + }) { + return DataSource( + file: file ?? this.file, + videoSource: videoSource ?? this.videoSource, + audioSource: audioSource ?? this.audioSource, + subFiles: subFiles ?? this.subFiles, + type: type ?? this.type, + httpHeaders: httpHeaders ?? this.httpHeaders, + ); + } +} diff --git a/lib/plugin/pl_player/models/data_status.dart b/lib/plugin/pl_player/models/data_status.dart new file mode 100644 index 00000000..a2cbb163 --- /dev/null +++ b/lib/plugin/pl_player/models/data_status.dart @@ -0,0 +1,12 @@ +import 'package:get/get.dart'; + +enum DataStatus { none, loading, loaded, error } + +class PlPlayerDataStatus { + Rx status = Rx(DataStatus.none); + + bool get none => status.value == DataStatus.none; + bool get loading => status.value == DataStatus.loading; + bool get loaded => status.value == DataStatus.loaded; + bool get error => status.value == DataStatus.error; +} diff --git a/lib/plugin/pl_player/models/play_status.dart b/lib/plugin/pl_player/models/play_status.dart new file mode 100644 index 00000000..2af09a61 --- /dev/null +++ b/lib/plugin/pl_player/models/play_status.dart @@ -0,0 +1,19 @@ +import 'package:get/get.dart'; + +enum PlayerStatus { completed, playing, paused } + +class PlPlayerStatus { + Rx status = Rx(PlayerStatus.paused); + + bool get playing { + return status.value == PlayerStatus.playing; + } + + bool get paused { + return status.value == PlayerStatus.paused; + } + + bool get completed { + return status.value == PlayerStatus.completed; + } +} diff --git a/lib/plugin/pl_player/utils.dart b/lib/plugin/pl_player/utils.dart new file mode 100644 index 00000000..44f62da7 --- /dev/null +++ b/lib/plugin/pl_player/utils.dart @@ -0,0 +1,29 @@ +String printDuration(Duration? duration) { + if (duration == null) return "--:--"; + + /*String twoDigits(int n) { + if (n >= 10||n < 0) return "$n"; + return "0$n"; + }*/ + String twoDigits(int n) => n.toString().padLeft(2, "0"); + + String twoDigitMinutes = twoDigits(duration.inMinutes).replaceAll("-", ""); + String twoDigitSeconds = + twoDigits(duration.inSeconds.remainder(60)).replaceAll("-", ""); + //customDebugPrint(duration.inSeconds.remainder(60)); + return "$twoDigitMinutes:$twoDigitSeconds"; +} + +String printDurationWithHours(Duration? duration) { + if (duration == null) return "--:--:--"; + + String twoDigits(int n) { + if (n >= 10) return "$n"; + return "0$n"; + } + + String twoDigitHours = twoDigits(duration.inHours); + String twoDigitMinutes = twoDigits(duration.inMinutes.remainder(60)); + String twoDigitSeconds = twoDigits(duration.inSeconds.remainder(60)); + return "$twoDigitHours:$twoDigitMinutes:$twoDigitSeconds"; +} diff --git a/lib/plugin/pl_player/view.dart b/lib/plugin/pl_player/view.dart new file mode 100644 index 00000000..854c7277 --- /dev/null +++ b/lib/plugin/pl_player/view.dart @@ -0,0 +1,252 @@ +import 'package:audio_video_progress_bar/audio_video_progress_bar.dart'; +import 'package:flutter/material.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; +import 'package:get/get.dart'; +import 'package:media_kit_video/media_kit_video.dart'; +import 'package:pilipala/common/widgets/app_bar_ani.dart'; +import 'package:pilipala/plugin/pl_player/controller.dart'; +import 'package:pilipala/plugin/pl_player/models/play_status.dart'; +import 'package:pilipala/utils/feed_back.dart'; + +import 'widgets/bottom_control.dart'; +import 'widgets/common_btn.dart'; +import 'widgets/header_control.dart'; + +class PLVideoPlayer extends StatefulWidget { + final PlPlayerController controller; + + const PLVideoPlayer({required this.controller, super.key}); + + @override + State createState() => _PLVideoPlayerState(); +} + +class _PLVideoPlayerState extends State + with TickerProviderStateMixin { + late AnimationController animationController; + late VideoController videoController; + + @override + void initState() { + super.initState(); + animationController = AnimationController( + vsync: this, duration: const Duration(milliseconds: 300)); + videoController = widget.controller.videoController!; + } + + @override + void dispose() { + super.dispose(); + animationController.dispose(); + } + + @override + Widget build(BuildContext context) { + final _ = widget.controller; + Color colorTheme = Theme.of(context).colorScheme.primary; + TextStyle subTitleStyle = const TextStyle( + height: 1.5, + fontSize: 40.0, + letterSpacing: 0.0, + wordSpacing: 0.0, + color: Color(0xffffffff), + fontWeight: FontWeight.normal, + backgroundColor: Color(0xaa000000), + ); + return Stack( + clipBehavior: Clip.hardEdge, + fit: StackFit.passthrough, + children: [ + Video( + controller: videoController, + controls: NoVideoControls, + subtitleViewConfiguration: SubtitleViewConfiguration( + style: subTitleStyle, + textAlign: TextAlign.center, + padding: const EdgeInsets.all(24.0), + ), + ), + Padding( + padding: const EdgeInsets.only(top: 20, bottom: 15), + child: GestureDetector( + onTap: () { + _.controls = !_.showControls.value; + }, + onDoubleTap: () { + if (_.playerStatus.status.value == PlayerStatus.playing) { + _.togglePlay(); + } else { + _.play(); + } + }, + onLongPressStart: (detail) { + feedBack(); + double currentSpeed = _.playbackSpeed; + _.setDoubleSpeedStatus(true); + _.setPlaybackSpeed(currentSpeed * 2); + }, + onLongPressEnd: (details) { + double currentSpeed = _.playbackSpeed; + _.setDoubleSpeedStatus(false); + _.setPlaybackSpeed(currentSpeed / 2); + }, + // 水平位置 快进 + onHorizontalDragUpdate: (DragUpdateDetails details) {}, + onHorizontalDragEnd: (DragEndDetails details) {}, + // 垂直方向 音量/亮度调节 + onVerticalDragUpdate: (DragUpdateDetails details) {}, + onVerticalDragEnd: (DragEndDetails details) {}), + ), + if (_.controlsEnabled) + Obx( + () => Column( + children: [ + ClipRect( + clipBehavior: Clip.hardEdge, + child: AppBarAni( + controller: animationController, + visible: !_.controlsClose.value && _.showControls.value, + position: 'top', + child: HeaderControl(controller: widget.controller), + ), + ), + const Spacer(), + ClipRect( + clipBehavior: Clip.hardEdge, + child: AppBarAni( + controller: animationController, + visible: !_.controlsClose.value && _.showControls.value, + position: 'bottom', + child: BottomControl(controller: widget.controller), + ), + ), + ], + ), + ), + // 进度条 + Obx( + () { + final int value = _.sliderPosition.value.inSeconds; + final int max = _.duration.value.inSeconds; + final int buffer = _.buffered.value.inSeconds; + if (value > max || max <= 0) { + return Container(); + } + return Positioned( + bottom: -4, + left: 0, + right: 0, + child: SlideTransition( + position: Tween( + begin: Offset.zero, + end: const Offset(0, -1), + ).animate(CurvedAnimation( + parent: animationController, + curve: Curves.easeInOut, + )), + child: ProgressBar( + progress: Duration(seconds: value), + buffered: Duration(seconds: buffer), + total: Duration(seconds: max), + progressBarColor: colorTheme, + baseBarColor: Colors.white.withOpacity(0.2), + bufferedBarColor: + Theme.of(context).colorScheme.primary.withOpacity(0.4), + timeLabelLocation: TimeLabelLocation.none, + thumbColor: colorTheme, + barHeight: 3, + thumbRadius: 0.0, + onDragStart: (duration) { + _.onChangedSliderStart(); + }, + onDragEnd: () { + _.onChangedSliderEnd(); + }, + // onDragUpdate: (details) { + // print(details); + // }, + onSeek: (duration) { + print(duration); + _.onChangedSlider(duration.inSeconds.toDouble()); + _.seekTo(duration); + }, + )), + ); + }, + ), + // 长按倍速 + Obx( + () => Align( + alignment: Alignment.topCenter, + child: FractionalTranslation( + translation: const Offset(0.0, 1.5), // 上下偏移量(负数向上偏移) + child: Visibility( + visible: _.doubleSpeedStatus.value, + child: const Text( + '** 倍速中 **', + style: TextStyle( + fontSize: 13, + backgroundColor: Color(0xaa000000), + color: Colors.white, + ), + ), + ), + ), + ), + ), + // 锁 + Obx( + () => Align( + alignment: Alignment.centerLeft, + child: FractionalTranslation( + translation: const Offset(0.5, 0.0), + child: Visibility( + visible: _.showControls.value, + child: ComBtn( + icon: Icon( + _.controlsClose.value + ? FontAwesomeIcons.lock + : FontAwesomeIcons.lockOpen, + size: 15, + color: Colors.white, + ), + fuc: () => _.onCloseControl(!_.controlsClose.value), + ), + ), + ), + ), + ), + // + Obx(() { + if (_.dataStatus.loading || _.isBuffering.value) { + return Center( + child: Image.asset( + 'assets/images/loading.gif', + height: 25, + ), + ); + } else { + return Container(); + } + }), + ], + ); + } +} + +class MSliderTrackShape extends RoundedRectSliderTrackShape { + @override + Rect getPreferredRect({ + required RenderBox parentBox, + Offset offset = Offset.zero, + SliderThemeData? sliderTheme, + bool isEnabled = false, + bool isDiscrete = false, + }) { + final double trackLeft = offset.dx; + final double trackWidth = parentBox.size.width; + return Rect.fromLTWH(trackLeft, -1, trackWidth, 3); + } +} + +class PLPlayerCtr extends GetxController {} diff --git a/lib/plugin/pl_player/widgets/app_bar_ani.dart b/lib/plugin/pl_player/widgets/app_bar_ani.dart new file mode 100644 index 00000000..9a3af267 --- /dev/null +++ b/lib/plugin/pl_player/widgets/app_bar_ani.dart @@ -0,0 +1,57 @@ +import 'package:flutter/material.dart'; + +class AppBarAni extends StatelessWidget implements PreferredSizeWidget { + const AppBarAni({ + required this.child, + required this.controller, + required this.visible, + this.position, + Key? key, + }) : super(key: key); + + final PreferredSizeWidget child; + final AnimationController controller; + final bool visible; + final String? position; + + @override + Size get preferredSize => child.preferredSize; + + @override + Widget build(BuildContext context) { + visible ? controller.reverse() : controller.forward(); + return SlideTransition( + position: Tween( + begin: Offset.zero, + end: Offset(0, position! == 'top' ? -1 : 1), + ).animate(CurvedAnimation( + parent: controller, + curve: Curves.linear, + )), + child: Container( + decoration: BoxDecoration( + gradient: position! == 'top' + ? const LinearGradient( + begin: Alignment.bottomCenter, + end: Alignment.topCenter, + colors: [ + Colors.transparent, + Colors.black54, + ], + tileMode: TileMode.mirror, + ) + : const LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + Colors.transparent, + Colors.black54, + ], + tileMode: TileMode.mirror, + ), + ), + child: child, + ), + ); + } +} diff --git a/lib/plugin/pl_player/widgets/bottom_control.dart b/lib/plugin/pl_player/widgets/bottom_control.dart new file mode 100644 index 00000000..e1a50768 --- /dev/null +++ b/lib/plugin/pl_player/widgets/bottom_control.dart @@ -0,0 +1,149 @@ +import 'package:audio_video_progress_bar/audio_video_progress_bar.dart'; +import 'package:flutter/material.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; +import 'package:get/get.dart'; +import 'package:pilipala/plugin/pl_player/index.dart'; + +import '../utils.dart'; +import 'common_btn.dart'; + +class BottomControl extends StatelessWidget implements PreferredSizeWidget { + final PlPlayerController? controller; + const BottomControl({this.controller, Key? key}) : super(key: key); + + @override + Size get preferredSize => const Size(double.infinity, kToolbarHeight); + + @override + Widget build(BuildContext context) { + Color colorTheme = Theme.of(context).colorScheme.primary; + final _ = controller!; + const textStyle = TextStyle( + color: Colors.white, + fontSize: 12, + ); + + return AppBar( + backgroundColor: Colors.transparent, + foregroundColor: Colors.white, + elevation: 0, + scrolledUnderElevation: 0, + primary: false, + toolbarHeight: 73, + automaticallyImplyLeading: false, + titleSpacing: 14, + title: Column( + children: [ + const SizedBox(height: 23), + Obx( + () { + final int value = _.sliderPosition.value.inSeconds; + final int max = _.duration.value.inSeconds; + final int buffer = _.buffered.value.inSeconds; + if (value > max || max <= 0) { + return Container(); + } + return ProgressBar( + progress: Duration(seconds: value), + buffered: Duration(seconds: buffer), + total: Duration(seconds: max), + progressBarColor: colorTheme, + baseBarColor: Colors.white.withOpacity(0.2), + bufferedBarColor: colorTheme.withOpacity(0.4), + timeLabelLocation: TimeLabelLocation.none, + thumbColor: colorTheme, + barHeight: 3.0, + thumbRadius: 5.5, + onDragStart: (duration) { + _.onChangedSliderStart(); + }, + onSeek: (duration) { + _.onChangedSliderEnd(); + _.onChangedSlider(duration.inSeconds.toDouble()); + _.seekTo(Duration(seconds: duration.inSeconds)); + }, + ); + }, + ), + Row( + children: [ + Obx( + () => ComBtn( + icon: Icon( + _.playerStatus.paused + ? FontAwesomeIcons.play + : _.playerStatus.playing + ? FontAwesomeIcons.pause + : FontAwesomeIcons.rotateRight, + size: 15, + color: Colors.white, + ), + fuc: () => _.togglePlay(), + ), + ), + const SizedBox(width: 6), + // 播放时间 + Obx(() { + return Text( + _.duration.value.inMinutes >= 60 + ? printDurationWithHours(_.position.value) + : printDuration(_.position.value), + style: textStyle, + ); + }), + const SizedBox(width: 2), + const Text('/', style: textStyle), + const SizedBox(width: 2), + Obx( + () => Text( + _.duration.value.inMinutes >= 60 + ? printDurationWithHours(_.duration.value) + : printDuration(_.duration.value), + style: textStyle, + ), + ), + const Spacer(), + // 倍速 + Obx( + () => SizedBox( + width: 45, + height: 34, + child: TextButton( + style: ButtonStyle( + padding: MaterialStateProperty.all(EdgeInsets.zero), + ), + onPressed: () { + _.togglePlaybackSpeed(); + }, + child: Text( + '${_.playbackSpeed.toString()}X', + style: textStyle, + ), + ), + ), + ), + ComBtn( + icon: const Icon( + Icons.fit_screen_sharp, + size: 18, + color: Colors.white, + ), + fuc: () => _.toggleVideoFit(), + ), + const SizedBox(width: 4), + // 全屏 + ComBtn( + icon: const Icon( + FontAwesomeIcons.expand, + size: 15, + color: Colors.white, + ), + fuc: () => {}, + ), + ], + ), + ], + ), + ); + } +} diff --git a/lib/plugin/pl_player/widgets/common_btn.dart b/lib/plugin/pl_player/widgets/common_btn.dart new file mode 100644 index 00000000..5f33311c --- /dev/null +++ b/lib/plugin/pl_player/widgets/common_btn.dart @@ -0,0 +1,29 @@ +import 'package:flutter/material.dart'; + +class ComBtn extends StatelessWidget { + final Widget? icon; + final Function? fuc; + + const ComBtn({ + this.icon, + this.fuc, + super.key, + }); + + @override + Widget build(BuildContext context) { + return SizedBox( + width: 34, + height: 34, + child: IconButton( + style: ButtonStyle( + padding: MaterialStateProperty.all(EdgeInsets.zero), + ), + onPressed: () { + fuc!(); + }, + icon: icon!, + ), + ); + } +} diff --git a/lib/plugin/pl_player/widgets/header_control.dart b/lib/plugin/pl_player/widgets/header_control.dart new file mode 100644 index 00000000..ade38f80 --- /dev/null +++ b/lib/plugin/pl_player/widgets/header_control.dart @@ -0,0 +1,67 @@ +import 'package:flutter/material.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; +import 'package:get/get.dart'; +import 'package:pilipala/plugin/pl_player/index.dart'; + +import 'common_btn.dart'; + +class HeaderControl extends StatelessWidget implements PreferredSizeWidget { + final PlPlayerController? controller; + const HeaderControl({this.controller, Key? key}) : super(key: key); + + @override + Size get preferredSize => const Size(double.infinity, kToolbarHeight); + @override + Widget build(BuildContext context) { + final _ = controller!; + return AppBar( + backgroundColor: Colors.transparent, + foregroundColor: Colors.white, + elevation: 0, + scrolledUnderElevation: 0, + primary: false, + centerTitle: false, + automaticallyImplyLeading: false, + titleSpacing: 14, + title: Row( + children: [ + ComBtn( + icon: const Icon( + FontAwesomeIcons.arrowLeft, + size: 15, + color: Colors.white, + ), + fuc: () => Get.back(), + ), + const SizedBox(width: 4), + ComBtn( + icon: const Icon( + FontAwesomeIcons.house, + size: 15, + color: Colors.white, + ), + fuc: () => Get.back(), + ), + const Spacer(), + ComBtn( + icon: const Icon( + FontAwesomeIcons.cropSimple, + size: 15, + color: Colors.white, + ), + fuc: () => _.screenshot(), + ), + const SizedBox(width: 4), + ComBtn( + icon: const Icon( + FontAwesomeIcons.sliders, + size: 15, + color: Colors.white, + ), + fuc: () => _.screenshot(), + ), + ], + ), + ); + } +} diff --git a/lib/utils/storage.dart b/lib/utils/storage.dart index 36bd6bec..b39dcbfc 100644 --- a/lib/utils/storage.dart +++ b/lib/utils/storage.dart @@ -14,6 +14,7 @@ class GStrorage { static late final Box historyword; static late final Box localCache; static late final Box setting; + static late final Box video; static Future init() async { final dir = await getApplicationDocumentsDirectory(); @@ -48,6 +49,8 @@ class GStrorage { hotKeyword = await Hive.openBox('hotKeyword'); // 搜索历史 historyword = await Hive.openBox('historyWord'); + // 视频设置 + video = await Hive.openBox('video'); } } @@ -71,3 +74,12 @@ class LocalCacheKey { // 历史记录暂停状态 默认false static const String historyStatus = 'historyStatus'; } + +class VideoBoxKey { + // 视频比例 + static const String videoFit = 'videoFit'; + // 亮度 + static const String videoBrightness = 'videoBrightness'; + // 倍速 + static const String videoSpeed = 'videoSpeed'; +} diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index 65d4e6aa..82b739b5 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -7,33 +7,21 @@ #include "generated_plugin_registrant.h" #include -#include #include #include -#include #include -#include void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) dynamic_color_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "DynamicColorPlugin"); dynamic_color_plugin_register_with_registrar(dynamic_color_registrar); - g_autoptr(FlPluginRegistrar) flutter_meedu_media_kit_registrar = - fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterMeeduMediaKitPlugin"); - flutter_meedu_media_kit_plugin_register_with_registrar(flutter_meedu_media_kit_registrar); g_autoptr(FlPluginRegistrar) media_kit_libs_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "MediaKitLibsLinuxPlugin"); media_kit_libs_linux_plugin_register_with_registrar(media_kit_libs_linux_registrar); g_autoptr(FlPluginRegistrar) media_kit_video_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "MediaKitVideoPlugin"); media_kit_video_plugin_register_with_registrar(media_kit_video_registrar); - g_autoptr(FlPluginRegistrar) screen_retriever_registrar = - fl_plugin_registry_get_registrar_for_plugin(registry, "ScreenRetrieverPlugin"); - screen_retriever_plugin_register_with_registrar(screen_retriever_registrar); g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); - g_autoptr(FlPluginRegistrar) window_manager_registrar = - fl_plugin_registry_get_registrar_for_plugin(registry, "WindowManagerPlugin"); - window_manager_plugin_register_with_registrar(window_manager_registrar); } diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index a644aee9..047b2c8e 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -4,12 +4,9 @@ list(APPEND FLUTTER_PLUGIN_LIST dynamic_color - flutter_meedu_media_kit media_kit_libs_linux media_kit_video - screen_retriever url_launcher_linux - window_manager ) list(APPEND FLUTTER_FFI_PLUGIN_LIST diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index b11e254a..bc042145 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -8,33 +8,25 @@ import Foundation import connectivity_plus import device_info_plus import dynamic_color -import flutter_meedu_media_kit import media_kit_libs_macos_video import media_kit_video import package_info_plus import path_provider_foundation import screen_brightness_macos -import screen_retriever import share_plus -import shared_preferences_foundation import sqflite import wakelock_plus -import window_manager func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { ConnectivityPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlugin")) DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) DynamicColorPlugin.register(with: registry.registrar(forPlugin: "DynamicColorPlugin")) - FlutterMeeduMediaKitPlugin.register(with: registry.registrar(forPlugin: "FlutterMeeduMediaKitPlugin")) MediaKitLibsMacosVideoPlugin.register(with: registry.registrar(forPlugin: "MediaKitLibsMacosVideoPlugin")) MediaKitVideoPlugin.register(with: registry.registrar(forPlugin: "MediaKitVideoPlugin")) FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) ScreenBrightnessMacosPlugin.register(with: registry.registrar(forPlugin: "ScreenBrightnessMacosPlugin")) - ScreenRetrieverPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverPlugin")) SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) - SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) WakelockPlusMacosPlugin.register(with: registry.registrar(forPlugin: "WakelockPlusMacosPlugin")) - WindowManagerPlugin.register(with: registry.registrar(forPlugin: "WindowManagerPlugin")) } diff --git a/pubspec.lock b/pubspec.lock index c49e2ccd..00e97ad8 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -41,6 +41,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.11.0" + audio_video_progress_bar: + dependency: "direct main" + description: + name: audio_video_progress_bar + sha256: "67f3a5ea70d48b48caaf29f5a0606284a6aa3a393736daf9e82bec985d2f9b70" + url: "https://pub.dev" + source: hosted + version: "1.0.1" boolean_selector: dependency: transitive description: @@ -419,23 +427,6 @@ packages: description: flutter source: sdk version: "0.0.0" - flutter_meedu: - dependency: transitive - description: - name: flutter_meedu - sha256: "7be568c2df5036cd050358b589f2b3de3318562710f9a64ea364da5fd7eeafe7" - url: "https://pub.dev" - source: hosted - version: "8.3.1" - flutter_meedu_media_kit: - dependency: "direct main" - description: - path: package - ref: feature-custom - resolved-ref: d1d6d62f0059ec3501e21c9a94e72dae827162e9 - url: "https://github.com/guozhigq/flutter_meedu_media_kit.git" - source: git - version: "4.2.12" flutter_smart_dialog: dependency: "direct main" description: @@ -444,14 +435,6 @@ packages: url: "https://pub.dev" source: hosted version: "4.9.3+2" - flutter_spinkit: - dependency: transitive - description: - name: flutter_spinkit - sha256: b39c753e909d4796906c5696a14daf33639a76e017136c8d82bf3e620ce5bb8e - url: "https://pub.dev" - source: hosted - version: "5.2.0" flutter_test: dependency: "direct dev" description: flutter @@ -478,14 +461,6 @@ packages: url: "https://pub.dev" source: hosted version: "3.2.0" - fullscreen_window: - dependency: transitive - description: - name: fullscreen_window - sha256: fe3014f91bff16a82d142ba9d834980b8a84b4bb03347a92588d389ad92bd1d3 - url: "https://pub.dev" - source: hosted - version: "1.0.4" get: dependency: "direct main" description: @@ -671,23 +646,23 @@ packages: source: hosted version: "0.2.0" media_kit: - dependency: transitive + dependency: "direct main" description: name: media_kit - sha256: "1e6ea59b5f81d1db038ac5394c38cd3ad2f5e50bdf36f12154dee6fa04221bcb" + sha256: "272a9f1dd77ed57b48707fdb0ec0e4a048ef958feccc0d0dd751135fe924b63a" url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.1.1" media_kit_libs_android_video: - dependency: transitive + dependency: "direct main" description: name: media_kit_libs_android_video - sha256: b7dd06bdc615c7dc48c3718d81c1adfcb902be0aaa6310ed6a8caf524301553a + sha256: ddb0d26ecba72bf7117e37e29b6a50f4ba198bbccb4e47246cae1812087dc721 url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.3.0" media_kit_libs_ios_video: - dependency: transitive + dependency: "direct main" description: name: media_kit_libs_ios_video sha256: c691220334c1828e1fd24db4ebbdfdd6c576f0345bc6cc435b355798d6e4b7ed @@ -695,7 +670,7 @@ packages: source: hosted version: "1.0.5" media_kit_libs_linux: - dependency: transitive + dependency: "direct main" description: name: media_kit_libs_linux sha256: "21acc71cbae3518b3aeef9023a6a3a3decb579a40153764333814987ccd61040" @@ -703,7 +678,7 @@ packages: source: hosted version: "1.0.2" media_kit_libs_macos_video: - dependency: transitive + dependency: "direct main" description: name: media_kit_libs_macos_video sha256: "28ad624666cd20ed78f96a26917dddf6f286ea4bab21620676cc59ba62f3d3e5" @@ -711,15 +686,15 @@ packages: source: hosted version: "1.0.6" media_kit_libs_windows_video: - dependency: transitive + dependency: "direct main" description: name: media_kit_libs_windows_video - sha256: "4aa12f61c9989c4d7159ed0c15640d645dbe59026ac9057a3651d026a409dcb9" + sha256: b343e644927982a2ef3db63877b36d84bdda8173d8318ca0d1c68c1ea8a35982 url: "https://pub.dev" source: hosted - version: "1.0.4" + version: "1.0.5" media_kit_native_event_loop: - dependency: transitive + dependency: "direct main" description: name: media_kit_native_event_loop sha256: "5351f0c28124b5358756515d8619abad182cdefe967468d7fb5b274737cc2f59" @@ -727,21 +702,13 @@ packages: source: hosted version: "1.0.6" media_kit_video: - dependency: transitive + dependency: "direct main" description: name: media_kit_video - sha256: "55edf96bcf08f8bd158018d77afd10d5411f9f3b657fb34a4cd5690caec91596" + sha256: "3ac0403d67710dfb2bf6aabfa6caff1b163e70fb7e1a88423bc1be569b4df6b3" url: "https://pub.dev" source: hosted - version: "1.1.0" - meedu: - dependency: transitive - description: - name: meedu - sha256: "2cac0971ea211b1a18e3c4f038369f8ac57401760342da16d9e4e9d043f7de38" - url: "https://pub.dev" - source: hosted - version: "8.0.2" + version: "1.1.1" meta: dependency: transitive description: @@ -975,7 +942,7 @@ packages: source: hosted version: "1.0.2" screen_brightness: - dependency: transitive + dependency: "direct main" description: name: screen_brightness sha256: "62fd61a64e68b32b98b840bad7d8b6822bbc40e63c2b569a5f85528484c86b41" @@ -1022,14 +989,6 @@ packages: url: "https://pub.dev" source: hosted version: "0.1.1" - screen_retriever: - dependency: transitive - description: - name: screen_retriever - sha256: "4931f226ca158123ccd765325e9fbf360bfed0af9b460a10f960f9bb13d58323" - url: "https://pub.dev" - source: hosted - version: "0.1.6" share_plus: dependency: "direct main" description: @@ -1046,62 +1005,6 @@ packages: url: "https://pub.dev" source: hosted version: "3.2.1" - shared_preferences: - dependency: transitive - description: - name: shared_preferences - sha256: "0344316c947ffeb3a529eac929e1978fcd37c26be4e8468628bac399365a3ca1" - url: "https://pub.dev" - source: hosted - version: "2.2.0" - shared_preferences_android: - dependency: transitive - description: - name: shared_preferences_android - sha256: fe8401ec5b6dcd739a0fe9588802069e608c3fdbfd3c3c93e546cf2f90438076 - url: "https://pub.dev" - source: hosted - version: "2.2.0" - shared_preferences_foundation: - dependency: transitive - description: - name: shared_preferences_foundation - sha256: f39696b83e844923b642ce9dd4bd31736c17e697f6731a5adf445b1274cf3cd4 - url: "https://pub.dev" - source: hosted - version: "2.3.2" - shared_preferences_linux: - dependency: transitive - description: - name: shared_preferences_linux - sha256: "71d6806d1449b0a9d4e85e0c7a917771e672a3d5dc61149cc9fac871115018e1" - url: "https://pub.dev" - source: hosted - version: "2.3.0" - shared_preferences_platform_interface: - dependency: transitive - description: - name: shared_preferences_platform_interface - sha256: "23b052f17a25b90ff2b61aad4cc962154da76fb62848a9ce088efe30d7c50ab1" - url: "https://pub.dev" - source: hosted - version: "2.3.0" - shared_preferences_web: - dependency: transitive - description: - name: shared_preferences_web - sha256: "7347b194fb0bbeb4058e6a4e87ee70350b6b2b90f8ac5f8bd5b3a01548f6d33a" - url: "https://pub.dev" - source: hosted - version: "2.2.0" - shared_preferences_windows: - dependency: transitive - description: - name: shared_preferences_windows - sha256: f95e6a43162bce43c9c3405f3eb6f39e5b5d11f65fab19196cf8225e2777624d - url: "https://pub.dev" - source: hosted - version: "2.3.0" shelf: dependency: transitive description: @@ -1236,7 +1139,7 @@ packages: source: hosted version: "1.3.2" universal_platform: - dependency: transitive + dependency: "direct main" description: name: universal_platform sha256: d315be0f6641898b280ffa34e2ddb14f3d12b1a37882557869646e0cc363d0cc @@ -1308,7 +1211,7 @@ packages: source: hosted version: "0.4.0+2" volume_controller: - dependency: transitive + dependency: "direct main" description: name: volume_controller sha256: "189bdc7a554f476b412e4c8b2f474562b09d74bc458c23667356bce3ca1d48c9" @@ -1316,7 +1219,7 @@ packages: source: hosted version: "2.0.7" wakelock_plus: - dependency: transitive + dependency: "direct main" description: name: wakelock_plus sha256: aac3f3258f01781ec9212df94eecef1eb9ba9350e106728def405baa096ba413 @@ -1411,14 +1314,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.1" - window_manager: - dependency: transitive - description: - name: window_manager - sha256: "9eef00e393e7f9308309ce9a8b2398c9ee3ca78b50c96e8b4f9873945693ac88" - url: "https://pub.dev" - source: hosted - version: "0.3.5" xdg_directories: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index c33d717b..869f7a24 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -80,15 +80,33 @@ dependencies: # 下滑关闭 dismissible_page: ^1.0.2 # 媒体播放 - flutter_meedu_media_kit: + # flutter_meedu_media_kit: # path: /Users/rr/Desktop/code/flutter_meedu_media_kit/package - git: - url: https://github.com/guozhigq/flutter_meedu_media_kit.git - ref: feature-custom - path: package + # git: + # url: https://github.com/guozhigq/flutter_meedu_media_kit.git + # ref: feature-custom + # path: package custom_sliding_segmented_control: ^1.7.5 # 加密 crypto: ^3.0.3 + + # 视频播放器 + media_kit: ^1.1.1 # Primary package. + media_kit_video: ^1.1.1 # For video rendering. + media_kit_native_event_loop: ^1.0.6 # Support for higher number of concurrent instances & better performance. + media_kit_libs_android_video: ^1.3.0 # Android package for video native libraries. + media_kit_libs_ios_video: ^1.0.5 # iOS package for video native libraries. + media_kit_libs_macos_video: ^1.0.6 # macOS package for video native libraries. + media_kit_libs_windows_video: ^1.0.5 # Windows package for video native libraries. + media_kit_libs_linux: ^1.0.2 + + # 音量、亮度、屏幕控制 + volume_controller: ^2.0.7 + screen_brightness: ^0.2.2 + wakelock_plus: ^1.1.1 + universal_platform: ^1.0.0+1 + # 进度条 + audio_video_progress_bar: ^1.0.1 dev_dependencies: flutter_test: diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index d766cc9b..5483cc28 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -8,26 +8,18 @@ #include #include -#include -#include #include #include #include #include -#include #include #include -#include void RegisterPlugins(flutter::PluginRegistry* registry) { ConnectivityPlusWindowsPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin")); DynamicColorPluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("DynamicColorPluginCApi")); - FlutterMeeduMediaKitPluginCApiRegisterWithRegistrar( - registry->GetRegistrarForPlugin("FlutterMeeduMediaKitPluginCApi")); - FullscreenWindowPluginCApiRegisterWithRegistrar( - registry->GetRegistrarForPlugin("FullscreenWindowPluginCApi")); MediaKitLibsWindowsVideoPluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("MediaKitLibsWindowsVideoPluginCApi")); MediaKitVideoPluginCApiRegisterWithRegistrar( @@ -36,12 +28,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) { registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin")); ScreenBrightnessWindowsPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("ScreenBrightnessWindowsPlugin")); - ScreenRetrieverPluginRegisterWithRegistrar( - registry->GetRegistrarForPlugin("ScreenRetrieverPlugin")); SharePlusWindowsPluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi")); UrlLauncherWindowsRegisterWithRegistrar( registry->GetRegistrarForPlugin("UrlLauncherWindows")); - WindowManagerPluginRegisterWithRegistrar( - registry->GetRegistrarForPlugin("WindowManagerPlugin")); } diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 3fe4ff11..cd0b6c2a 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -5,16 +5,12 @@ list(APPEND FLUTTER_PLUGIN_LIST connectivity_plus dynamic_color - flutter_meedu_media_kit - fullscreen_window media_kit_libs_windows_video media_kit_video permission_handler_windows screen_brightness_windows - screen_retriever share_plus url_launcher_windows - window_manager ) list(APPEND FLUTTER_FFI_PLUGIN_LIST