mod: 视频播放器重构 - 基本功能
This commit is contained in:
25
.vscode/launch.json
vendored
Normal file
25
.vscode/launch.json
vendored
Normal file
@ -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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -62,4 +62,11 @@
|
|||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
|
<!--
|
||||||
|
Media access permissions.
|
||||||
|
Android 13 or higher.
|
||||||
|
https://developer.android.com/about/versions/13/behavior-changes-13#granular-media-permissions
|
||||||
|
-->
|
||||||
|
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
|
||||||
|
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
|
||||||
</manifest>
|
</manifest>
|
||||||
|
@ -1,9 +1,6 @@
|
|||||||
package com.guozhigq.pilipala
|
package com.guozhigq.pilipala
|
||||||
|
|
||||||
import io.flutter.embedding.android.FlutterActivity
|
import io.flutter.embedding.android.FlutterActivity
|
||||||
import com.zezo357.flutter_meedu_media_kit.MeeduPlayerFlutterActivity;
|
|
||||||
|
|
||||||
|
class MainActivity: FlutterActivity() {
|
||||||
class MainActivity: MeeduPlayerFlutterActivity() {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,8 +5,6 @@ PODS:
|
|||||||
- device_info_plus (0.0.1):
|
- device_info_plus (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- Flutter (1.0.0)
|
- Flutter (1.0.0)
|
||||||
- flutter_meedu_media_kit (0.0.1):
|
|
||||||
- Flutter
|
|
||||||
- FMDB (2.7.5):
|
- FMDB (2.7.5):
|
||||||
- FMDB/standard (= 2.7.5)
|
- FMDB/standard (= 2.7.5)
|
||||||
- FMDB/standard (2.7.5)
|
- FMDB/standard (2.7.5)
|
||||||
@ -30,9 +28,6 @@ PODS:
|
|||||||
- Flutter
|
- Flutter
|
||||||
- share_plus (0.0.1):
|
- share_plus (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- shared_preferences_foundation (0.0.1):
|
|
||||||
- Flutter
|
|
||||||
- FlutterMacOS
|
|
||||||
- sqflite (0.0.3):
|
- sqflite (0.0.3):
|
||||||
- Flutter
|
- Flutter
|
||||||
- FMDB (>= 2.7.5)
|
- FMDB (>= 2.7.5)
|
||||||
@ -49,7 +44,6 @@ DEPENDENCIES:
|
|||||||
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
|
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
|
||||||
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
|
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
|
||||||
- Flutter (from `Flutter`)
|
- 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`)
|
- 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_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`)
|
- 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`)
|
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
|
||||||
- screen_brightness_ios (from `.symlinks/plugins/screen_brightness_ios/ios`)
|
- screen_brightness_ios (from `.symlinks/plugins/screen_brightness_ios/ios`)
|
||||||
- share_plus (from `.symlinks/plugins/share_plus/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`)
|
- sqflite (from `.symlinks/plugins/sqflite/ios`)
|
||||||
- volume_controller (from `.symlinks/plugins/volume_controller/ios`)
|
- volume_controller (from `.symlinks/plugins/volume_controller/ios`)
|
||||||
- wakelock_plus (from `.symlinks/plugins/wakelock_plus/ios`)
|
- wakelock_plus (from `.symlinks/plugins/wakelock_plus/ios`)
|
||||||
@ -78,8 +71,6 @@ EXTERNAL SOURCES:
|
|||||||
:path: ".symlinks/plugins/device_info_plus/ios"
|
:path: ".symlinks/plugins/device_info_plus/ios"
|
||||||
Flutter:
|
Flutter:
|
||||||
:path: Flutter
|
:path: Flutter
|
||||||
flutter_meedu_media_kit:
|
|
||||||
:path: ".symlinks/plugins/flutter_meedu_media_kit/ios"
|
|
||||||
image_gallery_saver:
|
image_gallery_saver:
|
||||||
:path: ".symlinks/plugins/image_gallery_saver/ios"
|
:path: ".symlinks/plugins/image_gallery_saver/ios"
|
||||||
media_kit_libs_ios_video:
|
media_kit_libs_ios_video:
|
||||||
@ -98,8 +89,6 @@ EXTERNAL SOURCES:
|
|||||||
:path: ".symlinks/plugins/screen_brightness_ios/ios"
|
:path: ".symlinks/plugins/screen_brightness_ios/ios"
|
||||||
share_plus:
|
share_plus:
|
||||||
:path: ".symlinks/plugins/share_plus/ios"
|
:path: ".symlinks/plugins/share_plus/ios"
|
||||||
shared_preferences_foundation:
|
|
||||||
:path: ".symlinks/plugins/shared_preferences_foundation/darwin"
|
|
||||||
sqflite:
|
sqflite:
|
||||||
:path: ".symlinks/plugins/sqflite/ios"
|
:path: ".symlinks/plugins/sqflite/ios"
|
||||||
volume_controller:
|
volume_controller:
|
||||||
@ -115,19 +104,17 @@ SPEC CHECKSUMS:
|
|||||||
connectivity_plus: 413a8857dd5d9f1c399a39130850d02fe0feaf7e
|
connectivity_plus: 413a8857dd5d9f1c399a39130850d02fe0feaf7e
|
||||||
device_info_plus: 7545d84d8d1b896cb16a4ff98c19f07ec4b298ea
|
device_info_plus: 7545d84d8d1b896cb16a4ff98c19f07ec4b298ea
|
||||||
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
|
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
|
||||||
flutter_meedu_media_kit: 5059e8719e3fd4a65fe5312b0febc75491e553f9
|
|
||||||
FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
|
FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
|
||||||
image_gallery_saver: cb43cc43141711190510e92c460eb1655cd343cb
|
image_gallery_saver: cb43cc43141711190510e92c460eb1655cd343cb
|
||||||
media_kit_libs_ios_video: bcbf9a53dd3b60c0fcf92b0903e4706391ad0b65
|
media_kit_libs_ios_video: bcbf9a53dd3b60c0fcf92b0903e4706391ad0b65
|
||||||
media_kit_native_event_loop: 1eac6db2378101404392c80606103b42f7c2c491
|
media_kit_native_event_loop: 1eac6db2378101404392c80606103b42f7c2c491
|
||||||
media_kit_video: 6c61501e3ab980488d80df6d705905d14b150b63
|
media_kit_video: 8a750aa160f95fd6a5d6b88949c8df4c5cea6b0d
|
||||||
package_info_plus: fd030dabf36271f146f1f3beacd48f564b0f17f7
|
package_info_plus: fd030dabf36271f146f1f3beacd48f564b0f17f7
|
||||||
path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943
|
path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943
|
||||||
permission_handler_apple: e76247795d700c14ea09e3a2d8855d41ee80a2e6
|
permission_handler_apple: e76247795d700c14ea09e3a2d8855d41ee80a2e6
|
||||||
ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825
|
ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825
|
||||||
screen_brightness_ios: 715ca807df953bf676d339f11464e438143ee625
|
screen_brightness_ios: 715ca807df953bf676d339f11464e438143ee625
|
||||||
share_plus: 599aa54e4ea31d4b4c0e9c911bcc26c55e791028
|
share_plus: 599aa54e4ea31d4b4c0e9c911bcc26c55e791028
|
||||||
shared_preferences_foundation: 5b919d13b803cadd15ed2dc053125c68730e5126
|
|
||||||
sqflite: 31f7eba61e3074736dff8807a9b41581e4f7f15a
|
sqflite: 31f7eba61e3074736dff8807a9b41581e4f7f15a
|
||||||
volume_controller: 531ddf792994285c9b17f9d8a7e4dcdd29b3eae9
|
volume_controller: 531ddf792994285c9b17f9d8a7e4dcdd29b3eae9
|
||||||
wakelock_plus: 8b09852c8876491e4b6d179e17dfe2a0b5f60d47
|
wakelock_plus: 8b09852c8876491e4b6d179e17dfe2a0b5f60d47
|
||||||
|
57
lib/common/widgets/app_bar_ani.dart
Normal file
57
lib/common/widgets/app_bar_ani.dart
Normal file
@ -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<Offset>(
|
||||||
|
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: <Color>[
|
||||||
|
Colors.transparent,
|
||||||
|
Colors.black45,
|
||||||
|
],
|
||||||
|
tileMode: TileMode.clamp,
|
||||||
|
)
|
||||||
|
: const LinearGradient(
|
||||||
|
begin: Alignment.topCenter,
|
||||||
|
end: Alignment.bottomCenter,
|
||||||
|
colors: <Color>[
|
||||||
|
Colors.transparent,
|
||||||
|
Colors.black45,
|
||||||
|
],
|
||||||
|
tileMode: TileMode.mirror,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: child,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,4 @@
|
|||||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
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:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:flutter/material.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/pages/main/view.dart';
|
||||||
import 'package:pilipala/utils/data.dart';
|
import 'package:pilipala/utils/data.dart';
|
||||||
import 'package:pilipala/utils/storage.dart';
|
import 'package:pilipala/utils/storage.dart';
|
||||||
|
import 'package:media_kit/media_kit.dart'; // Provides [Player], [Media], [Playlist] etc.
|
||||||
|
|
||||||
void main() async {
|
void main() async {
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_meedu_media_kit/meedu_player.dart';
|
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:pilipala/http/constants.dart';
|
import 'package:pilipala/http/constants.dart';
|
||||||
import 'package:pilipala/http/live.dart';
|
import 'package:pilipala/http/live.dart';
|
||||||
import 'package:pilipala/models/live/room_info.dart';
|
import 'package:pilipala/models/live/room_info.dart';
|
||||||
|
import 'package:pilipala/plugin/pl_player/index.dart';
|
||||||
|
|
||||||
class LiveRoomController extends GetxController {
|
class LiveRoomController extends GetxController {
|
||||||
String cover = '';
|
String cover = '';
|
||||||
@ -13,13 +13,15 @@ class LiveRoomController extends GetxController {
|
|||||||
double volume = 0.0;
|
double volume = 0.0;
|
||||||
// 静音状态
|
// 静音状态
|
||||||
RxBool volumeOff = false.obs;
|
RxBool volumeOff = false.obs;
|
||||||
|
PlPlayerController plPlayerController =
|
||||||
|
PlPlayerController(controlsEnabled: false);
|
||||||
|
|
||||||
MeeduPlayerController meeduPlayerController = MeeduPlayerController(
|
// MeeduPlayerController meeduPlayerController = MeeduPlayerController(
|
||||||
colorTheme: Theme.of(Get.context!).colorScheme.primary,
|
// colorTheme: Theme.of(Get.context!).colorScheme.primary,
|
||||||
pipEnabled: true,
|
// pipEnabled: true,
|
||||||
controlsStyle: ControlsStyle.live,
|
// controlsStyle: ControlsStyle.live,
|
||||||
enabledButtons: const EnabledButtons(pip: true),
|
// enabledButtons: const EnabledButtons(pip: true),
|
||||||
);
|
// );
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onInit() {
|
void onInit() {
|
||||||
@ -36,19 +38,21 @@ class LiveRoomController extends GetxController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
playerInit(source) {
|
playerInit(source) {
|
||||||
meeduPlayerController.setDataSource(
|
plPlayerController.setDataSource(
|
||||||
DataSource(
|
DataSource(
|
||||||
|
videoSource: source,
|
||||||
|
audioSource: null,
|
||||||
type: DataSourceType.network,
|
type: DataSourceType.network,
|
||||||
source: source,
|
|
||||||
httpHeaders: {
|
httpHeaders: {
|
||||||
'user-agent':
|
'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',
|
'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
|
'referer': HttpString.baseUrl
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
// 硬解
|
||||||
|
enableHA: true,
|
||||||
autoplay: true,
|
autoplay: true,
|
||||||
);
|
);
|
||||||
volume = meeduPlayerController.volume.value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future queryLiveInfo() async {
|
Future queryLiveInfo() async {
|
||||||
@ -68,12 +72,12 @@ class LiveRoomController extends GetxController {
|
|||||||
if (value == 0) {
|
if (value == 0) {
|
||||||
// 设置音量
|
// 设置音量
|
||||||
volumeOff.value = false;
|
volumeOff.value = false;
|
||||||
meeduPlayerController.setVolume(volume);
|
// meeduPlayerController.setVolume(volume);
|
||||||
} else {
|
} else {
|
||||||
// 取消音量
|
// 取消音量
|
||||||
volume = value;
|
volume = value;
|
||||||
volumeOff.value = true;
|
volumeOff.value = true;
|
||||||
meeduPlayerController.setVolume(0);
|
// meeduPlayerController.setVolume(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_meedu_media_kit/meedu_player.dart';
|
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
||||||
|
import 'package:pilipala/plugin/pl_player/index.dart';
|
||||||
|
|
||||||
import 'controller.dart';
|
import 'controller.dart';
|
||||||
|
|
||||||
@ -14,7 +14,7 @@ class LiveRoomPage extends StatefulWidget {
|
|||||||
|
|
||||||
class _LiveRoomPageState extends State<LiveRoomPage> {
|
class _LiveRoomPageState extends State<LiveRoomPage> {
|
||||||
final LiveRoomController _liveRoomController = Get.put(LiveRoomController());
|
final LiveRoomController _liveRoomController = Get.put(LiveRoomController());
|
||||||
MeeduPlayerController? _meeduPlayerController;
|
PlPlayerController? plPlayerController;
|
||||||
|
|
||||||
bool isShowCover = true;
|
bool isShowCover = true;
|
||||||
bool isPlay = true;
|
bool isPlay = true;
|
||||||
@ -22,8 +22,8 @@ class _LiveRoomPageState extends State<LiveRoomPage> {
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_meeduPlayerController = _liveRoomController.meeduPlayerController;
|
plPlayerController = _liveRoomController.plPlayerController;
|
||||||
_meeduPlayerController!.onPlayerStatusChanged.listen(
|
plPlayerController!.onPlayerStatusChanged.listen(
|
||||||
(PlayerStatus status) {
|
(PlayerStatus status) {
|
||||||
if (status == PlayerStatus.playing) {
|
if (status == PlayerStatus.playing) {
|
||||||
isShowCover = false;
|
isShowCover = false;
|
||||||
@ -35,7 +35,7 @@ class _LiveRoomPageState extends State<LiveRoomPage> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_meeduPlayerController!.dispose();
|
plPlayerController!.dispose();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,39 +90,9 @@ class _LiveRoomPageState extends State<LiveRoomPage> {
|
|||||||
children: [
|
children: [
|
||||||
AspectRatio(
|
AspectRatio(
|
||||||
aspectRatio: 16 / 9,
|
aspectRatio: 16 / 9,
|
||||||
child: MeeduVideoPlayer(
|
child: plPlayerController!.videoPlayerController != null
|
||||||
header: (BuildContext context,
|
? PLVideoPlayer(controller: plPlayerController!)
|
||||||
MeeduPlayerController meeduPlayerController,
|
: const SizedBox(),
|
||||||
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)
|
if (_liveRoomController.liveItem.cover != null)
|
||||||
Visibility(
|
Visibility(
|
||||||
@ -140,7 +110,8 @@ class _LiveRoomPageState extends State<LiveRoomPage> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
)),
|
),
|
||||||
|
),
|
||||||
if (_liveRoomController.liveItem.watchedShow != null)
|
if (_liveRoomController.liveItem.watchedShow != null)
|
||||||
Container(
|
Container(
|
||||||
height: 45,
|
height: 45,
|
||||||
@ -153,35 +124,35 @@ class _LiveRoomPageState extends State<LiveRoomPage> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: Row(children: <Widget>[
|
child: Row(children: <Widget>[
|
||||||
// SizedBox(
|
SizedBox(
|
||||||
// width: 38,
|
width: 38,
|
||||||
// height: 38,
|
height: 38,
|
||||||
// child: IconButton(
|
child: IconButton(
|
||||||
// onPressed: () {},
|
onPressed: () {},
|
||||||
// icon: const Icon(
|
icon: const Icon(
|
||||||
// Icons.subtitles_outlined,
|
Icons.subtitles_outlined,
|
||||||
// size: 21,
|
size: 21,
|
||||||
// ),
|
),
|
||||||
// ),
|
),
|
||||||
// ),
|
),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
// SizedBox(
|
SizedBox(
|
||||||
// width: 38,
|
width: 38,
|
||||||
// height: 38,
|
height: 38,
|
||||||
// child: IconButton(
|
child: IconButton(
|
||||||
// onPressed: () {},
|
onPressed: () {},
|
||||||
// icon: const Icon(
|
icon: const Icon(
|
||||||
// Icons.hd_outlined,
|
Icons.hd_outlined,
|
||||||
// size: 20,
|
size: 20,
|
||||||
// ),
|
),
|
||||||
// ),
|
),
|
||||||
// ),
|
),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: 38,
|
width: 38,
|
||||||
height: 38,
|
height: 38,
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
onPressed: () => _liveRoomController
|
onPressed: () => _liveRoomController
|
||||||
.setVolumn(_meeduPlayerController!.volume.value),
|
.setVolumn(plPlayerController!.volume.value),
|
||||||
icon: Obx(() => Icon(
|
icon: Obx(() => Icon(
|
||||||
_liveRoomController.volumeOff.value
|
_liveRoomController.volumeOff.value
|
||||||
? Icons.volume_off_outlined
|
? Icons.volume_off_outlined
|
||||||
@ -194,8 +165,8 @@ class _LiveRoomPageState extends State<LiveRoomPage> {
|
|||||||
width: 38,
|
width: 38,
|
||||||
height: 38,
|
height: 38,
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
onPressed: () =>
|
onPressed: () => {},
|
||||||
_meeduPlayerController!.goToFullscreen(context),
|
// plPlayerController!.goToFullscreen(context),
|
||||||
icon: const Icon(
|
icon: const Icon(
|
||||||
Icons.fullscreen,
|
Icons.fullscreen,
|
||||||
),
|
),
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_meedu_media_kit/meedu_player.dart';
|
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:pilipala/http/constants.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/play/url.dart';
|
||||||
import 'package:pilipala/models/video/reply/item.dart';
|
import 'package:pilipala/models/video/reply/item.dart';
|
||||||
import 'package:pilipala/pages/video/detail/replyReply/index.dart';
|
import 'package:pilipala/pages/video/detail/replyReply/index.dart';
|
||||||
|
import 'package:pilipala/plugin/pl_player/index.dart';
|
||||||
import 'package:pilipala/utils/storage.dart';
|
import 'package:pilipala/utils/storage.dart';
|
||||||
|
|
||||||
class VideoDetailController extends GetxController
|
class VideoDetailController extends GetxController
|
||||||
@ -38,21 +38,12 @@ class VideoDetailController extends GetxController
|
|||||||
int fRpid = 0;
|
int fRpid = 0;
|
||||||
|
|
||||||
ReplyItemModel? firstFloor;
|
ReplyItemModel? firstFloor;
|
||||||
|
|
||||||
final scaffoldKey = GlobalKey<ScaffoldState>();
|
final scaffoldKey = GlobalKey<ScaffoldState>();
|
||||||
|
|
||||||
MeeduPlayerController meeduPlayerController = MeeduPlayerController(
|
|
||||||
colorTheme: Theme.of(Get.context!).colorScheme.primary,
|
|
||||||
pipEnabled: true,
|
|
||||||
controlsStyle: ControlsStyle.youtube,
|
|
||||||
enabledButtons: const EnabledButtons(pip: true),
|
|
||||||
);
|
|
||||||
|
|
||||||
Timer? timer;
|
Timer? timer;
|
||||||
|
|
||||||
RxString bgCover = ''.obs;
|
RxString bgCover = ''.obs;
|
||||||
Box user = GStrorage.user;
|
Box user = GStrorage.user;
|
||||||
Box localCache = GStrorage.localCache;
|
Box localCache = GStrorage.localCache;
|
||||||
|
PlPlayerController plPlayerController = PlPlayerController();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onInit() {
|
void onInit() {
|
||||||
@ -95,38 +86,30 @@ class VideoDetailController extends GetxController
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
playerInit(source, audioSource, {Duration defaultST = Duration.zero}) {
|
playerInit(source, audioSource,
|
||||||
meeduPlayerController.onVideoFitChange(BoxFit.cover);
|
{Duration defaultST = Duration.zero, int duration = 0}) async {
|
||||||
meeduPlayerController.setDataSource(
|
plPlayerController.setDataSource(
|
||||||
DataSource(
|
DataSource(
|
||||||
type: DataSourceType.network,
|
videoSource: source,
|
||||||
source: source,
|
|
||||||
audioSource: audioSource,
|
audioSource: audioSource,
|
||||||
|
type: DataSourceType.network,
|
||||||
httpHeaders: {
|
httpHeaders: {
|
||||||
'user-agent':
|
'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',
|
'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
|
'referer': HttpString.baseUrl
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
// 硬解
|
||||||
|
enableHA: true,
|
||||||
autoplay: true,
|
autoplay: true,
|
||||||
seekTo: defaultST,
|
seekTo: defaultST,
|
||||||
|
duration: Duration(milliseconds: duration),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Future<void> meeduDispose() async {
|
|
||||||
// if (meeduPlayerController != null) {
|
|
||||||
// _playerEventSubs?.cancel();
|
|
||||||
// await meeduPlayerController!.dispose();
|
|
||||||
// meeduPlayerController = null;
|
|
||||||
// // The next line disables the wakelock again.
|
|
||||||
// // await Wakelock.disable();
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// 视频链接
|
// 视频链接
|
||||||
queryVideoUrl() async {
|
queryVideoUrl() async {
|
||||||
var result = await VideoHttp.videoUrl(cid: cid, bvid: bvid);
|
var result = await VideoHttp.videoUrl(cid: cid, bvid: bvid);
|
||||||
// log('result: ${result.toString()}');
|
|
||||||
if (result['status']) {
|
if (result['status']) {
|
||||||
PlayUrlModel data = result['data'];
|
PlayUrlModel data = result['data'];
|
||||||
// 指定质量的视频 -> 最高质量的视频
|
// 指定质量的视频 -> 最高质量的视频
|
||||||
@ -134,7 +117,8 @@ class VideoDetailController extends GetxController
|
|||||||
String audioUrl =
|
String audioUrl =
|
||||||
data.dash!.audio!.isNotEmpty ? data.dash!.audio!.first.baseUrl! : '';
|
data.dash!.audio!.isNotEmpty ? data.dash!.audio!.first.baseUrl! : '';
|
||||||
playerInit(videoUrl, audioUrl,
|
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) {
|
if (localCache.get(LocalCacheKey.historyStatus) == true) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Duration progress = meeduPlayerController.position.value;
|
Duration progress = plPlayerController.position.value;
|
||||||
await VideoHttp.heartBeat(
|
await VideoHttp.heartBeat(
|
||||||
bvid: bvid,
|
bvid: bvid,
|
||||||
cid: cid,
|
cid: cid,
|
||||||
|
@ -595,11 +595,11 @@ InlineSpan buildContent(
|
|||||||
),
|
),
|
||||||
recognizer: TapGestureRecognizer()
|
recognizer: TapGestureRecognizer()
|
||||||
..onTap = () {
|
..onTap = () {
|
||||||
Get.find<VideoDetailController>(tag: Get.arguments['heroTag'])
|
// Get.find<VideoDetailController>(tag: Get.arguments['heroTag'])
|
||||||
.meeduPlayerController
|
// .meeduPlayerController
|
||||||
.seekTo(
|
// .seekTo(
|
||||||
Duration(seconds: Utils.duration(matchStr)),
|
// Duration(seconds: Utils.duration(matchStr)),
|
||||||
);
|
// );
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:extended_nested_scroll_view/extended_nested_scroll_view.dart';
|
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:get/get.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:pilipala/common/widgets/network_img_layer.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/controller.dart';
|
||||||
import 'package:pilipala/pages/video/detail/introduction/index.dart';
|
import 'package:pilipala/pages/video/detail/introduction/index.dart';
|
||||||
import 'package:pilipala/pages/video/detail/related/index.dart';
|
import 'package:pilipala/pages/video/detail/related/index.dart';
|
||||||
|
import 'package:pilipala/plugin/pl_player/index.dart';
|
||||||
|
|
||||||
import 'widgets/app_bar.dart';
|
import 'widgets/app_bar.dart';
|
||||||
|
|
||||||
@ -27,21 +27,20 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
with TickerProviderStateMixin, RouteAware {
|
with TickerProviderStateMixin, RouteAware {
|
||||||
final VideoDetailController videoDetailController =
|
final VideoDetailController videoDetailController =
|
||||||
Get.put(VideoDetailController(), tag: Get.arguments['heroTag']);
|
Get.put(VideoDetailController(), tag: Get.arguments['heroTag']);
|
||||||
MeeduPlayerController? _meeduPlayerController;
|
PlPlayerController? plPlayerController;
|
||||||
final ScrollController _extendNestCtr = ScrollController();
|
final ScrollController _extendNestCtr = ScrollController();
|
||||||
late StreamController<double> appbarStream;
|
late StreamController<double> appbarStream;
|
||||||
|
|
||||||
StreamSubscription? _playerEventSubs;
|
|
||||||
bool isPlay = false;
|
bool isPlay = false;
|
||||||
PlayerStatus playerStatus = PlayerStatus.paused;
|
PlayerStatus playerStatus = PlayerStatus.playing;
|
||||||
bool isShowCover = true;
|
bool isShowCover = true;
|
||||||
double doubleOffset = 0;
|
double doubleOffset = 0;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_meeduPlayerController = videoDetailController.meeduPlayerController;
|
plPlayerController = videoDetailController.plPlayerController;
|
||||||
_playerEventSubs = _meeduPlayerController!.onPlayerStatusChanged.listen(
|
plPlayerController!.onPlayerStatusChanged.listen(
|
||||||
(PlayerStatus status) {
|
(PlayerStatus status) {
|
||||||
videoDetailController.markHeartBeat();
|
videoDetailController.markHeartBeat();
|
||||||
playerStatus = status;
|
playerStatus = status;
|
||||||
@ -70,24 +69,15 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _meeduDispose() async {
|
|
||||||
if (_meeduPlayerController != null) {
|
|
||||||
_playerEventSubs?.cancel();
|
|
||||||
await _meeduPlayerController!.dispose();
|
|
||||||
_meeduPlayerController = null;
|
|
||||||
// The next line disables the wakelock again.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void continuePlay() async {
|
void continuePlay() async {
|
||||||
await _extendNestCtr.animateTo(0,
|
await _extendNestCtr.animateTo(0,
|
||||||
duration: const Duration(milliseconds: 500), curve: Curves.easeInOut);
|
duration: const Duration(milliseconds: 500), curve: Curves.easeInOut);
|
||||||
_meeduPlayerController!.play();
|
plPlayerController!.play();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
videoDetailController.meeduPlayerController.dispose();
|
plPlayerController!.dispose();
|
||||||
if (videoDetailController.timer != null) {
|
if (videoDetailController.timer != null) {
|
||||||
videoDetailController.timer!.cancel();
|
videoDetailController.timer!.cancel();
|
||||||
}
|
}
|
||||||
@ -97,9 +87,6 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
@override
|
@override
|
||||||
// 离开当前页面时
|
// 离开当前页面时
|
||||||
void didPushNext() async {
|
void didPushNext() async {
|
||||||
if (!_meeduPlayerController!.pipAvailable.value) {
|
|
||||||
_meeduPlayerController!.pause();
|
|
||||||
}
|
|
||||||
if (videoDetailController.timer!.isActive) {
|
if (videoDetailController.timer!.isActive) {
|
||||||
videoDetailController.timer!.cancel();
|
videoDetailController.timer!.cancel();
|
||||||
}
|
}
|
||||||
@ -111,7 +98,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
void didPopNext() async {
|
void didPopNext() async {
|
||||||
if (_extendNestCtr.position.pixels == 0) {
|
if (_extendNestCtr.position.pixels == 0) {
|
||||||
await Future.delayed(const Duration(milliseconds: 300));
|
await Future.delayed(const Duration(milliseconds: 300));
|
||||||
_meeduPlayerController!.play();
|
plPlayerController!.play();
|
||||||
}
|
}
|
||||||
if (!videoDetailController.timer!.isActive) {
|
if (!videoDetailController.timer!.isActive) {
|
||||||
videoDetailController.loopHeartBeat();
|
videoDetailController.loopHeartBeat();
|
||||||
@ -167,34 +154,11 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
tag: videoDetailController.heroTag,
|
tag: videoDetailController.heroTag,
|
||||||
child: Stack(
|
child: Stack(
|
||||||
children: [
|
children: [
|
||||||
AspectRatio(
|
if (plPlayerController!
|
||||||
aspectRatio: 16 / 9,
|
.videoPlayerController !=
|
||||||
child: MeeduVideoPlayer(
|
null)
|
||||||
controller: _meeduPlayerController!,
|
PLVideoPlayer(
|
||||||
header: (BuildContext context,
|
controller: plPlayerController!),
|
||||||
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,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Visibility(
|
Visibility(
|
||||||
visible: isShowCover,
|
visible: isShowCover,
|
||||||
child: Positioned(
|
child: Positioned(
|
||||||
@ -305,8 +269,8 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
builder: ((context, snapshot) {
|
builder: ((context, snapshot) {
|
||||||
return ScrollAppBar(
|
return ScrollAppBar(
|
||||||
snapshot.data!.toDouble(),
|
snapshot.data!.toDouble(),
|
||||||
continuePlay,
|
() {},
|
||||||
playerStatus,
|
playerStatus != PlayerStatus.playing,
|
||||||
null,
|
null,
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_meedu_media_kit/meedu_player.dart';
|
|
||||||
|
|
||||||
class ScrollAppBar extends StatelessWidget {
|
class ScrollAppBar extends StatelessWidget {
|
||||||
final double scrollVal;
|
final double scrollVal;
|
||||||
final Function callback;
|
final Function callback;
|
||||||
final PlayerStatus playerStatus;
|
final bool playerStatus;
|
||||||
|
|
||||||
const ScrollAppBar(
|
const ScrollAppBar(
|
||||||
this.scrollVal,
|
this.scrollVal,
|
||||||
@ -34,18 +33,9 @@ class ScrollAppBar extends StatelessWidget {
|
|||||||
centerTitle: true,
|
centerTitle: true,
|
||||||
title: TextButton(
|
title: TextButton(
|
||||||
onPressed: () => callback(),
|
onPressed: () => callback(),
|
||||||
child: Row(
|
child: const Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [Icon(Icons.play_arrow_rounded), Text('继续播放')],
|
||||||
const Icon(Icons.play_arrow_rounded),
|
|
||||||
Text(
|
|
||||||
playerStatus == PlayerStatus.paused
|
|
||||||
? '继续播放'
|
|
||||||
: playerStatus == PlayerStatus.completed
|
|
||||||
? '重新播放'
|
|
||||||
: '播放中',
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
actions: [
|
actions: [
|
||||||
|
645
lib/plugin/pl_player/controller.dart
Normal file
645
lib/plugin/pl_player/controller.dart
Normal file
@ -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<Duration> _position = Rx(Duration.zero);
|
||||||
|
final Rx<Duration> _sliderPosition = Rx(Duration.zero);
|
||||||
|
final Rx<Duration> _duration = Rx(Duration.zero);
|
||||||
|
final Rx<Duration> _buffered = Rx(Duration.zero);
|
||||||
|
|
||||||
|
final Rx<double> _playbackSpeed = 1.0.obs;
|
||||||
|
final Rx<double> _currentVolume = 1.0.obs;
|
||||||
|
final Rx<double> _currentBrightness = 0.0.obs;
|
||||||
|
|
||||||
|
final Rx<bool> _mute = false.obs;
|
||||||
|
final Rx<bool> _showControls = false.obs;
|
||||||
|
final Rx<bool> _showVolumeStatus = false.obs;
|
||||||
|
final Rx<bool> _showBrightnessStatus = false.obs;
|
||||||
|
final Rx<bool> _doubleSpeedStatus = false.obs;
|
||||||
|
final Rx<bool> _controlsClose = false.obs;
|
||||||
|
|
||||||
|
Rx<bool> videoFitChanged = false.obs;
|
||||||
|
final Rx<BoxFit> _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<BoxFit> fits = [
|
||||||
|
BoxFit.contain,
|
||||||
|
BoxFit.cover,
|
||||||
|
BoxFit.fill,
|
||||||
|
BoxFit.fitHeight,
|
||||||
|
BoxFit.fitWidth,
|
||||||
|
BoxFit.scaleDown
|
||||||
|
];
|
||||||
|
|
||||||
|
/// 数据加载监听
|
||||||
|
Stream<DataStatus> get onDataStatusChanged => dataStatus.status.stream;
|
||||||
|
|
||||||
|
/// 播放状态监听
|
||||||
|
Stream<PlayerStatus> get onPlayerStatusChanged => playerStatus.status.stream;
|
||||||
|
|
||||||
|
/// 视频时长
|
||||||
|
Rx<Duration> get duration => _duration;
|
||||||
|
Stream<Duration> get onDurationChanged => _duration.stream;
|
||||||
|
|
||||||
|
/// 视频当前播放位置
|
||||||
|
Rx<Duration> get position => _position;
|
||||||
|
Stream<Duration> get onPositionChanged => _position.stream;
|
||||||
|
|
||||||
|
/// 视频播放速度
|
||||||
|
double get playbackSpeed => _playbackSpeed.value;
|
||||||
|
|
||||||
|
/// 视频缓冲
|
||||||
|
Rx<Duration> get buffered => _buffered;
|
||||||
|
Stream<Duration> get onBufferedChanged => _buffered.stream;
|
||||||
|
|
||||||
|
// 视频静音
|
||||||
|
Rx<bool> get mute => _mute;
|
||||||
|
Stream<bool> get onMuteChanged => _mute.stream;
|
||||||
|
|
||||||
|
/// [videoPlayerController] instace of Player
|
||||||
|
Player? get videoPlayerController => _videoPlayerController;
|
||||||
|
|
||||||
|
/// [videoController] instace of Player
|
||||||
|
VideoController? get videoController => _videoController;
|
||||||
|
|
||||||
|
/// 进度条位置及监听
|
||||||
|
Rx<Duration> get sliderPosition => _sliderPosition;
|
||||||
|
Stream<Duration> get onSliderPositionChanged => _sliderPosition.stream;
|
||||||
|
|
||||||
|
/// 是否展示控制条及监听
|
||||||
|
Rx<bool> get showControls => _showControls;
|
||||||
|
Stream<bool> get onShowControlsChanged => _showControls.stream;
|
||||||
|
|
||||||
|
/// 音量控制条展示/隐藏
|
||||||
|
Rx<bool> get showVolumeStatus => _showVolumeStatus;
|
||||||
|
Stream<bool> get onShowVolumeStatusChanged => _showVolumeStatus.stream;
|
||||||
|
|
||||||
|
/// 亮度控制条展示/隐藏
|
||||||
|
Rx<bool> get showBrightnessStatus => _showBrightnessStatus;
|
||||||
|
Stream<bool> get onShowBrightnessStatusChanged =>
|
||||||
|
_showBrightnessStatus.stream;
|
||||||
|
|
||||||
|
/// 音量控制条
|
||||||
|
Rx<double> get volume => _currentVolume;
|
||||||
|
Stream<double> get onVolumeChanged => _currentVolume.stream;
|
||||||
|
|
||||||
|
/// 亮度控制条
|
||||||
|
Rx<double> get brightness => _currentBrightness;
|
||||||
|
Stream<double> get onBrightnessChanged => _currentBrightness.stream;
|
||||||
|
|
||||||
|
/// 是否循环
|
||||||
|
PlaylistMode get looping => _looping;
|
||||||
|
|
||||||
|
/// 是否自动播放
|
||||||
|
bool get autoplay => _autoPlay;
|
||||||
|
|
||||||
|
/// 视频比例
|
||||||
|
Rx<BoxFit> get videoFit => _videoFit;
|
||||||
|
|
||||||
|
/// 是否长按倍速
|
||||||
|
Rx<bool> get doubleSpeedStatus => _doubleSpeedStatus;
|
||||||
|
|
||||||
|
Rx<bool> isBuffering = true.obs;
|
||||||
|
|
||||||
|
Rx<bool> 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<void> 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<Player> _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<StreamSubscription> 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<void> 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<void> setPlaybackSpeed(double speed) async {
|
||||||
|
await _videoPlayerController?.setRate(speed);
|
||||||
|
_playbackSpeed.value = speed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 设置倍速
|
||||||
|
Future<void> togglePlaybackSpeed() async {
|
||||||
|
List<double> 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<void> 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<void> pause({bool notify = true}) async {
|
||||||
|
await _videoPlayerController?.pause();
|
||||||
|
playerStatus.status.value = PlayerStatus.paused;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 更改播放状态
|
||||||
|
Future<void> 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<void> getCurrentVolume() async {
|
||||||
|
_currentVolume.value = await VolumeController().getVolume();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> 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<void> getCurrentBrightness() async {
|
||||||
|
try {
|
||||||
|
_currentBrightness.value = await ScreenBrightness().current;
|
||||||
|
} catch (e) {
|
||||||
|
throw 'Failed to get current brightness';
|
||||||
|
//return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> setBrightness(double brightnes) async {
|
||||||
|
try {
|
||||||
|
brightness.value = brightnes;
|
||||||
|
ScreenBrightness().setScreenBrightness(brightnes);
|
||||||
|
setVideoBrightness();
|
||||||
|
} catch (e) {
|
||||||
|
throw 'Failed to set brightness';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> 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<void> setVideoFit() async {
|
||||||
|
videoStorage.put(VideoBoxKey.videoBrightness, _videoFit.value.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 读取fit
|
||||||
|
Future<void> getVideoFit() async {
|
||||||
|
String fitValue = videoStorage.get(VideoBoxKey.videoBrightness,
|
||||||
|
defaultValue: 'fitHeight');
|
||||||
|
_videoFit.value = fits.firstWhere((element) => element.name == fitValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 缓存亮度
|
||||||
|
Future<void> setVideoBrightness() async {}
|
||||||
|
|
||||||
|
/// 读取亮度
|
||||||
|
Future<void> 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<void> videoPlayerClosed() async {
|
||||||
|
_timer?.cancel();
|
||||||
|
_timerForVolume?.cancel();
|
||||||
|
_timerForGettingVolume?.cancel();
|
||||||
|
timerForTrackingMouse?.cancel();
|
||||||
|
_timerForSeek?.cancel();
|
||||||
|
videoFitChangedTimer?.cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> 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;
|
||||||
|
}
|
||||||
|
}
|
7
lib/plugin/pl_player/index.dart
Normal file
7
lib/plugin/pl_player/index.dart
Normal file
@ -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';
|
55
lib/plugin/pl_player/models/data_source.dart
Normal file
55
lib/plugin/pl_player/models/data_source.dart
Normal file
@ -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<String, String>? 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<String, String>? 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,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
12
lib/plugin/pl_player/models/data_status.dart
Normal file
12
lib/plugin/pl_player/models/data_status.dart
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
|
enum DataStatus { none, loading, loaded, error }
|
||||||
|
|
||||||
|
class PlPlayerDataStatus {
|
||||||
|
Rx<DataStatus> 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;
|
||||||
|
}
|
19
lib/plugin/pl_player/models/play_status.dart
Normal file
19
lib/plugin/pl_player/models/play_status.dart
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
|
enum PlayerStatus { completed, playing, paused }
|
||||||
|
|
||||||
|
class PlPlayerStatus {
|
||||||
|
Rx<PlayerStatus> 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;
|
||||||
|
}
|
||||||
|
}
|
29
lib/plugin/pl_player/utils.dart
Normal file
29
lib/plugin/pl_player/utils.dart
Normal file
@ -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";
|
||||||
|
}
|
252
lib/plugin/pl_player/view.dart
Normal file
252
lib/plugin/pl_player/view.dart
Normal file
@ -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<PLVideoPlayer> createState() => _PLVideoPlayerState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||||
|
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<Offset>(
|
||||||
|
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 {}
|
57
lib/plugin/pl_player/widgets/app_bar_ani.dart
Normal file
57
lib/plugin/pl_player/widgets/app_bar_ani.dart
Normal file
@ -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<Offset>(
|
||||||
|
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: <Color>[
|
||||||
|
Colors.transparent,
|
||||||
|
Colors.black54,
|
||||||
|
],
|
||||||
|
tileMode: TileMode.mirror,
|
||||||
|
)
|
||||||
|
: const LinearGradient(
|
||||||
|
begin: Alignment.topCenter,
|
||||||
|
end: Alignment.bottomCenter,
|
||||||
|
colors: <Color>[
|
||||||
|
Colors.transparent,
|
||||||
|
Colors.black54,
|
||||||
|
],
|
||||||
|
tileMode: TileMode.mirror,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: child,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
149
lib/plugin/pl_player/widgets/bottom_control.dart
Normal file
149
lib/plugin/pl_player/widgets/bottom_control.dart
Normal file
@ -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: () => {},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
29
lib/plugin/pl_player/widgets/common_btn.dart
Normal file
29
lib/plugin/pl_player/widgets/common_btn.dart
Normal file
@ -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!,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
67
lib/plugin/pl_player/widgets/header_control.dart
Normal file
67
lib/plugin/pl_player/widgets/header_control.dart
Normal file
@ -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(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -14,6 +14,7 @@ class GStrorage {
|
|||||||
static late final Box historyword;
|
static late final Box historyword;
|
||||||
static late final Box localCache;
|
static late final Box localCache;
|
||||||
static late final Box setting;
|
static late final Box setting;
|
||||||
|
static late final Box video;
|
||||||
|
|
||||||
static Future<void> init() async {
|
static Future<void> init() async {
|
||||||
final dir = await getApplicationDocumentsDirectory();
|
final dir = await getApplicationDocumentsDirectory();
|
||||||
@ -48,6 +49,8 @@ class GStrorage {
|
|||||||
hotKeyword = await Hive.openBox('hotKeyword');
|
hotKeyword = await Hive.openBox('hotKeyword');
|
||||||
// 搜索历史
|
// 搜索历史
|
||||||
historyword = await Hive.openBox('historyWord');
|
historyword = await Hive.openBox('historyWord');
|
||||||
|
// 视频设置
|
||||||
|
video = await Hive.openBox('video');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,3 +74,12 @@ class LocalCacheKey {
|
|||||||
// 历史记录暂停状态 默认false
|
// 历史记录暂停状态 默认false
|
||||||
static const String historyStatus = 'historyStatus';
|
static const String historyStatus = 'historyStatus';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class VideoBoxKey {
|
||||||
|
// 视频比例
|
||||||
|
static const String videoFit = 'videoFit';
|
||||||
|
// 亮度
|
||||||
|
static const String videoBrightness = 'videoBrightness';
|
||||||
|
// 倍速
|
||||||
|
static const String videoSpeed = 'videoSpeed';
|
||||||
|
}
|
||||||
|
@ -7,33 +7,21 @@
|
|||||||
#include "generated_plugin_registrant.h"
|
#include "generated_plugin_registrant.h"
|
||||||
|
|
||||||
#include <dynamic_color/dynamic_color_plugin.h>
|
#include <dynamic_color/dynamic_color_plugin.h>
|
||||||
#include <flutter_meedu_media_kit/flutter_meedu_media_kit_plugin.h>
|
|
||||||
#include <media_kit_libs_linux/media_kit_libs_linux_plugin.h>
|
#include <media_kit_libs_linux/media_kit_libs_linux_plugin.h>
|
||||||
#include <media_kit_video/media_kit_video_plugin.h>
|
#include <media_kit_video/media_kit_video_plugin.h>
|
||||||
#include <screen_retriever/screen_retriever_plugin.h>
|
|
||||||
#include <url_launcher_linux/url_launcher_plugin.h>
|
#include <url_launcher_linux/url_launcher_plugin.h>
|
||||||
#include <window_manager/window_manager_plugin.h>
|
|
||||||
|
|
||||||
void fl_register_plugins(FlPluginRegistry* registry) {
|
void fl_register_plugins(FlPluginRegistry* registry) {
|
||||||
g_autoptr(FlPluginRegistrar) dynamic_color_registrar =
|
g_autoptr(FlPluginRegistrar) dynamic_color_registrar =
|
||||||
fl_plugin_registry_get_registrar_for_plugin(registry, "DynamicColorPlugin");
|
fl_plugin_registry_get_registrar_for_plugin(registry, "DynamicColorPlugin");
|
||||||
dynamic_color_plugin_register_with_registrar(dynamic_color_registrar);
|
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 =
|
g_autoptr(FlPluginRegistrar) media_kit_libs_linux_registrar =
|
||||||
fl_plugin_registry_get_registrar_for_plugin(registry, "MediaKitLibsLinuxPlugin");
|
fl_plugin_registry_get_registrar_for_plugin(registry, "MediaKitLibsLinuxPlugin");
|
||||||
media_kit_libs_linux_plugin_register_with_registrar(media_kit_libs_linux_registrar);
|
media_kit_libs_linux_plugin_register_with_registrar(media_kit_libs_linux_registrar);
|
||||||
g_autoptr(FlPluginRegistrar) media_kit_video_registrar =
|
g_autoptr(FlPluginRegistrar) media_kit_video_registrar =
|
||||||
fl_plugin_registry_get_registrar_for_plugin(registry, "MediaKitVideoPlugin");
|
fl_plugin_registry_get_registrar_for_plugin(registry, "MediaKitVideoPlugin");
|
||||||
media_kit_video_plugin_register_with_registrar(media_kit_video_registrar);
|
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 =
|
g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
|
||||||
fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
|
fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
|
||||||
url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
|
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);
|
|
||||||
}
|
}
|
||||||
|
@ -4,12 +4,9 @@
|
|||||||
|
|
||||||
list(APPEND FLUTTER_PLUGIN_LIST
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
dynamic_color
|
dynamic_color
|
||||||
flutter_meedu_media_kit
|
|
||||||
media_kit_libs_linux
|
media_kit_libs_linux
|
||||||
media_kit_video
|
media_kit_video
|
||||||
screen_retriever
|
|
||||||
url_launcher_linux
|
url_launcher_linux
|
||||||
window_manager
|
|
||||||
)
|
)
|
||||||
|
|
||||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||||
|
@ -8,33 +8,25 @@ import Foundation
|
|||||||
import connectivity_plus
|
import connectivity_plus
|
||||||
import device_info_plus
|
import device_info_plus
|
||||||
import dynamic_color
|
import dynamic_color
|
||||||
import flutter_meedu_media_kit
|
|
||||||
import media_kit_libs_macos_video
|
import media_kit_libs_macos_video
|
||||||
import media_kit_video
|
import media_kit_video
|
||||||
import package_info_plus
|
import package_info_plus
|
||||||
import path_provider_foundation
|
import path_provider_foundation
|
||||||
import screen_brightness_macos
|
import screen_brightness_macos
|
||||||
import screen_retriever
|
|
||||||
import share_plus
|
import share_plus
|
||||||
import shared_preferences_foundation
|
|
||||||
import sqflite
|
import sqflite
|
||||||
import wakelock_plus
|
import wakelock_plus
|
||||||
import window_manager
|
|
||||||
|
|
||||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||||
ConnectivityPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlugin"))
|
ConnectivityPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlugin"))
|
||||||
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
|
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
|
||||||
DynamicColorPlugin.register(with: registry.registrar(forPlugin: "DynamicColorPlugin"))
|
DynamicColorPlugin.register(with: registry.registrar(forPlugin: "DynamicColorPlugin"))
|
||||||
FlutterMeeduMediaKitPlugin.register(with: registry.registrar(forPlugin: "FlutterMeeduMediaKitPlugin"))
|
|
||||||
MediaKitLibsMacosVideoPlugin.register(with: registry.registrar(forPlugin: "MediaKitLibsMacosVideoPlugin"))
|
MediaKitLibsMacosVideoPlugin.register(with: registry.registrar(forPlugin: "MediaKitLibsMacosVideoPlugin"))
|
||||||
MediaKitVideoPlugin.register(with: registry.registrar(forPlugin: "MediaKitVideoPlugin"))
|
MediaKitVideoPlugin.register(with: registry.registrar(forPlugin: "MediaKitVideoPlugin"))
|
||||||
FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin"))
|
FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin"))
|
||||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||||
ScreenBrightnessMacosPlugin.register(with: registry.registrar(forPlugin: "ScreenBrightnessMacosPlugin"))
|
ScreenBrightnessMacosPlugin.register(with: registry.registrar(forPlugin: "ScreenBrightnessMacosPlugin"))
|
||||||
ScreenRetrieverPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverPlugin"))
|
|
||||||
SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin"))
|
SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin"))
|
||||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
|
||||||
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
|
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
|
||||||
WakelockPlusMacosPlugin.register(with: registry.registrar(forPlugin: "WakelockPlusMacosPlugin"))
|
WakelockPlusMacosPlugin.register(with: registry.registrar(forPlugin: "WakelockPlusMacosPlugin"))
|
||||||
WindowManagerPlugin.register(with: registry.registrar(forPlugin: "WindowManagerPlugin"))
|
|
||||||
}
|
}
|
||||||
|
161
pubspec.lock
161
pubspec.lock
@ -41,6 +41,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.11.0"
|
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:
|
boolean_selector:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -419,23 +427,6 @@ packages:
|
|||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
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:
|
flutter_smart_dialog:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -444,14 +435,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.9.3+2"
|
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:
|
flutter_test:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description: flutter
|
description: flutter
|
||||||
@ -478,14 +461,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.2.0"
|
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:
|
get:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -671,23 +646,23 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "0.2.0"
|
version: "0.2.0"
|
||||||
media_kit:
|
media_kit:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: media_kit
|
name: media_kit
|
||||||
sha256: "1e6ea59b5f81d1db038ac5394c38cd3ad2f5e50bdf36f12154dee6fa04221bcb"
|
sha256: "272a9f1dd77ed57b48707fdb0ec0e4a048ef958feccc0d0dd751135fe924b63a"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.0"
|
version: "1.1.1"
|
||||||
media_kit_libs_android_video:
|
media_kit_libs_android_video:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: media_kit_libs_android_video
|
name: media_kit_libs_android_video
|
||||||
sha256: b7dd06bdc615c7dc48c3718d81c1adfcb902be0aaa6310ed6a8caf524301553a
|
sha256: ddb0d26ecba72bf7117e37e29b6a50f4ba198bbccb4e47246cae1812087dc721
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.0"
|
version: "1.3.0"
|
||||||
media_kit_libs_ios_video:
|
media_kit_libs_ios_video:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: media_kit_libs_ios_video
|
name: media_kit_libs_ios_video
|
||||||
sha256: c691220334c1828e1fd24db4ebbdfdd6c576f0345bc6cc435b355798d6e4b7ed
|
sha256: c691220334c1828e1fd24db4ebbdfdd6c576f0345bc6cc435b355798d6e4b7ed
|
||||||
@ -695,7 +670,7 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.5"
|
version: "1.0.5"
|
||||||
media_kit_libs_linux:
|
media_kit_libs_linux:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: media_kit_libs_linux
|
name: media_kit_libs_linux
|
||||||
sha256: "21acc71cbae3518b3aeef9023a6a3a3decb579a40153764333814987ccd61040"
|
sha256: "21acc71cbae3518b3aeef9023a6a3a3decb579a40153764333814987ccd61040"
|
||||||
@ -703,7 +678,7 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.2"
|
version: "1.0.2"
|
||||||
media_kit_libs_macos_video:
|
media_kit_libs_macos_video:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: media_kit_libs_macos_video
|
name: media_kit_libs_macos_video
|
||||||
sha256: "28ad624666cd20ed78f96a26917dddf6f286ea4bab21620676cc59ba62f3d3e5"
|
sha256: "28ad624666cd20ed78f96a26917dddf6f286ea4bab21620676cc59ba62f3d3e5"
|
||||||
@ -711,15 +686,15 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.6"
|
version: "1.0.6"
|
||||||
media_kit_libs_windows_video:
|
media_kit_libs_windows_video:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: media_kit_libs_windows_video
|
name: media_kit_libs_windows_video
|
||||||
sha256: "4aa12f61c9989c4d7159ed0c15640d645dbe59026ac9057a3651d026a409dcb9"
|
sha256: b343e644927982a2ef3db63877b36d84bdda8173d8318ca0d1c68c1ea8a35982
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.4"
|
version: "1.0.5"
|
||||||
media_kit_native_event_loop:
|
media_kit_native_event_loop:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: media_kit_native_event_loop
|
name: media_kit_native_event_loop
|
||||||
sha256: "5351f0c28124b5358756515d8619abad182cdefe967468d7fb5b274737cc2f59"
|
sha256: "5351f0c28124b5358756515d8619abad182cdefe967468d7fb5b274737cc2f59"
|
||||||
@ -727,21 +702,13 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.6"
|
version: "1.0.6"
|
||||||
media_kit_video:
|
media_kit_video:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: media_kit_video
|
name: media_kit_video
|
||||||
sha256: "55edf96bcf08f8bd158018d77afd10d5411f9f3b657fb34a4cd5690caec91596"
|
sha256: "3ac0403d67710dfb2bf6aabfa6caff1b163e70fb7e1a88423bc1be569b4df6b3"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.0"
|
version: "1.1.1"
|
||||||
meedu:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: meedu
|
|
||||||
sha256: "2cac0971ea211b1a18e3c4f038369f8ac57401760342da16d9e4e9d043f7de38"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "8.0.2"
|
|
||||||
meta:
|
meta:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -975,7 +942,7 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.2"
|
version: "1.0.2"
|
||||||
screen_brightness:
|
screen_brightness:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: screen_brightness
|
name: screen_brightness
|
||||||
sha256: "62fd61a64e68b32b98b840bad7d8b6822bbc40e63c2b569a5f85528484c86b41"
|
sha256: "62fd61a64e68b32b98b840bad7d8b6822bbc40e63c2b569a5f85528484c86b41"
|
||||||
@ -1022,14 +989,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.1.1"
|
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:
|
share_plus:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -1046,62 +1005,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.2.1"
|
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:
|
shelf:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -1236,7 +1139,7 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.2"
|
version: "1.3.2"
|
||||||
universal_platform:
|
universal_platform:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: universal_platform
|
name: universal_platform
|
||||||
sha256: d315be0f6641898b280ffa34e2ddb14f3d12b1a37882557869646e0cc363d0cc
|
sha256: d315be0f6641898b280ffa34e2ddb14f3d12b1a37882557869646e0cc363d0cc
|
||||||
@ -1308,7 +1211,7 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "0.4.0+2"
|
version: "0.4.0+2"
|
||||||
volume_controller:
|
volume_controller:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: volume_controller
|
name: volume_controller
|
||||||
sha256: "189bdc7a554f476b412e4c8b2f474562b09d74bc458c23667356bce3ca1d48c9"
|
sha256: "189bdc7a554f476b412e4c8b2f474562b09d74bc458c23667356bce3ca1d48c9"
|
||||||
@ -1316,7 +1219,7 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.7"
|
version: "2.0.7"
|
||||||
wakelock_plus:
|
wakelock_plus:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: wakelock_plus
|
name: wakelock_plus
|
||||||
sha256: aac3f3258f01781ec9212df94eecef1eb9ba9350e106728def405baa096ba413
|
sha256: aac3f3258f01781ec9212df94eecef1eb9ba9350e106728def405baa096ba413
|
||||||
@ -1411,14 +1314,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.1"
|
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:
|
xdg_directories:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
28
pubspec.yaml
28
pubspec.yaml
@ -80,16 +80,34 @@ dependencies:
|
|||||||
# 下滑关闭
|
# 下滑关闭
|
||||||
dismissible_page: ^1.0.2
|
dismissible_page: ^1.0.2
|
||||||
# 媒体播放
|
# 媒体播放
|
||||||
flutter_meedu_media_kit:
|
# flutter_meedu_media_kit:
|
||||||
# path: /Users/rr/Desktop/code/flutter_meedu_media_kit/package
|
# path: /Users/rr/Desktop/code/flutter_meedu_media_kit/package
|
||||||
git:
|
# git:
|
||||||
url: https://github.com/guozhigq/flutter_meedu_media_kit.git
|
# url: https://github.com/guozhigq/flutter_meedu_media_kit.git
|
||||||
ref: feature-custom
|
# ref: feature-custom
|
||||||
path: package
|
# path: package
|
||||||
custom_sliding_segmented_control: ^1.7.5
|
custom_sliding_segmented_control: ^1.7.5
|
||||||
# 加密
|
# 加密
|
||||||
crypto: ^3.0.3
|
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:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
@ -8,26 +8,18 @@
|
|||||||
|
|
||||||
#include <connectivity_plus/connectivity_plus_windows_plugin.h>
|
#include <connectivity_plus/connectivity_plus_windows_plugin.h>
|
||||||
#include <dynamic_color/dynamic_color_plugin_c_api.h>
|
#include <dynamic_color/dynamic_color_plugin_c_api.h>
|
||||||
#include <flutter_meedu_media_kit/flutter_meedu_media_kit_plugin_c_api.h>
|
|
||||||
#include <fullscreen_window/fullscreen_window_plugin_c_api.h>
|
|
||||||
#include <media_kit_libs_windows_video/media_kit_libs_windows_video_plugin_c_api.h>
|
#include <media_kit_libs_windows_video/media_kit_libs_windows_video_plugin_c_api.h>
|
||||||
#include <media_kit_video/media_kit_video_plugin_c_api.h>
|
#include <media_kit_video/media_kit_video_plugin_c_api.h>
|
||||||
#include <permission_handler_windows/permission_handler_windows_plugin.h>
|
#include <permission_handler_windows/permission_handler_windows_plugin.h>
|
||||||
#include <screen_brightness_windows/screen_brightness_windows_plugin.h>
|
#include <screen_brightness_windows/screen_brightness_windows_plugin.h>
|
||||||
#include <screen_retriever/screen_retriever_plugin.h>
|
|
||||||
#include <share_plus/share_plus_windows_plugin_c_api.h>
|
#include <share_plus/share_plus_windows_plugin_c_api.h>
|
||||||
#include <url_launcher_windows/url_launcher_windows.h>
|
#include <url_launcher_windows/url_launcher_windows.h>
|
||||||
#include <window_manager/window_manager_plugin.h>
|
|
||||||
|
|
||||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||||
ConnectivityPlusWindowsPluginRegisterWithRegistrar(
|
ConnectivityPlusWindowsPluginRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin"));
|
registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin"));
|
||||||
DynamicColorPluginCApiRegisterWithRegistrar(
|
DynamicColorPluginCApiRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("DynamicColorPluginCApi"));
|
registry->GetRegistrarForPlugin("DynamicColorPluginCApi"));
|
||||||
FlutterMeeduMediaKitPluginCApiRegisterWithRegistrar(
|
|
||||||
registry->GetRegistrarForPlugin("FlutterMeeduMediaKitPluginCApi"));
|
|
||||||
FullscreenWindowPluginCApiRegisterWithRegistrar(
|
|
||||||
registry->GetRegistrarForPlugin("FullscreenWindowPluginCApi"));
|
|
||||||
MediaKitLibsWindowsVideoPluginCApiRegisterWithRegistrar(
|
MediaKitLibsWindowsVideoPluginCApiRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("MediaKitLibsWindowsVideoPluginCApi"));
|
registry->GetRegistrarForPlugin("MediaKitLibsWindowsVideoPluginCApi"));
|
||||||
MediaKitVideoPluginCApiRegisterWithRegistrar(
|
MediaKitVideoPluginCApiRegisterWithRegistrar(
|
||||||
@ -36,12 +28,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
|
|||||||
registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin"));
|
registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin"));
|
||||||
ScreenBrightnessWindowsPluginRegisterWithRegistrar(
|
ScreenBrightnessWindowsPluginRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("ScreenBrightnessWindowsPlugin"));
|
registry->GetRegistrarForPlugin("ScreenBrightnessWindowsPlugin"));
|
||||||
ScreenRetrieverPluginRegisterWithRegistrar(
|
|
||||||
registry->GetRegistrarForPlugin("ScreenRetrieverPlugin"));
|
|
||||||
SharePlusWindowsPluginCApiRegisterWithRegistrar(
|
SharePlusWindowsPluginCApiRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi"));
|
registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi"));
|
||||||
UrlLauncherWindowsRegisterWithRegistrar(
|
UrlLauncherWindowsRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
|
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
|
||||||
WindowManagerPluginRegisterWithRegistrar(
|
|
||||||
registry->GetRegistrarForPlugin("WindowManagerPlugin"));
|
|
||||||
}
|
}
|
||||||
|
@ -5,16 +5,12 @@
|
|||||||
list(APPEND FLUTTER_PLUGIN_LIST
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
connectivity_plus
|
connectivity_plus
|
||||||
dynamic_color
|
dynamic_color
|
||||||
flutter_meedu_media_kit
|
|
||||||
fullscreen_window
|
|
||||||
media_kit_libs_windows_video
|
media_kit_libs_windows_video
|
||||||
media_kit_video
|
media_kit_video
|
||||||
permission_handler_windows
|
permission_handler_windows
|
||||||
screen_brightness_windows
|
screen_brightness_windows
|
||||||
screen_retriever
|
|
||||||
share_plus
|
share_plus
|
||||||
url_launcher_windows
|
url_launcher_windows
|
||||||
window_manager
|
|
||||||
)
|
)
|
||||||
|
|
||||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||||
|
Reference in New Issue
Block a user