mirror of
https://github.com/HChaZZY/PiliPlus.git
synced 2025-12-06 09:13:48 +08:00
opt: live: send danmaku
Closes #618 Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
@@ -4,6 +4,7 @@ import 'package:PiliPlus/http/video.dart';
|
|||||||
import 'package:PiliPlus/models/live/danmu_info.dart';
|
import 'package:PiliPlus/models/live/danmu_info.dart';
|
||||||
import 'package:PiliPlus/models/live/quality.dart';
|
import 'package:PiliPlus/models/live/quality.dart';
|
||||||
import 'package:PiliPlus/pages/mine/controller.dart';
|
import 'package:PiliPlus/pages/mine/controller.dart';
|
||||||
|
import 'package:PiliPlus/pages/video/detail/widgets/send_danmaku_panel.dart';
|
||||||
import 'package:PiliPlus/services/service_locator.dart';
|
import 'package:PiliPlus/services/service_locator.dart';
|
||||||
import 'package:PiliPlus/tcp/live.dart';
|
import 'package:PiliPlus/tcp/live.dart';
|
||||||
import 'package:PiliPlus/utils/danmaku.dart';
|
import 'package:PiliPlus/utils/danmaku.dart';
|
||||||
@@ -12,11 +13,13 @@ import 'package:canvas_danmaku/canvas_danmaku.dart';
|
|||||||
import 'package:connectivity_plus/connectivity_plus.dart';
|
import 'package:connectivity_plus/connectivity_plus.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/rendering.dart';
|
import 'package:flutter/rendering.dart';
|
||||||
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:PiliPlus/http/constants.dart';
|
import 'package:PiliPlus/http/constants.dart';
|
||||||
import 'package:PiliPlus/http/live.dart';
|
import 'package:PiliPlus/http/live.dart';
|
||||||
import 'package:PiliPlus/models/live/room_info.dart';
|
import 'package:PiliPlus/models/live/room_info.dart';
|
||||||
import 'package:PiliPlus/plugin/pl_player/index.dart';
|
import 'package:PiliPlus/plugin/pl_player/index.dart';
|
||||||
|
import 'package:get/get_navigation/src/dialog/dialog_route.dart';
|
||||||
import '../../models/live/room_info_h5.dart';
|
import '../../models/live/room_info_h5.dart';
|
||||||
import '../../utils/video_utils.dart';
|
import '../../utils/video_utils.dart';
|
||||||
|
|
||||||
@@ -42,6 +45,10 @@ class LiveRoomController extends GetxController {
|
|||||||
late List<Map> acceptQnList = <Map>[];
|
late List<Map> acceptQnList = <Map>[];
|
||||||
RxString currentQnDesc = ''.obs;
|
RxString currentQnDesc = ''.obs;
|
||||||
|
|
||||||
|
String? savedDanmaku;
|
||||||
|
|
||||||
|
late final isLogin = Accounts.main.isLogin;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onInit() {
|
void onInit() {
|
||||||
super.onInit();
|
super.onInit();
|
||||||
@@ -241,4 +248,41 @@ class LiveRoomController extends GetxController {
|
|||||||
.description;
|
.description;
|
||||||
await queryLiveInfo();
|
await queryLiveInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void onSendDanmaku() {
|
||||||
|
if (!isLogin) {
|
||||||
|
SmartDialog.showToast('未登录');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Navigator.of(Get.context!).push(
|
||||||
|
GetDialogRoute(
|
||||||
|
pageBuilder: (buildContext, animation, secondaryAnimation) {
|
||||||
|
return SendDanmakuPanel(
|
||||||
|
roomId: roomId,
|
||||||
|
initialValue: savedDanmaku,
|
||||||
|
onSave: (danmaku) => savedDanmaku = danmaku,
|
||||||
|
callback: (danmakuModel) {
|
||||||
|
savedDanmaku = null;
|
||||||
|
plPlayerController.danmakuController?.addDanmaku(danmakuModel);
|
||||||
|
},
|
||||||
|
darkVideoPage: false,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
transitionDuration: const Duration(milliseconds: 500),
|
||||||
|
transitionBuilder: (context, animation, secondaryAnimation, child) {
|
||||||
|
const begin = Offset(0.0, 1.0);
|
||||||
|
const end = Offset.zero;
|
||||||
|
const curve = Curves.linear;
|
||||||
|
|
||||||
|
var tween =
|
||||||
|
Tween(begin: begin, end: end).chain(CurveTween(curve: curve));
|
||||||
|
|
||||||
|
return SlideTransition(
|
||||||
|
position: animation.drive(tween),
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,7 +44,6 @@ class _LiveRoomPageState extends State<LiveRoomPage>
|
|||||||
bool isPlay = true;
|
bool isPlay = true;
|
||||||
Floating? floating;
|
Floating? floating;
|
||||||
|
|
||||||
late final _isLogin = GStorage.userInfo.get('userInfoCache') != null;
|
|
||||||
late final _node = FocusNode();
|
late final _node = FocusNode();
|
||||||
late final _ctr = TextEditingController();
|
late final _ctr = TextEditingController();
|
||||||
StreamSubscription? _listener;
|
StreamSubscription? _listener;
|
||||||
@@ -175,6 +174,7 @@ class _LiveRoomPageState extends State<LiveRoomPage>
|
|||||||
headerControl: LiveHeaderControl(
|
headerControl: LiveHeaderControl(
|
||||||
plPlayerController: plPlayerController,
|
plPlayerController: plPlayerController,
|
||||||
floating: floating,
|
floating: floating,
|
||||||
|
onSendDanmaku: _liveRoomController.onSendDanmaku,
|
||||||
),
|
),
|
||||||
bottomControl: BottomControl(
|
bottomControl: BottomControl(
|
||||||
plPlayerController: plPlayerController,
|
plPlayerController: plPlayerController,
|
||||||
@@ -601,18 +601,25 @@ class _LiveRoomPageState extends State<LiveRoomPage>
|
|||||||
);
|
);
|
||||||
|
|
||||||
void _onSendMsg(msg) async {
|
void _onSendMsg(msg) async {
|
||||||
if (!_isLogin) {
|
if (!_liveRoomController.isLogin) {
|
||||||
SmartDialog.showToast('未登录');
|
SmartDialog.showToast('未登录');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
dynamic res = await LiveHttp.sendLiveMsg(
|
dynamic res = await LiveHttp.sendLiveMsg(
|
||||||
roomId: _liveRoomController.roomId, msg: msg);
|
roomId: _liveRoomController.roomId, msg: msg);
|
||||||
if (res['status']) {
|
if (res['status']) {
|
||||||
_ctr.clear();
|
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
FocusScope.of(context).unfocus();
|
FocusScope.of(context).unfocus();
|
||||||
}
|
}
|
||||||
SmartDialog.showToast('发送成功');
|
SmartDialog.showToast('发送成功');
|
||||||
|
plPlayerController.danmakuController?.addDanmaku(
|
||||||
|
DanmakuContentItem(
|
||||||
|
_ctr.text,
|
||||||
|
type: DanmakuItemType.scroll,
|
||||||
|
selfSend: true,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
_ctr.clear();
|
||||||
} else {
|
} else {
|
||||||
SmartDialog.showToast(res['msg']);
|
SmartDialog.showToast(res['msg']);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,11 +11,13 @@ class LiveHeaderControl extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
const LiveHeaderControl({
|
const LiveHeaderControl({
|
||||||
required this.plPlayerController,
|
required this.plPlayerController,
|
||||||
this.floating,
|
this.floating,
|
||||||
|
required this.onSendDanmaku,
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
final Floating? floating;
|
final Floating? floating;
|
||||||
final PlPlayerController plPlayerController;
|
final PlPlayerController plPlayerController;
|
||||||
|
final VoidCallback onSendDanmaku;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Size get preferredSize => const Size(double.infinity, kToolbarHeight);
|
Size get preferredSize => const Size(double.infinity, kToolbarHeight);
|
||||||
@@ -31,6 +33,22 @@ class LiveHeaderControl extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
title: Row(
|
title: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
children: [
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
width: 42,
|
||||||
|
height: 34,
|
||||||
|
child: IconButton(
|
||||||
|
tooltip: '发弹幕',
|
||||||
|
style: ButtonStyle(
|
||||||
|
padding: WidgetStateProperty.all(EdgeInsets.zero),
|
||||||
|
),
|
||||||
|
onPressed: onSendDanmaku,
|
||||||
|
icon: const Icon(
|
||||||
|
Icons.comment_outlined,
|
||||||
|
size: 19,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
Obx(
|
Obx(
|
||||||
() => IconButton(
|
() => IconButton(
|
||||||
onPressed: plPlayerController.setOnlyPlayAudio,
|
onPressed: plPlayerController.setOnlyPlayAudio,
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import 'dart:async';
|
|||||||
|
|
||||||
import 'package:PiliPlus/common/widgets/icon_button.dart';
|
import 'package:PiliPlus/common/widgets/icon_button.dart';
|
||||||
import 'package:PiliPlus/http/danmaku.dart';
|
import 'package:PiliPlus/http/danmaku.dart';
|
||||||
|
import 'package:PiliPlus/http/live.dart';
|
||||||
import 'package:PiliPlus/main.dart';
|
import 'package:PiliPlus/main.dart';
|
||||||
import 'package:PiliPlus/pages/common/common_publish_page.dart';
|
import 'package:PiliPlus/pages/common/common_publish_page.dart';
|
||||||
import 'package:PiliPlus/pages/setting/slide_color_picker.dart';
|
import 'package:PiliPlus/pages/setting/slide_color_picker.dart';
|
||||||
@@ -13,9 +14,14 @@ import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
|||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
class SendDanmakuPanel extends CommonPublishPage {
|
class SendDanmakuPanel extends CommonPublishPage {
|
||||||
|
// video
|
||||||
final dynamic cid;
|
final dynamic cid;
|
||||||
final dynamic bvid;
|
final dynamic bvid;
|
||||||
final dynamic progress;
|
final dynamic progress;
|
||||||
|
|
||||||
|
// live
|
||||||
|
final dynamic roomId;
|
||||||
|
|
||||||
final ValueChanged<DanmakuContentItem> callback;
|
final ValueChanged<DanmakuContentItem> callback;
|
||||||
final bool darkVideoPage;
|
final bool darkVideoPage;
|
||||||
|
|
||||||
@@ -23,9 +29,10 @@ class SendDanmakuPanel extends CommonPublishPage {
|
|||||||
super.key,
|
super.key,
|
||||||
super.initialValue,
|
super.initialValue,
|
||||||
super.onSave,
|
super.onSave,
|
||||||
required this.cid,
|
this.cid,
|
||||||
required this.bvid,
|
this.bvid,
|
||||||
required this.progress,
|
this.progress,
|
||||||
|
this.roomId,
|
||||||
required this.callback,
|
required this.callback,
|
||||||
required this.darkVideoPage,
|
required this.darkVideoPage,
|
||||||
});
|
});
|
||||||
@@ -313,27 +320,28 @@ class _SendDanmakuPanelState extends CommonPublishPageState<SendDanmakuPanel> {
|
|||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Obx(
|
if (widget.roomId == null)
|
||||||
() => iconButton(
|
Obx(
|
||||||
context: context,
|
() => iconButton(
|
||||||
tooltip: '弹幕样式',
|
context: context,
|
||||||
onPressed: () {
|
tooltip: '弹幕样式',
|
||||||
if (selectKeyboard.value) {
|
onPressed: () {
|
||||||
selectKeyboard.value = false;
|
if (selectKeyboard.value) {
|
||||||
updatePanelType(PanelType.emoji);
|
selectKeyboard.value = false;
|
||||||
} else {
|
updatePanelType(PanelType.emoji);
|
||||||
selectKeyboard.value = true;
|
} else {
|
||||||
updatePanelType(PanelType.keyboard);
|
selectKeyboard.value = true;
|
||||||
}
|
updatePanelType(PanelType.keyboard);
|
||||||
},
|
}
|
||||||
bgColor: Colors.transparent,
|
},
|
||||||
iconSize: 24,
|
bgColor: Colors.transparent,
|
||||||
icon: Icons.text_format,
|
iconSize: 24,
|
||||||
iconColor: selectKeyboard.value.not
|
icon: Icons.text_format,
|
||||||
? themeData.colorScheme.primary
|
iconColor: selectKeyboard.value.not
|
||||||
: themeData.colorScheme.onSurfaceVariant,
|
? themeData.colorScheme.primary
|
||||||
|
: themeData.colorScheme.onSurfaceVariant,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: 12),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Form(
|
child: Form(
|
||||||
@@ -441,33 +449,58 @@ class _SendDanmakuPanelState extends CommonPublishPageState<SendDanmakuPanel> {
|
|||||||
@override
|
@override
|
||||||
Future onCustomPublish({required String message, List? pictures}) async {
|
Future onCustomPublish({required String message, List? pictures}) async {
|
||||||
SmartDialog.showLoading(msg: '发送中...');
|
SmartDialog.showLoading(msg: '发送中...');
|
||||||
final dynamic res = await DanmakuHttp.shootDanmaku(
|
if (widget.roomId != null) {
|
||||||
oid: widget.cid,
|
final res = await LiveHttp.sendLiveMsg(
|
||||||
bvid: widget.bvid,
|
roomId: widget.roomId,
|
||||||
progress: widget.progress,
|
msg: editController.text,
|
||||||
msg: editController.text,
|
|
||||||
mode: _mode.value,
|
|
||||||
fontsize: _fontsize.value,
|
|
||||||
color: _color.value.value & 0xFFFFFF,
|
|
||||||
);
|
|
||||||
SmartDialog.dismiss();
|
|
||||||
if (res['status']) {
|
|
||||||
Get.back();
|
|
||||||
SmartDialog.showToast('发送成功');
|
|
||||||
widget.callback(
|
|
||||||
DanmakuContentItem(
|
|
||||||
editController.text,
|
|
||||||
color: _color.value,
|
|
||||||
type: switch (_mode.value) {
|
|
||||||
5 => DanmakuItemType.top,
|
|
||||||
4 => DanmakuItemType.bottom,
|
|
||||||
_ => DanmakuItemType.scroll,
|
|
||||||
},
|
|
||||||
selfSend: true,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
if (res['status']) {
|
||||||
|
Get.back();
|
||||||
|
SmartDialog.showToast('发送成功');
|
||||||
|
widget.callback(
|
||||||
|
DanmakuContentItem(
|
||||||
|
editController.text,
|
||||||
|
color: _color.value,
|
||||||
|
type: switch (_mode.value) {
|
||||||
|
5 => DanmakuItemType.top,
|
||||||
|
4 => DanmakuItemType.bottom,
|
||||||
|
_ => DanmakuItemType.scroll,
|
||||||
|
},
|
||||||
|
selfSend: true,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
SmartDialog.showToast('发送失败: ${res['msg']}');
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
SmartDialog.showToast('发送失败: ${res['msg']}');
|
final dynamic res = await DanmakuHttp.shootDanmaku(
|
||||||
|
oid: widget.cid,
|
||||||
|
bvid: widget.bvid,
|
||||||
|
progress: widget.progress,
|
||||||
|
msg: editController.text,
|
||||||
|
mode: _mode.value,
|
||||||
|
fontsize: _fontsize.value,
|
||||||
|
color: _color.value.value & 0xFFFFFF,
|
||||||
|
);
|
||||||
|
SmartDialog.dismiss();
|
||||||
|
if (res['status']) {
|
||||||
|
Get.back();
|
||||||
|
SmartDialog.showToast('发送成功');
|
||||||
|
widget.callback(
|
||||||
|
DanmakuContentItem(
|
||||||
|
editController.text,
|
||||||
|
color: _color.value,
|
||||||
|
type: switch (_mode.value) {
|
||||||
|
5 => DanmakuItemType.top,
|
||||||
|
4 => DanmakuItemType.bottom,
|
||||||
|
_ => DanmakuItemType.scroll,
|
||||||
|
},
|
||||||
|
selfSend: true,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
SmartDialog.showToast('发送失败: ${res['msg']}');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1047,7 +1047,6 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
!plPlayerController.showControls.value;
|
!plPlayerController.showControls.value;
|
||||||
},
|
},
|
||||||
onDoubleTapDown: (TapDownDetails details) {
|
onDoubleTapDown: (TapDownDetails details) {
|
||||||
// live模式下禁用 锁定时🔒禁用
|
|
||||||
if (plPlayerController.controlsLock.value) {
|
if (plPlayerController.controlsLock.value) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user