mirror of
https://github.com/HChaZZY/PiliPlus.git
synced 2025-12-19 08:36:17 +08:00
@@ -1,4 +1,3 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:PiliPlus/grpc/bilibili/community/service/dm/v1.pb.dart';
|
||||
@@ -14,13 +13,15 @@ import 'package:get/get.dart';
|
||||
class PlDanmaku extends StatefulWidget {
|
||||
final int cid;
|
||||
final PlPlayerController playerController;
|
||||
final bool? isPipMode;
|
||||
final bool isPipMode;
|
||||
final bool isFullScreen;
|
||||
|
||||
const PlDanmaku({
|
||||
super.key,
|
||||
required this.cid,
|
||||
required this.playerController,
|
||||
this.isPipMode,
|
||||
this.isPipMode = false,
|
||||
required this.isFullScreen,
|
||||
});
|
||||
|
||||
@override
|
||||
@@ -33,8 +34,6 @@ class _PlDanmakuState extends State<PlDanmaku> {
|
||||
late PlDanmakuController _plDanmakuController;
|
||||
DanmakuController? _controller;
|
||||
int latestAddedPosition = -1;
|
||||
bool? _isFullScreen;
|
||||
StreamSubscription? _listenerFS;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -53,20 +52,27 @@ class _PlDanmakuState extends State<PlDanmaku> {
|
||||
playerController
|
||||
..addStatusLister(playerListener)
|
||||
..addPositionListener(videoPositionListen);
|
||||
_listenerFS = playerController.isFullScreen.listen((isFullScreen) {
|
||||
if (isFullScreen != _isFullScreen) {
|
||||
_isFullScreen = isFullScreen;
|
||||
if (_controller != null) {
|
||||
_controller!.updateOption(
|
||||
_controller!.option.copyWith(
|
||||
fontSize: _getFontSize(isFullScreen),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(PlDanmaku oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (oldWidget.isPipMode != widget.isPipMode ||
|
||||
oldWidget.isFullScreen != widget.isFullScreen) {
|
||||
_updateFontSize();
|
||||
}
|
||||
}
|
||||
|
||||
void _updateFontSize() {
|
||||
_controller?.updateOption(
|
||||
_controller!.option.copyWith(fontSize: _fontSize),
|
||||
);
|
||||
}
|
||||
|
||||
double get _fontSize => !widget.isFullScreen || widget.isPipMode
|
||||
? 15 * playerController.danmakuFontScale
|
||||
: 15 * playerController.danmakuFontScaleFS;
|
||||
|
||||
// 播放器状态监听
|
||||
void playerListener(PlayerStatus? status) {
|
||||
if (status == PlayerStatus.playing) {
|
||||
@@ -77,15 +83,11 @@ class _PlDanmakuState extends State<PlDanmaku> {
|
||||
}
|
||||
|
||||
void videoPositionListen(Duration position) {
|
||||
if (!playerController.enableShowDanmaku.value) {
|
||||
if (_controller == null || !playerController.enableShowDanmaku.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_controller == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!playerController.showDanmaku && widget.isPipMode != true) {
|
||||
if (!playerController.showDanmaku && !widget.isPipMode) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -138,7 +140,6 @@ class _PlDanmakuState extends State<PlDanmaku> {
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_listenerFS?.cancel();
|
||||
playerController
|
||||
..removePositionListener(videoPositionListen)
|
||||
..removeStatusLister(playerListener);
|
||||
@@ -146,45 +147,35 @@ class _PlDanmakuState extends State<PlDanmaku> {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
double _getFontSize(bool isFullScreen) =>
|
||||
!isFullScreen || widget.isPipMode == true
|
||||
? 15 * playerController.danmakuFontScale
|
||||
: 15 * playerController.danmakuFontScaleFS;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return LayoutBuilder(
|
||||
builder: (context, box) {
|
||||
// double initDuration = box.maxWidth / 12;
|
||||
return Obx(
|
||||
() => AnimatedOpacity(
|
||||
opacity: playerController.enableShowDanmaku.value ? 1 : 0,
|
||||
duration: const Duration(milliseconds: 100),
|
||||
child: DanmakuScreen(
|
||||
createdController: (DanmakuController e) {
|
||||
playerController.danmakuController = _controller = e;
|
||||
},
|
||||
option: DanmakuOption(
|
||||
fontSize: _getFontSize(playerController.isFullScreen.value),
|
||||
fontWeight: playerController.fontWeight,
|
||||
area: playerController.showArea,
|
||||
opacity: playerController.danmakuOpacity,
|
||||
hideTop: playerController.blockTypes.contains(5),
|
||||
hideScroll: playerController.blockTypes.contains(2),
|
||||
hideBottom: playerController.blockTypes.contains(4),
|
||||
duration:
|
||||
playerController.danmakuDuration /
|
||||
playerController.playbackSpeed,
|
||||
staticDuration:
|
||||
playerController.danmakuStaticDuration /
|
||||
playerController.playbackSpeed,
|
||||
strokeWidth: playerController.strokeWidth,
|
||||
lineHeight: playerController.danmakuLineHeight,
|
||||
),
|
||||
),
|
||||
return Obx(
|
||||
() => AnimatedOpacity(
|
||||
opacity: playerController.enableShowDanmaku.value ? 1 : 0,
|
||||
duration: const Duration(milliseconds: 100),
|
||||
child: DanmakuScreen(
|
||||
createdController: (DanmakuController e) {
|
||||
playerController.danmakuController = _controller = e;
|
||||
},
|
||||
option: DanmakuOption(
|
||||
fontSize: _fontSize,
|
||||
fontWeight: playerController.fontWeight,
|
||||
area: playerController.showArea,
|
||||
opacity: playerController.danmakuOpacity,
|
||||
hideTop: playerController.blockTypes.contains(5),
|
||||
hideScroll: playerController.blockTypes.contains(2),
|
||||
hideBottom: playerController.blockTypes.contains(4),
|
||||
duration:
|
||||
playerController.danmakuDuration /
|
||||
playerController.playbackSpeed,
|
||||
staticDuration:
|
||||
playerController.danmakuStaticDuration /
|
||||
playerController.playbackSpeed,
|
||||
strokeWidth: playerController.strokeWidth,
|
||||
lineHeight: playerController.danmakuLineHeight,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ class HistoryItem extends StatelessWidget {
|
||||
final hasDuration = item.duration != null && item.duration != 0;
|
||||
int aid = item.history.oid!;
|
||||
String bvid = item.history.bvid ?? IdUtils.av2bv(aid);
|
||||
final business = item.history.business;
|
||||
return Material(
|
||||
type: MaterialType.transparency,
|
||||
child: InkWell(
|
||||
@@ -44,25 +45,25 @@ class HistoryItem extends StatelessWidget {
|
||||
ctr.onSelect(item);
|
||||
return;
|
||||
}
|
||||
if (item.history.business?.contains('article') == true) {
|
||||
if (business?.contains('article') == true) {
|
||||
PageUtils.toDupNamed(
|
||||
'/articlePage',
|
||||
parameters: {
|
||||
'id': item.history.business == 'article-list'
|
||||
'id': business == 'article-list'
|
||||
? '${item.history.cid}'
|
||||
: '${item.history.oid}',
|
||||
'type': 'read',
|
||||
},
|
||||
);
|
||||
} else if (item.history.business == 'live') {
|
||||
} else if (business == 'live') {
|
||||
if (item.liveStatus == 1) {
|
||||
PageUtils.toLiveRoom(item.history.oid);
|
||||
} else {
|
||||
SmartDialog.showToast('直播未开播');
|
||||
}
|
||||
} else if (item.history.business == 'pgc') {
|
||||
} else if (business == 'pgc') {
|
||||
PageUtils.viewPgc(epId: item.history.epid);
|
||||
} else if (item.history.business == 'cheese') {
|
||||
} else if (business == 'cheese') {
|
||||
if (item.uri?.isNotEmpty == true) {
|
||||
PageUtils.viewPgcFromUri(
|
||||
item.uri!,
|
||||
@@ -144,8 +145,7 @@ class HistoryItem extends StatelessWidget {
|
||||
top: 6.0,
|
||||
right: 6.0,
|
||||
type:
|
||||
item.history.business ==
|
||||
HistoryBusinessType.live.type &&
|
||||
business == HistoryBusinessType.live.type &&
|
||||
item.liveStatus != 1
|
||||
? PBadgeType.gray
|
||||
: PBadgeType.primary,
|
||||
@@ -255,11 +255,11 @@ class HistoryItem extends StatelessWidget {
|
||||
],
|
||||
),
|
||||
),
|
||||
if (item.history.business != 'pgc' &&
|
||||
if (business != 'pgc' &&
|
||||
item.badge != '番剧' &&
|
||||
item.tagName?.contains('动画') != true &&
|
||||
item.history.business != 'live' &&
|
||||
item.history.business?.contains('article') != true)
|
||||
business != 'live' &&
|
||||
business?.contains('article') != true)
|
||||
PopupMenuItem<String>(
|
||||
onTap: () async {
|
||||
var res = await UserHttp.toViewLater(
|
||||
@@ -277,8 +277,7 @@ class HistoryItem extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
PopupMenuItem<String>(
|
||||
onTap: () =>
|
||||
onDelete(item.kid!, item.history.business!),
|
||||
onTap: () => onDelete(item.kid!, business!),
|
||||
height: 35,
|
||||
child: const Row(
|
||||
children: [
|
||||
|
||||
@@ -40,8 +40,6 @@ class LiveRoomController extends GetxController {
|
||||
|
||||
// dm
|
||||
LiveDmInfoData? dmInfo;
|
||||
bool showDanmaku = true;
|
||||
DanmakuController? controller;
|
||||
List<RichTextItem>? savedDanmaku;
|
||||
RxList<dynamic> messages = [].obs;
|
||||
RxBool disableAutoScroll = false.obs;
|
||||
@@ -289,8 +287,9 @@ class LiveRoomController extends GetxController {
|
||||
'emots': extra['emots'],
|
||||
'uemote': first[13],
|
||||
});
|
||||
if (showDanmaku) {
|
||||
controller?.addDanmaku(
|
||||
|
||||
if (plPlayerController.showDanmaku) {
|
||||
plPlayerController.danmakuController?.addDanmaku(
|
||||
DanmakuContentItem(
|
||||
extra['content'],
|
||||
color: DmUtils.decimalToColor(extra['color']),
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'dart:ui';
|
||||
|
||||
@@ -12,6 +11,7 @@ import 'package:PiliPlus/pages/live_room/widgets/chat.dart';
|
||||
import 'package:PiliPlus/pages/live_room/widgets/header_control.dart';
|
||||
import 'package:PiliPlus/plugin/pl_player/controller.dart';
|
||||
import 'package:PiliPlus/plugin/pl_player/models/play_status.dart';
|
||||
import 'package:PiliPlus/plugin/pl_player/utils/fullscreen.dart';
|
||||
import 'package:PiliPlus/plugin/pl_player/view.dart';
|
||||
import 'package:PiliPlus/services/service_locator.dart';
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
@@ -23,7 +23,7 @@ import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:canvas_danmaku/canvas_danmaku.dart';
|
||||
import 'package:floating/floating.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart' show MethodChannel, SystemUiOverlayStyle;
|
||||
import 'package:flutter/services.dart' show SystemUiOverlayStyle;
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:screen_brightness/screen_brightness.dart';
|
||||
@@ -42,14 +42,8 @@ class _LiveRoomPageState extends State<LiveRoomPage>
|
||||
late final PlPlayerController plPlayerController;
|
||||
bool get isFullScreen => plPlayerController.isFullScreen.value;
|
||||
|
||||
StreamSubscription? _listener;
|
||||
|
||||
bool? _isFullScreen;
|
||||
bool? _isPipMode;
|
||||
|
||||
final GlobalKey chatKey = GlobalKey();
|
||||
final GlobalKey playerKey = GlobalKey();
|
||||
final GlobalKey videoPlayerKey = GlobalKey();
|
||||
|
||||
final Color _color = const Color(0xFFEEEEEE);
|
||||
|
||||
@@ -66,12 +60,6 @@ class _LiveRoomPageState extends State<LiveRoomPage>
|
||||
plPlayerController
|
||||
..autoEnterFullscreen()
|
||||
..addStatusLister(playerListener);
|
||||
_listener = plPlayerController.isFullScreen.listen((isFullScreen) {
|
||||
if (isFullScreen != _isFullScreen) {
|
||||
_isFullScreen = isFullScreen;
|
||||
_updateFontSize();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void playerListener(PlayerStatus? status) {
|
||||
@@ -86,31 +74,9 @@ class _LiveRoomPageState extends State<LiveRoomPage>
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _updateFontSize() async {
|
||||
if (Platform.isAndroid) {
|
||||
_isPipMode = await const MethodChannel(
|
||||
"floating",
|
||||
).invokeMethod('inPipAlready');
|
||||
}
|
||||
if (_liveRoomController.controller != null) {
|
||||
_liveRoomController.controller!.updateOption(
|
||||
_liveRoomController.controller!.option.copyWith(
|
||||
fontSize: _getFontSize(isFullScreen),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
double _getFontSize(bool isFullScreen) {
|
||||
return isFullScreen == false || _isPipMode == true
|
||||
? 15 * plPlayerController.danmakuFontScale
|
||||
: 15 * plPlayerController.danmakuFontScaleFS;
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
videoPlayerServiceHandler.onVideoDetailDispose(heroTag);
|
||||
_listener?.cancel();
|
||||
WidgetsBinding.instance.removeObserver(this);
|
||||
ScreenBrightness.instance.resetApplicationScreenBrightness();
|
||||
PlPlayerController.setPlayCallBack(null);
|
||||
@@ -126,30 +92,38 @@ class _LiveRoomPageState extends State<LiveRoomPage>
|
||||
@override
|
||||
void didChangeAppLifecycleState(AppLifecycleState state) {
|
||||
if (state == AppLifecycleState.resumed) {
|
||||
_liveRoomController.showDanmaku = true;
|
||||
if (!plPlayerController.showDanmaku) {
|
||||
plPlayerController.showDanmaku = true;
|
||||
if (isFullScreen && Platform.isIOS) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (!_liveRoomController.isPortrait.value) {
|
||||
landScape();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
} else if (state == AppLifecycleState.paused) {
|
||||
_liveRoomController.showDanmaku = false;
|
||||
plPlayerController.danmakuController?.clear();
|
||||
plPlayerController
|
||||
..showDanmaku = false
|
||||
..danmakuController?.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
_updateFontSize();
|
||||
});
|
||||
|
||||
final isPortrait = context.isPortrait;
|
||||
if (Platform.isAndroid) {
|
||||
return Floating().isPipMode
|
||||
? videoPlayerPanel()
|
||||
? videoPlayerPanel(isFullScreen, isPipMode: true)
|
||||
: childWhenDisabled(isPortrait);
|
||||
} else {
|
||||
return childWhenDisabled(isPortrait);
|
||||
}
|
||||
}
|
||||
|
||||
Widget videoPlayerPanel({
|
||||
Widget videoPlayerPanel(
|
||||
bool isFullScreen, {
|
||||
bool isPipMode = false,
|
||||
Color? fill,
|
||||
Alignment? alignment,
|
||||
bool needDm = true,
|
||||
@@ -183,36 +157,10 @@ class _LiveRoomPageState extends State<LiveRoomPage>
|
||||
),
|
||||
danmuWidget: !needDm
|
||||
? null
|
||||
: Obx(
|
||||
() => AnimatedOpacity(
|
||||
opacity: plPlayerController.enableShowDanmaku.value
|
||||
? 1
|
||||
: 0,
|
||||
duration: const Duration(milliseconds: 100),
|
||||
child: DanmakuScreen(
|
||||
createdController: (DanmakuController e) {
|
||||
plPlayerController.danmakuController =
|
||||
_liveRoomController.controller = e;
|
||||
},
|
||||
option: DanmakuOption(
|
||||
fontSize: _getFontSize(isFullScreen),
|
||||
fontWeight: plPlayerController.fontWeight,
|
||||
area: plPlayerController.showArea,
|
||||
opacity: plPlayerController.danmakuOpacity,
|
||||
hideTop: plPlayerController.blockTypes.contains(5),
|
||||
hideScroll: plPlayerController.blockTypes.contains(2),
|
||||
hideBottom: plPlayerController.blockTypes.contains(4),
|
||||
duration:
|
||||
plPlayerController.danmakuDuration /
|
||||
plPlayerController.playbackSpeed,
|
||||
staticDuration:
|
||||
plPlayerController.danmakuStaticDuration /
|
||||
plPlayerController.playbackSpeed,
|
||||
strokeWidth: plPlayerController.strokeWidth,
|
||||
lineHeight: plPlayerController.danmakuLineHeight,
|
||||
),
|
||||
),
|
||||
),
|
||||
: LiveDanmaku(
|
||||
plPlayerController: plPlayerController,
|
||||
isFullScreen: isFullScreen,
|
||||
isPipMode: isPipMode,
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -303,18 +251,14 @@ class _LiveRoomPageState extends State<LiveRoomPage>
|
||||
Widget get _buildPH {
|
||||
final isFullScreen = this.isFullScreen;
|
||||
final size = Get.size;
|
||||
Widget child = SizedBox(
|
||||
width: size.width,
|
||||
height: isFullScreen ? size.height : size.width * 9 / 16,
|
||||
child: videoPlayerPanel(),
|
||||
);
|
||||
if (isFullScreen) {
|
||||
return child;
|
||||
}
|
||||
return Column(
|
||||
children: [
|
||||
_buildAppBar,
|
||||
child,
|
||||
if (!isFullScreen) _buildAppBar,
|
||||
SizedBox(
|
||||
width: size.width,
|
||||
height: isFullScreen ? size.height : size.width * 9 / 16,
|
||||
child: videoPlayerPanel(isFullScreen),
|
||||
),
|
||||
..._buildBottomWidget,
|
||||
],
|
||||
);
|
||||
@@ -322,36 +266,38 @@ class _LiveRoomPageState extends State<LiveRoomPage>
|
||||
|
||||
Widget get _buildPP {
|
||||
final isFullScreen = this.isFullScreen;
|
||||
|
||||
final child = videoPlayerPanel(
|
||||
alignment: isFullScreen ? null : Alignment.topCenter,
|
||||
needDm: isFullScreen,
|
||||
Widget child = Stack(
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
Positioned.fill(
|
||||
child: videoPlayerPanel(
|
||||
isFullScreen,
|
||||
alignment: isFullScreen ? null : Alignment.topCenter,
|
||||
needDm: isFullScreen,
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 55,
|
||||
child: Visibility(
|
||||
maintainState: true,
|
||||
visible: !isFullScreen,
|
||||
child: SizedBox(
|
||||
height: Get.height * 0.32,
|
||||
child: _buildChatWidget(true),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
if (isFullScreen) {
|
||||
return child;
|
||||
}
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
_buildAppBar,
|
||||
Expanded(
|
||||
child: Stack(
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
Positioned.fill(child: child),
|
||||
Positioned(
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 55,
|
||||
child: SizedBox(
|
||||
height: Get.height * 0.32,
|
||||
child: _buildChatWidget(true),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(child: child),
|
||||
_buildInputWidget,
|
||||
],
|
||||
);
|
||||
@@ -362,7 +308,6 @@ class _LiveRoomPageState extends State<LiveRoomPage>
|
||||
return AppBar(
|
||||
backgroundColor: Colors.transparent,
|
||||
foregroundColor: Colors.white,
|
||||
toolbarHeight: isFullScreen ? 0 : null,
|
||||
titleTextStyle: const TextStyle(color: Colors.white),
|
||||
title: Obx(
|
||||
() {
|
||||
@@ -482,13 +427,26 @@ class _LiveRoomPageState extends State<LiveRoomPage>
|
||||
() {
|
||||
final isFullScreen = this.isFullScreen;
|
||||
final size = Get.size;
|
||||
Widget child = Container(
|
||||
margin: EdgeInsets.only(
|
||||
bottom: MediaQuery.paddingOf(context).bottom,
|
||||
),
|
||||
width: isFullScreen ? size.width : videoWidth,
|
||||
height: isFullScreen ? size.height : size.width * 9 / 16,
|
||||
child: videoPlayerPanel(fill: Colors.transparent),
|
||||
Widget child = Row(
|
||||
children: [
|
||||
Container(
|
||||
margin: EdgeInsets.only(
|
||||
bottom: MediaQuery.paddingOf(context).bottom,
|
||||
),
|
||||
width: isFullScreen ? size.width : videoWidth,
|
||||
height: isFullScreen ? size.height : size.width * 9 / 16,
|
||||
child: videoPlayerPanel(
|
||||
isFullScreen,
|
||||
fill: Colors.transparent,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: _buildBottomWidget,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
if (isFullScreen) {
|
||||
return child;
|
||||
@@ -496,19 +454,7 @@ class _LiveRoomPageState extends State<LiveRoomPage>
|
||||
return Column(
|
||||
children: [
|
||||
_buildAppBar,
|
||||
Expanded(
|
||||
child: Row(
|
||||
children: [
|
||||
child,
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: _buildBottomWidget,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(child: child),
|
||||
],
|
||||
);
|
||||
},
|
||||
@@ -630,3 +576,82 @@ class _LiveRoomPageState extends State<LiveRoomPage>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class LiveDanmaku extends StatefulWidget {
|
||||
final PlPlayerController plPlayerController;
|
||||
final bool isPipMode;
|
||||
final bool isFullScreen;
|
||||
|
||||
const LiveDanmaku({
|
||||
super.key,
|
||||
required this.plPlayerController,
|
||||
this.isPipMode = false,
|
||||
required this.isFullScreen,
|
||||
});
|
||||
|
||||
@override
|
||||
State<LiveDanmaku> createState() => _LiveDanmakuState();
|
||||
}
|
||||
|
||||
class _LiveDanmakuState extends State<LiveDanmaku> {
|
||||
PlPlayerController get plPlayerController => widget.plPlayerController;
|
||||
|
||||
@override
|
||||
void didUpdateWidget(LiveDanmaku oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (oldWidget.isPipMode != widget.isPipMode ||
|
||||
oldWidget.isFullScreen != widget.isFullScreen) {
|
||||
_updateFontSize();
|
||||
}
|
||||
}
|
||||
|
||||
void _updateFontSize() {
|
||||
plPlayerController.danmakuController?.updateOption(
|
||||
plPlayerController.danmakuController!.option.copyWith(
|
||||
fontSize: _fontSize,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
double get _fontSize => !widget.isFullScreen || widget.isPipMode
|
||||
? 15 * plPlayerController.danmakuFontScale
|
||||
: 15 * plPlayerController.danmakuFontScaleFS;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Obx(
|
||||
() {
|
||||
return AnimatedOpacity(
|
||||
opacity: plPlayerController.enableShowDanmaku.value ? 1 : 0,
|
||||
duration: const Duration(milliseconds: 100),
|
||||
child: DanmakuScreen(
|
||||
createdController: (DanmakuController e) {
|
||||
plPlayerController.danmakuController = e;
|
||||
},
|
||||
option: DanmakuOption(
|
||||
fontSize: _fontSize,
|
||||
fontWeight: plPlayerController.fontWeight,
|
||||
area: plPlayerController.showArea,
|
||||
opacity: plPlayerController.danmakuOpacity,
|
||||
hideTop: plPlayerController.blockTypes.contains(5),
|
||||
hideScroll: plPlayerController.blockTypes.contains(
|
||||
2,
|
||||
),
|
||||
hideBottom: plPlayerController.blockTypes.contains(
|
||||
4,
|
||||
),
|
||||
duration:
|
||||
plPlayerController.danmakuDuration /
|
||||
plPlayerController.playbackSpeed,
|
||||
staticDuration:
|
||||
plPlayerController.danmakuStaticDuration /
|
||||
plPlayerController.playbackSpeed,
|
||||
strokeWidth: plPlayerController.strokeWidth,
|
||||
lineHeight: plPlayerController.danmakuLineHeight,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,6 @@ class LiveRoomChat extends StatelessWidget {
|
||||
? Colors.white.withValues(alpha: 0.9)
|
||||
: Colors.white.withValues(alpha: 0.6);
|
||||
return Stack(
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
Obx(
|
||||
() => ListView.separated(
|
||||
|
||||
@@ -102,8 +102,6 @@ class VideoDetailController extends GetxController
|
||||
late VideoDecodeFormatType currentDecodeFormats;
|
||||
// 是否开始自动播放 存在多p的情况下,第二p需要为true
|
||||
final RxBool autoPlay = true.obs;
|
||||
// 封面图的展示
|
||||
final RxBool isShowCover = true.obs;
|
||||
|
||||
final scaffoldKey = GlobalKey<ScaffoldState>();
|
||||
final childKey = GlobalKey<ScaffoldState>();
|
||||
@@ -285,7 +283,6 @@ class VideoDetailController extends GetxController
|
||||
);
|
||||
|
||||
autoPlay.value = Pref.autoPlayEnable;
|
||||
if (autoPlay.value) isShowCover.value = false;
|
||||
|
||||
// 预设的解码格式
|
||||
cacheDecode = Pref.defaultDecode;
|
||||
@@ -732,7 +729,7 @@ class VideoDetailController extends GetxController
|
||||
);
|
||||
|
||||
if (positionSubscription == null &&
|
||||
!isShowCover.value &&
|
||||
autoPlay.value &&
|
||||
plPlayerController.videoPlayerController != null) {
|
||||
final currPost =
|
||||
plPlayerController.position.value.inMilliseconds;
|
||||
@@ -781,7 +778,7 @@ class VideoDetailController extends GetxController
|
||||
);
|
||||
|
||||
if (positionSubscription == null &&
|
||||
(!isShowCover.value || plPlayerController.preInitPlayer)) {
|
||||
(autoPlay.value || plPlayerController.preInitPlayer)) {
|
||||
initSkip();
|
||||
plPlayerController.segmentList.value = segmentProgressList!;
|
||||
}
|
||||
@@ -799,7 +796,7 @@ class VideoDetailController extends GetxController
|
||||
?.stream
|
||||
.position
|
||||
.listen((position) {
|
||||
if (isShowCover.value) {
|
||||
if (!autoPlay.value) {
|
||||
return;
|
||||
}
|
||||
int currentPos = position.inSeconds;
|
||||
@@ -1000,7 +997,7 @@ class VideoDetailController extends GetxController
|
||||
|
||||
/// 更新画质、音质
|
||||
void updatePlayer() {
|
||||
isShowCover.value = false;
|
||||
autoPlay.value = true;
|
||||
playedTime = plPlayerController.position.value;
|
||||
plPlayerController.removeListeners();
|
||||
plPlayerController.isBuffering.value = false;
|
||||
@@ -1209,10 +1206,7 @@ class VideoDetailController extends GetxController
|
||||
setVideoHeight();
|
||||
currentDecodeFormats = VideoDecodeFormatTypeExt.fromString('avc1')!;
|
||||
currentVideoQa = VideoQuality.fromCode(data.quality!);
|
||||
if (autoPlay.value) {
|
||||
isShowCover.value = false;
|
||||
await playerInit();
|
||||
} else if (plPlayerController.preInitPlayer) {
|
||||
if (autoPlay.value || plPlayerController.preInitPlayer) {
|
||||
await playerInit();
|
||||
}
|
||||
isQuerying = false;
|
||||
@@ -1221,7 +1215,6 @@ class VideoDetailController extends GetxController
|
||||
if (data.dash == null) {
|
||||
SmartDialog.showToast('视频资源不存在');
|
||||
autoPlay.value = false;
|
||||
isShowCover.value = true;
|
||||
videoState.value = const Error('视频资源不存在');
|
||||
if (plPlayerController.isFullScreen.value) {
|
||||
plPlayerController.toggleFullScreen(false);
|
||||
@@ -1340,15 +1333,11 @@ class VideoDetailController extends GetxController
|
||||
? Duration.zero
|
||||
: Duration(milliseconds: data.lastPlayTime!));
|
||||
}
|
||||
if (autoPlay.value) {
|
||||
isShowCover.value = false;
|
||||
await playerInit();
|
||||
} else if (plPlayerController.preInitPlayer) {
|
||||
if (autoPlay.value || plPlayerController.preInitPlayer) {
|
||||
await playerInit();
|
||||
}
|
||||
} else {
|
||||
autoPlay.value = false;
|
||||
isShowCover.value = true;
|
||||
videoState.value = Error(result['msg']);
|
||||
if (plPlayerController.isFullScreen.value) {
|
||||
plPlayerController.toggleFullScreen(false);
|
||||
|
||||
@@ -121,6 +121,7 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
|
||||
|
||||
final GlobalKey relatedVideoPanelKey = GlobalKey();
|
||||
final GlobalKey videoPlayerKey = GlobalKey();
|
||||
final GlobalKey playerKey = GlobalKey();
|
||||
final GlobalKey videoReplyPanelKey = GlobalKey();
|
||||
late final GlobalKey ugcPanelKey = GlobalKey();
|
||||
late final GlobalKey pgcPanelKey = GlobalKey();
|
||||
@@ -188,24 +189,26 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
|
||||
@override
|
||||
void didChangeAppLifecycleState(AppLifecycleState state) {
|
||||
if (state == AppLifecycleState.resumed) {
|
||||
introController.startTimer();
|
||||
videoDetailController.plPlayerController.showDanmaku = true;
|
||||
if (!videoDetailController.plPlayerController.showDanmaku) {
|
||||
introController.startTimer();
|
||||
videoDetailController.plPlayerController.showDanmaku = true;
|
||||
|
||||
// 修复从后台恢复时全屏状态下屏幕方向错误的问题
|
||||
if (isFullScreen && Platform.isIOS) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
// 根据视频方向重新设置屏幕方向
|
||||
final isVertical = videoDetailController.isVertical.value;
|
||||
final mode = plPlayerController?.mode;
|
||||
// 修复从后台恢复时全屏状态下屏幕方向错误的问题
|
||||
if (isFullScreen && Platform.isIOS) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
// 根据视频方向重新设置屏幕方向
|
||||
final isVertical = videoDetailController.isVertical.value;
|
||||
final mode = plPlayerController?.mode;
|
||||
|
||||
late final size = Get.size;
|
||||
if (!(mode == FullScreenMode.vertical ||
|
||||
(mode == FullScreenMode.auto && isVertical) ||
|
||||
(mode == FullScreenMode.ratio &&
|
||||
(isVertical || size.height / size.width < 1.25)))) {
|
||||
landScape();
|
||||
}
|
||||
});
|
||||
late final size = Get.size;
|
||||
if (!(mode == FullScreenMode.vertical ||
|
||||
(mode == FullScreenMode.auto && isVertical) ||
|
||||
(mode == FullScreenMode.ratio &&
|
||||
(isVertical || size.height / size.width < 1.25)))) {
|
||||
landScape();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
} else if (state == AppLifecycleState.paused) {
|
||||
introController.canelTimer();
|
||||
@@ -317,9 +320,7 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
|
||||
return;
|
||||
}
|
||||
plPlayerController = videoDetailController.plPlayerController;
|
||||
videoDetailController
|
||||
..isShowCover.value = false
|
||||
..autoPlay.value = true;
|
||||
videoDetailController.autoPlay.value = true;
|
||||
if (videoDetailController.plPlayerController.preInitPlayer) {
|
||||
await plPlayerController!.play();
|
||||
} else {
|
||||
@@ -452,9 +453,7 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
|
||||
}
|
||||
}
|
||||
super.didPopNext();
|
||||
final isShowCover = videoDetailController.isShowCover.value;
|
||||
videoDetailController.autoPlay.value = !isShowCover;
|
||||
if (!isShowCover) {
|
||||
if (videoDetailController.autoPlay.value) {
|
||||
await videoDetailController.playerInit(
|
||||
autoplay: videoDetailController.playerStatus == PlayerStatus.playing,
|
||||
);
|
||||
@@ -1212,41 +1211,8 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
|
||||
),
|
||||
);
|
||||
|
||||
Widget get childWhenEnabled => Obx(
|
||||
() => !videoDetailController.autoPlay.value
|
||||
? const SizedBox.shrink()
|
||||
: PLVideoPlayer(
|
||||
key: Key(heroTag),
|
||||
plPlayerController: plPlayerController!,
|
||||
videoDetailController: videoDetailController,
|
||||
ugcIntroController: videoDetailController.isUgc
|
||||
? ugcIntroController
|
||||
: null,
|
||||
pgcIntroController: videoDetailController.isUgc
|
||||
? null
|
||||
: pgcIntroController,
|
||||
headerControl: HeaderControl(
|
||||
controller: plPlayerController!,
|
||||
videoDetailCtr: videoDetailController,
|
||||
heroTag: heroTag,
|
||||
),
|
||||
danmuWidget: pipNoDanmaku
|
||||
? null
|
||||
: Obx(
|
||||
() => PlDanmaku(
|
||||
key: Key(videoDetailController.cid.value.toString()),
|
||||
isPipMode: true,
|
||||
cid: videoDetailController.cid.value,
|
||||
playerController: plPlayerController!,
|
||||
),
|
||||
),
|
||||
showEpisodes: showEpisodes,
|
||||
showViewPoints: showViewPoints,
|
||||
),
|
||||
);
|
||||
|
||||
Widget get manualPlayerWidget => Obx(() {
|
||||
if (videoDetailController.isShowCover.value) {
|
||||
if (!videoDetailController.autoPlay.value) {
|
||||
return Stack(
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
@@ -1387,15 +1353,15 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
|
||||
return const SizedBox.shrink();
|
||||
});
|
||||
|
||||
Widget get plPlayer => Obx(
|
||||
Widget plPlayer([bool isPipMode = false]) => Obx(
|
||||
key: videoPlayerKey,
|
||||
() => videoDetailController.videoState.value is! Success
|
||||
? const SizedBox.shrink()
|
||||
: !videoDetailController.autoPlay.value ||
|
||||
plPlayerController?.videoController == null
|
||||
() =>
|
||||
videoDetailController.videoState.value is! Success ||
|
||||
!videoDetailController.autoPlay.value ||
|
||||
plPlayerController?.videoController == null
|
||||
? const SizedBox.shrink()
|
||||
: PLVideoPlayer(
|
||||
key: Key(heroTag),
|
||||
key: playerKey,
|
||||
plPlayerController: plPlayerController!,
|
||||
videoDetailController: videoDetailController,
|
||||
ugcIntroController: videoDetailController.isUgc
|
||||
@@ -1410,13 +1376,17 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
|
||||
videoDetailCtr: videoDetailController,
|
||||
heroTag: heroTag,
|
||||
),
|
||||
danmuWidget: Obx(
|
||||
() => PlDanmaku(
|
||||
key: Key(videoDetailController.cid.value.toString()),
|
||||
cid: videoDetailController.cid.value,
|
||||
playerController: plPlayerController!,
|
||||
),
|
||||
),
|
||||
danmuWidget: isPipMode && pipNoDanmaku
|
||||
? null
|
||||
: Obx(
|
||||
() => PlDanmaku(
|
||||
key: ValueKey(videoDetailController.cid.value),
|
||||
isPipMode: isPipMode,
|
||||
cid: videoDetailController.cid.value,
|
||||
playerController: plPlayerController!,
|
||||
isFullScreen: plPlayerController!.isFullScreen.value,
|
||||
),
|
||||
),
|
||||
showEpisodes: showEpisodes,
|
||||
showViewPoints: showViewPoints,
|
||||
),
|
||||
@@ -1424,7 +1394,7 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
|
||||
|
||||
Widget autoChoose(Widget childWhenDisabled) {
|
||||
if (Platform.isAndroid) {
|
||||
return Floating().isPipMode ? childWhenEnabled : childWhenDisabled;
|
||||
return Floating().isPipMode ? plPlayer(true) : childWhenDisabled;
|
||||
}
|
||||
return childWhenDisabled;
|
||||
}
|
||||
@@ -1624,10 +1594,10 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
|
||||
children: [
|
||||
const Positioned.fill(child: ColoredBox(color: Colors.black)),
|
||||
|
||||
if (isShowing) plPlayer,
|
||||
if (isShowing) plPlayer(),
|
||||
|
||||
Obx(() {
|
||||
if (videoDetailController.isShowCover.value) {
|
||||
if (!videoDetailController.autoPlay.value) {
|
||||
return Positioned.fill(
|
||||
child: GestureDetector(
|
||||
onTap: handlePlay,
|
||||
|
||||
Reference in New Issue
Block a user