Compare commits
5 Commits
fix
...
feature-da
| Author | SHA1 | Date | |
|---|---|---|---|
| 415280acb8 | |||
| 888f9d3d60 | |||
| 25c416f9bf | |||
| 9ec9fc2830 | |||
| c0f3b4f3a2 |
@ -55,13 +55,7 @@ class _PlDanmakuState extends State<PlDanmaku> {
|
|||||||
..addStatusLister(playerListener)
|
..addStatusLister(playerListener)
|
||||||
..addPositionListener(videoPositionListen);
|
..addPositionListener(videoPositionListen);
|
||||||
}
|
}
|
||||||
playerController.isOpenDanmu.listen((p0) {
|
danmakuStatusListen();
|
||||||
if (p0 && !_plDanmakuController.initiated) {
|
|
||||||
_plDanmakuController.initiate(
|
|
||||||
playerController.duration.value.inMilliseconds,
|
|
||||||
playerController.position.value.inMilliseconds);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
blockTypes = playerController.blockTypes;
|
blockTypes = playerController.blockTypes;
|
||||||
showArea = playerController.showArea;
|
showArea = playerController.showArea;
|
||||||
opacityVal = playerController.opacityVal;
|
opacityVal = playerController.opacityVal;
|
||||||
@ -111,6 +105,20 @@ class _PlDanmakuState extends State<PlDanmaku> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 弹幕开关状态监听
|
||||||
|
void danmakuStatusListen() {
|
||||||
|
playerController.isOpenDanmu.listen((p0) {
|
||||||
|
if (p0 && !_plDanmakuController.initiated) {
|
||||||
|
_plDanmakuController.initiate(
|
||||||
|
playerController.duration.value.inMilliseconds,
|
||||||
|
playerController.position.value.inMilliseconds);
|
||||||
|
} else {
|
||||||
|
playerController.danmakuController!.pause();
|
||||||
|
playerController.danmakuController!.clear();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
playerController.removePositionListener(videoPositionListen);
|
playerController.removePositionListener(videoPositionListen);
|
||||||
@ -122,28 +130,23 @@ class _PlDanmakuState extends State<PlDanmaku> {
|
|||||||
return LayoutBuilder(builder: (context, box) {
|
return LayoutBuilder(builder: (context, box) {
|
||||||
// double initDuration = box.maxWidth / 12;
|
// double initDuration = box.maxWidth / 12;
|
||||||
return Obx(
|
return Obx(
|
||||||
() => AnimatedOpacity(
|
() => DanmakuView(
|
||||||
opacity: playerController.isOpenDanmu.value ? 1 : 0,
|
createdController: (DanmakuController e) async {
|
||||||
duration: const Duration(milliseconds: 100),
|
playerController.danmakuController = _controller = e;
|
||||||
child: DanmakuView(
|
},
|
||||||
createdController: (DanmakuController e) async {
|
option: DanmakuOption(
|
||||||
playerController.danmakuController = _controller = e;
|
fontSize: 15 * fontSizeVal,
|
||||||
},
|
area: showArea,
|
||||||
option: DanmakuOption(
|
opacity: opacityVal,
|
||||||
fontSize: 15 * fontSizeVal,
|
hideTop: blockTypes.contains(5),
|
||||||
area: showArea,
|
hideScroll: blockTypes.contains(2),
|
||||||
opacity: opacityVal,
|
hideBottom: blockTypes.contains(4),
|
||||||
hideTop: blockTypes.contains(5),
|
duration: danmakuDurationVal / playerController.playbackSpeed,
|
||||||
hideScroll: blockTypes.contains(2),
|
strokeWidth: strokeWidth,
|
||||||
hideBottom: blockTypes.contains(4),
|
// initDuration /
|
||||||
duration:
|
// (danmakuSpeedVal * widget.playerController.playbackSpeed),
|
||||||
danmakuDurationVal / playerController.playbackSpeed,
|
|
||||||
strokeWidth: strokeWidth,
|
|
||||||
// initDuration /
|
|
||||||
// (danmakuSpeedVal * widget.playerController.playbackSpeed),
|
|
||||||
),
|
|
||||||
statusChanged: (isPlaying) {},
|
|
||||||
),
|
),
|
||||||
|
statusChanged: (isPlaying) {},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -17,6 +17,7 @@ import 'package:pilipala/plugin/pl_player/index.dart';
|
|||||||
import 'package:pilipala/plugin/pl_player/models/play_repeat.dart';
|
import 'package:pilipala/plugin/pl_player/models/play_repeat.dart';
|
||||||
import 'package:pilipala/utils/storage.dart';
|
import 'package:pilipala/utils/storage.dart';
|
||||||
import 'package:pilipala/services/shutdown_timer_service.dart';
|
import 'package:pilipala/services/shutdown_timer_service.dart';
|
||||||
|
import '../../../../http/danmaku.dart';
|
||||||
import '../../../../models/common/search_type.dart';
|
import '../../../../models/common/search_type.dart';
|
||||||
import '../../../../models/video_detail_res.dart';
|
import '../../../../models/video_detail_res.dart';
|
||||||
import '../introduction/index.dart';
|
import '../introduction/index.dart';
|
||||||
@ -52,7 +53,7 @@ class _HeaderControlState extends State<HeaderControl> {
|
|||||||
final Box<dynamic> videoStorage = GStrorage.video;
|
final Box<dynamic> videoStorage = GStrorage.video;
|
||||||
late List<double> speedsList;
|
late List<double> speedsList;
|
||||||
double buttonSpace = 8;
|
double buttonSpace = 8;
|
||||||
bool showTitle = false;
|
RxBool isFullScreen = false.obs;
|
||||||
late String heroTag;
|
late String heroTag;
|
||||||
late VideoIntroController videoIntroController;
|
late VideoIntroController videoIntroController;
|
||||||
late VideoDetailData videoDetail;
|
late VideoDetailData videoDetail;
|
||||||
@ -69,13 +70,8 @@ class _HeaderControlState extends State<HeaderControl> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void fullScreenStatusListener() {
|
void fullScreenStatusListener() {
|
||||||
widget.videoDetailCtr!.plPlayerController.isFullScreen
|
widget.videoDetailCtr!.plPlayerController.isFullScreen.listen((bool val) {
|
||||||
.listen((bool isFullScreen) {
|
isFullScreen.value = val;
|
||||||
if (isFullScreen) {
|
|
||||||
showTitle = true;
|
|
||||||
} else {
|
|
||||||
showTitle = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// TODO setState() called after dispose()
|
/// TODO setState() called after dispose()
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
@ -218,6 +214,87 @@ class _HeaderControlState extends State<HeaderControl> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 发送弹幕
|
||||||
|
void showShootDanmakuSheet() {
|
||||||
|
final TextEditingController textController = TextEditingController();
|
||||||
|
bool isSending = false; // 追踪是否正在发送
|
||||||
|
showDialog(
|
||||||
|
context: Get.context!,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
// TODO: 支持更多类型和颜色的弹幕
|
||||||
|
return AlertDialog(
|
||||||
|
title: const Text('发送弹幕(测试)'),
|
||||||
|
content: StatefulBuilder(
|
||||||
|
builder: (BuildContext context, StateSetter setState) {
|
||||||
|
return TextField(
|
||||||
|
controller: textController,
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Get.back(),
|
||||||
|
child: Text(
|
||||||
|
'取消',
|
||||||
|
style: TextStyle(color: Theme.of(context).colorScheme.outline),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
StatefulBuilder(
|
||||||
|
builder: (BuildContext context, StateSetter setState) {
|
||||||
|
return TextButton(
|
||||||
|
onPressed: isSending
|
||||||
|
? null
|
||||||
|
: () async {
|
||||||
|
final String msg = textController.text;
|
||||||
|
if (msg.isEmpty) {
|
||||||
|
SmartDialog.showToast('弹幕内容不能为空');
|
||||||
|
return;
|
||||||
|
} else if (msg.length > 100) {
|
||||||
|
SmartDialog.showToast('弹幕内容不能超过100个字符');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setState(() {
|
||||||
|
isSending = true; // 开始发送,更新状态
|
||||||
|
});
|
||||||
|
//修改按钮文字
|
||||||
|
final dynamic res = await DanmakaHttp.shootDanmaku(
|
||||||
|
oid: widget.videoDetailCtr!.cid.value,
|
||||||
|
msg: textController.text,
|
||||||
|
bvid: widget.videoDetailCtr!.bvid,
|
||||||
|
progress:
|
||||||
|
widget.controller!.position.value.inMilliseconds,
|
||||||
|
type: 1,
|
||||||
|
);
|
||||||
|
setState(() {
|
||||||
|
isSending = false; // 发送结束,更新状态
|
||||||
|
});
|
||||||
|
if (res['status']) {
|
||||||
|
SmartDialog.showToast('发送成功');
|
||||||
|
// 发送成功,自动预览该弹幕,避免重新请求
|
||||||
|
// TODO: 暂停状态下预览弹幕仍会移动与计时,可考虑添加到dmSegList或其他方式实现
|
||||||
|
widget.controller!.danmakuController!.addItems([
|
||||||
|
DanmakuItem(
|
||||||
|
msg,
|
||||||
|
color: Colors.white,
|
||||||
|
time: widget
|
||||||
|
.controller!.position.value.inMilliseconds,
|
||||||
|
type: DanmakuItemType.scroll,
|
||||||
|
isSend: true,
|
||||||
|
)
|
||||||
|
]);
|
||||||
|
Get.back();
|
||||||
|
} else {
|
||||||
|
SmartDialog.showToast('发送失败,错误信息为${res['msg']}');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Text(isSending ? '发送中...' : '发送'),
|
||||||
|
);
|
||||||
|
})
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// 定时关闭
|
/// 定时关闭
|
||||||
void scheduleExit() async {
|
void scheduleExit() async {
|
||||||
const List<int> scheduleTimeChoices = [
|
const List<int> scheduleTimeChoices = [
|
||||||
@ -1079,7 +1156,7 @@ class _HeaderControlState extends State<HeaderControl> {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
SizedBox(width: buttonSpace),
|
SizedBox(width: buttonSpace),
|
||||||
if (showTitle &&
|
if (isFullScreen.value &&
|
||||||
isLandscape &&
|
isLandscape &&
|
||||||
widget.videoType == SearchType.video) ...[
|
widget.videoType == SearchType.video) ...[
|
||||||
Column(
|
Column(
|
||||||
@ -1131,6 +1208,43 @@ class _HeaderControlState extends State<HeaderControl> {
|
|||||||
// ),
|
// ),
|
||||||
// fuc: () => _.screenshot(),
|
// fuc: () => _.screenshot(),
|
||||||
// ),
|
// ),
|
||||||
|
if (isFullScreen.value) ...[
|
||||||
|
SizedBox(
|
||||||
|
width: 56,
|
||||||
|
height: 34,
|
||||||
|
child: TextButton(
|
||||||
|
style: ButtonStyle(
|
||||||
|
padding: MaterialStateProperty.all(EdgeInsets.zero),
|
||||||
|
),
|
||||||
|
onPressed: () => showShootDanmakuSheet(),
|
||||||
|
child: const Text(
|
||||||
|
'发弹幕',
|
||||||
|
style: textStyle,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
width: 34,
|
||||||
|
height: 34,
|
||||||
|
child: Obx(
|
||||||
|
() => IconButton(
|
||||||
|
style: ButtonStyle(
|
||||||
|
padding: MaterialStateProperty.all(EdgeInsets.zero),
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
_.isOpenDanmu.value = !_.isOpenDanmu.value;
|
||||||
|
},
|
||||||
|
icon: Icon(
|
||||||
|
_.isOpenDanmu.value
|
||||||
|
? Icons.subtitles_outlined
|
||||||
|
: Icons.subtitles_off_outlined,
|
||||||
|
size: 19,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
SizedBox(width: buttonSpace),
|
SizedBox(width: buttonSpace),
|
||||||
if (Platform.isAndroid) ...<Widget>[
|
if (Platform.isAndroid) ...<Widget>[
|
||||||
SizedBox(
|
SizedBox(
|
||||||
|
|||||||
Reference in New Issue
Block a user