From b56e03c65e7d20065e6481a688531d596e3187b6 Mon Sep 17 00:00:00 2001 From: orz12 Date: Tue, 31 Dec 2024 01:57:25 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E9=95=9C=E5=83=8F=E3=80=81=E5=BC=B9?= =?UTF-8?q?=E5=B9=95=E8=BF=87=E6=BB=A4=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/danmaku/controller.dart | 33 +++++----- lib/pages/danmaku/view.dart | 7 +- lib/pages/danmaku_block/index.dart | 39 ++++++----- .../video/introduction/widgets/menu_row.dart | 10 +++ lib/pages/video/widgets/header_control.dart | 66 ++++++++++++------- lib/plugin/pl_player/controller.dart | 19 ++---- lib/plugin/pl_player/view.dart | 25 ++++--- 7 files changed, 113 insertions(+), 86 deletions(-) diff --git a/lib/pages/danmaku/controller.dart b/lib/pages/danmaku/controller.dart index c7f5c40ae..6194c9342 100644 --- a/lib/pages/danmaku/controller.dart +++ b/lib/pages/danmaku/controller.dart @@ -2,13 +2,16 @@ import 'package:PiliPalaX/http/danmaku.dart'; import 'package:PiliPalaX/models/danmaku/dm.pb.dart'; import 'package:flutter/cupertino.dart'; +import '../../utils/storage.dart'; + class PlDanmakuController { - PlDanmakuController(this.cid, this.danmakuWeightNotifier, this.danmakuFilterNotifier); final int cid; - final ValueNotifier danmakuWeightNotifier; - final ValueNotifier>> danmakuFilterNotifier; - int danmakuWeight = 0; - List> danmakuFilter = []; + static int danmakuWeight = 0; + static List> danmakuFilter = []; + PlDanmakuController(this.cid){ + refresh(); + } + Map> dmSegMap = {}; // 已请求的段落标记 List requestedSeg = []; @@ -17,19 +20,19 @@ class PlDanmakuController { static int segmentLength = 60 * 6 * 1000; + static void refresh() { + danmakuWeight = + GStorage.setting.get(SettingBoxKey.danmakuWeight, defaultValue: 0); + danmakuFilter = GStorage.onlineCache.get(OnlineCacheKey.danmakuFilterRule, + defaultValue: []).map>((e) { + return Map.from(e); + }).toList(); + } + void initiate(int videoDuration, int progress) { if (videoDuration <= 0) { return; } - danmakuWeightNotifier.addListener(() { - print( - "danmakuWeight changed from $danmakuWeight to ${danmakuWeightNotifier.value}"); - danmakuWeight = danmakuWeightNotifier.value; - }); - danmakuFilterNotifier.addListener(() { - print("danmakuFilter changed from $danmakuFilter to ${danmakuFilterNotifier.value}"); - danmakuFilter = danmakuFilterNotifier.value; - }); if (requestedSeg.isEmpty) { int segCount = (videoDuration / segmentLength).ceil(); requestedSeg = List.generate(segCount, (index) => false); @@ -38,8 +41,6 @@ class PlDanmakuController { } void dispose() { - danmakuWeightNotifier.removeListener(() {}); - danmakuFilterNotifier.removeListener(() {}); danmakuFilter.clear(); dmSegMap.clear(); requestedSeg.clear(); diff --git a/lib/pages/danmaku/view.dart b/lib/pages/danmaku/view.dart index cefe902be..60e115814 100644 --- a/lib/pages/danmaku/view.dart +++ b/lib/pages/danmaku/view.dart @@ -45,10 +45,7 @@ class _PlDanmakuState extends State { super.initState(); enableShowDanmaku = setting.get(SettingBoxKey.enableShowDanmaku, defaultValue: true); - _plDanmakuController = PlDanmakuController( - widget.cid, - widget.playerController.danmakuWeight, - widget.playerController.danmakuFilterRule); + _plDanmakuController = PlDanmakuController(widget.cid); if (mounted) { playerController = widget.playerController; if (enableShowDanmaku || playerController.isOpenDanmu.value) { @@ -103,7 +100,7 @@ class _PlDanmakuState extends State { if (currentDanmakuList != null && _controller != null) { Color? defaultColor = playerController.blockTypes.contains(6) - ? Colors.white//DmUtils.decimalToColor(16777215) + ? Colors.white //DmUtils.decimalToColor(16777215) : null; currentDanmakuList .map((e) => _controller!.addDanmaku(DanmakuContentItem( diff --git a/lib/pages/danmaku_block/index.dart b/lib/pages/danmaku_block/index.dart index 7ff778e08..d5c9dcf57 100644 --- a/lib/pages/danmaku_block/index.dart +++ b/lib/pages/danmaku_block/index.dart @@ -9,6 +9,8 @@ import '../../models/user/danmaku_block.dart'; import '../../plugin/pl_player/controller.dart'; import 'package:PiliPalaX/common/widgets/spring_physics.dart'; +import '../danmaku/controller.dart'; + class DanmakuBlockPage extends StatefulWidget { const DanmakuBlockPage({super.key}); @@ -21,7 +23,6 @@ class _DanmakuBlockPageState extends State { Get.put(DanmakuBlockController()); final ScrollController scrollController = ScrollController(); Box onlineCache = GStorage.onlineCache; - late PlPlayerController plPlayerController; static const Map ruleLabels = { 0: '关键词', @@ -35,24 +36,11 @@ class _DanmakuBlockPageState extends State { WidgetsBinding.instance.addPostFrameCallback((_) { _danmakuBlockController.queryDanmakuFilter(); }); - plPlayerController = Get.arguments as PlPlayerController; } + @override void dispose() { - List> simpleRuleList = _danmakuBlockController - .ruleTypes.values - .expand((element) => element) - .map>((e) { - //当正则表达式前后都有"/"时,去掉,避免RegExp解析错误 - if (e.type == 1 && e.filter.startsWith('/') && e.filter.endsWith('/')) { - e.filter = e.filter.substring(1, e.filter.length - 1); - } - return e.toMap(); - }).toList(); - print("simpleRuleList:$simpleRuleList"); - onlineCache.put(OnlineCacheKey.danmakuFilterRule, simpleRuleList); - plPlayerController.danmakuFilterRule.value = simpleRuleList; scrollController.removeListener(() {}); scrollController.dispose(); super.dispose(); @@ -181,6 +169,21 @@ class DanmakuBlockController extends GetxController tabController = TabController(length: 3, vsync: this); } + void danmakuFilterRefresh() { + List> simpleRuleList = ruleTypes.values + .expand((element) => element) + .map>((e) { + //当正则表达式前后都有"/"时,去掉,避免RegExp解析错误 + if (e.type == 1 && e.filter.startsWith('/') && e.filter.endsWith('/')) { + e.filter = e.filter.substring(1, e.filter.length - 1); + } + return e.toMap(); + }).toList(); + print("simpleRuleList:$simpleRuleList"); + onlineCache.put(OnlineCacheKey.danmakuFilterRule, simpleRuleList); + PlDanmakuController.refresh(); + } + @override void onClose() { tabController.dispose(); @@ -193,11 +196,15 @@ class DanmakuBlockController extends GetxController SmartDialog.dismiss(); if (result['status']) { danmakuRules.value = result['data'].rule; + for (var element in ruleTypes.values) { + element.clear(); + } danmakuRules.map((e) { SimpleRule simpleRule = SimpleRule(e.id!, e.type!, e.filter!); ruleTypes[e.type!]!.add(simpleRule); }).toList(); ruleTypes.refresh(); + danmakuFilterRefresh(); SmartDialog.showToast(result['data'].toast); } else { SmartDialog.showToast(result['msg']); @@ -213,6 +220,7 @@ class DanmakuBlockController extends GetxController danmakuRules.removeWhere((e) => e.id == id); ruleTypes[type]!.removeWhere((e) => e.id == id); ruleTypes.refresh(); + danmakuFilterRefresh(); SmartDialog.showToast(result['msg']); } else { SmartDialog.showToast(result['msg']); @@ -230,6 +238,7 @@ class DanmakuBlockController extends GetxController SimpleRule simpleRule = SimpleRule(data.id!, data.type!, data.filter!); ruleTypes[type]!.add(simpleRule); ruleTypes.refresh(); + danmakuFilterRefresh(); SmartDialog.showToast('添加成功'); } else { SmartDialog.showToast(result['msg']); diff --git a/lib/pages/video/introduction/widgets/menu_row.dart b/lib/pages/video/introduction/widgets/menu_row.dart index f741332fb..a4096e2e5 100644 --- a/lib/pages/video/introduction/widgets/menu_row.dart +++ b/lib/pages/video/introduction/widgets/menu_row.dart @@ -103,11 +103,13 @@ class ActionRowLineItem extends StatelessWidget { this.onTap, this.text, this.loadingStatus = false, + this.icon, }); final bool? selectStatus; final Function? onTap; final bool? loadingStatus; final String? text; + final IconData? icon; @override Widget build(BuildContext context) { @@ -135,6 +137,14 @@ class ActionRowLineItem extends StatelessWidget { child: Row( mainAxisSize: MainAxisSize.min, children: [ + if (icon != null) + Icon( + icon, + size: 13, + color: selectStatus! + ? Theme.of(context).colorScheme.onSecondaryContainer + : Theme.of(context).colorScheme.outline, + ), AnimatedOpacity( opacity: loadingStatus! ? 0 : 1, duration: const Duration(milliseconds: 200), diff --git a/lib/pages/video/widgets/header_control.dart b/lib/pages/video/widgets/header_control.dart index 8aeed534e..6346c9ab1 100644 --- a/lib/pages/video/widgets/header_control.dart +++ b/lib/pages/video/widgets/header_control.dart @@ -27,6 +27,8 @@ import 'package:PiliPalaX/services/shutdown_timer_service.dart'; import '../../../../models/video/play/CDN.dart'; import '../../../../models/video_detail_res.dart'; import '../../../services/service_locator.dart'; +import '../../danmaku/controller.dart'; +import '../../danmaku_block/index.dart'; import '../../setting/widgets/select_dialog.dart'; import 'package:PiliPalaX/pages/video/introduction/detail/index.dart'; import 'package:marquee/marquee.dart'; @@ -229,19 +231,27 @@ class _HeaderControlState extends State { }, ), ListTile( - onTap: () { - Get.back(); - }, dense: true, - leading: - const Icon(Icons.switch_video_outlined, size: 20), - title: const Text('功能', style: titleStyle), - trailing: Row( + title: Row( mainAxisSize: MainAxisSize.min, children: [ + Obx( + () => ActionRowLineItem( + key: const Key('flipX'), + icon: Icons.flip, + onTap: () { + widget.controller!.flipX.value = + !widget.controller!.flipX.value; + }, + text: " 镜像翻转 ", + selectStatus: widget.controller!.flipX.value, + ), + ), + const SizedBox(width: 10), Obx( () => ActionRowLineItem( key: const Key('onlyPlayAudio'), + icon: Icons.headphones, onTap: () { widget.controller!.setOnlyPlayAudio(null); }, @@ -255,6 +265,7 @@ class _HeaderControlState extends State { Obx( () => ActionRowLineItem( key: const Key('continuePlayInBackground'), + icon: Icons.play_circle_outline, onTap: () { widget.controller! .setContinuePlayInBackground(null); @@ -978,7 +989,7 @@ class _HeaderControlState extends State { {'value': 1.0, 'label': '满屏'}, ]; // 智能云屏蔽 - int danmakuWeight = widget.controller!.danmakuWeight.value; + int danmakuWeight = PlDanmakuController.danmakuWeight; // 显示区域 double showArea = widget.controller!.showArea; // 不透明度 @@ -1032,13 +1043,20 @@ class _HeaderControlState extends State { minimumSize: Size.zero, tapTargetSize: MaterialTapTargetSize.shrinkWrap, ), - onPressed: () => { - Get.back(), - Get.toNamed('/danmakuBlock', - arguments: widget.controller) - }, + onPressed: () { + // 弹出对话框 + Get.back(); + showDialog( + context: context, + useSafeArea: true, + builder: (_) => const Dialog( + insetPadding: EdgeInsets.zero, + child: DanmakuBlockPage(), + ), + ); + }, child: Text( - "屏蔽管理(${widget.controller!.danmakuFilterRule.value.length})")), + "屏蔽管理(${PlDanmakuController.danmakuFilter.length})")), ], ), Padding( @@ -1065,8 +1083,7 @@ class _HeaderControlState extends State { label: '$danmakuWeight', onChanged: (double val) { danmakuWeight = val.toInt(); - widget.controller!.danmakuWeight.value = - danmakuWeight; + PlDanmakuController.danmakuWeight = danmakuWeight; widget.controller!.putDanmakuSettings(); setState(() {}); // try { @@ -1439,6 +1456,7 @@ class _HeaderControlState extends State { nowSemanticsLabel = '当前时间:$hour点$minute分'; now.value = '$hour:$minute'; } + startClock() { getNow(); clock = Timer.periodic(const Duration(seconds: 1), (Timer t) { @@ -1742,15 +1760,13 @@ class _HeaderControlState extends State { if (widget.controller!.isFullScreen.value || equivalentFullScreen()) Row(mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ Obx( - () => Text( - " ${now.value}", - style: const TextStyle( - color: Colors.white, - fontSize: 11, - fontFeatures: [FontFeature.tabularFigures()], - ), - semanticsLabel: nowSemanticsLabel - ), + () => Text(" ${now.value}", + style: const TextStyle( + color: Colors.white, + fontSize: 11, + fontFeatures: [FontFeature.tabularFigures()], + ), + semanticsLabel: nowSemanticsLabel), ), const SizedBox(width: 1.5), if (widget.controller!.isFullScreen.value) diff --git a/lib/plugin/pl_player/controller.dart b/lib/plugin/pl_player/controller.dart index c5295ef54..dd21ba881 100644 --- a/lib/plugin/pl_player/controller.dart +++ b/lib/plugin/pl_player/controller.dart @@ -33,6 +33,7 @@ import 'package:PiliPalaX/utils/storage.dart'; // import 'package:screen_brightness/screen_brightness.dart'; import 'package:universal_platform/universal_platform.dart'; import '../../models/video/play/subtitle.dart'; +import '../../pages/danmaku/controller.dart'; import '../../pages/video/controller.dart'; import '../../pages/video/introduction/bangumi/controller.dart'; import '../../pages/video/introduction/detail/controller.dart'; @@ -106,6 +107,8 @@ class PlPlayerController { Rx _onlyPlayAudio = false.obs; + Rx _flipX = false.obs; + /// // ignore: prefer_final_fields Rx _isSliderMoving = false.obs; @@ -236,6 +239,9 @@ class PlPlayerController { /// 听视频 Rx get onlyPlayAudio => _onlyPlayAudio; + /// 镜像 + Rx get flipX => _flipX; + /// 是否长按倍速 Rx get doubleSpeedStatus => _doubleSpeedStatus; @@ -257,11 +263,6 @@ class PlPlayerController { /// 弹幕开关 Rx isOpenDanmu = false.obs; - - /// 弹幕权重 - ValueNotifier danmakuWeight = ValueNotifier(0); - ValueNotifier>> danmakuFilterRule = - ValueNotifier([]); // 关联弹幕控制器 DanmakuController? danmakuController; // 弹幕相关配置 @@ -358,12 +359,6 @@ class PlPlayerController { _videoType = videoType; isOpenDanmu.value = setting.get(SettingBoxKey.enableShowDanmaku, defaultValue: true); - danmakuWeight.value = - setting.get(SettingBoxKey.danmakuWeight, defaultValue: 0); - danmakuFilterRule.value = onlineCache.get(OnlineCacheKey.danmakuFilterRule, - defaultValue: []).map>((e) { - return Map.from(e); - }).toList(); blockTypes = setting.get(SettingBoxKey.danmakuBlockType, defaultValue: []); showArea = setting.get(SettingBoxKey.danmakuShowArea, defaultValue: 0.5); // 不透明度 @@ -1555,7 +1550,7 @@ class PlPlayerController { } void putDanmakuSettings() { - setting.put(SettingBoxKey.danmakuWeight, danmakuWeight.value); + setting.put(SettingBoxKey.danmakuWeight, PlDanmakuController.danmakuWeight); setting.put(SettingBoxKey.danmakuBlockType, blockTypes); setting.put(SettingBoxKey.danmakuShowArea, showArea); setting.put(SettingBoxKey.danmakuOpacity, opacityVal); diff --git a/lib/plugin/pl_player/view.dart b/lib/plugin/pl_player/view.dart index 49e0ff31d..868986ace 100644 --- a/lib/plugin/pl_player/view.dart +++ b/lib/plugin/pl_player/view.dart @@ -599,6 +599,17 @@ class _PLVideoPlayerState extends State color: Colors.white, fontSize: 12, ); + Widget video = Video( + key: ValueKey('${_.videoFit.value}${_.continuePlayInBackground.value}'), + controller: videoController, + controls: NoVideoControls, + pauseUponEnteringBackgroundMode: !_.continuePlayInBackground.value, + resumeUponEnteringForegroundMode: true, + // 字幕尺寸调节 + subtitleViewConfiguration: const SubtitleViewConfiguration( + style: subTitleStyle, padding: EdgeInsets.all(24.0)), + fit: _.videoFit.value, + ); return Stack( fit: StackFit.passthrough, key: _playerKey, @@ -792,19 +803,7 @@ class _PLVideoPlayerState extends State _initialFocalPoint = Offset.zero; _gestureType = null; }, - child: Video( - key: ValueKey( - '${_.videoFit.value}${_.continuePlayInBackground.value}'), - controller: videoController, - controls: NoVideoControls, - pauseUponEnteringBackgroundMode: - !_.continuePlayInBackground.value, - resumeUponEnteringForegroundMode: true, - // 字幕尺寸调节 - subtitleViewConfiguration: const SubtitleViewConfiguration( - style: subTitleStyle, padding: EdgeInsets.all(24.0)), - fit: _.videoFit.value, - ), + child: Transform.flip(flipX: _.flipX.value, child: video), ), ),