Merge branch 'feature-liveRoomRender'

This commit is contained in:
guozhigq
2024-02-11 18:48:24 +08:00
11 changed files with 319 additions and 109 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -233,6 +233,10 @@ class Api {
static const String liveRoomInfo = static const String liveRoomInfo =
'${HttpString.liveBaseUrl}/xlive/web-room/v2/index/getRoomPlayInfo'; '${HttpString.liveBaseUrl}/xlive/web-room/v2/index/getRoomPlayInfo';
// 直播间详情 H5
static const String liveRoomInfoH5 =
'${HttpString.liveBaseUrl}/xlive/web-room/v1/index/getH5InfoByRoom';
// 用户信息 需要Wbi签名 // 用户信息 需要Wbi签名
// https://api.bilibili.com/x/space/wbi/acc/info?mid=503427686&token=&platform=web&web_location=1550101&w_rid=d709892496ce93e3d94d6d37c95bde91&wts=1689301482 // https://api.bilibili.com/x/space/wbi/acc/info?mid=503427686&token=&platform=web&web_location=1550101&w_rid=d709892496ce93e3d94d6d37c95bde91&wts=1689301482
static const String memberInfo = '/x/space/wbi/acc/info'; static const String memberInfo = '/x/space/wbi/acc/info';

View File

@ -1,5 +1,6 @@
import '../models/live/item.dart'; import '../models/live/item.dart';
import '../models/live/room_info.dart'; import '../models/live/room_info.dart';
import '../models/live/room_info_h5.dart';
import 'api.dart'; import 'api.dart';
import 'init.dart'; import 'init.dart';
@ -46,4 +47,22 @@ class LiveHttp {
}; };
} }
} }
static Future liveRoomInfoH5({roomId, qn}) async {
var res = await Request().get(Api.liveRoomInfoH5, data: {
'room_id': roomId,
});
if (res.data['code'] == 0) {
return {
'status': true,
'data': RoomInfoH5Model.fromJson(res.data['data'])
};
} else {
return {
'status': false,
'data': [],
'msg': res.data['message'],
};
}
}
} }

View File

@ -25,7 +25,6 @@ import 'package:pilipala/utils/recommend_filter.dart';
import 'package:catcher_2/catcher_2.dart'; import 'package:catcher_2/catcher_2.dart';
import './services/loggeer.dart'; import './services/loggeer.dart';
void main() async { void main() async {
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
MediaKit.ensureInitialized(); MediaKit.ensureInitialized();
@ -63,7 +62,6 @@ void main() async {
}, },
); );
// 小白条、导航栏沉浸 // 小白条、导航栏沉浸
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle( SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(
@ -72,7 +70,6 @@ void main() async {
statusBarColor: Colors.transparent, statusBarColor: Colors.transparent,
)); ));
Data.init(); Data.init();
GStrorage.lazyInit();
PiliSchame.init(); PiliSchame.init();
}); });
} }

View File

