mirror of
https://github.com/bggRGjQaUbCoE/PiliPlus.git
synced 2026-05-21 16:48:43 +00:00
tweaks (#1816)
* opt: getFileName * opt: audio-pitch-correction * opt: spring dialog * opt: account dialog * update [skip ci] --------- Co-authored-by: dom <githubaccount56556@proton.me>
This commit is contained in:
committed by
GitHub
parent
9442b17d63
commit
d6e6e52df2
@@ -740,116 +740,114 @@ class LoginPageController extends GetxController
|
|||||||
bool quickSelect = selectAccount.every((e) => e == selectAccount.first);
|
bool quickSelect = selectAccount.every((e) => e == selectAccount.first);
|
||||||
return showDialog(
|
return showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => Builder(
|
builder: (context) => AlertDialog(
|
||||||
builder: (context) => AlertDialog(
|
title: Row(
|
||||||
title: Row(
|
crossAxisAlignment: .start,
|
||||||
crossAxisAlignment: .start,
|
mainAxisAlignment: .spaceBetween,
|
||||||
mainAxisAlignment: .spaceBetween,
|
children: [
|
||||||
children: [
|
Text.rich(
|
||||||
Text.rich(
|
style: const TextStyle(height: 1.5),
|
||||||
style: const TextStyle(height: 1.5),
|
TextSpan(
|
||||||
TextSpan(
|
children: [
|
||||||
children: [
|
const TextSpan(text: '账号切换'),
|
||||||
const TextSpan(text: '账号切换'),
|
TextSpan(
|
||||||
TextSpan(
|
text: '\nmid 为0时使用匿名',
|
||||||
text: '\nmid 为0时使用匿名',
|
style: TextStyle(
|
||||||
style: TextStyle(
|
fontSize: 14,
|
||||||
fontSize: 14,
|
color: ColorScheme.of(context).outline,
|
||||||
color: ColorScheme.of(context).outline,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
),
|
],
|
||||||
),
|
|
||||||
TextButton(
|
|
||||||
onPressed: () {
|
|
||||||
quickSelect = !quickSelect;
|
|
||||||
(context as Element).markNeedsBuild();
|
|
||||||
},
|
|
||||||
child: const Text('切换'),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
titlePadding: const .only(left: 22, top: 16, right: 22, bottom: 3),
|
|
||||||
contentPadding: const .symmetric(vertical: 5),
|
|
||||||
actionsPadding: const .only(left: 16, right: 16, bottom: 10),
|
|
||||||
content: SingleChildScrollView(
|
|
||||||
child: AnimatedSize(
|
|
||||||
curve: Curves.easeIn,
|
|
||||||
alignment: .topCenter,
|
|
||||||
duration: const Duration(milliseconds: 200),
|
|
||||||
child: quickSelect
|
|
||||||
? Builder(
|
|
||||||
builder: (context) => RadioGroup<Account>(
|
|
||||||
groupValue: selectAccount[0],
|
|
||||||
onChanged: (v) {
|
|
||||||
for (int i = 0; i < selectAccount.length; i++) {
|
|
||||||
selectAccount[i] = v!;
|
|
||||||
}
|
|
||||||
(context as Element).markNeedsBuild();
|
|
||||||
},
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: .start,
|
|
||||||
children: options.entries
|
|
||||||
.map(
|
|
||||||
(entry) => RadioWidget<Account>(
|
|
||||||
value: entry.key,
|
|
||||||
title: entry.value,
|
|
||||||
mainAxisSize: .max,
|
|
||||||
padding: const .only(left: 12),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.toList(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
: Column(
|
|
||||||
crossAxisAlignment: .start,
|
|
||||||
children: AccountType.values
|
|
||||||
.map(
|
|
||||||
(e) => Builder(
|
|
||||||
builder: (context) => RadioGroup<Account>(
|
|
||||||
groupValue: selectAccount[e.index],
|
|
||||||
onChanged: (v) {
|
|
||||||
selectAccount[e.index] = v!;
|
|
||||||
(context as Element).markNeedsBuild();
|
|
||||||
},
|
|
||||||
child: WrapRadioOptionsGroup<Account>(
|
|
||||||
groupTitle: e.title,
|
|
||||||
options: options,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.toList(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
actions: [
|
|
||||||
TextButton(
|
|
||||||
onPressed: Get.back,
|
|
||||||
child: Text(
|
|
||||||
'取消',
|
|
||||||
style: TextStyle(color: ColorScheme.of(context).outline),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Get.back();
|
quickSelect = !quickSelect;
|
||||||
for (final type in AccountType.values) {
|
(context as Element).markNeedsBuild();
|
||||||
final index = type.index;
|
|
||||||
final account = quickSelect
|
|
||||||
? selectAccount.first
|
|
||||||
: selectAccount[index];
|
|
||||||
if (account != Accounts.accountMode[index]) {
|
|
||||||
Accounts.set(type, account);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
child: const Text('确定'),
|
child: const Text('切换'),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
titlePadding: const .only(left: 22, top: 16, right: 22, bottom: 3),
|
||||||
|
contentPadding: const .symmetric(vertical: 5),
|
||||||
|
actionsPadding: const .only(left: 16, right: 16, bottom: 10),
|
||||||
|
content: SingleChildScrollView(
|
||||||
|
child: AnimatedSize(
|
||||||
|
curve: Curves.easeIn,
|
||||||
|
alignment: .topCenter,
|
||||||
|
duration: const Duration(milliseconds: 200),
|
||||||
|
child: quickSelect
|
||||||
|
? Builder(
|
||||||
|
builder: (context) => RadioGroup<Account>(
|
||||||
|
groupValue: selectAccount[0],
|
||||||
|
onChanged: (v) {
|
||||||
|
selectAccount.fillRange(0, selectAccount.length, v);
|
||||||
|
(context as Element).markNeedsBuild();
|
||||||
|
},
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: .start,
|
||||||
|
children: options.entries
|
||||||
|
.map(
|
||||||
|
(entry) => RadioWidget<Account>(
|
||||||
|
value: entry.key,
|
||||||
|
title: entry.value,
|
||||||
|
mainAxisSize: .max,
|
||||||
|
padding: PlatformUtils.isDesktop
|
||||||
|
? const .only(left: 12)
|
||||||
|
: const .only(left: 12, top: 2, bottom: 2),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: Column(
|
||||||
|
crossAxisAlignment: .start,
|
||||||
|
children: AccountType.values
|
||||||
|
.map(
|
||||||
|
(e) => Builder(
|
||||||
|
builder: (context) => RadioGroup<Account>(
|
||||||
|
groupValue: selectAccount[e.index],
|
||||||
|
onChanged: (v) {
|
||||||
|
selectAccount[e.index] = v!;
|
||||||
|
(context as Element).markNeedsBuild();
|
||||||
|
},
|
||||||
|
child: WrapRadioOptionsGroup<Account>(
|
||||||
|
groupTitle: e.title,
|
||||||
|
options: options,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: Get.back,
|
||||||
|
child: Text(
|
||||||
|
'取消',
|
||||||
|
style: TextStyle(color: ColorScheme.of(context).outline),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
Get.back();
|
||||||
|
for (final type in AccountType.values) {
|
||||||
|
final index = type.index;
|
||||||
|
final account = quickSelect
|
||||||
|
? selectAccount.first
|
||||||
|
: selectAccount[index];
|
||||||
|
if (account != Accounts.accountMode[index]) {
|
||||||
|
Accounts.set(type, account);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: const Text('确定'),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
import 'dart:math' as math;
|
||||||
|
|
||||||
import 'package:PiliPlus/common/widgets/color_palette.dart';
|
import 'package:PiliPlus/common/widgets/color_palette.dart';
|
||||||
import 'package:PiliPlus/common/widgets/custom_toast.dart';
|
import 'package:PiliPlus/common/widgets/custom_toast.dart';
|
||||||
@@ -490,60 +491,41 @@ void _showUiScaleDialog(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _showSpringDurationDialog(BuildContext context) {
|
|
||||||
String initialValue = '500';
|
|
||||||
showDialog(
|
|
||||||
context: context,
|
|
||||||
builder: (context) => AlertDialog(
|
|
||||||
title: const Text('滑动时间'),
|
|
||||||
content: TextFormField(
|
|
||||||
autofocus: true,
|
|
||||||
keyboardType: .number,
|
|
||||||
initialValue: initialValue,
|
|
||||||
onChanged: (value) => initialValue = value,
|
|
||||||
inputFormatters: [FilteringTextInputFormatter.digitsOnly],
|
|
||||||
decoration: const InputDecoration(suffixText: 'ms'),
|
|
||||||
),
|
|
||||||
actions: [
|
|
||||||
TextButton(
|
|
||||||
onPressed: Get.back,
|
|
||||||
child: Text(
|
|
||||||
'取消',
|
|
||||||
style: TextStyle(color: ColorScheme.of(context).outline),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
TextButton(
|
|
||||||
onPressed: () {
|
|
||||||
try {
|
|
||||||
final milliseconds = int.parse(initialValue);
|
|
||||||
Get.back();
|
|
||||||
final springDescription = SpringDescription.withDurationAndBounce(
|
|
||||||
duration: Duration(milliseconds: milliseconds),
|
|
||||||
);
|
|
||||||
GStorage.setting.put(
|
|
||||||
SettingBoxKey.springDescription,
|
|
||||||
[
|
|
||||||
springDescription.mass,
|
|
||||||
springDescription.stiffness,
|
|
||||||
springDescription.damping,
|
|
||||||
],
|
|
||||||
);
|
|
||||||
SmartDialog.showToast('设置成功,重启生效');
|
|
||||||
} catch (e) {
|
|
||||||
SmartDialog.showToast(e.toString());
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: const Text('确定'),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _showSpringDialog(BuildContext context, _) {
|
void _showSpringDialog(BuildContext context, _) {
|
||||||
final List<String> springDescription = Pref.springDescription
|
final List<String> springDescription = Pref.springDescription
|
||||||
.map((i) => i.toString())
|
.map((i) => i.toString())
|
||||||
.toList();
|
.toList(growable: false);
|
||||||
|
bool physicalMode = true;
|
||||||
|
|
||||||
|
void physical2Duration() {
|
||||||
|
final mass = double.parse(springDescription[0]);
|
||||||
|
final stiffness = double.parse(springDescription[1]);
|
||||||
|
final damping = double.parse(springDescription[2]);
|
||||||
|
|
||||||
|
final duration = math.sqrt(4 * math.pi * math.pi * mass / stiffness);
|
||||||
|
final dampingRatio = damping / (2.0 * math.sqrt(mass * stiffness));
|
||||||
|
final bounce = dampingRatio < 1.0
|
||||||
|
? 1.0 - dampingRatio
|
||||||
|
: 1.0 / dampingRatio - 1;
|
||||||
|
|
||||||
|
springDescription[0] = duration.toString();
|
||||||
|
springDescription[1] = bounce.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// from [SpringDescription.withDurationAndBounce] but with higher precision
|
||||||
|
void duration2Physical() {
|
||||||
|
final duration = double.parse(springDescription[0]);
|
||||||
|
final bounce = double.parse(springDescription[1]).clamp(-1.0, 1.0);
|
||||||
|
|
||||||
|
final stiffness = 4 * math.pi * math.pi / math.pow(duration, 2);
|
||||||
|
final dampingRatio = bounce > 0 ? 1.0 - bounce : 1.0 / (bounce + 1);
|
||||||
|
final damping = 2 * math.sqrt(stiffness) * dampingRatio;
|
||||||
|
|
||||||
|
springDescription[0] = '1';
|
||||||
|
springDescription[1] = stiffness.toString();
|
||||||
|
springDescription[2] = damping.toString();
|
||||||
|
}
|
||||||
|
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => AlertDialog(
|
builder: (context) => AlertDialog(
|
||||||
@@ -557,27 +539,45 @@ void _showSpringDialog(BuildContext context, _) {
|
|||||||
tapTargetSize: .shrinkWrap,
|
tapTargetSize: .shrinkWrap,
|
||||||
),
|
),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Get.back();
|
try {
|
||||||
_showSpringDurationDialog(context);
|
if (physicalMode) {
|
||||||
|
physical2Duration();
|
||||||
|
} else {
|
||||||
|
duration2Physical();
|
||||||
|
}
|
||||||
|
physicalMode = !physicalMode;
|
||||||
|
(context as Element).markNeedsBuild();
|
||||||
|
} catch (e) {
|
||||||
|
SmartDialog.showToast(e.toString());
|
||||||
|
}
|
||||||
},
|
},
|
||||||
child: const Text('滑动时间'),
|
child: Text(physicalMode ? '滑动时间' : '物理参数'),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
content: Column(
|
content: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
key: ValueKey(physicalMode),
|
||||||
|
mainAxisSize: .min,
|
||||||
children: List.generate(
|
children: List.generate(
|
||||||
3,
|
physicalMode ? 3 : 2,
|
||||||
(index) => TextFormField(
|
(index) => TextFormField(
|
||||||
autofocus: index == 0,
|
autofocus: index == 0,
|
||||||
initialValue: springDescription[index],
|
initialValue: springDescription[index],
|
||||||
keyboardType: const .numberWithOptions(decimal: true),
|
keyboardType: .numberWithOptions(
|
||||||
|
signed: !physicalMode && index == 1,
|
||||||
|
decimal: true,
|
||||||
|
),
|
||||||
onChanged: (value) => springDescription[index] = value,
|
onChanged: (value) => springDescription[index] = value,
|
||||||
inputFormatters: [
|
inputFormatters: [
|
||||||
FilteringTextInputFormatter.allow(RegExp(r'[\d\.]+')),
|
!physicalMode && index == 1
|
||||||
|
? FilteringTextInputFormatter.allow(RegExp(r'[-\d\.]+'))
|
||||||
|
: FilteringTextInputFormatter.allow(RegExp(r'[\d\.]+')),
|
||||||
],
|
],
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: const ['mass', 'stiffness', 'damping'][index],
|
labelText: (physicalMode
|
||||||
|
? const ['mass', 'stiffness', 'damping']
|
||||||
|
: const ['duration', 'bounce'])[index],
|
||||||
|
suffixText: !physicalMode && index == 0 ? 's' : null,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -601,6 +601,9 @@ void _showSpringDialog(BuildContext context, _) {
|
|||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
try {
|
try {
|
||||||
|
if (!physicalMode) {
|
||||||
|
duration2Physical();
|
||||||
|
}
|
||||||
final res = springDescription.map(double.parse).toList();
|
final res = springDescription.map(double.parse).toList();
|
||||||
Get.back();
|
Get.back();
|
||||||
GStorage.setting.put(SettingBoxKey.springDescription, res);
|
GStorage.setting.put(SettingBoxKey.springDescription, res);
|
||||||
|
|||||||
@@ -793,7 +793,7 @@ class PlPlayerController {
|
|||||||
if (isAnim) {
|
if (isAnim) {
|
||||||
setShader(superResolutionType.value, pp);
|
setShader(superResolutionType.value, pp);
|
||||||
}
|
}
|
||||||
await pp.setProperty("af", "scaletempo2=max-speed=8");
|
// await pp.setProperty('audio-pitch-correction', 'yes'); // default yes
|
||||||
if (Platform.isAndroid) {
|
if (Platform.isAndroid) {
|
||||||
await pp.setProperty("volume-max", "100");
|
await pp.setProperty("volume-max", "100");
|
||||||
await pp.setProperty("ao", Pref.audioOutput);
|
await pp.setProperty("ao", Pref.audioOutput);
|
||||||
|
|||||||
@@ -162,15 +162,28 @@ abstract final class Utils {
|
|||||||
return randomBase64.substring(0, randomBase64.length - 2);
|
return randomBase64.substring(0, randomBase64.length - 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int _getExt(String uri) {
|
|
||||||
final i = uri.indexOf('?');
|
|
||||||
return i == -1 ? uri.length : i;
|
|
||||||
}
|
|
||||||
|
|
||||||
static String getFileName(String uri, {bool fileExt = true}) {
|
static String getFileName(String uri, {bool fileExt = true}) {
|
||||||
final i0 = uri.lastIndexOf('/') + 1;
|
int slash = -1;
|
||||||
final i1 = fileExt ? _getExt(uri) : uri.lastIndexOf('.');
|
int dot = -1;
|
||||||
return uri.substring(i0, i1);
|
int qMark = uri.length;
|
||||||
|
|
||||||
|
loop:
|
||||||
|
for (int index = uri.length - 1; index >= 0; index--) {
|
||||||
|
switch (uri.codeUnitAt(index)) {
|
||||||
|
case 0x2F: // `/`
|
||||||
|
slash = index;
|
||||||
|
break loop;
|
||||||
|
case 0x2E: // `.`
|
||||||
|
if (dot == -1) dot = index;
|
||||||
|
break;
|
||||||
|
case 0x3F: // `?`
|
||||||
|
qMark = index;
|
||||||
|
if (dot > qMark) dot = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RangeError.checkNotNegative(slash, '/');
|
||||||
|
return uri.substring(slash + 1, (fileExt || dot == -1) ? qMark : dot);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// When calling this from a `catch` block consider annotating the method
|
/// When calling this from a `catch` block consider annotating the method
|
||||||
|
|||||||
Reference in New Issue
Block a user