mirror of
https://github.com/HChaZZY/PiliPlus.git
synced 2025-12-06 09:13:48 +08:00
feat: 调整设置,支持导入导出,代码优化
This commit is contained in:
@@ -56,6 +56,7 @@ class VideoDetailController extends GetxController
|
||||
RxBool isShowCover = true.obs;
|
||||
// 硬解
|
||||
RxBool enableHA = true.obs;
|
||||
RxString hwdec = 'auto-safe'.obs;
|
||||
|
||||
/// 本地存储
|
||||
Box userInfoCache = GStrorage.userInfo;
|
||||
@@ -117,7 +118,8 @@ class VideoDetailController extends GetxController
|
||||
autoPlay.value =
|
||||
setting.get(SettingBoxKey.autoPlayEnable, defaultValue: true);
|
||||
enableHA.value = setting.get(SettingBoxKey.enableHA, defaultValue: true);
|
||||
|
||||
hwdec.value = setting.get(SettingBoxKey.hardwareDecoding,
|
||||
defaultValue: Platform.isAndroid ? 'auto-safe' : 'auto');
|
||||
if (userInfo == null ||
|
||||
localCache.get(LocalCacheKey.historyPause) == true) {
|
||||
enableHeart = false;
|
||||
@@ -270,6 +272,7 @@ class VideoDetailController extends GetxController
|
||||
),
|
||||
// 硬解
|
||||
enableHA: enableHA.value,
|
||||
hwdec: hwdec.value,
|
||||
seekTo: seekToTime ?? defaultST,
|
||||
duration: duration ?? data.timeLength == null
|
||||
? null
|
||||
@@ -300,109 +303,112 @@ class VideoDetailController extends GetxController
|
||||
'该视频为专属视频,仅提供试看',
|
||||
displayTime: const Duration(seconds: 3),
|
||||
);
|
||||
}
|
||||
if (data.dash == null && data.durl != null) {
|
||||
videoUrl = data.durl!.first.url!;
|
||||
audioUrl = '';
|
||||
defaultST = Duration.zero;
|
||||
firstVideo = VideoItem();
|
||||
// 实际为FLV/MP4格式,但已被淘汰,这里仅做兜底处理
|
||||
firstVideo = VideoItem(
|
||||
id: data.quality!,
|
||||
baseUrl: videoUrl,
|
||||
codecs: 'avc1',
|
||||
quality: VideoQualityCode.fromCode(data.quality!)!
|
||||
);
|
||||
currentDecodeFormats = VideoDecodeFormatsCode.fromString('avc1')!;
|
||||
currentVideoQa = VideoQualityCode.fromCode(data.quality!)!;
|
||||
if (autoPlay.value) {
|
||||
await playerInit();
|
||||
isShowCover.value = false;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
final List<VideoItem> allVideosList = data.dash!.video!;
|
||||
try {
|
||||
// 当前可播放的最高质量视频
|
||||
int currentHighVideoQa = allVideosList.first.quality!.code;
|
||||
// 预设的画质为null,则当前可用的最高质量
|
||||
cacheVideoQa ??= currentHighVideoQa;
|
||||
int resVideoQa = currentHighVideoQa;
|
||||
if (cacheVideoQa! <= currentHighVideoQa) {
|
||||
// 如果预设的画质低于当前最高
|
||||
final List<int> numbers = data.acceptQuality!
|
||||
.where((e) => e <= currentHighVideoQa)
|
||||
.toList();
|
||||
resVideoQa = Utils.findClosestNumber(cacheVideoQa!, numbers);
|
||||
}
|
||||
currentVideoQa = VideoQualityCode.fromCode(resVideoQa)!;
|
||||
|
||||
/// 取出符合当前画质的videoList
|
||||
final List<VideoItem> videosList =
|
||||
allVideosList.where((e) => e.quality!.code == resVideoQa).toList();
|
||||
|
||||
/// 优先顺序 设置中指定解码格式 -> 当前可选的首个解码格式
|
||||
final List<FormatItem> supportFormats = data.supportFormats!;
|
||||
// 根据画质选编码格式
|
||||
final List supportDecodeFormats =
|
||||
supportFormats.firstWhere((e) => e.quality == resVideoQa).codecs!;
|
||||
// 默认从设置中取AV1
|
||||
currentDecodeFormats = VideoDecodeFormatsCode.fromString(cacheDecode)!;
|
||||
VideoDecodeFormats secondDecodeFormats =
|
||||
VideoDecodeFormatsCode.fromString(cacheSecondDecode)!;
|
||||
try {
|
||||
// 当前视频没有对应格式返回第一个
|
||||
int flag = 0;
|
||||
for (var i in supportDecodeFormats) {
|
||||
if (i.startsWith(currentDecodeFormats.code)) {
|
||||
flag = 1;
|
||||
break;
|
||||
} else if (i.startsWith(secondDecodeFormats.code)) {
|
||||
flag = 2;
|
||||
}
|
||||
}
|
||||
if (flag == 2) {
|
||||
currentDecodeFormats = secondDecodeFormats;
|
||||
} else if (flag == 0) {
|
||||
currentDecodeFormats =
|
||||
VideoDecodeFormatsCode.fromString(supportDecodeFormats.first)!;
|
||||
}
|
||||
} catch (err) {
|
||||
SmartDialog.showToast('DecodeFormats error: $err');
|
||||
}
|
||||
|
||||
/// 取出符合当前解码格式的videoItem
|
||||
try {
|
||||
firstVideo = videosList.firstWhere(
|
||||
(e) => e.codecs!.startsWith(currentDecodeFormats.code));
|
||||
} catch (_) {
|
||||
firstVideo = videosList.first;
|
||||
}
|
||||
videoUrl = enableCDN
|
||||
? VideoUtils.getCdnUrl(firstVideo)
|
||||
: (firstVideo.backupUrl ?? firstVideo.baseUrl!);
|
||||
} catch (err) {
|
||||
SmartDialog.showToast('firstVideo error: $err');
|
||||
if (data.dash == null) {
|
||||
SmartDialog.showToast('视频资源不存在');
|
||||
isShowCover.value = false;
|
||||
return result;
|
||||
}
|
||||
final List<VideoItem> allVideosList = data.dash!.video!;
|
||||
print("allVideosList:${allVideosList}");
|
||||
// 当前可播放的最高质量视频
|
||||
int currentHighVideoQa = allVideosList.first.quality!.code;
|
||||
// 预设的画质为null,则当前可用的最高质量
|
||||
cacheVideoQa ??= currentHighVideoQa;
|
||||
int resVideoQa = currentHighVideoQa;
|
||||
if (cacheVideoQa! <= currentHighVideoQa) {
|
||||
// 如果预设的画质低于当前最高
|
||||
final List<int> numbers =
|
||||
data.acceptQuality!.where((e) => e <= currentHighVideoQa).toList();
|
||||
resVideoQa = Utils.findClosestNumber(cacheVideoQa!, numbers);
|
||||
}
|
||||
currentVideoQa = VideoQualityCode.fromCode(resVideoQa)!;
|
||||
|
||||
/// 取出符合当前画质的videoList
|
||||
final List<VideoItem> videosList =
|
||||
allVideosList.where((e) => e.quality!.code == resVideoQa).toList();
|
||||
|
||||
/// 优先顺序 设置中指定解码格式 -> 当前可选的首个解码格式
|
||||
final List<FormatItem> supportFormats = data.supportFormats!;
|
||||
// 根据画质选编码格式
|
||||
final List supportDecodeFormats = supportFormats
|
||||
.firstWhere((e) => e.quality == resVideoQa,
|
||||
orElse: () => supportFormats.first)
|
||||
.codecs!;
|
||||
// 默认从设置中取AV1
|
||||
currentDecodeFormats = VideoDecodeFormatsCode.fromString(cacheDecode)!;
|
||||
VideoDecodeFormats secondDecodeFormats =
|
||||
VideoDecodeFormatsCode.fromString(cacheSecondDecode)!;
|
||||
// 当前视频没有对应格式返回第一个
|
||||
int flag = 0;
|
||||
for (var i in supportDecodeFormats) {
|
||||
if (i.startsWith(currentDecodeFormats.code)) {
|
||||
flag = 1;
|
||||
break;
|
||||
} else if (i.startsWith(secondDecodeFormats.code)) {
|
||||
flag = 2;
|
||||
}
|
||||
}
|
||||
if (flag == 2) {
|
||||
currentDecodeFormats = secondDecodeFormats;
|
||||
} else if (flag == 0) {
|
||||
currentDecodeFormats =
|
||||
VideoDecodeFormatsCode.fromString(supportDecodeFormats.first)!;
|
||||
}
|
||||
|
||||
/// 取出符合当前解码格式的videoItem
|
||||
firstVideo = videosList.firstWhere(
|
||||
(e) => e.codecs!.startsWith(currentDecodeFormats.code),
|
||||
orElse: () => videosList.first);
|
||||
|
||||
videoUrl = enableCDN
|
||||
? VideoUtils.getCdnUrl(firstVideo)
|
||||
: (firstVideo.backupUrl ?? firstVideo.baseUrl!);
|
||||
|
||||
/// 优先顺序 设置中指定质量 -> 当前可选的最高质量
|
||||
late AudioItem? firstAudio;
|
||||
final List<AudioItem> audiosList = data.dash!.audio!;
|
||||
|
||||
try {
|
||||
if (data.dash!.dolby?.audio?.isNotEmpty == true) {
|
||||
// 杜比
|
||||
audiosList.insert(0, data.dash!.dolby!.audio!.first);
|
||||
}
|
||||
if (data.dash!.dolby?.audio != null && data.dash!.dolby!.audio!.isNotEmpty) {
|
||||
// 杜比
|
||||
audiosList.insert(0, data.dash!.dolby!.audio!.first);
|
||||
}
|
||||
|
||||
if (data.dash!.flac?.audio != null) {
|
||||
// 无损
|
||||
audiosList.insert(0, data.dash!.flac!.audio!);
|
||||
}
|
||||
if (data.dash!.flac?.audio != null) {
|
||||
// 无损
|
||||
audiosList.insert(0, data.dash!.flac!.audio!);
|
||||
}
|
||||
|
||||
if (audiosList.isNotEmpty) {
|
||||
final List<int> numbers = audiosList.map((map) => map.id!).toList();
|
||||
int closestNumber = Utils.findClosestNumber(cacheAudioQa, numbers);
|
||||
if (!numbers.contains(cacheAudioQa) &&
|
||||
numbers.any((e) => e > cacheAudioQa)) {
|
||||
closestNumber = 30280;
|
||||
}
|
||||
firstAudio = audiosList.firstWhere((e) => e.id == closestNumber);
|
||||
} else {
|
||||
firstAudio = AudioItem();
|
||||
if (audiosList.isNotEmpty) {
|
||||
final List<int> numbers = audiosList.map((map) => map.id!).toList();
|
||||
int closestNumber = Utils.findClosestNumber(cacheAudioQa, numbers);
|
||||
if (!numbers.contains(cacheAudioQa) &&
|
||||
numbers.any((e) => e > cacheAudioQa)) {
|
||||
closestNumber = 30280;
|
||||
}
|
||||
} catch (err) {
|
||||
firstAudio = audiosList.isNotEmpty ? audiosList.first : AudioItem();
|
||||
SmartDialog.showToast('firstAudio error: $err');
|
||||
firstAudio = audiosList.firstWhere((e) => e.id == closestNumber,
|
||||
orElse: () => audiosList.first);
|
||||
} else {
|
||||
firstAudio = AudioItem();
|
||||
}
|
||||
|
||||
audioUrl = enableCDN
|
||||
|
||||
@@ -18,7 +18,6 @@ import 'package:PiliPalaX/utils/feed_back.dart';
|
||||
import 'package:PiliPalaX/utils/storage.dart';
|
||||
import 'package:PiliPalaX/utils/utils.dart';
|
||||
|
||||
import '../../../../utils/id_utils.dart';
|
||||
import 'widgets/action_item.dart';
|
||||
import 'widgets/action_row_item.dart';
|
||||
import 'widgets/fav_panel.dart';
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:PiliPalaX/common/constants.dart';
|
||||
import 'package:PiliPalaX/utils/feed_back.dart';
|
||||
|
||||
class ActionItem extends StatelessWidget {
|
||||
|
||||
@@ -3,7 +3,6 @@ import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:PiliPalaX/models/video_detail_res.dart';
|
||||
import 'package:PiliPalaX/pages/video/detail/index.dart';
|
||||
import 'package:PiliPalaX/utils/id_utils.dart';
|
||||
|
||||
class SeasonPanel extends StatefulWidget {
|
||||
const SeasonPanel({
|
||||
|
||||
@@ -109,7 +109,6 @@ class _VideoReplyNewDialogState extends State<VideoReplyNewDialog>
|
||||
@override
|
||||
void didChangeMetrics() {
|
||||
super.didChangeMetrics();
|
||||
final String routePath = Get.currentRoute;
|
||||
if (!mounted) return;
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (!mounted) return;
|
||||
|
||||
@@ -66,8 +66,6 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
||||
RxBool isFullScreen = false.obs;
|
||||
late StreamSubscription<bool> fullScreenStatusListener;
|
||||
late final MethodChannel onUserLeaveHintListener;
|
||||
late AnimationController _animationController;
|
||||
late Animation<double> _animation;
|
||||
StreamSubscription<Duration>? _bufferedListener;
|
||||
|
||||
@override
|
||||
@@ -292,29 +290,33 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
||||
if (mounted) {
|
||||
setState(() => {});
|
||||
}
|
||||
super.didPopNext();
|
||||
videoDetailController.isFirstTime = false;
|
||||
final bool autoplay = autoPlayEnable;
|
||||
videoDetailController.playerInit(autoplay: autoplay);
|
||||
await videoDetailController.playerInit(autoplay: autoplay);
|
||||
|
||||
/// 未开启自动播放时,未播放跳转下一页返回/播放后跳转下一页返回
|
||||
videoDetailController.autoPlay.value =
|
||||
!videoDetailController.isShowCover.value;
|
||||
videoIntroController.isPaused = false;
|
||||
if (autoplay) {
|
||||
// await Future.delayed(const Duration(milliseconds: 300));
|
||||
if (plPlayerController?.buffered.value == Duration.zero) {
|
||||
_bufferedListener = plPlayerController?.buffered.listen((p0) {
|
||||
if (p0 > Duration.zero) {
|
||||
_bufferedListener!.cancel();
|
||||
plPlayerController?.seekTo(videoDetailController.defaultST);
|
||||
plPlayerController?.play();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
plPlayerController?.seekTo(videoDetailController.defaultST);
|
||||
plPlayerController?.play();
|
||||
}
|
||||
}
|
||||
// if (autoplay) {
|
||||
// // await Future.delayed(const Duration(milliseconds: 300));
|
||||
// print(plPlayerController);
|
||||
// if (plPlayerController?.buffered.value == Duration.zero) {
|
||||
// _bufferedListener = plPlayerController?.buffered.listen((p0) {
|
||||
// print("p0");
|
||||
// print(p0);
|
||||
// if (p0 > Duration.zero) {
|
||||
// _bufferedListener!.cancel();
|
||||
// plPlayerController?.seekTo(videoDetailController.defaultST);
|
||||
// plPlayerController?.play();
|
||||
// }
|
||||
// });
|
||||
// } else {
|
||||
// plPlayerController?.seekTo(videoDetailController.defaultST);
|
||||
// plPlayerController?.play();
|
||||
// }
|
||||
// }
|
||||
Future.delayed(const Duration(milliseconds: 600), () {
|
||||
AutoOrientation.fullAutoMode();
|
||||
});
|
||||
@@ -322,7 +324,6 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
||||
if (plPlayerController != null) {
|
||||
listenFullScreenStatus();
|
||||
}
|
||||
super.didPopNext();
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -409,6 +410,9 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
||||
child: AppBar(
|
||||
backgroundColor: Colors.transparent,
|
||||
elevation: 0,
|
||||
// systemOverlayStyle: const SystemUiOverlayStyle(
|
||||
// statusBarColor: Colors.transparent,
|
||||
// statusBarIconBrightness: Brightness.light),
|
||||
),
|
||||
),
|
||||
body: Column(
|
||||
@@ -417,10 +421,9 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
||||
() {
|
||||
double videoheight = context.width * 9 / 16;
|
||||
final double videowidth = context.width;
|
||||
print(videoDetailController.tabCtr.index);
|
||||
// print(videoDetailController.tabCtr.index);
|
||||
if (enableVerticalExpand &&
|
||||
plPlayerController?.direction.value == 'vertical' &&
|
||||
videoDetailController.tabCtr.index != 1) {
|
||||
plPlayerController?.direction.value == 'vertical') {
|
||||
videoheight = context.width * 5 / 4;
|
||||
}
|
||||
if (MediaQuery.of(context).orientation ==
|
||||
@@ -812,6 +815,9 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
||||
child: AppBar(
|
||||
backgroundColor: Colors.transparent,
|
||||
elevation: 0,
|
||||
// systemOverlayStyle: const SystemUiOverlayStyle(
|
||||
// statusBarColor: Colors.transparent,
|
||||
// statusBarIconBrightness: Brightness.dark),
|
||||
),
|
||||
),
|
||||
body: childWhenDisabledLandscapeInner)
|
||||
|
||||
@@ -20,10 +20,8 @@ import 'package:PiliPalaX/utils/storage.dart';
|
||||
import 'package:PiliPalaX/http/danmaku.dart';
|
||||
import 'package:PiliPalaX/services/shutdown_timer_service.dart';
|
||||
import '../../../../models/video_detail_res.dart';
|
||||
import '../../../../services/service_locator.dart';
|
||||
import '../introduction/index.dart';
|
||||
import 'package:marquee/marquee.dart';
|
||||
import '../../../danmaku/controller.dart';
|
||||
|
||||
class HeaderControl extends StatefulWidget implements PreferredSizeWidget {
|
||||
const HeaderControl({
|
||||
@@ -440,6 +438,10 @@ class _HeaderControlState extends State<HeaderControl> {
|
||||
|
||||
/// 选择画质
|
||||
void showSetVideoQa() {
|
||||
if (videoInfo.dash == null) {
|
||||
SmartDialog.showToast('当前视频不支持选择画质');
|
||||
return;
|
||||
}
|
||||
final List<FormatItem> videoFormat = videoInfo.supportFormats!;
|
||||
final VideoQuality currentVideoQa = widget.videoDetailCtr!.currentVideoQa;
|
||||
|
||||
@@ -634,9 +636,13 @@ class _HeaderControlState extends State<HeaderControl> {
|
||||
final VideoItem firstVideo = widget.videoDetailCtr!.firstVideo;
|
||||
// 当前视频可用的解码格式
|
||||
final List<FormatItem> videoFormat = videoInfo.supportFormats!;
|
||||
final List list = videoFormat
|
||||
final List? list = videoFormat
|
||||
.firstWhere((FormatItem e) => e.quality == firstVideo.quality!.code)
|
||||
.codecs!;
|
||||
.codecs;
|
||||
if (list == null) {
|
||||
SmartDialog.showToast('当前视频不支持选择解码格式');
|
||||
return;
|
||||
}
|
||||
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
|
||||
Reference in New Issue
Block a user