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