From 569184a507c9233fb0bb5c599e61d6dd76ab9f8d Mon Sep 17 00:00:00 2001 From: orz12 Date: Mon, 22 Jan 2024 12:03:17 +0800 Subject: [PATCH 01/25] =?UTF-8?q?opt:=20=E5=88=87=E6=8D=A2=E9=A1=B5?= =?UTF-8?q?=E9=9D=A2=E6=97=B6=E9=94=80=E6=AF=81=E6=92=AD=E6=94=BE=E5=99=A8?= =?UTF-8?q?=E7=BB=84=E4=BB=B6=E6=8F=90=E5=8D=87=E6=80=A7=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/view.dart | 78 ++++++++++++++++---------------- 1 file changed, 40 insertions(+), 38 deletions(-) diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 59fa39b2..3d3c1c30 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -61,6 +61,7 @@ class _VideoDetailPageState extends State final Floating floating = Floating(); // 生命周期监听 late final AppLifecycleListener _lifecycleListener; + bool isShowing = true; @override void initState() { @@ -216,15 +217,15 @@ class _VideoDetailPageState extends State videoIntroController.isPaused = true; plPlayerController!.removeStatusLister(playerListener); plPlayerController!.pause(); - plPlayerController!.danmakuController?.pause(); - plPlayerController!.danmakuController?.clear(); } + setState(() => isShowing = false); super.didPushNext(); } @override // 返回当前页面时 void didPopNext() async { + setState(() => isShowing = true); videoDetailController.isFirstTime = false; final bool autoplay = autoPlayEnable; videoDetailController.playerInit(autoplay: autoplay); @@ -354,44 +355,45 @@ class _VideoDetailPageState extends State boxConstraints.maxHeight; return Stack( children: [ - FutureBuilder( - future: _futureBuilderFuture, - builder: (BuildContext context, - AsyncSnapshot snapshot) { - if (snapshot.hasData && - snapshot.data['status']) { - return Obx( - () => !videoDetailController - .autoPlay.value - ? const SizedBox() - : PLVideoPlayer( - controller: - plPlayerController!, - headerControl: - videoDetailController - .headerControl, - danmuWidget: Obx( - () => PlDanmaku( - key: Key( - videoDetailController - .danmakuCid - .value - .toString()), - cid: - videoDetailController - .danmakuCid - .value, - playerController: - plPlayerController!, + if (isShowing) + FutureBuilder( + future: _futureBuilderFuture, + builder: (BuildContext context, + AsyncSnapshot snapshot) { + if (snapshot.hasData && + snapshot.data['status']) { + return Obx( + () => !videoDetailController + .autoPlay.value + ? nil + : PLVideoPlayer( + controller: + plPlayerController!, + headerControl: + videoDetailController + .headerControl, + danmuWidget: Obx( + () => PlDanmaku( + key: Key( + videoDetailController + .danmakuCid + .value + .toString()), + cid: + videoDetailController + .danmakuCid + .value, + playerController: + plPlayerController!, + ), ), ), - ), - ); - } else { - return const SizedBox(); - } - }, - ), + ); + } else { + return const SizedBox(); + } + }, + ), /// 关闭自动播放时 手动播放 if (!videoDetailController From 103423abf7503e376a79039a74c56b82e5b5633d Mon Sep 17 00:00:00 2001 From: orz12 Date: Mon, 22 Jan 2024 12:04:00 +0800 Subject: [PATCH 02/25] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E9=83=A8?= =?UTF-8?q?=E5=88=86=E6=89=8B=E6=9C=BA=E6=A8=AA=E5=B1=8F=E4=B8=A4=E4=BE=A7?= =?UTF-8?q?=E4=B8=8D=E7=AD=89=E5=AE=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/view.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 3d3c1c30..278e2c64 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -292,8 +292,8 @@ class _VideoDetailPageState extends State plPlayerController?.isFullScreen.value == true, bottom: MediaQuery.of(context).orientation == Orientation.portrait && plPlayerController?.isFullScreen.value == true, - left: plPlayerController?.isFullScreen.value != true, - right: plPlayerController?.isFullScreen.value != true, + left: false,//plPlayerController?.isFullScreen.value != true, + right: false, //plPlayerController?.isFullScreen.value != true, child: Stack( children: [ Scaffold( From 1dd70f482fb2162992aeeb27f37a99e24698a7ab Mon Sep 17 00:00:00 2001 From: orz12 Date: Mon, 22 Jan 2024 12:54:32 +0800 Subject: [PATCH 03/25] =?UTF-8?q?fix:=20=E6=97=8B=E8=BD=AC=E6=A8=AA?= =?UTF-8?q?=E5=B1=8F=E4=BB=8D=E6=9C=89=E7=8A=B6=E6=80=81=E6=A0=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/view.dart | 362 ++++++++++++++++--------------- 1 file changed, 184 insertions(+), 178 deletions(-) diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 278e2c64..507ea66e 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -281,18 +281,12 @@ class _VideoDetailPageState extends State final double videoHeight = MediaQuery.sizeOf(context).width * 9 / 16; final double pinnedHeaderHeight = statusBarHeight + kToolbarHeight + videoHeight; - if (MediaQuery.of(context).orientation == Orientation.landscape || - plPlayerController?.isFullScreen.value == true) { - enterFullScreen(); - } else { - exitFullScreen(); - } Widget childWhenDisabled = SafeArea( top: MediaQuery.of(context).orientation == Orientation.portrait && plPlayerController?.isFullScreen.value == true, bottom: MediaQuery.of(context).orientation == Orientation.portrait && plPlayerController?.isFullScreen.value == true, - left: false,//plPlayerController?.isFullScreen.value != true, + left: false, //plPlayerController?.isFullScreen.value != true, right: false, //plPlayerController?.isFullScreen.value != true, child: Stack( children: [ @@ -313,185 +307,197 @@ class _VideoDetailPageState extends State (BuildContext context, bool innerBoxIsScrolled) { return [ Obx( - () => SliverAppBar( - automaticallyImplyLeading: false, - // 假装使用一个非空变量,避免Obx检测不到而罢工 - pinned: videoDetailController.autoPlay.value ^ - false ^ - videoDetailController.autoPlay.value, - elevation: 0, - scrolledUnderElevation: 0, - forceElevated: innerBoxIsScrolled, - expandedHeight: MediaQuery.of(context).orientation == - Orientation.landscape || - plPlayerController?.isFullScreen.value == true - ? MediaQuery.sizeOf(context).height - - (MediaQuery.of(context).orientation == - Orientation.landscape - ? 0 - : MediaQuery.of(context).padding.top) - : videoHeight, - backgroundColor: Colors.black, - flexibleSpace: FlexibleSpaceBar( - background: PopScope( - canPop: - plPlayerController?.isFullScreen.value != true, - onPopInvoked: (bool didPop) { - if (plPlayerController?.isFullScreen.value == - true) { - plPlayerController! - .triggerFullScreen(status: false); - } - if (MediaQuery.of(context).orientation == - Orientation.landscape) { - verticalScreen(); - } - }, - child: LayoutBuilder( - builder: (BuildContext context, - BoxConstraints boxConstraints) { - final double maxWidth = boxConstraints.maxWidth; - final double maxHeight = - boxConstraints.maxHeight; - return Stack( - children: [ - if (isShowing) - FutureBuilder( - future: _futureBuilderFuture, - builder: (BuildContext context, - AsyncSnapshot snapshot) { - if (snapshot.hasData && - snapshot.data['status']) { - return Obx( - () => !videoDetailController - .autoPlay.value - ? nil - : PLVideoPlayer( - controller: - plPlayerController!, - headerControl: - videoDetailController - .headerControl, - danmuWidget: Obx( - () => PlDanmaku( - key: Key( - videoDetailController - .danmakuCid - .value - .toString()), - cid: - videoDetailController - .danmakuCid - .value, - playerController: - plPlayerController!, - ), - ), - ), - ); - } else { - return const SizedBox(); - } - }, - ), + () { + if (MediaQuery.of(context).orientation == + Orientation.landscape || + plPlayerController?.isFullScreen.value == true) { + enterFullScreen(); + } else { + exitFullScreen(); + } + return SliverAppBar( + automaticallyImplyLeading: false, + // 假装使用一个非空变量,避免Obx检测不到而罢工 + pinned: videoDetailController.autoPlay.value ^ + false ^ + videoDetailController.autoPlay.value, + elevation: 0, + scrolledUnderElevation: 0, + forceElevated: innerBoxIsScrolled, + expandedHeight: MediaQuery.of(context).orientation == + Orientation.landscape || + plPlayerController?.isFullScreen.value == true + ? MediaQuery.sizeOf(context).height - + (MediaQuery.of(context).orientation == + Orientation.landscape + ? 0 + : MediaQuery.of(context).padding.top) + : videoHeight, + backgroundColor: Colors.black, + flexibleSpace: FlexibleSpaceBar( + background: PopScope( + canPop: plPlayerController?.isFullScreen.value != + true, + onPopInvoked: (bool didPop) { + if (plPlayerController?.isFullScreen.value == + true) { + plPlayerController! + .triggerFullScreen(status: false); + } + if (MediaQuery.of(context).orientation == + Orientation.landscape) { + verticalScreen(); + } + }, + child: LayoutBuilder( + builder: (BuildContext context, + BoxConstraints boxConstraints) { + final double maxWidth = + boxConstraints.maxWidth; + final double maxHeight = + boxConstraints.maxHeight; + return Stack( + children: [ + if (isShowing) + FutureBuilder( + future: _futureBuilderFuture, + builder: (BuildContext context, + AsyncSnapshot snapshot) { + if (snapshot.hasData && + snapshot.data['status']) { + return Obx( + () => + !videoDetailController + .autoPlay.value + ? nil + : PLVideoPlayer( + controller: + plPlayerController!, + headerControl: + videoDetailController + .headerControl, + danmuWidget: Obx( + () => PlDanmaku( + key: Key(videoDetailController + .danmakuCid + .value + .toString()), + cid: videoDetailController + .danmakuCid + .value, + playerController: + plPlayerController!, + ), + ), + ), + ); + } else { + return const SizedBox(); + } + }, + ), - /// 关闭自动播放时 手动播放 - if (!videoDetailController - .autoPlay.value) ...[ - Obx( - () => Visibility( - visible: videoDetailController - .isShowCover.value, - child: Positioned( - top: 0, - left: 0, - right: 0, - child: GestureDetector( - onTap: () { - handlePlay(); - }, - child: NetworkImgLayer( - type: 'emote', - src: videoDetailController - .videoItem['pic'], - width: maxWidth, - height: maxHeight, + /// 关闭自动播放时 手动播放 + if (!videoDetailController + .autoPlay.value) ...[ + Obx( + () => Visibility( + visible: videoDetailController + .isShowCover.value, + child: Positioned( + top: 0, + left: 0, + right: 0, + child: GestureDetector( + onTap: () { + handlePlay(); + }, + child: NetworkImgLayer( + type: 'emote', + src: videoDetailController + .videoItem['pic'], + width: maxWidth, + height: maxHeight, + ), ), ), ), ), - ), - Obx( - () => Visibility( - visible: videoDetailController - .isShowCover.value && - videoDetailController - .isEffective.value, - child: Stack( - children: [ - Positioned( - top: 0, - left: 0, - right: 0, - child: AppBar( - primary: false, - foregroundColor: - Colors.white, - elevation: 0, - scrolledUnderElevation: 0, - backgroundColor: - Colors.transparent, - actions: [ - IconButton( - tooltip: '稍后再看', - onPressed: () async { - var res = await UserHttp - .toViewLater( - bvid: - videoDetailController - .bvid); - SmartDialog.showToast( - res['msg']); - }, - icon: const Icon(Icons - .history_outlined), - ), - const SizedBox(width: 14) - ], - ), - ), - Positioned( - right: 12, - bottom: 10, - child: TextButton.icon( - style: ButtonStyle( + Obx( + () => Visibility( + visible: videoDetailController + .isShowCover.value && + videoDetailController + .isEffective.value, + child: Stack( + children: [ + Positioned( + top: 0, + left: 0, + right: 0, + child: AppBar( + primary: false, + foregroundColor: + Colors.white, + elevation: 0, + scrolledUnderElevation: 0, backgroundColor: - MaterialStateProperty - .resolveWith( - (states) { - return Colors.white - .withOpacity(0.8); - }), + Colors.transparent, + actions: [ + IconButton( + tooltip: '稍后再看', + onPressed: () async { + var res = await UserHttp + .toViewLater( + bvid: videoDetailController + .bvid); + SmartDialog + .showToast( + res['msg']); + }, + icon: const Icon(Icons + .history_outlined), + ), + const SizedBox( + width: 14) + ], ), - onPressed: () => - handlePlay(), - icon: const Icon( - Icons.play_circle_outline, - size: 20, - ), - label: const Text('轻触封面播放'), ), - ), - ], - )), - ), - ] - ], - ); - }, - )), - ), - ), + Positioned( + right: 12, + bottom: 10, + child: TextButton.icon( + style: ButtonStyle( + backgroundColor: + MaterialStateProperty + .resolveWith( + (states) { + return Colors.white + .withOpacity(0.8); + }), + ), + onPressed: () => + handlePlay(), + icon: const Icon( + Icons + .play_circle_outline, + size: 20, + ), + label: + const Text('轻触封面播放'), + ), + ), + ], + )), + ), + ] + ], + ); + }, + )), + ), + ); + }, ), ]; }, From a68c04001b883636cce6c432bc86f6f8c427aa3a Mon Sep 17 00:00:00 2001 From: orz12 Date: Mon, 22 Jan 2024 13:19:29 +0800 Subject: [PATCH 04/25] =?UTF-8?q?fix:=20=E7=AB=96=E5=B1=8F=E5=85=A8?= =?UTF-8?q?=E5=B1=8F=E5=BC=82=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/view.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 507ea66e..dec474ba 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -304,7 +304,7 @@ class _VideoDetailPageState extends State body: ExtendedNestedScrollView( controller: _extendNestCtr, headerSliverBuilder: - (BuildContext context, bool innerBoxIsScrolled) { + (BuildContext context2, bool innerBoxIsScrolled) { return [ Obx( () { From 16895b5c32a2e26522323f4d40cc62c7f7a7a17f Mon Sep 17 00:00:00 2001 From: orz12 Date: Tue, 23 Jan 2024 15:12:52 +0800 Subject: [PATCH 05/25] =?UTF-8?q?fix:=20=E7=82=B9=E5=87=BB=E8=A7=86?= =?UTF-8?q?=E9=A2=91=E8=AF=84=E8=AE=BA=E5=8C=BA=E7=94=A8=E6=88=B7=E5=A4=B4?= =?UTF-8?q?=E5=83=8F=E5=90=8E=E8=BF=94=E5=9B=9E=E8=AF=A6=E6=83=85=E9=A1=B5?= =?UTF-8?q?=E7=81=B0=E5=B1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/related/controller.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pages/video/detail/related/controller.dart b/lib/pages/video/detail/related/controller.dart index eb1a14d5..f7042871 100644 --- a/lib/pages/video/detail/related/controller.dart +++ b/lib/pages/video/detail/related/controller.dart @@ -4,7 +4,7 @@ import 'package:pilipala/http/video.dart'; class ReleatedController extends GetxController { // 视频aid - String bvid = Get.parameters['bvid']!; + String bvid = Get.parameters['bvid'] ?? ""; // 推荐视频列表 List relatedVideoList = []; From aaeecc9e531d3340bce5a02074286e79d4015d6a Mon Sep 17 00:00:00 2001 From: orz12 Date: Wed, 24 Jan 2024 11:01:54 +0800 Subject: [PATCH 06/25] =?UTF-8?q?fix:=20=E9=87=8D=E5=8A=9B=E6=97=8B?= =?UTF-8?q?=E8=BD=AC=E5=90=8E=E5=88=92=E5=87=BA=E4=B8=8B=E6=96=B9=E7=9A=84?= =?UTF-8?q?=E8=AF=A6=E6=83=85=E9=A1=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/view.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index dec474ba..1ce78b3c 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -508,7 +508,9 @@ class _VideoDetailPageState extends State // }, /// 不收回 pinnedHeaderSliverHeightBuilder: () { - return plPlayerController?.isFullScreen.value == true + return MediaQuery.of(context).orientation == + Orientation.landscape || + plPlayerController?.isFullScreen.value == true ? MediaQuery.sizeOf(context).height : pinnedHeaderHeight; }, From 545def36e6a6b8ec64fcbc038712f86099041308 Mon Sep 17 00:00:00 2001 From: orz12 Date: Thu, 25 Jan 2024 12:59:21 +0800 Subject: [PATCH 07/25] =?UTF-8?q?mod:=20=E8=87=AA=E5=8A=A8=E6=92=AD?= =?UTF-8?q?=E6=94=BE=E6=8C=89=E9=92=AE=E6=94=B9=E4=B8=BA=E5=AE=98=E6=96=B9?= =?UTF-8?q?=E7=89=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/view.dart | 29 +++++++++-------------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 1ce78b3c..3644fa41 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -466,26 +466,15 @@ class _VideoDetailPageState extends State Positioned( right: 12, bottom: 10, - child: TextButton.icon( - style: ButtonStyle( - backgroundColor: - MaterialStateProperty - .resolveWith( - (states) { - return Colors.white - .withOpacity(0.8); - }), - ), - onPressed: () => - handlePlay(), - icon: const Icon( - Icons - .play_circle_outline, - size: 20, - ), - label: - const Text('轻触封面播放'), - ), + child: IconButton( + tooltip: '播放', + onPressed: () => + handlePlay(), + icon: Image.asset( + 'assets/images/play.png', + width: 60, + height: 60, + )), ), ], )), From bf8ae0f3172c77e234e1512eb32715a3e8e27bf3 Mon Sep 17 00:00:00 2001 From: orz12 Date: Thu, 25 Jan 2024 11:36:20 +0800 Subject: [PATCH 08/25] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E5=BC=B9?= =?UTF-8?q?=E5=B9=95=E6=8F=8F=E8=BE=B9=E7=B2=97=E7=BB=86=E8=AE=BE=E7=BD=AE?= =?UTF-8?q?=EF=BC=8C=E9=BB=98=E8=AE=A4=E5=80=BC=E9=99=8D=E4=BD=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/danmaku/view.dart | 3 ++ .../video/detail/widgets/header_control.dart | 41 +++++++++++++++++++ lib/plugin/pl_player/controller.dart | 5 +++ lib/utils/storage.dart | 3 +- 4 files changed, 51 insertions(+), 1 deletion(-) diff --git a/lib/pages/danmaku/view.dart b/lib/pages/danmaku/view.dart index 83d6020e..109f0206 100644 --- a/lib/pages/danmaku/view.dart +++ b/lib/pages/danmaku/view.dart @@ -35,6 +35,7 @@ class _PlDanmakuState extends State { late double opacityVal; late double fontSizeVal; late double danmakuDurationVal; + late double strokeWidth; int latestAddedPosition = -1; @override @@ -65,6 +66,7 @@ class _PlDanmakuState extends State { showArea = playerController.showArea; opacityVal = playerController.opacityVal; fontSizeVal = playerController.fontSizeVal; + strokeWidth = playerController.strokeWidth; danmakuDurationVal = playerController.danmakuDurationVal; } @@ -136,6 +138,7 @@ class _PlDanmakuState extends State { hideBottom: blockTypes.contains(4), duration: danmakuDurationVal / playerController.playbackSpeed, + strokeWidth: strokeWidth, // initDuration / // (danmakuSpeedVal * widget.playerController.playbackSpeed), ), diff --git a/lib/pages/video/detail/widgets/header_control.dart b/lib/pages/video/detail/widgets/header_control.dart index f2e38870..3af35efd 100644 --- a/lib/pages/video/detail/widgets/header_control.dart +++ b/lib/pages/video/detail/widgets/header_control.dart @@ -1,4 +1,5 @@ import 'dart:io'; +import 'dart:math'; import 'package:floating/floating.dart'; import 'package:flutter/material.dart'; @@ -724,6 +725,8 @@ class _HeaderControlState extends State { double fontSizeVal = widget.controller!.fontSizeVal; // 弹幕速度 double danmakuDurationVal = widget.controller!.danmakuDurationVal; + // 弹幕描边 + double strokeWidth = widget.controller!.strokeWidth; final DanmakuController danmakuController = widget.controller!.danmakuController!; @@ -857,6 +860,44 @@ class _HeaderControlState extends State { ), ), ), + Text('描边粗细 $strokeWidth'), + Padding( + padding: const EdgeInsets.only( + top: 0, + bottom: 6, + left: 10, + right: 10, + ), + child: SliderTheme( + data: SliderThemeData( + trackShape: MSliderTrackShape(), + thumbColor: Theme.of(context).colorScheme.primary, + activeTrackColor: Theme.of(context).colorScheme.primary, + trackHeight: 10, + thumbShape: const RoundSliderThumbShape( + enabledThumbRadius: 6.0), + ), + child: Slider( + min: 0, + max: 3, + value: strokeWidth, + divisions: 6, + label: '$strokeWidth', + onChanged: (double val) { + strokeWidth = val; + widget.controller!.strokeWidth = val; + setState(() {}); + try { + final DanmakuOption currentOption = + danmakuController.option; + final DanmakuOption updatedOption = + currentOption.copyWith(strokeWidth: val); + danmakuController.updateOption(updatedOption); + } catch (_) {} + }, + ), + ), + ), Text('字体大小 ${(fontSizeVal * 100).toStringAsFixed(1)}%'), Padding( padding: const EdgeInsets.only( diff --git a/lib/plugin/pl_player/controller.dart b/lib/plugin/pl_player/controller.dart index d35987f8..dfa580ab 100644 --- a/lib/plugin/pl_player/controller.dart +++ b/lib/plugin/pl_player/controller.dart @@ -221,6 +221,7 @@ class PlPlayerController { late double showArea; late double opacityVal; late double fontSizeVal; + late double strokeWidth; late double danmakuDurationVal; late List speedsList; // 缓存 @@ -275,6 +276,9 @@ class PlPlayerController { // 弹幕时间 danmakuDurationVal = localCache.get(LocalCacheKey.danmakuDuration, defaultValue: 4.0); + // 描边粗细 + strokeWidth = + localCache.get(LocalCacheKey.strokeWidth, defaultValue: 1.5); playRepeat = PlayRepeat.values.toList().firstWhere( (e) => e.value == @@ -1086,6 +1090,7 @@ class PlPlayerController { localCache.put(LocalCacheKey.danmakuOpacity, opacityVal); localCache.put(LocalCacheKey.danmakuFontScale, fontSizeVal); localCache.put(LocalCacheKey.danmakuDuration, danmakuDurationVal); + localCache.put(LocalCacheKey.strokeWidth, strokeWidth); if (_videoPlayerController != null) { var pp = _videoPlayerController!.platform as NativePlayer; await pp.setProperty('audio-files', ''); diff --git a/lib/utils/storage.dart b/lib/utils/storage.dart index 99037611..e4bc8048 100644 --- a/lib/utils/storage.dart +++ b/lib/utils/storage.dart @@ -158,12 +158,13 @@ class LocalCacheKey { wbiKeys = 'wbiKeys', timeStamp = 'timeStamp', - // 弹幕相关设置 屏蔽类型 显示区域 透明度 字体大小 弹幕时间 + // 弹幕相关设置 屏蔽类型 显示区域 透明度 字体大小 弹幕时间 描边粗细 danmakuBlockType = 'danmakuBlockType', danmakuShowArea = 'danmakuShowArea', danmakuOpacity = 'danmakuOpacity', danmakuFontScale = 'danmakuFontScale', danmakuDuration = 'danmakuDuration', + strokeWidth = 'strokeWidth', // 代理host port systemProxyHost = 'systemProxyHost', From 1b03f3f31ffd594eb3bb50411219c91e5a575de7 Mon Sep 17 00:00:00 2001 From: orz12 Date: Wed, 24 Jan 2024 21:43:02 +0800 Subject: [PATCH 09/25] =?UTF-8?q?mod:=20AI=E8=A7=86=E9=A2=91=E6=80=BB?= =?UTF-8?q?=E7=BB=93=E6=9C=AA=E6=94=AF=E6=8C=81=E6=8F=90=E9=86=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/video.dart | 7 ++++++- lib/pages/video/detail/introduction/controller.dart | 4 +++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/http/video.dart b/lib/http/video.dart index 923e93a2..e7b09801 100644 --- a/lib/http/video.dart +++ b/lib/http/video.dart @@ -447,11 +447,16 @@ class VideoHttp { 'up_mid': upMid, }); var res = await Request().get(Api.aiConclusion, data: params); - if (res.data['code'] == 0) { + if (res.data['code'] == 0 && res.data['data']['code'] == 0) { return { 'status': true, 'data': AiConclusionModel.fromJson(res.data['data']), }; + } else { + return { + 'status': false, + 'data': [] + }; } } } diff --git a/lib/pages/video/detail/introduction/controller.dart b/lib/pages/video/detail/introduction/controller.dart index 1230a94b..1deadb07 100644 --- a/lib/pages/video/detail/introduction/controller.dart +++ b/lib/pages/video/detail/introduction/controller.dart @@ -570,10 +570,12 @@ class VideoIntroController extends GetxController { cid: lastPlayCid.value, upMid: videoDetail.value.owner!.mid!, ); + SmartDialog.dismiss(); if (res['status']) { modelResult = res['data'].modelResult; + } else { + SmartDialog.showToast("当前视频可能暂不支持AI视频总结"); } - SmartDialog.dismiss(); return res; } } From 9663278916014376f4142d557c0d328bf5ee3825 Mon Sep 17 00:00:00 2001 From: orz12 Date: Thu, 25 Jan 2024 20:24:13 +0800 Subject: [PATCH 10/25] =?UTF-8?q?feat:=20=E7=A7=81=E4=BF=A1=E6=98=BE?= =?UTF-8?q?=E7=A4=BA=E5=88=86=E4=BA=AB=E8=A7=86=E9=A2=91=E5=86=85=E5=AE=B9?= =?UTF-8?q?=E3=80=81=E5=AF=8C=E6=96=87=E6=9C=AC=E8=A1=A8=E6=83=85=EF=BC=8C?= =?UTF-8?q?=E8=A1=A5=E5=85=85=E4=BF=A1=E6=81=AF=E7=B1=BB=E5=9E=8B=E6=9E=9A?= =?UTF-8?q?=E4=B8=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/models/msg/session.dart | 2 +- lib/pages/whisper_detail/controller.dart | 6 + lib/pages/whisper_detail/view.dart | 8 +- .../whisper_detail/widget/chat_item.dart | 206 +++++++++++++++--- 4 files changed, 187 insertions(+), 35 deletions(-) diff --git a/lib/models/msg/session.dart b/lib/models/msg/session.dart index 1fa05cb0..ea241249 100644 --- a/lib/models/msg/session.dart +++ b/lib/models/msg/session.dart @@ -166,7 +166,7 @@ class SessionMsgDataModel { int? hasMore; int? minSeqno; int? maxSeqno; - List? eInfos; + List? eInfos; SessionMsgDataModel.fromJson(Map json) { messages = json['messages'] diff --git a/lib/pages/whisper_detail/controller.dart b/lib/pages/whisper_detail/controller.dart index 52a70c49..71dd4c03 100644 --- a/lib/pages/whisper_detail/controller.dart +++ b/lib/pages/whisper_detail/controller.dart @@ -1,3 +1,4 @@ +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:pilipala/http/msg.dart'; import 'package:pilipala/models/msg/session.dart'; @@ -8,6 +9,8 @@ class WhisperDetailController extends GetxController { late String face; late String mid; RxList messageList = [].obs; + //表情转换图片规则 + List? eInfos; @override void onInit() { @@ -22,6 +25,9 @@ class WhisperDetailController extends GetxController { var res = await MsgHttp.sessionMsg(talkerId: talkerId); if (res['status']) { messageList.value = res['data'].messages; + if (messageList.isNotEmpty && res['data'].eInfos != null) { + eInfos = res['data'].eInfos; + } } return res; } diff --git a/lib/pages/whisper_detail/view.dart b/lib/pages/whisper_detail/view.dart index 5d108037..8d2297c4 100644 --- a/lib/pages/whisper_detail/view.dart +++ b/lib/pages/whisper_detail/view.dart @@ -110,12 +110,16 @@ class _WhisperDetailPageState extends State { if (i == 0) { return Column( children: [ - ChatItem(item: messageList[i]), + ChatItem( + item: messageList[i], + e_infos: _whisperDetailController.eInfos), const SizedBox(height: 12), ], ); } else { - return ChatItem(item: messageList[i]); + return ChatItem( + item: messageList[i], + e_infos: _whisperDetailController.eInfos); } }, ), diff --git a/lib/pages/whisper_detail/widget/chat_item.dart b/lib/pages/whisper_detail/widget/chat_item.dart index 9ea6bf2a..9f0e191b 100644 --- a/lib/pages/whisper_detail/widget/chat_item.dart +++ b/lib/pages/whisper_detail/widget/chat_item.dart @@ -1,38 +1,200 @@ // ignore_for_file: must_be_immutable +import 'dart:convert'; + import 'package:flutter/material.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; +import 'package:get/get.dart'; +import 'package:get/get_core/src/get_main.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; import 'package:pilipala/utils/utils.dart'; import 'package:pilipala/utils/storage.dart'; +import '../../../http/search.dart'; + +enum MsgType { + invalid(value: 0, label: "空空的~"), + text(value: 1, label: "文本消息"), + pic(value: 2, label: "图片消息"), + audio(value: 3, label: "语音消息"), + share(value: 4, label: "分享消息"), + revoke(value: 5, label: "撤回消息"), + custom_face(value: 6, label: "自定义表情"), + share_v2(value: 7, label: "分享v2消息"), + sys_cancel(value: 8, label: "系统撤销"), + mini_program(value: 9, label: "小程序"), + notify_msg(value: 10, label: "业务通知"), + archive_card(value: 11, label: "投稿卡片"), + article_card(value: 12, label: "专栏卡片"), + pic_card(value: 13, label: "图片卡片"), + common_share(value: 14, label: "异形卡片"), + notify_text(value: 18, label: "文本提示"); + + final int value; + final String label; + const MsgType({required this.value, required this.label}); + static MsgType parse(int value) { + return MsgType.values + .firstWhere((e) => e.value == value, orElse: () => MsgType.invalid); + } +} + class ChatItem extends StatelessWidget { dynamic item; + List? e_infos; ChatItem({ super.key, this.item, + this.e_infos, }); @override Widget build(BuildContext context) { bool isOwner = item.senderUid == GStrorage.userInfo.get('userInfoCache').mid; - bool isPic = item.msgType == 2; // 图片 - bool isText = item.msgType == 1; // 文本 - // bool isAchive = item.msgType == 11; // 投稿 + bool isPic = item.msgType == MsgType.pic; // 图片 + bool isText = item.msgType == MsgType.text; // 文本 + // bool isArchive = item.msgType == 11; // 投稿 // bool isArticle = item.msgType == 12; // 专栏 - bool isRevoke = item.msgType == 5; // 撤回消息 - + bool isRevoke = item.msgType == MsgType.revoke; // 撤回消息 + bool isShareV2 = item.msgType == MsgType.share_v2; bool isSystem = item.msgType == 18 || item.msgType == 10 || item.msgType == 13; - int msgType = item.msgType; dynamic content = item.content ?? ''; + Color textColor(BuildContext context) { + return isOwner + ? Theme.of(context).colorScheme.onPrimary + : Theme.of(context).colorScheme.onSecondaryContainer; + } + + Widget richTextMessage(BuildContext context) { + var text = content['content']; + if (e_infos != null) { + final List children = []; + Map emojiMap = {}; + for (var e in e_infos!) { + emojiMap[e['text']] = e['url']; + } + text.splitMapJoin( + RegExp(r"\[.+?\]"), + onMatch: (Match match) { + final String emojiKey = match[0]!; + if (emojiMap.containsKey(emojiKey)) { + children.add(WidgetSpan( + child: NetworkImgLayer( + width: 18, height: 18, + src: emojiMap[emojiKey]!,), + )); + } + return ''; + }, + onNonMatch: (String text) { + children.add(TextSpan(text: text, style: TextStyle( + color: textColor(context), + letterSpacing: 0.6, + height: 1.5, + ))); + return ''; + }, + ); + return RichText( + text: TextSpan( + children: children, + ), + ); + } else { + return Text( + text, + style: TextStyle( + letterSpacing: 0.6, + color: textColor(context), + height: 1.5, + ), + ); + } + } + + Widget messageContent(BuildContext context) { + switch (MsgType.parse(item.msgType)) { + case MsgType.notify_msg: + return SystemNotice(item: item); + case MsgType.pic_card: + return SystemNotice2(item: item); + case MsgType.notify_text: + return Text( + jsonDecode(content['content']).map((m) => m['text'] as String).join("\n"), + style: TextStyle( + letterSpacing: 0.6, + height: 5, + color: Theme.of(context).colorScheme.outline.withOpacity(0.8) + ), + ); + case MsgType.text: + return richTextMessage(context); + case MsgType.pic: + return NetworkImgLayer( + width: 220, + height: 220 * content['height'] / content['width'], + src: content['url'], + ); + case MsgType.share_v2: + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + GestureDetector( + onTap: () async { + SmartDialog.showLoading(); + var bvid = content["bvid"]; + final int cid = await SearchHttp.ab2c(bvid: bvid); + final String heroTag = Utils.makeHeroTag(bvid); + SmartDialog.dismiss().then( + (e) => Get.toNamed('/video?bvid=$bvid&cid=$cid', + arguments: { + 'pic': content['thumb'], + 'heroTag': heroTag, + }), + ); + }, + child: NetworkImgLayer( + width: 220, + height: 220 * 9 / 16, + src: content['thumb'], + ), + ), + const SizedBox(height: 6), + Text( + content['title'], + style: TextStyle( + letterSpacing: 0.6, + height: 1.5, + color: textColor(context), fontWeight: FontWeight.bold), + ), + const SizedBox(height: 1), + Text( + content['author'], + style: TextStyle( + letterSpacing: 0.6, + height: 1.5, + color: textColor(context).withOpacity(0.6), + fontSize: 12, + ), + ), + ], + ); + default: + return Text( + content['content'] ?? content.toString(), + style: TextStyle( + letterSpacing: 0.6, + height: 1.5, + color: textColor(context), fontWeight: FontWeight.bold), + ); + } + } + return isSystem - ? (msgType == 10 - ? SystemNotice(item: item) - : msgType == 13 - ? SystemNotice2(item: item) - : const SizedBox()) + ? messageContent(context) : isRevoke ? const SizedBox() : Row( @@ -66,27 +228,7 @@ class ChatItem extends StatelessWidget { ? CrossAxisAlignment.end : CrossAxisAlignment.start, children: [ - isText - ? Text( - content['content'], - style: TextStyle( - color: isOwner - ? Theme.of(context) - .colorScheme - .onPrimary - : Theme.of(context) - .colorScheme - .onSecondaryContainer), - ) - : isPic - ? NetworkImgLayer( - width: 220, - height: 220 * - content['height'] / - content['width'], - src: content['url'], - ) - : const SizedBox(), + messageContent(context), SizedBox(height: isPic ? 7 : 2), Row( mainAxisSize: MainAxisSize.min, From c0162892ef310a4d34daaf9c370bc2ba6017f84f Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 27 Jan 2024 13:05:59 +0800 Subject: [PATCH 11/25] =?UTF-8?q?mod:=20=E9=95=BF=E5=9B=BE=E6=A0=87?= =?UTF-8?q?=E7=AD=BE=E6=98=BE=E7=A4=BA=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/dynamics/widgets/content_panel.dart | 3 ++- lib/pages/dynamics/widgets/pic_panel.dart | 3 ++- lib/pages/video/detail/reply/widgets/reply_item.dart | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/pages/dynamics/widgets/content_panel.dart b/lib/pages/dynamics/widgets/content_panel.dart index eee4255c..d10804d7 100644 --- a/lib/pages/dynamics/widgets/content_panel.dart +++ b/lib/pages/dynamics/widgets/content_panel.dart @@ -1,5 +1,6 @@ // 内容 import 'package:flutter/material.dart'; +import 'package:get/get.dart'; import 'package:pilipala/common/widgets/badge.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; import 'package:pilipala/models/dynamics/result.dart'; @@ -80,7 +81,7 @@ class _ContentState extends State { height: height, ), ), - height > maxHeight + height > Get.size.height * 0.9 ? const PBadge( text: '长图', right: 8, diff --git a/lib/pages/dynamics/widgets/pic_panel.dart b/lib/pages/dynamics/widgets/pic_panel.dart index 25b22c21..4e94e6fd 100644 --- a/lib/pages/dynamics/widgets/pic_panel.dart +++ b/lib/pages/dynamics/widgets/pic_panel.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:get/get.dart'; import 'package:pilipala/common/constants.dart'; import 'package:pilipala/common/widgets/badge.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; @@ -87,7 +88,7 @@ Widget picWidget(item, context) { childAspectRatio: aspectRatio, children: list, ), - if (len == 1 && origAspectRatio < 0.4) + if (len == 1 && height > Get.size.height * 0.9) const PBadge( text: '长图', top: null, diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index 475c0939..48518bee 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -880,7 +880,7 @@ InlineSpan buildContent( height: height, ), ), - height > maxHeight + height > Get.size.height * 0.9 ? const PBadge( text: '长图', right: 8, From e972d17f1ca94361c72499a76b5910aa9771b3b6 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 27 Jan 2024 22:37:16 +0800 Subject: [PATCH 12/25] =?UTF-8?q?mod:=20=E4=BC=98=E5=8C=96=E8=AF=B7?= =?UTF-8?q?=E6=B1=82=E5=BC=82=E5=B8=B8=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/init.dart | 20 ++++++++++++++++---- lib/http/interceptor.dart | 40 ++++++++++++++++----------------------- 2 files changed, 32 insertions(+), 28 deletions(-) diff --git a/lib/http/init.dart b/lib/http/init.dart index 6244df04..dcc9f402 100644 --- a/lib/http/init.dart +++ b/lib/http/init.dart @@ -179,8 +179,14 @@ class Request { ); return response; } on DioException catch (e) { - print('get error: $e'); - return Future.error(await ApiInterceptor.dioError(e)); + Response errResponse = Response( + data: { + 'message': await ApiInterceptor.dioError(e) + }, // 将自定义 Map 数据赋值给 Response 的 data 属性 + statusCode: 200, + requestOptions: RequestOptions(), + ); + return errResponse; } } @@ -201,8 +207,14 @@ class Request { // print('post success: ${response.data}'); return response; } on DioException catch (e) { - print('post error: $e'); - return Future.error(await ApiInterceptor.dioError(e)); + Response errResponse = Response( + data: { + 'message': await ApiInterceptor.dioError(e) + }, // 将自定义 Map 数据赋值给 Response 的 data 属性 + statusCode: 200, + requestOptions: RequestOptions(), + ); + return errResponse; } } diff --git a/lib/http/interceptor.dart b/lib/http/interceptor.dart index 48ba7e60..362ff17a 100644 --- a/lib/http/interceptor.dart +++ b/lib/http/interceptor.dart @@ -5,7 +5,6 @@ import 'package:dio/dio.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:hive/hive.dart'; import '../utils/storage.dart'; -// import 'package:get/get.dart' hide Response; class ApiInterceptor extends Interceptor { @override @@ -71,35 +70,28 @@ class ApiInterceptor extends Interceptor { return '发送请求超时,请检查网络设置'; case DioExceptionType.unknown: final String res = await checkConnect(); - return '$res \n 网络异常,请稍后重试!'; - // default: - // return 'Dio异常'; + return '$res,网络异常!'; } } static Future checkConnect() async { final ConnectivityResult connectivityResult = await Connectivity().checkConnectivity(); - if (connectivityResult == ConnectivityResult.mobile) { - return 'connected with mobile network'; - } else if (connectivityResult == ConnectivityResult.wifi) { - return 'connected with wifi network'; - } else if (connectivityResult == ConnectivityResult.ethernet) { - // I am connected to a ethernet network. - return ''; - } else if (connectivityResult == ConnectivityResult.vpn) { - // I am connected to a vpn network. - // Note for iOS and macOS: - // There is no separate network interface type for [vpn]. - // It returns [other] on any device (also simulator) - return ''; - } else if (connectivityResult == ConnectivityResult.other) { - // I am connected to a network which is not in the above mentioned networks. - return ''; - } else if (connectivityResult == ConnectivityResult.none) { - return 'not connected to any network'; - } else { - return ''; + switch (connectivityResult) { + case ConnectivityResult.mobile: + return '正在使用移动流量'; + case ConnectivityResult.wifi: + return '正在使用wifi'; + case ConnectivityResult.ethernet: + return '正在使用局域网'; + case ConnectivityResult.vpn: + return '正在使用代理网络'; + case ConnectivityResult.other: + return '正在使用其他网络'; + case ConnectivityResult.none: + return '未连接到任何网络'; + default: + return ''; } } } From 22a2628513e65606bf84fe026392c4c54a25af25 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 27 Jan 2024 22:42:04 +0800 Subject: [PATCH 13/25] =?UTF-8?q?fix:=20=E8=AF=84=E8=AE=BA=E5=8C=BA?= =?UTF-8?q?=E5=8A=A0=E8=BD=BD=E8=B6=85=E6=97=B6=E5=AF=BC=E8=87=B4=E8=A7=86?= =?UTF-8?q?=E5=9B=BE=E5=B4=A9=E6=BA=83=20issues=20#389?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/reply/view.dart | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/pages/video/detail/reply/view.dart b/lib/pages/video/detail/reply/view.dart index 7232ebab..b2c67b1e 100644 --- a/lib/pages/video/detail/reply/view.dart +++ b/lib/pages/video/detail/reply/view.dart @@ -256,7 +256,12 @@ class _VideoReplyPanelState extends State // 请求错误 return HttpError( errMsg: data['msg'], - fn: () => setState(() {}), + fn: () { + setState(() { + _futureBuilderFuture = + _videoReplyController.queryReplyList(); + }); + }, ); } } else { From a78ce4f6d456d1a5a99e35fafd051d35b1a39b5b Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 28 Jan 2024 15:52:30 +0800 Subject: [PATCH 14/25] =?UTF-8?q?feat:=20=E9=94=99=E8=AF=AF=E6=97=A5?= =?UTF-8?q?=E5=BF=97=E8=AE=B0=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- android/app/build.gradle | 3 +- ios/Podfile.lock | 16 +++ lib/main.dart | 29 ++++- lib/pages/about/index.dart | 10 ++ lib/pages/setting/pages/logs.dart | 201 ++++++++++++++++++++++++++++++ lib/router/app_pages.dart | 3 + lib/services/loggeer.dart | 56 +++++++++ pubspec.lock | 50 +++++++- pubspec.yaml | 3 + 9 files changed, 367 insertions(+), 4 deletions(-) create mode 100644 lib/pages/setting/pages/logs.dart create mode 100644 lib/services/loggeer.dart diff --git a/android/app/build.gradle b/android/app/build.gradle index 0e87d789..3dc4f82a 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -58,11 +58,10 @@ android { applicationId "com.guozhigq.pilipala" // You can update the following values to match your application needs. // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. - // minSdkVersion flutter.minSdkVersion targetSdkVersion flutter.targetSdkVersion versionCode flutterVersionCode.toInteger() versionName flutterVersionName - minSdkVersion 19 + minSdkVersion 21 multiDexEnabled true } diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 5f855b9d..8db59815 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -13,8 +13,13 @@ PODS: - device_info_plus (0.0.1): - Flutter - Flutter (1.0.0) + - flutter_mailer (0.0.1): + - Flutter - flutter_volume_controller (0.0.1): - Flutter + - fluttertoast (0.0.2): + - Flutter + - Toast - FMDB (2.7.5): - FMDB/standard (= 2.7.5) - FMDB/standard (2.7.5) @@ -49,6 +54,7 @@ PODS: - Flutter - system_proxy (0.0.1): - Flutter + - Toast (4.1.0) - url_launcher_ios (0.0.1): - Flutter - volume_controller (0.0.1): @@ -68,7 +74,9 @@ DEPENDENCIES: - connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`) - device_info_plus (from `.symlinks/plugins/device_info_plus/ios`) - Flutter (from `Flutter`) + - flutter_mailer (from `.symlinks/plugins/flutter_mailer/ios`) - flutter_volume_controller (from `.symlinks/plugins/flutter_volume_controller/ios`) + - fluttertoast (from `.symlinks/plugins/fluttertoast/ios`) - gt3_flutter_plugin (from `.symlinks/plugins/gt3_flutter_plugin/ios`) - media_kit_libs_ios_video (from `.symlinks/plugins/media_kit_libs_ios_video/ios`) - media_kit_native_event_loop (from `.symlinks/plugins/media_kit_native_event_loop/ios`) @@ -93,6 +101,7 @@ SPEC REPOS: - FMDB - GT3Captcha-iOS - ReachabilitySwift + - Toast EXTERNAL SOURCES: appscheme: @@ -109,8 +118,12 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/device_info_plus/ios" Flutter: :path: Flutter + flutter_mailer: + :path: ".symlinks/plugins/flutter_mailer/ios" flutter_volume_controller: :path: ".symlinks/plugins/flutter_volume_controller/ios" + fluttertoast: + :path: ".symlinks/plugins/fluttertoast/ios" gt3_flutter_plugin: :path: ".symlinks/plugins/gt3_flutter_plugin/ios" media_kit_libs_ios_video: @@ -156,7 +169,9 @@ SPEC CHECKSUMS: connectivity_plus: 07c49e96d7fc92bc9920617b83238c4d178b446a device_info_plus: c6fb39579d0f423935b0c9ce7ee2f44b71b9fce6 Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 + flutter_mailer: 2ef5a67087bc8c6c4cefd04a178bf1ae2c94cd83 flutter_volume_controller: e4d5832f08008180f76e30faf671ffd5a425e529 + fluttertoast: 31b00dabfa7fb7bacd9e7dbee580d7a2ff4bf265 FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a gt3_flutter_plugin: bfa1f26e9a09dc00401514be5ed437f964cabf23 GT3Captcha-iOS: 5e3b1077834d8a9d6f4d64a447a30af3e14affe6 @@ -173,6 +188,7 @@ SPEC CHECKSUMS: sqflite: 31f7eba61e3074736dff8807a9b41581e4f7f15a status_bar_control: 7c84146799e6a076315cc1550f78ef53aae3e446 system_proxy: bec1a5c5af67dd3e3ebf43979400a8756c04cc44 + Toast: ec33c32b8688982cecc6348adeae667c1b9938da url_launcher_ios: bf5ce03e0e2088bad9cc378ea97fa0ed5b49673b volume_controller: 531ddf792994285c9b17f9d8a7e4dcdd29b3eae9 wakelock_plus: 8b09852c8876491e4b6d179e17dfe2a0b5f60d47 diff --git a/lib/main.dart b/lib/main.dart index 2c7ef1ee..bdf3f6b7 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -21,6 +21,8 @@ import 'package:pilipala/utils/app_scheme.dart'; import 'package:pilipala/utils/data.dart'; import 'package:pilipala/utils/storage.dart'; import 'package:media_kit/media_kit.dart'; // Provides [Player], [Media], [Playlist] etc. +import 'package:catcher_2/catcher_2.dart'; +import './services/loggeer.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); @@ -32,7 +34,32 @@ void main() async { await setupServiceLocator(); Request(); await Request.setCookie(); - runApp(const MyApp()); + + // 异常捕获 logo记录 + final Catcher2Options debugConfig = Catcher2Options( + SilentReportMode(), + [ + FileHandler(await getLogsPath()), + ConsoleHandler( + enableDeviceParameters: false, + enableApplicationParameters: false, + ) + ], + ); + + final Catcher2Options releaseConfig = Catcher2Options( + SilentReportMode(), + [FileHandler(await getLogsPath())], + ); + + Catcher2( + debugConfig: debugConfig, + releaseConfig: releaseConfig, + runAppFunction: () { + runApp(const MyApp()); + }, + ); + // 小白条、导航栏沉浸 SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle( diff --git a/lib/pages/about/index.dart b/lib/pages/about/index.dart index 997adbfe..5df05d31 100644 --- a/lib/pages/about/index.dart +++ b/lib/pages/about/index.dart @@ -133,6 +133,11 @@ class _AboutPageState extends State { title: const Text('赞助'), trailing: Icon(Icons.arrow_forward_ios, size: 16, color: outline), ), + ListTile( + onTap: () => _aboutController.logs(), + title: const Text('错误日志'), + trailing: Icon(Icons.arrow_forward_ios, size: 16, color: outline), + ), ], ), ), @@ -260,4 +265,9 @@ class AboutController extends GetxController { mode: LaunchMode.externalApplication, ); } + + // 日志 + logs() { + Get.toNamed('/logs'); + } } diff --git a/lib/pages/setting/pages/logs.dart b/lib/pages/setting/pages/logs.dart new file mode 100644 index 00000000..0958edb8 --- /dev/null +++ b/lib/pages/setting/pages/logs.dart @@ -0,0 +1,201 @@ +import 'dart:io'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:pilipala/common/widgets/no_data.dart'; +import 'package:url_launcher/url_launcher.dart'; +import '../../../services/loggeer.dart'; + +class LogsPage extends StatefulWidget { + const LogsPage({super.key}); + + @override + State createState() => _LogsPageState(); +} + +class _LogsPageState extends State { + late File logsPath; + late String fileContent; + List logsContent = []; + + @override + void initState() { + getPath(); + super.initState(); + } + + void getPath() async { + logsPath = await getLogsPath(); + fileContent = await logsPath.readAsString(); + logsContent = await parseLogs(fileContent); + setState(() {}); + } + + Future>> parseLogs(String fileContent) async { + const String splitToken = + '======================================================================'; + List contentList = fileContent.split(splitToken).map((item) { + return item + .replaceAll( + '============================== CATCHER 2 LOG ==============================', + 'Pilipala错误日志 \n ********************') + .replaceAll('DEVICE INFO', '设备信息') + .replaceAll('APP INFO', '应用信息') + .replaceAll('ERROR', '错误信息') + .replaceAll('STACK TRACE', '错误堆栈'); + }).toList(); + List> result = []; + for (String i in contentList) { + DateTime? date; + String body = i + .split("\n") + .map((l) { + if (l.startsWith("Crash occurred on")) { + date = DateTime.parse( + l.split("Crash occurred on")[1].trim().split('.')[0], + ); + return ""; + } + return l; + }) + .where((dynamic l) => l.replaceAll("\n", "").trim().isNotEmpty) + .join("\n"); + if (date != null || body != '') { + result.add({'date': date, 'body': body, 'expand': false}); + } + } + return result.reversed.toList(); + } + + void copyLogs() async { + await Clipboard.setData(ClipboardData(text: fileContent)); + if (context.mounted) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('复制成功')), + ); + } + } + + void feedback() { + launchUrl( + Uri.parse('https://github.com/guozhigq/pilipala/issues'), + // 系统自带浏览器打开 + mode: LaunchMode.externalApplication, + ); + } + + void clearLogsHandle() async { + if (await clearLogs()) { + if (context.mounted) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('已清空')), + ); + logsContent = []; + setState(() {}); + } + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + centerTitle: false, + titleSpacing: 0, + title: Text('日志', style: Theme.of(context).textTheme.titleMedium), + actions: [ + PopupMenuButton( + onSelected: (String type) { + // 处理菜单项选择的逻辑 + switch (type) { + case 'copy': + copyLogs(); + break; + case 'feedback': + feedback(); + break; + case 'clear': + clearLogsHandle(); + break; + default: + } + }, + itemBuilder: (BuildContext context) => >[ + const PopupMenuItem( + value: 'copy', + child: Text('复制日志'), + ), + const PopupMenuItem( + value: 'feedback', + child: Text('错误反馈'), + ), + const PopupMenuItem( + value: 'clear', + child: Text('清空日志'), + ), + ], + ), + const SizedBox(width: 6), + ], + ), + body: logsContent.isNotEmpty + ? ListView.builder( + itemCount: logsContent.length, + itemBuilder: (context, index) { + final log = logsContent[index]; + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Row( + children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: Text( + log['date'].toString(), + style: Theme.of(context).textTheme.titleMedium, + ), + ), + TextButton.icon( + onPressed: () async { + await Clipboard.setData( + ClipboardData(text: log['body']), + ); + if (context.mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + '已将 ${log['date'].toString()} 复制至剪贴板', + ), + ), + ); + } + }, + icon: const Icon(Icons.copy_outlined, size: 16), + label: const Text('复制'), + ) + ], + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: Card( + elevation: 1, + clipBehavior: Clip.antiAliasWithSaveLayer, + child: Padding( + padding: const EdgeInsets.all(12.0), + child: SelectableText(log['body']), + ), + ), + ), + const Divider(indent: 12, endIndent: 12), + ], + ); + }, + ) + : const CustomScrollView( + slivers: [ + NoData(), + ], + ), + ); + } +} diff --git a/lib/router/app_pages.dart b/lib/router/app_pages.dart index 64d8da31..23172d3c 100644 --- a/lib/router/app_pages.dart +++ b/lib/router/app_pages.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; +import 'package:pilipala/pages/setting/pages/logs.dart'; import '../pages/about/index.dart'; import '../pages/blacklist/index.dart'; @@ -151,6 +152,8 @@ class Routes { // 用户专栏 CustomGetPage( name: '/memberSeasons', page: () => const MemberSeasonsPage()), + // 日志 + CustomGetPage(name: '/logs', page: () => const LogsPage()), ]; } diff --git a/lib/services/loggeer.dart b/lib/services/loggeer.dart new file mode 100644 index 00000000..5555432c --- /dev/null +++ b/lib/services/loggeer.dart @@ -0,0 +1,56 @@ +// final _loggerFactory = + +import 'dart:io'; + +import 'package:logger/logger.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:path/path.dart' as p; + +final _loggerFactory = PiliLogger(); + +PiliLogger getLogger() { + return _loggerFactory; +} + +class PiliLogger extends Logger { + PiliLogger() : super(); + + @override + void log(Level level, dynamic message, + {Object? error, StackTrace? stackTrace, DateTime? time}) async { + if (level == Level.error) { + String dir = (await getApplicationDocumentsDirectory()).path; + // 创建logo文件 + final String filename = p.join(dir, ".pili_logs"); + // 添加至文件末尾 + await File(filename).writeAsString( + "**${DateTime.now()}** \n $message \n $stackTrace", + mode: FileMode.writeOnlyAppend, + ); + } + super.log(level, "$message", error: error, stackTrace: stackTrace); + } +} + +Future getLogsPath() async { + String dir = (await getApplicationDocumentsDirectory()).path; + final String filename = p.join(dir, ".pili_logs"); + final file = File(filename); + if (!await file.exists()) { + await file.create(); + } + return file; +} + +Future clearLogs() async { + String dir = (await getApplicationDocumentsDirectory()).path; + final String filename = p.join(dir, ".pili_logs"); + final file = File(filename); + try { + await file.writeAsString(''); + } catch (e) { + print('Error clearing file: $e'); + return false; + } + return true; +} diff --git a/pubspec.lock b/pubspec.lock index b8f7aff8..d9b6a01d 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -209,6 +209,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "1.1.0" + catcher_2: + dependency: "direct main" + description: + name: catcher_2 + sha256: ca94d45ffb52bf4b16a425cdff6734ae8443d36d5f06c276f1c2a593120b11ed + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.1.0" characters: dependency: transitive description: @@ -547,6 +555,14 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_mailer: + dependency: transitive + description: + name: flutter_mailer + sha256: "4fffaa35e911ff5ec2e5a4ebbca62c372e99a154eb3bb2c0bf79f09adf6ecf4c" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.1.2" flutter_plugin_android_lifecycle: dependency: transitive description: @@ -589,6 +605,14 @@ packages: description: flutter source: sdk version: "0.0.0" + fluttertoast: + dependency: transitive + description: + name: fluttertoast + sha256: dfdde255317af381bfc1c486ed968d5a43a2ded9c931e87cbecd88767d6a71c1 + url: "https://pub.flutter-io.cn" + source: hosted + version: "8.2.4" font_awesome_flutter: dependency: "direct main" description: @@ -781,6 +805,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "3.0.0" + logger: + dependency: "direct main" + description: + name: logger + sha256: "6bbb9d6f7056729537a4309bda2e74e18e5d9f14302489cc1e93f33b3fe32cac" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.0.2+1" logging: dependency: transitive description: @@ -789,6 +821,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "1.2.0" + mailer: + dependency: transitive + description: + name: mailer + sha256: "57f6dd1496699999a7bfd0aa6be0645384f477f4823e16d4321c40a434346382" + url: "https://pub.flutter-io.cn" + source: hosted + version: "6.0.1" matcher: dependency: transitive description: @@ -951,7 +991,7 @@ packages: source: hosted version: "2.0.1" path: - dependency: transitive + dependency: "direct main" description: name: path sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" @@ -1214,6 +1254,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "0.3.8" + sentry: + dependency: transitive + description: + name: sentry + sha256: "5686ed515bb620dc52b4ae99a6586fe720d443591183cf1f620ec5d1f0eec100" + url: "https://pub.flutter-io.cn" + source: hosted + version: "7.15.0" share_plus: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 5f22a7de..d81781c8 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -134,6 +134,9 @@ dependencies: uuid: ^3.0.7 scrollable_positioned_list: ^0.3.8 nil: ^1.1.1 + catcher_2: ^1.1.0 + logger: ^2.0.2+1 + path: 1.8.3 dev_dependencies: From 127c6734f89a12332d6c8406cb4ae4d2c1d9fe8f Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 28 Jan 2024 23:15:34 +0800 Subject: [PATCH 15/25] =?UTF-8?q?feat:=20=E8=87=AA=E5=8A=A8=E6=89=93?= =?UTF-8?q?=E5=8C=85ipa=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/main.yml | 201 +++++++++++++++++++++++++------------ 1 file changed, 137 insertions(+), 64 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 90fa6311..e7f09f96 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,84 +1,157 @@ -name: build_apk +name: Pilipala Release # action事件触发 on: push: # push tag时触发 tags: - - 'v*.*.*' + - "v*.*.*" # 可以有多个jobs jobs: - build_apk: - # 运行环境 ubuntu-latest window-latest mac-latest - runs-on: ubuntu-latest + android: + # 运行环境 ubuntu-latest window-latest mac-latest + runs-on: ubuntu-latest - # 每个jobs中可以有多个steps - steps: - - name: 代码迁出 - uses: actions/checkout@v3 + # 每个jobs中可以有多个steps + steps: + - name: 代码迁出 + uses: actions/checkout@v3 - - name: 构建Java环境 - uses: actions/setup-java@v3 - with: - distribution: "zulu" - java-version: "17" - token: ${{secrets.GIT_TOKEN}} + - name: 构建Java环境 + uses: actions/setup-java@v3 + with: + distribution: "zulu" + java-version: "17" + token: ${{secrets.GIT_TOKEN}} - - name: 检查缓存 - uses: actions/cache@v2 - id: cache-flutter - with: - path: /root/flutter-sdk # Flutter SDK 的路径 - key: ${{ runner.os }}-flutter-${{ hashFiles('**/pubspec.lock') }} + - name: 检查缓存 + uses: actions/cache@v2 + id: cache-flutter + with: + path: /root/flutter-sdk # Flutter SDK 的路径 + key: ${{ runner.os }}-flutter-${{ hashFiles('**/pubspec.lock') }} - - name: 安装Flutter - if: steps.cache-flutter.outputs.cache-hit != 'true' - uses: subosito/flutter-action@v2 - with: - flutter-version: 3.16.5 - channel: any + - name: 安装Flutter + if: steps.cache-flutter.outputs.cache-hit != 'true' + uses: subosito/flutter-action@v2 + with: + flutter-version: 3.16.5 + channel: any - - name: 下载项目依赖 - run: flutter pub get + - name: 下载项目依赖 + run: flutter pub get - - name: 解码生成 jks - run: echo $KEYSTORE_BASE64 | base64 -di > android/app/vvex.jks - env: - KEYSTORE_BASE64: ${{ secrets.KEYSTORE_BASE64 }} + - name: 解码生成 jks + run: echo $KEYSTORE_BASE64 | base64 -di > android/app/vvex.jks + env: + KEYSTORE_BASE64: ${{ secrets.KEYSTORE_BASE64 }} - - name: flutter build apk - # 对应 android/app/build.gradle signingConfigs中的配置项 - run: flutter build apk --release --split-per-abi - env: - KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }} - KEY_ALIAS: ${{ secrets.KEY_ALIAS }} - KEY_PASSWORD: ${{ secrets.KEY_PASSWORD}} + - name: flutter build apk + run: flutter build apk --release --split-per-abi + env: + KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }} + KEY_ALIAS: ${{ secrets.KEY_ALIAS }} + KEY_PASSWORD: ${{ secrets.KEY_PASSWORD}} - - name: 获取版本号 - id: version - run: echo "version=${GITHUB_REF#refs/tags/v}" >>$GITHUB_OUTPUT + - name: flutter build apk + run: flutter build apk --release + env: + KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }} + KEY_ALIAS: ${{ secrets.KEY_ALIAS }} + KEY_PASSWORD: ${{ secrets.KEY_PASSWORD}} - # - name: 获取当前日期 - # id: date - # run: echo "date=$(date +'%m%d')" >>$GITHUB_OUTPUT + - name: 获取版本号 + id: version + run: echo "version=${GITHUB_REF#refs/tags/v}" >>$GITHUB_OUTPUT - - name: 重命名应用 Pili-arm64-v8a-*.*.*.0101.apk - run: | - # DATE=${{ steps.date.outputs.date }} - for file in build/app/outputs/flutter-apk/app-*-release.apk; do - if [[ $file =~ app-(.*)-release.apk ]]; then - new_file_name="build/app/outputs/flutter-apk/Pili-${BASH_REMATCH[1]}-${{ steps.version.outputs.version }}.apk" - mv "$file" "$new_file_name" - fi - done + # - name: 获取当前日期 + # id: date + # run: echo "date=$(date +'%m%d')" >>$GITHUB_OUTPUT - - name: 构建和发布release - uses: ncipollo/release-action@v1 - with: - # release title - name: v${{ steps.version.outputs.version }} - artifacts: "build/app/outputs/flutter-apk/Pili-*.apk" - bodyFile: "change_log/${{steps.version.outputs.version}}.md" - token: ${{ secrets.GIT_TOKEN }} - allowUpdates: true + - name: 重命名应用 + run: | + # DATE=${{ steps.date.outputs.date }} + for file in build/app/outputs/flutter-apk/app-*.apk; do + if [[ $file =~ app-(.?*)release.apk ]]; then + new_file_name="build/app/outputs/flutter-apk/Pili-${BASH_REMATCH[1]}${{ steps.version.outputs.version }}.apk" + mv "$file" "$new_file_name" + fi + done + + - name: 上传 + uses: actions/upload-artifact@v3 + with: + name: Pilipala-Release + path: | + build/app/outputs/flutter-apk/Pili-*.apk + + iOS: + runs-on: macos-latest + + steps: + - name: 代码迁出 + uses: actions/checkout@v4 + + - name: 安装Flutter + if: steps.cache-flutter.outputs.cache-hit != 'true' + uses: subosito/flutter-action@v2.10.0 + with: + cache: true + flutter-version: 3.10.6 + + - name: flutter build ipa + run: | + flutter build ios --release --no-codesign + ln -sf ./build/ios/iphoneos Payload + zip -r9 app.ipa Payload/runner.app + + - name: 获取版本号 + id: version + run: echo "version=${GITHUB_REF#refs/tags/v}" >>$GITHUB_OUTPUT + + - name: 重命名应用 + run: | + DATE=${{ steps.date.outputs.date }} + for file in app.ipa; do + new_file_name="build/Pili-${{ steps.version.outputs.version }}.ipa" + mv "$file" "$new_file_name" + done + + - name: 上传 + uses: actions/upload-artifact@v3 + with: + if-no-files-found: error + name: Pilipala-Release + path: | + build/Pili-*.ipa + + upload: + runs-on: ubuntu-latest + + needs: + - android + - iOS + steps: + - uses: actions/download-artifact@v3 + with: + name: Pilipala-Release + path: ./Pilipala-Release + + - name: Install dependencies + run: sudo apt-get install tree -y + + - name: Get version + id: version + run: echo "version=${GITHUB_REF#refs/tags/v}" >>$GITHUB_OUTPUT + + - name: Upload Release + uses: ncipollo/release-action@v1 + with: + name: v${{ steps.version.outputs.version }} + token: ${{ secrets.GIT_TOKEN }} + omitBodyDuringUpdate: true + omitNameDuringUpdate: true + omitPrereleaseDuringUpdate: true + allowUpdates: true + artifacts: Pilipala-Release/* From 8169f5739cdc68765c8fc8b00b95d1bf9694afa8 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 28 Jan 2024 23:44:41 +0800 Subject: [PATCH 16/25] =?UTF-8?q?fix:=20=E9=A6=96=E9=A1=B5tabbar=E6=8E=92?= =?UTF-8?q?=E5=BA=8F=E6=97=A0=E6=95=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/home/controller.dart | 11 +++++++---- lib/pages/setting/pages/home_tabbar_set.dart | 11 +++++++++++ 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/lib/pages/home/controller.dart b/lib/pages/home/controller.dart index e6fafba6..685ff5c3 100644 --- a/lib/pages/home/controller.dart +++ b/lib/pages/home/controller.dart @@ -63,13 +63,16 @@ class HomeController extends GetxController with GetTickerProviderStateMixin { } void setTabConfig() async { - defaultTabs = tabsConfig; + defaultTabs = [...tabsConfig]; tabbarSort = settingStorage.get(SettingBoxKey.tabbarSort, defaultValue: ['live', 'rcmd', 'hot', 'bangumi']); + defaultTabs.retainWhere( + (item) => tabbarSort.contains((item['type'] as TabType).id)); + defaultTabs.sort((a, b) => tabbarSort + .indexOf((a['type'] as TabType).id) + .compareTo(tabbarSort.indexOf((b['type'] as TabType).id))); - tabs.value = defaultTabs - .where((i) => tabbarSort.contains((i['type'] as TabType).id)) - .toList(); + tabs.value = defaultTabs; if (tabbarSort.contains(TabType.rcmd.id)) { initialIndex.value = tabbarSort.indexOf(TabType.rcmd.id); diff --git a/lib/pages/setting/pages/home_tabbar_set.dart b/lib/pages/setting/pages/home_tabbar_set.dart index 445ca4f5..4cb3944c 100644 --- a/lib/pages/setting/pages/home_tabbar_set.dart +++ b/lib/pages/setting/pages/home_tabbar_set.dart @@ -22,6 +22,17 @@ class _TabbarSetPageState extends State { defaultTabs = tabsConfig; tabbarSort = settingStorage.get(SettingBoxKey.tabbarSort, defaultValue: ['live', 'rcmd', 'hot', 'bangumi']); + // 对 tabData 进行排序 + defaultTabs.sort((a, b) { + int indexA = tabbarSort.indexOf((a['type'] as TabType).id); + int indexB = tabbarSort.indexOf((b['type'] as TabType).id); + + // 如果类型在 sortOrder 中不存在,则放在末尾 + if (indexA == -1) indexA = tabbarSort.length; + if (indexB == -1) indexB = tabbarSort.length; + + return indexA.compareTo(indexB); + }); } void saveEdit() { From 699361e04c6e6df69a8090f2db31353d1d784b5f Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 30 Jan 2024 23:11:54 +0800 Subject: [PATCH 17/25] fix: aid --- lib/http/reply.dart | 4 ++-- lib/http/video.dart | 2 +- lib/pages/bangumi/introduction/controller.dart | 2 +- lib/pages/dynamics/detail/view.dart | 4 ++-- lib/pages/html/view.dart | 3 ++- lib/pages/video/detail/introduction/controller.dart | 2 +- lib/pages/video/detail/reply/controller.dart | 6 +++--- lib/pages/video/detail/reply/view.dart | 7 ++++--- lib/pages/video/detail/reply/widgets/reply_item.dart | 2 +- lib/pages/video/detail/reply_new/view.dart | 2 +- 10 files changed, 18 insertions(+), 16 deletions(-) diff --git a/lib/http/reply.dart b/lib/http/reply.dart index fab433fc..4c165b3b 100644 --- a/lib/http/reply.dart +++ b/lib/http/reply.dart @@ -4,7 +4,7 @@ import 'init.dart'; class ReplyHttp { static Future replyList({ - required int oid, + required dynamic oid, required int pageNum, required int type, int? ps, @@ -76,7 +76,7 @@ class ReplyHttp { // 评论点赞 static Future likeReply({ required int type, - required int oid, + required dynamic oid, required int rpid, required int action, }) async { diff --git a/lib/http/video.dart b/lib/http/video.dart index 923e93a2..7929be63 100644 --- a/lib/http/video.dart +++ b/lib/http/video.dart @@ -331,7 +331,7 @@ class VideoHttp { // plat num 发送平台标识 非必要 1:web端 2:安卓客户端 3:ios客户端 4:wp客户端 static Future replyAdd({ required ReplyType type, - required int oid, + required dynamic oid, required String message, int? root, int? parent, diff --git a/lib/pages/bangumi/introduction/controller.dart b/lib/pages/bangumi/introduction/controller.dart index 13dd50c0..695bca3a 100644 --- a/lib/pages/bangumi/introduction/controller.dart +++ b/lib/pages/bangumi/introduction/controller.dart @@ -266,7 +266,7 @@ class BangumiIntroController extends GetxController { /// 未渲染回复组件时可能异常 VideoReplyController videoReplyCtr = Get.find(tag: Get.arguments['heroTag']); - videoReplyCtr.aid = aid; + videoReplyCtr.oid = bvid; videoReplyCtr.queryReplyList(type: 'init'); } catch (_) {} } diff --git a/lib/pages/dynamics/detail/view.dart b/lib/pages/dynamics/detail/view.dart index 840cd33f..9489346f 100644 --- a/lib/pages/dynamics/detail/view.dart +++ b/lib/pages/dynamics/detail/view.dart @@ -385,8 +385,8 @@ class _DynamicDetailPageState extends State isScrollControlled: true, builder: (BuildContext context) { return VideoReplyNewDialog( - oid: _dynamicDetailController.oid ?? - IdUtils.bv2av(Get.parameters['bvid']!), + oid: _dynamicDetailController.oid?.toString() ?? + Get.parameters['bvid'], root: 0, parent: 0, replyType: ReplyType.values[replyType], diff --git a/lib/pages/html/view.dart b/lib/pages/html/view.dart index 9f0c865c..7625306f 100644 --- a/lib/pages/html/view.dart +++ b/lib/pages/html/view.dart @@ -13,6 +13,7 @@ import 'package:pilipala/pages/video/detail/reply/widgets/reply_item.dart'; import 'package:pilipala/pages/video/detail/reply_new/index.dart'; import 'package:pilipala/pages/video/detail/reply_reply/index.dart'; import 'package:pilipala/utils/feed_back.dart'; +import 'package:pilipala/utils/id_utils.dart'; import 'controller.dart'; @@ -427,7 +428,7 @@ class _HtmlRenderPageState extends State isScrollControlled: true, builder: (BuildContext context) { return VideoReplyNewDialog( - oid: _htmlRenderCtr.oid.value, + oid: IdUtils.av2bv(_htmlRenderCtr.oid.value), root: 0, parent: 0, replyType: ReplyType.values[type], diff --git a/lib/pages/video/detail/introduction/controller.dart b/lib/pages/video/detail/introduction/controller.dart index 1230a94b..73165117 100644 --- a/lib/pages/video/detail/introduction/controller.dart +++ b/lib/pages/video/detail/introduction/controller.dart @@ -478,7 +478,7 @@ class VideoIntroController extends GetxController { /// 未渲染回复组件时可能异常 final VideoReplyController videoReplyCtr = Get.find(tag: heroTag); - videoReplyCtr.aid = aid; + videoReplyCtr.oid = bvid; videoReplyCtr.queryReplyList(type: 'init'); } catch (_) {} this.bvid = bvid; diff --git a/lib/pages/video/detail/reply/controller.dart b/lib/pages/video/detail/reply/controller.dart index 9e05542b..03c5fb0e 100644 --- a/lib/pages/video/detail/reply/controller.dart +++ b/lib/pages/video/detail/reply/controller.dart @@ -11,13 +11,13 @@ import 'package:pilipala/utils/storage.dart'; class VideoReplyController extends GetxController { VideoReplyController( - this.aid, + this.oid, this.rpid, this.replyLevel, ); final ScrollController scrollController = ScrollController(); // 视频aid 请求时使用的oid - int? aid; + String? oid; // 层级 2为楼中楼 String? replyLevel; // rpid 请求楼中楼回复 @@ -57,7 +57,7 @@ class VideoReplyController extends GetxController { return; } final res = await ReplyHttp.replyList( - oid: aid!, + oid: oid!, pageNum: currentPage + 1, ps: ps, type: ReplyType.video.index, diff --git a/lib/pages/video/detail/reply/view.dart b/lib/pages/video/detail/reply/view.dart index b2c67b1e..474615fc 100644 --- a/lib/pages/video/detail/reply/view.dart +++ b/lib/pages/video/detail/reply/view.dart @@ -40,6 +40,7 @@ class _VideoReplyPanelState extends State bool _isFabVisible = true; String replyLevel = '1'; late String heroTag; + late String oid; // 添加页面缓存 @override @@ -48,7 +49,7 @@ class _VideoReplyPanelState extends State @override void initState() { super.initState(); - int oid = widget.bvid != null ? IdUtils.bv2av(widget.bvid!) : 0; + oid = widget.bvid != null ? widget.bvid! : '0'; heroTag = Get.arguments['heroTag']; replyLevel = widget.replyLevel ?? '1'; if (replyLevel == '2') { @@ -297,8 +298,8 @@ class _VideoReplyPanelState extends State isScrollControlled: true, builder: (BuildContext context) { return VideoReplyNewDialog( - oid: _videoReplyController.aid ?? - IdUtils.bv2av(Get.parameters['bvid']!), + oid: + _videoReplyController.oid ?? Get.parameters['bvid'], root: 0, parent: 0, replyType: ReplyType.video, diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index 48518bee..3352cdc3 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -353,7 +353,7 @@ class ReplyItem extends StatelessWidget { isScrollControlled: true, builder: (builder) { return VideoReplyNewDialog( - oid: replyItem!.oid, + oid: IdUtils.av2bv(replyItem!.oid!), root: replyItem!.rpid, parent: replyItem!.rpid, replyType: replyType, diff --git a/lib/pages/video/detail/reply_new/view.dart b/lib/pages/video/detail/reply_new/view.dart index 01c95adc..61a78869 100644 --- a/lib/pages/video/detail/reply_new/view.dart +++ b/lib/pages/video/detail/reply_new/view.dart @@ -8,7 +8,7 @@ import 'package:pilipala/models/video/reply/item.dart'; import 'package:pilipala/utils/feed_back.dart'; class VideoReplyNewDialog extends StatefulWidget { - final int? oid; + final String? oid; final int? root; final int? parent; final ReplyType? replyType; From 163bb3c8da88ab843c8afef5526bad2d105a74b6 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 30 Jan 2024 23:17:00 +0800 Subject: [PATCH 18/25] =?UTF-8?q?v1.0.18=20=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- change_log/1.0.18.0130.md | 16 ++++++++++++++++ pubspec.yaml | 18 ++++++++---------- 2 files changed, 24 insertions(+), 10 deletions(-) create mode 100644 change_log/1.0.18.0130.md diff --git a/change_log/1.0.18.0130.md b/change_log/1.0.18.0130.md new file mode 100644 index 00000000..2f0b80ca --- /dev/null +++ b/change_log/1.0.18.0130.md @@ -0,0 +1,16 @@ +## 1.0.18 + + +### 功能 + + +### 修复 + + +### 优化 + + + + +更多更新日志可在Github上查看 +问题反馈、功能建议请查看「关于」页面。 diff --git a/pubspec.yaml b/pubspec.yaml index d81781c8..81e6ca9f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # In Windows, build-name is used as the major, minor, and patch parts # of the product and file versions while build-number is used as the build suffix. -version: 1.0.17+1017 +version: 1.0.18+1018 environment: sdk: ">=2.19.6 <3.0.0" @@ -51,7 +51,7 @@ dependencies: cached_network_image: ^3.3.0 extended_image: ^8.2.0 saver_gallery: ^3.0.1 - + # 存储 path_provider: ^2.1.1 hive: ^2.2.3 @@ -85,14 +85,14 @@ dependencies: encrypt: ^5.0.3 # 视频播放器 - media_kit: ^1.1.10 # Primary package. - media_kit_video: ^1.2.4 # For video rendering. + media_kit: ^1.1.10 # Primary package. + media_kit_video: ^1.2.4 # For video rendering. media_kit_libs_video: ^1.0.4 # 媒体通知 audio_service: ^0.18.12 audio_session: ^0.1.16 - + # 音量、亮度、屏幕控制 flutter_volume_controller: ^1.3.1 screen_brightness: ^0.2.2+1 @@ -103,7 +103,7 @@ dependencies: auto_orientation: ^2.3.1 protobuf: ^3.0.0 animations: ^2.0.8 - + # 获取appx信息 package_info_plus: ^4.1.0 url_launcher: ^6.1.14 @@ -116,7 +116,7 @@ dependencies: appscheme: ^1.0.8 # 弹幕 ns_danmaku: - git: + git: url: https://github.com/guozhigq/flutter_ns_danmaku.git ref: master # 状态栏图标控制 @@ -138,7 +138,6 @@ dependencies: logger: ^2.0.2+1 path: 1.8.3 - dev_dependencies: flutter_test: sdk: flutter @@ -207,7 +206,6 @@ flutter: # - family: HarmonyOS # fonts: # - asset: assets/fonts/HarmonyOS_Sans_SC_Regular.ttf - - + # For details regarding fonts from package dependencies, # see https://flutter.dev/custom-fonts/#from-packages From 7157f892457ae35f46058b2d849a4f800f1506e7 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 30 Jan 2024 23:23:12 +0800 Subject: [PATCH 19/25] Update main.yml --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index e7f09f96..78230645 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -98,7 +98,7 @@ jobs: uses: subosito/flutter-action@v2.10.0 with: cache: true - flutter-version: 3.10.6 + flutter-version: 3.16.5 - name: flutter build ipa run: | From 9e93b50860ac997d789fe963dc4113fec5168a71 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 31 Jan 2024 22:33:04 +0800 Subject: [PATCH 20/25] =?UTF-8?q?mod:=20=E8=BF=98=E5=8E=9Faid?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/reply.dart | 4 ++-- lib/http/video.dart | 2 +- lib/pages/bangumi/introduction/controller.dart | 2 +- lib/pages/dynamics/detail/view.dart | 4 ++-- lib/pages/html/view.dart | 3 +-- lib/pages/video/detail/introduction/controller.dart | 2 +- lib/pages/video/detail/reply/controller.dart | 6 +++--- lib/pages/video/detail/reply/view.dart | 7 +++---- lib/pages/video/detail/reply/widgets/reply_item.dart | 2 +- lib/pages/video/detail/reply_new/view.dart | 2 +- 10 files changed, 16 insertions(+), 18 deletions(-) diff --git a/lib/http/reply.dart b/lib/http/reply.dart index 4c165b3b..fab433fc 100644 --- a/lib/http/reply.dart +++ b/lib/http/reply.dart @@ -4,7 +4,7 @@ import 'init.dart'; class ReplyHttp { static Future replyList({ - required dynamic oid, + required int oid, required int pageNum, required int type, int? ps, @@ -76,7 +76,7 @@ class ReplyHttp { // 评论点赞 static Future likeReply({ required int type, - required dynamic oid, + required int oid, required int rpid, required int action, }) async { diff --git a/lib/http/video.dart b/lib/http/video.dart index 7929be63..923e93a2 100644 --- a/lib/http/video.dart +++ b/lib/http/video.dart @@ -331,7 +331,7 @@ class VideoHttp { // plat num 发送平台标识 非必要 1:web端 2:安卓客户端 3:ios客户端 4:wp客户端 static Future replyAdd({ required ReplyType type, - required dynamic oid, + required int oid, required String message, int? root, int? parent, diff --git a/lib/pages/bangumi/introduction/controller.dart b/lib/pages/bangumi/introduction/controller.dart index 695bca3a..13dd50c0 100644 --- a/lib/pages/bangumi/introduction/controller.dart +++ b/lib/pages/bangumi/introduction/controller.dart @@ -266,7 +266,7 @@ class BangumiIntroController extends GetxController { /// 未渲染回复组件时可能异常 VideoReplyController videoReplyCtr = Get.find(tag: Get.arguments['heroTag']); - videoReplyCtr.oid = bvid; + videoReplyCtr.aid = aid; videoReplyCtr.queryReplyList(type: 'init'); } catch (_) {} } diff --git a/lib/pages/dynamics/detail/view.dart b/lib/pages/dynamics/detail/view.dart index 9489346f..840cd33f 100644 --- a/lib/pages/dynamics/detail/view.dart +++ b/lib/pages/dynamics/detail/view.dart @@ -385,8 +385,8 @@ class _DynamicDetailPageState extends State isScrollControlled: true, builder: (BuildContext context) { return VideoReplyNewDialog( - oid: _dynamicDetailController.oid?.toString() ?? - Get.parameters['bvid'], + oid: _dynamicDetailController.oid ?? + IdUtils.bv2av(Get.parameters['bvid']!), root: 0, parent: 0, replyType: ReplyType.values[replyType], diff --git a/lib/pages/html/view.dart b/lib/pages/html/view.dart index 7625306f..9f0c865c 100644 --- a/lib/pages/html/view.dart +++ b/lib/pages/html/view.dart @@ -13,7 +13,6 @@ import 'package:pilipala/pages/video/detail/reply/widgets/reply_item.dart'; import 'package:pilipala/pages/video/detail/reply_new/index.dart'; import 'package:pilipala/pages/video/detail/reply_reply/index.dart'; import 'package:pilipala/utils/feed_back.dart'; -import 'package:pilipala/utils/id_utils.dart'; import 'controller.dart'; @@ -428,7 +427,7 @@ class _HtmlRenderPageState extends State isScrollControlled: true, builder: (BuildContext context) { return VideoReplyNewDialog( - oid: IdUtils.av2bv(_htmlRenderCtr.oid.value), + oid: _htmlRenderCtr.oid.value, root: 0, parent: 0, replyType: ReplyType.values[type], diff --git a/lib/pages/video/detail/introduction/controller.dart b/lib/pages/video/detail/introduction/controller.dart index 73165117..1230a94b 100644 --- a/lib/pages/video/detail/introduction/controller.dart +++ b/lib/pages/video/detail/introduction/controller.dart @@ -478,7 +478,7 @@ class VideoIntroController extends GetxController { /// 未渲染回复组件时可能异常 final VideoReplyController videoReplyCtr = Get.find(tag: heroTag); - videoReplyCtr.oid = bvid; + videoReplyCtr.aid = aid; videoReplyCtr.queryReplyList(type: 'init'); } catch (_) {} this.bvid = bvid; diff --git a/lib/pages/video/detail/reply/controller.dart b/lib/pages/video/detail/reply/controller.dart index 03c5fb0e..9e05542b 100644 --- a/lib/pages/video/detail/reply/controller.dart +++ b/lib/pages/video/detail/reply/controller.dart @@ -11,13 +11,13 @@ import 'package:pilipala/utils/storage.dart'; class VideoReplyController extends GetxController { VideoReplyController( - this.oid, + this.aid, this.rpid, this.replyLevel, ); final ScrollController scrollController = ScrollController(); // 视频aid 请求时使用的oid - String? oid; + int? aid; // 层级 2为楼中楼 String? replyLevel; // rpid 请求楼中楼回复 @@ -57,7 +57,7 @@ class VideoReplyController extends GetxController { return; } final res = await ReplyHttp.replyList( - oid: oid!, + oid: aid!, pageNum: currentPage + 1, ps: ps, type: ReplyType.video.index, diff --git a/lib/pages/video/detail/reply/view.dart b/lib/pages/video/detail/reply/view.dart index 474615fc..b2c67b1e 100644 --- a/lib/pages/video/detail/reply/view.dart +++ b/lib/pages/video/detail/reply/view.dart @@ -40,7 +40,6 @@ class _VideoReplyPanelState extends State bool _isFabVisible = true; String replyLevel = '1'; late String heroTag; - late String oid; // 添加页面缓存 @override @@ -49,7 +48,7 @@ class _VideoReplyPanelState extends State @override void initState() { super.initState(); - oid = widget.bvid != null ? widget.bvid! : '0'; + int oid = widget.bvid != null ? IdUtils.bv2av(widget.bvid!) : 0; heroTag = Get.arguments['heroTag']; replyLevel = widget.replyLevel ?? '1'; if (replyLevel == '2') { @@ -298,8 +297,8 @@ class _VideoReplyPanelState extends State isScrollControlled: true, builder: (BuildContext context) { return VideoReplyNewDialog( - oid: - _videoReplyController.oid ?? Get.parameters['bvid'], + oid: _videoReplyController.aid ?? + IdUtils.bv2av(Get.parameters['bvid']!), root: 0, parent: 0, replyType: ReplyType.video, diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index 3352cdc3..48518bee 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -353,7 +353,7 @@ class ReplyItem extends StatelessWidget { isScrollControlled: true, builder: (builder) { return VideoReplyNewDialog( - oid: IdUtils.av2bv(replyItem!.oid!), + oid: replyItem!.oid, root: replyItem!.rpid, parent: replyItem!.rpid, replyType: replyType, diff --git a/lib/pages/video/detail/reply_new/view.dart b/lib/pages/video/detail/reply_new/view.dart index 61a78869..01c95adc 100644 --- a/lib/pages/video/detail/reply_new/view.dart +++ b/lib/pages/video/detail/reply_new/view.dart @@ -8,7 +8,7 @@ import 'package:pilipala/models/video/reply/item.dart'; import 'package:pilipala/utils/feed_back.dart'; class VideoReplyNewDialog extends StatefulWidget { - final String? oid; + final int? oid; final int? root; final int? parent; final ReplyType? replyType; From d703e38c3fd9599f08cd37f31a22f0e8fcd5afb3 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 31 Jan 2024 22:43:40 +0800 Subject: [PATCH 21/25] =?UTF-8?q?fix:=20avbv=E8=BD=AC=E6=8D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/utils/id_utils.dart | 79 +++++++++++++++++++++-------------------- 1 file changed, 40 insertions(+), 39 deletions(-) diff --git a/lib/utils/id_utils.dart b/lib/utils/id_utils.dart index a68bfb40..d0451b17 100644 --- a/lib/utils/id_utils.dart +++ b/lib/utils/id_utils.dart @@ -1,51 +1,52 @@ -// ignore_for_file: constant_identifier_names +// ignore_for_file: constant_identifier_names, non_constant_identifier_names import 'dart:convert'; -import 'dart:math'; - -import 'package:flutter/material.dart'; class IdUtils { - static const String TABLE = - 'fZodR9XQDSUm21yCkr6zBqiveYah8bt4xsWpHnJE7jL5VG3guMTKNPAwcF'; - static const List S = [11, 10, 3, 8, 4, 6]; // 位置编码表 - static const int XOR = 177451812; // 固定异或值 - static const int ADD = 8728348608; // 固定加法值 - static const List r = [ - 'B', - 'V', - '1', - '', - '', - '4', - '', - '1', - '', - '7', - '', - '' - ]; + static final XOR_CODE = BigInt.parse('23442827791579'); + static final MASK_CODE = BigInt.parse('2251799813685247'); + static final MAX_AID = BigInt.one << (BigInt.from(51)).toInt(); + static final BASE = BigInt.from(58); + + static const data = + 'FcwAPNKTMug3GV5Lj7EJnHpWsx4tb8haYeviqBz6rkCy12mUSDQX9RdoZf'; /// av转bv - static String av2bv(int av) { - int x_ = (av ^ XOR) + ADD; - List newR = []; - newR.addAll(r); - for (int i = 0; i < S.length; i++) { - newR[S[i]] = - TABLE.characters.elementAt((x_ / pow(58, i).toInt() % 58).toInt()); + static String av2bv(int aid) { + List bytes = List.filled(12, '0', growable: false); + int bvIndex = bytes.length - 1; + BigInt tmp = (MAX_AID | BigInt.from(aid)) ^ XOR_CODE; + while (tmp > BigInt.zero) { + bytes[bvIndex] = data[(tmp % BASE).toInt()]; + tmp = tmp ~/ BASE; + bvIndex -= 1; } - return newR.join(); + final tmpValue = bytes[3]; + bytes[3] = bytes[9]; + bytes[9] = tmpValue; + + final tmpValue2 = bytes[4]; + bytes[4] = bytes[7]; + bytes[7] = tmpValue2; + + return bytes.join(); } - /// bv转bv - static int bv2av(String bv) { - int r = 0; - for (int i = 0; i < S.length; i++) { - r += (TABLE.indexOf(bv.characters.elementAt(S[i])).toInt()) * - pow(58, i).toInt(); - } - return (r - ADD) ^ XOR; + /// bv转av + static int bv2av(String bvid) { + List bvidArr = bvid.split(''); + final tmpValue = bvidArr[3]; + bvidArr[3] = bvidArr[9]; + bvidArr[9] = tmpValue; + + final tmpValue2 = bvidArr[4]; + bvidArr[4] = bvidArr[7]; + bvidArr[7] = tmpValue2; + + bvidArr.removeRange(0, 3); + BigInt tmp = bvidArr.fold(BigInt.zero, + (pre, bvidChar) => pre * BASE + BigInt.from(data.indexOf(bvidChar))); + return ((tmp & MASK_CODE) ^ XOR_CODE).toInt(); } // 匹配 From 8a2c0233430a2c5e31498b4fa14943131df85fd4 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 31 Jan 2024 23:03:45 +0800 Subject: [PATCH 22/25] fix: magType value --- .../whisper_detail/widget/chat_item.dart | 56 +++++++++++-------- 1 file changed, 33 insertions(+), 23 deletions(-) diff --git a/lib/pages/whisper_detail/widget/chat_item.dart b/lib/pages/whisper_detail/widget/chat_item.dart index 9f0e191b..41df23ab 100644 --- a/lib/pages/whisper_detail/widget/chat_item.dart +++ b/lib/pages/whisper_detail/widget/chat_item.dart @@ -5,7 +5,6 @@ import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; -import 'package:get/get_core/src/get_main.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; import 'package:pilipala/utils/utils.dart'; import 'package:pilipala/utils/storage.dart'; @@ -53,12 +52,13 @@ class ChatItem extends StatelessWidget { Widget build(BuildContext context) { bool isOwner = item.senderUid == GStrorage.userInfo.get('userInfoCache').mid; - bool isPic = item.msgType == MsgType.pic; // 图片 - bool isText = item.msgType == MsgType.text; // 文本 + + bool isPic = item.msgType == MsgType.pic.value; // 图片 + bool isText = item.msgType == MsgType.text.value; // 文本 // bool isArchive = item.msgType == 11; // 投稿 // bool isArticle = item.msgType == 12; // 专栏 - bool isRevoke = item.msgType == MsgType.revoke; // 撤回消息 - bool isShareV2 = item.msgType == MsgType.share_v2; + bool isRevoke = item.msgType == MsgType.revoke.value; // 撤回消息 + bool isShareV2 = item.msgType == MsgType.share_v2.value; bool isSystem = item.msgType == 18 || item.msgType == 10 || item.msgType == 13; dynamic content = item.content ?? ''; @@ -72,7 +72,7 @@ class ChatItem extends StatelessWidget { var text = content['content']; if (e_infos != null) { final List children = []; - Map emojiMap = {}; + Map emojiMap = {}; for (var e in e_infos!) { emojiMap[e['text']] = e['url']; } @@ -83,18 +83,22 @@ class ChatItem extends StatelessWidget { if (emojiMap.containsKey(emojiKey)) { children.add(WidgetSpan( child: NetworkImgLayer( - width: 18, height: 18, - src: emojiMap[emojiKey]!,), + width: 18, + height: 18, + src: emojiMap[emojiKey]!, + ), )); } return ''; }, onNonMatch: (String text) { - children.add(TextSpan(text: text, style: TextStyle( - color: textColor(context), - letterSpacing: 0.6, - height: 1.5, - ))); + children.add(TextSpan( + text: text, + style: TextStyle( + color: textColor(context), + letterSpacing: 0.6, + height: 1.5, + ))); return ''; }, ); @@ -123,11 +127,13 @@ class ChatItem extends StatelessWidget { return SystemNotice2(item: item); case MsgType.notify_text: return Text( - jsonDecode(content['content']).map((m) => m['text'] as String).join("\n"), + jsonDecode(content['content']) + .map((m) => m['text'] as String) + .join("\n"), style: TextStyle( - letterSpacing: 0.6, - height: 5, - color: Theme.of(context).colorScheme.outline.withOpacity(0.8) + letterSpacing: 0.6, + height: 5, + color: Theme.of(context).colorScheme.outline.withOpacity(0.8), ), ); case MsgType.text: @@ -166,9 +172,11 @@ class ChatItem extends StatelessWidget { Text( content['title'], style: TextStyle( - letterSpacing: 0.6, - height: 1.5, - color: textColor(context), fontWeight: FontWeight.bold), + letterSpacing: 0.6, + height: 1.5, + color: textColor(context), + fontWeight: FontWeight.bold, + ), ), const SizedBox(height: 1), Text( @@ -186,9 +194,11 @@ class ChatItem extends StatelessWidget { return Text( content['content'] ?? content.toString(), style: TextStyle( - letterSpacing: 0.6, - height: 1.5, - color: textColor(context), fontWeight: FontWeight.bold), + letterSpacing: 0.6, + height: 1.5, + color: textColor(context), + fontWeight: FontWeight.bold, + ), ); } } From ea8af288289a3d45203e3eec4e32a1eec8f86600 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 31 Jan 2024 23:11:03 +0800 Subject: [PATCH 23/25] =?UTF-8?q?fix:=20=E4=B8=93=E6=A0=8F=E5=B0=81?= =?UTF-8?q?=E9=9D=A2=E5=9B=BE=E5=B0=BA=E5=AF=B8=E5=BC=82=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/search_panel/widgets/article_panel.dart | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/pages/search_panel/widgets/article_panel.dart b/lib/pages/search_panel/widgets/article_panel.dart index 35e39640..c7074229 100644 --- a/lib/pages/search_panel/widgets/article_panel.dart +++ b/lib/pages/search_panel/widgets/article_panel.dart @@ -25,16 +25,17 @@ Widget searchArticlePanel(BuildContext context, ctr, list) { padding: const EdgeInsets.fromLTRB( StyleString.safeSpace, 5, StyleString.safeSpace, 5), child: LayoutBuilder(builder: (context, boxConstraints) { - double width = (boxConstraints.maxWidth - - StyleString.cardSpace * - 6 / - MediaQuery.textScalerOf(context).scale(2.0)); + final double width = (boxConstraints.maxWidth - + StyleString.cardSpace * + 6 / + MediaQuery.textScalerOf(context).scale(1.0)) / + 2; return Container( constraints: const BoxConstraints(minHeight: 88), height: width / StyleString.aspectRatio, child: Row( crossAxisAlignment: CrossAxisAlignment.start, - children: [ + children: [ if (list[index].imageUrls != null && list[index].imageUrls.isNotEmpty) AspectRatio( From 89a43b1285b66a7caacdc46bcfc2217f820cf8ce Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 31 Jan 2024 23:28:51 +0800 Subject: [PATCH 24/25] =?UTF-8?q?v1.0.19=20=E6=9B=B4=E6=96=B0=E6=97=A5?= =?UTF-8?q?=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- change_log/1.0.19.0131.md | 15 +++++++++++++++ pubspec.yaml | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 change_log/1.0.19.0131.md diff --git a/change_log/1.0.19.0131.md b/change_log/1.0.19.0131.md new file mode 100644 index 00000000..1fd3071b --- /dev/null +++ b/change_log/1.0.19.0131.md @@ -0,0 +1,15 @@ +## 1.0.19 + + +### 修复 ++ 视频404、评论加载错误 ++ bvav转换 + +### 优化 ++ 视频详情页内存占用 + + + + +更多更新日志可在Github上查看 +问题反馈、功能建议请查看「关于」页面。 diff --git a/pubspec.yaml b/pubspec.yaml index 81e6ca9f..f4b8fe61 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # In Windows, build-name is used as the major, minor, and patch parts # of the product and file versions while build-number is used as the build suffix. -version: 1.0.18+1018 +version: 1.0.19+1019 environment: sdk: ">=2.19.6 <3.0.0" From e24ccc16fae4cb252c65c8188a4f096a94cc44d2 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 1 Feb 2024 00:32:52 +0800 Subject: [PATCH 25/25] =?UTF-8?q?mod:=20av2bv=E6=96=B9=E6=B3=95=E4=BF=AE?= =?UTF-8?q?=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/utils/id_utils.dart | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/lib/utils/id_utils.dart b/lib/utils/id_utils.dart index d0451b17..906f6348 100644 --- a/lib/utils/id_utils.dart +++ b/lib/utils/id_utils.dart @@ -13,7 +13,20 @@ class IdUtils { /// av转bv static String av2bv(int aid) { - List bytes = List.filled(12, '0', growable: false); + List bytes = [ + 'B', + 'V', + '1', + '0', + '0', + '0', + '0', + '0', + '0', + '0', + '0', + '0' + ]; int bvIndex = bytes.length - 1; BigInt tmp = (MAX_AID | BigInt.from(aid)) ^ XOR_CODE; while (tmp > BigInt.zero) { @@ -21,13 +34,13 @@ class IdUtils { tmp = tmp ~/ BASE; bvIndex -= 1; } - final tmpValue = bytes[3]; + String tmpSwap = bytes[3]; bytes[3] = bytes[9]; - bytes[9] = tmpValue; + bytes[9] = tmpSwap; - final tmpValue2 = bytes[4]; + tmpSwap = bytes[4]; bytes[4] = bytes[7]; - bytes[7] = tmpValue2; + bytes[7] = tmpSwap; return bytes.join(); }