@ -0,0 +1,130 @@
class RoomInfoH5Model {
RoomInfoH5Model({
this.roomInfo,
this.anchorInfo,
this.isRoomFeed,
this.watchedShow,
this.likeInfoV3,
this.blockInfo,
});
RoomInfo? roomInfo;
AnchorInfo? anchorInfo;
int? isRoomFeed;
Map? watchedShow;
LikeInfoV3? likeInfoV3;
Map? blockInfo;
RoomInfoH5Model.fromJson(Map<String, dynamic> json) {
roomInfo = RoomInfo.fromJson(json['room_info']);
anchorInfo = AnchorInfo.fromJson(json['anchor_info']);
isRoomFeed = json['is_room_feed'];
watchedShow = json['watched_show'];
likeInfoV3 = LikeInfoV3.fromJson(json['like_info_v3']);
blockInfo = json['block_info'];
}
}
class RoomInfo {
RoomInfo({
this.uid,
this.roomId,
this.title,
this.cover,
this.description,
this.liveStatus,
this.liveStartTime,
this.areaId,
this.areaName,
this.parentAreaId,
this.parentAreaName,
this.online,
this.background,
this.appBackground,
this.liveId,
});
int? uid;
int? roomId;
String? title;
String? cover;
String? description;
int? liveStatus;
int? liveStartTime;
int? areaId;
String? areaName;
int? parentAreaId;
String? parentAreaName;
int? online;
String? background;
String? appBackground;
String? liveId;
RoomInfo.fromJson(Map<String, dynamic> json) {
uid = json['uid'];
roomId = json['room_id'];
title = json['title'];
cover = json['cover'];
description = json['description'];
liveStatus = json['liveS_satus'];
liveStartTime = json['live_start_time'];
areaId = json['area_id'];
areaName = json['area_name'];
parentAreaId = json['parent_area_id'];
parentAreaName = json['parent_area_name'];
online = json['online'];
background = json['background'];
appBackground = json['app_background'];
liveId = json['live_id'];
}
}
class AnchorInfo {
AnchorInfo({
this.baseInfo,
this.relationInfo,
});
BaseInfo? baseInfo;
RelationInfo? relationInfo;
AnchorInfo.fromJson(Map<String, dynamic> json) {
baseInfo = BaseInfo.fromJson(json['base_info']);
relationInfo = RelationInfo.fromJson(json['relation_info']);
}
}
class BaseInfo {
BaseInfo({
this.uname,
this.face,
});
String? uname;
String? face;
BaseInfo.fromJson(Map<String, dynamic> json) {
uname = json['uname'];
face = json['face'];
}
}
class RelationInfo {
RelationInfo({this.attention});
int? attention;
RelationInfo.fromJson(Map<String, dynamic> json) {
attention = json['attention'];
}
}
class LikeInfoV3 {
LikeInfoV3({this.totalLikes});
int? totalLikes;
LikeInfoV3.fromJson(Map<String, dynamic> json) {
totalLikes = json['total_likes'];
}
}

View File

@ -184,18 +184,32 @@ class VideoStat extends StatelessWidget {
tileMode: TileMode.mirror, tileMode: TileMode.mirror,
), ),
), ),
child: RichText( child: Row(
maxLines: 1, mainAxisAlignment: MainAxisAlignment.spaceBetween,
textAlign: TextAlign.justify, children: [
softWrap: false, Text(
text: TextSpan( liveItem!.areaName!,
style: const TextStyle(fontSize: 11, color: Colors.white), style: const TextStyle(fontSize: 11, color: Colors.white),
children: [ ),
TextSpan(text: liveItem!.areaName!), Text(
TextSpan(text: liveItem!.watchedShow!['text_small']), liveItem!.watchedShow!['text_small'],
], style: const TextStyle(fontSize: 11, color: Colors.white),
), ),
],
), ),
// child: RichText(
// maxLines: 1,
// textAlign: TextAlign.justify,
// softWrap: false,
// text: TextSpan(
// style: const TextStyle(fontSize: 11, color: Colors.white),
// children: [
// TextSpan(text: liveItem!.areaName!),
// TextSpan(text: liveItem!.watchedShow!['text_small']),
// ],
// ),
// ),
); );
} }
} }

View File

@ -4,6 +4,8 @@ import 'package:pilipala/http/live.dart';
import 'package:pilipala/models/live/room_info.dart'; import 'package:pilipala/models/live/room_info.dart';
import 'package:pilipala/plugin/pl_player/index.dart'; import 'package:pilipala/plugin/pl_player/index.dart';
import '../../models/live/room_info_h5.dart';
class LiveRoomController extends GetxController { class LiveRoomController extends GetxController {
String cover = ''; String cover = '';
late int roomId; late int roomId;
@ -21,6 +23,7 @@ class LiveRoomController extends GetxController {
// controlsStyle: ControlsStyle.live, // controlsStyle: ControlsStyle.live,
// enabledButtons: const EnabledButtons(pip: true), // enabledButtons: const EnabledButtons(pip: true),
// ); // );
Rx<RoomInfoH5Model> roomInfoH5 = RoomInfoH5Model().obs;
@override @override
void onInit() { void onInit() {
@ -37,10 +40,11 @@ class LiveRoomController extends GetxController {
} }
} }
queryLiveInfo(); queryLiveInfo();
queryLiveInfoH5();
} }
playerInit(source) { playerInit(source) async {
plPlayerController.setDataSource( await plPlayerController.setDataSource(
DataSource( DataSource(
videoSource: source, videoSource: source,
audioSource: null, audioSource: null,
@ -66,7 +70,8 @@ class LiveRoomController extends GetxController {
String videoUrl = (item.urlInfo?.first.host)! + String videoUrl = (item.urlInfo?.first.host)! +
item.baseUrl! + item.baseUrl! +
item.urlInfo!.first.extra!; item.urlInfo!.first.extra!;
playerInit(videoUrl); await playerInit(videoUrl);
return res;
} }
} }
@ -80,4 +85,12 @@ class LiveRoomController extends GetxController {
volumeOff.value = true; volumeOff.value = true;
} }
} }
Future queryLiveInfoH5() async {
var res = await LiveHttp.liveRoomInfoH5(roomId: roomId);
if (res['status']) {
roomInfoH5.value = res['data'];
}
return res;
}
} }

