mirror of
https://github.com/bggRGjQaUbCoE/PiliPlus.git
synced 2026-05-13 20:53:58 +08:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d3c99cc1c6 | ||
|
|
cc4f08e500 | ||
|
|
0afb6a3523 |
12
lib/models/common/audio_normalization.dart
Normal file
12
lib/models/common/audio_normalization.dart
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
enum AudioNormalization { disable, dynaudnorm, loudnorm, custom }
|
||||||
|
|
||||||
|
extension AudioNormalizationExt on AudioNormalization {
|
||||||
|
String get title => ['禁用', '预设 dynaudnorm', '预设 loudnorm', '自定义参数'][index];
|
||||||
|
String get param => [
|
||||||
|
'',
|
||||||
|
// ref https://github.com/KRTirtho/spotube/commit/da10ab2e291d4ba4d3082b9a6ae535639fb8f1b7
|
||||||
|
'dynaudnorm=g=5:f=250:r=0.9:p=0.5',
|
||||||
|
'loudnorm=I=-16:LRA=11:TP=-1.5',
|
||||||
|
'',
|
||||||
|
][index];
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ import 'dart:math';
|
|||||||
import 'package:PiliPlus/common/widgets/refresh_indicator.dart'
|
import 'package:PiliPlus/common/widgets/refresh_indicator.dart'
|
||||||
show kDragContainerExtentPercentage, displacement;
|
show kDragContainerExtentPercentage, displacement;
|
||||||
import 'package:PiliPlus/http/interceptor_anonymity.dart';
|
import 'package:PiliPlus/http/interceptor_anonymity.dart';
|
||||||
|
import 'package:PiliPlus/models/common/audio_normalization.dart';
|
||||||
import 'package:PiliPlus/models/common/dynamic_badge_mode.dart';
|
import 'package:PiliPlus/models/common/dynamic_badge_mode.dart';
|
||||||
import 'package:PiliPlus/models/common/dynamics_type.dart';
|
import 'package:PiliPlus/models/common/dynamics_type.dart';
|
||||||
import 'package:PiliPlus/models/common/nav_bar_config.dart';
|
import 'package:PiliPlus/models/common/nav_bar_config.dart';
|
||||||
@@ -1776,6 +1777,89 @@ List<SettingsModel> get extraSettings => [
|
|||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
SettingsModel(
|
||||||
|
settingsType: SettingsType.normal,
|
||||||
|
title: '音量均衡',
|
||||||
|
setKey: SettingBoxKey.audioNormalization,
|
||||||
|
leading: const Icon(Icons.multitrack_audio),
|
||||||
|
getSubtitle: () {
|
||||||
|
String audioNormalization = GStorage.audioNormalization;
|
||||||
|
audioNormalization = switch (audioNormalization) {
|
||||||
|
'0' => AudioNormalization.disable.title,
|
||||||
|
'1' => AudioNormalization.dynaudnorm.title,
|
||||||
|
'2' => AudioNormalization.loudnorm.title,
|
||||||
|
_ => audioNormalization,
|
||||||
|
};
|
||||||
|
return '当前:「$audioNormalization」';
|
||||||
|
},
|
||||||
|
onTap: (setState) async {
|
||||||
|
String? result = await showDialog(
|
||||||
|
context: Get.context!,
|
||||||
|
builder: (context) {
|
||||||
|
String audioNormalization = GStorage.audioNormalization;
|
||||||
|
Set<String> values = {'0', '1', '2', audioNormalization, '3'};
|
||||||
|
return SelectDialog<String>(
|
||||||
|
title: '音量均衡',
|
||||||
|
value: audioNormalization,
|
||||||
|
values: values.map((e) {
|
||||||
|
return {
|
||||||
|
'title': switch (e) {
|
||||||
|
'0' => AudioNormalization.disable.title,
|
||||||
|
'1' => AudioNormalization.dynaudnorm.title,
|
||||||
|
'2' => AudioNormalization.loudnorm.title,
|
||||||
|
'3' => AudioNormalization.custom.title,
|
||||||
|
_ => e,
|
||||||
|
},
|
||||||
|
'value': e,
|
||||||
|
};
|
||||||
|
}).toList());
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if (result != null) {
|
||||||
|
if (result == '3') {
|
||||||
|
String param = '';
|
||||||
|
showDialog(
|
||||||
|
context: Get.context!,
|
||||||
|
builder: (context) {
|
||||||
|
return AlertDialog(
|
||||||
|
title: const Text(
|
||||||
|
'自定义参数',
|
||||||
|
style: TextStyle(fontSize: 18),
|
||||||
|
),
|
||||||
|
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: Text('确定'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await GStorage.setting
|
||||||
|
.put(SettingBoxKey.audioNormalization, result);
|
||||||
|
setState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
SettingsModel(
|
SettingsModel(
|
||||||
settingsType: SettingsType.sw1tch,
|
settingsType: SettingsType.sw1tch,
|
||||||
enableFeedback: true,
|
enableFeedback: true,
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import 'dart:io';
|
|||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:PiliPlus/common/widgets/segment_progress_bar.dart';
|
import 'package:PiliPlus/common/widgets/segment_progress_bar.dart';
|
||||||
|
import 'package:PiliPlus/models/common/audio_normalization.dart';
|
||||||
import 'package:PiliPlus/utils/extension.dart';
|
import 'package:PiliPlus/utils/extension.dart';
|
||||||
import 'package:PiliPlus/utils/utils.dart';
|
import 'package:PiliPlus/utils/utils.dart';
|
||||||
import 'package:canvas_danmaku/canvas_danmaku.dart';
|
import 'package:canvas_danmaku/canvas_danmaku.dart';
|
||||||
@@ -513,7 +514,7 @@ class PlPlayerController {
|
|||||||
}
|
}
|
||||||
// 配置Player 音轨、字幕等等
|
// 配置Player 音轨、字幕等等
|
||||||
_videoPlayerController = await _createVideoController(
|
_videoPlayerController = await _createVideoController(
|
||||||
dataSource, _looping, enableHA, hwdec, width, height);
|
dataSource, _looping, enableHA, hwdec, width, height, seekTo);
|
||||||
callback?.call();
|
callback?.call();
|
||||||
// 获取视频时长 00:00
|
// 获取视频时长 00:00
|
||||||
_duration.value = duration ?? _videoPlayerController!.state.duration;
|
_duration.value = duration ?? _videoPlayerController!.state.duration;
|
||||||
@@ -530,7 +531,7 @@ class PlPlayerController {
|
|||||||
if (!_listenersInitialized) {
|
if (!_listenersInitialized) {
|
||||||
startListeners();
|
startListeners();
|
||||||
}
|
}
|
||||||
await _initializePlayer(seekTo: seekTo ?? Duration.zero);
|
await _initializePlayer();
|
||||||
setSubtitle(this.vttSubtitlesIndex.value);
|
setSubtitle(this.vttSubtitlesIndex.value);
|
||||||
} catch (err, stackTrace) {
|
} catch (err, stackTrace) {
|
||||||
dataStatus.status.value = DataStatus.error;
|
dataStatus.status.value = DataStatus.error;
|
||||||
@@ -547,6 +548,7 @@ class PlPlayerController {
|
|||||||
String? hwdec,
|
String? hwdec,
|
||||||
double? width,
|
double? width,
|
||||||
double? height,
|
double? height,
|
||||||
|
Duration? seekTo,
|
||||||
) async {
|
) async {
|
||||||
// 每次配置时先移除监听
|
// 每次配置时先移除监听
|
||||||
removeListeners();
|
removeListeners();
|
||||||
@@ -571,7 +573,19 @@ class PlPlayerController {
|
|||||||
);
|
);
|
||||||
var pp = player.platform as NativePlayer;
|
var pp = player.platform as NativePlayer;
|
||||||
// 解除倍速限制
|
// 解除倍速限制
|
||||||
await pp.setProperty("af", "scaletempo2=max-speed=8");
|
if (_videoPlayerController == null) {
|
||||||
|
String audioNormalization = GStorage.audioNormalization;
|
||||||
|
audioNormalization = switch (audioNormalization) {
|
||||||
|
'0' => '',
|
||||||
|
'1' => ',${AudioNormalization.dynaudnorm.param}',
|
||||||
|
'2' => ',${AudioNormalization.loudnorm.param}',
|
||||||
|
_ => ',$audioNormalization',
|
||||||
|
};
|
||||||
|
await pp.setProperty(
|
||||||
|
"af",
|
||||||
|
"scaletempo2=max-speed=8$audioNormalization",
|
||||||
|
);
|
||||||
|
}
|
||||||
// 音量不一致
|
// 音量不一致
|
||||||
if (Platform.isAndroid) {
|
if (Platform.isAndroid) {
|
||||||
await pp.setProperty("volume-max", "100");
|
await pp.setProperty("volume-max", "100");
|
||||||
@@ -633,12 +647,13 @@ class PlPlayerController {
|
|||||||
? dataSource.videoSource!
|
? dataSource.videoSource!
|
||||||
: "asset://${dataSource.videoSource!}";
|
: "asset://${dataSource.videoSource!}";
|
||||||
await player.open(
|
await player.open(
|
||||||
Media(assetUrl, httpHeaders: dataSource.httpHeaders),
|
Media(assetUrl, httpHeaders: dataSource.httpHeaders, start: seekTo),
|
||||||
play: false,
|
play: false,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
await player.open(
|
await player.open(
|
||||||
Media(dataSource.videoSource!, httpHeaders: dataSource.httpHeaders),
|
Media(dataSource.videoSource!,
|
||||||
|
httpHeaders: dataSource.httpHeaders, start: seekTo),
|
||||||
play: false,
|
play: false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -650,15 +665,15 @@ class PlPlayerController {
|
|||||||
return player;
|
return player;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future refreshPlayer() async {
|
Future<bool> refreshPlayer() async {
|
||||||
Duration currentPos = _position.value;
|
Duration currentPos = _position.value;
|
||||||
if (_videoPlayerController == null) {
|
if (_videoPlayerController == null) {
|
||||||
SmartDialog.showToast('视频播放器为空,请重新进入本页面');
|
SmartDialog.showToast('视频播放器为空,请重新进入本页面');
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
if (dataSource.videoSource?.isEmpty ?? true) {
|
if (dataSource.videoSource?.isEmpty ?? true) {
|
||||||
SmartDialog.showToast('视频源为空,请重新进入本页面');
|
SmartDialog.showToast('视频源为空,请重新进入本页面');
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
if (dataSource.audioSource?.isEmpty ?? true) {
|
if (dataSource.audioSource?.isEmpty ?? true) {
|
||||||
SmartDialog.showToast('音频源为空');
|
SmartDialog.showToast('音频源为空');
|
||||||
@@ -674,14 +689,16 @@ class PlPlayerController {
|
|||||||
Media(
|
Media(
|
||||||
dataSource.videoSource!,
|
dataSource.videoSource!,
|
||||||
httpHeaders: dataSource.httpHeaders,
|
httpHeaders: dataSource.httpHeaders,
|
||||||
|
start: currentPos,
|
||||||
),
|
),
|
||||||
play: true,
|
play: true,
|
||||||
);
|
);
|
||||||
seekTo(currentPos);
|
return true;
|
||||||
|
// seekTo(currentPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 开始播放
|
// 开始播放
|
||||||
Future _initializePlayer({Duration seekTo = Duration.zero}) async {
|
Future _initializePlayer() async {
|
||||||
if (_instance == null) return;
|
if (_instance == null) return;
|
||||||
// 设置倍速
|
// 设置倍速
|
||||||
if (videoType.value == 'live') {
|
if (videoType.value == 'live') {
|
||||||
@@ -699,9 +716,9 @@ class PlPlayerController {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
// 跳转播放
|
// 跳转播放
|
||||||
if (seekTo != Duration.zero) {
|
// if (seekTo != Duration.zero) {
|
||||||
await this.seekTo(seekTo);
|
// await this.seekTo(seekTo);
|
||||||
}
|
// }
|
||||||
|
|
||||||
// 自动播放
|
// 自动播放
|
||||||
if (_autoPlay) {
|
if (_autoPlay) {
|
||||||
@@ -830,7 +847,8 @@ class PlPlayerController {
|
|||||||
SmartDialog.showToast('无法加载解码器, $event,可能会切换至软解');
|
SmartDialog.showToast('无法加载解码器, $event,可能会切换至软解');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
SmartDialog.showToast('视频加载错误, $event');
|
// SmartDialog.showToast('视频加载错误, $event');
|
||||||
|
debugPrint('视频加载错误, $event');
|
||||||
}),
|
}),
|
||||||
// videoPlayerController!.stream.volume.listen((event) {
|
// videoPlayerController!.stream.volume.listen((event) {
|
||||||
// if (!mute.value && _volumeBeforeMute != event) {
|
// if (!mute.value && _volumeBeforeMute != event) {
|
||||||
|
|||||||
@@ -345,6 +345,9 @@ class GStorage {
|
|||||||
static bool get showHotRcmd =>
|
static bool get showHotRcmd =>
|
||||||
GStorage.setting.get(SettingBoxKey.showHotRcmd, defaultValue: false);
|
GStorage.setting.get(SettingBoxKey.showHotRcmd, defaultValue: false);
|
||||||
|
|
||||||
|
static String get audioNormalization =>
|
||||||
|
GStorage.setting.get(SettingBoxKey.audioNormalization, defaultValue: '0');
|
||||||
|
|
||||||
static List<double> get dynamicDetailRatio => List<double>.from(setting
|
static List<double> get dynamicDetailRatio => List<double>.from(setting
|
||||||
.get(SettingBoxKey.dynamicDetailRatio, defaultValue: [60.0, 40.0]));
|
.get(SettingBoxKey.dynamicDetailRatio, defaultValue: [60.0, 40.0]));
|
||||||
|
|
||||||
@@ -566,6 +569,7 @@ class SettingBoxKey {
|
|||||||
showVipDanmaku = 'showVipDanmaku',
|
showVipDanmaku = 'showVipDanmaku',
|
||||||
mergeDanmaku = 'mergeDanmaku',
|
mergeDanmaku = 'mergeDanmaku',
|
||||||
showHotRcmd = 'showHotRcmd',
|
showHotRcmd = 'showHotRcmd',
|
||||||
|
audioNormalization = 'audioNormalization',
|
||||||
|
|
||||||
// Sponsor Block
|
// Sponsor Block
|
||||||
enableSponsorBlock = 'enableSponsorBlock',
|
enableSponsorBlock = 'enableSponsorBlock',
|
||||||
|
|||||||
@@ -252,7 +252,7 @@ packages:
|
|||||||
description:
|
description:
|
||||||
path: "."
|
path: "."
|
||||||
ref: main
|
ref: main
|
||||||
resolved-ref: f58dd3bce638a07b4fab480222b6dbd1189687a9
|
resolved-ref: ce086d5d13770d8cabb78204f9af3f6edd4af3b1
|
||||||
url: "https://github.com/bggRGjQaUbCoE/canvas_danmaku.git"
|
url: "https://github.com/bggRGjQaUbCoE/canvas_danmaku.git"
|
||||||
source: git
|
source: git
|
||||||
version: "0.2.6"
|
version: "0.2.6"
|
||||||
|
|||||||
Reference in New Issue
Block a user