show live online count

update live title

update live watchedshow

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
bggRGjQaUbCoE
2025-11-26 11:46:56 +08:00
parent 38a7afd63a
commit 10808c2a84
9 changed files with 179 additions and 106 deletions

View File

@@ -1,14 +1,11 @@
class WatchedShow { class WatchedShow {
String? textSmall;
String? textLarge; String? textLarge;
WatchedShow({ WatchedShow({
this.textSmall,
this.textLarge, this.textLarge,
}); });
factory WatchedShow.fromJson(Map<String, dynamic> json) => WatchedShow( factory WatchedShow.fromJson(Map<String, dynamic> json) => WatchedShow(
textSmall: json['text_small'] as String?,
textLarge: json['text_large'] as String?, textLarge: json['text_large'] as String?,
); );
} }

View File

@@ -2,7 +2,6 @@ import 'package:PiliPlus/common/constants.dart';
import 'package:PiliPlus/common/widgets/image/image_save.dart'; import 'package:PiliPlus/common/widgets/image/image_save.dart';
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart'; import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
import 'package:PiliPlus/models_new/live/live_feed_index/card_data_list_item.dart'; import 'package:PiliPlus/models_new/live/live_feed_index/card_data_list_item.dart';
import 'package:PiliPlus/utils/num_utils.dart';
import 'package:PiliPlus/utils/page_utils.dart'; import 'package:PiliPlus/utils/page_utils.dart';
import 'package:PiliPlus/utils/utils.dart'; import 'package:PiliPlus/utils/utils.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@@ -130,9 +129,9 @@ class LiveCardVApp extends StatelessWidget {
'${item.areaName}', '${item.areaName}',
style: const TextStyle(fontSize: 11, color: Colors.white), style: const TextStyle(fontSize: 11, color: Colors.white),
), ),
if (item.watchedShow?.textSmall != null) if (item.watchedShow?.textLarge case final textLarge?)
Text( Text(
'${NumUtils.numFormat(item.watchedShow!.textSmall)}围观', textLarge,
style: const TextStyle(fontSize: 11, color: Colors.white), style: const TextStyle(fontSize: 11, color: Colors.white),
), ),
], ],

View File

@@ -129,10 +129,11 @@ class LiveCardVFollow extends StatelessWidget {
'${liveItem.areaName}', '${liveItem.areaName}',
style: const TextStyle(fontSize: 11, color: Colors.white), style: const TextStyle(fontSize: 11, color: Colors.white),
), ),
Text( if (liveItem.textSmall case final textSmall?)
liveItem.textSmall ?? '', Text(
style: const TextStyle(fontSize: 11, color: Colors.white), '$textSmall围观',
), style: const TextStyle(fontSize: 11, color: Colors.white),
),
], ],
), ),
); );

View File

