Files
PiliPlus/lib/pages/setting/models/play_settings.dart
2026-04-07 12:56:29 +08:00

342 lines
11 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'dart:io';
import 'package:PiliPlus/common/widgets/custom_icon.dart';
import 'package:PiliPlus/models/common/super_chat_type.dart';
import 'package:PiliPlus/models/common/video/subtitle_pref_type.dart';
import 'package:PiliPlus/pages/main/controller.dart';
import 'package:PiliPlus/pages/setting/models/model.dart';
import 'package:PiliPlus/pages/setting/widgets/select_dialog.dart';
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/play_repeat.dart';
import 'package:PiliPlus/services/service_locator.dart';
import 'package:PiliPlus/utils/platform_utils.dart';
import 'package:PiliPlus/utils/storage.dart';
import 'package:PiliPlus/utils/storage_key.dart';
import 'package:PiliPlus/utils/storage_pref.dart';
import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
List<SettingsModel> get playSettings => [
const SwitchModel(
title: '弹幕开关',
subtitle: '是否展示弹幕',
leading: Icon(CustomIcons.dm_settings),
setKey: SettingBoxKey.enableShowDanmaku,
defaultVal: true,
),
if (PlatformUtils.isMobile)
const SwitchModel(
title: '启用点击弹幕',
subtitle: '点击弹幕悬停,支持点赞、复制、举报操作',
leading: Icon(Icons.touch_app_outlined),
setKey: SettingBoxKey.enableTapDm,
defaultVal: true,
),
NormalModel(
onTap: (context, setState) => Get.toNamed('/playSpeedSet'),
leading: const Icon(Icons.speed_outlined),
title: '倍速设置',
subtitle: '设置视频播放速度',
),
const SwitchModel(
title: '自动播放',
subtitle: '进入详情页自动播放',
leading: Icon(Icons.motion_photos_auto_outlined),
setKey: SettingBoxKey.autoPlayEnable,
defaultVal: false,
),
const SwitchModel(
title: '全屏显示锁定按钮',
leading: Icon(Icons.lock_outline),
setKey: SettingBoxKey.showFsLockBtn,
defaultVal: true,
),
const SwitchModel(
title: '全屏显示截图按钮',
leading: Icon(Icons.photo_camera_outlined),
setKey: SettingBoxKey.showFsScreenshotBtn,
defaultVal: true,
),
SwitchModel(
title: '全屏显示电池电量',
leading: const Icon(Icons.battery_3_bar),
setKey: SettingBoxKey.showBatteryLevel,
defaultVal: PlatformUtils.isMobile,
),
const SwitchModel(
title: '双击快退/快进',
subtitle: '左侧双击快退/右侧双击快进,关闭则双击均为暂停/播放',
leading: Icon(Icons.touch_app_outlined),
setKey: SettingBoxKey.enableQuickDouble,
defaultVal: true,
),
const SwitchModel(
title: '左右侧滑动调节亮度/音量',
leading: Icon(MdiIcons.tuneVerticalVariant),
setKey: SettingBoxKey.enableSlideVolumeBrightness,
defaultVal: true,
),
if (Platform.isAndroid)
const SwitchModel(
title: '调节系统亮度',
leading: Icon(Icons.brightness_6_outlined),
setKey: SettingBoxKey.setSystemBrightness,
defaultVal: false,
),
const SwitchModel(
title: '中间滑动进入/退出全屏',
leading: Icon(MdiIcons.panVertical),
setKey: SettingBoxKey.enableSlideFS,
defaultVal: true,
),
getVideoFilterSelectModel(
title: '双击快进/快退时长',
suffix: 's',
key: SettingBoxKey.fastForBackwardDuration,
values: [5, 10, 15],
defaultValue: 10,
isFilter: false,
),
const SwitchModel(
title: '滑动快进/快退使用相对时长',
leading: Icon(Icons.swap_horiz_outlined),
setKey: SettingBoxKey.useRelativeSlide,
defaultVal: false,
),
getVideoFilterSelectModel(
title: '滑动快进/快退时长',
subtitle: '从播放器一端滑到另一端的快进/快退时长',
suffix: Pref.useRelativeSlide ? '%' : 's',
key: SettingBoxKey.sliderDuration,
values: [25, 50, 90, 100],
defaultValue: 90,
isFilter: false,
),
NormalModel(
title: '自动启用字幕',
leading: const Icon(Icons.closed_caption_outlined),
getSubtitle: () => '当前选择偏好:${Pref.subtitlePreferenceV2.desc}',
onTap: _showSubtitleDialog,
),
if (PlatformUtils.isDesktop)
SwitchModel(
title: '最小化时暂停/还原时播放',
leading: const Icon(Icons.pause_circle_outline),
setKey: SettingBoxKey.pauseOnMinimize,
defaultVal: false,
onChanged: (value) {
try {
Get.find<MainController>().pauseOnMinimize = value;
} catch (_) {}
},
),
const SwitchModel(
title: '启用键盘控制',
leading: Icon(Icons.keyboard_alt_outlined),
setKey: SettingBoxKey.keyboardControl,
defaultVal: true,
),
NormalModel(
title: 'SuperChat (醒目留言) 显示类型',
leading: const Icon(Icons.live_tv),
getSubtitle: () => '当前:「${Pref.superChatType.title}',
onTap: _showSuperChatDialog,
),
const SwitchModel(
title: '竖屏扩大展示',
subtitle: '小屏竖屏视频宽高比由16:9扩大至1:1不支持收起横屏适配时扩大至9:16',
leading: Icon(Icons.expand_outlined),
setKey: SettingBoxKey.enableVerticalExpand,
defaultVal: false,
),
const SwitchModel(
title: '自动全屏',
subtitle: '视频开始播放时进入全屏',
leading: Icon(Icons.fullscreen_outlined),
setKey: SettingBoxKey.enableAutoEnter,
defaultVal: false,
),
const SwitchModel(
title: '自动退出全屏',
subtitle: '视频结束播放时退出全屏',
leading: Icon(Icons.fullscreen_exit_outlined),
setKey: SettingBoxKey.enableAutoExit,
defaultVal: true,
),
const SwitchModel(
title: '延长播放控件显示时间',
subtitle: '开启后延长至30秒便于屏幕阅读器滑动切换控件焦点',
leading: Icon(Icons.timer_outlined),
setKey: SettingBoxKey.enableLongShowControl,
defaultVal: false,
),
if (PlatformUtils.isMobile)
const SwitchModel(
title: '后台播放',
subtitle: '进入后台时继续播放',
leading: Icon(Icons.motion_photos_pause_outlined),
setKey: SettingBoxKey.continuePlayInBackground,
defaultVal: false,
),
if (Platform.isAndroid) ...[
SwitchModel(
title: '后台画中画',
subtitle: '进入后台时以小窗形式PiP播放',
leading: const Icon(Icons.picture_in_picture_outlined),
setKey: SettingBoxKey.autoPiP,
defaultVal: false,
onChanged: (val) {
if (val && !videoPlayerServiceHandler!.enableBackgroundPlay) {
SmartDialog.showToast('建议开启后台音频服务');
}
},
),
const SwitchModel(
title: '画中画不加载弹幕',
subtitle: '当弹幕开关开启时,小窗屏蔽弹幕以获得较好的体验',
leading: Icon(CustomIcons.dm_off),
setKey: SettingBoxKey.pipNoDanmaku,
defaultVal: false,
),
],
const SwitchModel(
title: '全屏手势反向',
subtitle: '默认播放器中部向上滑动进入全屏,向下退出\n开启后向下全屏,向上退出',
leading: Icon(Icons.swap_vert),
setKey: SettingBoxKey.fullScreenGestureReverse,
defaultVal: false,
),
const SwitchModel(
title: '全屏展示点赞/投币/收藏等操作按钮',
leading: Icon(MdiIcons.dotsHorizontalCircleOutline),
setKey: SettingBoxKey.showFSActionItem,
defaultVal: true,
),
const SwitchModel(
title: '观看人数',
subtitle: '展示同时在看人数',
leading: Icon(Icons.people_outlined),
setKey: SettingBoxKey.enableOnlineTotal,
defaultVal: false,
),
NormalModel(
title: '默认全屏方向',
leading: const Icon(Icons.open_with_outlined),
getSubtitle: () => '当前全屏方向:${Pref.fullScreenMode.desc}',
onTap: _showFullScreenModeDialog,
),
NormalModel(
title: '底部进度条展示',
leading: const Icon(Icons.border_bottom_outlined),
getSubtitle: () => '当前展示方式:${Pref.btmProgressBehavior.desc}',
onTap: _showProgressBehaviorDialog,
),
if (PlatformUtils.isMobile)
SwitchModel(
title: '后台音频服务',
subtitle: '避免画中画没有播放暂停功能',
leading: const Icon(Icons.volume_up_outlined),
setKey: SettingBoxKey.enableBackgroundPlay,
defaultVal: true,
onChanged: (value) =>
videoPlayerServiceHandler!.enableBackgroundPlay = value,
),
PopupModel(
title: '播放顺序',
leading: const Icon(Icons.repeat),
value: () => Pref.playRepeat,
items: PlayRepeat.values,
onSelected: (value, setState) => GStorage.video
.put(VideoBoxKey.playRepeat, value.index)
.whenComplete(setState),
),
const SwitchModel(
title: '播放器设置仅对当前生效',
subtitle: '弹幕、字幕及部分设置中没有的设置除外',
leading: Icon(Icons.video_settings_outlined),
setKey: SettingBoxKey.tempPlayerConf,
defaultVal: false,
),
];
Future<void> _showSubtitleDialog(
BuildContext context,
VoidCallback setState,
) async {
final res = await showDialog<SubtitlePrefType>(
context: context,
builder: (context) => SelectDialog<SubtitlePrefType>(
title: '字幕选择偏好',
value: Pref.subtitlePreferenceV2,
values: SubtitlePrefType.values.map((e) => (e, e.desc)).toList(),
),
);
if (res != null) {
await GStorage.setting.put(
SettingBoxKey.subtitlePreferenceV2,
res.index,
);
setState();
}
}
Future<void> _showSuperChatDialog(
BuildContext context,
VoidCallback setState,
) async {
final res = await showDialog<SuperChatType>(
context: context,
builder: (context) => SelectDialog<SuperChatType>(
title: 'SuperChat (醒目留言) 显示类型',
value: Pref.superChatType,
values: SuperChatType.values.map((e) => (e, e.title)).toList(),
),
);
if (res != null) {
await GStorage.setting.put(SettingBoxKey.superChatType, res.index);
setState();
}
}
Future<void> _showFullScreenModeDialog(
BuildContext context,
VoidCallback setState,
) async {
final res = await showDialog<FullScreenMode>(
context: context,
builder: (context) => SelectDialog<FullScreenMode>(
title: '默认全屏方向',
value: Pref.fullScreenMode,
values: FullScreenMode.values.map((e) => (e, e.desc)).toList(),
),
);
if (res != null) {
await GStorage.setting.put(SettingBoxKey.fullScreenMode, res.index);
setState();
}
}
Future<void> _showProgressBehaviorDialog(
BuildContext context,
VoidCallback setState,
) async {
final res = await showDialog<BtmProgressBehavior>(
context: context,
builder: (context) => SelectDialog<BtmProgressBehavior>(
title: '底部进度条展示',
value: Pref.btmProgressBehavior,
values: BtmProgressBehavior.values.map((e) => (e, e.desc)).toList(),
),
);
if (res != null) {
await GStorage.setting.put(
SettingBoxKey.btmProgressBehavior,
res.index,
);
setState();
}
}