View File

@ -19,6 +19,8 @@ class LiveRoomPage extends StatefulWidget {
class _LiveRoomPageState extends State<LiveRoomPage> { class _LiveRoomPageState extends State<LiveRoomPage> {
final LiveRoomController _liveRoomController = Get.put(LiveRoomController()); final LiveRoomController _liveRoomController = Get.put(LiveRoomController());
PlPlayerController? plPlayerController; PlPlayerController? plPlayerController;
late Future? _futureBuilder;
late Future? _futureBuilderFuture;
bool isShowCover = true; bool isShowCover = true;
bool isPlay = true; bool isPlay = true;
@ -39,6 +41,8 @@ class _LiveRoomPageState extends State<LiveRoomPage> {
if (Platform.isAndroid) { if (Platform.isAndroid) {
floating = Floating(); floating = Floating();
} }
_futureBuilder = _liveRoomController.queryLiveInfoH5();
_futureBuilderFuture = _liveRoomController.queryLiveInfo();
} }
@override @override
@ -52,57 +56,123 @@ class _LiveRoomPageState extends State<LiveRoomPage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
Widget videoPlayerPanel = FutureBuilder(
future: _futureBuilderFuture,
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData && snapshot.data['status']) {
return PLVideoPlayer(
controller: plPlayerController!,
bottomControl: BottomControl(
controller: plPlayerController,
liveRoomCtr: _liveRoomController,
floating: floating,
),
);
} else {
return const SizedBox();
}
},
);
Widget childWhenDisabled = Scaffold( Widget childWhenDisabled = Scaffold(
primary: true, primary: true,
appBar: PreferredSize( backgroundColor: Colors.black,
preferredSize: Size.fromHeight( body: Stack(
MediaQuery.of(context).orientation == Orientation.portrait ? 56 : 0,
),
child: AppBar(
centerTitle: false,
titleSpacing: 0,
title: _liveRoomController.liveItem != null
? Row(
children: [
NetworkImgLayer(
width: 34,
height: 34,
type: 'avatar',
src: _liveRoomController.liveItem.face,
),
const SizedBox(width: 10),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
_liveRoomController.liveItem.uname,
style: const TextStyle(fontSize: 14),
),
const SizedBox(height: 1),
if (_liveRoomController.liveItem.watchedShow != null)
Text(
_liveRoomController
.liveItem.watchedShow['text_large'] ??
'',
style: const TextStyle(fontSize: 12)),
],
),
],
)
: const SizedBox(),
// actions: [
// SizedBox(
// height: 34,
// child: ElevatedButton(onPressed: () {}, child: const Text('关注')),
// ),
// const SizedBox(width: 12),
// ],
),
),
body: Column(
children: [ children: [
Stack( // Obx(
// () => Positioned.fill(
// child: Opacity(
// opacity: 0.8,
// child: _liveRoomController
// .roomInfoH5.value.roomInfo?.appBackground !=
// '' &&
// _liveRoomController
// .roomInfoH5.value.roomInfo?.appBackground !=
// null
// ? NetworkImgLayer(
// width: Get.width,
// height: Get.height,
// src: _liveRoomController
// .roomInfoH5.value.roomInfo?.appBackground ??
// '',
// )
// : Image.asset(
// 'assets/images/live/default_bg.webp',
// width: Get.width,
// height: Get.height,
// ),
// ),
// ),
// ),
Positioned.fill(
child: Opacity(
opacity: 0.8,
child: Image.asset(
'assets/images/live/default_bg.webp',
width: Get.width,
height: Get.height,
),
),
),
Column(
children: [ children: [
AppBar(
centerTitle: false,
titleSpacing: 0,
backgroundColor: Colors.transparent,
foregroundColor: Colors.white,
toolbarHeight:
MediaQuery.of(context).orientation == Orientation.portrait
? 56
: 0,
title: FutureBuilder(
future: _futureBuilder,
builder: (context, snapshot) {
if (snapshot.data == null) {
return const SizedBox();
}
Map data = snapshot.data as Map;
if (data['status']) {
return Obx(
() => Row(
children: [
NetworkImgLayer(
width: 34,
height: 34,
type: 'avatar',
src: _liveRoomController
.roomInfoH5.value.anchorInfo!.baseInfo!.face,
),
const SizedBox(width: 10),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
_liveRoomController.roomInfoH5.value
.anchorInfo!.baseInfo!.uname!,
style: const TextStyle(fontSize: 14),
),
const SizedBox(height: 1),
if (_liveRoomController
.roomInfoH5.value.watchedShow !=
null)
Text(
_liveRoomController.roomInfoH5.value
.watchedShow!['text_large'] ??
'',
style: const TextStyle(fontSize: 12),
),
],
),
],
),
);
} else {
return const SizedBox();
}
},
),
),
PopScope( PopScope(
canPop: plPlayerController?.isFullScreen.value != true, canPop: plPlayerController?.isFullScreen.value != true,
onPopInvoked: (bool didPop) { onPopInvoked: (bool didPop) {
@ -120,55 +190,19 @@ class _LiveRoomPageState extends State<LiveRoomPage> {
Orientation.landscape Orientation.landscape
? Get.size.height ? Get.size.height
: Get.size.width * 9 / 16, : Get.size.width * 9 / 16,
child: plPlayerController!.videoPlayerController != null child: videoPlayerPanel,
? PLVideoPlayer(
controller: plPlayerController!,
bottomControl: BottomControl(
controller: plPlayerController,
liveRoomCtr: _liveRoomController,
floating: floating,
),
)
: const SizedBox(),
), ),
), ),
// if (_liveRoomController.liveItem != null &&
// _liveRoomController.liveItem.cover != null)
// Visibility(
// visible: isShowCover,
// child: Positioned(
// top: 0,
// left: 0,
// right: 0,
// child: NetworkImgLayer(
// type: 'emote',
// src: _liveRoomController.liveItem.cover,
// width: Get.size.width,
// height: videoHeight,
// ),
// ),
// ),
], ],
), ),
], ],
), ),
); );
Widget childWhenEnabled = AspectRatio(
aspectRatio: 16 / 9,
child: plPlayerController!.videoPlayerController != null
? PLVideoPlayer(
controller: plPlayerController!,
bottomControl: BottomControl(
controller: plPlayerController,
liveRoomCtr: _liveRoomController,
),
)
: const SizedBox(),
);
if (Platform.isAndroid) { if (Platform.isAndroid) {
return PiPSwitcher( return PiPSwitcher(
childWhenDisabled: childWhenDisabled, childWhenDisabled: childWhenDisabled,
childWhenEnabled: childWhenEnabled, childWhenEnabled: videoPlayerPanel,
floating: floating,
); );
} else { } else {
return childWhenDisabled; return childWhenDisabled;

View File

@ -586,6 +586,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
), ),
/// 进度条 live模式下禁用 /// 进度条 live模式下禁用
Obx( Obx(
() { () {
final int value = _.sliderPositionSeconds.value; final int value = _.sliderPositionSeconds.value;
@ -609,7 +610,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
} }
if (_.videoType.value == 'live') { if (_.videoType.value == 'live') {
return nil; return const SizedBox();
} }
if (value > max || max <= 0) { if (value > max || max <= 0) {
return nil; return nil;

View File

@ -42,6 +42,8 @@ class GStrorage {
return deletedEntries > 10; return deletedEntries > 10;
}, },
); );
// 视频设置
video = await Hive.openBox('video');
} }
static void regAdapter() { static void regAdapter() {
@ -52,11 +54,6 @@ class GStrorage {
Hive.registerAdapter(HotSearchItemAdapter()); Hive.registerAdapter(HotSearchItemAdapter());
} }
static Future<void> lazyInit() async {
// 视频设置
video = await Hive.openBox('video');
}
static Future<void> close() async { static Future<void> close() async {
// user.compact(); // user.compact();
// user.close(); // user.close();

View File

@ -188,6 +188,7 @@ flutter:
- assets/images/ - assets/images/
- assets/images/lv/ - assets/images/lv/
- assets/images/logo/ - assets/images/logo/
- assets/images/live/
# An image asset can refer to one or more resolution-specific "variants", see # An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware # https://flutter.dev/assets-and-images/#resolution-aware