mod: live schedule

Closes #581

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
bggRGjQaUbCoE
2025-04-02 18:03:47 +08:00
parent d1a6798f2e
commit 86abf006d0
8 changed files with 378 additions and 293 deletions

View File

@@ -145,24 +145,26 @@ class LiveRoomController extends GetxController {
}
void liveMsg() {
LiveHttp.liveRoomDanmaPrefetch(roomId: roomId).then((v) {
if (v['status']) {
messages.addAll((v['data'] as List)
.map((obj) => {
'name': obj['user']['base']['name'],
'uid': obj['user']['uid'],
'text': obj['text'],
'emots': obj['emots'],
'uemote': obj['emoticon']['emoticon_unique'] != ""
? obj['emoticon']
: null,
})
.toList());
WidgetsBinding.instance.addPostFrameCallback(
(_) => scrollToBottom(),
);
}
});
if (messages.isEmpty) {
LiveHttp.liveRoomDanmaPrefetch(roomId: roomId).then((v) {
if (v['status']) {
messages.addAll((v['data'] as List)
.map((obj) => {
'name': obj['user']['base']['name'],
'uid': obj['user']['uid'],
'text': obj['text'],
'emots': obj['emots'],
'uemote': obj['emoticon']['emoticon_unique'] != ""
? obj['emoticon']
: null,
})
.toList());
WidgetsBinding.instance.addPostFrameCallback(
(_) => scrollToBottom(),
);
}
});
}
LiveHttp.liveRoomGetDanmakuToken(roomId: roomId).then((v) {
if (v['status']) {
LiveDanmakuInfo info = v['data'];

View File

@@ -4,6 +4,7 @@ import 'dart:ui';
import 'package:PiliPlus/http/live.dart';
import 'package:PiliPlus/pages/live_room/widgets/chat.dart';
import 'package:PiliPlus/pages/live_room/widgets/header_control.dart';
import 'package:PiliPlus/services/service_locator.dart';
import 'package:PiliPlus/utils/extension.dart';
import 'package:PiliPlus/utils/utils.dart';
@@ -75,7 +76,7 @@ class _LiveRoomPageState extends State<LiveRoomPage>
videoSourceInit();
_futureBuilderFuture = _liveRoomController.queryLiveInfo();
plPlayerController.autoEnterFullscreen();
_liveRoomController.liveMsg();
plPlayerController.addStatusLister(playerListener);
_listener = plPlayerController.isFullScreen.listen((isFullScreen) {
if (isFullScreen != _isFullScreen) {
_isFullScreen = isFullScreen;
@@ -84,6 +85,16 @@ class _LiveRoomPageState extends State<LiveRoomPage>
});
}
void playerListener(PlayerStatus? status) {
if (status != PlayerStatus.playing) {
plPlayerController.danmakuController?.pause();
_liveRoomController.msgStream?.close();
} else {
plPlayerController.danmakuController?.resume();
_liveRoomController.liveMsg();
}
}
void _updateFontSize() async {
if (Platform.isAndroid) {
_isPipMode =
@@ -119,6 +130,7 @@ class _LiveRoomPageState extends State<LiveRoomPage>
_liveRoomController.msgStream?.close();
// floating?.dispose();
_node.dispose();
plPlayerController.removeStatusLister(playerListener);
plPlayerController.dispose();
_ctr.dispose();
super.dispose();
@@ -160,10 +172,13 @@ class _LiveRoomPageState extends State<LiveRoomPage>
fill: fill,
alignment: alignment,
plPlayerController: plPlayerController,
headerControl: LiveHeaderControl(
plPlayerController: plPlayerController,
floating: floating,
),
bottomControl: BottomControl(
plPlayerController: plPlayerController,
liveRoomCtr: _liveRoomController,
floating: floating,
onRefresh: () {
_futureBuilderFuture = _liveRoomController.queryLiveInfo();
},

View File

@@ -1,25 +1,20 @@
import 'dart:io';
import 'package:PiliPlus/plugin/pl_player/widgets/play_pause_btn.dart';
import 'package:PiliPlus/utils/storage.dart';
import 'package:floating/floating.dart';
import 'package:flutter/material.dart';
import 'package:PiliPlus/pages/live_room/index.dart';
import 'package:PiliPlus/plugin/pl_player/index.dart';
import 'package:get/get.dart';
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
class BottomControl extends StatelessWidget implements PreferredSizeWidget {
const BottomControl({
required this.plPlayerController,
required this.liveRoomCtr,
this.floating,
required this.onRefresh,
super.key,
});
final PlPlayerController plPlayerController;
final LiveRoomController liveRoomCtr;
final Floating? floating;
final VoidCallback onRefresh;
final TextStyle subTitleStyle = const TextStyle(fontSize: 12);
@@ -39,6 +34,10 @@ class BottomControl extends StatelessWidget implements PreferredSizeWidget {
titleSpacing: 14,
title: Row(
children: [
PlayOrPauseButton(
plPlayerController: plPlayerController,
),
const SizedBox(width: 10),
ComBtn(
icon: const Icon(
Icons.refresh,
@@ -78,18 +77,7 @@ class BottomControl extends StatelessWidget implements PreferredSizeWidget {
// ),
// ),
// const SizedBox(width: 4),
Obx(
() => IconButton(
onPressed: plPlayerController.setOnlyPlayAudio,
icon: Icon(
size: 18,
plPlayerController.onlyPlayAudio.value
? MdiIcons.musicCircle
: MdiIcons.musicCircleOutline,
color: Colors.white,
),
),
),
Obx(
() => IconButton(
onPressed: () {
@@ -140,32 +128,6 @@ class BottomControl extends StatelessWidget implements PreferredSizeWidget {
),
),
const SizedBox(width: 10),
if (Platform.isAndroid) ...[
SizedBox(
width: 34,
height: 34,
child: IconButton(
tooltip: '画中画',
style: ButtonStyle(
padding: WidgetStateProperty.all(EdgeInsets.zero),
),
onPressed: () async {
try {
if ((await floating?.isPipAvailable) == true) {
plPlayerController.hiddenControls(false);
floating!.enable(const EnableManual());
}
} catch (_) {}
},
icon: const Icon(
Icons.picture_in_picture_outlined,
size: 18,
color: Colors.white,
),
),
),
const SizedBox(width: 10),
],
Obx(
() => SizedBox(
width: 30,

View File

@@ -0,0 +1,89 @@
import 'dart:io';
import 'package:PiliPlus/utils/utils.dart';
import 'package:floating/floating.dart';
import 'package:flutter/material.dart';
import 'package:PiliPlus/plugin/pl_player/index.dart';
import 'package:get/get.dart';
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
class LiveHeaderControl extends StatelessWidget implements PreferredSizeWidget {
const LiveHeaderControl({
required this.plPlayerController,
this.floating,
super.key,
});
final Floating? floating;
final PlPlayerController plPlayerController;
@override
Size get preferredSize => const Size(double.infinity, kToolbarHeight);
@override
Widget build(BuildContext context) {
return AppBar(
backgroundColor: Colors.transparent,
foregroundColor: Colors.white,
primary: false,
automaticallyImplyLeading: false,
titleSpacing: 14,
title: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Obx(
() => IconButton(
onPressed: plPlayerController.setOnlyPlayAudio,
icon: Icon(
size: 18,
plPlayerController.onlyPlayAudio.value
? MdiIcons.musicCircle
: MdiIcons.musicCircleOutline,
color: Colors.white,
),
),
),
const SizedBox(width: 10),
if (Platform.isAndroid) ...[
SizedBox(
width: 34,
height: 34,
child: IconButton(
tooltip: '画中画',
style: ButtonStyle(
padding: WidgetStateProperty.all(EdgeInsets.zero),
),
onPressed: () async {
try {
if ((await floating?.isPipAvailable) == true) {
plPlayerController.hiddenControls(false);
floating!.enable(const EnableManual());
}
} catch (_) {}
},
icon: const Icon(
Icons.picture_in_picture_outlined,
size: 18,
color: Colors.white,
),
),
),
const SizedBox(width: 10),
],
IconButton(
onPressed: () => Utils.scheduleExit(
context,
plPlayerController.isFullScreen.value,
true,
),
icon: Icon(
size: 18,
Icons.schedule,
color: Colors.white,
),
),
],
),
);
}
}