mirror of
https://github.com/bggRGjQaUbCoE/PiliPlus.git
synced 2026-06-24 19:20:15 +08:00
feat: support dynaudnorm & webp (#1186)
* feat: support dynaudnorm & webp
* Revert "remove audio_normalization"
This reverts commit 477b59ce89.
* feat: save webp
* mod: strokeWidth
* feat: webp preset
* feat: save webp select qa
* upgrade volume_controller
This commit is contained in:
committed by
GitHub
parent
f0828ea18c
commit
e8a674ca2a
@@ -8,6 +8,7 @@ import 'package:PiliPlus/common/widgets/radio_widget.dart';
|
||||
import 'package:PiliPlus/common/widgets/refresh_indicator.dart';
|
||||
import 'package:PiliPlus/grpc/reply.dart';
|
||||
import 'package:PiliPlus/http/fav.dart';
|
||||
import 'package:PiliPlus/models/common/audio_normalization.dart';
|
||||
import 'package:PiliPlus/models/common/dynamic/dynamics_type.dart';
|
||||
import 'package:PiliPlus/models/common/member/tab_type.dart';
|
||||
import 'package:PiliPlus/models/common/reply/reply_sort_type.dart';
|
||||
@@ -438,6 +439,94 @@ 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('确定'),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
} else {
|
||||
await GStorage.setting.put(SettingBoxKey.audioNormalization, result);
|
||||
setState();
|
||||
}
|
||||
}
|
||||
},
|
||||
),
|
||||
SettingsModel(
|
||||
settingsType: SettingsType.normal,
|
||||
title: '超分辨率',
|
||||
|
||||
@@ -242,7 +242,7 @@ List<SettingsModel> get videoSettings => [
|
||||
title: '首选解码格式',
|
||||
leading: const Icon(Icons.movie_creation_outlined),
|
||||
getSubtitle: () =>
|
||||
'首选解码格式:${VideoDecodeFormatTypeExt.fromCode(Pref.defaultDecode)!.description},请根据设备支持情况与需求调整',
|
||||
'首选解码格式:${VideoDecodeFormatType.fromCode(Pref.defaultDecode).description},请根据设备支持情况与需求调整',
|
||||
onTap: (setState) async {
|
||||
String? result = await showDialog(
|
||||
context: Get.context!,
|
||||
@@ -266,7 +266,7 @@ List<SettingsModel> get videoSettings => [
|
||||
settingsType: SettingsType.normal,
|
||||
title: '次选解码格式',
|
||||
getSubtitle: () =>
|
||||
'非杜比视频次选:${VideoDecodeFormatTypeExt.fromCode(Pref.secondDecode)!.description},仍无则选择首个提供的解码格式',
|
||||
'非杜比视频次选:${VideoDecodeFormatType.fromCode(Pref.secondDecode).description},仍无则选择首个提供的解码格式',
|
||||
leading: const Icon(Icons.swap_horizontal_circle_outlined),
|
||||
onTap: (setState) async {
|
||||
String? result = await showDialog(
|
||||
|
||||
@@ -56,12 +56,12 @@ import 'package:extended_nested_scroll_view/extended_nested_scroll_view.dart';
|
||||
import 'package:flutter/foundation.dart' show kDebugMode;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:flutter_volume_controller/flutter_volume_controller.dart';
|
||||
import 'package:get/get.dart' hide ContextExtensionss;
|
||||
import 'package:get/get_navigation/src/dialog/dialog_route.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
|
||||
import 'package:media_kit/media_kit.dart';
|
||||
import 'package:volume_controller/volume_controller.dart';
|
||||
|
||||
class VideoDetailController extends GetxController
|
||||
with GetTickerProviderStateMixin {
|
||||
@@ -993,6 +993,33 @@ class VideoDetailController extends GetxController
|
||||
}
|
||||
}
|
||||
|
||||
VideoItem findVideoByQa(int qa) {
|
||||
/// 根据currentVideoQa和currentDecodeFormats 重新设置videoUrl
|
||||
final videoList = data.dash!.video!.where((i) => i.id == qa).toList();
|
||||
|
||||
final currentDecodeFormats = this.currentDecodeFormats.code;
|
||||
final defaultDecodeFormats = VideoDecodeFormatType.fromString(
|
||||
cacheDecode,
|
||||
).code;
|
||||
final secondDecodeFormats = VideoDecodeFormatType.fromString(
|
||||
cacheSecondDecode,
|
||||
).code;
|
||||
|
||||
VideoItem? video;
|
||||
for (var i in videoList) {
|
||||
final codec = i.codecs!;
|
||||
if (codec.startsWith(currentDecodeFormats)) {
|
||||
video = i;
|
||||
break;
|
||||
} else if (codec.startsWith(defaultDecodeFormats)) {
|
||||
video = i;
|
||||
} else if (video == null && codec.startsWith(secondDecodeFormats)) {
|
||||
video = i;
|
||||
}
|
||||
}
|
||||
return video ?? videoList.first;
|
||||
}
|
||||
|
||||
/// 更新画质、音质
|
||||
void updatePlayer() {
|
||||
autoPlay.value = true;
|
||||
@@ -1001,76 +1028,17 @@ class VideoDetailController extends GetxController
|
||||
plPlayerController.isBuffering.value = false;
|
||||
plPlayerController.buffered.value = Duration.zero;
|
||||
|
||||
/// 根据currentVideoQa和currentDecodeFormats 重新设置videoUrl
|
||||
List<VideoItem> videoList = data.dash!.video!
|
||||
.where((i) => i.id == currentVideoQa.value.code)
|
||||
.toList();
|
||||
|
||||
final List<String> supportDecodeFormats = videoList
|
||||
.map((e) => e.codecs!)
|
||||
.toList();
|
||||
VideoDecodeFormatType defaultDecodeFormats =
|
||||
VideoDecodeFormatTypeExt.fromString(cacheDecode)!;
|
||||
VideoDecodeFormatType secondDecodeFormats =
|
||||
VideoDecodeFormatTypeExt.fromString(cacheSecondDecode)!;
|
||||
try {
|
||||
// 当前视频没有对应格式返回第一个
|
||||
int flag = 0;
|
||||
for (var i in supportDecodeFormats) {
|
||||
if (i.startsWith(currentDecodeFormats.code)) {
|
||||
flag = 1;
|
||||
break;
|
||||
} else if (i.startsWith(defaultDecodeFormats.code)) {
|
||||
flag = 2;
|
||||
} else if (i.startsWith(secondDecodeFormats.code)) {
|
||||
if (flag == 0) {
|
||||
flag = 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (flag == 1) {
|
||||
//currentDecodeFormats
|
||||
firstVideo = videoList.firstWhere(
|
||||
(i) => i.codecs!.startsWith(currentDecodeFormats.code),
|
||||
orElse: () => videoList.first,
|
||||
);
|
||||
} else {
|
||||
if (currentVideoQa.value == VideoQuality.dolbyVision) {
|
||||
currentDecodeFormats = VideoDecodeFormatTypeExt.fromString(
|
||||
videoList.first.codecs!,
|
||||
)!;
|
||||
firstVideo = videoList.first;
|
||||
} else if (flag == 2) {
|
||||
//defaultDecodeFormats
|
||||
currentDecodeFormats = defaultDecodeFormats;
|
||||
firstVideo = videoList.firstWhere(
|
||||
(i) => i.codecs!.startsWith(defaultDecodeFormats.code),
|
||||
orElse: () => videoList.first,
|
||||
);
|
||||
} else if (flag == 4) {
|
||||
//secondDecodeFormats
|
||||
currentDecodeFormats = secondDecodeFormats;
|
||||
firstVideo = videoList.firstWhere(
|
||||
(i) => i.codecs!.startsWith(secondDecodeFormats.code),
|
||||
orElse: () => videoList.first,
|
||||
);
|
||||
} else if (flag == 0) {
|
||||
currentDecodeFormats = VideoDecodeFormatTypeExt.fromString(
|
||||
supportDecodeFormats.first,
|
||||
)!;
|
||||
firstVideo = videoList.first;
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
SmartDialog.showToast('DecodeFormats error: $err');
|
||||
final video = findVideoByQa(currentVideoQa.value.code);
|
||||
if (firstVideo.codecs != video.codecs) {
|
||||
currentDecodeFormats = VideoDecodeFormatType.fromString(video.codecs!);
|
||||
}
|
||||
|
||||
videoUrl = firstVideo.baseUrl!;
|
||||
firstVideo = video;
|
||||
videoUrl = video.baseUrl!;
|
||||
|
||||
/// 根据currentAudioQa 重新设置audioUrl
|
||||
if (currentAudioQa != null) {
|
||||
final AudioItem firstAudio = data.dash!.audio!.firstWhere(
|
||||
(AudioItem i) => i.id == currentAudioQa!.code,
|
||||
final firstAudio = data.dash!.audio!.firstWhere(
|
||||
(i) => i.id == currentAudioQa!.code,
|
||||
orElse: () => data.dash!.audio!.first,
|
||||
);
|
||||
audioUrl = firstAudio.baseUrl ?? '';
|
||||
@@ -1204,7 +1172,7 @@ class VideoDetailController extends GetxController
|
||||
quality: VideoQuality.fromCode(data.quality!),
|
||||
);
|
||||
setVideoHeight();
|
||||
currentDecodeFormats = VideoDecodeFormatTypeExt.fromString('avc1')!;
|
||||
currentDecodeFormats = VideoDecodeFormatType.fromString('avc1');
|
||||
currentVideoQa = Rx(VideoQuality.fromCode(data.quality!));
|
||||
if (autoPlay.value || plPlayerController.preInitPlayer) {
|
||||
await playerInit();
|
||||
@@ -1256,9 +1224,9 @@ class VideoDetailController extends GetxController
|
||||
)
|
||||
.codecs!;
|
||||
// 默认从设置中取AV1
|
||||
currentDecodeFormats = VideoDecodeFormatTypeExt.fromString(cacheDecode)!;
|
||||
currentDecodeFormats = VideoDecodeFormatType.fromString(cacheDecode);
|
||||
VideoDecodeFormatType secondDecodeFormats =
|
||||
VideoDecodeFormatTypeExt.fromString(cacheSecondDecode)!;
|
||||
VideoDecodeFormatType.fromString(cacheSecondDecode);
|
||||
// 当前视频没有对应格式返回第一个
|
||||
int flag = 0;
|
||||
for (var i in supportDecodeFormats) {
|
||||
@@ -1272,9 +1240,9 @@ class VideoDetailController extends GetxController
|
||||
if (flag == 2) {
|
||||
currentDecodeFormats = secondDecodeFormats;
|
||||
} else if (flag == 0) {
|
||||
currentDecodeFormats = VideoDecodeFormatTypeExt.fromString(
|
||||
currentDecodeFormats = VideoDecodeFormatType.fromString(
|
||||
supportDecodeFormats.first,
|
||||
)!;
|
||||
);
|
||||
}
|
||||
|
||||
/// 取出符合当前解码格式的videoItem
|
||||
@@ -1539,7 +1507,7 @@ class VideoDetailController extends GetxController
|
||||
if (idx == 0) {
|
||||
if (preference == SubtitlePrefType.on ||
|
||||
(preference == SubtitlePrefType.auto &&
|
||||
(await FlutterVolumeController.getVolume() ?? 0) <= 0)) {
|
||||
await VolumeController.instance.getVolume() <= 0)) {
|
||||
idx = 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ class ActionItem extends StatelessWidget {
|
||||
animation: animation!,
|
||||
builder: (context, child) => CustomPaint(
|
||||
size: const Size.square(28),
|
||||
painter: _ArcPainter(
|
||||
painter: ArcPainter(
|
||||
color: primary,
|
||||
sweepAngle: animation!.value,
|
||||
),
|
||||
@@ -110,13 +110,15 @@ class ActionItem extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class _ArcPainter extends CustomPainter {
|
||||
const _ArcPainter({
|
||||
class ArcPainter extends CustomPainter {
|
||||
const ArcPainter({
|
||||
required this.color,
|
||||
required this.sweepAngle,
|
||||
this.strokeWidth = 2,
|
||||
});
|
||||
final Color color;
|
||||
final double sweepAngle;
|
||||
final double strokeWidth;
|
||||
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
@@ -126,7 +128,7 @@ class _ArcPainter extends CustomPainter {
|
||||
|
||||
final paint = Paint()
|
||||
..color = color
|
||||
..strokeWidth = 2
|
||||
..strokeWidth = strokeWidth
|
||||
..style = PaintingStyle.stroke;
|
||||
|
||||
final rect = Rect.fromCircle(
|
||||
@@ -140,7 +142,7 @@ class _ArcPainter extends CustomPainter {
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldRepaint(covariant _ArcPainter oldDelegate) {
|
||||
bool shouldRepaint(covariant ArcPainter oldDelegate) {
|
||||
return sweepAngle != oldDelegate.sweepAngle || color != oldDelegate.color;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,6 +37,154 @@ class PostPanel extends CommonCollapseSlidePage {
|
||||
|
||||
@override
|
||||
State<PostPanel> createState() => _PostPanelState();
|
||||
|
||||
static void updateSegment({
|
||||
required bool isFirst,
|
||||
required PostSegmentModel item,
|
||||
required double value,
|
||||
}) {
|
||||
if (isFirst) {
|
||||
item.segment.first = value;
|
||||
} else {
|
||||
item.segment.second = value;
|
||||
}
|
||||
if (item.category == SegmentType.poi_highlight ||
|
||||
item.actionType == ActionType.full) {
|
||||
item.segment.second = value;
|
||||
}
|
||||
}
|
||||
|
||||
static Widget segmentWidget(
|
||||
ThemeData theme, {
|
||||
required PostSegmentModel item,
|
||||
required double currentPos,
|
||||
required double videoDuration,
|
||||
}) {
|
||||
List<Widget> segment(BuildContext context, bool isFirst) {
|
||||
String value = DurationUtil.formatDuration(
|
||||
isFirst ? item.segment.first : item.segment.second,
|
||||
);
|
||||
return [
|
||||
Text(
|
||||
'${isFirst ? '开始' : '结束'}: $value',
|
||||
),
|
||||
iconButton(
|
||||
context: context,
|
||||
size: 26,
|
||||
tooltip: '设为当前',
|
||||
icon: Icons.my_location,
|
||||
onPressed: () {
|
||||
updateSegment(
|
||||
isFirst: isFirst,
|
||||
item: item,
|
||||
value: currentPos,
|
||||
);
|
||||
(context as Element).markNeedsBuild();
|
||||
},
|
||||
),
|
||||
iconButton(
|
||||
context: context,
|
||||
size: 26,
|
||||
tooltip: isFirst ? '视频开头' : '视频结尾',
|
||||
icon: isFirst ? Icons.first_page : Icons.last_page,
|
||||
onPressed: () {
|
||||
updateSegment(
|
||||
isFirst: isFirst,
|
||||
item: item,
|
||||
value: isFirst ? 0 : videoDuration,
|
||||
);
|
||||
(context as Element).markNeedsBuild();
|
||||
},
|
||||
),
|
||||
iconButton(
|
||||
context: context,
|
||||
size: 26,
|
||||
tooltip: '编辑',
|
||||
icon: Icons.edit,
|
||||
onPressed: () {
|
||||
showDialog<String>(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
String initV = value;
|
||||
return AlertDialog(
|
||||
content: TextFormField(
|
||||
initialValue: value,
|
||||
autofocus: true,
|
||||
onChanged: (value) => initV = value,
|
||||
inputFormatters: [
|
||||
FilteringTextInputFormatter.allow(RegExp(r'[\d:.]+')),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: Get.back,
|
||||
child: Text(
|
||||
'取消',
|
||||
style: TextStyle(color: theme.colorScheme.outline),
|
||||
),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () => Get.back(result: initV),
|
||||
child: const Text('确定'),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
).then((res) {
|
||||
if (res != null) {
|
||||
try {
|
||||
List<num> split = res
|
||||
.split(':')
|
||||
.reversed
|
||||
.map(num.parse)
|
||||
.toList();
|
||||
double duration = 0;
|
||||
for (int i = 0; i < split.length; i++) {
|
||||
duration += split[i] * pow(60, i);
|
||||
}
|
||||
if (duration <= videoDuration) {
|
||||
updateSegment(
|
||||
isFirst: isFirst,
|
||||
item: item,
|
||||
value: duration,
|
||||
);
|
||||
(context as Element).markNeedsBuild();
|
||||
}
|
||||
} catch (e) {
|
||||
if (kDebugMode) debugPrint(e.toString());
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
final child = Builder(
|
||||
builder: (context) => Row(
|
||||
spacing: 5,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: segment(context, true),
|
||||
),
|
||||
);
|
||||
if (item.category != SegmentType.poi_highlight) {
|
||||
return Wrap(
|
||||
runSpacing: 8,
|
||||
spacing: 16,
|
||||
children: [
|
||||
child,
|
||||
Builder(
|
||||
builder: (context) => Row(
|
||||
spacing: 5,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: segment(context, false),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
return child;
|
||||
}
|
||||
}
|
||||
|
||||
class _PostPanelState extends CommonCollapseSlidePageState<PostPanel> {
|
||||
@@ -162,130 +310,6 @@ class _PostPanelState extends CommonCollapseSlidePageState<PostPanel> {
|
||||
);
|
||||
}
|
||||
|
||||
void updateSegment({
|
||||
required bool isFirst,
|
||||
required PostSegmentModel item,
|
||||
required double value,
|
||||
}) {
|
||||
if (isFirst) {
|
||||
item.segment.first = value;
|
||||
} else {
|
||||
item.segment.second = value;
|
||||
}
|
||||
if (item.category == SegmentType.poi_highlight ||
|
||||
item.actionType == ActionType.full) {
|
||||
item.segment.second = value;
|
||||
}
|
||||
}
|
||||
|
||||
List<Widget> segmentWidget(
|
||||
BuildContext context,
|
||||
ThemeData theme, {
|
||||
required PostSegmentModel item,
|
||||
required bool isFirst,
|
||||
}) {
|
||||
String value = DurationUtil.formatDuration(
|
||||
isFirst ? item.segment.first : item.segment.second,
|
||||
);
|
||||
return [
|
||||
Text(
|
||||
'${isFirst ? '开始' : '结束'}: $value',
|
||||
),
|
||||
const SizedBox(width: 5),
|
||||
iconButton(
|
||||
context: context,
|
||||
size: 26,
|
||||
tooltip: '设为当前',
|
||||
icon: Icons.my_location,
|
||||
onPressed: () {
|
||||
updateSegment(
|
||||
isFirst: isFirst,
|
||||
item: item,
|
||||
value: currentPos,
|
||||
);
|
||||
(context as Element).markNeedsBuild();
|
||||
},
|
||||
),
|
||||
const SizedBox(width: 5),
|
||||
iconButton(
|
||||
context: context,
|
||||
size: 26,
|
||||
tooltip: isFirst ? '视频开头' : '视频结尾',
|
||||
icon: isFirst ? Icons.first_page : Icons.last_page,
|
||||
onPressed: () {
|
||||
updateSegment(
|
||||
isFirst: isFirst,
|
||||
item: item,
|
||||
value: isFirst ? 0 : videoDuration,
|
||||
);
|
||||
(context as Element).markNeedsBuild();
|
||||
},
|
||||
),
|
||||
const SizedBox(width: 5),
|
||||
iconButton(
|
||||
context: context,
|
||||
size: 26,
|
||||
tooltip: '编辑',
|
||||
icon: Icons.edit,
|
||||
onPressed: () {
|
||||
showDialog<String>(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
String initV = value;
|
||||
return AlertDialog(
|
||||
content: TextFormField(
|
||||
initialValue: value,
|
||||
autofocus: true,
|
||||
onChanged: (value) => initV = value,
|
||||
inputFormatters: [
|
||||
FilteringTextInputFormatter.allow(RegExp(r'[\d:.]+')),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: Get.back,
|
||||
child: Text(
|
||||
'取消',
|
||||
style: TextStyle(color: theme.colorScheme.outline),
|
||||
),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () => Get.back(result: initV),
|
||||
child: const Text('确定'),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
).then((res) {
|
||||
if (res != null) {
|
||||
try {
|
||||
List<num> split = res
|
||||
.split(':')
|
||||
.reversed
|
||||
.map(num.parse)
|
||||
.toList();
|
||||
double duration = 0;
|
||||
for (int i = 0; i < split.length; i++) {
|
||||
duration += split[i] * pow(60, i);
|
||||
}
|
||||
if (duration <= videoDuration) {
|
||||
updateSegment(
|
||||
isFirst: isFirst,
|
||||
item: item,
|
||||
value: duration,
|
||||
);
|
||||
(context as Element).markNeedsBuild();
|
||||
}
|
||||
} catch (e) {
|
||||
if (kDebugMode) debugPrint(e.toString());
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
void _onPost() {
|
||||
Request()
|
||||
.post(
|
||||
@@ -376,42 +400,13 @@ class _PostPanelState extends CommonCollapseSlidePageState<PostPanel> {
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
if (item.actionType != ActionType.full) ...[
|
||||
Wrap(
|
||||
runSpacing: 8,
|
||||
spacing: 16,
|
||||
children: [
|
||||
Builder(
|
||||
builder: (context) {
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: segmentWidget(
|
||||
context,
|
||||
theme,
|
||||
isFirst: true,
|
||||
item: item,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
if (item.category != SegmentType.poi_highlight)
|
||||
Builder(
|
||||
builder: (context) {
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: segmentWidget(
|
||||
context,
|
||||
theme,
|
||||
isFirst: false,
|
||||
item: item,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
if (item.actionType != ActionType.full)
|
||||
PostPanel.segmentWidget(
|
||||
theme,
|
||||
item: item,
|
||||
currentPos: currentPos,
|
||||
videoDuration: videoDuration,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
],
|
||||
Wrap(
|
||||
runSpacing: 8,
|
||||
spacing: 16,
|
||||
@@ -430,14 +425,14 @@ class _PostPanelState extends CommonCollapseSlidePageState<PostPanel> {
|
||||
}
|
||||
switch (e) {
|
||||
case SegmentType.poi_highlight:
|
||||
updateSegment(
|
||||
PostPanel.updateSegment(
|
||||
isFirst: false,
|
||||
item: item,
|
||||
value: item.segment.first,
|
||||
);
|
||||
break;
|
||||
case SegmentType.exclusive_access:
|
||||
updateSegment(
|
||||
PostPanel.updateSegment(
|
||||
isFirst: true,
|
||||
item: item,
|
||||
value: 0,
|
||||
@@ -491,7 +486,7 @@ class _PostPanelState extends CommonCollapseSlidePageState<PostPanel> {
|
||||
onSelected: (e) {
|
||||
item.actionType = e;
|
||||
if (e == ActionType.full) {
|
||||
updateSegment(
|
||||
PostPanel.updateSegment(
|
||||
isFirst: true,
|
||||
item: item,
|
||||
value: 0,
|
||||
|
||||
@@ -837,7 +837,7 @@ class HeaderControlState extends TripleState<HeaderControl> {
|
||||
}
|
||||
videoDetailCtr
|
||||
..currentDecodeFormats =
|
||||
VideoDecodeFormatTypeExt.fromString(i)!
|
||||
VideoDecodeFormatType.fromString(i)
|
||||
..updatePlayer();
|
||||
Get.back();
|
||||
},
|
||||
@@ -846,7 +846,7 @@ class HeaderControlState extends TripleState<HeaderControl> {
|
||||
right: 20,
|
||||
),
|
||||
title: Text(
|
||||
VideoDecodeFormatTypeExt.fromString(i)!.description,
|
||||
VideoDecodeFormatType.fromString(i).description,
|
||||
),
|
||||
subtitle: Text(
|
||||
i,
|
||||
|
||||
Reference in New Issue
Block a user