mirror of
https://github.com/HChaZZY/PiliPlus.git
synced 2025-12-06 09:13:48 +08:00
opt: live danmaku
This commit is contained in:
@@ -3,6 +3,7 @@ import 'package:PiliPalaX/http/constants.dart';
|
||||
import 'package:PiliPalaX/http/live.dart';
|
||||
import 'package:PiliPalaX/models/live/room_info.dart';
|
||||
import 'package:PiliPalaX/plugin/pl_player/index.dart';
|
||||
import 'package:ns_danmaku/danmaku_controller.dart';
|
||||
import '../../models/live/room_info_h5.dart';
|
||||
import '../../utils/video_utils.dart';
|
||||
|
||||
@@ -21,6 +22,8 @@ class LiveRoomController extends GetxController {
|
||||
|
||||
RxList<dynamic> messages = [].obs;
|
||||
RxBool disableAutoScroll = false.obs;
|
||||
double? brightness;
|
||||
DanmakuController? controller;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
|
||||
@@ -9,6 +9,10 @@ import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:PiliPalaX/common/widgets/network_img_layer.dart';
|
||||
import 'package:PiliPalaX/plugin/pl_player/index.dart';
|
||||
import 'package:ns_danmaku/danmaku_controller.dart';
|
||||
import 'package:ns_danmaku/danmaku_view.dart';
|
||||
import 'package:ns_danmaku/models/danmaku_option.dart';
|
||||
import 'package:screen_brightness/screen_brightness.dart';
|
||||
|
||||
import '../../utils/storage.dart';
|
||||
import 'controller.dart';
|
||||
@@ -24,7 +28,7 @@ class LiveRoomPage extends StatefulWidget {
|
||||
class _LiveRoomPageState extends State<LiveRoomPage> {
|
||||
late final int _roomId;
|
||||
final LiveRoomController _liveRoomController = Get.put(LiveRoomController());
|
||||
PlPlayerController? plPlayerController;
|
||||
late final PlPlayerController plPlayerController;
|
||||
late Future? _futureBuilder;
|
||||
late Future? _futureBuilderFuture;
|
||||
|
||||
@@ -36,8 +40,18 @@ class _LiveRoomPageState extends State<LiveRoomPage> {
|
||||
late final _node = FocusNode();
|
||||
late final _ctr = TextEditingController();
|
||||
|
||||
late bool enableShowDanmaku;
|
||||
late List blockTypes;
|
||||
late double showArea;
|
||||
late double opacityVal;
|
||||
late double fontSizeVal;
|
||||
late double danmakuDurationVal;
|
||||
late double strokeWidth;
|
||||
late int fontWeight;
|
||||
int latestAddedPosition = -1;
|
||||
|
||||
void playCallBack() {
|
||||
plPlayerController?.play();
|
||||
plPlayerController.play();
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -50,20 +64,28 @@ class _LiveRoomPageState extends State<LiveRoomPage> {
|
||||
}
|
||||
videoSourceInit();
|
||||
_futureBuilderFuture = _liveRoomController.queryLiveInfo();
|
||||
plPlayerController!.autoEnterFullscreen();
|
||||
plPlayerController.autoEnterFullscreen();
|
||||
}
|
||||
|
||||
Future<void> videoSourceInit() async {
|
||||
_futureBuilder = _liveRoomController.queryLiveInfoH5();
|
||||
plPlayerController = _liveRoomController.plPlayerController;
|
||||
blockTypes = plPlayerController.blockTypes;
|
||||
showArea = plPlayerController.showArea;
|
||||
opacityVal = plPlayerController.opacityVal;
|
||||
fontSizeVal = plPlayerController.fontSizeVal;
|
||||
strokeWidth = plPlayerController.strokeWidth;
|
||||
fontWeight = plPlayerController.fontWeight;
|
||||
danmakuDurationVal = plPlayerController.danmakuDurationVal;
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
ScreenBrightness().resetApplicationScreenBrightness();
|
||||
PlPlayerController.setPlayCallBack(null);
|
||||
floating?.dispose();
|
||||
_node.dispose();
|
||||
plPlayerController?.dispose();
|
||||
plPlayerController.dispose();
|
||||
_ctr.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
@@ -75,12 +97,39 @@ class _LiveRoomPageState extends State<LiveRoomPage> {
|
||||
builder: (BuildContext context, AsyncSnapshot snapshot) {
|
||||
if (snapshot.hasData && snapshot.data['status']) {
|
||||
return PLVideoPlayer(
|
||||
controller: plPlayerController!,
|
||||
controller: plPlayerController,
|
||||
bottomControl: BottomControl(
|
||||
controller: plPlayerController,
|
||||
liveRoomCtr: _liveRoomController,
|
||||
floating: floating,
|
||||
),
|
||||
danmuWidget: Obx(
|
||||
() => AnimatedOpacity(
|
||||
opacity: plPlayerController.isOpenDanmu.value ? 1 : 0,
|
||||
duration: const Duration(milliseconds: 100),
|
||||
child: DanmakuView(
|
||||
createdController: (DanmakuController e) async {
|
||||
plPlayerController.danmakuController =
|
||||
_liveRoomController.controller = e;
|
||||
},
|
||||
option: DanmakuOption(
|
||||
fontSize: 15 * fontSizeVal,
|
||||
fontWeight: fontWeight,
|
||||
area: showArea,
|
||||
opacity: opacityVal,
|
||||
hideTop: blockTypes.contains(5),
|
||||
hideScroll: blockTypes.contains(2),
|
||||
hideBottom: blockTypes.contains(4),
|
||||
duration:
|
||||
danmakuDurationVal / plPlayerController.playbackSpeed,
|
||||
strokeWidth: strokeWidth,
|
||||
// initDuration /
|
||||
// (danmakuSpeedVal * widget.playerController.playbackSpeed),
|
||||
),
|
||||
statusChanged: (isPlaying) {},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return const SizedBox();
|
||||
@@ -236,10 +285,10 @@ class _LiveRoomPageState extends State<LiveRoomPage> {
|
||||
),
|
||||
),
|
||||
PopScope(
|
||||
canPop: plPlayerController?.isFullScreen.value != true,
|
||||
canPop: plPlayerController.isFullScreen.value != true,
|
||||
onPopInvokedWithResult: (bool didPop, Object? result) {
|
||||
if (plPlayerController?.isFullScreen.value == true) {
|
||||
plPlayerController!.triggerFullScreen(status: false);
|
||||
if (plPlayerController.isFullScreen.value == true) {
|
||||
plPlayerController.triggerFullScreen(status: false);
|
||||
}
|
||||
if (MediaQuery.of(context).orientation ==
|
||||
Orientation.landscape) {
|
||||
@@ -273,9 +322,9 @@ class _LiveRoomPageState extends State<LiveRoomPage> {
|
||||
),
|
||||
Container(
|
||||
padding: EdgeInsets.only(
|
||||
left: 16,
|
||||
left: 10,
|
||||
top: 10,
|
||||
right: 16,
|
||||
right: 10,
|
||||
bottom: 25 + MediaQuery.of(context).padding.bottom,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
@@ -290,6 +339,21 @@ class _LiveRoomPageState extends State<LiveRoomPage> {
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Obx(
|
||||
() => IconButton(
|
||||
onPressed: () {
|
||||
plPlayerController.isOpenDanmu.value =
|
||||
!plPlayerController.isOpenDanmu.value;
|
||||
GStorage.setting.put(SettingBoxKey.enableShowDanmaku,
|
||||
plPlayerController.isOpenDanmu.value);
|
||||
},
|
||||
icon: Icon(
|
||||
plPlayerController.isOpenDanmu.value
|
||||
? Icons.subtitles_outlined
|
||||
: Icons.subtitles_off_outlined,
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: TextField(
|
||||
focusNode: _node,
|
||||
|
||||
@@ -6,12 +6,13 @@ import 'package:PiliPalaX/models/live/danmu_info.dart';
|
||||
import 'package:PiliPalaX/pages/live_room/controller.dart';
|
||||
import 'package:PiliPalaX/services/loggeer.dart';
|
||||
import 'package:PiliPalaX/tcp/live.dart';
|
||||
import 'package:PiliPalaX/utils/danmaku.dart';
|
||||
import 'package:PiliPalaX/utils/utils.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:ns_danmaku/models/danmaku_item.dart';
|
||||
|
||||
import '../../../utils/storage.dart';
|
||||
|
||||
@@ -50,56 +51,58 @@ class _LiveRoomChatState extends State<LiveRoomChat> {
|
||||
Widget build(BuildContext context) {
|
||||
return Stack(
|
||||
children: [
|
||||
ListView.separated(
|
||||
controller: _scrollController,
|
||||
separatorBuilder: (_, index) => const SizedBox(height: 6),
|
||||
itemCount: widget.liveRoomController.messages.length,
|
||||
itemBuilder: (context, index) {
|
||||
return Container(
|
||||
alignment: Alignment.centerLeft,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Container(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
|
||||
decoration: const BoxDecoration(
|
||||
color: Color(0x15FFFFFF),
|
||||
borderRadius: BorderRadius.all(Radius.circular(18)),
|
||||
),
|
||||
child: Text.rich(
|
||||
TextSpan(
|
||||
children: [
|
||||
TextSpan(
|
||||
text:
|
||||
'${widget.liveRoomController.messages[index]['info'][0][15]['user']['base']['name']}: ',
|
||||
style: const TextStyle(
|
||||
color: Color(0xFFAAAAAA),
|
||||
fontSize: 14,
|
||||
Obx(
|
||||
() => ListView.separated(
|
||||
controller: _scrollController,
|
||||
separatorBuilder: (_, index) => const SizedBox(height: 6),
|
||||
itemCount: widget.liveRoomController.messages.length,
|
||||
itemBuilder: (context, index) {
|
||||
return Container(
|
||||
alignment: Alignment.centerLeft,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Container(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
|
||||
decoration: const BoxDecoration(
|
||||
color: Color(0x15FFFFFF),
|
||||
borderRadius: BorderRadius.all(Radius.circular(18)),
|
||||
),
|
||||
child: Text.rich(
|
||||
TextSpan(
|
||||
children: [
|
||||
TextSpan(
|
||||
text:
|
||||
'${widget.liveRoomController.messages[index]['info'][0][15]['user']['base']['name']}: ',
|
||||
style: const TextStyle(
|
||||
color: Color(0xFFAAAAAA),
|
||||
fontSize: 14,
|
||||
),
|
||||
recognizer: TapGestureRecognizer()
|
||||
..onTap = () {
|
||||
try {
|
||||
dynamic uid =
|
||||
widget.liveRoomController.messages[index]
|
||||
['info'][0][15]['user']['uid'];
|
||||
Get.toNamed(
|
||||
'/member?mid=$uid',
|
||||
arguments: {
|
||||
'heroTag': Utils.makeHeroTag(uid),
|
||||
},
|
||||
);
|
||||
} catch (err) {
|
||||
print(err.toString());
|
||||
// SmartDialog.showToast(err.toString());
|
||||
}
|
||||
},
|
||||
),
|
||||
recognizer: TapGestureRecognizer()
|
||||
..onTap = () {
|
||||
try {
|
||||
dynamic uid =
|
||||
widget.liveRoomController.messages[index]
|
||||
['info'][0][15]['user']['uid'];
|
||||
Get.toNamed(
|
||||
'/member?mid=$uid',
|
||||
arguments: {
|
||||
'heroTag': Utils.makeHeroTag(uid),
|
||||
},
|
||||
);
|
||||
} catch (err) {
|
||||
print(err.toString());
|
||||
// SmartDialog.showToast(err.toString());
|
||||
}
|
||||
},
|
||||
),
|
||||
_buildMsg(widget.liveRoomController.messages[index]),
|
||||
],
|
||||
_buildMsg(widget.liveRoomController.messages[index]),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
Obx(
|
||||
() => widget.liveRoomController.disableAutoScroll.value
|
||||
@@ -138,12 +141,19 @@ class _LiveRoomChatState extends State<LiveRoomChat> {
|
||||
msgStream?.addEventListener((obj) {
|
||||
if (obj['cmd'] == 'DANMU_MSG') {
|
||||
// logger.i(' 原始弹幕消息 ======> ${jsonEncode(obj)}');
|
||||
setState(() {
|
||||
widget.liveRoomController.messages.add(obj);
|
||||
WidgetsBinding.instance.addPostFrameCallback(
|
||||
(_) => _scrollToBottom(),
|
||||
);
|
||||
});
|
||||
widget.liveRoomController.messages.add(obj);
|
||||
Map json = jsonDecode(obj['info'][0][15]['extra']);
|
||||
widget.liveRoomController.controller?.addItems([
|
||||
DanmakuItem(
|
||||
json['content'],
|
||||
color: DmUtils.decimalToColor(json['color']),
|
||||
// time: e.progress,
|
||||
type: DmUtils.getPosition(json['mode']),
|
||||
)
|
||||
]);
|
||||
WidgetsBinding.instance.addPostFrameCallback(
|
||||
(_) => _scrollToBottom(),
|
||||
);
|
||||
}
|
||||
});
|
||||
msgStream?.init();
|
||||
|
||||
@@ -115,16 +115,15 @@ class VideoReplyReplyController extends CommonController
|
||||
begin: Theme.of(Get.context!).colorScheme.onInverseSurface,
|
||||
end: Theme.of(Get.context!).colorScheme.surface,
|
||||
).animate(controller!);
|
||||
() async {
|
||||
await Future.delayed(const Duration(milliseconds: 200));
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||
itemScrollCtr.jumpTo(
|
||||
index: hasRoot ? index! + 3 : index! + 1, alignment: 0.25,
|
||||
// duration: const Duration(milliseconds: 200),
|
||||
index: hasRoot ? index! + 3 : index! + 1,
|
||||
alignment: 0.25,
|
||||
);
|
||||
await Future.delayed(const Duration(milliseconds: 800));
|
||||
await controller?.forward();
|
||||
index = null;
|
||||
}();
|
||||
});
|
||||
}
|
||||
id = null;
|
||||
}
|
||||
|
||||
@@ -276,7 +276,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
||||
@override
|
||||
void dispose() {
|
||||
if (!Get.previousRoute.startsWith('/video')) {
|
||||
ScreenBrightness().resetScreenBrightness();
|
||||
ScreenBrightness().resetApplicationScreenBrightness();
|
||||
PlPlayerController.setPlayCallBack(null);
|
||||
}
|
||||
videoDetailController.positionSubscription?.cancel();
|
||||
@@ -310,7 +310,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
||||
void didPushNext() async {
|
||||
// _bufferedListener?.cancel();
|
||||
|
||||
ScreenBrightness().resetScreenBrightness();
|
||||
ScreenBrightness().resetApplicationScreenBrightness();
|
||||
|
||||
videoDetailController.positionSubscription?.cancel();
|
||||
videoIntroController.canelTimer();
|
||||
@@ -344,13 +344,13 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
||||
plPlayerController
|
||||
?.setCurrBrightness(videoDetailController.brightness!);
|
||||
if (videoDetailController.brightness != -1.0) {
|
||||
ScreenBrightness()
|
||||
.setScreenBrightness(videoDetailController.brightness!);
|
||||
ScreenBrightness().setApplicationScreenBrightness(
|
||||
videoDetailController.brightness!);
|
||||
} else {
|
||||
ScreenBrightness().resetScreenBrightness();
|
||||
ScreenBrightness().resetApplicationScreenBrightness();
|
||||
}
|
||||
} else {
|
||||
ScreenBrightness().resetScreenBrightness();
|
||||
ScreenBrightness().resetApplicationScreenBrightness();
|
||||
}
|
||||
}
|
||||
super.didPopNext();
|
||||
|
||||
@@ -1035,7 +1035,7 @@ class PlPlayerController {
|
||||
Future<void> setBrightness(double brightness) async {
|
||||
try {
|
||||
this.brightness.value = brightness;
|
||||
ScreenBrightness().setScreenBrightness(brightness);
|
||||
ScreenBrightness().setApplicationScreenBrightness(brightness);
|
||||
// setVideoBrightness();
|
||||
} catch (e) {
|
||||
throw 'Failed to set brightness';
|
||||
|
||||
@@ -205,7 +205,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
|
||||
Future<void> setBrightness(double value) async {
|
||||
try {
|
||||
await ScreenBrightness().setScreenBrightness(value);
|
||||
await ScreenBrightness().setApplicationScreenBrightness(value);
|
||||
} catch (_) {}
|
||||
_brightnessIndicator.value = true;
|
||||
_brightnessTimer?.cancel();
|
||||
|
||||
@@ -216,13 +216,13 @@ class LiveMessageStream {
|
||||
}
|
||||
_processingData(decompressedData);
|
||||
} catch (e) {
|
||||
logger.w(e);
|
||||
logger.i(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
socket.close();
|
||||
} catch (e) {
|
||||
logger.e('$logTag ===> TCP连接失败: $e');
|
||||
logger.i('$logTag ===> TCP连接失败: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -241,7 +241,7 @@ class LiveMessageStream {
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
logger.e('ParseHeader错误: $e');
|
||||
logger.i('ParseHeader错误: $e');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user