From aa378d924b738d31718c25f12c61b79d20300fc2 Mon Sep 17 00:00:00 2001 From: bggRGjQaUbCoE Date: Wed, 26 Mar 2025 12:03:21 +0800 Subject: [PATCH] opt: post segments Closes #531 Signed-off-by: bggRGjQaUbCoE --- .../sponsor_block/post_segment_model.dart | 2 +- lib/pages/video/detail/controller.dart | 22 ++--- .../video/detail/post_panel/post_panel.dart | 83 ++++++++++++++----- lib/utils/utils.dart | 7 +- 4 files changed, 79 insertions(+), 35 deletions(-) diff --git a/lib/models/common/sponsor_block/post_segment_model.dart b/lib/models/common/sponsor_block/post_segment_model.dart index 5b83bd2c..51215a0d 100644 --- a/lib/models/common/sponsor_block/post_segment_model.dart +++ b/lib/models/common/sponsor_block/post_segment_model.dart @@ -8,7 +8,7 @@ class PostSegmentModel { required this.category, required this.actionType, }); - Pair segment; + Pair segment; SegmentType category; ActionType actionType; } diff --git a/lib/pages/video/detail/controller.dart b/lib/pages/video/detail/controller.dart index 0e33c73c..cb3235aa 100644 --- a/lib/pages/video/detail/controller.dart +++ b/lib/pages/video/detail/controller.dart @@ -745,12 +745,20 @@ class VideoDetailController extends GetxController skipType = SkipType.showOnly; } } + int convert(value) { + return switch (value) { + int() => value, + double() => value.round(), + _ => -1, + }; + } + return SegmentModel( UUID: item['UUID'], segmentType: segmentType, segment: Pair( - first: _convert(item['segment'][0]), - second: _convert(item['segment'][1]), + first: convert(item['segment'][0]), + second: convert(item['segment'][1]), ), skipType: skipType, ); @@ -772,14 +780,6 @@ class VideoDetailController extends GetxController } } - int _convert(value) { - return value is double - ? value.round() - : value is int - ? value - : -1; - } - void initSkip() { if (segmentList.isNotEmpty) { positionSubscription = plPlayerController @@ -1319,7 +1319,7 @@ class VideoDetailController extends GetxController PostSegmentModel( segment: Pair( first: 0, - second: plPlayerController.positionSeconds.value, + second: plPlayerController.position.value.inMilliseconds / 1000, ), category: SegmentType.sponsor, actionType: ActionType.skip, diff --git a/lib/pages/video/detail/post_panel/post_panel.dart b/lib/pages/video/detail/post_panel/post_panel.dart index 8b7c4306..5704807d 100644 --- a/lib/pages/video/detail/post_panel/post_panel.dart +++ b/lib/pages/video/detail/post_panel/post_panel.dart @@ -42,6 +42,12 @@ class _PostPanelState extends CommonCollapseSlidePageState { PlPlayerController get plPlayerController => widget.plPlayerController; List? get list => videoDetailController.list; + late final double videoDuration = + plPlayerController.durationSeconds.value.inMilliseconds / 1000; + + double get currentPos => + plPlayerController.position.value.inMilliseconds / 1000; + @override Widget get buildPage => Scaffold( resizeToAvoidBottomInset: false, @@ -60,7 +66,7 @@ class _PostPanelState extends CommonCollapseSlidePageState { PostSegmentModel( segment: Pair( first: 0, - second: plPlayerController.positionSeconds.value, + second: currentPos, ), category: SegmentType.sponsor, actionType: ActionType.skip, @@ -299,6 +305,50 @@ class _PostPanelState extends CommonCollapseSlidePageState { }, ), ), + Positioned( + top: 0, + left: 4, + child: iconButton( + context: context, + size: 26, + tooltip: '预览', + icon: Icons.preview_outlined, + onPressed: () async { + if (widget.plPlayerController + .videoPlayerController != + null) { + int start = max( + 0, + (list![index].segment.first * 1000).toInt() - + 1, + ); + await widget + .plPlayerController.videoPlayerController! + .seek( + Duration(milliseconds: start), + ); + if (widget.plPlayerController + .videoPlayerController!.state.playing.not) { + await widget + .plPlayerController.videoPlayerController! + .play(); + } + if (start != 0) { + await Future.delayed( + const Duration(seconds: 1)); + } + widget.plPlayerController.videoPlayerController! + .seek( + Duration( + milliseconds: + (list![index].segment.second * 1000) + .toInt(), + ), + ); + } + }, + ), + ), ], ), ), @@ -338,8 +388,7 @@ class _PostPanelState extends CommonCollapseSlidePageState { 'cid': videoDetailController.cid.value, 'userID': GStorage.blockUserID, 'userAgent': Constants.userAgent, - 'videoDuration': plPlayerController - .durationSeconds.value.inSeconds, + 'videoDuration': videoDuration, }, data: { 'segments': list! @@ -400,7 +449,7 @@ class _PostPanelState extends CommonCollapseSlidePageState { void updateSegment({ required bool isFirst, required int index, - required int value, + required double value, }) { if (isFirst) { list![index].segment.first = value; @@ -434,7 +483,7 @@ class _PostPanelState extends CommonCollapseSlidePageState { updateSegment( isFirst: isFirst, index: index, - value: plPlayerController.positionSeconds.value, + value: currentPos, ); }); }, @@ -450,9 +499,7 @@ class _PostPanelState extends CommonCollapseSlidePageState { updateSegment( isFirst: isFirst, index: index, - value: isFirst - ? 0 - : plPlayerController.durationSeconds.value.inSeconds, + value: isFirst ? 0 : videoDuration, ); }); }, @@ -464,7 +511,7 @@ class _PostPanelState extends CommonCollapseSlidePageState { tooltip: '编辑', icon: Icons.edit, onPressed: () { - showDialog( + showDialog( context: context, builder: (context) { String initV = value; @@ -477,7 +524,7 @@ class _PostPanelState extends CommonCollapseSlidePageState { }, inputFormatters: [ FilteringTextInputFormatter.allow( - RegExp(r'[\d:]+'), + RegExp(r'[\d:.]+'), ), ], ), @@ -500,19 +547,13 @@ class _PostPanelState extends CommonCollapseSlidePageState { ).then((res) { if (res != null) { try { - List split = (res as String) - .split(':') - .toList() - .reversed - .toList() - .map((e) => int.parse(e)) - .toList(); - int duration = 0; + List split = + res.split(':').reversed.map((e) => num.parse(e)).toList(); + double duration = 0; for (int i = 0; i < split.length; i++) { - duration += split[i] * pow(60, i).toInt(); + duration += split[i] * pow(60, i); } - if (duration <= - plPlayerController.durationSeconds.value.inSeconds) { + if (duration <= videoDuration) { setState(() { updateSegment( isFirst: isFirst, diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart index 30600e00..2eefe980 100644 --- a/lib/utils/utils.dart +++ b/lib/utils/utils.dart @@ -1423,10 +1423,13 @@ class Utils { return v.toString() + random.nextInt(9999).toString(); } - static String formatDuration(int seconds) { + static String formatDuration(num seconds) { int hours = seconds ~/ 3600; int minutes = (seconds % 3600) ~/ 60; - int remainingSeconds = seconds % 60; + num remainingSeconds = seconds % 60; + if (remainingSeconds is double) { + remainingSeconds = remainingSeconds.toPrecision(3); + } String minutesStr = minutes.toString().padLeft(2, '0'); String secondsStr = remainingSeconds.toString().padLeft(2, '0');