feat: 简单实现弹幕功能

This commit is contained in:
guozhigq
2023-08-29 23:10:22 +08:00
parent 5f730af347
commit dfbe3b1f6c
23 changed files with 646 additions and 241 deletions

View File

@ -11,18 +11,15 @@ import 'package:hive/hive.dart';
import 'package:media_kit/media_kit.dart';
import 'package:media_kit_video/media_kit_video.dart';
import 'package:pilipala/http/video.dart';
import 'package:pilipala/plugin/pl_player/models/data_source.dart';
import 'package:pilipala/plugin/pl_player/index.dart';
import 'package:pilipala/utils/feed_back.dart';
import 'package:pilipala/utils/storage.dart';
import 'package:screen_brightness/screen_brightness.dart';
import 'package:universal_platform/universal_platform.dart';
// import 'package:wakelock_plus/wakelock_plus.dart';
import 'models/data_status.dart';
import 'models/play_speed.dart';
import 'models/play_status.dart';
Box videoStorage = GStrorage.video;
Box setting = GStrorage.setting;
class PlPlayerController {
Player? _videoPlayerController;
@ -84,6 +81,7 @@ class PlPlayerController {
int _cid = 0;
int _heartDuration = 0;
bool _enableHeart = true;
bool _isFirstTime = true;
Timer? _timer;
Timer? _timerForSeek;
@ -105,6 +103,7 @@ class PlPlayerController {
];
PreferredSizeWidget? headerControl;
Widget? danmuWidget;
/// 数据加载监听
Stream<DataStatus> get onDataStatusChanged => dataStatus.status.stream;
@ -195,6 +194,9 @@ class PlPlayerController {
///
Rx<String> get videoType => _videoType;
/// 弹幕开关
Rx<bool> isOpenDanmu = true.obs;
// 添加一个私有构造函数
PlPlayerController._() {
_videoType = videoType;
@ -248,6 +250,8 @@ class PlPlayerController {
int cid = 0,
// 历史记录开关
bool enableHeart = true,
// 是否首次加载
bool isFirstTime = true,
}) async {
try {
_autoPlay = autoplay;
@ -261,6 +265,7 @@ class PlPlayerController {
_bvid = bvid;
_cid = cid;
_enableHeart = enableHeart;
_isFirstTime = isFirstTime;
if (_videoPlayerController != null &&
_videoPlayerController!.state.playing) {
@ -281,6 +286,12 @@ class PlPlayerController {
if (!_listenersInitialized) {
startListeners();
}
bool autoEnterFullcreen =
setting.get(SettingBoxKey.enableAutoEnter, defaultValue: false);
if (autoEnterFullcreen && _isFirstTime) {
await Future.delayed(const Duration(milliseconds: 100));
triggerFullScreen();
}
} catch (err) {
dataStatus.status.value = DataStatus.error;
print('plPlayer err: $err');
@ -397,6 +408,8 @@ class PlPlayerController {
}
List<StreamSubscription> subscriptions = [];
final List<Function(Duration position)> _positionListeners = [];
final List<Function(PlayerStatus status)> _statusListeners = [];
/// 播放事件监听
void startListeners() {
@ -408,11 +421,21 @@ class PlPlayerController {
} else {
// playerStatus.status.value = PlayerStatus.paused;
}
/// 触发回调事件
for (var element in _statusListeners) {
element(event ? PlayerStatus.playing : PlayerStatus.paused);
}
makeHeartBeat(_position.value.inSeconds, type: 'status');
}),
videoPlayerController!.stream.completed.listen((event) {
if (event) {
playerStatus.status.value = PlayerStatus.completed;
/// 触发回调事件
for (var element in _statusListeners) {
element(PlayerStatus.completed);
}
} else {
// playerStatus.status.value = PlayerStatus.playing;
}
@ -423,6 +446,11 @@ class PlPlayerController {
if (!isSliderMoving.value) {
_sliderPosition.value = event;
}
/// 触发回调事件
for (var element in _positionListeners) {
element(event);
}
makeHeartBeat(event.inSeconds);
}),
videoPlayerController!.stream.duration.listen((event) {
@ -714,6 +742,79 @@ class PlPlayerController {
_isFullScreen.value = val;
}
// 全屏
Future<void> triggerFullScreen({bool status = true}) async {
FullScreenMode mode = FullScreenModeCode.fromCode(
setting.get(SettingBoxKey.fullScreenMode, defaultValue: 0))!;
if (!isFullScreen.value && status) {
/// 按照视频宽高比决定全屏方向
switch (mode) {
case FullScreenMode.auto:
if (direction.value == 'horizontal') {
/// 进入全屏
await enterFullScreen();
// 横屏
await landScape();
} else {
// 竖屏
await verticalScreen();
}
break;
case FullScreenMode.vertical:
/// 进入全屏
await enterFullScreen();
// 横屏
await verticalScreen();
break;
case FullScreenMode.horizontal:
/// 进入全屏
await enterFullScreen();
// 横屏
await landScape();
break;
}
toggleFullScreen(true);
print(headerControl);
print(danmuWidget);
var result = await showDialog(
context: Get.context!,
useSafeArea: false,
builder: (context) => Dialog.fullscreen(
backgroundColor: Colors.black,
child: PLVideoPlayer(
controller: this,
headerControl: headerControl,
danmuWidget: danmuWidget,
),
),
);
if (result == null) {
// 退出全屏
exitFullScreen();
await verticalScreen();
toggleFullScreen(false);
}
} else if (isFullScreen.value) {
Get.back();
exitFullScreen();
await verticalScreen();
toggleFullScreen(false);
}
}
void addPositionListener(Function(Duration position) listener) =>
_positionListeners.add(listener);
void removePositionListener(Function(Duration position) listener) =>
_positionListeners.remove(listener);
void addStatusLister(Function(PlayerStatus status) listener) =>
_statusListeners.add(listener);
void removeStatusLister(Function(PlayerStatus status) listener) =>
_statusListeners.remove(listener);
/// 截屏
Future screenshot() async {
final Uint8List? screenshot =