diff --git a/lib/pages/video/detail/controller.dart b/lib/pages/video/detail/controller.dart index ae6ef96c..164b868e 100644 --- a/lib/pages/video/detail/controller.dart +++ b/lib/pages/video/detail/controller.dart @@ -41,6 +41,7 @@ import 'package:PiliPlus/utils/utils.dart'; import 'package:PiliPlus/utils/video_utils.dart'; 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 '../../../utils/id_utils.dart'; @@ -633,34 +634,29 @@ class VideoDetailController extends GetxController item.skipType.title, style: TextStyle(fontSize: 13), ), - if (item.skipType == SkipType.showOnly && - item.segment.second != 0) + if (item.segment.second != 0) SizedBox( width: 36, height: 36, child: IconButton( - tooltip: '跳转至此片段', + tooltip: item.skipType == SkipType.showOnly + ? '跳至此片段' + : '跳过此片段', onPressed: () async { Get.back(); - try { - plPlayerController.danmakuController?.clear(); - await plPlayerController.videoPlayerController - ?.seek(Duration( - seconds: item.segment.first)); - if (GStorage.blockToast) { - _showBlockToast( - '已跳至${item.segmentType.shortTitle}'); - } - } catch (e) { - _showBlockToast('跳转失败: $e'); - } + onSkip( + item, + item.skipType != SkipType.showOnly, + ); }, style: IconButton.styleFrom( padding: EdgeInsets.zero, tapTargetSize: MaterialTapTargetSize.shrinkWrap, ), icon: Icon( - Icons.my_location, + item.skipType == SkipType.showOnly + ? Icons.my_location + : MdiIcons.debugStepOver, size: 18, color: Theme.of(context) .colorScheme @@ -695,14 +691,16 @@ class VideoDetailController extends GetxController videoLabel.value = ''; segmentList.clear(); segmentProgressList = null; - dynamic result = await Request().get( + final result = await Request().get( '${GStorage.blockServer}/api/skipSegments', queryParameters: { 'videoID': bvid, 'cid': cid.value, }, ); - handleSBData(result); + if (result.statusCode == 200) { + handleSBData(result); + } } void handleSBData(result) { @@ -736,6 +734,7 @@ class VideoDetailController extends GetxController skipType = SkipType.showOnly; } } + int convert(value) { return switch (value) { int() => value, @@ -744,7 +743,7 @@ class VideoDetailController extends GetxController }; } - return SegmentModel( + final segmentModel = SegmentModel( UUID: item['UUID'], segmentType: segmentType, segment: Pair( @@ -753,18 +752,51 @@ class VideoDetailController extends GetxController ), skipType: skipType, ); + + if (item['segment'][0] == 0 && + item['segment'][1] > 0 && + !isShowCover.value) { + if (plPlayerController.videoPlayerController != null && + plPlayerController + .videoPlayerController!.state.position.inSeconds < + item['segment'][1]) { + if (segmentModel.skipType == SkipType.alwaysSkip) { + _lastPos = 0; + plPlayerController.videoPlayerController!.stream.buffer.first + .then((_) { + onSkip(segmentModel); + }); + } else if (segmentModel.skipType == SkipType.skipOnce) { + _lastPos = 0; + segmentModel.hasSkipped = true; + plPlayerController.videoPlayerController!.stream.buffer.first + .then((_) { + onSkip(segmentModel); + }); + } else if (segmentModel.skipType == SkipType.skipManually) { + onAddItem(segmentModel); + } + } + } + + return segmentModel; }, ).toList()); // _segmentProgressList segmentProgressList ??= []; segmentProgressList!.addAll(segmentList.map((item) { - double start = (item.segment.first / ((data.timeLength ?? 0) / 1000)) - .clamp(0.0, 1.0); - double end = (item.segment.second / ((data.timeLength ?? 0) / 1000)) - .clamp(0.0, 1.0); + double start = + (item.segment.first / (data.timeLength! / 1000)).clamp(0.0, 1.0); + double end = + (item.segment.second / (data.timeLength! / 1000)).clamp(0.0, 1.0); return Segment(start, end, _getColor(item.segmentType)); }).toList()); + + if (positionSubscription == null && autoPlay.value) { + initSkip(); + plPlayerController.segmentList.value = segmentProgressList!; + } } catch (e) { debugPrint('failed to parse sponsorblock: $e'); } @@ -773,9 +805,10 @@ class VideoDetailController extends GetxController void initSkip() { if (segmentList.isNotEmpty) { + positionSubscription?.cancel(); positionSubscription = plPlayerController .videoPlayerController?.stream.position - .listen((position) async { + .listen((position) { if (isShowCover.value) { return; } @@ -804,6 +837,7 @@ class VideoDetailController extends GetxController } void onAddItem(item) { + if (listData.contains(item)) return; listData.insert(0, item); listKey.currentState?.insertItem(0); skipTimer ??= Timer.periodic(const Duration(seconds: 4), (_) { @@ -890,23 +924,31 @@ class VideoDetailController extends GetxController ); } - void onSkip(SegmentModel item) async { + void onSkip(SegmentModel item, [bool isSkip = true]) async { try { plPlayerController.danmakuController?.clear(); await plPlayerController.videoPlayerController ?.seek(Duration(seconds: item.segment.second)); - if (GStorage.blockToast) { - _showBlockToast('已跳过${item.segmentType.shortTitle}片段'); - } - if (GStorage.blockTrack) { - Request().post( - '${GStorage.blockServer}/api/viewedVideoSponsorTime', - queryParameters: {'UUID': item.UUID}, - ); + if (isSkip) { + if (GStorage.blockToast) { + _showBlockToast('已跳过${item.segmentType.shortTitle}片段'); + } + if (GStorage.blockTrack) { + Request().post( + '${GStorage.blockServer}/api/viewedVideoSponsorTime', + queryParameters: {'UUID': item.UUID}, + ); + } + } else { + _showBlockToast('已跳至${item.segmentType.shortTitle}'); } } catch (e) { debugPrint('failed to skip: $e'); - _showBlockToast('${item.segmentType.shortTitle}片段跳过失败'); + if (isSkip) { + _showBlockToast('${item.segmentType.shortTitle}片段跳过失败'); + } else { + _showBlockToast('跳转失败'); + } } } @@ -1134,7 +1176,7 @@ class VideoDetailController extends GetxController data = result['data']; if (enableSponsorBlock) { - await _querySponsorBlock(); + _querySponsorBlock(); } if (data.acceptDesc!.isNotEmpty && data.acceptDesc!.contains('试看')) { @@ -1457,23 +1499,25 @@ class VideoDetailController extends GetxController if (GStorage.showViewPoints && res["view_points"] is List && res["view_points"].isNotEmpty) { - viewPointList = (res["view_points"] as List).map((item) { - double start = - (item['to'] / ((data.timeLength ?? 0) / 1000)).clamp(0.0, 1.0); - return Segment( - start, - start, - Colors.black.withOpacity(0.5), - item?['content'], - item?['imgUrl'], - item?['from'], - item?['to'], - ); - }).toList(); - if (plPlayerController.viewPointList.isEmpty) { - plPlayerController.viewPointList.value = viewPointList; - plPlayerController.showVP.value = showVP = true; - } + try { + viewPointList = (res["view_points"] as List).map((item) { + double start = + (item['to'] / (data.timeLength! / 1000)).clamp(0.0, 1.0); + return Segment( + start, + start, + Colors.black.withOpacity(0.5), + item?['content'], + item?['imgUrl'], + item?['from'], + item?['to'], + ); + }).toList(); + if (plPlayerController.viewPointList.isEmpty) { + plPlayerController.viewPointList.value = viewPointList; + plPlayerController.showVP.value = showVP = true; + } + } catch (_) {} } if (res["subtitles"] is List && res["subtitles"].isNotEmpty) { @@ -1563,6 +1607,7 @@ class VideoDetailController extends GetxController // sponsor block if (enableSponsorBlock) { positionSubscription?.cancel(); + positionSubscription = null; videoLabel.value = ''; segmentList.clear(); segmentProgressList = null;