@@ -21,7 +21,9 @@ import 'package:PiliPlus/services/service_locator.dart';
import 'package:PiliPlus/tcp/live.dart'; import 'package:PiliPlus/tcp/live.dart';
import 'package:PiliPlus/utils/accounts.dart'; import 'package:PiliPlus/utils/accounts.dart';
import 'package:PiliPlus/utils/danmaku_utils.dart'; import 'package:PiliPlus/utils/danmaku_utils.dart';
import 'package:PiliPlus/utils/duration_utils.dart';
import 'package:PiliPlus/utils/extension.dart'; import 'package:PiliPlus/utils/extension.dart';
import 'package:PiliPlus/utils/num_utils.dart';
import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:PiliPlus/utils/storage_pref.dart';
import 'package:PiliPlus/utils/utils.dart'; import 'package:PiliPlus/utils/utils.dart';
import 'package:PiliPlus/utils/video_utils.dart'; import 'package:PiliPlus/utils/video_utils.dart';
@@ -62,6 +64,28 @@ class LiveRoomController extends GetxController {
liveTimeTimer = null; liveTimeTimer = null;
} }
Widget get timeWidget => Obx(() {
final liveTime = this.liveTime.value;
String text = '';
if (liveTime != null) {
final duration = DurationUtils.formatDurationBetween(
liveTime * 1000,
DateTime.now().millisecondsSinceEpoch,
);
text += duration.isEmpty ? '刚刚开播' : '开播$duration';
}
if (text.isEmpty) {
return const SizedBox.shrink();
}
return Text(
text,
style: const TextStyle(
fontSize: 12,
color: Colors.white,
),
);
});
// dm // dm
LiveDmInfoData? dmInfo; LiveDmInfoData? dmInfo;
List<RichTextItem>? savedDanmaku; List<RichTextItem>? savedDanmaku;
@@ -91,6 +115,36 @@ class LiveRoomController extends GetxController {
final headerKey = GlobalKey<TimeBatteryMixin>(); final headerKey = GlobalKey<TimeBatteryMixin>();
final RxString title = ''.obs;
final RxnString onlineCount = RxnString();
Widget get onlineWidget => Obx(() {
if (onlineCount.value case final onlineCount?) {
return Text(
'高能观众($onlineCount)',
style: const TextStyle(
fontSize: 12,
color: Colors.white,
),
);
}
return const SizedBox.shrink();
});
final RxnString watchedShow = RxnString();
Widget get watchedWidget => Obx(() {
if (watchedShow.value case final watchedShow?) {
return Text(
watchedShow,
style: const TextStyle(
fontSize: 12,
color: Colors.white,
),
);
}
return const SizedBox.shrink();
});
@override @override
void onInit() { void onInit() {
super.onInit(); super.onInit();
@@ -179,6 +233,8 @@ class LiveRoomController extends GetxController {
if (res['status']) { if (res['status']) {
RoomInfoH5Data data = res['data']; RoomInfoH5Data data = res['data'];
roomInfoH5.value = data; roomInfoH5.value = data;
title.value = data.roomInfo?.title ?? '';
watchedShow.value = data.watchedShow?.textLarge;
videoPlayerServiceHandler?.onVideoDetailChange(data, roomId, heroTag); videoPlayerServiceHandler?.onVideoDetailChange(data, roomId, heroTag);
} else { } else {
if (res['msg'] != null) { if (res['msg'] != null) {
@@ -347,9 +403,7 @@ class LiveRoomController extends GetxController {
final info = obj['info']; final info = obj['info'];
final first = info[0]; final first = info[0];
final content = first[15]; final content = first[15];
final Map<String, dynamic> extra = jsonDecode( final Map<String, dynamic> extra = jsonDecode(content['extra']);
content['extra'],
);
final user = content['user']; final user = content['user'];
// final midHash = first[7]; // final midHash = first[7];
final uid = user['uid']; final uid = user['uid'];
@@ -408,6 +462,15 @@ class LiveRoomController extends GetxController {
fsSC.value = item; fsSC.value = item;
} }
break; break;
case 'WATCHED_CHANGE':
watchedShow.value = obj['data']['text_large'];
break;
case 'ONLINE_RANK_COUNT':
onlineCount.value = NumUtils.numFormat(obj['data']['count']);
break;
case 'ROOM_CHANGE':
title.value = obj['data']['title'];
break;
} }
} catch (_) {} } catch (_) {}
} }

View File

@@ -23,7 +23,6 @@ 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/utils/fullscreen.dart';
import 'package:PiliPlus/plugin/pl_player/view.dart'; import 'package:PiliPlus/plugin/pl_player/view.dart';
import 'package:PiliPlus/services/service_locator.dart'; import 'package:PiliPlus/services/service_locator.dart';
import 'package:PiliPlus/utils/duration_utils.dart';
import 'package:PiliPlus/utils/extension.dart'; import 'package:PiliPlus/utils/extension.dart';
import 'package:PiliPlus/utils/page_utils.dart'; import 'package:PiliPlus/utils/page_utils.dart';
import 'package:PiliPlus/utils/storage.dart'; import 'package:PiliPlus/utils/storage.dart';
@@ -240,6 +239,7 @@ class _LiveRoomPageState extends State<LiveRoomPage>
onSendDanmaku: _liveRoomController.onSendDanmaku, onSendDanmaku: _liveRoomController.onSendDanmaku,
onPlayAudio: _liveRoomController.queryLiveUrl, onPlayAudio: _liveRoomController.queryLiveUrl,
isPortrait: isPortrait, isPortrait: isPortrait,
liveController: _liveRoomController,
), ),
bottomControl: BottomControl( bottomControl: BottomControl(
plPlayerController: plPlayerController, plPlayerController: plPlayerController,
@@ -493,62 +493,65 @@ class _LiveRoomPageState extends State<LiveRoomPage>
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
foregroundColor: Colors.white, foregroundColor: Colors.white,
titleTextStyle: const TextStyle(color: Colors.white), titleTextStyle: const TextStyle(color: Colors.white),
title: Obx( title: isFullScreen || plPlayerController.isDesktopPip
() { ? null
RoomInfoH5Data? roomInfoH5 = _liveRoomController.roomInfoH5.value; : Obx(
if (roomInfoH5 == null) { () {
return const SizedBox.shrink(); RoomInfoH5Data? roomInfoH5 =
} _liveRoomController.roomInfoH5.value;
return GestureDetector( if (roomInfoH5 == null) {
behavior: HitTestBehavior.opaque, return const SizedBox.shrink();
onTap: () => Get.toNamed('/member?mid=${roomInfoH5.roomInfo?.uid}'), }
child: Row( return GestureDetector(
spacing: 10, behavior: HitTestBehavior.opaque,
mainAxisSize: MainAxisSize.min, onTap: () =>
children: [ Get.toNamed('/member?mid=${roomInfoH5.roomInfo?.uid}'),
NetworkImgLayer( child: Row(
width: 34, spacing: 10,
height: 34, mainAxisSize: MainAxisSize.min,
type: ImageType.avatar, children: [
src: roomInfoH5.anchorInfo!.baseInfo!.face, NetworkImgLayer(
), width: 34,
Column( height: 34,
spacing: 1, type: ImageType.avatar,
crossAxisAlignment: CrossAxisAlignment.start, src: roomInfoH5.anchorInfo!.baseInfo!.face,
children: [ ),
Text( Expanded(
roomInfoH5.anchorInfo!.baseInfo!.uname!, child: Column(
style: const TextStyle(fontSize: 14), spacing: 1,
), crossAxisAlignment: CrossAxisAlignment.start,
Obx(() { children: [
final liveTime = _liveRoomController.liveTime.value; Row(
final textLarge = roomInfoH5.watchedShow?.textLarge; spacing: 10,
String text = ''; crossAxisAlignment: CrossAxisAlignment.end,
if (textLarge != null) { children: [
text += textLarge; Flexible(
} child: Text(
if (liveTime != null) { roomInfoH5.anchorInfo!.baseInfo!.uname!,
if (text.isNotEmpty) { style: const TextStyle(
text += ' '; fontSize: 14,
} color: Colors.white,
final duration = DurationUtils.formatDurationBetween( ),
liveTime * 1000, ),
DateTime.now().millisecondsSinceEpoch, ),
); _liveRoomController.onlineWidget,
text += duration.isEmpty ? '刚刚开播' : '开播$duration'; ],
} ),
if (text.isEmpty) { Row(
return const SizedBox.shrink(); spacing: 10,
} children: [
return Text(text, style: const TextStyle(fontSize: 12)); _liveRoomController.watchedWidget,
}), _liveRoomController.timeWidget,
], ],
), ),
], ],
),
),
],
),
);
},
), ),
);
},
),
actions: [ actions: [
// IconButton( // IconButton(
// tooltip: '刷新', // tooltip: '刷新',

View File

@@ -1,6 +1,7 @@
import 'dart:io'; import 'dart:io';
import 'package:PiliPlus/common/widgets/marquee.dart'; import 'package:PiliPlus/common/widgets/marquee.dart';
import 'package:PiliPlus/pages/live_room/controller.dart';
import 'package:PiliPlus/pages/video/widgets/header_control.dart'; import 'package:PiliPlus/pages/video/widgets/header_control.dart';
import 'package:PiliPlus/plugin/pl_player/controller.dart'; import 'package:PiliPlus/plugin/pl_player/controller.dart';
import 'package:PiliPlus/plugin/pl_player/widgets/common_btn.dart'; import 'package:PiliPlus/plugin/pl_player/widgets/common_btn.dart';
@@ -21,6 +22,7 @@ class LiveHeaderControl extends StatefulWidget {
required this.onSendDanmaku, required this.onSendDanmaku,
required this.onPlayAudio, required this.onPlayAudio,
required this.isPortrait, required this.isPortrait,
required this.liveController,
}); });
final String? title; final String? title;
@@ -29,6 +31,7 @@ class LiveHeaderControl extends StatefulWidget {
final VoidCallback onSendDanmaku; final VoidCallback onSendDanmaku;
final VoidCallback onPlayAudio; final VoidCallback onPlayAudio;
final bool isPortrait; final bool isPortrait;
final LiveRoomController liveController;
@override @override
State<LiveHeaderControl> createState() => _LiveHeaderControlState(); State<LiveHeaderControl> createState() => _LiveHeaderControlState();
@@ -36,6 +39,7 @@ class LiveHeaderControl extends StatefulWidget {
class _LiveHeaderControlState extends State<LiveHeaderControl> class _LiveHeaderControlState extends State<LiveHeaderControl>
with TimeBatteryMixin { with TimeBatteryMixin {
@override
late final plPlayerController = widget.plPlayerController; late final plPlayerController = widget.plPlayerController;
@override @override
@@ -51,11 +55,12 @@ class _LiveHeaderControlState extends State<LiveHeaderControl>
Widget build(BuildContext context) { Widget build(BuildContext context) {
final isFullScreen = this.isFullScreen; final isFullScreen = this.isFullScreen;
showCurrTimeIfNeeded(isFullScreen); showCurrTimeIfNeeded(isFullScreen);
final liveController = widget.liveController;
Widget child; Widget child;
if (widget.title case final title?) { child = Obx(
child = MarqueeText( () => MarqueeText(
key: titleKey, key: titleKey,
title, liveController.title.value,
spacing: 30, spacing: 30,
velocity: 30, velocity: 30,
style: const TextStyle( style: const TextStyle(
@@ -63,30 +68,35 @@ class _LiveHeaderControlState extends State<LiveHeaderControl>
height: 1, height: 1,
color: Colors.white, color: Colors.white,
), ),
),
);
if (isFullScreen) {
child = Column(
spacing: 5,
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
child,
Row(
spacing: 10,
children: [
if (widget.upName case final upName?)
Text(
upName,
style: const TextStyle(
fontSize: 12,
color: Colors.white,
),
),
liveController.watchedWidget,
liveController.onlineWidget,
liveController.timeWidget,
],
),
],
); );
if (isFullScreen && widget.upName != null) {
child = Column(
spacing: 5,
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
child,
Text(
widget.upName!,
maxLines: 1,
style: const TextStyle(
fontSize: 12,
height: 1,
color: Colors.white,
),
),
],
);
}
child = Expanded(child: child);
} else {
child = const Spacer();
} }
child = Expanded(child: child);
return AppBar( return AppBar(
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
foregroundColor: Colors.white, foregroundColor: Colors.white,

View File

@@ -2,7 +2,6 @@ import 'package:PiliPlus/common/constants.dart';
import 'package:PiliPlus/common/widgets/image/image_save.dart'; import 'package:PiliPlus/common/widgets/image/image_save.dart';
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart'; import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
import 'package:PiliPlus/models_new/live/live_search/room_item.dart'; import 'package:PiliPlus/models_new/live/live_search/room_item.dart';
import 'package:PiliPlus/utils/num_utils.dart';
import 'package:PiliPlus/utils/page_utils.dart'; import 'package:PiliPlus/utils/page_utils.dart';
import 'package:PiliPlus/utils/utils.dart'; import 'package:PiliPlus/utils/utils.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@@ -101,9 +100,9 @@ class LiveCardVSearch extends StatelessWidget {
'${item.name}', '${item.name}',
style: const TextStyle(fontSize: 11, color: Colors.white), style: const TextStyle(fontSize: 11, color: Colors.white),
), ),
if (item.watchedShow?.textSmall != null) if (item.watchedShow?.textLarge case final textLarge?)
Text( Text(
'${NumUtils.numFormat(item.watchedShow!.textSmall)}围观', textLarge,
style: const TextStyle(fontSize: 11, color: Colors.white), style: const TextStyle(fontSize: 11, color: Colors.white),
), ),
], ],

View File

@@ -2,6 +2,7 @@ import 'package:PiliPlus/common/constants.dart';
import 'package:PiliPlus/common/widgets/image/image_save.dart'; import 'package:PiliPlus/common/widgets/image/image_save.dart';
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart'; import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
import 'package:PiliPlus/models/search/result.dart'; import 'package:PiliPlus/models/search/result.dart';
import 'package:PiliPlus/utils/num_utils.dart';
import 'package:PiliPlus/utils/page_utils.dart'; import 'package:PiliPlus/utils/page_utils.dart';
import 'package:PiliPlus/utils/utils.dart'; import 'package:PiliPlus/utils/utils.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@@ -126,7 +127,7 @@ class LiveItem extends StatelessWidget {
style: const TextStyle(fontSize: 11, color: Colors.white), style: const TextStyle(fontSize: 11, color: Colors.white),
), ),
Text( Text(
'围观:${online.toString()}', '${NumUtils.numFormat(online)}围观',
style: const TextStyle(fontSize: 11, color: Colors.white), style: const TextStyle(fontSize: 11, color: Colors.white),
), ),
], ],

View File

@@ -209,6 +209,14 @@ class PlayerFocus extends StatelessWidget {
} }
return true; return true;
case LogicalKeyboardKey.keyL:
if (isFullScreen || plPlayerController.isDesktopPip) {
plPlayerController.onLockControl(
!plPlayerController.controlsLock.value,
);
}
return true;
case LogicalKeyboardKey.enter: case LogicalKeyboardKey.enter:
if (onSkipSegment?.call() ?? false) { if (onSkipSegment?.call() ?? false) {
return true; return true;
@@ -248,14 +256,6 @@ class PlayerFocus extends StatelessWidget {
} }
return true; return true;
case LogicalKeyboardKey.keyL:
if (isFullScreen || plPlayerController.isDesktopPip) {
plPlayerController.onLockControl(
!plPlayerController.controlsLock.value,
);
}
return true;
case LogicalKeyboardKey.bracketLeft: case LogicalKeyboardKey.bracketLeft:
if (introController case final introController?) { if (introController case final introController?) {
if (!introController.prevPlay()) { if (!introController.prevPlay()) {