diff --git a/lib/models/video/play/subtitle.dart b/lib/models/video/play/subtitle.dart index 0c92b5443..d69352e1c 100644 --- a/lib/models/video/play/subtitle.dart +++ b/lib/models/video/play/subtitle.dart @@ -3,8 +3,8 @@ enum SubtitlePreference { off, on, withoutAi } extension SubtitlePreferenceDesc on SubtitlePreference { static final List _descList = [ '默认不显示字幕', - '选择第一个可用字幕', - '跳过自动生成(ai)字幕,选择第一个可用字幕' + '默认显示,优先选择非自动生成(ai)字幕', + '默认仅显示非自动生成(ai)字幕', ]; get description => _descList[index]; } diff --git a/lib/pages/setting/pages/play_speed_set.dart b/lib/pages/setting/pages/play_speed_set.dart index 5a0025e68..c1e95650a 100644 --- a/lib/pages/setting/pages/play_speed_set.dart +++ b/lib/pages/setting/pages/play_speed_set.dart @@ -21,6 +21,7 @@ class _PlaySpeedPageState extends State { late double longPressSpeedDefault; late List customSpeedsList; late bool enableAutoLongPressSpeed; + late bool enableLongPressSpeedIncrease; List> sheetMenu = [ { 'id': 1, @@ -75,6 +76,8 @@ class _PlaySpeedPageState extends State { sheetMenu[1] = newItem; }); } + enableLongPressSpeedIncrease = settingStorage + .get(SettingBoxKey.enableLongPressSpeedIncrease, defaultValue: false); } // 添加自定义倍速 @@ -114,6 +117,7 @@ class _PlaySpeedPageState extends State { customSpeedsList.add(customSpeed); await videoStorage.put( VideoBoxKey.customSpeedsList, customSpeedsList); + PlPlayerController.updateSettingsIfExist(); setState(() {}); Get.back(); }, @@ -197,6 +201,7 @@ class _PlaySpeedPageState extends State { customSpeedsList.removeAt(index); await videoStorage.put(VideoBoxKey.customSpeedsList, customSpeedsList); } + PlPlayerController.updateSettingsIfExist(); setState(() {}); SmartDialog.showToast('操作成功'); } @@ -218,18 +223,10 @@ class _PlaySpeedPageState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Padding( - padding: - const EdgeInsets.only(left: 14, right: 14, top: 6, bottom: 0), - child: Text( - '点击下方按钮设置默认(长按)倍速', - style: TextStyle(color: Theme.of(context).colorScheme.outline), - ), - ), ListTile( dense: false, title: - Text('默认倍速', style: Theme.of(context).textTheme.titleMedium), + Text('当前默认倍速', style: Theme.of(context).textTheme.titleMedium), subtitle: Text(playSpeedDefault.toString()), ), SetSwitchItem( @@ -244,6 +241,7 @@ class _PlaySpeedPageState extends State { sheetMenu[1] = newItem; enableAutoLongPressSpeed = val; }); + PlPlayerController.updateSettingsIfExist(); }, ), !enableAutoLongPressSpeed @@ -254,6 +252,21 @@ class _PlaySpeedPageState extends State { subtitle: Text(longPressSpeedDefault.toString()), ) : const SizedBox(), + const SetSwitchItem( + title: '长按倍速递增', + subTitle: '每长按半秒,倍速*1.15,最大8倍速', + setKey: SettingBoxKey.enableLongPressSpeedIncrease, + defaultVal: false, + callFn: PlPlayerController.updateSettingsIfExist, + ), + Padding( + padding: + const EdgeInsets.only(left: 14, right: 14, top: 6, bottom: 0), + child: Text( + '点击下方按钮设置默认倍速、默认长按倍速', + style: TextStyle(color: Theme.of(context).colorScheme.outline), + ), + ), Padding( padding: const EdgeInsets.only( left: 14, @@ -335,6 +348,14 @@ class _PlaySpeedPageState extends State { ), ), ), + ListTile( + subtitle: Text( + '注:由于播放器性能限制,4k、8k视频以大于2倍速播放,可能会出现卡顿、音画不同步等问题,请酌情选择。', + style: TextStyle( + color: Theme.of(context).colorScheme.outline, + ), + ), + ) ], ), ), diff --git a/lib/pages/setting/play_setting.dart b/lib/pages/setting/play_setting.dart index f81ce649e..838dad476 100644 --- a/lib/pages/setting/play_setting.dart +++ b/lib/pages/setting/play_setting.dart @@ -72,6 +72,7 @@ class _PlaySettingState extends State { leading: Icon(Icons.comment_outlined), setKey: SettingBoxKey.enableShowDanmaku, defaultVal: true, + callFn: PlPlayerController.updateSettingsIfExist, ), ListTile( dense: false, @@ -86,6 +87,7 @@ class _PlaySettingState extends State { leading: Icon(MdiIcons.playPause), setKey: SettingBoxKey.autoPlayEnable, defaultVal: true, + callFn: PlPlayerController.updateSettingsIfExist, ), const SetSwitchItem( title: '左右侧双击快退/快进', @@ -141,6 +143,7 @@ class _PlaySettingState extends State { ); if (result != null) { setting.put(SettingBoxKey.subtitlePreference, result); + PlPlayerController.updateSettingsIfExist(); defaultSubtitlePreference = result; setState(() {}); } @@ -186,9 +189,7 @@ class _PlaySettingState extends State { leading: Icon(MdiIcons.locationExit), setKey: SettingBoxKey.continuePlayInBackground, defaultVal: false, - callFn: (val) { - SmartDialog.showToast('如果该设置未生效,请重启'); - }, + callFn: PlPlayerController.updateSettingsIfExist, ), const SetSwitchItem( title: '应用内小窗', diff --git a/lib/pages/setting/style_setting.dart b/lib/pages/setting/style_setting.dart index b782c15e6..7332bf155 100644 --- a/lib/pages/setting/style_setting.dart +++ b/lib/pages/setting/style_setting.dart @@ -88,7 +88,7 @@ class _StyleSettingState extends State { AutoOrientation.portraitUpMode(); SmartDialog.showToast('已关闭横屏适配'); } - PlPlayerController.updateSettings(); + PlPlayerController.updateSettingsIfExist(); }), // const SetSwitchItem( // title: '改用侧边栏', diff --git a/lib/plugin/pl_player/controller.dart b/lib/plugin/pl_player/controller.dart index acc45ff7e..447feefbd 100644 --- a/lib/plugin/pl_player/controller.dart +++ b/lib/plugin/pl_player/controller.dart @@ -88,7 +88,7 @@ class PlPlayerController { final Rx _showControls = false.obs; final Rx _showVolumeStatus = false.obs; final Rx _showBrightnessStatus = false.obs; - final Rx _doubleSpeedStatus = false.obs; + final RxDouble _doubleSpeedStatus = 0.0.obs; final Rx _controlsLock = false.obs; final Rx _isFullScreen = false.obs; // 默认投稿视频格式 @@ -247,8 +247,8 @@ class PlPlayerController { /// 镜像 Rx get flipX => _flipX; - /// 是否长按倍速 - Rx get doubleSpeedStatus => _doubleSpeedStatus; + /// 长按倍速值(0为非长按倍速) + RxDouble get doubleSpeedStatus => _doubleSpeedStatus; Rx isBuffering = true.obs; @@ -281,7 +281,8 @@ class PlPlayerController { late bool massiveMode; late List speedsList; // int? defaultDuration; - late bool enableAutoLongPressSpeed = false; + late bool enableAutoLongPressSpeed; + late bool enableLongPressSpeedIncrease; late bool enableLongShowControl; late bool horizontalScreen; @@ -330,17 +331,11 @@ class PlPlayerController { return _instance != null; } - static void updateSettings() { - _instance?.horizontalScreen = - setting.get(SettingBoxKey.horizontalScreen, defaultValue: false); - } - static Future playIfExists( {bool repeat = false, bool hideControls = true}) async { await _instance?.play(repeat: repeat, hideControls: hideControls); } - // try to get PlayerStatus static PlayerStatus? getPlayerStatusIfExists() { return _instance?.playerStatus.status.value; } @@ -365,9 +360,11 @@ class PlPlayerController { await _instance?.setVolume(volumeNew, videoPlayerVolume: videoPlayerVolume); } - // 添加一个私有构造函数 - PlPlayerController._() { - _videoType = videoType; + static void updateSettingsIfExist() { + _instance?.updateSettings(); + } + + void updateSettings() { isOpenDanmu.value = setting.get(SettingBoxKey.enableShowDanmaku, defaultValue: true); blockTypes = setting.get(SettingBoxKey.danmakuBlockType, defaultValue: []); @@ -403,14 +400,16 @@ class PlPlayerController { .toDouble(); enableAutoLongPressSpeed = setting .get(SettingBoxKey.enableAutoLongPressSpeed, defaultValue: false); - // 后台播放 - _continuePlayInBackground.value = setting - .get(SettingBoxKey.continuePlayInBackground, defaultValue: false); + enableLongPressSpeedIncrease = setting + .get(SettingBoxKey.enableLongPressSpeedIncrease, defaultValue: false); if (!enableAutoLongPressSpeed) { _longPressSpeed.value = videoStorage .get(VideoBoxKey.longPressSpeedDefault, defaultValue: 3.0) .toDouble(); } + // 后台播放 + _continuePlayInBackground.value = setting + .get(SettingBoxKey.continuePlayInBackground, defaultValue: false); enableLongShowControl = setting.get(SettingBoxKey.enableLongShowControl, defaultValue: false); horizontalScreen = @@ -439,6 +438,12 @@ class PlPlayerController { speedsList.add(i.value); } speedsList.sort(); + } + + // 添加一个私有构造函数 + PlPlayerController._() { + _videoType = videoType; + updateSettings(); // _playerEventSubs = onPlayerStatusChanged.listen((PlayerStatus status) { // if (status == PlayerStatus.playing) { // WakelockPlus.enable(); @@ -560,32 +565,8 @@ class PlPlayerController { } await _initializePlayer(seekTo: seekTo); if (videoType.value != 'live' && _cid != 0) { - refreshSubtitles().then((value) { - if (_vttSubtitles.isNotEmpty) { - if (_vttSubtitlesIndex > 0 && - _vttSubtitlesIndex < _vttSubtitles.length) { - setSubtitle(_vttSubtitlesIndex.value); - } else { - String preference = setting.get(SettingBoxKey.subtitlePreference, - defaultValue: SubtitlePreference.values.first.code); - if (preference == 'on') { - setSubtitle(1); - } else if (preference == 'withoutAi') { - bool found = false; - for (int i = 1; i < _vttSubtitles.length; i++) { - if (_vttSubtitles[i]['language']!.startsWith('ai')) { - continue; - } - found = true; - setSubtitle(i); - break; - } - if (!found) _vttSubtitlesIndex.value = 0; - } else { - _vttSubtitlesIndex.value = 0; - } - } - } + refreshSubtitles().then((_) { + chooseSubtitle(); }); } } catch (err, stackTrace) { @@ -964,7 +945,7 @@ class PlPlayerController { // danmakuController!.updateOption(updatedOption); // } catch (_) {} // fix 长按倍速后放开不恢复 - if (!doubleSpeedStatus.value) { + if (doubleSpeedStatus.value == 0) { _playbackSpeed.value = speed; } } @@ -1267,12 +1248,23 @@ class PlPlayerController { if (controlsLock.value) { return; } - _doubleSpeedStatus.value = val; if (val) { - await setPlaybackSpeed( - enableAutoLongPressSpeed ? playbackSpeed * 2 : longPressSpeed); + _doubleSpeedStatus.value = + enableAutoLongPressSpeed ? playbackSpeed * 2 : longPressSpeed; + await setPlaybackSpeed(_doubleSpeedStatus.value); + if (enableLongPressSpeedIncrease) { + Timer.periodic(const Duration(milliseconds: 500), (timer) async { + if (_doubleSpeedStatus.value > 0) { + _doubleSpeedStatus.value = min(8, _doubleSpeedStatus.value * 1.15); + await setPlaybackSpeed(_doubleSpeedStatus.value); + } else { + timer.cancel(); + } + }); + } } else { - print(playbackSpeed); + print("playbackSpeed: $playbackSpeed"); + _doubleSpeedStatus.value = 0; await setPlaybackSpeed(playbackSpeed); } } @@ -1654,6 +1646,45 @@ class PlPlayerController { return; } + void chooseSubtitle() { + if (_vttSubtitles.isEmpty) return; + + int findSubtitleWithoutAi() { + return _vttSubtitles.indexWhere((element) { + return !element['language']!.startsWith('ai'); + }); + } + + void setSubtitleFallback(int defaultIndex) { + int index = findSubtitleWithoutAi(); + setSubtitle(index != -1 ? index : defaultIndex); + } + + String preference = setting.get(SettingBoxKey.subtitlePreference, + defaultValue: SubtitlePreference.values.first.code); + + if (_vttSubtitlesIndex < 1 || _vttSubtitlesIndex >= _vttSubtitles.length) { + switch (preference) { + case 'on': + setSubtitleFallback(1); + break; + case 'withoutAi': + setSubtitleFallback(0); + break; + default: + setSubtitle(0); + } + return; + } + + if (_vttSubtitles[_vttSubtitlesIndex.value]['language']!.startsWith('ai')) { + setSubtitleFallback( + preference == 'withoutAi' ? 0 : _vttSubtitlesIndex.value); + } else { + setSubtitle(_vttSubtitlesIndex.value); + } + } + // 设定字幕轨道 setSubtitle(int index) { if (index == 0) { diff --git a/lib/plugin/pl_player/view.dart b/lib/plugin/pl_player/view.dart index e6d81a27f..b2f4089ac 100644 --- a/lib/plugin/pl_player/view.dart +++ b/lib/plugin/pl_player/view.dart @@ -842,7 +842,7 @@ class _PLVideoPlayerState extends State translation: const Offset(0.0, 0.3), // 上下偏移量(负数向上偏移) child: AnimatedOpacity( curve: Curves.easeInOut, - opacity: _.doubleSpeedStatus.value ? 1.0 : 0.0, + opacity: _.doubleSpeedStatus.value > 0 ? 1.0 : 0.0, duration: const Duration(milliseconds: 150), child: Container( alignment: Alignment.center, @@ -851,10 +851,12 @@ class _PLVideoPlayerState extends State borderRadius: BorderRadius.circular(16.0), ), height: 32.0, - width: 70.0, + width: 85.0, child: Center( child: Obx(() => Text( - '${_.enableAutoLongPressSpeed ? _.playbackSpeed * 2 : _.longPressSpeed}倍速中', + _.doubleSpeedStatus.value > 0 + ? '${_.doubleSpeedStatus.value.toStringAsFixed(2)}倍速中' + : '${_.playbackSpeed}倍速', style: const TextStyle( color: Colors.white, fontSize: 13), )), diff --git a/lib/plugin/pl_player/widgets/bottom_control.dart b/lib/plugin/pl_player/widgets/bottom_control.dart index 451ddf0fc..b5824dc08 100644 --- a/lib/plugin/pl_player/widgets/bottom_control.dart +++ b/lib/plugin/pl_player/widgets/bottom_control.dart @@ -6,8 +6,7 @@ import 'package:get/get.dart'; import 'package:nil/nil.dart'; import 'package:PiliPalaX/plugin/pl_player/index.dart'; import 'package:PiliPalaX/utils/feed_back.dart'; -//dart-math -import 'dart:math' as math; +import 'dart:math'; import '../../../common/widgets/audio_video_progress_bar.dart'; @@ -32,9 +31,9 @@ class BottomControl extends StatelessWidget implements PreferredSizeWidget { double lastAnnouncedValue = -1; return Obx(() { final int value = _.sliderPositionSeconds.value; - final int max = _.durationSeconds.value; + final int durationSec = _.durationSeconds.value; final int buffer = _.bufferedSeconds.value; - if (value > max || max <= 0) { + if (value > durationSec || durationSec <= 0) { return nil; } bool isEquivalentFullScreen = _.isFullScreen.value || @@ -54,12 +53,12 @@ class BottomControl extends StatelessWidget implements PreferredSizeWidget { bottom: 3 + (isEquivalentFullScreen ? Get.height * 0.01 : 0)), child: Semantics( // label: '${(value / max * 100).round()}%', - value: '${(value / max * 100).round()}%', + value: '${(value / durationSec * 100).round()}%', // enabled: false, child: ProgressBar( progress: Duration(seconds: value), buffered: Duration(seconds: buffer), - total: Duration(seconds: max), + total: Duration(seconds: durationSec), progressBarColor: colorTheme, baseBarColor: Colors.white.withOpacity(0.2), bufferedBarColor: colorTheme.withOpacity(0.4), @@ -74,7 +73,7 @@ class BottomControl extends StatelessWidget implements PreferredSizeWidget { _.onChangedSliderStart(); }, onDragUpdate: (duration) { - double newProgress = duration.timeStamp.inSeconds / max; + double newProgress = duration.timeStamp.inSeconds / durationSec; if ((newProgress - lastAnnouncedValue).abs() > 0.02) { accessibilityDebounce?.cancel(); accessibilityDebounce = @@ -93,7 +92,7 @@ class BottomControl extends StatelessWidget implements PreferredSizeWidget { _.seekTo(Duration(seconds: duration.inSeconds), type: 'slider'); SemanticsService.announce( - "${(duration.inSeconds / max * 100).round()}%", + "${(duration.inSeconds / durationSec * 100).round()}%", TextDirection.ltr); }, )), @@ -104,7 +103,7 @@ class BottomControl extends StatelessWidget implements PreferredSizeWidget { ), const SizedBox(height: 6), if (isEquivalentFullScreen) - SizedBox(height: math.max(Get.height * 0.08 - 15, 0)), + SizedBox(height: max(Get.height * 0.08 - 15, 0)), ], ), ); diff --git a/lib/utils/storage.dart b/lib/utils/storage.dart index deb935c4e..ca3e79bcb 100644 --- a/lib/utils/storage.dart +++ b/lib/utils/storage.dart @@ -146,6 +146,7 @@ class SettingBoxKey { autoPiP = 'autoPiP', pipNoDanmaku = 'pipNoDanmaku', enableAutoLongPressSpeed = 'enableAutoLongPressSpeed', + enableLongPressSpeedIncrease = 'enableLongPressSpeedIncrease', subtitlePreference = 'subtitlePreference', playerGestureActionMap = 'playerGestureActionMap', // youtube 双击快进快退