mirror of
https://github.com/bggRGjQaUbCoE/PiliPlus.git
synced 2026-08-06 02:30:12 +08:00
@@ -1,40 +1,23 @@
|
|||||||
enum LiveQuality {
|
enum LiveQuality {
|
||||||
dolby,
|
dolby(30000, '杜比'),
|
||||||
super4K,
|
origin4K(25000, '4K 原画'),
|
||||||
origin,
|
super4K(20000, '4K'),
|
||||||
bluRay,
|
origin(10000, '原画'),
|
||||||
superHD,
|
bluRay(400, '蓝光'),
|
||||||
smooth,
|
superHD(250, '超清'),
|
||||||
flunt,
|
smooth(150, '高清'),
|
||||||
}
|
flunt(80, '流畅');
|
||||||
|
|
||||||
extension LiveQualityExt on LiveQuality {
|
final int code;
|
||||||
static const List<int> _codeList = [
|
final String desc;
|
||||||
30000,
|
const LiveQuality(this.code, this.desc);
|
||||||
20000,
|
|
||||||
10000,
|
|
||||||
400,
|
|
||||||
250,
|
|
||||||
150,
|
|
||||||
80,
|
|
||||||
];
|
|
||||||
int get code => _codeList[index];
|
|
||||||
|
|
||||||
static LiveQuality? fromCode(int code) {
|
static LiveQuality? fromCode(int? code) {
|
||||||
final index = _codeList.indexOf(code);
|
for (var e in LiveQuality.values) {
|
||||||
if (index != -1) {
|
if (e.code == code) {
|
||||||
return LiveQuality.values[index];
|
return e;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
String get description => const [
|
|
||||||
'杜比',
|
|
||||||
'4K',
|
|
||||||
'原画',
|
|
||||||
'蓝光',
|
|
||||||
'超清',
|
|
||||||
'高清',
|
|
||||||
'流畅',
|
|
||||||
][index];
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -97,12 +97,11 @@ class _AboutPageState extends State<AboutPage> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: ConstrainedBox(
|
child: ExcludeSemantics(
|
||||||
constraints: const BoxConstraints(maxHeight: 150),
|
child: Image.asset(
|
||||||
child: ExcludeSemantics(
|
width: 150,
|
||||||
child: Image.asset(
|
height: 150,
|
||||||
'assets/images/logo/logo.png',
|
'assets/images/logo/logo.png',
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -106,20 +106,21 @@ abstract class ReplyController<R> extends CommonListController<R, ReplyInfo> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
(bool inputDisable, String? hint) get replyHint {
|
(bool inputDisable, String? hint) get replyHint {
|
||||||
|
bool inputDisable = false;
|
||||||
|
String? hint;
|
||||||
try {
|
try {
|
||||||
if (subjectControl != null && subjectControl!.hasRootText()) {
|
if (subjectControl != null && subjectControl!.hasRootText()) {
|
||||||
if (subjectControl!.inputDisable) {
|
final rootText = subjectControl!.rootText;
|
||||||
SmartDialog.showToast(subjectControl!.rootText);
|
inputDisable = subjectControl!.inputDisable;
|
||||||
return (true, null);
|
if (inputDisable) {
|
||||||
|
SmartDialog.showToast(rootText);
|
||||||
}
|
}
|
||||||
if ((subjectControl!.hasSwitcherType() &&
|
if (rootText.contains('可发') || rootText.contains('可见')) {
|
||||||
subjectControl!.switcherType != 1) ||
|
hint = rootText;
|
||||||
subjectControl!.rootText.contains('可发评论')) {
|
|
||||||
return (false, subjectControl!.rootText);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
return (false, null);
|
return (inputDisable, hint);
|
||||||
}
|
}
|
||||||
|
|
||||||
void onReply(
|
void onReply(
|
||||||
|
|||||||
@@ -144,18 +144,11 @@ class LiveRoomController extends GetxController {
|
|||||||
acceptQnList = item.acceptQn!.map((e) {
|
acceptQnList = item.acceptQn!.map((e) {
|
||||||
return (
|
return (
|
||||||
code: e,
|
code: e,
|
||||||
desc:
|
desc: LiveQuality.fromCode(e)?.desc ?? e.toString(),
|
||||||
LiveQuality.values
|
|
||||||
.firstWhereOrNull((element) => element.code == e)
|
|
||||||
?.description ??
|
|
||||||
e.toString(),
|
|
||||||
);
|
);
|
||||||
}).toList();
|
}).toList();
|
||||||
currentQnDesc.value =
|
currentQnDesc.value =
|
||||||
LiveQuality.values
|
LiveQuality.fromCode(currentQn)?.desc ?? currentQn.toString();
|
||||||
.firstWhereOrNull((element) => element.code == currentQn)
|
|
||||||
?.description ??
|
|
||||||
currentQn.toString();
|
|
||||||
videoUrl = VideoUtils.getCdnUrl(item);
|
videoUrl = VideoUtils.getCdnUrl(item);
|
||||||
await playerInit();
|
await playerInit();
|
||||||
isLoaded.value = true;
|
isLoaded.value = true;
|
||||||
@@ -286,10 +279,7 @@ class LiveRoomController extends GetxController {
|
|||||||
}
|
}
|
||||||
currentQn = qn;
|
currentQn = qn;
|
||||||
currentQnDesc.value =
|
currentQnDesc.value =
|
||||||
LiveQuality.values
|
LiveQuality.fromCode(currentQn)?.desc ?? currentQn.toString();
|
||||||
.firstWhereOrNull((element) => element.code == currentQn)
|
|
||||||
?.description ??
|
|
||||||
currentQn.toString();
|
|
||||||
return queryLiveUrl();
|
return queryLiveUrl();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import 'package:PiliPlus/plugin/pl_player/controller.dart';
|
|||||||
import 'package:PiliPlus/plugin/pl_player/models/play_status.dart';
|
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/plugin/pl_player/widgets/common_btn.dart';
|
||||||
import 'package:PiliPlus/services/service_locator.dart';
|
import 'package:PiliPlus/services/service_locator.dart';
|
||||||
import 'package:PiliPlus/utils/duration_util.dart';
|
import 'package:PiliPlus/utils/duration_util.dart';
|
||||||
import 'package:PiliPlus/utils/extension.dart';
|
import 'package:PiliPlus/utils/extension.dart';
|
||||||
@@ -46,8 +47,6 @@ class _LiveRoomPageState extends State<LiveRoomPage>
|
|||||||
final GlobalKey chatKey = GlobalKey();
|
final GlobalKey chatKey = GlobalKey();
|
||||||
final GlobalKey playerKey = GlobalKey();
|
final GlobalKey playerKey = GlobalKey();
|
||||||
|
|
||||||
final Color _color = const Color(0xFFEEEEEE);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
@@ -630,13 +629,14 @@ class _LiveRoomPageState extends State<LiveRoomPage>
|
|||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.only(top: 5, bottom: 10),
|
padding: const EdgeInsets.only(top: 5, bottom: 10),
|
||||||
child: Row(
|
child: Row(
|
||||||
|
spacing: 6,
|
||||||
children: [
|
children: [
|
||||||
Obx(
|
Obx(
|
||||||
() {
|
() {
|
||||||
final enableShowDanmaku =
|
final enableShowDanmaku =
|
||||||
plPlayerController.enableShowDanmaku.value;
|
plPlayerController.enableShowDanmaku.value;
|
||||||
return IconButton(
|
return ComBtn(
|
||||||
onPressed: () {
|
onTap: () {
|
||||||
final newVal = !enableShowDanmaku;
|
final newVal = !enableShowDanmaku;
|
||||||
plPlayerController.enableShowDanmaku.value = newVal;
|
plPlayerController.enableShowDanmaku.value = newVal;
|
||||||
if (!plPlayerController.tempPlayerConf) {
|
if (!plPlayerController.tempPlayerConf) {
|
||||||
@@ -646,19 +646,24 @@ class _LiveRoomPageState extends State<LiveRoomPage>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
icon: Icon(
|
icon: enableShowDanmaku
|
||||||
enableShowDanmaku
|
? const Icon(
|
||||||
? Icons.subtitles_outlined
|
size: 22,
|
||||||
: Icons.subtitles_off_outlined,
|
Icons.subtitles_outlined,
|
||||||
color: _color,
|
color: Color(0xFFEEEEEE),
|
||||||
),
|
)
|
||||||
|
: const Icon(
|
||||||
|
size: 22,
|
||||||
|
Icons.subtitles_off_outlined,
|
||||||
|
color: Color(0xFFEEEEEE),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
Expanded(
|
const Expanded(
|
||||||
child: Text(
|
child: Text(
|
||||||
'发送弹幕',
|
'发送弹幕',
|
||||||
style: TextStyle(color: _color),
|
style: TextStyle(color: Color(0xFFEEEEEE)),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Builder(
|
Builder(
|
||||||
@@ -675,9 +680,13 @@ class _LiveRoomPageState extends State<LiveRoomPage>
|
|||||||
onTapDown: _liveRoomController.onLikeTapDown,
|
onTapDown: _liveRoomController.onLikeTapDown,
|
||||||
onTapUp: _liveRoomController.onLikeTapUp,
|
onTapUp: _liveRoomController.onLikeTapUp,
|
||||||
onTapCancel: _liveRoomController.onLikeTapUp,
|
onTapCancel: _liveRoomController.onLikeTapUp,
|
||||||
child: Padding(
|
child: const SizedBox.square(
|
||||||
padding: const EdgeInsets.all(8.0),
|
dimension: 34,
|
||||||
child: Icon(Icons.thumb_up_off_alt, color: _color),
|
child: Icon(
|
||||||
|
size: 22,
|
||||||
|
color: Color(0xFFEEEEEE),
|
||||||
|
Icons.thumb_up_off_alt,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Positioned(
|
Positioned(
|
||||||
@@ -715,9 +724,13 @@ class _LiveRoomPageState extends State<LiveRoomPage>
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
IconButton(
|
ComBtn(
|
||||||
onPressed: () => onSendDanmaku(true),
|
onTap: () => onSendDanmaku(true),
|
||||||
icon: Icon(Icons.emoji_emotions_outlined, color: _color),
|
icon: const Icon(
|
||||||
|
size: 22,
|
||||||
|
color: Color(0xFFEEEEEE),
|
||||||
|
Icons.emoji_emotions_outlined,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ class BottomControl extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
PlayOrPauseButton(plPlayerController: plPlayerController),
|
PlayOrPauseButton(plPlayerController: plPlayerController),
|
||||||
ComBtn(
|
ComBtn(
|
||||||
|
height: 30,
|
||||||
icon: const Icon(
|
icon: const Icon(
|
||||||
Icons.refresh,
|
Icons.refresh,
|
||||||
size: 18,
|
size: 18,
|
||||||
@@ -45,92 +46,81 @@ class BottomControl extends StatelessWidget {
|
|||||||
onTap: onRefresh,
|
onTap: onRefresh,
|
||||||
),
|
),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
SizedBox(
|
ComBtn(
|
||||||
width: 35,
|
height: 30,
|
||||||
height: 35,
|
icon: const Icon(
|
||||||
child: IconButton(
|
size: 18,
|
||||||
tooltip: '弹幕屏蔽',
|
Icons.block,
|
||||||
style: ButtonStyle(
|
color: Colors.white,
|
||||||
padding: WidgetStateProperty.all(EdgeInsets.zero),
|
|
||||||
),
|
|
||||||
onPressed: () {
|
|
||||||
if (liveRoomCtr.isLogin) {
|
|
||||||
Get.toNamed(
|
|
||||||
'/liveDmBlockPage',
|
|
||||||
parameters: {
|
|
||||||
'roomId': liveRoomCtr.roomId.toString(),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
SmartDialog.showToast('账号未登录');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
icon: const Icon(
|
|
||||||
size: 18,
|
|
||||||
Icons.block,
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
|
onTap: () {
|
||||||
|
if (liveRoomCtr.isLogin) {
|
||||||
|
Get.toNamed(
|
||||||
|
'/liveDmBlockPage',
|
||||||
|
parameters: {
|
||||||
|
'roomId': liveRoomCtr.roomId.toString(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
SmartDialog.showToast('账号未登录');
|
||||||
|
}
|
||||||
|
},
|
||||||
),
|
),
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 3),
|
||||||
Obx(
|
Obx(
|
||||||
() {
|
() {
|
||||||
final enableShowDanmaku =
|
final enableShowDanmaku =
|
||||||
plPlayerController.enableShowDanmaku.value;
|
plPlayerController.enableShowDanmaku.value;
|
||||||
return SizedBox(
|
return ComBtn(
|
||||||
width: 35,
|
icon: enableShowDanmaku
|
||||||
height: 35,
|
? const Icon(
|
||||||
child: IconButton(
|
size: 18,
|
||||||
tooltip: '弹幕开关',
|
Icons.subtitles_outlined,
|
||||||
style: ButtonStyle(
|
color: Colors.white,
|
||||||
padding: WidgetStateProperty.all(EdgeInsets.zero),
|
)
|
||||||
),
|
: const Icon(
|
||||||
onPressed: () {
|
size: 18,
|
||||||
final newVal = !enableShowDanmaku;
|
Icons.subtitles_off_outlined,
|
||||||
plPlayerController.enableShowDanmaku.value = newVal;
|
color: Colors.white,
|
||||||
if (!plPlayerController.tempPlayerConf) {
|
),
|
||||||
GStorage.setting.put(
|
onTap: () {
|
||||||
SettingBoxKey.enableShowDanmaku,
|
final newVal = !enableShowDanmaku;
|
||||||
newVal,
|
plPlayerController.enableShowDanmaku.value = newVal;
|
||||||
);
|
if (!plPlayerController.tempPlayerConf) {
|
||||||
}
|
GStorage.setting.put(
|
||||||
},
|
SettingBoxKey.enableShowDanmaku,
|
||||||
icon: Icon(
|
newVal,
|
||||||
size: 18,
|
);
|
||||||
enableShowDanmaku
|
}
|
||||||
? Icons.subtitles_outlined
|
},
|
||||||
: Icons.subtitles_off_outlined,
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
Obx(
|
Obx(
|
||||||
() => Container(
|
() => PopupMenuButton<BoxFit>(
|
||||||
height: 30,
|
initialValue: plPlayerController.videoFit.value,
|
||||||
margin: const EdgeInsets.symmetric(horizontal: 10),
|
color: Colors.black.withValues(alpha: 0.8),
|
||||||
alignment: Alignment.center,
|
itemBuilder: (context) {
|
||||||
child: PopupMenuButton<BoxFit>(
|
return BoxFit.values
|
||||||
initialValue: plPlayerController.videoFit.value,
|
.map(
|
||||||
color: Colors.black.withValues(alpha: 0.8),
|
(BoxFit boxFit) => PopupMenuItem<BoxFit>(
|
||||||
itemBuilder: (BuildContext context) {
|
height: 35,
|
||||||
return BoxFit.values.map((BoxFit boxFit) {
|
padding: const EdgeInsets.only(left: 30),
|
||||||
return PopupMenuItem<BoxFit>(
|
value: boxFit,
|
||||||
height: 35,
|
onTap: () => plPlayerController.toggleVideoFit(boxFit),
|
||||||
padding: const EdgeInsets.only(left: 30),
|
child: Text(
|
||||||
value: boxFit,
|
boxFit.desc,
|
||||||
onTap: () => plPlayerController.toggleVideoFit(boxFit),
|
style: const TextStyle(
|
||||||
child: Text(
|
color: Colors.white,
|
||||||
boxFit.desc,
|
fontSize: 13,
|
||||||
style: const TextStyle(
|
),
|
||||||
color: Colors.white,
|
|
||||||
fontSize: 13,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
)
|
||||||
}).toList();
|
.toList();
|
||||||
},
|
},
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 10),
|
||||||
child: Text(
|
child: Text(
|
||||||
plPlayerController.videoFit.value.desc,
|
plPlayerController.videoFit.value.desc,
|
||||||
style: const TextStyle(color: Colors.white, fontSize: 13),
|
style: const TextStyle(color: Colors.white, fontSize: 13),
|
||||||
@@ -138,46 +128,54 @@ class BottomControl extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 10),
|
|
||||||
Obx(
|
Obx(
|
||||||
() => SizedBox(
|
() => PopupMenuButton<int>(
|
||||||
width: 30,
|
padding: EdgeInsets.zero,
|
||||||
child: PopupMenuButton<int>(
|
initialValue: liveRoomCtr.currentQn,
|
||||||
padding: EdgeInsets.zero,
|
color: Colors.black.withValues(alpha: 0.8),
|
||||||
initialValue: liveRoomCtr.currentQn,
|
itemBuilder: (context) {
|
||||||
color: Colors.black.withValues(alpha: 0.8),
|
return liveRoomCtr.acceptQnList
|
||||||
|
.map(
|
||||||
|
(e) => PopupMenuItem<int>(
|
||||||
|
height: 35,
|
||||||
|
padding: const EdgeInsets.only(left: 30),
|
||||||
|
value: e.code,
|
||||||
|
onTap: () => liveRoomCtr.changeQn(e.code),
|
||||||
|
child: Text(
|
||||||
|
e.desc,
|
||||||
|
style: const TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: 13,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList();
|
||||||
|
},
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 10),
|
||||||
child: Text(
|
child: Text(
|
||||||
liveRoomCtr.currentQnDesc.value,
|
liveRoomCtr.currentQnDesc.value,
|
||||||
style: const TextStyle(color: Colors.white, fontSize: 13),
|
style: const TextStyle(color: Colors.white, fontSize: 13),
|
||||||
),
|
),
|
||||||
itemBuilder: (BuildContext context) {
|
|
||||||
return liveRoomCtr.acceptQnList.map((e) {
|
|
||||||
return PopupMenuItem<int>(
|
|
||||||
height: 35,
|
|
||||||
padding: const EdgeInsets.only(left: 30),
|
|
||||||
value: e.code,
|
|
||||||
onTap: () => liveRoomCtr.changeQn(e.code),
|
|
||||||
child: Text(
|
|
||||||
e.desc,
|
|
||||||
style: const TextStyle(
|
|
||||||
color: Colors.white,
|
|
||||||
fontSize: 13,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}).toList();
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 10),
|
|
||||||
ComBtn(
|
ComBtn(
|
||||||
icon: const Icon(
|
height: 30,
|
||||||
Icons.fullscreen,
|
icon: plPlayerController.isFullScreen.value
|
||||||
semanticLabel: '全屏切换',
|
? const Icon(
|
||||||
size: 20,
|
Icons.fullscreen_exit,
|
||||||
color: Colors.white,
|
semanticLabel: '退出全屏',
|
||||||
),
|
size: 24,
|
||||||
|
color: Colors.white,
|
||||||
|
)
|
||||||
|
: const Icon(
|
||||||
|
Icons.fullscreen,
|
||||||
|
semanticLabel: '全屏',
|
||||||
|
size: 24,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
onTap: () => plPlayerController.triggerFullScreen(
|
onTap: () => plPlayerController.triggerFullScreen(
|
||||||
status: !plPlayerController.isFullScreen.value,
|
status: !plPlayerController.isFullScreen.value,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
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/utils/page_utils.dart';
|
import 'package:PiliPlus/utils/page_utils.dart';
|
||||||
import 'package:floating/floating.dart';
|
import 'package:floating/floating.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@@ -27,6 +28,39 @@ class LiveHeaderControl extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final isFullScreen = plPlayerController.isFullScreen.value;
|
final isFullScreen = plPlayerController.isFullScreen.value;
|
||||||
|
Widget child;
|
||||||
|
if (title != null) {
|
||||||
|
child = Text(
|
||||||
|
title!,
|
||||||
|
maxLines: 1,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 15,
|
||||||
|
height: 1,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
if (isFullScreen && upName != null) {
|
||||||
|
child = Column(
|
||||||
|
spacing: 5,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
child,
|
||||||
|
Text(
|
||||||
|
upName!,
|
||||||
|
maxLines: 1,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
height: 1,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
child = Expanded(child: child);
|
||||||
|
} else {
|
||||||
|
child = const Spacer();
|
||||||
|
}
|
||||||
return AppBar(
|
return AppBar(
|
||||||
backgroundColor: Colors.transparent,
|
backgroundColor: Colors.transparent,
|
||||||
foregroundColor: Colors.white,
|
foregroundColor: Colors.white,
|
||||||
@@ -37,147 +71,75 @@ class LiveHeaderControl extends StatelessWidget {
|
|||||||
spacing: 10,
|
spacing: 10,
|
||||||
children: [
|
children: [
|
||||||
if (isFullScreen)
|
if (isFullScreen)
|
||||||
SizedBox(
|
ComBtn(
|
||||||
width: 35,
|
icon: const Icon(FontAwesomeIcons.arrowLeft, size: 15),
|
||||||
height: 35,
|
onTap: () => plPlayerController.triggerFullScreen(status: false),
|
||||||
child: IconButton(
|
|
||||||
tooltip: '返回',
|
|
||||||
icon: const Icon(
|
|
||||||
FontAwesomeIcons.arrowLeft,
|
|
||||||
size: 15,
|
|
||||||
),
|
|
||||||
style: ButtonStyle(
|
|
||||||
padding: WidgetStateProperty.all(EdgeInsets.zero),
|
|
||||||
),
|
|
||||||
onPressed: () =>
|
|
||||||
plPlayerController.triggerFullScreen(status: false),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
if (title != null)
|
child,
|
||||||
Expanded(
|
ComBtn(
|
||||||
child: Column(
|
icon: const Icon(
|
||||||
spacing: 5,
|
size: 18,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
Icons.comment_outlined,
|
||||||
children: [
|
color: Colors.white,
|
||||||
Text(
|
|
||||||
title!,
|
|
||||||
maxLines: 1,
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 15,
|
|
||||||
height: 1,
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (isFullScreen && upName != null)
|
|
||||||
Text(
|
|
||||||
upName!,
|
|
||||||
maxLines: 1,
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
height: 1,
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
)
|
|
||||||
else
|
|
||||||
const Spacer(),
|
|
||||||
SizedBox(
|
|
||||||
width: 35,
|
|
||||||
height: 35,
|
|
||||||
child: IconButton(
|
|
||||||
tooltip: '发弹幕',
|
|
||||||
style: ButtonStyle(
|
|
||||||
padding: WidgetStateProperty.all(EdgeInsets.zero),
|
|
||||||
),
|
|
||||||
onPressed: onSendDanmaku,
|
|
||||||
icon: const Icon(
|
|
||||||
Icons.comment_outlined,
|
|
||||||
size: 18,
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
|
onTap: onSendDanmaku,
|
||||||
),
|
),
|
||||||
Obx(
|
Obx(
|
||||||
() {
|
() {
|
||||||
final onlyPlayAudio = plPlayerController.onlyPlayAudio.value;
|
final onlyPlayAudio = plPlayerController.onlyPlayAudio.value;
|
||||||
return SizedBox(
|
return ComBtn(
|
||||||
width: 35,
|
onTap: () {
|
||||||
height: 35,
|
plPlayerController.onlyPlayAudio.value = !onlyPlayAudio;
|
||||||
child: IconButton(
|
onPlayAudio();
|
||||||
onPressed: () {
|
},
|
||||||
plPlayerController.onlyPlayAudio.value = !onlyPlayAudio;
|
icon: onlyPlayAudio
|
||||||
onPlayAudio();
|
? const Icon(
|
||||||
},
|
size: 18,
|
||||||
style: ButtonStyle(
|
MdiIcons.musicCircle,
|
||||||
padding: WidgetStateProperty.all(EdgeInsets.zero),
|
color: Colors.white,
|
||||||
),
|
)
|
||||||
icon: onlyPlayAudio
|
: const Icon(
|
||||||
? const Icon(
|
size: 18,
|
||||||
size: 18,
|
MdiIcons.musicCircleOutline,
|
||||||
MdiIcons.musicCircle,
|
color: Colors.white,
|
||||||
color: Colors.white,
|
),
|
||||||
)
|
|
||||||
: const Icon(
|
|
||||||
size: 18,
|
|
||||||
MdiIcons.musicCircleOutline,
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
if (Platform.isAndroid)
|
if (Platform.isAndroid)
|
||||||
SizedBox(
|
ComBtn(
|
||||||
width: 35,
|
onTap: () async {
|
||||||
height: 35,
|
try {
|
||||||
child: IconButton(
|
var floating = Floating();
|
||||||
tooltip: '画中画',
|
if ((await floating.isPipAvailable) == true) {
|
||||||
style: ButtonStyle(
|
plPlayerController.hiddenControls(false);
|
||||||
padding: WidgetStateProperty.all(EdgeInsets.zero),
|
floating.enable(
|
||||||
),
|
plPlayerController.isVertical
|
||||||
onPressed: () async {
|
? const EnableManual(
|
||||||
try {
|
aspectRatio: Rational.vertical(),
|
||||||
var floating = Floating();
|
)
|
||||||
if ((await floating.isPipAvailable) == true) {
|
: const EnableManual(),
|
||||||
plPlayerController.hiddenControls(false);
|
);
|
||||||
floating.enable(
|
}
|
||||||
plPlayerController.isVertical
|
} catch (_) {}
|
||||||
? const EnableManual(
|
},
|
||||||
aspectRatio: Rational.vertical(),
|
|
||||||
)
|
|
||||||
: const EnableManual(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} catch (_) {}
|
|
||||||
},
|
|
||||||
icon: const Icon(
|
|
||||||
Icons.picture_in_picture_outlined,
|
|
||||||
size: 18,
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
SizedBox(
|
|
||||||
width: 35,
|
|
||||||
height: 35,
|
|
||||||
child: IconButton(
|
|
||||||
tooltip: '定时关闭',
|
|
||||||
style: ButtonStyle(
|
|
||||||
padding: WidgetStateProperty.all(EdgeInsets.zero),
|
|
||||||
),
|
|
||||||
onPressed: () => PageUtils.scheduleExit(
|
|
||||||
context,
|
|
||||||
plPlayerController.isFullScreen.value,
|
|
||||||
true,
|
|
||||||
),
|
|
||||||
icon: const Icon(
|
icon: const Icon(
|
||||||
size: 18,
|
size: 18,
|
||||||
Icons.schedule,
|
Icons.picture_in_picture_outlined,
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
ComBtn(
|
||||||
|
onTap: () => PageUtils.scheduleExit(
|
||||||
|
context,
|
||||||
|
plPlayerController.isFullScreen.value,
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
icon: const Icon(
|
||||||
|
size: 18,
|
||||||
|
Icons.schedule,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ class MemberController extends CommonDataController<SpaceData, SpaceData?>
|
|||||||
MemberController({required this.mid});
|
MemberController({required this.mid});
|
||||||
int mid;
|
int mid;
|
||||||
String? username;
|
String? username;
|
||||||
RxBool showUname = false.obs;
|
|
||||||
|
|
||||||
AccountService accountService = Get.find<AccountService>();
|
AccountService accountService = Get.find<AccountService>();
|
||||||
|
|
||||||
@@ -131,7 +130,6 @@ class MemberController extends CommonDataController<SpaceData, SpaceData?>
|
|||||||
vsync: this,
|
vsync: this,
|
||||||
length: tabs.length,
|
length: tabs.length,
|
||||||
);
|
);
|
||||||
showUname.value = true;
|
|
||||||
username = errMsg;
|
username = errMsg;
|
||||||
loadingState.value = const Success(null);
|
loadingState.value = const Success(null);
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ class UserInfoCard extends StatelessWidget {
|
|||||||
|
|
||||||
List<Widget> _buildLeft(BuildContext context, ThemeData theme) => [
|
List<Widget> _buildLeft(BuildContext context, ThemeData theme) => [
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(left: 20, right: 20, top: 5),
|
padding: const EdgeInsets.only(left: 20, right: 20),
|
||||||
child: Wrap(
|
child: Wrap(
|
||||||
spacing: 8,
|
spacing: 8,
|
||||||
runSpacing: 8,
|
runSpacing: 8,
|
||||||
@@ -461,6 +461,7 @@ class UserInfoCard extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
const SizedBox(height: 5),
|
||||||
..._buildLeft(context, theme),
|
..._buildLeft(context, theme),
|
||||||
if (card.prInfo?.content?.isNotEmpty == true) buildPrInfo(theme),
|
if (card.prInfo?.content?.isNotEmpty == true) buildPrInfo(theme),
|
||||||
const SizedBox(height: 5),
|
const SizedBox(height: 5),
|
||||||
|
|||||||
@@ -61,27 +61,28 @@ class _MemberVideoState extends State<MemberVideo>
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
super.build(context);
|
super.build(context);
|
||||||
final theme = Theme.of(context);
|
final theme = Theme.of(context);
|
||||||
return Stack(
|
Widget child = refreshIndicator(
|
||||||
clipBehavior: Clip.none,
|
onRefresh: _controller.onRefresh,
|
||||||
children: [
|
child: CustomScrollView(
|
||||||
refreshIndicator(
|
physics: ReloadScrollPhysics(controller: _controller),
|
||||||
onRefresh: _controller.onRefresh,
|
slivers: [
|
||||||
child: CustomScrollView(
|
SliverPadding(
|
||||||
physics: ReloadScrollPhysics(controller: _controller),
|
padding: EdgeInsets.only(
|
||||||
slivers: [
|
bottom: MediaQuery.paddingOf(context).bottom + 80,
|
||||||
SliverPadding(
|
),
|
||||||
padding: EdgeInsets.only(
|
sliver: Obx(
|
||||||
bottom: MediaQuery.paddingOf(context).bottom + 80,
|
() => _buildBody(theme, _controller.loadingState.value),
|
||||||
),
|
),
|
||||||
sliver: Obx(
|
|
||||||
() => _buildBody(theme, _controller.loadingState.value),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
if (widget.type == ContributeType.video &&
|
),
|
||||||
_controller.fromViewAid?.isNotEmpty == true)
|
);
|
||||||
|
if (widget.type == ContributeType.video &&
|
||||||
|
_controller.fromViewAid?.isNotEmpty == true) {
|
||||||
|
return Stack(
|
||||||
|
clipBehavior: Clip.none,
|
||||||
|
children: [
|
||||||
|
child,
|
||||||
Obx(
|
Obx(
|
||||||
() => _controller.isLocating.value != true
|
() => _controller.isLocating.value != true
|
||||||
? Positioned(
|
? Positioned(
|
||||||
@@ -103,6 +104,7 @@ class _MemberVideoState extends State<MemberVideo>
|
|||||||
?.indexWhere((i) => i.param == fromViewAid);
|
?.indexWhere((i) => i.param == fromViewAid);
|
||||||
if (locatedIndex == null || locatedIndex == -1) {
|
if (locatedIndex == null || locatedIndex == -1) {
|
||||||
_controller
|
_controller
|
||||||
|
..reload = true
|
||||||
..page = 0
|
..page = 0
|
||||||
..loadingState.value = LoadingState.loading()
|
..loadingState.value = LoadingState.loading()
|
||||||
..queryData();
|
..queryData();
|
||||||
@@ -120,8 +122,10 @@ class _MemberVideoState extends State<MemberVideo>
|
|||||||
)
|
)
|
||||||
: const SizedBox.shrink(),
|
: const SizedBox.shrink(),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
return child;
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildBody(
|
Widget _buildBody(
|
||||||
|
|||||||
@@ -527,14 +527,14 @@ List<SettingsModel> get extraSettings => [
|
|||||||
title: '超分辨率',
|
title: '超分辨率',
|
||||||
leading: const Icon(Icons.stay_current_landscape_outlined),
|
leading: const Icon(Icons.stay_current_landscape_outlined),
|
||||||
getSubtitle: () =>
|
getSubtitle: () =>
|
||||||
'当前:「${SuperResolutionType.values[Pref.superResolutionType].title}」\n默认设置对番剧生效, 其他视频默认关闭\n超分辨率需要启用硬件解码, 若启用硬件解码后仍然不生效, 尝试切换硬件解码器为 auto-copy',
|
'当前:「${Pref.superResolutionType.title}」\n默认设置对番剧生效, 其他视频默认关闭\n超分辨率需要启用硬件解码, 若启用硬件解码后仍然不生效, 尝试切换硬件解码器为 auto-copy',
|
||||||
onTap: (setState) async {
|
onTap: (setState) async {
|
||||||
SuperResolutionType? result = await showDialog(
|
SuperResolutionType? result = await showDialog(
|
||||||
context: Get.context!,
|
context: Get.context!,
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return SelectDialog<SuperResolutionType>(
|
return SelectDialog<SuperResolutionType>(
|
||||||
title: '超分辨率',
|
title: '超分辨率',
|
||||||
value: SuperResolutionType.values[Pref.superResolutionType],
|
value: Pref.superResolutionType,
|
||||||
values: SuperResolutionType.values
|
values: SuperResolutionType.values
|
||||||
.map((e) => (e, e.title))
|
.map((e) => (e, e.title))
|
||||||
.toList(),
|
.toList(),
|
||||||
|
|||||||
@@ -204,8 +204,7 @@ List<SettingsModel> get videoSettings => [
|
|||||||
settingsType: SettingsType.normal,
|
settingsType: SettingsType.normal,
|
||||||
title: '直播默认画质',
|
title: '直播默认画质',
|
||||||
leading: const Icon(Icons.video_settings_outlined),
|
leading: const Icon(Icons.video_settings_outlined),
|
||||||
getSubtitle: () =>
|
getSubtitle: () => '当前画质:${LiveQuality.fromCode(Pref.liveQuality)?.desc}',
|
||||||
'当前画质:${LiveQualityExt.fromCode(Pref.liveQuality)!.description}',
|
|
||||||
onTap: (setState) async {
|
onTap: (setState) async {
|
||||||
int? result = await showDialog(
|
int? result = await showDialog(
|
||||||
context: Get.context!,
|
context: Get.context!,
|
||||||
@@ -213,9 +212,7 @@ List<SettingsModel> get videoSettings => [
|
|||||||
return SelectDialog<int>(
|
return SelectDialog<int>(
|
||||||
title: '直播默认画质',
|
title: '直播默认画质',
|
||||||
value: Pref.liveQuality,
|
value: Pref.liveQuality,
|
||||||
values: LiveQuality.values
|
values: LiveQuality.values.map((e) => (e.code, e.desc)).toList(),
|
||||||
.map((e) => (e.code, e.description))
|
|
||||||
.toList(),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -230,17 +227,15 @@ List<SettingsModel> get videoSettings => [
|
|||||||
title: '蜂窝网络直播默认画质',
|
title: '蜂窝网络直播默认画质',
|
||||||
leading: const Icon(Icons.video_settings_outlined),
|
leading: const Icon(Icons.video_settings_outlined),
|
||||||
getSubtitle: () =>
|
getSubtitle: () =>
|
||||||
'当前画质:${LiveQualityExt.fromCode(Pref.liveQualityCellular)!.description}',
|
'当前画质:${LiveQuality.fromCode(Pref.liveQualityCellular)?.desc}',
|
||||||
onTap: (setState) async {
|
onTap: (setState) async {
|
||||||
int? result = await showDialog(
|
int? result = await showDialog(
|
||||||
context: Get.context!,
|
context: Get.context!,
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return SelectDialog<int>(
|
return SelectDialog<int>(
|
||||||
title: '直播默认画质',
|
title: '蜂窝网络直播默认画质',
|
||||||
value: Pref.liveQualityCellular,
|
value: Pref.liveQualityCellular,
|
||||||
values: LiveQuality.values
|
values: LiveQuality.values.map((e) => (e.code, e.desc)).toList(),
|
||||||
.map((e) => (e.code, e.description))
|
|
||||||
.toList(),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -201,18 +201,18 @@ class HeaderControlState extends TripleState<HeaderControl> {
|
|||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
Builder(
|
Builder(
|
||||||
builder: (context) => PopupMenuButton(
|
builder: (context) => PopupMenuButton(
|
||||||
initialValue: SuperResolutionType
|
initialValue:
|
||||||
.values[widget.controller.superResolutionType],
|
widget.controller.superResolutionType.value,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(4),
|
padding: const EdgeInsets.all(4),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
SuperResolutionType
|
widget
|
||||||
.values[widget
|
.controller
|
||||||
.controller
|
.superResolutionType
|
||||||
.superResolutionType]
|
.value
|
||||||
.title,
|
.title,
|
||||||
strutStyle: const StrutStyle(
|
strutStyle: const StrutStyle(
|
||||||
leading: 0,
|
leading: 0,
|
||||||
@@ -235,7 +235,7 @@ class HeaderControlState extends TripleState<HeaderControl> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
onSelected: (value) {
|
onSelected: (value) {
|
||||||
widget.controller.setShader(value.index);
|
widget.controller.setShader(value);
|
||||||
if (context.mounted) {
|
if (context.mounted) {
|
||||||
(context as Element).markNeedsBuild();
|
(context as Element).markNeedsBuild();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -121,15 +121,15 @@ class _WebDavSettingPageState extends State<WebDavSettingPage> {
|
|||||||
floatingActionButton: FloatingActionButton(
|
floatingActionButton: FloatingActionButton(
|
||||||
child: const Icon(Icons.save),
|
child: const Icon(Icons.save),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
|
await GStorage.setting.putAll({
|
||||||
|
SettingBoxKey.webdavUri: _uriCtr.text,
|
||||||
|
SettingBoxKey.webdavUsername: _usernameCtr.text,
|
||||||
|
SettingBoxKey.webdavPassword: _passwordCtr.text,
|
||||||
|
SettingBoxKey.webdavDirectory: _directoryCtr.text,
|
||||||
|
});
|
||||||
if (_uriCtr.text.isEmpty) {
|
if (_uriCtr.text.isEmpty) {
|
||||||
SmartDialog.showToast('地址不能为空');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final setting = GStorage.setting;
|
|
||||||
await setting.put(SettingBoxKey.webdavUri, _uriCtr.text);
|
|
||||||
await setting.put(SettingBoxKey.webdavUsername, _usernameCtr.text);
|
|
||||||
await setting.put(SettingBoxKey.webdavPassword, _passwordCtr.text);
|
|
||||||
await setting.put(SettingBoxKey.webdavDirectory, _directoryCtr.text);
|
|
||||||
try {
|
try {
|
||||||
final res = await WebDav().init();
|
final res = await WebDav().init();
|
||||||
if (res.first) {
|
if (res.first) {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import 'package:PiliPlus/http/video.dart';
|
|||||||
import 'package:PiliPlus/models/common/account_type.dart';
|
import 'package:PiliPlus/models/common/account_type.dart';
|
||||||
import 'package:PiliPlus/models/common/audio_normalization.dart';
|
import 'package:PiliPlus/models/common/audio_normalization.dart';
|
||||||
import 'package:PiliPlus/models/common/sponsor_block/skip_type.dart';
|
import 'package:PiliPlus/models/common/sponsor_block/skip_type.dart';
|
||||||
|
import 'package:PiliPlus/models/common/super_resolution_type.dart';
|
||||||
import 'package:PiliPlus/models/common/video/video_type.dart';
|
import 'package:PiliPlus/models/common/video/video_type.dart';
|
||||||
import 'package:PiliPlus/models/user/danmaku_rule.dart';
|
import 'package:PiliPlus/models/user/danmaku_rule.dart';
|
||||||
import 'package:PiliPlus/models_new/video/video_shot/data.dart';
|
import 'package:PiliPlus/models_new/video/video_shot/data.dart';
|
||||||
@@ -646,41 +647,43 @@ class PlPlayerController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
late final isAnim = _pgcType == 1 || _pgcType == 4;
|
late final isAnim = _pgcType == 1 || _pgcType == 4;
|
||||||
late int superResolutionType = isAnim ? Pref.superResolutionType : 0;
|
late final Rx<SuperResolutionType> superResolutionType =
|
||||||
Future<void> setShader([int? type, NativePlayer? pp]) async {
|
(isAnim ? Pref.superResolutionType : SuperResolutionType.disable).obs;
|
||||||
|
Future<void> setShader([SuperResolutionType? type, NativePlayer? pp]) async {
|
||||||
if (type == null) {
|
if (type == null) {
|
||||||
type ??= superResolutionType;
|
type = superResolutionType.value;
|
||||||
} else {
|
} else {
|
||||||
superResolutionType = type;
|
superResolutionType.value = type;
|
||||||
if (isAnim && !tempPlayerConf) {
|
if (isAnim && !tempPlayerConf) {
|
||||||
GStorage.setting.put(SettingBoxKey.superResolutionType, type);
|
setting.put(SettingBoxKey.superResolutionType, type.index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pp ??= _videoPlayerController?.platform as NativePlayer;
|
pp ??= _videoPlayerController?.platform as NativePlayer;
|
||||||
await pp.waitForPlayerInitialization;
|
await pp.waitForPlayerInitialization;
|
||||||
await pp.waitForVideoControllerInitializationIfAttached;
|
await pp.waitForVideoControllerInitializationIfAttached;
|
||||||
if (type == 1) {
|
switch (type) {
|
||||||
await pp.command([
|
case SuperResolutionType.disable:
|
||||||
'change-list',
|
return pp.command(['change-list', 'glsl-shaders', 'clr', '']);
|
||||||
'glsl-shaders',
|
case SuperResolutionType.efficiency:
|
||||||
'set',
|
return pp.command([
|
||||||
Utils.buildShadersAbsolutePath(
|
'change-list',
|
||||||
(await copyShadersToExternalDirectory())?.path ?? '',
|
'glsl-shaders',
|
||||||
Constants.mpvAnime4KShadersLite,
|
'set',
|
||||||
),
|
Utils.buildShadersAbsolutePath(
|
||||||
]);
|
(await copyShadersToExternalDirectory())?.path ?? '',
|
||||||
} else if (type == 2) {
|
Constants.mpvAnime4KShadersLite,
|
||||||
await pp.command([
|
),
|
||||||
'change-list',
|
]);
|
||||||
'glsl-shaders',
|
case SuperResolutionType.quality:
|
||||||
'set',
|
return pp.command([
|
||||||
Utils.buildShadersAbsolutePath(
|
'change-list',
|
||||||
(await copyShadersToExternalDirectory())?.path ?? '',
|
'glsl-shaders',
|
||||||
Constants.mpvAnime4KShaders,
|
'set',
|
||||||
),
|
Utils.buildShadersAbsolutePath(
|
||||||
]);
|
(await copyShadersToExternalDirectory())?.path ?? '',
|
||||||
} else {
|
Constants.mpvAnime4KShaders,
|
||||||
await pp.command(['change-list', 'glsl-shaders', 'clr', '']);
|
),
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -711,7 +714,7 @@ class PlPlayerController {
|
|||||||
var pp = player.platform as NativePlayer;
|
var pp = player.platform as NativePlayer;
|
||||||
if (_videoPlayerController == null) {
|
if (_videoPlayerController == null) {
|
||||||
if (isAnim) {
|
if (isAnim) {
|
||||||
setShader(superResolutionType, pp);
|
setShader(superResolutionType.value, pp);
|
||||||
}
|
}
|
||||||
String audioNormalization = Pref.audioNormalization;
|
String audioNormalization = Pref.audioNormalization;
|
||||||
audioNormalization = switch (audioNormalization) {
|
audioNormalization = switch (audioNormalization) {
|
||||||
@@ -1500,29 +1503,31 @@ class PlPlayerController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void putDanmakuSettings() {
|
void putDanmakuSettings() {
|
||||||
setting
|
setting.putAll({
|
||||||
..put(SettingBoxKey.danmakuWeight, danmakuWeight)
|
SettingBoxKey.danmakuWeight: danmakuWeight,
|
||||||
..put(SettingBoxKey.danmakuBlockType, blockTypes.toList())
|
SettingBoxKey.danmakuBlockType: blockTypes.toList(),
|
||||||
..put(SettingBoxKey.danmakuShowArea, showArea)
|
SettingBoxKey.danmakuShowArea: showArea,
|
||||||
..put(SettingBoxKey.danmakuOpacity, danmakuOpacity)
|
SettingBoxKey.danmakuOpacity: danmakuOpacity,
|
||||||
..put(SettingBoxKey.danmakuFontScale, danmakuFontScale)
|
SettingBoxKey.danmakuFontScale: danmakuFontScale,
|
||||||
..put(SettingBoxKey.danmakuFontScaleFS, danmakuFontScaleFS)
|
SettingBoxKey.danmakuFontScaleFS: danmakuFontScaleFS,
|
||||||
..put(SettingBoxKey.danmakuDuration, danmakuDuration)
|
SettingBoxKey.danmakuDuration: danmakuDuration,
|
||||||
..put(SettingBoxKey.danmakuStaticDuration, danmakuStaticDuration)
|
SettingBoxKey.danmakuStaticDuration: danmakuStaticDuration,
|
||||||
..put(SettingBoxKey.strokeWidth, strokeWidth)
|
SettingBoxKey.strokeWidth: strokeWidth,
|
||||||
..put(SettingBoxKey.fontWeight, fontWeight)
|
SettingBoxKey.fontWeight: fontWeight,
|
||||||
..put(SettingBoxKey.danmakuLineHeight, danmakuLineHeight);
|
SettingBoxKey.danmakuLineHeight: danmakuLineHeight,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void putSubtitleSettings() {
|
void putSubtitleSettings() {
|
||||||
setting
|
setting.putAll({
|
||||||
..put(SettingBoxKey.subtitleFontScale, subtitleFontScale)
|
SettingBoxKey.subtitleFontScale: subtitleFontScale,
|
||||||
..put(SettingBoxKey.subtitleFontScaleFS, subtitleFontScaleFS)
|
SettingBoxKey.subtitleFontScaleFS: subtitleFontScaleFS,
|
||||||
..put(SettingBoxKey.subtitlePaddingH, subtitlePaddingH)
|
SettingBoxKey.subtitlePaddingH: subtitlePaddingH,
|
||||||
..put(SettingBoxKey.subtitlePaddingB, subtitlePaddingB)
|
SettingBoxKey.subtitlePaddingB: subtitlePaddingB,
|
||||||
..put(SettingBoxKey.subtitleBgOpaticy, subtitleBgOpaticy)
|
SettingBoxKey.subtitleBgOpaticy: subtitleBgOpaticy,
|
||||||
..put(SettingBoxKey.subtitleStrokeWidth, subtitleStrokeWidth)
|
SettingBoxKey.subtitleStrokeWidth: subtitleStrokeWidth,
|
||||||
..put(SettingBoxKey.subtitleFontWeight, subtitleFontWeight);
|
SettingBoxKey.subtitleFontWeight: subtitleFontWeight,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> dispose() async {
|
Future<void> dispose() async {
|
||||||
|
|||||||
@@ -266,49 +266,43 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
Widget progressWidget(
|
Widget progressWidget(
|
||||||
BottomControlType bottomControl,
|
BottomControlType bottomControl,
|
||||||
) => switch (bottomControl) {
|
) => switch (bottomControl) {
|
||||||
/// 上一集
|
|
||||||
BottomControlType.pre => Container(
|
|
||||||
width: widgetWidth,
|
|
||||||
height: 30,
|
|
||||||
alignment: Alignment.center,
|
|
||||||
child: ComBtn(
|
|
||||||
icon: const Icon(
|
|
||||||
Icons.skip_previous,
|
|
||||||
semanticLabel: '上一集',
|
|
||||||
size: 22,
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
onTap: () {
|
|
||||||
if (!introController.prevPlay()) {
|
|
||||||
SmartDialog.showToast('已经是第一集了');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
/// 播放暂停
|
/// 播放暂停
|
||||||
BottomControlType.playOrPause => PlayOrPauseButton(
|
BottomControlType.playOrPause => PlayOrPauseButton(
|
||||||
plPlayerController: plPlayerController,
|
plPlayerController: plPlayerController,
|
||||||
),
|
),
|
||||||
|
|
||||||
/// 下一集
|
/// 上一集
|
||||||
BottomControlType.next => Container(
|
BottomControlType.pre => ComBtn(
|
||||||
width: widgetWidth,
|
width: widgetWidth,
|
||||||
height: 30,
|
height: 30,
|
||||||
alignment: Alignment.center,
|
icon: const Icon(
|
||||||
child: ComBtn(
|
Icons.skip_previous,
|
||||||
icon: const Icon(
|
semanticLabel: '上一集',
|
||||||
Icons.skip_next,
|
size: 22,
|
||||||
semanticLabel: '下一集',
|
color: Colors.white,
|
||||||
size: 22,
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
onTap: () {
|
|
||||||
if (!introController.nextPlay()) {
|
|
||||||
SmartDialog.showToast('已经是最后一集了');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
|
onTap: () {
|
||||||
|
if (!introController.prevPlay()) {
|
||||||
|
SmartDialog.showToast('已经是第一集了');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
|
||||||
|
/// 下一集
|
||||||
|
BottomControlType.next => ComBtn(
|
||||||
|
width: widgetWidth,
|
||||||
|
height: 30,
|
||||||
|
icon: const Icon(
|
||||||
|
Icons.skip_next,
|
||||||
|
semanticLabel: '下一集',
|
||||||
|
size: 22,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
onTap: () {
|
||||||
|
if (!introController.nextPlay()) {
|
||||||
|
SmartDialog.showToast('已经是最后一集了');
|
||||||
|
}
|
||||||
|
},
|
||||||
),
|
),
|
||||||
|
|
||||||
/// 时间进度
|
/// 时间进度
|
||||||
@@ -317,8 +311,8 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
crossAxisAlignment: CrossAxisAlignment.end,
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
children: [
|
children: [
|
||||||
// 播放时间
|
// 播放时间
|
||||||
Obx(() {
|
Obx(
|
||||||
return Text(
|
() => Text(
|
||||||
DurationUtil.formatDuration(
|
DurationUtil.formatDuration(
|
||||||
plPlayerController.positionSeconds.value,
|
plPlayerController.positionSeconds.value,
|
||||||
),
|
),
|
||||||
@@ -328,8 +322,8 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
height: 1.4,
|
height: 1.4,
|
||||||
fontFeatures: [FontFeature.tabularFigures()],
|
fontFeatures: [FontFeature.tabularFigures()],
|
||||||
),
|
),
|
||||||
);
|
),
|
||||||
}),
|
),
|
||||||
Obx(
|
Obx(
|
||||||
() => Text(
|
() => Text(
|
||||||
DurationUtil.formatDuration(
|
DurationUtil.formatDuration(
|
||||||
@@ -350,72 +344,63 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
BottomControlType.dmChart => Obx(
|
BottomControlType.dmChart => Obx(
|
||||||
() => plPlayerController.dmTrend.isEmpty
|
() => plPlayerController.dmTrend.isEmpty
|
||||||
? const SizedBox.shrink()
|
? const SizedBox.shrink()
|
||||||
: Container(
|
: ComBtn(
|
||||||
width: widgetWidth,
|
width: widgetWidth,
|
||||||
height: 30,
|
height: 30,
|
||||||
alignment: Alignment.center,
|
icon: plPlayerController.showDmTreandChart.value
|
||||||
child: ComBtn(
|
? const Icon(
|
||||||
icon: plPlayerController.showDmTreandChart.value
|
Icons.show_chart,
|
||||||
? const Icon(
|
size: 22,
|
||||||
Icons.show_chart,
|
color: Colors.white,
|
||||||
size: 22,
|
)
|
||||||
color: Colors.white,
|
: const Stack(
|
||||||
)
|
clipBehavior: Clip.none,
|
||||||
: const Stack(
|
alignment: Alignment.center,
|
||||||
clipBehavior: Clip.none,
|
children: [
|
||||||
alignment: Alignment.center,
|
Icon(
|
||||||
children: [
|
Icons.show_chart,
|
||||||
Icon(
|
size: 22,
|
||||||
Icons.show_chart,
|
color: Colors.white,
|
||||||
size: 22,
|
),
|
||||||
color: Colors.white,
|
Icon(
|
||||||
),
|
Icons.hide_source,
|
||||||
Icon(
|
size: 22,
|
||||||
Icons.hide_source,
|
color: Colors.white,
|
||||||
size: 22,
|
),
|
||||||
color: Colors.white,
|
],
|
||||||
),
|
),
|
||||||
],
|
onTap: () => plPlayerController.showDmTreandChart.value =
|
||||||
),
|
!plPlayerController.showDmTreandChart.value,
|
||||||
onTap: () => plPlayerController.showDmTreandChart.value =
|
|
||||||
!plPlayerController.showDmTreandChart.value,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
/// 超分辨率
|
/// 超分辨率
|
||||||
BottomControlType.superResolution => Container(
|
BottomControlType.superResolution => Obx(
|
||||||
height: 30,
|
() => PopupMenuButton<SuperResolutionType>(
|
||||||
margin: const EdgeInsets.symmetric(horizontal: 10),
|
initialValue: plPlayerController.superResolutionType.value,
|
||||||
alignment: Alignment.center,
|
|
||||||
child: PopupMenuButton<SuperResolutionType>(
|
|
||||||
initialValue: SuperResolutionType
|
|
||||||
.values[plPlayerController.superResolutionType],
|
|
||||||
color: Colors.black.withValues(alpha: 0.8),
|
color: Colors.black.withValues(alpha: 0.8),
|
||||||
itemBuilder: (BuildContext context) {
|
itemBuilder: (context) {
|
||||||
return SuperResolutionType.values.map((
|
return SuperResolutionType.values
|
||||||
SuperResolutionType type,
|
.map(
|
||||||
) {
|
(type) => PopupMenuItem<SuperResolutionType>(
|
||||||
return PopupMenuItem<SuperResolutionType>(
|
height: 35,
|
||||||
height: 35,
|
padding: const EdgeInsets.only(left: 30),
|
||||||
padding: const EdgeInsets.only(left: 30),
|
value: type,
|
||||||
value: type,
|
onTap: () => plPlayerController.setShader(type),
|
||||||
onTap: () => plPlayerController.setShader(type.index),
|
child: Text(
|
||||||
child: Text(
|
type.title,
|
||||||
type.title,
|
style: const TextStyle(color: Colors.white, fontSize: 13),
|
||||||
style: const TextStyle(
|
),
|
||||||
color: Colors.white,
|
|
||||||
fontSize: 13,
|
|
||||||
),
|
),
|
||||||
),
|
)
|
||||||
);
|
.toList();
|
||||||
}).toList();
|
|
||||||
},
|
},
|
||||||
child: Text(
|
child: Padding(
|
||||||
SuperResolutionType
|
padding: const EdgeInsets.symmetric(horizontal: 10),
|
||||||
.values[plPlayerController.superResolutionType]
|
child: Text(
|
||||||
.title,
|
plPlayerController.superResolutionType.value.title,
|
||||||
style: const TextStyle(color: Colors.white, fontSize: 13),
|
style: const TextStyle(color: Colors.white, fontSize: 13),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -424,110 +409,105 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
BottomControlType.viewPoints => Obx(
|
BottomControlType.viewPoints => Obx(
|
||||||
() => plPlayerController.viewPointList.isEmpty
|
() => plPlayerController.viewPointList.isEmpty
|
||||||
? const SizedBox.shrink()
|
? const SizedBox.shrink()
|
||||||
: Container(
|
: ComBtn(
|
||||||
width: widgetWidth,
|
width: widgetWidth,
|
||||||
height: 30,
|
height: 30,
|
||||||
alignment: Alignment.center,
|
icon: Transform.rotate(
|
||||||
child: ComBtn(
|
angle: pi / 2,
|
||||||
icon: Transform.rotate(
|
child: const Icon(
|
||||||
angle: pi / 2,
|
MdiIcons.viewHeadline,
|
||||||
child: const Icon(
|
semanticLabel: '分段信息',
|
||||||
MdiIcons.viewHeadline,
|
size: 22,
|
||||||
semanticLabel: '分段信息',
|
color: Colors.white,
|
||||||
size: 22,
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
onTap: widget.showViewPoints,
|
|
||||||
onLongPress: () {
|
|
||||||
Feedback.forLongPress(context);
|
|
||||||
plPlayerController.showVP.value =
|
|
||||||
!plPlayerController.showVP.value;
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
|
onTap: widget.showViewPoints,
|
||||||
|
onLongPress: () {
|
||||||
|
Feedback.forLongPress(context);
|
||||||
|
plPlayerController.showVP.value =
|
||||||
|
!plPlayerController.showVP.value;
|
||||||
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
/// 选集
|
/// 选集
|
||||||
BottomControlType.episode => Container(
|
BottomControlType.episode => ComBtn(
|
||||||
width: widgetWidth,
|
width: widgetWidth,
|
||||||
height: 30,
|
height: 30,
|
||||||
alignment: Alignment.center,
|
icon: const Icon(
|
||||||
child: ComBtn(
|
Icons.list,
|
||||||
icon: const Icon(
|
semanticLabel: '选集',
|
||||||
Icons.list,
|
size: 22,
|
||||||
semanticLabel: '选集',
|
color: Colors.white,
|
||||||
size: 22,
|
),
|
||||||
color: Colors.white,
|
onTap: () {
|
||||||
),
|
// part -> playAll -> season(pgc)
|
||||||
onTap: () {
|
if (isPlayAll && !isPart) {
|
||||||
// part -> playAll -> season(pgc)
|
widget.showEpisodes?.call();
|
||||||
if (isPlayAll && !isPart) {
|
return;
|
||||||
widget.showEpisodes?.call();
|
}
|
||||||
return;
|
int? index;
|
||||||
}
|
int currentCid = plPlayerController.cid;
|
||||||
int? index;
|
String bvid = plPlayerController.bvid;
|
||||||
int currentCid = plPlayerController.cid;
|
List episodes = [];
|
||||||
String bvid = plPlayerController.bvid;
|
if (isSeason) {
|
||||||
List episodes = [];
|
final List<SectionItem> sections = videoDetail.ugcSeason!.sections!;
|
||||||
if (isSeason) {
|
for (int i = 0; i < sections.length; i++) {
|
||||||
final List<SectionItem> sections =
|
final List<EpisodeItem> episodesList = sections[i].episodes!;
|
||||||
videoDetail.ugcSeason!.sections!;
|
for (int j = 0; j < episodesList.length; j++) {
|
||||||
for (int i = 0; i < sections.length; i++) {
|
if (episodesList[j].cid == plPlayerController.cid) {
|
||||||
final List<EpisodeItem> episodesList = sections[i].episodes!;
|
index = i;
|
||||||
for (int j = 0; j < episodesList.length; j++) {
|
episodes = episodesList;
|
||||||
if (episodesList[j].cid == plPlayerController.cid) {
|
break;
|
||||||
index = i;
|
|
||||||
episodes = episodesList;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (isPart) {
|
|
||||||
episodes = videoDetail.pages!;
|
|
||||||
} else if (isPgc) {
|
|
||||||
episodes =
|
|
||||||
(introController as PgcIntroController).pgcItem.episodes!;
|
|
||||||
}
|
}
|
||||||
widget.showEpisodes?.call(
|
} else if (isPart) {
|
||||||
index,
|
episodes = videoDetail.pages!;
|
||||||
isSeason ? videoDetail.ugcSeason! : null,
|
} else if (isPgc) {
|
||||||
isSeason ? null : episodes,
|
episodes =
|
||||||
bvid,
|
(introController as PgcIntroController).pgcItem.episodes!;
|
||||||
IdUtils.bv2av(bvid),
|
}
|
||||||
isSeason && isPart
|
widget.showEpisodes?.call(
|
||||||
? widget.videoDetailController?.seasonCid ?? currentCid
|
index,
|
||||||
: currentCid,
|
isSeason ? videoDetail.ugcSeason! : null,
|
||||||
);
|
isSeason ? null : episodes,
|
||||||
},
|
bvid,
|
||||||
),
|
IdUtils.bv2av(bvid),
|
||||||
|
isSeason && isPart
|
||||||
|
? widget.videoDetailController?.seasonCid ?? currentCid
|
||||||
|
: currentCid,
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
|
|
||||||
/// 画面比例
|
/// 画面比例
|
||||||
BottomControlType.fit => Container(
|
BottomControlType.fit => Obx(
|
||||||
height: 30,
|
() => PopupMenuButton<BoxFit>(
|
||||||
margin: const EdgeInsets.symmetric(horizontal: 10),
|
|
||||||
alignment: Alignment.center,
|
|
||||||
child: PopupMenuButton<BoxFit>(
|
|
||||||
initialValue: plPlayerController.videoFit.value,
|
initialValue: plPlayerController.videoFit.value,
|
||||||
color: Colors.black.withValues(alpha: 0.8),
|
color: Colors.black.withValues(alpha: 0.8),
|
||||||
itemBuilder: (BuildContext context) {
|
itemBuilder: (context) {
|
||||||
return BoxFit.values.map((BoxFit boxFit) {
|
return BoxFit.values
|
||||||
return PopupMenuItem<BoxFit>(
|
.map(
|
||||||
height: 35,
|
(BoxFit boxFit) => PopupMenuItem<BoxFit>(
|
||||||
padding: const EdgeInsets.only(left: 30),
|
height: 35,
|
||||||
value: boxFit,
|
padding: const EdgeInsets.only(left: 30),
|
||||||
onTap: () => plPlayerController.toggleVideoFit(boxFit),
|
value: boxFit,
|
||||||
child: Text(
|
onTap: () => plPlayerController.toggleVideoFit(boxFit),
|
||||||
boxFit.desc,
|
child: Text(
|
||||||
style: const TextStyle(color: Colors.white, fontSize: 13),
|
boxFit.desc,
|
||||||
),
|
style: const TextStyle(color: Colors.white, fontSize: 13),
|
||||||
);
|
),
|
||||||
}).toList();
|
),
|
||||||
|
)
|
||||||
|
.toList();
|
||||||
},
|
},
|
||||||
child: Text(
|
child: Padding(
|
||||||
plPlayerController.videoFit.value.desc,
|
padding: const EdgeInsets.symmetric(horizontal: 10),
|
||||||
style: const TextStyle(color: Colors.white, fontSize: 13),
|
child: Text(
|
||||||
|
plPlayerController.videoFit.value.desc,
|
||||||
|
style: const TextStyle(color: Colors.white, fontSize: 13),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -536,83 +516,79 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
BottomControlType.subtitle => Obx(
|
BottomControlType.subtitle => Obx(
|
||||||
() => widget.videoDetailController?.subtitles.isEmpty == true
|
() => widget.videoDetailController?.subtitles.isEmpty == true
|
||||||
? const SizedBox.shrink()
|
? const SizedBox.shrink()
|
||||||
: SizedBox(
|
: PopupMenuButton<int>(
|
||||||
width: widgetWidth,
|
initialValue: widget
|
||||||
height: 30,
|
.videoDetailController!
|
||||||
child: PopupMenuButton<int>(
|
.vttSubtitlesIndex
|
||||||
initialValue: widget
|
.value
|
||||||
.videoDetailController!
|
.clamp(0, widget.videoDetailController!.subtitles.length),
|
||||||
.vttSubtitlesIndex
|
color: Colors.black.withValues(alpha: 0.8),
|
||||||
.value
|
itemBuilder: (context) {
|
||||||
.clamp(0, widget.videoDetailController!.subtitles.length),
|
return [
|
||||||
color: Colors.black.withValues(alpha: 0.8),
|
PopupMenuItem<int>(
|
||||||
itemBuilder: (BuildContext context) {
|
value: 0,
|
||||||
return [
|
onTap: () => widget.videoDetailController!.setSubtitle(0),
|
||||||
PopupMenuItem<int>(
|
child: const Text(
|
||||||
value: 0,
|
"关闭字幕",
|
||||||
onTap: () =>
|
style: TextStyle(color: Colors.white),
|
||||||
widget.videoDetailController!.setSubtitle(0),
|
|
||||||
child: const Text(
|
|
||||||
"关闭字幕",
|
|
||||||
style: TextStyle(color: Colors.white),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
...widget.videoDetailController!.subtitles.indexed.map((
|
|
||||||
e,
|
|
||||||
) {
|
|
||||||
return PopupMenuItem<int>(
|
|
||||||
value: e.$1 + 1,
|
|
||||||
onTap: () => widget.videoDetailController!
|
|
||||||
.setSubtitle(e.$1 + 1),
|
|
||||||
child: Text(
|
|
||||||
"${e.$2.lanDoc}",
|
|
||||||
style: const TextStyle(color: Colors.white),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
},
|
|
||||||
child: Container(
|
|
||||||
width: 35,
|
|
||||||
height: 30,
|
|
||||||
alignment: Alignment.center,
|
|
||||||
child: Icon(
|
|
||||||
widget.videoDetailController!.vttSubtitlesIndex.value == 0
|
|
||||||
? Icons.closed_caption_off_outlined
|
|
||||||
: Icons.closed_caption_off_rounded,
|
|
||||||
size: 22,
|
|
||||||
color: Colors.white,
|
|
||||||
semanticLabel: '字幕',
|
|
||||||
),
|
),
|
||||||
),
|
...widget.videoDetailController!.subtitles.indexed.map((e) {
|
||||||
|
return PopupMenuItem<int>(
|
||||||
|
value: e.$1 + 1,
|
||||||
|
onTap: () =>
|
||||||
|
widget.videoDetailController!.setSubtitle(e.$1 + 1),
|
||||||
|
child: Text(
|
||||||
|
"${e.$2.lanDoc}",
|
||||||
|
style: const TextStyle(color: Colors.white),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
},
|
||||||
|
child: SizedBox(
|
||||||
|
width: widgetWidth,
|
||||||
|
height: 30,
|
||||||
|
child:
|
||||||
|
widget.videoDetailController!.vttSubtitlesIndex.value == 0
|
||||||
|
? const Icon(
|
||||||
|
Icons.closed_caption_off_outlined,
|
||||||
|
size: 22,
|
||||||
|
color: Colors.white,
|
||||||
|
)
|
||||||
|
: const Icon(
|
||||||
|
Icons.closed_caption_off_rounded,
|
||||||
|
size: 22,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
/// 播放速度
|
/// 播放速度
|
||||||
BottomControlType.speed => Obx(
|
BottomControlType.speed => Obx(
|
||||||
() => Container(
|
() => PopupMenuButton<double>(
|
||||||
height: 30,
|
initialValue: plPlayerController.playbackSpeed,
|
||||||
margin: const EdgeInsets.symmetric(horizontal: 10),
|
color: Colors.black.withValues(alpha: 0.8),
|
||||||
alignment: Alignment.center,
|
itemBuilder: (context) {
|
||||||
child: PopupMenuButton<double>(
|
return plPlayerController.speedList
|
||||||
initialValue: plPlayerController.playbackSpeed,
|
.map(
|
||||||
color: Colors.black.withValues(alpha: 0.8),
|
(double speed) => PopupMenuItem<double>(
|
||||||
itemBuilder: (BuildContext context) {
|
height: 35,
|
||||||
return plPlayerController.speedList.map((double speed) {
|
padding: const EdgeInsets.only(left: 30),
|
||||||
return PopupMenuItem<double>(
|
value: speed,
|
||||||
height: 35,
|
onTap: () => plPlayerController.setPlaybackSpeed(speed),
|
||||||
padding: const EdgeInsets.only(left: 30),
|
child: Text(
|
||||||
value: speed,
|
"${speed}X",
|
||||||
onTap: () => plPlayerController.setPlaybackSpeed(speed),
|
style: const TextStyle(color: Colors.white, fontSize: 13),
|
||||||
child: Text(
|
semanticsLabel: "$speed倍速",
|
||||||
"${speed}X",
|
),
|
||||||
style: const TextStyle(color: Colors.white, fontSize: 13),
|
|
||||||
semanticsLabel: "$speed倍速",
|
|
||||||
),
|
),
|
||||||
);
|
)
|
||||||
}).toList();
|
.toList();
|
||||||
},
|
},
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 10),
|
||||||
child: Text(
|
child: Text(
|
||||||
"${plPlayerController.playbackSpeed}X",
|
"${plPlayerController.playbackSpeed}X",
|
||||||
style: const TextStyle(color: Colors.white, fontSize: 13),
|
style: const TextStyle(color: Colors.white, fontSize: 13),
|
||||||
@@ -623,20 +599,25 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
),
|
),
|
||||||
|
|
||||||
/// 全屏
|
/// 全屏
|
||||||
BottomControlType.fullscreen => SizedBox(
|
BottomControlType.fullscreen => Obx(
|
||||||
width: widgetWidth,
|
() => ComBtn(
|
||||||
height: 30,
|
width: widgetWidth,
|
||||||
child: Obx(
|
height: 30,
|
||||||
() => ComBtn(
|
icon: isFullScreen
|
||||||
icon: Icon(
|
? const Icon(
|
||||||
isFullScreen ? Icons.fullscreen_exit : Icons.fullscreen,
|
Icons.fullscreen_exit,
|
||||||
semanticLabel: isFullScreen ? '退出全屏' : '全屏',
|
semanticLabel: '退出全屏',
|
||||||
size: 24,
|
size: 24,
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
),
|
)
|
||||||
onTap: () =>
|
: const Icon(
|
||||||
plPlayerController.triggerFullScreen(status: !isFullScreen),
|
Icons.fullscreen,
|
||||||
),
|
semanticLabel: '全屏',
|
||||||
|
size: 24,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
onTap: () =>
|
||||||
|
plPlayerController.triggerFullScreen(status: !isFullScreen),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,19 +4,23 @@ class ComBtn extends StatelessWidget {
|
|||||||
final Widget icon;
|
final Widget icon;
|
||||||
final VoidCallback? onTap;
|
final VoidCallback? onTap;
|
||||||
final VoidCallback? onLongPress;
|
final VoidCallback? onLongPress;
|
||||||
|
final double width;
|
||||||
|
final double height;
|
||||||
|
|
||||||
const ComBtn({
|
const ComBtn({
|
||||||
|
super.key,
|
||||||
required this.icon,
|
required this.icon,
|
||||||
this.onTap,
|
this.onTap,
|
||||||
this.onLongPress,
|
this.onLongPress,
|
||||||
super.key,
|
this.width = 34,
|
||||||
|
this.height = 34,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
width: 34,
|
width: width,
|
||||||
height: 34,
|
height: height,
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
onLongPress: onLongPress,
|
onLongPress: onLongPress,
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import 'package:PiliPlus/models/common/member/tab_type.dart';
|
|||||||
import 'package:PiliPlus/models/common/msg/msg_unread_type.dart';
|
import 'package:PiliPlus/models/common/msg/msg_unread_type.dart';
|
||||||
import 'package:PiliPlus/models/common/sponsor_block/segment_type.dart';
|
import 'package:PiliPlus/models/common/sponsor_block/segment_type.dart';
|
||||||
import 'package:PiliPlus/models/common/sponsor_block/skip_type.dart';
|
import 'package:PiliPlus/models/common/sponsor_block/skip_type.dart';
|
||||||
|
import 'package:PiliPlus/models/common/super_resolution_type.dart';
|
||||||
import 'package:PiliPlus/models/common/theme/theme_type.dart';
|
import 'package:PiliPlus/models/common/theme/theme_type.dart';
|
||||||
import 'package:PiliPlus/models/common/video/audio_quality.dart';
|
import 'package:PiliPlus/models/common/video/audio_quality.dart';
|
||||||
import 'package:PiliPlus/models/common/video/cdn_type.dart';
|
import 'package:PiliPlus/models/common/video/cdn_type.dart';
|
||||||
@@ -22,6 +23,7 @@ import 'package:PiliPlus/plugin/pl_player/models/bottom_progress_behavior.dart';
|
|||||||
import 'package:PiliPlus/plugin/pl_player/models/fullscreen_mode.dart';
|
import 'package:PiliPlus/plugin/pl_player/models/fullscreen_mode.dart';
|
||||||
import 'package:PiliPlus/plugin/pl_player/models/play_repeat.dart';
|
import 'package:PiliPlus/plugin/pl_player/models/play_repeat.dart';
|
||||||
import 'package:PiliPlus/utils/context_ext.dart';
|
import 'package:PiliPlus/utils/context_ext.dart';
|
||||||
|
import 'package:PiliPlus/utils/extension.dart';
|
||||||
import 'package:PiliPlus/utils/global_data.dart';
|
import 'package:PiliPlus/utils/global_data.dart';
|
||||||
import 'package:PiliPlus/utils/storage.dart';
|
import 'package:PiliPlus/utils/storage.dart';
|
||||||
import 'package:PiliPlus/utils/storage_key.dart';
|
import 'package:PiliPlus/utils/storage_key.dart';
|
||||||
@@ -406,8 +408,15 @@ class Pref {
|
|||||||
static String get audioNormalization =>
|
static String get audioNormalization =>
|
||||||
_setting.get(SettingBoxKey.audioNormalization, defaultValue: '0');
|
_setting.get(SettingBoxKey.audioNormalization, defaultValue: '0');
|
||||||
|
|
||||||
static int get superResolutionType =>
|
static SuperResolutionType get superResolutionType {
|
||||||
_setting.get(SettingBoxKey.superResolutionType, defaultValue: 0);
|
SuperResolutionType? superResolutionType;
|
||||||
|
final index = _setting.get(SettingBoxKey.superResolutionType);
|
||||||
|
if (index != null) {
|
||||||
|
superResolutionType = SuperResolutionType.values.getOrNull(index);
|
||||||
|
}
|
||||||
|
superResolutionType ??= SuperResolutionType.disable;
|
||||||
|
return superResolutionType;
|
||||||
|
}
|
||||||
|
|
||||||
static bool get preInitPlayer =>
|
static bool get preInitPlayer =>
|
||||||
_setting.get(SettingBoxKey.preInitPlayer, defaultValue: false);
|
_setting.get(SettingBoxKey.preInitPlayer, defaultValue: false);
|
||||||
|
|||||||
Reference in New Issue
Block a user