mirror of
https://github.com/HChaZZY/PiliPlus.git
synced 2025-12-06 09:13:48 +08:00
feat: loudnorm (#1358)
* feat: loudnorm * fix * fix: android only * fix: toString
This commit is contained in:
committed by
GitHub
parent
046412b709
commit
22c57bf468
@@ -23,6 +23,7 @@ import 'package:PiliPlus/pages/setting/models/model.dart';
|
||||
import 'package:PiliPlus/pages/setting/widgets/select_dialog.dart';
|
||||
import 'package:PiliPlus/pages/setting/widgets/slide_dialog.dart';
|
||||
import 'package:PiliPlus/pages/video/reply/widgets/reply_item_grpc.dart';
|
||||
import 'package:PiliPlus/plugin/pl_player/controller.dart';
|
||||
import 'package:PiliPlus/utils/accounts.dart';
|
||||
import 'package:PiliPlus/utils/cache_manage.dart';
|
||||
import 'package:PiliPlus/utils/feed_back.dart';
|
||||
@@ -32,6 +33,7 @@ import 'package:PiliPlus/utils/storage_key.dart';
|
||||
import 'package:PiliPlus/utils/storage_pref.dart';
|
||||
import 'package:PiliPlus/utils/update.dart';
|
||||
import 'package:PiliPlus/utils/utils.dart';
|
||||
import 'package:flutter/foundation.dart' show kDebugMode;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
@@ -453,94 +455,27 @@ List<SettingsModel> get extraSettings => [
|
||||
} catch (_) {}
|
||||
},
|
||||
),
|
||||
SettingsModel(
|
||||
settingsType: SettingsType.normal,
|
||||
title: '音量均衡',
|
||||
setKey: SettingBoxKey.audioNormalization,
|
||||
leading: const Icon(Icons.multitrack_audio),
|
||||
getSubtitle: () {
|
||||
String audioNormalization = Pref.audioNormalization;
|
||||
// TODO: remove next version
|
||||
if (audioNormalization == '2') {
|
||||
GStorage.setting.put(SettingBoxKey.audioNormalization, '1');
|
||||
audioNormalization = '1';
|
||||
}
|
||||
audioNormalization = switch (audioNormalization) {
|
||||
'0' => AudioNormalization.disable.title,
|
||||
'1' => AudioNormalization.dynaudnorm.title,
|
||||
_ => audioNormalization,
|
||||
};
|
||||
return '当前:「$audioNormalization」';
|
||||
},
|
||||
onTap: (setState) async {
|
||||
String? result = await showDialog(
|
||||
context: Get.context!,
|
||||
builder: (context) {
|
||||
String audioNormalization = Pref.audioNormalization;
|
||||
final values = {'0', '1', audioNormalization, '2'};
|
||||
return SelectDialog<String>(
|
||||
title: '音量均衡',
|
||||
value: audioNormalization,
|
||||
values: values
|
||||
.map(
|
||||
(e) => (
|
||||
e,
|
||||
switch (e) {
|
||||
'0' => AudioNormalization.disable.title,
|
||||
'1' => AudioNormalization.dynaudnorm.title,
|
||||
'2' => AudioNormalization.custom.title,
|
||||
_ => e,
|
||||
},
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
);
|
||||
},
|
||||
);
|
||||
if (result != null) {
|
||||
if (result == '2') {
|
||||
String param = '';
|
||||
showDialog(
|
||||
context: Get.context!,
|
||||
builder: (context) {
|
||||
return AlertDialog(
|
||||
title: const Text('自定义参数'),
|
||||
content: TextField(
|
||||
autofocus: true,
|
||||
onChanged: (value) => param = value,
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: Get.back,
|
||||
child: Text(
|
||||
'取消',
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.outline,
|
||||
),
|
||||
),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
Get.back();
|
||||
await GStorage.setting.put(
|
||||
SettingBoxKey.audioNormalization,
|
||||
param,
|
||||
);
|
||||
setState();
|
||||
},
|
||||
child: const Text('确定'),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
if (kDebugMode || Platform.isAndroid)
|
||||
SettingsModel(
|
||||
settingsType: SettingsType.normal,
|
||||
title: '音量均衡',
|
||||
setKey: SettingBoxKey.audioNormalization,
|
||||
leading: const Icon(Icons.multitrack_audio),
|
||||
getSubtitle: () {
|
||||
final audioNormalization = AudioNormalization.getTitleFromConfig(
|
||||
Pref.audioNormalization,
|
||||
);
|
||||
String fallback = Pref.fallbackNormalization;
|
||||
if (fallback == '0') {
|
||||
fallback = '';
|
||||
} else {
|
||||
await GStorage.setting.put(SettingBoxKey.audioNormalization, result);
|
||||
setState();
|
||||
fallback =
|
||||
',无参数时:「${AudioNormalization.getTitleFromConfig(fallback)}」';
|
||||
}
|
||||
}
|
||||
},
|
||||
),
|
||||
return '当前:「$audioNormalization」$fallback';
|
||||
},
|
||||
onTap: audioNormalization,
|
||||
),
|
||||
SettingsModel(
|
||||
settingsType: SettingsType.normal,
|
||||
title: '超分辨率',
|
||||
@@ -1188,3 +1123,99 @@ List<SettingsModel> get extraSettings => [
|
||||
},
|
||||
),
|
||||
];
|
||||
|
||||
Future<void> audioNormalization(
|
||||
VoidCallback setState, {
|
||||
bool fallback = false,
|
||||
}) async {
|
||||
final key = fallback
|
||||
? SettingBoxKey.fallbackNormalization
|
||||
: SettingBoxKey.audioNormalization;
|
||||
final result = await showDialog<String>(
|
||||
context: Get.context!,
|
||||
builder: (context) {
|
||||
String audioNormalization = fallback
|
||||
? Pref.fallbackNormalization
|
||||
: Pref.audioNormalization;
|
||||
Set<String> values = {
|
||||
'0',
|
||||
'1',
|
||||
if (!fallback) '2',
|
||||
audioNormalization,
|
||||
'3',
|
||||
};
|
||||
return SelectDialog<String>(
|
||||
title: fallback ? '服务器无loudnorm配置时使用' : '音量均衡',
|
||||
toggleable: true,
|
||||
value: audioNormalization,
|
||||
values: values
|
||||
.map(
|
||||
(e) => (
|
||||
e,
|
||||
switch (e) {
|
||||
'0' => AudioNormalization.disable.title,
|
||||
'1' => AudioNormalization.dynaudnorm.title,
|
||||
'2' => AudioNormalization.loudnorm.title,
|
||||
'3' => AudioNormalization.custom.title,
|
||||
_ => e,
|
||||
},
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
);
|
||||
},
|
||||
);
|
||||
if (result != null) {
|
||||
if (result == '3') {
|
||||
String param = '';
|
||||
await showDialog(
|
||||
context: Get.context!,
|
||||
builder: (context) {
|
||||
return AlertDialog(
|
||||
title: const Text('自定义参数'),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
spacing: 16,
|
||||
children: [
|
||||
const Text('等同于 --lavfi-complex="[aid1] 参数 [ao]"'),
|
||||
TextField(
|
||||
autofocus: true,
|
||||
onChanged: (value) => param = value,
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: Get.back,
|
||||
child: Text(
|
||||
'取消',
|
||||
style: TextStyle(
|
||||
color: ColorScheme.of(context).outline,
|
||||
),
|
||||
),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
Get.back();
|
||||
await GStorage.setting.put(key, param);
|
||||
if (!fallback &&
|
||||
PlPlayerController.loudnormRegExp.hasMatch(param)) {
|
||||
audioNormalization(setState, fallback: true);
|
||||
}
|
||||
setState();
|
||||
},
|
||||
child: const Text('确定'),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
} else {
|
||||
await GStorage.setting.put(key, result);
|
||||
if (result == '2') {
|
||||
audioNormalization(setState, fallback: true);
|
||||
}
|
||||
setState();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ class SettingsModel {
|
||||
SettingsType.sw1tch => SetSwitchItem(
|
||||
title: title,
|
||||
subtitle: subtitle,
|
||||
setKey: setKey,
|
||||
setKey: setKey!,
|
||||
defaultVal: defaultVal,
|
||||
onChanged: onChanged,
|
||||
needReboot: needReboot,
|
||||
|
||||
@@ -16,6 +16,7 @@ class SelectDialog<T> extends StatelessWidget {
|
||||
final String title;
|
||||
final List<(T, String)> values;
|
||||
final Widget Function(BuildContext, int)? subtitleBuilder;
|
||||
final bool toggleable;
|
||||
|
||||
const SelectDialog({
|
||||
super.key,
|
||||
@@ -23,11 +24,12 @@ class SelectDialog<T> extends StatelessWidget {
|
||||
required this.values,
|
||||
required this.title,
|
||||
this.subtitleBuilder,
|
||||
this.toggleable = false,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final titleMedium = Theme.of(context).textTheme.titleMedium!;
|
||||
final titleMedium = TextTheme.of(context).titleMedium!;
|
||||
return AlertDialog(
|
||||
clipBehavior: Clip.hardEdge,
|
||||
title: Text(title),
|
||||
@@ -37,7 +39,7 @@ class SelectDialog<T> extends StatelessWidget {
|
||||
contentPadding: const EdgeInsets.symmetric(vertical: 12),
|
||||
content: SingleChildScrollView(
|
||||
child: RadioGroup<T>(
|
||||
onChanged: Navigator.of(context).pop,
|
||||
onChanged: (v) => Navigator.of(context).pop(v ?? value),
|
||||
groupValue: value,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
@@ -46,6 +48,7 @@ class SelectDialog<T> extends StatelessWidget {
|
||||
(index) {
|
||||
final item = values[index];
|
||||
return RadioListTile<T>(
|
||||
toggleable: toggleable,
|
||||
dense: true,
|
||||
value: item.$1,
|
||||
title: Text(
|
||||
|
||||
@@ -9,7 +9,7 @@ import 'package:get/get.dart';
|
||||
class SetSwitchItem extends StatefulWidget {
|
||||
final String? title;
|
||||
final String? subtitle;
|
||||
final String? setKey;
|
||||
final String setKey;
|
||||
final bool defaultVal;
|
||||
final ValueChanged<bool>? onChanged;
|
||||
final bool needReboot;
|
||||
@@ -21,7 +21,7 @@ class SetSwitchItem extends StatefulWidget {
|
||||
const SetSwitchItem({
|
||||
this.title,
|
||||
this.subtitle,
|
||||
this.setKey,
|
||||
required this.setKey,
|
||||
this.defaultVal = false,
|
||||
this.onChanged,
|
||||
this.needReboot = false,
|
||||
|
||||
Reference in New Issue
Block a user