Compare commits

..

5 Commits

Author SHA1 Message Date
bggRGjQaUbCoE
047e3cd26f fix: #147
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-01-12 19:57:17 +08:00
bggRGjQaUbCoE
a04da4c34a fix: live qa btn
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-01-12 18:23:17 +08:00
guozhigq
95c35cac58 feat: 直播画质切换
Closes #146

Co-authored-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-01-12 18:09:24 +08:00
bggRGjQaUbCoE
9429225029 mod: show lottery result
Closes #145

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-01-12 17:29:18 +08:00
bggRGjQaUbCoE
9cf9867fac opt: card width
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-01-12 14:18:27 +08:00
11 changed files with 132 additions and 23 deletions

View File

@@ -0,0 +1,43 @@
enum LiveQuality {
dolby,
super4K,
origin,
veryHigh,
bluRay,
superHD,
smooth,
flunt,
}
extension LiveQualityCode on LiveQuality {
static final List<int> _codeList = [
30000,
20000,
10000,
400,
250,
150,
80,
];
int get code => _codeList[index];
static LiveQuality? fromCode(int code) {
final index = _codeList.indexOf(code);
if (index != -1) {
return LiveQuality.values[index];
}
return null;
}
}
extension VideoQualityDesc on LiveQuality {
static final List<String> _descList = [
'杜比',
'4K',
'原画',
'蓝光',
'超清',
'流畅',
];
get description => _descList[index];
}

View File

