diff --git a/lib/models/common/video/audio_quality.dart b/lib/models/common/video/audio_quality.dart index 3398a9329..6adae89bd 100644 --- a/lib/models/common/video/audio_quality.dart +++ b/lib/models/common/video/audio_quality.dart @@ -1,9 +1,9 @@ enum AudioQuality { - k64(30216, '64K'), - k132(30232, '132K'), - k192(30280, '192K'), + hiRes(30251, 'Hi-Res无损'), dolby(30250, '杜比全景声'), - hiRes(30251, 'Hi-Res无损'); + k192(30280, '192K'), + k132(30232, '132K'), + k64(30216, '64K'); final int code; final String desc; diff --git a/lib/models/common/video/video_quality.dart b/lib/models/common/video/video_quality.dart index 362ceaaad..ac5bfee4f 100644 --- a/lib/models/common/video/video_quality.dart +++ b/lib/models/common/video/video_quality.dart @@ -1,21 +1,22 @@ enum VideoQuality { - speed240(6, '240P 极速'), - fluent360(16, '360P 流畅'), - clear480(32, '480P 清晰'), - high720(64, '720P 高清'), - high72060(74, '720P60 高帧率'), - high1080(80, '1080P 高清'), - high1080plus(112, '1080P+ 高码率'), - high108060(116, '1080P60 高帧率'), - super4K(120, '4K 超清'), - hdr(125, 'HDR 真彩色'), - dolbyVision(126, '杜比视界'), - super8k(127, '8K 超高清'); + super8k(127, '8K 超高清', '8K'), + dolbyVision(126, '杜比视界', '杜比'), + hdr(125, 'HDR 真彩色', 'HDR'), + super4K(120, '4K 超清', '4K'), + high108060(116, '1080P60 高帧率', '1080P60'), + high1080plus(112, '1080P+ 高码率', '1080P+'), + high1080(80, '1080P 高清', '1080P'), + high72060(74, '720P60 高帧率', '720P60'), + high720(64, '720P 高清', '720P'), + clear480(32, '480P 清晰', '480P'), + fluent360(16, '360P 流畅', '360P'), + speed240(6, '240P 极速', '240P'); final int code; final String desc; + final String shortDesc; - const VideoQuality(this.code, this.desc); + const VideoQuality(this.code, this.desc, this.shortDesc); static final _codeMap = {for (var i in values) i.code: i}; diff --git a/lib/pages/setting/models/video_settings.dart b/lib/pages/setting/models/video_settings.dart index f772ded1d..2dd73cf5c 100644 --- a/lib/pages/setting/models/video_settings.dart +++ b/lib/pages/setting/models/video_settings.dart @@ -107,9 +107,7 @@ List get videoSettings => [ return SelectDialog( title: '默认画质', value: Pref.defaultVideoQa, - values: VideoQuality.values.reversed - .map((e) => (e.code, e.desc)) - .toList(), + values: VideoQuality.values.map((e) => (e.code, e.desc)).toList(), ); }, ); @@ -132,9 +130,7 @@ List get videoSettings => [ return SelectDialog( title: '蜂窝网络画质', value: Pref.defaultVideoQaCellular, - values: VideoQuality.values.reversed - .map((e) => (e.code, e.desc)) - .toList(), + values: VideoQuality.values.map((e) => (e.code, e.desc)).toList(), ); }, ); @@ -160,9 +156,7 @@ List get videoSettings => [ return SelectDialog( title: '默认音质', value: Pref.defaultAudioQa, - values: AudioQuality.values.reversed - .map((e) => (e.code, e.desc)) - .toList(), + values: AudioQuality.values.map((e) => (e.code, e.desc)).toList(), ); }, ); @@ -185,9 +179,7 @@ List get videoSettings => [ return SelectDialog( title: '蜂窝网络音质', value: Pref.defaultAudioQaCellular, - values: AudioQuality.values.reversed - .map((e) => (e.code, e.desc)) - .toList(), + values: AudioQuality.values.map((e) => (e.code, e.desc)).toList(), ); }, ); diff --git a/lib/pages/video/controller.dart b/lib/pages/video/controller.dart index c2d8a0b89..52424d7ae 100644 --- a/lib/pages/video/controller.dart +++ b/lib/pages/video/controller.dart @@ -98,7 +98,7 @@ class VideoDetailController extends GetxController final Rx videoState = LoadingState.loading().obs; /// 播放器配置 画质 音质 解码格式 - late VideoQuality currentVideoQa; + late Rx currentVideoQa; AudioQuality? currentAudioQa; late VideoDecodeFormatType currentDecodeFormats; // 是否开始自动播放 存在多p的情况下,第二p需要为true @@ -1004,7 +1004,7 @@ class VideoDetailController extends GetxController /// 根据currentVideoQa和currentDecodeFormats 重新设置videoUrl List videoList = data.dash!.video! - .where((i) => i.id == currentVideoQa.code) + .where((i) => i.id == currentVideoQa.value.code) .toList(); final List supportDecodeFormats = videoList @@ -1036,7 +1036,7 @@ class VideoDetailController extends GetxController orElse: () => videoList.first, ); } else { - if (currentVideoQa == VideoQuality.dolbyVision) { + if (currentVideoQa.value == VideoQuality.dolbyVision) { currentDecodeFormats = VideoDecodeFormatTypeExt.fromString( videoList.first.codecs!, )!; @@ -1203,7 +1203,7 @@ class VideoDetailController extends GetxController ); setVideoHeight(); currentDecodeFormats = VideoDecodeFormatTypeExt.fromString('avc1')!; - currentVideoQa = VideoQuality.fromCode(data.quality!); + currentVideoQa = Rx(VideoQuality.fromCode(data.quality!)); if (autoPlay.value || plPlayerController.preInitPlayer) { await playerInit(); } @@ -1236,7 +1236,7 @@ class VideoDetailController extends GetxController numbers, ); } - currentVideoQa = VideoQuality.fromCode(resVideoQa); + currentVideoQa = Rx(VideoQuality.fromCode(resVideoQa)); /// 取出符合当前画质的videoList final List videosList = allVideosList diff --git a/lib/pages/video/reply/widgets/reply_item_grpc.dart b/lib/pages/video/reply/widgets/reply_item_grpc.dart index 7a37b6795..88f09f377 100644 --- a/lib/pages/video/reply/widgets/reply_item_grpc.dart +++ b/lib/pages/video/reply/widgets/reply_item_grpc.dart @@ -740,17 +740,9 @@ class ReplyItemGrpc extends StatelessWidget { final ctr = Get.find( tag: getTag?.call() ?? Get.arguments['heroTag'], ); - int duration = ctr.data.timeLength!; - List split = matchStr - .split(':') - .reversed - .map((item) => int.parse(item)) - .toList(); - int seek = 0; - for (int i = 0; i < split.length; i++) { - seek += split[i] * pow(60, i).toInt(); - } - isValid = seek * 1000 <= duration; + isValid = + ctr.data.timeLength! * 1000 <= + DurationUtil.parseDuration(matchStr); } catch (e) { if (kDebugMode) debugPrint('failed to validate: $e'); } diff --git a/lib/pages/video/widgets/header_control.dart b/lib/pages/video/widgets/header_control.dart index 0863cd2e9..a713f463e 100644 --- a/lib/pages/video/widgets/header_control.dart +++ b/lib/pages/video/widgets/header_control.dart @@ -361,7 +361,7 @@ class HeaderControlState extends TripleState { leading: const Icon(Icons.play_circle_outline, size: 20), title: const Text('选择画质', style: titleStyle), subtitle: Text( - '当前画质 ${videoDetailCtr.currentVideoQa.desc}', + '当前画质 ${videoDetailCtr.currentVideoQa.value.desc}', style: subTitleStyle, ), ), @@ -604,7 +604,7 @@ class HeaderControlState extends TripleState { return; } final List videoFormat = videoInfo.supportFormats!; - final VideoQuality currentVideoQa = videoDetailCtr.currentVideoQa; + final VideoQuality currentVideoQa = videoDetailCtr.currentVideoQa.value; /// 总质量分类 final int totalQaSam = videoFormat.length; @@ -663,10 +663,13 @@ class HeaderControlState extends TripleState { } Get.back(); final int quality = item.quality!; + final newQa = VideoQuality.fromCode(quality); videoDetailCtr - ..currentVideoQa = VideoQuality.fromCode(quality) + ..currentVideoQa.value = newQa ..updatePlayer(); + SmartDialog.showToast("画质已变为:${newQa.desc}"); + // update if (!plPlayerController.tempPlayerConf) { final res = await Connectivity().checkConnectivity(); @@ -682,9 +685,6 @@ class HeaderControlState extends TripleState { ); } } - SmartDialog.showToast( - "画质已变为:${VideoQuality.fromCode(quality).desc}", - ); }, // 可能包含会员解锁画质 enabled: index >= totalQaSam - userfulQaSam, @@ -740,10 +740,13 @@ class HeaderControlState extends TripleState { } Get.back(); final int quality = i.id!; + final newQa = AudioQuality.fromCode(quality); videoDetailCtr - ..currentAudioQa = AudioQuality.fromCode(quality) + ..currentAudioQa = newQa ..updatePlayer(); + SmartDialog.showToast("音质已变为:${newQa.desc}"); + // update if (!plPlayerController.tempPlayerConf) { final res = await Connectivity().checkConnectivity(); @@ -759,9 +762,6 @@ class HeaderControlState extends TripleState { ); } } - SmartDialog.showToast( - "音质已变为:${AudioQuality.fromCode(quality).desc}", - ); }, contentPadding: const EdgeInsets.only(left: 20, right: 20), title: Text(i.quality), diff --git a/lib/plugin/pl_player/models/bottom_control_type.dart b/lib/plugin/pl_player/models/bottom_control_type.dart index 187a1197a..4cd848902 100644 --- a/lib/plugin/pl_player/models/bottom_control_type.dart +++ b/lib/plugin/pl_player/models/bottom_control_type.dart @@ -1,6 +1,6 @@ enum BottomControlType { - pre, playOrPause, + pre, next, time, episode, @@ -11,4 +11,5 @@ enum BottomControlType { viewPoints, superResolution, dmChart, + qa, } diff --git a/lib/plugin/pl_player/view.dart b/lib/plugin/pl_player/view.dart index 3250af8af..fc543d06b 100644 --- a/lib/plugin/pl_player/view.dart +++ b/lib/plugin/pl_player/view.dart @@ -9,6 +9,8 @@ import 'package:PiliPlus/common/widgets/progress_bar/segment_progress_bar.dart'; import 'package:PiliPlus/common/widgets/view_safe_area.dart'; import 'package:PiliPlus/http/init.dart'; import 'package:PiliPlus/models/common/super_resolution_type.dart'; +import 'package:PiliPlus/models/common/video/video_quality.dart'; +import 'package:PiliPlus/models/video/play/url.dart'; import 'package:PiliPlus/models_new/video/video_detail/episode.dart'; import 'package:PiliPlus/models_new/video/video_detail/section.dart'; import 'package:PiliPlus/models_new/video/video_shot/data.dart'; @@ -31,6 +33,9 @@ import 'package:PiliPlus/plugin/pl_player/widgets/forward_seek.dart'; import 'package:PiliPlus/plugin/pl_player/widgets/play_pause_btn.dart'; import 'package:PiliPlus/utils/duration_util.dart'; import 'package:PiliPlus/utils/id_utils.dart'; +import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_key.dart'; +import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:dio/dio.dart'; import 'package:easy_debounce/easy_throttle.dart'; import 'package:fl_chart/fl_chart.dart'; @@ -400,7 +405,7 @@ class _PLVideoPlayerState extends State .toList(); }, child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 10), + padding: const EdgeInsets.symmetric(horizontal: 8), child: Text( plPlayerController.superResolutionType.value.title, style: const TextStyle(color: Colors.white, fontSize: 13), @@ -508,7 +513,7 @@ class _PLVideoPlayerState extends State .toList(); }, child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 10), + padding: const EdgeInsets.symmetric(horizontal: 8), child: Text( plPlayerController.videoFit.value.desc, style: const TextStyle(color: Colors.white, fontSize: 13), @@ -595,7 +600,7 @@ class _PLVideoPlayerState extends State .toList(); }, child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 10), + padding: const EdgeInsets.symmetric(horizontal: 8), child: Text( "${plPlayerController.playbackSpeed}X", style: const TextStyle(color: Colors.white, fontSize: 13), @@ -605,6 +610,88 @@ class _PLVideoPlayerState extends State ), ), + BottomControlType.qa => Obx( + () { + final videoDetailCtr = widget.videoDetailController!; + final VideoQuality currentVideoQa = + videoDetailCtr.currentVideoQa.value; + final PlayUrlModel videoInfo = videoDetailCtr.data; + final List videoFormat = videoInfo.supportFormats!; + final int totalQaSam = videoFormat.length; + int userfulQaSam = 0; + final List video = videoInfo.dash!.video!; + final Set idSet = {}; + for (final VideoItem item in video) { + final int id = item.id!; + if (!idSet.contains(id)) { + idSet.add(id); + userfulQaSam++; + } + } + return PopupMenuButton( + requestFocus: false, + initialValue: currentVideoQa.code, + color: Colors.black.withValues(alpha: 0.8), + itemBuilder: (context) { + return List.generate( + totalQaSam, + (index) { + final item = videoFormat[index]; + final enabled = index >= totalQaSam - userfulQaSam; + return PopupMenuItem( + enabled: enabled, + height: 35, + padding: const EdgeInsets.only(left: 20), + value: item.quality, + onTap: () async { + if (currentVideoQa.code == item.quality) { + return; + } + final int quality = item.quality!; + final newQa = VideoQuality.fromCode(quality); + videoDetailCtr + ..currentVideoQa.value = newQa + ..updatePlayer(); + + SmartDialog.showToast("画质已变为:${newQa.desc}"); + + // update + if (!plPlayerController.tempPlayerConf) { + final res = await Connectivity().checkConnectivity(); + if (res.contains(ConnectivityResult.wifi)) { + GStorage.setting.put( + SettingBoxKey.defaultVideoQa, + quality, + ); + } else { + GStorage.setting.put( + SettingBoxKey.defaultVideoQaCellular, + quality, + ); + } + } + }, + child: Text( + item.newDesc ?? '', + style: enabled + ? const TextStyle(color: Colors.white, fontSize: 13) + : null, + ), + ); + }, + ); + }, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 8), + child: Text( + currentVideoQa.shortDesc, + style: const TextStyle(color: Colors.white, fontSize: 13), + ), + ), + ); + }, + ), + /// 全屏 BottomControlType.fullscreen => ComBtn( width: widgetWidth, @@ -644,6 +731,7 @@ class _PLVideoPlayerState extends State if (isFullScreen) BottomControlType.fit, BottomControlType.subtitle, BottomControlType.speed, + if (isFullScreen) BottomControlType.qa, BottomControlType.fullscreen, ]; diff --git a/lib/utils/page_utils.dart b/lib/utils/page_utils.dart index 88c204291..06e44e530 100644 --- a/lib/utils/page_utils.dart +++ b/lib/utils/page_utils.dart @@ -372,9 +372,9 @@ class PageUtils { EnableManual( aspectRatio: aspectRatio.fitsInAndroidRequirements ? aspectRatio - : width > height - ? const Rational.landscape() - : const Rational.vertical(), + : height > width + ? const Rational.vertical() + : const Rational.landscape(), ), ); } else { diff --git a/lib/utils/storage_pref.dart b/lib/utils/storage_pref.dart index 3f224067f..ba27284e4 100644 --- a/lib/utils/storage_pref.dart +++ b/lib/utils/storage_pref.dart @@ -198,7 +198,7 @@ class Pref { static int get defaultVideoQa => _setting.get( SettingBoxKey.defaultVideoQa, - defaultValue: VideoQuality.values.last.code, + defaultValue: VideoQuality.super8k.code, ); static int get defaultVideoQaCellular => _setting.get( @@ -208,7 +208,7 @@ class Pref { static int get defaultAudioQa => _setting.get( SettingBoxKey.defaultAudioQa, - defaultValue: AudioQuality.values.last.code, + defaultValue: AudioQuality.hiRes.code, ); static int get defaultAudioQaCellular => _setting.get(