Signed-off-by: dom <githubaccount56556@proton.me>
This commit is contained in:
dom
2026-01-29 13:35:51 +08:00
parent bcbfe5c849
commit 5979ddb60c
46 changed files with 3266 additions and 3293 deletions

View File

@@ -38,13 +38,16 @@ List<SettingsModel> get videoSettings => [
title: 'B站定向流量支持',
subtitle: '若套餐含B站定向流量则会自动使用。可查阅运营商的流量记录确认。',
leading: const Icon(Icons.perm_data_setting_outlined),
getTrailing: () => IgnorePointer(
getTrailing: (theme) => IgnorePointer(
child: Transform.scale(
alignment: Alignment.centerRight,
scale: 0.8,
alignment: Alignment.centerRight,
child: Switch(
value: true,
onChanged: (_) {},
thumbIcon: WidgetStateProperty.all(
const Icon(Icons.lock_outline_rounded),
),
),
),
),
@@ -54,68 +57,13 @@ List<SettingsModel> get videoSettings => [
leading: const Icon(MdiIcons.cloudPlusOutline),
getSubtitle: () =>
'当前使用:${VideoUtils.cdnService.desc},部分 CDN 可能失效,如无法播放请尝试切换',
onTap: (context, setState) async {
final result = await showDialog<CDNService>(
context: context,
builder: (context) {
return const CdnSelectDialog();
},
);
if (result != null) {
VideoUtils.cdnService = result;
await GStorage.setting.put(SettingBoxKey.CDNService, result.name);
setState();
}
},
onTap: _showCDNDialog,
),
NormalModel(
title: '直播 CDN 设置',
leading: const Icon(MdiIcons.cloudPlusOutline),
getSubtitle: () => '当前使用:${Pref.liveCdnUrl ?? "默认"}',
onTap: (context, setState) async {
String? result = await showDialog<String>(
context: context,
builder: (context) {
String host = Pref.liveCdnUrl ?? '';
return AlertDialog(
title: const Text('输入CDN host'),
content: TextFormField(
initialValue: host,
autofocus: true,
onChanged: (value) => host = value,
),
actions: [
TextButton(
onPressed: Get.back,
child: Text(
'取消',
style: TextStyle(
color: ColorScheme.of(context).outline,
),
),
),
TextButton(
onPressed: () => Get.back(result: host),
child: const Text('确定'),
),
],
);
},
);
if (result != null) {
if (result.isEmpty) {
result = null;
await GStorage.setting.delete(SettingBoxKey.liveCdnUrl);
} else {
if (!result.startsWith('http')) {
result = 'https://$result';
}
await GStorage.setting.put(SettingBoxKey.liveCdnUrl, result);
}
VideoUtils.liveCdnUrl = result;
setState();
}
},
onTap: _showLiveCDNDialog,
),
const SwitchModel(
title: 'CDN 测速',
@@ -130,221 +78,69 @@ List<SettingsModel> get videoSettings => [
leading: const Icon(MdiIcons.musicNotePlus),
setKey: SettingBoxKey.disableAudioCDN,
defaultVal: false,
onChanged: (value) {
VideoUtils.disableAudioCDN = value;
},
onChanged: (value) => VideoUtils.disableAudioCDN = value,
),
NormalModel(
title: '默认画质',
leading: const Icon(Icons.video_settings_outlined),
getSubtitle: () =>
'当前画质:${VideoQuality.fromCode(Pref.defaultVideoQa).desc}',
onTap: (context, setState) async {
final result = await showDialog<int>(
context: context,
builder: (context) {
return SelectDialog<int>(
title: '默认画质',
value: Pref.defaultVideoQa,
values: VideoQuality.values.map((e) => (e.code, e.desc)).toList(),
);
},
);
if (result != null) {
await GStorage.setting.put(SettingBoxKey.defaultVideoQa, result);
setState();
}
},
onTap: _showVideoQaDialog,
),
NormalModel(
title: '蜂窝网络画质',
leading: const Icon(Icons.video_settings_outlined),
getSubtitle: () =>
'当前画质:${VideoQuality.fromCode(Pref.defaultVideoQaCellular).desc}',
onTap: (context, setState) async {
final result = await showDialog<int>(
context: context,
builder: (context) {
return SelectDialog<int>(
title: '蜂窝网络画质',
value: Pref.defaultVideoQaCellular,
values: VideoQuality.values.map((e) => (e.code, e.desc)).toList(),
);
},
);
if (result != null) {
await GStorage.setting.put(
SettingBoxKey.defaultVideoQaCellular,
result,
);
setState();
}
},
onTap: _showVideoCellularQaDialog,
),
NormalModel(
title: '默认音质',
leading: const Icon(Icons.music_video_outlined),
getSubtitle: () =>
'当前音质:${AudioQuality.fromCode(Pref.defaultAudioQa).desc}',
onTap: (context, setState) async {
final result = await showDialog<int>(
context: context,
builder: (context) {
return SelectDialog<int>(
title: '默认音质',
value: Pref.defaultAudioQa,
values: AudioQuality.values.map((e) => (e.code, e.desc)).toList(),
);
},
);
if (result != null) {
await GStorage.setting.put(SettingBoxKey.defaultAudioQa, result);
setState();
}
},
onTap: _showAudioQaDialog,
),
NormalModel(
title: '蜂窝网络音质',
leading: const Icon(Icons.music_video_outlined),
getSubtitle: () =>
'当前音质:${AudioQuality.fromCode(Pref.defaultAudioQaCellular).desc}',
onTap: (context, setState) async {
final result = await showDialog<int>(
context: context,
builder: (context) {
return SelectDialog<int>(
title: '蜂窝网络音质',
value: Pref.defaultAudioQaCellular,
values: AudioQuality.values.map((e) => (e.code, e.desc)).toList(),
);
},
);
if (result != null) {
await GStorage.setting.put(
SettingBoxKey.defaultAudioQaCellular,
result,
);
setState();
}
},
onTap: _showAudioCellularQaDialog,
),
NormalModel(
title: '直播默认画质',
leading: const Icon(Icons.video_settings_outlined),
getSubtitle: () => '当前画质:${LiveQuality.fromCode(Pref.liveQuality)?.desc}',
onTap: (context, setState) async {
final result = await showDialog<int>(
context: context,
builder: (context) {
return SelectDialog<int>(
title: '直播默认画质',
value: Pref.liveQuality,
values: LiveQuality.values.map((e) => (e.code, e.desc)).toList(),
);
},
);
if (result != null) {
await GStorage.setting.put(SettingBoxKey.liveQuality, result);
setState();
}
},
onTap: _showLiveQaDialog,
),
NormalModel(
title: '蜂窝网络直播默认画质',
leading: const Icon(Icons.video_settings_outlined),
getSubtitle: () =>
'当前画质:${LiveQuality.fromCode(Pref.liveQualityCellular)?.desc}',
onTap: (context, setState) async {
final result = await showDialog<int>(
context: context,
builder: (context) {
return SelectDialog<int>(
title: '蜂窝网络直播默认画质',
value: Pref.liveQualityCellular,
values: LiveQuality.values.map((e) => (e.code, e.desc)).toList(),
);
},
);
if (result != null) {
await GStorage.setting.put(SettingBoxKey.liveQualityCellular, result);
setState();
}
},
onTap: _showLiveCellularQaDialog,
),
NormalModel(
title: '首选解码格式',
leading: const Icon(Icons.movie_creation_outlined),
getSubtitle: () =>
'首选解码格式:${VideoDecodeFormatType.fromCode(Pref.defaultDecode).description},请根据设备支持情况与需求调整',
onTap: (context, setState) async {
final result = await showDialog<String>(
context: context,
builder: (context) {
return SelectDialog<String>(
title: '默认解码格式',
value: Pref.defaultDecode,
values: VideoDecodeFormatType.values
.map((e) => (e.codes.first, e.description))
.toList(),
);
},
);
if (result != null) {
await GStorage.setting.put(SettingBoxKey.defaultDecode, result);
setState();
}
},
onTap: _showDecodeDialog,
),
NormalModel(
title: '次选解码格式',
getSubtitle: () =>
'非杜比视频次选:${VideoDecodeFormatType.fromCode(Pref.secondDecode).description},仍无则选择首个提供的解码格式',
leading: const Icon(Icons.swap_horizontal_circle_outlined),
onTap: (context, setState) async {
final result = await showDialog<String>(
context: context,
builder: (context) {
return SelectDialog<String>(
title: '次选解码格式',
value: Pref.secondDecode,
values: VideoDecodeFormatType.values
.map((e) => (e.codes.first, e.description))
.toList(),
);
},
);
if (result != null) {
await GStorage.setting.put(SettingBoxKey.secondDecode, result);
setState();
}
},
onTap: _showSecondDecodeDialog,
),
if (kDebugMode || Platform.isAndroid)
NormalModel(
title: '音频输出设备',
leading: const Icon(Icons.speaker_outlined),
getSubtitle: () => '当前:${Pref.audioOutput}',
onTap: (context, setState) async {
final result = await showDialog<List<String>>(
context: context,
builder: (context) {
return OrderedMultiSelectDialog<String>(
title: '音频输出设备',
initValues: Pref.audioOutput.split(','),
values: {
for (final e in AudioOutput.values) e.name: e.label,
},
);
},
);
if (result != null && result.isNotEmpty) {
await GStorage.setting.put(
SettingBoxKey.audioOutput,
result.join(','),
);
setState();
}
},
onTap: _showAudioOutputDialog,
),
const SwitchModel(
title: '扩大缓冲区',
@@ -358,58 +154,298 @@ List<SettingsModel> get videoSettings => [
title: '视频同步',
leading: const Icon(Icons.view_timeline_outlined),
getSubtitle: () => '当前:${Pref.videoSync}此项即mpv的--video-sync',
onTap: (context, setState) async {
final result = await showDialog<String>(
context: context,
builder: (context) {
return SelectDialog<String>(
title: '视频同步',
value: Pref.videoSync,
values: const [
'audio',
'display-resample',
'display-resample-vdrop',
'display-resample-desync',
'display-tempo',
'display-vdrop',
'display-adrop',
'display-desync',
'desync',
].map((e) => (e, e)).toList(),
);
},
);
if (result != null) {
await GStorage.setting.put(SettingBoxKey.videoSync, result);
setState();
}
},
onTap: _showVideoSyncDialog,
),
NormalModel(
title: '硬解模式',
leading: const Icon(Icons.memory_outlined),
getSubtitle: () => '当前:${Pref.hardwareDecoding}此项即mpv的--hwdec',
onTap: (context, setState) async {
final result = await showDialog<List<String>>(
context: context,
builder: (context) {
return OrderedMultiSelectDialog<String>(
title: '硬解模式',
initValues: Pref.hardwareDecoding.split(','),
values: {
for (final e in HwDecType.values)
e.hwdec: '${e.hwdec}\n${e.desc}',
},
);
},
);
if (result != null && result.isNotEmpty) {
await GStorage.setting.put(
SettingBoxKey.hardwareDecoding,
result.join(','),
);
setState();
}
},
onTap: _showHwDecDialog,
),
];
Future<void> _showCDNDialog(BuildContext context, VoidCallback setState) async {
final res = await showDialog<CDNService>(
context: context,
builder: (context) => const CdnSelectDialog(),
);
if (res != null) {
VideoUtils.cdnService = res;
await GStorage.setting.put(SettingBoxKey.CDNService, res.name);
setState();
}
}
Future<void> _showLiveCDNDialog(
BuildContext context,
VoidCallback setState,
) async {
String host = Pref.liveCdnUrl ?? '';
String? res = await showDialog<String>(
context: context,
builder: (context) => AlertDialog(
title: const Text('输入CDN host'),
content: TextFormField(
initialValue: host,
autofocus: true,
onChanged: (value) => host = value,
),
actions: [
TextButton(
onPressed: Get.back,
child: Text(
'取消',
style: TextStyle(
color: ColorScheme.of(context).outline,
),
),
),
TextButton(
onPressed: () => Get.back(result: host),
child: const Text('确定'),
),
],
),
);
if (res != null) {
if (res.isEmpty) {
res = null;
await GStorage.setting.delete(SettingBoxKey.liveCdnUrl);
} else {
if (!res.startsWith('http')) {
res = 'https://$res';
}
await GStorage.setting.put(SettingBoxKey.liveCdnUrl, res);
}
VideoUtils.liveCdnUrl = res;
setState();
}
}
Future<void> _showVideoQaDialog(
BuildContext context,
VoidCallback setState,
) async {
final res = await showDialog<int>(
context: context,
builder: (context) => SelectDialog<int>(
title: '默认画质',
value: Pref.defaultVideoQa,
values: VideoQuality.values.map((e) => (e.code, e.desc)).toList(),
),
);
if (res != null) {
await GStorage.setting.put(SettingBoxKey.defaultVideoQa, res);
setState();
}
}
Future<void> _showVideoCellularQaDialog(
BuildContext context,
VoidCallback setState,
) async {
final res = await showDialog<int>(
context: context,
builder: (context) => SelectDialog<int>(
title: '蜂窝网络画质',
value: Pref.defaultVideoQaCellular,
values: VideoQuality.values.map((e) => (e.code, e.desc)).toList(),
),
);
if (res != null) {
await GStorage.setting.put(
SettingBoxKey.defaultVideoQaCellular,
res,
);
setState();
}
}
Future<void> _showAudioQaDialog(
BuildContext context,
VoidCallback setState,
) async {
final res = await showDialog<int>(
context: context,
builder: (context) => SelectDialog<int>(
title: '默认音质',
value: Pref.defaultAudioQa,
values: AudioQuality.values.map((e) => (e.code, e.desc)).toList(),
),
);
if (res != null) {
await GStorage.setting.put(SettingBoxKey.defaultAudioQa, res);
setState();
}
}
Future<void> _showAudioCellularQaDialog(
BuildContext context,
VoidCallback setState,
) async {
final res = await showDialog<int>(
context: context,
builder: (context) => SelectDialog<int>(
title: '蜂窝网络音质',
value: Pref.defaultAudioQaCellular,
values: AudioQuality.values.map((e) => (e.code, e.desc)).toList(),
),
);
if (res != null) {
await GStorage.setting.put(
SettingBoxKey.defaultAudioQaCellular,
res,
);
setState();
}
}
Future<void> _showLiveQaDialog(
BuildContext context,
VoidCallback setState,
) async {
final res = await showDialog<int>(
context: context,
builder: (context) => SelectDialog<int>(
title: '直播默认画质',
value: Pref.liveQuality,
values: LiveQuality.values.map((e) => (e.code, e.desc)).toList(),
),
);
if (res != null) {
await GStorage.setting.put(SettingBoxKey.liveQuality, res);
setState();
}
}
Future<void> _showLiveCellularQaDialog(
BuildContext context,
VoidCallback setState,
) async {
final res = await showDialog<int>(
context: context,
builder: (context) => SelectDialog<int>(
title: '蜂窝网络直播默认画质',
value: Pref.liveQualityCellular,
values: LiveQuality.values.map((e) => (e.code, e.desc)).toList(),
),
);
if (res != null) {
await GStorage.setting.put(SettingBoxKey.liveQualityCellular, res);
setState();
}
}
Future<void> _showDecodeDialog(
BuildContext context,
VoidCallback setState,
) async {
final res = await showDialog<String>(
context: context,
builder: (context) => SelectDialog<String>(
title: '默认解码格式',
value: Pref.defaultDecode,
values: VideoDecodeFormatType.values
.map((e) => (e.codes.first, e.description))
.toList(),
),
);
if (res != null) {
await GStorage.setting.put(SettingBoxKey.defaultDecode, res);
setState();
}
}
Future<void> _showSecondDecodeDialog(
BuildContext context,
VoidCallback setState,
) async {
final res = await showDialog<String>(
context: context,
builder: (context) => SelectDialog<String>(
title: '次选解码格式',
value: Pref.secondDecode,
values: VideoDecodeFormatType.values
.map((e) => (e.codes.first, e.description))
.toList(),
),
);
if (res != null) {
await GStorage.setting.put(SettingBoxKey.secondDecode, res);
setState();
}
}
Future<void> _showAudioOutputDialog(
BuildContext context,
VoidCallback setState,
) async {
final res = await showDialog<List<String>>(
context: context,
builder: (context) => OrderedMultiSelectDialog<String>(
title: '音频输出设备',
initValues: Pref.audioOutput.split(','),
values: {
for (final e in AudioOutput.values) e.name: e.label,
},
),
);
if (res != null && res.isNotEmpty) {
await GStorage.setting.put(
SettingBoxKey.audioOutput,
res.join(','),
);
setState();
}
}
Future<void> _showVideoSyncDialog(
BuildContext context,
VoidCallback setState,
) async {
final res = await showDialog<String>(
context: context,
builder: (context) => SelectDialog<String>(
title: '视频同步',
value: Pref.videoSync,
values: const [
'audio',
'display-resample',
'display-resample-vdrop',
'display-resample-desync',
'display-tempo',
'display-vdrop',
'display-adrop',
'display-desync',
'desync',
].map((e) => (e, e)).toList(),
),
);
if (res != null) {
await GStorage.setting.put(SettingBoxKey.videoSync, res);
setState();
}
}
Future<void> _showHwDecDialog(
BuildContext context,
VoidCallback setState,
) async {
final res = await showDialog<List<String>>(
context: context,
builder: (context) => OrderedMultiSelectDialog<String>(
title: '硬解模式',
initValues: Pref.hardwareDecoding.split(','),
values: {
for (final e in HwDecType.values) e.hwdec: '${e.hwdec}\n${e.desc}',
},
),
);
if (res != null && res.isNotEmpty) {
await GStorage.setting.put(
SettingBoxKey.hardwareDecoding,
res.join(','),
);
setState();
}
}