@@ -114,7 +114,7 @@ class _BangumiPageState extends State<BangumiPage>
), ),
), ),
SizedBox( SizedBox(
height: Grid.mediumCardWidth / 2 / 0.75 + height: Grid.smallCardWidth / 2 / 0.75 +
MediaQuery.textScalerOf(context).scale(50), MediaQuery.textScalerOf(context).scale(50),
child: Obx( child: Obx(
() => _buildFollowBody( () => _buildFollowBody(
@@ -199,7 +199,7 @@ class _BangumiPageState extends State<BangumiPage>
_bangumiController.queryBangumiFollow(false); _bangumiController.queryBangumiFollow(false);
} }
return Container( return Container(
width: Grid.mediumCardWidth / 2, width: Grid.smallCardWidth / 2,
margin: EdgeInsets.only( margin: EdgeInsets.only(
left: StyleString.safeSpace, left: StyleString.safeSpace,
right: index == loadingState.response.length - 1 right: index == loadingState.response.length - 1

View File

@@ -320,7 +320,7 @@ class _DynamicDetailPageState extends State<DynamicDetailPage>
children: [ children: [
Builder( Builder(
builder: (context) { builder: (context) {
double padding = max(context.width / 2 - Grid.mediumCardWidth, 0); double padding = max(context.width / 2 - Grid.smallCardWidth, 0);
if (orientation == Orientation.portrait) { if (orientation == Orientation.portrait) {
return CustomScrollView( return CustomScrollView(
controller: _dynamicDetailController.scrollController, controller: _dynamicDetailController.scrollController,

View File

@@ -111,7 +111,7 @@ class _DynamicsTabPageState extends State<DynamicsTabPage>
slivers: [ slivers: [
const SliverFillRemaining(), const SliverFillRemaining(),
SliverConstrainedCrossAxis( SliverConstrainedCrossAxis(
maxExtent: Grid.mediumCardWidth * 2, maxExtent: Grid.smallCardWidth * 2,
sliver: SliverList( sliver: SliverList(
delegate: SliverChildBuilderDelegate( delegate: SliverChildBuilderDelegate(
(context, index) { (context, index) {
@@ -184,7 +184,7 @@ class _DynamicsTabPageState extends State<DynamicsTabPage>
slivers: [ slivers: [
const SliverFillRemaining(), const SliverFillRemaining(),
SliverConstrainedCrossAxis( SliverConstrainedCrossAxis(
maxExtent: Grid.mediumCardWidth * 2, maxExtent: Grid.smallCardWidth * 2,
sliver: SliverList( sliver: SliverList(
delegate: SliverChildBuilderDelegate( delegate: SliverChildBuilderDelegate(
(context, index) { (context, index) {

View File

@@ -177,7 +177,15 @@ InlineSpan? richNode(item, context) {
WidgetSpan( WidgetSpan(
alignment: PlaceholderAlignment.middle, alignment: PlaceholderAlignment.middle,
child: GestureDetector( child: GestureDetector(
onTap: () {}, onTap: () {
Get.toNamed(
'/webview',
parameters: {
'url':
'https://www.bilibili.com/h5/lottery/result?business_id=${item.idStr}'
},
);
},
child: Text( child: Text(
'${i.origText} ', '${i.origText} ',
style: authorStyle, style: authorStyle,

View File

@@ -296,7 +296,7 @@ class _HtmlRenderPageState extends State<HtmlRenderPage>
children: [ children: [
OrientationBuilder( OrientationBuilder(
builder: (context, orientation) { builder: (context, orientation) {
double padding = max(context.width / 2 - Grid.mediumCardWidth, 0); double padding = max(context.width / 2 - Grid.smallCardWidth, 0);
return Row( return Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [

View File

@@ -1,6 +1,7 @@
import 'dart:convert'; import 'dart:convert';
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/tcp/live.dart'; import 'package:PiliPlus/tcp/live.dart';
import 'package:PiliPlus/utils/danmaku.dart'; import 'package:PiliPlus/utils/danmaku.dart';
import 'package:PiliPlus/utils/storage.dart'; import 'package:PiliPlus/utils/storage.dart';
@@ -31,6 +32,10 @@ class LiveRoomController extends GetxController {
DanmakuController? controller; DanmakuController? controller;
bool showDanmaku = true; bool showDanmaku = true;
late int currentQn = 10000;
late List<Map> acceptQnList = <Map>[];
RxString currentQnDesc = ''.obs;
@override @override
void onInit() { void onInit() {
super.onInit(); super.onInit();
@@ -58,12 +63,26 @@ class LiveRoomController extends GetxController {
bool? isPortrait; bool? isPortrait;
Future queryLiveInfo() async { Future queryLiveInfo() async {
var res = await LiveHttp.liveRoomInfo(roomId: roomId, qn: 10000); var res = await LiveHttp.liveRoomInfo(roomId: roomId, qn: currentQn);
if (res['status']) { if (res['status']) {
isPortrait = res['data'].isPortrait; isPortrait = res['data'].isPortrait;
List<CodecItem> codec = List<CodecItem> codec =
res['data'].playurlInfo.playurl.stream.first.format.first.codec; res['data'].playurlInfo.playurl.stream.first.format.first.codec;
CodecItem item = codec.first; CodecItem item = codec.first;
// 以服务端返回的码率为准
currentQn = item.currentQn!;
List acceptQn = item.acceptQn!;
acceptQnList = acceptQn.map((e) {
return {
'code': e,
'desc': LiveQuality.values
.firstWhere((element) => element.code == e)
.description,
};
}).toList();
currentQnDesc.value = LiveQuality.values
.firstWhere((element) => element.code == currentQn)
.description;
String videoUrl = VideoUtils.getCdnUrl(item); String videoUrl = VideoUtils.getCdnUrl(item);
await playerInit(videoUrl); await playerInit(videoUrl);
return res; return res;
@@ -183,4 +202,16 @@ class LiveRoomController extends GetxController {
scrollController.dispose(); scrollController.dispose();
super.onClose(); super.onClose();
} }
// 修改画质
void changeQn(int qn) async {
if (currentQn == qn) {
return;
}
currentQn = qn;
currentQnDesc.value = LiveQuality.values
.firstWhere((element) => element.code == currentQn)
.description;
await queryLiveInfo();
}
} }

View File

@@ -5,14 +5,15 @@ import 'package:flutter/material.dart';
import 'package:PiliPlus/models/video/play/url.dart'; import 'package:PiliPlus/models/video/play/url.dart';
import 'package:PiliPlus/pages/live_room/index.dart'; import 'package:PiliPlus/pages/live_room/index.dart';
import 'package:PiliPlus/plugin/pl_player/index.dart'; import 'package:PiliPlus/plugin/pl_player/index.dart';
import 'package:get/get.dart';
class BottomControl extends StatefulWidget implements PreferredSizeWidget { class BottomControl extends StatefulWidget implements PreferredSizeWidget {
final PlPlayerController? controller; final PlPlayerController controller;
final LiveRoomController? liveRoomCtr; final LiveRoomController liveRoomCtr;
final Floating? floating; final Floating? floating;
const BottomControl({ const BottomControl({
this.controller, required this.controller,
this.liveRoomCtr, required this.liveRoomCtr,
this.floating, this.floating,
super.key, super.key,
}); });
@@ -87,7 +88,7 @@ class _BottomControlState extends State<BottomControl> {
), ),
onPressed: () async { onPressed: () async {
bool canUsePiP = false; bool canUsePiP = false;
widget.controller!.hiddenControls(false); widget.controller.hiddenControls(false);
try { try {
canUsePiP = await widget.floating!.isPipAvailable; canUsePiP = await widget.floating!.isPipAvailable;
} catch (_) {} } catch (_) {}
@@ -104,6 +105,31 @@ class _BottomControlState extends State<BottomControl> {
), ),
const SizedBox(width: 4), const SizedBox(width: 4),
], ],
Obx(
() => SizedBox(
width: 30,
child: PopupMenuButton<int>(
padding: EdgeInsets.zero,
initialValue: widget.liveRoomCtr.currentQn,
onSelected: (value) {
widget.liveRoomCtr.changeQn(value);
},
child: Text(
widget.liveRoomCtr.currentQnDesc.value,
style: const TextStyle(color: Colors.white, fontSize: 13),
),
itemBuilder: (BuildContext context) {
return widget.liveRoomCtr.acceptQnList.map((e) {
return PopupMenuItem<int>(
value: e['code'],
child: Text(e['desc']),
);
}).toList();
},
),
),
),
const SizedBox(width: 10),
ComBtn( ComBtn(
icon: const Icon( icon: const Icon(
Icons.fullscreen, Icons.fullscreen,
@@ -111,8 +137,8 @@ class _BottomControlState extends State<BottomControl> {
size: 20, size: 20,
color: Colors.white, color: Colors.white,
), ),
fuc: () => widget.controller!.triggerFullScreen( fuc: () => widget.controller.triggerFullScreen(
status: !widget.controller!.isFullScreen.value), status: !widget.controller.isFullScreen.value),
), ),
], ],
), ),

View File

@@ -117,7 +117,7 @@ class _MemberDynamicsPageState extends State<MemberDynamicsPage>
slivers: [ slivers: [
const SliverFillRemaining(), const SliverFillRemaining(),
SliverConstrainedCrossAxis( SliverConstrainedCrossAxis(
maxExtent: Grid.mediumCardWidth * 2, maxExtent: Grid.smallCardWidth * 2,
sliver: SliverList( sliver: SliverList(
delegate: SliverChildBuilderDelegate( delegate: SliverChildBuilderDelegate(
(context, index) { (context, index) {

View File

@@ -72,7 +72,7 @@ class SearchDynamic extends StatelessWidget {
slivers: [ slivers: [
const SliverFillRemaining(), const SliverFillRemaining(),
SliverConstrainedCrossAxis( SliverConstrainedCrossAxis(
maxExtent: Grid.mediumCardWidth * 2, maxExtent: Grid.smallCardWidth * 2,
sliver: SliverList( sliver: SliverList(
delegate: SliverChildBuilderDelegate( delegate: SliverChildBuilderDelegate(
(context, index) { (context, index) {

View File

@@ -50,14 +50,15 @@ class GStorage {
), ),
); );
static List<String> get tabbarSort => setting.get(SettingBoxKey.tabbarSort, static List<String> get tabbarSort =>
defaultValue: TabType.values.map((item) => item.name).toList()); List<String>.from(setting.get(SettingBoxKey.tabbarSort,
defaultValue: TabType.values.map((item) => item.name).toList()));
static List<Pair<SegmentType, SkipType>> get blockSettings { static List<Pair<SegmentType, SkipType>> get blockSettings {
List list = setting.get( List<int> list = List<int>.from(setting.get(
SettingBoxKey.blockSettings, SettingBoxKey.blockSettings,
defaultValue: List.generate(SegmentType.values.length, (_) => 1), defaultValue: List.generate(SegmentType.values.length, (_) => 1),
); ));
return SegmentType.values return SegmentType.values
.map((item) => Pair<SegmentType, SkipType>( .map((item) => Pair<SegmentType, SkipType>(
first: item, first: item,
@@ -67,10 +68,10 @@ class GStorage {
} }
static List<Color> get blockColor { static List<Color> get blockColor {
List list = setting.get( List<String> list = List<String>.from(setting.get(
SettingBoxKey.blockColor, SettingBoxKey.blockColor,
defaultValue: List.generate(SegmentType.values.length, (_) => ''), defaultValue: List.generate(SegmentType.values.length, (_) => ''),
); ));
return SegmentType.values return SegmentType.values
.map((item) => list[item.index].isNotEmpty .map((item) => list[item.index].isNotEmpty
? Color( ? Color(