Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
bggRGjQaUbCoE
2025-07-23 16:47:11 +08:00
parent 148e0872b4
commit 418a1e8d39
821 changed files with 29467 additions and 25520 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -10,34 +10,34 @@ import 'package:get/get.dart';
extension SettingsModelExt on SettingsModel {
Widget get widget => switch (settingsType) {
SettingsType.normal => NormalItem(
title: title,
getTitle: getTitle,
subtitle: subtitle,
getSubtitle: getSubtitle,
setKey: setKey,
defaultVal: defaultVal,
onChanged: onChanged,
needReboot: needReboot,
leading: leading,
getTrailing: getTrailing,
onTap: onTap,
contentPadding: contentPadding,
titleStyle: titleStyle,
),
SettingsType.sw1tch => SetSwitchItem(
title: title,
subtitle: subtitle,
setKey: setKey,
defaultVal: defaultVal,
onChanged: onChanged,
needReboot: needReboot,
leading: leading,
onTap: onTap,
contentPadding: contentPadding,
titleStyle: titleStyle,
),
};
SettingsType.normal => NormalItem(
title: title,
getTitle: getTitle,
subtitle: subtitle,
getSubtitle: getSubtitle,
setKey: setKey,
defaultVal: defaultVal,
onChanged: onChanged,
needReboot: needReboot,
leading: leading,
getTrailing: getTrailing,
onTap: onTap,
contentPadding: contentPadding,
titleStyle: titleStyle,
),
SettingsType.sw1tch => SetSwitchItem(
title: title,
subtitle: subtitle,
setKey: setKey,
defaultVal: defaultVal,
onChanged: onChanged,
needReboot: needReboot,
leading: leading,
onTap: onTap,
contentPadding: contentPadding,
titleStyle: titleStyle,
),
};
}
class SettingsModel {
@@ -104,7 +104,7 @@ SettingsModel getBanwordModel({
minLines: 1,
maxLines: 4,
onChanged: (value) => banWord = value,
)
),
],
),
actions: [
@@ -112,8 +112,9 @@ SettingsModel getBanwordModel({
onPressed: Get.back,
child: Text(
'取消',
style:
TextStyle(color: Theme.of(context).colorScheme.outline),
style: TextStyle(
color: Theme.of(context).colorScheme.outline,
),
),
),
TextButton(
@@ -154,22 +155,26 @@ SettingsModel getVideoFilterSelectModel({
subtitle: subtitle,
getSubtitle: subtitle == null
? () => isFilter
? '过滤掉$title小于$value${suffix ?? ""}」的视频'
: '当前$title:「$value${suffix ?? ""}'
? '过滤掉$title小于$value${suffix ?? ""}」的视频'
: '当前$title:「$value${suffix ?? ""}'
: null,
onTap: (setState) async {
var result = await showDialog<int>(
context: context,
builder: (context) {
return SelectDialog<int>(
title: '选择$title${isFilter ? '0即不过滤' : ''}',
value: value,
values: (values
..addIf(!values.contains(value), value)
..sort())
.map((e) => (e, suffix == null ? e.toString() : '$e $suffix'))
.toList()
..add((-1, '自定义')));
title: '选择$title${isFilter ? '0即不过滤' : ''}',
value: value,
values:
(values
..addIf(!values.contains(value), value)
..sort())
.map(
(e) => (e, suffix == null ? e.toString() : '$e $suffix'),
)
.toList()
..add((-1, '自定义')),
);
},
);
if (result != null) {
@@ -193,7 +198,8 @@ SettingsModel getVideoFilterSelectModel({
child: Text(
'取消',
style: TextStyle(
color: Theme.of(context).colorScheme.outline),
color: Theme.of(context).colorScheme.outline,
),
),
),
TextButton(

View File

@@ -19,247 +19,249 @@ import 'package:get/get.dart';
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
List<SettingsModel> get playSettings => [
SettingsModel(
settingsType: SettingsType.sw1tch,
title: '弹幕开关',
subtitle: '是否展示弹幕',
leading: const Icon(CustomIcon.dm_settings),
setKey: SettingBoxKey.enableShowDanmaku,
defaultVal: true,
),
SettingsModel(
settingsType: SettingsType.normal,
onTap: (setState) => Get.toNamed('/playSpeedSet'),
leading: const Icon(Icons.speed_outlined),
title: '倍速设置',
subtitle: '设置视频播放速度',
),
SettingsModel(
settingsType: SettingsType.sw1tch,
title: '自动播放',
subtitle: '进入详情页自动播放',
leading: const Icon(Icons.motion_photos_auto_outlined),
setKey: SettingBoxKey.autoPlayEnable,
defaultVal: false,
),
SettingsModel(
settingsType: SettingsType.sw1tch,
title: '双击快退/快进',
subtitle: '左侧双击快退/右侧双击快进,关闭则双击均为暂停/播放',
leading: const Icon(Icons.touch_app_outlined),
setKey: SettingBoxKey.enableQuickDouble,
defaultVal: true,
),
SettingsModel(
settingsType: SettingsType.sw1tch,
title: '左右侧滑动调节亮度/音量',
leading: const Icon(MdiIcons.tuneVerticalVariant),
setKey: SettingBoxKey.enableSlideVolumeBrightness,
defaultVal: true,
),
SettingsModel(
settingsType: SettingsType.sw1tch,
title: '中间滑动进入/退出全屏',
leading: const Icon(MdiIcons.panVertical),
setKey: SettingBoxKey.enableSlideFS,
defaultVal: true,
),
getVideoFilterSelectModel(
SettingsModel(
settingsType: SettingsType.sw1tch,
title: '弹幕开关',
subtitle: '是否展示弹幕',
leading: const Icon(CustomIcon.dm_settings),
setKey: SettingBoxKey.enableShowDanmaku,
defaultVal: true,
),
SettingsModel(
settingsType: SettingsType.normal,
onTap: (setState) => Get.toNamed('/playSpeedSet'),
leading: const Icon(Icons.speed_outlined),
title: '倍速设置',
subtitle: '设置视频播放速度',
),
SettingsModel(
settingsType: SettingsType.sw1tch,
title: '自动播放',
subtitle: '进入详情页自动播放',
leading: const Icon(Icons.motion_photos_auto_outlined),
setKey: SettingBoxKey.autoPlayEnable,
defaultVal: false,
),
SettingsModel(
settingsType: SettingsType.sw1tch,
title: '双击快退/快进',
subtitle: '左侧双击快退/右侧双击快进,关闭则双击均为暂停/播放',
leading: const Icon(Icons.touch_app_outlined),
setKey: SettingBoxKey.enableQuickDouble,
defaultVal: true,
),
SettingsModel(
settingsType: SettingsType.sw1tch,
title: '左右侧滑动调节亮度/音量',
leading: const Icon(MdiIcons.tuneVerticalVariant),
setKey: SettingBoxKey.enableSlideVolumeBrightness,
defaultVal: true,
),
SettingsModel(
settingsType: SettingsType.sw1tch,
title: '中间滑动进入/退出全屏',
leading: const Icon(MdiIcons.panVertical),
setKey: SettingBoxKey.enableSlideFS,
defaultVal: true,
),
getVideoFilterSelectModel(
context: Get.context!,
title: '双击快进/快退时长',
suffix: 's',
key: SettingBoxKey.fastForBackwardDuration,
values: [5, 10, 15],
defaultValue: 10,
isFilter: false,
),
SettingsModel(
settingsType: SettingsType.sw1tch,
title: '滑动快进/快退使用相对时长',
leading: const Icon(Icons.swap_horiz_outlined),
setKey: SettingBoxKey.useRelativeSlide,
defaultVal: false,
),
getVideoFilterSelectModel(
context: Get.context!,
title: '滑动快进/快退时长',
subtitle: '从播放器一端滑到另一端的快进/快退时长',
suffix: Pref.useRelativeSlide ? '%' : 's',
key: SettingBoxKey.sliderDuration,
values: [25, 50, 90, 100],
defaultValue: 90,
isFilter: false,
),
SettingsModel(
settingsType: SettingsType.normal,
title: '自动启用字幕',
leading: const Icon(Icons.closed_caption_outlined),
getSubtitle: () =>
'当前选择偏好:${SubtitlePrefTypeExt.fromCode(Pref.subtitlePreference)!.description}',
onTap: (setState) async {
String? result = await showDialog(
context: Get.context!,
title: '双击快进/快退时长',
suffix: 's',
key: SettingBoxKey.fastForBackwardDuration,
values: [5, 10, 15],
defaultValue: 10,
isFilter: false,
),
SettingsModel(
settingsType: SettingsType.sw1tch,
title: '滑动快进/快退使用相对时长',
leading: const Icon(Icons.swap_horiz_outlined),
setKey: SettingBoxKey.useRelativeSlide,
defaultVal: false,
),
getVideoFilterSelectModel(
builder: (context) {
return SelectDialog<String>(
title: '字幕选择偏好',
value: Pref.subtitlePreference,
values: SubtitlePrefType.values
.map((e) => (e.code, e.description))
.toList(),
);
},
);
if (result != null) {
await GStorage.setting.put(SettingBoxKey.subtitlePreference, result);
setState();
}
},
),
SettingsModel(
settingsType: SettingsType.sw1tch,
title: '竖屏扩大展示',
subtitle: '小屏竖屏视频宽高比由16:9扩大至1:1不支持收起横屏适配时扩大至9:16',
leading: const Icon(Icons.expand_outlined),
setKey: SettingBoxKey.enableVerticalExpand,
defaultVal: false,
),
SettingsModel(
settingsType: SettingsType.sw1tch,
title: '自动全屏',
subtitle: '视频开始播放时进入全屏',
leading: const Icon(Icons.fullscreen_outlined),
setKey: SettingBoxKey.enableAutoEnter,
defaultVal: false,
),
SettingsModel(
settingsType: SettingsType.sw1tch,
title: '自动退出全屏',
subtitle: '视频结束播放时退出全屏',
leading: const Icon(Icons.fullscreen_exit_outlined),
setKey: SettingBoxKey.enableAutoExit,
defaultVal: true,
),
SettingsModel(
settingsType: SettingsType.sw1tch,
title: '延长播放控件显示时间',
subtitle: '开启后延长至30秒便于屏幕阅读器滑动切换控件焦点',
leading: const Icon(Icons.timer_outlined),
setKey: SettingBoxKey.enableLongShowControl,
defaultVal: false,
),
SettingsModel(
settingsType: SettingsType.sw1tch,
title: '全向旋转',
subtitle: '小屏可受重力转为临时全屏,若系统锁定旋转仍触发请关闭,关闭会影响横屏适配',
leading: const Icon(Icons.screen_rotation_alt_outlined),
setKey: SettingBoxKey.allowRotateScreen,
defaultVal: true,
onChanged: (value) => allowRotateScreen = value,
),
SettingsModel(
settingsType: SettingsType.sw1tch,
title: '后台播放',
subtitle: '进入后台时继续播放',
leading: const Icon(Icons.motion_photos_pause_outlined),
setKey: SettingBoxKey.continuePlayInBackground,
defaultVal: false,
),
if (Platform.isAndroid)
SettingsModel(
settingsType: SettingsType.sw1tch,
title: '后台画中画',
subtitle: '进入后台时以小窗形式PiP播放',
leading: const Icon(Icons.picture_in_picture_outlined),
setKey: SettingBoxKey.autoPiP,
defaultVal: false,
onChanged: (val) {
if (val && !videoPlayerServiceHandler.enableBackgroundPlay) {
SmartDialog.showToast('建议开启后台音频服务');
}
},
),
if (Platform.isAndroid)
SettingsModel(
settingsType: SettingsType.sw1tch,
title: '画中画不加载弹幕',
subtitle: '当弹幕开关开启时,小窗屏蔽弹幕以获得较好的体验',
leading: const Icon(Icons.subtitles_off_outlined),
setKey: SettingBoxKey.pipNoDanmaku,
defaultVal: false,
),
SettingsModel(
settingsType: SettingsType.sw1tch,
title: '全屏手势反向',
subtitle: '默认播放器中部向上滑动进入全屏,向下退出\n开启后向下全屏,向上退出',
leading: const Icon(Icons.swap_vert_outlined),
setKey: SettingBoxKey.fullScreenGestureReverse,
defaultVal: false,
),
SettingsModel(
settingsType: SettingsType.sw1tch,
title: '观看人数',
subtitle: '展示同时在看人数',
leading: const Icon(Icons.people_outlined),
setKey: SettingBoxKey.enableOnlineTotal,
defaultVal: false,
),
SettingsModel(
settingsType: SettingsType.normal,
title: '默认全屏方向',
leading: const Icon(Icons.open_with_outlined),
getSubtitle: () =>
'当前全屏方向:${FullScreenMode.values[Pref.fullScreenMode].desc}',
onTap: (setState) async {
int? result = await showDialog(
context: Get.context!,
title: '滑动快进/快退时长',
subtitle: '从播放器一端滑到另一端的快进/快退时长',
suffix: Pref.useRelativeSlide ? '%' : 's',
key: SettingBoxKey.sliderDuration,
values: [25, 50, 90, 100],
defaultValue: 90,
isFilter: false,
),
SettingsModel(
settingsType: SettingsType.normal,
title: '自动启用字幕',
leading: const Icon(Icons.closed_caption_outlined),
getSubtitle: () =>
'当前选择偏好:${SubtitlePrefTypeExt.fromCode(Pref.subtitlePreference)!.description}',
onTap: (setState) async {
String? result = await showDialog(
context: Get.context!,
builder: (context) {
return SelectDialog<String>(
title: '字幕选择偏好',
value: Pref.subtitlePreference,
values: SubtitlePrefType.values
.map((e) => (e.code, e.description))
.toList(),
);
},
builder: (context) {
return SelectDialog<int>(
title: '默认全屏方向',
value: Pref.fullScreenMode,
values: FullScreenMode.values.map((e) {
return (e.index, e.desc);
}).toList(),
);
if (result != null) {
await GStorage.setting
.put(SettingBoxKey.subtitlePreference, result);
setState();
}
},
),
SettingsModel(
settingsType: SettingsType.sw1tch,
title: '竖屏扩大展示',
subtitle: '小屏竖屏视频宽高比由16:9扩大至1:1不支持收起横屏适配时扩大至9:16',
leading: const Icon(Icons.expand_outlined),
setKey: SettingBoxKey.enableVerticalExpand,
defaultVal: false,
),
SettingsModel(
settingsType: SettingsType.sw1tch,
title: '自动全屏',
subtitle: '视频开始播放时进入全屏',
leading: const Icon(Icons.fullscreen_outlined),
setKey: SettingBoxKey.enableAutoEnter,
defaultVal: false,
),
SettingsModel(
settingsType: SettingsType.sw1tch,
title: '自动退出全屏',
subtitle: '视频结束播放时退出全屏',
leading: const Icon(Icons.fullscreen_exit_outlined),
setKey: SettingBoxKey.enableAutoExit,
defaultVal: true,
),
SettingsModel(
settingsType: SettingsType.sw1tch,
title: '延长播放控件显示时间',
subtitle: '开启后延长至30秒便于屏幕阅读器滑动切换控件焦点',
leading: const Icon(Icons.timer_outlined),
setKey: SettingBoxKey.enableLongShowControl,
defaultVal: false),
SettingsModel(
settingsType: SettingsType.sw1tch,
title: '全向旋转',
subtitle: '小屏可受重力转为临时全屏,若系统锁定旋转仍触发请关闭,关闭会影响横屏适配',
leading: const Icon(Icons.screen_rotation_alt_outlined),
setKey: SettingBoxKey.allowRotateScreen,
defaultVal: true,
onChanged: (value) => allowRotateScreen = value,
),
SettingsModel(
settingsType: SettingsType.sw1tch,
title: '后台播放',
subtitle: '进入后台时继续播放',
leading: const Icon(Icons.motion_photos_pause_outlined),
setKey: SettingBoxKey.continuePlayInBackground,
defaultVal: false,
),
if (Platform.isAndroid)
SettingsModel(
settingsType: SettingsType.sw1tch,
title: '后台画中画',
subtitle: '进入后台时以小窗形式PiP播放',
leading: const Icon(Icons.picture_in_picture_outlined),
setKey: SettingBoxKey.autoPiP,
defaultVal: false,
onChanged: (val) {
if (val && !videoPlayerServiceHandler.enableBackgroundPlay) {
SmartDialog.showToast('建议开启后台音频服务');
}
}),
if (Platform.isAndroid)
SettingsModel(
settingsType: SettingsType.sw1tch,
title: '画中画不加载弹幕',
subtitle: '当弹幕开关开启时,小窗屏蔽弹幕以获得较好的体验',
leading: const Icon(Icons.subtitles_off_outlined),
setKey: SettingBoxKey.pipNoDanmaku,
defaultVal: false,
),
SettingsModel(
settingsType: SettingsType.sw1tch,
title: '全屏手势反向',
subtitle: '默认播放器中部向上滑动进入全屏,向下退出\n开启后向下全屏,向上退出',
leading: const Icon(Icons.swap_vert_outlined),
setKey: SettingBoxKey.fullScreenGestureReverse,
defaultVal: false,
),
SettingsModel(
settingsType: SettingsType.sw1tch,
title: '观看人数',
subtitle: '展示同时在看人数',
leading: const Icon(Icons.people_outlined),
setKey: SettingBoxKey.enableOnlineTotal,
defaultVal: false,
),
SettingsModel(
settingsType: SettingsType.normal,
title: '默认全屏方向',
leading: const Icon(Icons.open_with_outlined),
getSubtitle: () =>
'当前全屏方向:${FullScreenMode.values[Pref.fullScreenMode].desc}',
onTap: (setState) async {
int? result = await showDialog(
context: Get.context!,
builder: (context) {
return SelectDialog<int>(
title: '默认全屏方向',
value: Pref.fullScreenMode,
values: FullScreenMode.values.map((e) {
return (e.index, e.desc);
}).toList());
},
);
if (result != null) {
await GStorage.setting.put(SettingBoxKey.fullScreenMode, result);
setState();
}
},
),
SettingsModel(
settingsType: SettingsType.normal,
title: '底部进度条展示',
leading: const Icon(Icons.border_bottom_outlined),
getSubtitle: () =>
'当前展示方式:${BtmProgressBehavior.values[Pref.btmProgressBehavior].desc}',
onTap: (setState) async {
int? result = await showDialog(
context: Get.context!,
builder: (context) {
return SelectDialog<int>(
title: '底部进度条展示',
value: Pref.btmProgressBehavior,
values: BtmProgressBehavior.values.map((e) {
return (e.index, e.desc);
}).toList(),
);
if (result != null) {
await GStorage.setting.put(SettingBoxKey.fullScreenMode, result);
setState();
}
},
),
SettingsModel(
settingsType: SettingsType.normal,
title: '底部进度条展示',
leading: const Icon(Icons.border_bottom_outlined),
getSubtitle: () =>
'当前展示方式:${BtmProgressBehavior.values[Pref.btmProgressBehavior].desc}',
onTap: (setState) async {
int? result = await showDialog(
context: Get.context!,
builder: (context) {
return SelectDialog<int>(
title: '底部进度条展示',
value: Pref.btmProgressBehavior,
values: BtmProgressBehavior.values.map((e) {
return (e.index, e.desc);
}).toList());
},
);
if (result != null) {
await GStorage.setting
.put(SettingBoxKey.btmProgressBehavior, result);
setState();
}
},
),
SettingsModel(
settingsType: SettingsType.sw1tch,
title: '后台音频服务',
subtitle: '避免画中画没有播放暂停功能',
leading: const Icon(Icons.volume_up_outlined),
setKey: SettingBoxKey.enableBackgroundPlay,
defaultVal: true,
onChanged: (value) {
videoPlayerServiceHandler.enableBackgroundPlay = value;
},
),
];
);
if (result != null) {
await GStorage.setting.put(SettingBoxKey.btmProgressBehavior, result);
setState();
}
},
),
SettingsModel(
settingsType: SettingsType.sw1tch,
title: '后台音频服务',
subtitle: '避免画中画没有播放暂停功能',
leading: const Icon(Icons.volume_up_outlined),
setKey: SettingBoxKey.enableBackgroundPlay,
defaultVal: true,
onChanged: (value) {
videoPlayerServiceHandler.enableBackgroundPlay = value;
},
),
];

View File

@@ -9,57 +9,56 @@ import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
List<SettingsModel> get privacySettings => [
SettingsModel(
settingsType: SettingsType.normal,
onTap: (setState) {
if (!Accounts.main.isLogin) {
SmartDialog.showToast('登录后查看');
return;
}
Get.toNamed('/blackListPage');
},
title: '黑名单管理',
subtitle: '已拉黑用户',
leading: const Icon(Icons.block),
),
SettingsModel(
settingsType: SettingsType.normal,
onTap: (setState) {
MineController.onChangeAnonymity();
setState();
},
leading: const Icon(Icons.privacy_tip_outlined),
getTitle: () => MineController.anonymity.value ? '退出无痕模式' : '进入无痕模式',
getSubtitle: () => MineController.anonymity.value
? '已进入无痕模式,搜索、观看视频/直播不携带Cookie与CSRF其余操作不受影响'
: '未开启无痕模式,将使用账户信息提供完整服务',
),
SettingsModel(
settingsType: SettingsType.normal,
onTap: (setState) {
showDialog(
context: Get.context!,
builder: (context) {
return AlertDialog(
title: const Text('查看详情'),
content: SingleChildScrollView(
child: Text(
AccountManager.apiTypeSet[AccountType.heartbeat]!
.join('\n'),
),
),
actions: [
TextButton(
onPressed: Get.back,
child: const Text('确认'),
)
],
);
},
SettingsModel(
settingsType: SettingsType.normal,
onTap: (setState) {
if (!Accounts.main.isLogin) {
SmartDialog.showToast('登录后查看');
return;
}
Get.toNamed('/blackListPage');
},
title: '黑名单管理',
subtitle: '已拉黑用户',
leading: const Icon(Icons.block),
),
SettingsModel(
settingsType: SettingsType.normal,
onTap: (setState) {
MineController.onChangeAnonymity();
setState();
},
leading: const Icon(Icons.privacy_tip_outlined),
getTitle: () => MineController.anonymity.value ? '退出无痕模式' : '进入无痕模式',
getSubtitle: () => MineController.anonymity.value
? '已进入无痕模式,搜索、观看视频/直播不携带Cookie与CSRF其余操作不受影响'
: '未开启无痕模式,将使用账户信息提供完整服务',
),
SettingsModel(
settingsType: SettingsType.normal,
onTap: (setState) {
showDialog(
context: Get.context!,
builder: (context) {
return AlertDialog(
title: const Text('查看详情'),
content: SingleChildScrollView(
child: Text(
AccountManager.apiTypeSet[AccountType.heartbeat]!.join('\n'),
),
),
actions: [
TextButton(
onPressed: Get.back,
child: const Text('确认'),
),
],
);
},
leading: const Icon(Icons.flag_outlined),
title: '了解无痕模式',
subtitle: '查看无痕模式作用的API列表',
),
];
);
},
leading: const Icon(Icons.flag_outlined),
title: '了解无痕模式',
subtitle: '查看无痕模式作用的API列表',
),
];

View File

@@ -9,118 +9,117 @@ import 'package:flutter/material.dart';
import 'package:get/get.dart';
List<SettingsModel> get recommendSettings => [
SettingsModel(
settingsType: SettingsType.sw1tch,
title: '首页使用app端推荐',
subtitle: '若web端推荐不太符合预期可尝试切换至app端推荐',
leading: const Icon(Icons.model_training_outlined),
setKey: SettingBoxKey.appRcmd,
defaultVal: true,
needReboot: true,
),
SettingsModel(
settingsType: SettingsType.sw1tch,
title: '推荐动态',
subtitle: '是否在推荐内容中展示动态(仅app端)',
leading: const Icon(Icons.motion_photos_on_outlined),
setKey: SettingBoxKey.enableRcmdDynamic,
defaultVal: true,
),
SettingsModel(
settingsType: SettingsType.sw1tch,
title: '保留首页推荐刷新',
subtitle: '下拉刷新时保留上次内容',
leading: const Icon(Icons.refresh),
setKey: SettingBoxKey.enableSaveLastData,
defaultVal: false,
onChanged: (value) {
try {
Get.find<RcmdController>().enableSaveLastData = value;
} catch (e) {
if (kDebugMode) debugPrint('$e');
}
},
),
SettingsModel(
settingsType: SettingsType.sw1tch,
title: '显示上次看到位置提示',
subtitle: '保留上次推荐时,在上次刷新位置显示提示',
leading: const Icon(Icons.tips_and_updates_outlined),
setKey: SettingBoxKey.savedRcmdTip,
defaultVal: true,
onChanged: (value) {
try {
RcmdController ctr = Get.find<RcmdController>()
..savedRcmdTip = value;
if (!value) {
ctr.lastRefreshAt = null;
}
} catch (e) {
if (kDebugMode) debugPrint('$e');
}
},
),
getVideoFilterSelectModel(
context: Get.context!,
title: '点赞率',
suffix: '%',
key: SettingBoxKey.minLikeRatioForRecommend,
values: [0, 1, 2, 3, 4],
onChanged: (value) => RecommendFilter.minLikeRatioForRecommend = value,
),
getBanwordModel(
context: Get.context!,
title: '标题关键词过滤',
key: SettingBoxKey.banWordForRecommend,
onChanged: (value) {
RecommendFilter.rcmdRegExp = value;
RecommendFilter.enableFilter = value.pattern.isNotEmpty;
},
),
getBanwordModel(
context: Get.context!,
title: 'App推荐/热门/排行榜: 视频分区关键词过滤',
key: SettingBoxKey.banWordForZone,
onChanged: (value) {
VideoHttp.zoneRegExp = value;
VideoHttp.enableFilter = value.pattern.isNotEmpty;
},
),
getVideoFilterSelectModel(
context: Get.context!,
title: '视频时长',
suffix: 's',
key: SettingBoxKey.minDurationForRcmd,
values: [0, 30, 60, 90, 120],
onChanged: (value) => RecommendFilter.minDurationForRcmd = value,
),
getVideoFilterSelectModel(
context: Get.context!,
title: '播放量',
key: SettingBoxKey.minPlayForRcmd,
values: [0, 50, 100, 500, 1000],
onChanged: (value) => RecommendFilter.minPlayForRcmd = value,
),
SettingsModel(
settingsType: SettingsType.sw1tch,
title: '已关注UP豁免推荐过滤',
subtitle: '推荐中已关注用户发布的内容不会被过滤',
leading: const Icon(Icons.favorite_border_outlined),
setKey: SettingBoxKey.exemptFilterForFollowed,
defaultVal: true,
onChanged: (value) {
RecommendFilter.exemptFilterForFollowed = value;
},
),
SettingsModel(
settingsType: SettingsType.sw1tch,
title: '过滤器也应用于相关视频',
subtitle: '视频详情页的相关视频也进行过滤¹',
leading: const Icon(Icons.explore_outlined),
setKey: SettingBoxKey.applyFilterToRelatedVideos,
defaultVal: true,
onChanged: (value) {
RecommendFilter.applyFilterToRelatedVideos = value;
},
),
];
SettingsModel(
settingsType: SettingsType.sw1tch,
title: '首页使用app端推荐',
subtitle: '若web端推荐不太符合预期可尝试切换至app端推荐',
leading: const Icon(Icons.model_training_outlined),
setKey: SettingBoxKey.appRcmd,
defaultVal: true,
needReboot: true,
),
SettingsModel(
settingsType: SettingsType.sw1tch,
title: '推荐动态',
subtitle: '是否在推荐内容中展示动态(仅app端)',
leading: const Icon(Icons.motion_photos_on_outlined),
setKey: SettingBoxKey.enableRcmdDynamic,
defaultVal: true,
),
SettingsModel(
settingsType: SettingsType.sw1tch,
title: '保留首页推荐刷新',
subtitle: '下拉刷新时保留上次内容',
leading: const Icon(Icons.refresh),
setKey: SettingBoxKey.enableSaveLastData,
defaultVal: false,
onChanged: (value) {
try {
Get.find<RcmdController>().enableSaveLastData = value;
} catch (e) {
if (kDebugMode) debugPrint('$e');
}
},
),
SettingsModel(
settingsType: SettingsType.sw1tch,
title: '显示上次看到位置提示',
subtitle: '保留上次推荐时,在上次刷新位置显示提示',
leading: const Icon(Icons.tips_and_updates_outlined),
setKey: SettingBoxKey.savedRcmdTip,
defaultVal: true,
onChanged: (value) {
try {
RcmdController ctr = Get.find<RcmdController>()..savedRcmdTip = value;
if (!value) {
ctr.lastRefreshAt = null;
}
} catch (e) {
if (kDebugMode) debugPrint('$e');
}
},
),
getVideoFilterSelectModel(
context: Get.context!,
title: '点赞率',
suffix: '%',
key: SettingBoxKey.minLikeRatioForRecommend,
values: [0, 1, 2, 3, 4],
onChanged: (value) => RecommendFilter.minLikeRatioForRecommend = value,
),
getBanwordModel(
context: Get.context!,
title: '标题关键词过滤',
key: SettingBoxKey.banWordForRecommend,
onChanged: (value) {
RecommendFilter.rcmdRegExp = value;
RecommendFilter.enableFilter = value.pattern.isNotEmpty;
},
),
getBanwordModel(
context: Get.context!,
title: 'App推荐/热门/排行榜: 视频分区关键词过滤',
key: SettingBoxKey.banWordForZone,
onChanged: (value) {
VideoHttp.zoneRegExp = value;
VideoHttp.enableFilter = value.pattern.isNotEmpty;
},
),
getVideoFilterSelectModel(
context: Get.context!,
title: '视频时长',
suffix: 's',
key: SettingBoxKey.minDurationForRcmd,
values: [0, 30, 60, 90, 120],
onChanged: (value) => RecommendFilter.minDurationForRcmd = value,
),
getVideoFilterSelectModel(
context: Get.context!,
title: '播放量',
key: SettingBoxKey.minPlayForRcmd,
values: [0, 50, 100, 500, 1000],
onChanged: (value) => RecommendFilter.minPlayForRcmd = value,
),
SettingsModel(
settingsType: SettingsType.sw1tch,
title: '已关注UP豁免推荐过滤',
subtitle: '推荐中已关注用户发布的内容不会被过滤',
leading: const Icon(Icons.favorite_border_outlined),
setKey: SettingBoxKey.exemptFilterForFollowed,
defaultVal: true,
onChanged: (value) {
RecommendFilter.exemptFilterForFollowed = value;
},
),
SettingsModel(
settingsType: SettingsType.sw1tch,
title: '过滤器也应用于相关视频',
subtitle: '视频详情页的相关视频也进行过滤¹',
leading: const Icon(Icons.explore_outlined),
setKey: SettingBoxKey.applyFilterToRelatedVideos,
defaultVal: true,
onChanged: (value) {
RecommendFilter.applyFilterToRelatedVideos = value;
},
),
];

File diff suppressed because it is too large Load Diff

View File

@@ -18,360 +18,367 @@ import 'package:get/get.dart';
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
List<SettingsModel> get videoSettings => [
SettingsModel(
settingsType: SettingsType.sw1tch,
title: '开启硬解',
subtitle: '以较低功耗播放视频,若异常卡死请关闭',
leading: const Icon(Icons.flash_on_outlined),
setKey: SettingBoxKey.enableHA,
defaultVal: true,
),
SettingsModel(
settingsType: SettingsType.sw1tch,
title: '免登录1080P',
subtitle: '免登录查看1080P视频',
leading: const Icon(Icons.hd_outlined),
setKey: SettingBoxKey.p1080,
defaultVal: true,
onChanged: (value) {
VideoHttp.p1080 = value;
},
),
SettingsModel(
settingsType: SettingsType.normal,
title: 'B站定向流量支持',
subtitle: '若套餐含B站定向流量则会自动使用。可查阅运营商的流量记录确认。',
leading: const Icon(Icons.perm_data_setting_outlined),
getTrailing: () => Transform.scale(
alignment: Alignment.centerRight,
scale: 0.8,
child: Switch(
thumbIcon: WidgetStateProperty.resolveWith<Icon?>(
(Set<WidgetState> states) {
if (states.isNotEmpty && states.first == WidgetState.selected) {
return const Icon(Icons.lock_outline_rounded);
}
return null;
}),
value: true,
onChanged: (_) {},
),
),
),
SettingsModel(
settingsType: SettingsType.normal,
title: 'CDN 设置',
leading: const Icon(MdiIcons.cloudPlusOutline),
getSubtitle: () =>
'当前使用:${CDNService.fromCode(VideoUtils.cdnService).desc},部分 CDN 可能失效,如无法播放请尝试切换',
onTap: (setState) async {
String? result = await showDialog(
context: Get.context!,
builder: (context) {
return const CdnSelectDialog();
},
);
if (result != null) {
VideoUtils.cdnService = result;
await GStorage.setting.put(SettingBoxKey.CDNService, result);
setState();
SettingsModel(
settingsType: SettingsType.sw1tch,
title: '开启硬解',
subtitle: '以较低功耗播放视频,若异常卡死请关闭',
leading: const Icon(Icons.flash_on_outlined),
setKey: SettingBoxKey.enableHA,
defaultVal: true,
),
SettingsModel(
settingsType: SettingsType.sw1tch,
title: '免登录1080P',
subtitle: '免登录查看1080P视频',
leading: const Icon(Icons.hd_outlined),
setKey: SettingBoxKey.p1080,
defaultVal: true,
onChanged: (value) {
VideoHttp.p1080 = value;
},
),
SettingsModel(
settingsType: SettingsType.normal,
title: 'B站定向流量支持',
subtitle: '若套餐含B站定向流量则会自动使用。可查阅运营商的流量记录确认。',
leading: const Icon(Icons.perm_data_setting_outlined),
getTrailing: () => Transform.scale(
alignment: Alignment.centerRight,
scale: 0.8,
child: Switch(
thumbIcon: WidgetStateProperty.resolveWith<Icon?>((
Set<WidgetState> states,
) {
if (states.isNotEmpty && states.first == WidgetState.selected) {
return const Icon(Icons.lock_outline_rounded);
}
return null;
}),
value: true,
onChanged: (_) {},
),
),
),
SettingsModel(
settingsType: SettingsType.normal,
title: 'CDN 设置',
leading: const Icon(MdiIcons.cloudPlusOutline),
getSubtitle: () =>
'当前使用:${CDNService.fromCode(VideoUtils.cdnService).desc},部分 CDN 可能失效,如无法播放请尝试切换',
onTap: (setState) async {
String? result = await showDialog(
context: Get.context!,
builder: (context) {
return const CdnSelectDialog();
},
),
SettingsModel(
settingsType: SettingsType.sw1tch,
title: 'CDN 测速',
leading: const Icon(Icons.speed),
subtitle: '测速通过模拟加载视频实现,注意流量消耗,结果仅供参考',
setKey: SettingBoxKey.cdnSpeedTest,
defaultVal: true,
),
SettingsModel(
settingsType: SettingsType.sw1tch,
title: '音频不跟随 CDN 设置',
subtitle: '直接采用备用 URL可解决部分视频无声',
leading: const Icon(MdiIcons.musicNotePlus),
setKey: SettingBoxKey.disableAudioCDN,
defaultVal: true,
onChanged: (value) {
VideoUtils.disableAudioCDN = value;
},
),
SettingsModel(
settingsType: SettingsType.normal,
title: '默认画质',
leading: const Icon(Icons.video_settings_outlined),
getSubtitle: () =>
'当前画质:${VideoQuality.fromCode(Pref.defaultVideoQa).desc}',
onTap: (setState) async {
int? result = await showDialog(
context: Get.context!,
builder: (context) {
return SelectDialog<int>(
title: '默认画质',
value: Pref.defaultVideoQa,
values: VideoQuality.values.reversed
.map((e) => (e.code, e.desc))
.toList(),
);
},
);
if (result != null) {
VideoUtils.cdnService = result;
await GStorage.setting.put(SettingBoxKey.CDNService, result);
setState();
}
},
),
SettingsModel(
settingsType: SettingsType.sw1tch,
title: 'CDN 测速',
leading: const Icon(Icons.speed),
subtitle: '测速通过模拟加载视频实现,注意流量消耗,结果仅供参考',
setKey: SettingBoxKey.cdnSpeedTest,
defaultVal: true,
),
SettingsModel(
settingsType: SettingsType.sw1tch,
title: '音频不跟随 CDN 设置',
subtitle: '直接采用备用 URL可解决部分视频无声',
leading: const Icon(MdiIcons.musicNotePlus),
setKey: SettingBoxKey.disableAudioCDN,
defaultVal: true,
onChanged: (value) {
VideoUtils.disableAudioCDN = value;
},
),
SettingsModel(
settingsType: SettingsType.normal,
title: '默认画质',
leading: const Icon(Icons.video_settings_outlined),
getSubtitle: () =>
'当前画质:${VideoQuality.fromCode(Pref.defaultVideoQa).desc}',
onTap: (setState) async {
int? result = await showDialog(
context: Get.context!,
builder: (context) {
return SelectDialog<int>(
title: '默认画质',
value: Pref.defaultVideoQa,
values: VideoQuality.values.reversed
.map((e) => (e.code, e.desc))
.toList(),
);
if (result != null) {
await GStorage.setting.put(SettingBoxKey.defaultVideoQa, result);
setState();
}
},
),
SettingsModel(
settingsType: SettingsType.normal,
title: '蜂窝网络画质',
leading: const Icon(Icons.video_settings_outlined),
getSubtitle: () =>
'当前画质:${VideoQuality.fromCode(Pref.defaultVideoQaCellular).desc}',
onTap: (setState) async {
int? result = await showDialog(
context: Get.context!,
builder: (context) {
return SelectDialog<int>(
title: '蜂窝网络画质',
value: Pref.defaultVideoQaCellular,
values: VideoQuality.values.reversed.map((e) {
return (e.code, e.desc);
);
if (result != null) {
await GStorage.setting.put(SettingBoxKey.defaultVideoQa, result);
setState();
}
},
),
SettingsModel(
settingsType: SettingsType.normal,
title: '蜂窝网络画质',
leading: const Icon(Icons.video_settings_outlined),
getSubtitle: () =>
'当前画质:${VideoQuality.fromCode(Pref.defaultVideoQaCellular).desc}',
onTap: (setState) async {
int? result = await showDialog(
context: Get.context!,
builder: (context) {
return SelectDialog<int>(
title: '蜂窝网络画质',
value: Pref.defaultVideoQaCellular,
values: VideoQuality.values.reversed.map((e) {
return (e.code, e.desc);
}).toList(),
);
},
);
if (result != null) {
await GStorage.setting.put(
SettingBoxKey.defaultVideoQaCellular,
result,
);
setState();
}
},
),
SettingsModel(
settingsType: SettingsType.normal,
title: '默认音质',
leading: const Icon(Icons.music_video_outlined),
getSubtitle: () =>
'当前音质:${AudioQuality.fromCode(Pref.defaultAudioQa).desc}',
onTap: (setState) async {
int? result = await showDialog(
context: Get.context!,
builder: (context) {
return SelectDialog<int>(
title: '默认音质',
value: Pref.defaultAudioQa,
values: AudioQuality.values.reversed
.map((e) => (e.code, e.desc))
.toList(),
);
},
);
if (result != null) {
await GStorage.setting.put(SettingBoxKey.defaultAudioQa, result);
setState();
}
},
),
SettingsModel(
settingsType: SettingsType.normal,
title: '蜂窝网络音质',
leading: const Icon(Icons.music_video_outlined),
getSubtitle: () =>
'当前音质:${AudioQuality.fromCode(Pref.defaultAudioQaCellular).desc}',
onTap: (setState) async {
int? result = await showDialog(
context: Get.context!,
builder: (context) {
return SelectDialog<int>(
title: '蜂窝网络音质',
value: Pref.defaultAudioQaCellular,
values: AudioQuality.values.reversed.map((e) {
return (e.code, e.desc);
}).toList(),
);
},
);
if (result != null) {
await GStorage.setting.put(
SettingBoxKey.defaultAudioQaCellular,
result,
);
setState();
}
},
),
SettingsModel(
settingsType: SettingsType.normal,
title: '直播默认画质',
leading: const Icon(Icons.video_settings_outlined),
getSubtitle: () =>
'当前画质:${LiveQualityExt.fromCode(Pref.liveQuality)!.description}',
onTap: (setState) async {
int? result = await showDialog(
context: Get.context!,
builder: (context) {
return SelectDialog<int>(
title: '直播默认画质',
value: Pref.liveQuality,
values: LiveQuality.values
.map((e) => (e.code, e.description))
.toList(),
);
},
);
if (result != null) {
await GStorage.setting.put(SettingBoxKey.liveQuality, result);
setState();
}
},
),
SettingsModel(
settingsType: SettingsType.normal,
title: '蜂窝网络直播默认画质',
leading: const Icon(Icons.video_settings_outlined),
getSubtitle: () =>
'当前画质:${LiveQualityExt.fromCode(Pref.liveQualityCellular)!.description}',
onTap: (setState) async {
int? result = await showDialog(
context: Get.context!,
builder: (context) {
return SelectDialog<int>(
title: '直播默认画质',
value: Pref.liveQualityCellular,
values: LiveQuality.values.map((e) {
return (e.code, e.description);
}).toList(),
);
},
);
if (result != null) {
await GStorage.setting.put(SettingBoxKey.liveQualityCellular, result);
setState();
}
},
),
SettingsModel(
settingsType: SettingsType.normal,
title: '首选解码格式',
leading: const Icon(Icons.movie_creation_outlined),
getSubtitle: () =>
'首选解码格式:${VideoDecodeFormatTypeExt.fromCode(Pref.defaultDecode)!.description},请根据设备支持情况与需求调整',
onTap: (setState) async {
String? result = await showDialog(
context: Get.context!,
builder: (context) {
return SelectDialog<String>(
title: '默认解码格式',
value: Pref.defaultDecode,
values: VideoDecodeFormatType.values
.map((e) => (e.code, e.description))
.toList(),
);
},
);
if (result != null) {
await GStorage.setting.put(SettingBoxKey.defaultDecode, result);
setState();
}
},
),
SettingsModel(
settingsType: SettingsType.normal,
title: '次选解码格式',
getSubtitle: () =>
'非杜比视频次选:${VideoDecodeFormatTypeExt.fromCode(Pref.secondDecode)!.description},仍无则选择首个提供的解码格式',
leading: const Icon(Icons.swap_horizontal_circle_outlined),
onTap: (setState) async {
String? result = await showDialog(
context: Get.context!,
builder: (context) {
return SelectDialog<String>(
title: '次选解码格式',
value: Pref.secondDecode,
values: VideoDecodeFormatType.values.map((e) {
return (e.code, e.description);
}).toList(),
);
},
);
if (result != null) {
await GStorage.setting.put(SettingBoxKey.secondDecode, result);
setState();
}
},
),
if (Platform.isAndroid)
SettingsModel(
settingsType: SettingsType.sw1tch,
title: '优先使用 OpenSL ES 输出音频',
leading: const Icon(Icons.speaker_outlined),
subtitle: '关闭则优先使用AudioTrack输出音频此项即mpv的--ao若遇系统音效丢失、无声、音画不同步等问题请尝试关闭。',
setKey: SettingBoxKey.useOpenSLES,
defaultVal: true,
),
SettingsModel(
settingsType: SettingsType.sw1tch,
title: '扩大缓冲区',
leading: const Icon(Icons.storage_outlined),
subtitle: '默认缓冲区为视频4MB/直播16MB开启后为32MB/64MB加载时间变长',
setKey: SettingBoxKey.expandBuffer,
defaultVal: false,
),
//video-sync
SettingsModel(
settingsType: SettingsType.normal,
title: '视频同步',
leading: const Icon(Icons.view_timeline_outlined),
getSubtitle: () => '当前:${Pref.videoSync}此项即mpv的--video-sync',
onTap: (setState) async {
String? result = await showDialog(
context: Get.context!,
builder: (context) {
return SelectDialog<String>(
title: '视频同步',
value: Pref.videoSync,
values:
[
'audio',
'display-resample',
'display-resample-vdrop',
'display-resample-desync',
'display-tempo',
'display-vdrop',
'display-adrop',
'display-desync',
'desync',
].map((e) {
return (e, e);
}).toList(),
);
},
);
if (result != null) {
await GStorage.setting
.put(SettingBoxKey.defaultVideoQaCellular, result);
setState();
}
},
),
SettingsModel(
settingsType: SettingsType.normal,
title: '默认音质',
leading: const Icon(Icons.music_video_outlined),
getSubtitle: () =>
'当前音质:${AudioQuality.fromCode(Pref.defaultAudioQa).desc}',
onTap: (setState) async {
int? result = await showDialog(
context: Get.context!,
builder: (context) {
return SelectDialog<int>(
title: '默认音质',
value: Pref.defaultAudioQa,
values: AudioQuality.values.reversed
.map((e) => (e.code, e.desc))
.toList(),
);
},
);
if (result != null) {
await GStorage.setting.put(SettingBoxKey.videoSync, result);
setState();
}
},
),
SettingsModel(
settingsType: SettingsType.normal,
title: '硬解模式',
leading: const Icon(Icons.memory_outlined),
getSubtitle: () => '当前:${Pref.hardwareDecoding}此项即mpv的--hwdec',
onTap: (setState) async {
String? result = await showDialog(
context: Get.context!,
builder: (context) {
return SelectDialog<String>(
title: '硬解模式',
value: Pref.hardwareDecoding,
values: ['auto', 'auto-copy', 'auto-safe', 'no', 'yes'].map((e) {
return (e, e);
}).toList(),
);
if (result != null) {
await GStorage.setting.put(SettingBoxKey.defaultAudioQa, result);
setState();
}
},
),
SettingsModel(
settingsType: SettingsType.normal,
title: '蜂窝网络音质',
leading: const Icon(Icons.music_video_outlined),
getSubtitle: () =>
'当前音质:${AudioQuality.fromCode(Pref.defaultAudioQaCellular).desc}',
onTap: (setState) async {
int? result = await showDialog(
context: Get.context!,
builder: (context) {
return SelectDialog<int>(
title: '蜂窝网络音质',
value: Pref.defaultAudioQaCellular,
values: AudioQuality.values.reversed.map((e) {
return (e.code, e.desc);
}).toList(),
);
},
);
if (result != null) {
await GStorage.setting
.put(SettingBoxKey.defaultAudioQaCellular, result);
setState();
}
},
),
SettingsModel(
settingsType: SettingsType.normal,
title: '直播默认画质',
leading: const Icon(Icons.video_settings_outlined),
getSubtitle: () =>
'当前画质:${LiveQualityExt.fromCode(Pref.liveQuality)!.description}',
onTap: (setState) async {
int? result = await showDialog(
context: Get.context!,
builder: (context) {
return SelectDialog<int>(
title: '直播默认画质',
value: Pref.liveQuality,
values: LiveQuality.values
.map((e) => (e.code, e.description))
.toList(),
);
},
);
if (result != null) {
await GStorage.setting.put(SettingBoxKey.liveQuality, result);
setState();
}
},
),
SettingsModel(
settingsType: SettingsType.normal,
title: '蜂窝网络直播默认画质',
leading: const Icon(Icons.video_settings_outlined),
getSubtitle: () =>
'当前画质:${LiveQualityExt.fromCode(Pref.liveQualityCellular)!.description}',
onTap: (setState) async {
int? result = await showDialog(
context: Get.context!,
builder: (context) {
return SelectDialog<int>(
title: '直播默认画质',
value: Pref.liveQualityCellular,
values: LiveQuality.values.map((e) {
return (e.code, e.description);
}).toList(),
);
},
);
if (result != null) {
await GStorage.setting
.put(SettingBoxKey.liveQualityCellular, result);
setState();
}
},
),
SettingsModel(
settingsType: SettingsType.normal,
title: '首选解码格式',
leading: const Icon(Icons.movie_creation_outlined),
getSubtitle: () =>
'首选解码格式:${VideoDecodeFormatTypeExt.fromCode(Pref.defaultDecode)!.description},请根据设备支持情况与需求调整',
onTap: (setState) async {
String? result = await showDialog(
context: Get.context!,
builder: (context) {
return SelectDialog<String>(
title: '默认解码格式',
value: Pref.defaultDecode,
values: VideoDecodeFormatType.values
.map((e) => (e.code, e.description))
.toList());
},
);
if (result != null) {
await GStorage.setting.put(SettingBoxKey.defaultDecode, result);
setState();
}
},
),
SettingsModel(
settingsType: SettingsType.normal,
title: '次选解码格式',
getSubtitle: () =>
'非杜比视频次选:${VideoDecodeFormatTypeExt.fromCode(Pref.secondDecode)!.description},仍无则选择首个提供的解码格式',
leading: const Icon(Icons.swap_horizontal_circle_outlined),
onTap: (setState) async {
String? result = await showDialog(
context: Get.context!,
builder: (context) {
return SelectDialog<String>(
title: '次选解码格式',
value: Pref.secondDecode,
values: VideoDecodeFormatType.values.map((e) {
return (e.code, e.description);
}).toList());
},
);
if (result != null) {
await GStorage.setting.put(SettingBoxKey.secondDecode, result);
setState();
}
},
),
if (Platform.isAndroid)
SettingsModel(
settingsType: SettingsType.sw1tch,
title: '优先使用 OpenSL ES 输出音频',
leading: const Icon(Icons.speaker_outlined),
subtitle:
'关闭则优先使用AudioTrack输出音频此项即mpv的--ao若遇系统音效丢失、无声、音画不同步等问题请尝试关闭。',
setKey: SettingBoxKey.useOpenSLES,
defaultVal: true,
),
SettingsModel(
settingsType: SettingsType.sw1tch,
title: '扩大缓冲区',
leading: const Icon(Icons.storage_outlined),
subtitle: '默认缓冲区为视频4MB/直播16MB开启后为32MB/64MB加载时间变长',
setKey: SettingBoxKey.expandBuffer,
defaultVal: false,
),
//video-sync
SettingsModel(
settingsType: SettingsType.normal,
title: '视频同步',
leading: const Icon(Icons.view_timeline_outlined),
getSubtitle: () => '当前:${Pref.videoSync}此项即mpv的--video-sync',
onTap: (setState) async {
String? result = await showDialog(
context: Get.context!,
builder: (context) {
return SelectDialog<String>(
title: '视频同步',
value: Pref.videoSync,
values: [
'audio',
'display-resample',
'display-resample-vdrop',
'display-resample-desync',
'display-tempo',
'display-vdrop',
'display-adrop',
'display-desync',
'desync'
].map((e) {
return (e, e);
}).toList());
},
);
if (result != null) {
await GStorage.setting.put(SettingBoxKey.videoSync, result);
setState();
}
},
),
SettingsModel(
settingsType: SettingsType.normal,
title: '硬解模式',
leading: const Icon(Icons.memory_outlined),
getSubtitle: () => '当前:${Pref.hardwareDecoding}此项即mpv的--hwdec',
onTap: (setState) async {
String? result = await showDialog(
context: Get.context!,
builder: (context) {
return SelectDialog<String>(
title: '硬解模式',
value: Pref.hardwareDecoding,
values:
['auto', 'auto-copy', 'auto-safe', 'no', 'yes'].map((e) {
return (e, e);
}).toList());
},
);
if (result != null) {
await GStorage.setting.put(SettingBoxKey.hardwareDecoding, result);
setState();
}
},
),
];
);
if (result != null) {
await GStorage.setting.put(SettingBoxKey.hardwareDecoding, result);
setState();
}
},
),
];