mirror of
https://github.com/bggRGjQaUbCoE/PiliPlus.git
synced 2026-08-04 17:50:13 +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
@@ -2,9 +2,24 @@ enum AudioNormalization {
|
|||||||
disable('禁用'),
|
disable('禁用'),
|
||||||
// ref https://github.com/KRTirtho/spotube/commit/da10ab2e291d4ba4d3082b9a6ae535639fb8f1b7
|
// ref https://github.com/KRTirtho/spotube/commit/da10ab2e291d4ba4d3082b9a6ae535639fb8f1b7
|
||||||
dynaudnorm('预设 dynaudnorm', 'dynaudnorm=g=5:f=250:r=0.9:p=0.5'),
|
dynaudnorm('预设 dynaudnorm', 'dynaudnorm=g=5:f=250:r=0.9:p=0.5'),
|
||||||
|
loudnorm('预设 loudnorm', 'loudnorm=I=-16:LRA=11:TP=-1.5'),
|
||||||
custom('自定义参数');
|
custom('自定义参数');
|
||||||
|
|
||||||
final String title;
|
final String title;
|
||||||
final String param;
|
final String param;
|
||||||
const AudioNormalization(this.title, [this.param = '']);
|
const AudioNormalization(this.title, [this.param = '']);
|
||||||
|
|
||||||
|
static String getTitleFromConfig(String config) => switch (config) {
|
||||||
|
'0' => disable.title,
|
||||||
|
'1' => dynaudnorm.title,
|
||||||
|
'2' => loudnorm.title,
|
||||||
|
_ => config,
|
||||||
|
};
|
||||||
|
|
||||||
|
static String getParamFromConfig(String config) => switch (config) {
|
||||||
|
'0' => disable.param,
|
||||||
|
'1' => dynaudnorm.param,
|
||||||
|
'2' => loudnorm.param,
|
||||||
|
_ => config,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ class PlayUrlModel {
|
|||||||
Dash? dash;
|
Dash? dash;
|
||||||
List<Durl>? durl;
|
List<Durl>? durl;
|
||||||
List<FormatItem>? supportFormats;
|
List<FormatItem>? supportFormats;
|
||||||
|
Volume? volume;
|
||||||
int? lastPlayTime;
|
int? lastPlayTime;
|
||||||
int? lastPlayCid;
|
int? lastPlayCid;
|
||||||
String? curLanguage;
|
String? curLanguage;
|
||||||
@@ -62,6 +63,7 @@ class PlayUrlModel {
|
|||||||
supportFormats = (json['support_formats'] as List?)
|
supportFormats = (json['support_formats'] as List?)
|
||||||
?.map<FormatItem>((e) => FormatItem.fromJson(e))
|
?.map<FormatItem>((e) => FormatItem.fromJson(e))
|
||||||
.toList();
|
.toList();
|
||||||
|
volume = json['volume'] == null ? null : Volume.fromJson(json['volume']);
|
||||||
lastPlayTime = json['last_play_time'];
|
lastPlayTime = json['last_play_time'];
|
||||||
lastPlayCid = json['last_play_cid'];
|
lastPlayCid = json['last_play_cid'];
|
||||||
curLanguage = json['cur_language'];
|
curLanguage = json['cur_language'];
|
||||||
@@ -304,3 +306,48 @@ class FormatItem {
|
|||||||
codecs = (json['codecs'] as List?)?.fromCast<String>();
|
codecs = (json['codecs'] as List?)?.fromCast<String>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class Volume {
|
||||||
|
Volume({
|
||||||
|
required this.measuredI,
|
||||||
|
required this.measuredLra,
|
||||||
|
required this.measuredTp,
|
||||||
|
required this.measuredThreshold,
|
||||||
|
required this.targetOffset,
|
||||||
|
required this.targetI,
|
||||||
|
required this.targetTp,
|
||||||
|
// required this.multiSceneArgs,
|
||||||
|
});
|
||||||
|
|
||||||
|
final num measuredI;
|
||||||
|
final num measuredLra;
|
||||||
|
final num measuredTp;
|
||||||
|
final num measuredThreshold;
|
||||||
|
final num targetOffset;
|
||||||
|
final num targetI;
|
||||||
|
final num targetTp;
|
||||||
|
// final MultiSceneArgs? multiSceneArgs;
|
||||||
|
|
||||||
|
factory Volume.fromJson(Map<String, dynamic> json) {
|
||||||
|
return Volume(
|
||||||
|
measuredI: json["measured_i"] ?? 0,
|
||||||
|
measuredLra: json["measured_lra"] ?? 0,
|
||||||
|
measuredTp: json["measured_tp"] ?? 0,
|
||||||
|
measuredThreshold: json["measured_threshold"] ?? 0,
|
||||||
|
targetOffset: json["target_offset"] ?? 0,
|
||||||
|
targetI: json["target_i"] ?? 0,
|
||||||
|
targetTp: json["target_tp"] ?? 0,
|
||||||
|
// multiSceneArgs: json["multi_scene_args"] == null ? null : MultiSceneArgs.fromJson(json["multi_scene_args"]),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() =>
|
||||||
|
'measured_I=$measuredI:measured_LRA=$measuredLra:measured_TP=$measuredTp:measured_thresh=$measuredThreshold';
|
||||||
|
|
||||||
|
bool get isNotEmpty =>
|
||||||
|
measuredI != 0 ||
|
||||||
|
measuredLra != 0 ||
|
||||||
|
measuredTp != 0 ||
|
||||||
|
measuredThreshold != 0;
|
||||||
|
}
|
||||||
|
|||||||
@@ -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/select_dialog.dart';
|
||||||
import 'package:PiliPlus/pages/setting/widgets/slide_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/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/accounts.dart';
|
||||||
import 'package:PiliPlus/utils/cache_manage.dart';
|
import 'package:PiliPlus/utils/cache_manage.dart';
|
||||||
import 'package:PiliPlus/utils/feed_back.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/storage_pref.dart';
|
||||||
import 'package:PiliPlus/utils/update.dart';
|
import 'package:PiliPlus/utils/update.dart';
|
||||||
import 'package:PiliPlus/utils/utils.dart';
|
import 'package:PiliPlus/utils/utils.dart';
|
||||||
|
import 'package:flutter/foundation.dart' show kDebugMode;
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
@@ -453,94 +455,27 @@ List<SettingsModel> get extraSettings => [
|
|||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
SettingsModel(
|
if (kDebugMode || Platform.isAndroid)
|
||||||
settingsType: SettingsType.normal,
|
SettingsModel(
|
||||||
title: '音量均衡',
|
settingsType: SettingsType.normal,
|
||||||
setKey: SettingBoxKey.audioNormalization,
|
title: '音量均衡',
|
||||||
leading: const Icon(Icons.multitrack_audio),
|
setKey: SettingBoxKey.audioNormalization,
|
||||||
getSubtitle: () {
|
leading: const Icon(Icons.multitrack_audio),
|
||||||
String audioNormalization = Pref.audioNormalization;
|
getSubtitle: () {
|
||||||
// TODO: remove next version
|
final audioNormalization = AudioNormalization.getTitleFromConfig(
|
||||||
if (audioNormalization == '2') {
|
Pref.audioNormalization,
|
||||||
GStorage.setting.put(SettingBoxKey.audioNormalization, '1');
|
);
|
||||||
audioNormalization = '1';
|
String fallback = Pref.fallbackNormalization;
|
||||||
}
|
if (fallback == '0') {
|
||||||
audioNormalization = switch (audioNormalization) {
|
fallback = '';
|
||||||
'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('确定'),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
await GStorage.setting.put(SettingBoxKey.audioNormalization, result);
|
fallback =
|
||||||
setState();
|
',无参数时:「${AudioNormalization.getTitleFromConfig(fallback)}」';
|
||||||
}
|
}
|
||||||
}
|
return '当前:「$audioNormalization」$fallback';
|
||||||
},
|
},
|
||||||
),
|
onTap: audioNormalization,
|
||||||
|
),
|
||||||
SettingsModel(
|
SettingsModel(
|
||||||
settingsType: SettingsType.normal,
|
settingsType: SettingsType.normal,
|
||||||
title: '超分辨率',
|
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(
|
SettingsType.sw1tch => SetSwitchItem(
|
||||||
title: title,
|
title: title,
|
||||||
subtitle: subtitle,
|
subtitle: subtitle,
|
||||||
setKey: setKey,
|
setKey: setKey!,
|
||||||
defaultVal: defaultVal,
|
defaultVal: defaultVal,
|
||||||
onChanged: onChanged,
|
onChanged: onChanged,
|
||||||
needReboot: needReboot,
|
needReboot: needReboot,
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ class SelectDialog<T> extends StatelessWidget {
|
|||||||
final String title;
|
final String title;
|
||||||
final List<(T, String)> values;
|
final List<(T, String)> values;
|
||||||
final Widget Function(BuildContext, int)? subtitleBuilder;
|
final Widget Function(BuildContext, int)? subtitleBuilder;
|
||||||
|
final bool toggleable;
|
||||||
|
|
||||||
const SelectDialog({
|
const SelectDialog({
|
||||||
super.key,
|
super.key,
|
||||||
@@ -23,11 +24,12 @@ class SelectDialog<T> extends StatelessWidget {
|
|||||||
required this.values,
|
required this.values,
|
||||||
required this.title,
|
required this.title,
|
||||||
this.subtitleBuilder,
|
this.subtitleBuilder,
|
||||||
|
this.toggleable = false,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final titleMedium = Theme.of(context).textTheme.titleMedium!;
|
final titleMedium = TextTheme.of(context).titleMedium!;
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
clipBehavior: Clip.hardEdge,
|
clipBehavior: Clip.hardEdge,
|
||||||
title: Text(title),
|
title: Text(title),
|
||||||
@@ -37,7 +39,7 @@ class SelectDialog<T> extends StatelessWidget {
|
|||||||
contentPadding: const EdgeInsets.symmetric(vertical: 12),
|
contentPadding: const EdgeInsets.symmetric(vertical: 12),
|
||||||
content: SingleChildScrollView(
|
content: SingleChildScrollView(
|
||||||
child: RadioGroup<T>(
|
child: RadioGroup<T>(
|
||||||
onChanged: Navigator.of(context).pop,
|
onChanged: (v) => Navigator.of(context).pop(v ?? value),
|
||||||
groupValue: value,
|
groupValue: value,
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
@@ -46,6 +48,7 @@ class SelectDialog<T> extends StatelessWidget {
|
|||||||
(index) {
|
(index) {
|
||||||
final item = values[index];
|
final item = values[index];
|
||||||
return RadioListTile<T>(
|
return RadioListTile<T>(
|
||||||
|
toggleable: toggleable,
|
||||||
dense: true,
|
dense: true,
|
||||||
value: item.$1,
|
value: item.$1,
|
||||||
title: Text(
|
title: Text(
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import 'package:get/get.dart';
|
|||||||
class SetSwitchItem extends StatefulWidget {
|
class SetSwitchItem extends StatefulWidget {
|
||||||
final String? title;
|
final String? title;
|
||||||
final String? subtitle;
|
final String? subtitle;
|
||||||
final String? setKey;
|
final String setKey;
|
||||||
final bool defaultVal;
|
final bool defaultVal;
|
||||||
final ValueChanged<bool>? onChanged;
|
final ValueChanged<bool>? onChanged;
|
||||||
final bool needReboot;
|
final bool needReboot;
|
||||||
@@ -21,7 +21,7 @@ class SetSwitchItem extends StatefulWidget {
|
|||||||
const SetSwitchItem({
|
const SetSwitchItem({
|
||||||
this.title,
|
this.title,
|
||||||
this.subtitle,
|
this.subtitle,
|
||||||
this.setKey,
|
required this.setKey,
|
||||||
this.defaultVal = false,
|
this.defaultVal = false,
|
||||||
this.onChanged,
|
this.onChanged,
|
||||||
this.needReboot = false,
|
this.needReboot = false,
|
||||||
|
|||||||
@@ -1056,6 +1056,7 @@ class VideoDetailController extends GetxController
|
|||||||
Duration? seekToTime,
|
Duration? seekToTime,
|
||||||
Duration? duration,
|
Duration? duration,
|
||||||
bool? autoplay,
|
bool? autoplay,
|
||||||
|
Volume? volume,
|
||||||
}) async {
|
}) async {
|
||||||
await plPlayerController.setDataSource(
|
await plPlayerController.setDataSource(
|
||||||
DataSource(
|
DataSource(
|
||||||
@@ -1095,6 +1096,7 @@ class VideoDetailController extends GetxController
|
|||||||
},
|
},
|
||||||
width: firstVideo.width,
|
width: firstVideo.width,
|
||||||
height: firstVideo.height,
|
height: firstVideo.height,
|
||||||
|
volume: volume ?? this.volume,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (plPlayerController.enableSponsorBlock) {
|
if (plPlayerController.enableSponsorBlock) {
|
||||||
@@ -1125,6 +1127,8 @@ class VideoDetailController extends GetxController
|
|||||||
queryVideoUrl(defaultST: playedTime);
|
queryVideoUrl(defaultST: playedTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Volume? volume;
|
||||||
|
|
||||||
// 视频链接
|
// 视频链接
|
||||||
Future<void> queryVideoUrl({
|
Future<void> queryVideoUrl({
|
||||||
Duration? defaultST,
|
Duration? defaultST,
|
||||||
@@ -1164,6 +1168,8 @@ class VideoDetailController extends GetxController
|
|||||||
languages.value = data.language?.items;
|
languages.value = data.language?.items;
|
||||||
currLang.value = data.curLanguage;
|
currLang.value = data.curLanguage;
|
||||||
|
|
||||||
|
volume = data.volume;
|
||||||
|
|
||||||
if (data.acceptDesc?.contains('试看') == true) {
|
if (data.acceptDesc?.contains('试看') == true) {
|
||||||
SmartDialog.showToast(
|
SmartDialog.showToast(
|
||||||
'该视频为专属视频,仅提供试看',
|
'该视频为专属视频,仅提供试看',
|
||||||
|
|||||||
@@ -43,7 +43,6 @@ import 'package:get/get.dart';
|
|||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:intl/intl.dart' show DateFormat;
|
import 'package:intl/intl.dart' show DateFormat;
|
||||||
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
|
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
|
||||||
import 'package:media_kit/media_kit.dart';
|
|
||||||
|
|
||||||
class HeaderControl extends StatefulWidget {
|
class HeaderControl extends StatefulWidget {
|
||||||
const HeaderControl({
|
const HeaderControl({
|
||||||
@@ -446,8 +445,9 @@ class HeaderControlState extends State<HeaderControl> {
|
|||||||
SmartDialog.showToast('播放器未初始化');
|
SmartDialog.showToast('播放器未初始化');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final hwdec = await (player.platform as NativePlayer)
|
final hwdec = await player.platform!.getProperty(
|
||||||
.getProperty('hwdec-current');
|
'hwdec-current',
|
||||||
|
);
|
||||||
if (!context.mounted) return;
|
if (!context.mounted) return;
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import 'package:PiliPlus/models/common/sponsor_block/skip_type.dart';
|
|||||||
import 'package:PiliPlus/models/common/super_resolution_type.dart';
|
import 'package:PiliPlus/models/common/super_resolution_type.dart';
|
||||||
import 'package:PiliPlus/models/common/video/video_type.dart';
|
import 'package:PiliPlus/models/common/video/video_type.dart';
|
||||||
import 'package:PiliPlus/models/user/danmaku_rule.dart';
|
import 'package:PiliPlus/models/user/danmaku_rule.dart';
|
||||||
|
import 'package:PiliPlus/models/video/play/url.dart';
|
||||||
import 'package:PiliPlus/models_new/video/video_shot/data.dart';
|
import 'package:PiliPlus/models_new/video/video_shot/data.dart';
|
||||||
import 'package:PiliPlus/pages/mine/controller.dart';
|
import 'package:PiliPlus/pages/mine/controller.dart';
|
||||||
import 'package:PiliPlus/plugin/pl_player/models/bottom_progress_behavior.dart';
|
import 'package:PiliPlus/plugin/pl_player/models/bottom_progress_behavior.dart';
|
||||||
@@ -54,7 +55,6 @@ import 'package:media_kit/media_kit.dart';
|
|||||||
import 'package:media_kit_video/media_kit_video.dart';
|
import 'package:media_kit_video/media_kit_video.dart';
|
||||||
import 'package:path/path.dart' as path;
|
import 'package:path/path.dart' as path;
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:universal_platform/universal_platform.dart';
|
|
||||||
import 'package:window_manager/window_manager.dart';
|
import 'package:window_manager/window_manager.dart';
|
||||||
|
|
||||||
class PlPlayerController {
|
class PlPlayerController {
|
||||||
@@ -598,6 +598,7 @@ class PlPlayerController {
|
|||||||
int? pgcType,
|
int? pgcType,
|
||||||
VideoType? videoType,
|
VideoType? videoType,
|
||||||
VoidCallback? callback,
|
VoidCallback? callback,
|
||||||
|
Volume? volume,
|
||||||
}) async {
|
}) async {
|
||||||
try {
|
try {
|
||||||
_isLive = isLive;
|
_isLive = isLive;
|
||||||
@@ -637,6 +638,7 @@ class PlPlayerController {
|
|||||||
dataSource,
|
dataSource,
|
||||||
_looping,
|
_looping,
|
||||||
seekTo,
|
seekTo,
|
||||||
|
volume,
|
||||||
);
|
);
|
||||||
callback?.call();
|
callback?.call();
|
||||||
// 获取视频时长 00:00
|
// 获取视频时长 00:00
|
||||||
@@ -716,7 +718,7 @@ class PlPlayerController {
|
|||||||
setting.put(SettingBoxKey.superResolutionType, type.index);
|
setting.put(SettingBoxKey.superResolutionType, type.index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pp ??= _videoPlayerController?.platform as NativePlayer;
|
pp ??= _videoPlayerController!.platform!;
|
||||||
await pp.waitForPlayerInitialization;
|
await pp.waitForPlayerInitialization;
|
||||||
await pp.waitForVideoControllerInitializationIfAttached;
|
await pp.waitForVideoControllerInitializationIfAttached;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
@@ -745,11 +747,14 @@ class PlPlayerController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static final loudnormRegExp = RegExp('loudnorm=[^,]+');
|
||||||
|
|
||||||
// 配置播放器
|
// 配置播放器
|
||||||
Future<Player> _createVideoController(
|
Future<Player> _createVideoController(
|
||||||
DataSource dataSource,
|
DataSource dataSource,
|
||||||
PlaylistMode looping,
|
PlaylistMode looping,
|
||||||
Duration? seekTo,
|
Duration? seekTo,
|
||||||
|
Volume? volume,
|
||||||
) async {
|
) async {
|
||||||
// 每次配置时先移除监听
|
// 每次配置时先移除监听
|
||||||
removeListeners();
|
removeListeners();
|
||||||
@@ -759,6 +764,7 @@ class PlPlayerController {
|
|||||||
_position.value = Duration.zero;
|
_position.value = Duration.zero;
|
||||||
// 初始化时清空弹幕,防止上次重叠
|
// 初始化时清空弹幕,防止上次重叠
|
||||||
danmakuController?.clear();
|
danmakuController?.clear();
|
||||||
|
|
||||||
Player player =
|
Player player =
|
||||||
_videoPlayerController ??
|
_videoPlayerController ??
|
||||||
Player(
|
Player(
|
||||||
@@ -769,20 +775,14 @@ class PlPlayerController {
|
|||||||
: (isLive ? 16 * 1024 * 1024 : 4 * 1024 * 1024),
|
: (isLive ? 16 * 1024 * 1024 : 4 * 1024 * 1024),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
var pp = player.platform as NativePlayer;
|
final pp = player.platform!;
|
||||||
if (_videoPlayerController == null) {
|
if (_videoPlayerController == null) {
|
||||||
if (isAnim) {
|
if (isAnim) {
|
||||||
setShader(superResolutionType.value, pp);
|
setShader(superResolutionType.value, pp);
|
||||||
}
|
}
|
||||||
String audioNormalization = Pref.audioNormalization;
|
|
||||||
audioNormalization = switch (audioNormalization) {
|
|
||||||
'0' => '',
|
|
||||||
'1' => ',${AudioNormalization.dynaudnorm.param}',
|
|
||||||
_ => ',$audioNormalization',
|
|
||||||
};
|
|
||||||
await pp.setProperty(
|
await pp.setProperty(
|
||||||
"af",
|
"af",
|
||||||
"scaletempo2=max-speed=8$audioNormalization",
|
"scaletempo2=max-speed=8",
|
||||||
);
|
);
|
||||||
if (Platform.isAndroid) {
|
if (Platform.isAndroid) {
|
||||||
await pp.setProperty("volume-max", "100");
|
await pp.setProperty("volume-max", "100");
|
||||||
@@ -804,7 +804,7 @@ class PlPlayerController {
|
|||||||
if (dataSource.audioSource?.isNotEmpty == true) {
|
if (dataSource.audioSource?.isNotEmpty == true) {
|
||||||
await pp.setProperty(
|
await pp.setProperty(
|
||||||
'audio-files',
|
'audio-files',
|
||||||
UniversalPlatform.isWindows
|
Platform.isWindows
|
||||||
? dataSource.audioSource!.replaceAll(';', '\\;')
|
? dataSource.audioSource!.replaceAll(';', '\\;')
|
||||||
: dataSource.audioSource!.replaceAll(':', '\\:'),
|
: dataSource.audioSource!.replaceAll(':', '\\:'),
|
||||||
);
|
);
|
||||||
@@ -816,7 +816,7 @@ class PlPlayerController {
|
|||||||
if (dataSource.subFiles?.isNotEmpty == true) {
|
if (dataSource.subFiles?.isNotEmpty == true) {
|
||||||
await pp.setProperty(
|
await pp.setProperty(
|
||||||
'sub-files',
|
'sub-files',
|
||||||
UniversalPlatform.isWindows
|
Platform.isWindows
|
||||||
? dataSource.subFiles!.replaceAll(';', '\\;')
|
? dataSource.subFiles!.replaceAll(';', '\\;')
|
||||||
: dataSource.subFiles!.replaceAll(':', '\\:'),
|
: dataSource.subFiles!.replaceAll(':', '\\:'),
|
||||||
);
|
);
|
||||||
@@ -835,12 +835,44 @@ class PlPlayerController {
|
|||||||
);
|
);
|
||||||
|
|
||||||
player.setPlaylistMode(looping);
|
player.setPlaylistMode(looping);
|
||||||
|
|
||||||
|
final Map<String, String>? filters;
|
||||||
|
if (kDebugMode || Platform.isAndroid) {
|
||||||
|
String audioNormalization = '';
|
||||||
|
audioNormalization = AudioNormalization.getParamFromConfig(
|
||||||
|
Pref.audioNormalization,
|
||||||
|
);
|
||||||
|
if (volume != null && volume.isNotEmpty) {
|
||||||
|
audioNormalization = audioNormalization.replaceFirstMapped(
|
||||||
|
loudnormRegExp,
|
||||||
|
(i) => '${i[0]}:$volume',
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
audioNormalization = audioNormalization.replaceFirst(
|
||||||
|
loudnormRegExp,
|
||||||
|
AudioNormalization.getParamFromConfig(Pref.fallbackNormalization),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
filters = audioNormalization.isEmpty
|
||||||
|
? null
|
||||||
|
: {'lavfi-complex': '"[aid1] $audioNormalization [ao]"'};
|
||||||
|
} else {
|
||||||
|
filters = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (kDebugMode) debugPrint(filters.toString());
|
||||||
|
|
||||||
if (dataSource.type == DataSourceType.asset) {
|
if (dataSource.type == DataSourceType.asset) {
|
||||||
final assetUrl = dataSource.videoSource!.startsWith("asset://")
|
final assetUrl = dataSource.videoSource!.startsWith("asset://")
|
||||||
? dataSource.videoSource!
|
? dataSource.videoSource!
|
||||||
: "asset://${dataSource.videoSource!}";
|
: "asset://${dataSource.videoSource!}";
|
||||||
await player.open(
|
await player.open(
|
||||||
Media(assetUrl, httpHeaders: dataSource.httpHeaders, start: seekTo),
|
Media(
|
||||||
|
assetUrl,
|
||||||
|
httpHeaders: dataSource.httpHeaders,
|
||||||
|
start: seekTo,
|
||||||
|
extras: filters,
|
||||||
|
),
|
||||||
play: false,
|
play: false,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
@@ -849,6 +881,7 @@ class PlPlayerController {
|
|||||||
dataSource.videoSource!,
|
dataSource.videoSource!,
|
||||||
httpHeaders: dataSource.httpHeaders,
|
httpHeaders: dataSource.httpHeaders,
|
||||||
start: seekTo,
|
start: seekTo,
|
||||||
|
extras: filters,
|
||||||
),
|
),
|
||||||
play: false,
|
play: false,
|
||||||
);
|
);
|
||||||
@@ -874,9 +907,9 @@ class PlPlayerController {
|
|||||||
if (dataSource.audioSource.isNullOrEmpty) {
|
if (dataSource.audioSource.isNullOrEmpty) {
|
||||||
SmartDialog.showToast('音频源为空');
|
SmartDialog.showToast('音频源为空');
|
||||||
} else {
|
} else {
|
||||||
await (_videoPlayerController!.platform as NativePlayer).setProperty(
|
await (_videoPlayerController!.platform!).setProperty(
|
||||||
'audio-files',
|
'audio-files',
|
||||||
UniversalPlatform.isWindows
|
Platform.isWindows
|
||||||
? dataSource.audioSource!.replaceAll(';', '\\;')
|
? dataSource.audioSource!.replaceAll(';', '\\;')
|
||||||
: dataSource.audioSource!.replaceAll(':', '\\:'),
|
: dataSource.audioSource!.replaceAll(':', '\\:'),
|
||||||
);
|
);
|
||||||
@@ -1658,7 +1691,7 @@ class PlPlayerController {
|
|||||||
// dataStatus.status.close();
|
// dataStatus.status.close();
|
||||||
|
|
||||||
if (_videoPlayerController != null) {
|
if (_videoPlayerController != null) {
|
||||||
var pp = _videoPlayerController!.platform as NativePlayer;
|
var pp = _videoPlayerController!.platform!;
|
||||||
await pp.setProperty('audio-files', '');
|
await pp.setProperty('audio-files', '');
|
||||||
removeListeners();
|
removeListeners();
|
||||||
await _videoPlayerController!.dispose();
|
await _videoPlayerController!.dispose();
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import 'package:get/get_rx/get_rx.dart';
|
|||||||
import 'package:media_kit/ffi/src/allocation.dart';
|
import 'package:media_kit/ffi/src/allocation.dart';
|
||||||
import 'package:media_kit/ffi/src/utf8.dart';
|
import 'package:media_kit/ffi/src/utf8.dart';
|
||||||
import 'package:media_kit/generated/libmpv/bindings.dart' as generated;
|
import 'package:media_kit/generated/libmpv/bindings.dart' as generated;
|
||||||
|
import 'package:media_kit/media_kit.dart';
|
||||||
import 'package:media_kit/src/player/native/core/initializer.dart';
|
import 'package:media_kit/src/player/native/core/initializer.dart';
|
||||||
import 'package:media_kit/src/player/native/core/native_library.dart';
|
import 'package:media_kit/src/player/native/core/native_library.dart';
|
||||||
|
|
||||||
@@ -54,7 +55,15 @@ class MpvConvertWebp {
|
|||||||
'${Pref.hardwareDecoding},auto-copy', // transcode only support copy
|
'${Pref.hardwareDecoding},auto-copy', // transcode only support copy
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
_setHeader();
|
NativePlayer.setHeader(
|
||||||
|
const {
|
||||||
|
'user-agent':
|
||||||
|
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36',
|
||||||
|
'referer': HttpString.baseUrl,
|
||||||
|
},
|
||||||
|
_mpv,
|
||||||
|
_ctx,
|
||||||
|
);
|
||||||
if (progress != null) {
|
if (progress != null) {
|
||||||
_observeProperty('time-pos');
|
_observeProperty('time-pos');
|
||||||
}
|
}
|
||||||
@@ -79,7 +88,7 @@ class MpvConvertWebp {
|
|||||||
switch (event.ref.event_id) {
|
switch (event.ref.event_id) {
|
||||||
case generated.mpv_event_id.MPV_EVENT_PROPERTY_CHANGE:
|
case generated.mpv_event_id.MPV_EVENT_PROPERTY_CHANGE:
|
||||||
final prop = event.ref.data.cast<generated.mpv_event_property>().ref;
|
final prop = event.ref.data.cast<generated.mpv_event_property>().ref;
|
||||||
if (prop.name.cast<Utf8>().toDartString() == 'time-pos' &&
|
if (prop.name.toDartString() == 'time-pos' &&
|
||||||
prop.format == generated.mpv_format.MPV_FORMAT_DOUBLE) {
|
prop.format == generated.mpv_format.MPV_FORMAT_DOUBLE) {
|
||||||
progress!.value = (prop.data.cast<Double>().value - start) / duration;
|
progress!.value = (prop.data.cast<Double>().value - start) / duration;
|
||||||
}
|
}
|
||||||
@@ -89,9 +98,9 @@ class MpvConvertWebp {
|
|||||||
break;
|
break;
|
||||||
case generated.mpv_event_id.MPV_EVENT_LOG_MESSAGE:
|
case generated.mpv_event_id.MPV_EVENT_LOG_MESSAGE:
|
||||||
final log = event.ref.data.cast<generated.mpv_event_log_message>().ref;
|
final log = event.ref.data.cast<generated.mpv_event_log_message>().ref;
|
||||||
final prefix = log.prefix.cast<Utf8>().toDartString().trim();
|
final prefix = log.prefix.toDartString().trim();
|
||||||
final level = log.level.cast<Utf8>().toDartString().trim();
|
final level = log.level.toDartString().trim();
|
||||||
final text = log.text.cast<Utf8>().toDartString().trim();
|
final text = log.text.toDartString().trim();
|
||||||
debugPrint('WebpConvert: $level $prefix : $text');
|
debugPrint('WebpConvert: $level $prefix : $text');
|
||||||
if (kDebugMode) {
|
if (kDebugMode) {
|
||||||
_success = level != 'error' && level != 'fatal';
|
_success = level != 'error' && level != 'fatal';
|
||||||
@@ -108,18 +117,8 @@ class MpvConvertWebp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _command(List<String> args) {
|
void _command(List<String> args) =>
|
||||||
final pointers = args.map((e) => e.toNativeUtf8()).toList();
|
NativePlayer.statiCommand(args, _mpv, _ctx);
|
||||||
final arr = calloc<Pointer<Utf8>>(128);
|
|
||||||
for (int i = 0; i < args.length; i++) {
|
|
||||||
arr[i] = pointers[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
_mpv.mpv_command(_ctx, arr.cast());
|
|
||||||
|
|
||||||
calloc.free(arr);
|
|
||||||
pointers.forEach(calloc.free);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _observeProperty(String property) {
|
void _observeProperty(String property) {
|
||||||
final name = property.toNativeUtf8();
|
final name = property.toNativeUtf8();
|
||||||
@@ -132,47 +131,6 @@ class MpvConvertWebp {
|
|||||||
|
|
||||||
calloc.free(name);
|
calloc.free(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _setHeader() {
|
|
||||||
final property = 'http-header-fields'.toNativeUtf8();
|
|
||||||
// Allocate & fill the [mpv_node] with the headers.
|
|
||||||
final value = calloc<generated.mpv_node>();
|
|
||||||
final valRef = value.ref
|
|
||||||
..format = generated.mpv_format.MPV_FORMAT_NODE_ARRAY;
|
|
||||||
valRef.u.list = calloc<generated.mpv_node_list>();
|
|
||||||
final valList = valRef.u.list.ref
|
|
||||||
..num = 2
|
|
||||||
..values = calloc<generated.mpv_node>(2);
|
|
||||||
|
|
||||||
const entries = [
|
|
||||||
(
|
|
||||||
'user-agent',
|
|
||||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36',
|
|
||||||
),
|
|
||||||
('referer', HttpString.baseUrl),
|
|
||||||
];
|
|
||||||
for (int i = 0; i < 2; i++) {
|
|
||||||
final (k, v) = entries[i];
|
|
||||||
valList.values[i]
|
|
||||||
..format = generated.mpv_format.MPV_FORMAT_STRING
|
|
||||||
..u.string = '$k: $v'.toNativeUtf8().cast();
|
|
||||||
}
|
|
||||||
_mpv.mpv_set_property(
|
|
||||||
_ctx,
|
|
||||||
property.cast(),
|
|
||||||
generated.mpv_format.MPV_FORMAT_NODE,
|
|
||||||
value.cast(),
|
|
||||||
);
|
|
||||||
// Free the allocated memory.
|
|
||||||
calloc.free(property);
|
|
||||||
for (int i = 0; i < valList.num; i++) {
|
|
||||||
calloc.free(valList.values[i].u.string);
|
|
||||||
}
|
|
||||||
calloc
|
|
||||||
..free(valList.values)
|
|
||||||
..free(valRef.u.list)
|
|
||||||
..free(value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum WebpPreset {
|
enum WebpPreset {
|
||||||
|
|||||||
@@ -93,6 +93,7 @@ abstract class SettingBoxKey {
|
|||||||
refreshDisplacement = 'refreshDisplacement',
|
refreshDisplacement = 'refreshDisplacement',
|
||||||
showHotRcmd = 'showHotRcmd',
|
showHotRcmd = 'showHotRcmd',
|
||||||
audioNormalization = 'audioNormalization',
|
audioNormalization = 'audioNormalization',
|
||||||
|
fallbackNormalization = 'fallbackNormalization',
|
||||||
superResolutionType = 'superResolutionType',
|
superResolutionType = 'superResolutionType',
|
||||||
preInitPlayer = 'preInitPlayer',
|
preInitPlayer = 'preInitPlayer',
|
||||||
mainTabBarView = 'mainTabBarView',
|
mainTabBarView = 'mainTabBarView',
|
||||||
|
|||||||
@@ -414,6 +414,9 @@ abstract class Pref {
|
|||||||
static String get audioNormalization =>
|
static String get audioNormalization =>
|
||||||
_setting.get(SettingBoxKey.audioNormalization, defaultValue: '0');
|
_setting.get(SettingBoxKey.audioNormalization, defaultValue: '0');
|
||||||
|
|
||||||
|
static String get fallbackNormalization =>
|
||||||
|
_setting.get(SettingBoxKey.fallbackNormalization, defaultValue: '0');
|
||||||
|
|
||||||
static SuperResolutionType get superResolutionType {
|
static SuperResolutionType get superResolutionType {
|
||||||
SuperResolutionType? superResolutionType;
|
SuperResolutionType? superResolutionType;
|
||||||
final index = _setting.get(SettingBoxKey.superResolutionType);
|
final index = _setting.get(SettingBoxKey.superResolutionType);
|
||||||
|
|||||||
47
pubspec.lock
47
pubspec.lock
@@ -1143,10 +1143,11 @@ packages:
|
|||||||
media_kit:
|
media_kit:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: media_kit
|
path: media_kit
|
||||||
sha256: "1f1deee148533d75129a6f38251ff8388e33ee05fc2d20a6a80e57d6051b7b62"
|
ref: "version_1.2.5"
|
||||||
url: "https://pub.dev"
|
resolved-ref: "4d68e69281b44f2c8e3c444cca3d8d8dc6dcff88"
|
||||||
source: hosted
|
url: "https://github.com/My-Responsitories/media-kit.git"
|
||||||
|
source: git
|
||||||
version: "1.1.11"
|
version: "1.1.11"
|
||||||
media_kit_libs_android_video:
|
media_kit_libs_android_video:
|
||||||
dependency: "direct overridden"
|
dependency: "direct overridden"
|
||||||
@@ -1184,10 +1185,11 @@ packages:
|
|||||||
media_kit_libs_video:
|
media_kit_libs_video:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: media_kit_libs_video
|
path: "libs/universal/media_kit_libs_video"
|
||||||
sha256: "20bb4aefa8fece282b59580e1cd8528117297083a6640c98c2e98cfc96b93288"
|
ref: "version_1.2.5"
|
||||||
url: "https://pub.dev"
|
resolved-ref: "4d68e69281b44f2c8e3c444cca3d8d8dc6dcff88"
|
||||||
source: hosted
|
url: "https://github.com/My-Responsitories/media-kit.git"
|
||||||
|
source: git
|
||||||
version: "1.0.5"
|
version: "1.0.5"
|
||||||
media_kit_libs_windows_video:
|
media_kit_libs_windows_video:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
@@ -1200,18 +1202,19 @@ packages:
|
|||||||
media_kit_native_event_loop:
|
media_kit_native_event_loop:
|
||||||
dependency: "direct overridden"
|
dependency: "direct overridden"
|
||||||
description:
|
description:
|
||||||
name: media_kit_native_event_loop
|
path: media_kit_native_event_loop
|
||||||
sha256: "7d82e3b3e9ded5c35c3146c5ba1da3118d1dd8ac3435bac7f29f458181471b40"
|
ref: "version_1.2.5"
|
||||||
url: "https://pub.dev"
|
resolved-ref: "4d68e69281b44f2c8e3c444cca3d8d8dc6dcff88"
|
||||||
source: hosted
|
url: "https://github.com/My-Responsitories/media-kit.git"
|
||||||
|
source: git
|
||||||
version: "1.0.9"
|
version: "1.0.9"
|
||||||
media_kit_video:
|
media_kit_video:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
path: media_kit_video
|
path: media_kit_video
|
||||||
ref: "version_1.2.5"
|
ref: "version_1.2.5"
|
||||||
resolved-ref: "902e962556c0bde4d384cd8f72b814598e5dfcf2"
|
resolved-ref: "4d68e69281b44f2c8e3c444cca3d8d8dc6dcff88"
|
||||||
url: "https://github.com/bggRGjQaUbCoE/media-kit.git"
|
url: "https://github.com/My-Responsitories/media-kit.git"
|
||||||
source: git
|
source: git
|
||||||
version: "1.2.5"
|
version: "1.2.5"
|
||||||
menu_base:
|
menu_base:
|
||||||
@@ -1474,10 +1477,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: safe_local_storage
|
name: safe_local_storage
|
||||||
sha256: ede4eb6cb7d88a116b3d3bf1df70790b9e2038bc37cb19112e381217c74d9440
|
sha256: e9a21b6fec7a8aa62cc2585ff4c1b127df42f3185adbd2aca66b47abe2e80236
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.2"
|
version: "2.0.1"
|
||||||
saver_gallery:
|
saver_gallery:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -1836,22 +1839,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.2"
|
version: "2.2.2"
|
||||||
universal_platform:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: universal_platform
|
|
||||||
sha256: "64e16458a0ea9b99260ceb5467a214c1f298d647c659af1bff6d3bf82536b1ec"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.1.0"
|
|
||||||
uri_parser:
|
uri_parser:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: uri_parser
|
name: uri_parser
|
||||||
sha256: "6543c9fd86d2862fac55d800a43e67c0dcd1a41677cb69c2f8edfe73bbcf1835"
|
sha256: ff4d2c720aca3f4f7d5445e23b11b2d15ef8af5ddce5164643f38ff962dcb270
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.2"
|
version: "3.0.0"
|
||||||
url_launcher:
|
url_launcher:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|||||||
26
pubspec.yaml
26
pubspec.yaml
@@ -219,7 +219,6 @@ dependencies:
|
|||||||
stream_transform: any
|
stream_transform: any
|
||||||
screen_brightness_platform_interface: any
|
screen_brightness_platform_interface: any
|
||||||
mime: any
|
mime: any
|
||||||
universal_platform: any
|
|
||||||
path: any
|
path: any
|
||||||
collection: any
|
collection: any
|
||||||
material_color_utilities: any
|
material_color_utilities: any
|
||||||
@@ -234,15 +233,32 @@ dependency_overrides:
|
|||||||
path: ^1.9.1
|
path: ^1.9.1
|
||||||
mime: ^2.0.0
|
mime: ^2.0.0
|
||||||
rxdart: ^0.28.0
|
rxdart: ^0.28.0
|
||||||
media_kit: 1.1.11
|
media_kit:
|
||||||
media_kit_libs_video: 1.0.5
|
git:
|
||||||
media_kit_native_event_loop: ^1.0.9
|
url: https://github.com/My-Responsitories/media-kit.git
|
||||||
font_awesome_flutter: 10.9.0
|
path: media_kit
|
||||||
|
ref: version_1.2.5
|
||||||
|
media_kit_video:
|
||||||
|
git:
|
||||||
|
url: https://github.com/My-Responsitories/media-kit.git
|
||||||
|
path: media_kit_video
|
||||||
|
ref: version_1.2.5
|
||||||
|
media_kit_libs_video:
|
||||||
|
git:
|
||||||
|
url: https://github.com/My-Responsitories/media-kit.git
|
||||||
|
path: libs/universal/media_kit_libs_video
|
||||||
|
ref: version_1.2.5
|
||||||
|
media_kit_native_event_loop:
|
||||||
|
git:
|
||||||
|
url: https://github.com/My-Responsitories/media-kit.git
|
||||||
|
path: media_kit_native_event_loop
|
||||||
|
ref: version_1.2.5
|
||||||
media_kit_libs_android_video:
|
media_kit_libs_android_video:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/bggRGjQaUbCoE/media-kit.git
|
url: https://github.com/bggRGjQaUbCoE/media-kit.git
|
||||||
path: libs/android/media_kit_libs_android_video
|
path: libs/android/media_kit_libs_android_video
|
||||||
ref: version_1.2.5
|
ref: version_1.2.5
|
||||||
|
font_awesome_flutter: 10.9.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|||||||
Reference in New Issue
Block a user