From 65bb5d7e3d762f8700101e4546f6d3058ec66b34 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 20 Nov 2024 00:02:12 +0800 Subject: [PATCH 1/6] refactor: buvidActivate call --- lib/http/common.dart | 44 ++++++++++++++++++++++++++++++++++++++++++++ lib/http/init.dart | 34 ---------------------------------- lib/main.dart | 2 ++ 3 files changed, 46 insertions(+), 34 deletions(-) diff --git a/lib/http/common.dart b/lib/http/common.dart index 2f5f0e84..9644d142 100644 --- a/lib/http/common.dart +++ b/lib/http/common.dart @@ -1,8 +1,14 @@ +import 'dart:convert'; +import 'dart:math'; +import 'package:dio/dio.dart'; +import 'package:flutter/material.dart'; import 'package:pilipala/models/sponsor_block/segment.dart'; import 'index.dart'; class CommonHttp { + static final RegExp spmPrefixExp = + RegExp(r''); static Future unReadDynamic() async { var res = await Request().get(Api.getUnreadDynamic, data: {'alltype_offset': 0, 'video_offset': '', 'article_offset': 0}); @@ -43,4 +49,42 @@ class CommonHttp { }; } } + + static Future buvidActivate() async { + try { + // 获取 HTML 数据 + var html = await Request().get(Api.dynamicSpmPrefix); + + // 提取 spmPrefix + String spmPrefix = spmPrefixExp.firstMatch(html.data)?.group(1) ?? ''; + + // 生成随机 PNG 结束部分 + Random rand = Random(); + String randPngEnd = base64.encode( + List.generate(32, (_) => rand.nextInt(256)) + ..addAll(List.filled(4, 0)) + ..addAll([73, 69, 78, 68]) + ..addAll(List.generate(4, (_) => rand.nextInt(256))), + ); + + // 构建 JSON 数据 + String jsonData = json.encode({ + '3064': 1, + '39c8': '$spmPrefix.fp.risk', + '3c43': { + 'adca': 'Linux', + 'bfe9': randPngEnd.substring(randPngEnd.length - 50), + }, + }); + + // 发送 POST 请求 + await Request().post( + Api.activateBuvidApi, + data: {'payload': jsonData}, + options: Options(contentType: 'application/json'), + ); + } catch (err) { + debugPrint('buvidActivate error: $err'); + } + } } diff --git a/lib/http/init.dart b/lib/http/init.dart index 8a11034c..03de43b7 100644 --- a/lib/http/init.dart +++ b/lib/http/init.dart @@ -1,9 +1,7 @@ // ignore_for_file: avoid_print import 'dart:async'; -import 'dart:convert'; import 'dart:developer'; import 'dart:io'; -import 'dart:math' show Random; import 'package:cookie_jar/cookie_jar.dart'; import 'package:dio/dio.dart'; import 'package:dio/io.dart'; @@ -13,7 +11,6 @@ import 'package:pilipala/models/user/info.dart'; import 'package:pilipala/utils/id_utils.dart'; import '../utils/storage.dart'; import '../utils/utils.dart'; -import 'api.dart'; import 'constants.dart'; import 'interceptor.dart'; @@ -27,8 +24,6 @@ class Request { late bool enableSystemProxy; late String systemProxyHost; late String systemProxyPort; - static final RegExp spmPrefixExp = - RegExp(r''); static String? buvid; /// 设置cookie @@ -62,11 +57,6 @@ class Request { baseUrlType = 'bangumi'; } setBaseUrl(type: baseUrlType); - try { - await buvidActivate(); - } catch (e) { - log("setCookie, ${e.toString()}"); - } final String cookieString = cookie .map((Cookie cookie) => '${cookie.name}=${cookie.value}') @@ -122,30 +112,6 @@ class Request { dio.options.headers['referer'] = 'https://www.bilibili.com/'; } - static Future buvidActivate() async { - var html = await Request().get(Api.dynamicSpmPrefix); - String spmPrefix = spmPrefixExp.firstMatch(html.data)!.group(1)!; - Random rand = Random(); - String rand_png_end = base64.encode( - List.generate(32, (_) => rand.nextInt(256)) + - List.filled(4, 0) + - [73, 69, 78, 68] + - List.generate(4, (_) => rand.nextInt(256))); - - String jsonData = json.encode({ - '3064': 1, - '39c8': '${spmPrefix}.fp.risk', - '3c43': { - 'adca': 'Linux', - 'bfe9': rand_png_end.substring(rand_png_end.length - 50), - }, - }); - - await Request().post(Api.activateBuvidApi, - data: {'payload': jsonData}, - options: Options(contentType: 'application/json')); - } - /* * config it and create */ diff --git a/lib/main.dart b/lib/main.dart index 1ec86c8e..fcb29fad 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -10,6 +10,7 @@ import 'package:flutter/material.dart'; import 'package:dynamic_color/dynamic_color.dart'; import 'package:hive/hive.dart'; import 'package:pilipala/common/widgets/custom_toast.dart'; +import 'package:pilipala/http/common.dart'; import 'package:pilipala/http/init.dart'; import 'package:pilipala/models/common/color_type.dart'; import 'package:pilipala/models/common/theme_type.dart'; @@ -66,6 +67,7 @@ void main() async { PiliSchame.init(); await GlobalDataCache().initialize(); + CommonHttp.buvidActivate(); } class MyApp extends StatelessWidget { From 121d32e40324d2f178493c07d0213f68bb02f5b8 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 20 Nov 2024 09:50:34 +0800 Subject: [PATCH 2/6] mod: reply appbar title spacing --- lib/pages/video/detail/reply_reply/view.dart | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/pages/video/detail/reply_reply/view.dart b/lib/pages/video/detail/reply_reply/view.dart index c697349d..cf72c3a4 100644 --- a/lib/pages/video/detail/reply_reply/view.dart +++ b/lib/pages/video/detail/reply_reply/view.dart @@ -89,9 +89,12 @@ class _VideoReplyReplyPanelState extends State { return AppBar( toolbarHeight: 45, automaticallyImplyLeading: false, - title: Text( - '评论详情', - style: Theme.of(context).textTheme.titleSmall, + title: Padding( + padding: const EdgeInsets.only(left: 14), + child: Text( + '评论详情', + style: Theme.of(context).textTheme.titleSmall, + ), ), actions: [ IconButton( @@ -102,7 +105,7 @@ class _VideoReplyReplyPanelState extends State { Navigator.pop(context); }, ), - const SizedBox(width: 14), + const SizedBox(width: 12), ], ); } From 14640f338ca4068f29214ea707c362083a775428 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 21 Nov 2024 23:43:55 +0800 Subject: [PATCH 3/6] opt: hide live users by default --- lib/pages/dynamics/widgets/up_panel.dart | 239 +++++++++++++++-------- 1 file changed, 156 insertions(+), 83 deletions(-) diff --git a/lib/pages/dynamics/widgets/up_panel.dart b/lib/pages/dynamics/widgets/up_panel.dart index d5d7958e..35dc5cd8 100644 --- a/lib/pages/dynamics/widgets/up_panel.dart +++ b/lib/pages/dynamics/widgets/up_panel.dart @@ -34,6 +34,7 @@ class _UpPanelState extends State { List liveList = []; static const itemPadding = EdgeInsets.symmetric(horizontal: 5, vertical: 0); late MyInfo userInfo; + RxBool showLiveUser = false.obs; void listFormat() { userInfo = widget.upData.myInfo!; @@ -131,21 +132,70 @@ class _UpPanelState extends State { children: [ const SizedBox(width: 10), if (liveList.isNotEmpty) ...[ - for (int i = 0; i < liveList.length; i++) ...[ - upItemBuild(liveList[i], i) - ], - VerticalDivider( - indent: 20, - endIndent: 40, - width: 26, - color: Theme.of(context) - .colorScheme - .primary - .withOpacity(0.5), + Obx( + () => AnimatedSwitcher( + duration: const Duration(milliseconds: 300), + transitionBuilder: (Widget child, + Animation animation) { + return FadeTransition( + opacity: animation, child: child); + }, + child: showLiveUser.value + ? Row( + key: ValueKey(showLiveUser.value), + children: [ + for (int i = 0; + i < liveList.length; + i++) + UpItemWidget( + data: liveList[i], + index: i, + currentMid: currentMid, + onClickUp: onClickUp, + onClickUpAni: onClickUpAni, + itemPadding: itemPadding, + contentWidth: contentWidth, + ) + ], + ) + : SizedBox.shrink( + key: ValueKey(showLiveUser.value), + ), + ), + ), + Obx( + () => IconButton( + onPressed: () { + showLiveUser.value = !showLiveUser.value; + }, + icon: AnimatedSwitcher( + duration: const Duration(milliseconds: 300), + transitionBuilder: (Widget child, + Animation animation) { + return ScaleTransition( + scale: animation, child: child); + }, + child: Icon( + !showLiveUser.value + ? Icons.arrow_forward_ios_rounded + : Icons.arrow_back_ios_rounded, + key: ValueKey(showLiveUser.value), + size: 18, + ), + ), + ), ), ], for (int i = 0; i < upList.length; i++) ...[ - upItemBuild(upList[i], i) + UpItemWidget( + data: upList[i], + index: i, + currentMid: currentMid, + onClickUp: onClickUp, + onClickUpAni: onClickUpAni, + itemPadding: itemPadding, + contentWidth: contentWidth, + ) ], const SizedBox(width: 10), ], @@ -165,8 +215,93 @@ class _UpPanelState extends State { )), ); } +} - Widget upItemBuild(data, i) { +class _SliverHeaderDelegate extends SliverPersistentHeaderDelegate { + _SliverHeaderDelegate({required this.height, required this.child}); + + final double height; + final Widget child; + + @override + Widget build( + BuildContext context, double shrinkOffset, bool overlapsContent) { + return child; + } + + @override + double get maxExtent => height; + + @override + double get minExtent => height; + + @override + bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) => + true; +} + +class UpPanelSkeleton extends StatelessWidget { + const UpPanelSkeleton({super.key}); + + @override + Widget build(BuildContext context) { + return ListView.builder( + scrollDirection: Axis.horizontal, + physics: const NeverScrollableScrollPhysics(), + itemCount: 10, + itemBuilder: ((context, index) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 0), + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + width: 50, + height: 50, + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.onInverseSurface, + borderRadius: BorderRadius.circular(50), + ), + ), + Container( + margin: const EdgeInsets.only(top: 6), + width: 45, + height: 12, + color: Theme.of(context).colorScheme.onInverseSurface, + ), + ], + ), + ); + }), + ); + } +} + +class UpItemWidget extends StatelessWidget { + final dynamic data; + final int index; + final RxInt currentMid; + final Function(dynamic, int) onClickUp; + final Function(dynamic, int) onClickUpAni; + // final Function() feedBack; + final EdgeInsets itemPadding; + final double contentWidth; + + const UpItemWidget({ + Key? key, + required this.data, + required this.index, + required this.currentMid, + required this.onClickUp, + required this.onClickUpAni, + // required this.feedBack, + required this.itemPadding, + required this.contentWidth, + }) : super(key: key); + + @override + Widget build(BuildContext context) { return InkWell( onTap: () { feedBack(); @@ -174,9 +309,9 @@ class _UpPanelState extends State { EasyThrottle.throttle('follow', const Duration(milliseconds: 300), () { if (GlobalDataCache().enableDynamicSwitch) { - onClickUp(data, i); + onClickUp(data, index); } else { - onClickUpAni(data, i); + onClickUpAni(data, index); } }); } else if (data.type == 'live') { @@ -251,13 +386,12 @@ class _UpPanelState extends State { softWrap: false, textAlign: TextAlign.center, style: TextStyle( - color: currentMid.value == data.mid - ? Theme.of(context).colorScheme.primary - : Theme.of(context).colorScheme.outline, - fontSize: Theme.of(context) - .textTheme - .labelMedium! - .fontSize), + color: currentMid.value == data.mid + ? Theme.of(context).colorScheme.primary + : Theme.of(context).colorScheme.outline, + fontSize: + Theme.of(context).textTheme.labelMedium!.fontSize, + ), ), ), ), @@ -269,64 +403,3 @@ class _UpPanelState extends State { ); } } - -class _SliverHeaderDelegate extends SliverPersistentHeaderDelegate { - _SliverHeaderDelegate({required this.height, required this.child}); - - final double height; - final Widget child; - - @override - Widget build( - BuildContext context, double shrinkOffset, bool overlapsContent) { - return child; - } - - @override - double get maxExtent => height; - - @override - double get minExtent => height; - - @override - bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) => - true; -} - -class UpPanelSkeleton extends StatelessWidget { - const UpPanelSkeleton({super.key}); - - @override - Widget build(BuildContext context) { - return ListView.builder( - scrollDirection: Axis.horizontal, - physics: const NeverScrollableScrollPhysics(), - itemCount: 10, - itemBuilder: ((context, index) { - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 0), - child: Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Container( - width: 50, - height: 50, - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.onInverseSurface, - borderRadius: BorderRadius.circular(50), - ), - ), - Container( - margin: const EdgeInsets.only(top: 6), - width: 45, - height: 12, - color: Theme.of(context).colorScheme.onInverseSurface, - ), - ], - ), - ); - }), - ); - } -} From 2db409b44909f2ce06a485248846e8ba67f12071 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 23 Nov 2024 15:15:58 +0800 Subject: [PATCH 4/6] opt: video intro skeleton --- lib/common/skeleton/video_intro.dart | 126 ++++++++++++++++++ lib/pages/video/detail/introduction/view.dart | 25 +--- 2 files changed, 133 insertions(+), 18 deletions(-) create mode 100644 lib/common/skeleton/video_intro.dart diff --git a/lib/common/skeleton/video_intro.dart b/lib/common/skeleton/video_intro.dart new file mode 100644 index 00000000..b7a5ec74 --- /dev/null +++ b/lib/common/skeleton/video_intro.dart @@ -0,0 +1,126 @@ +import 'package:flutter/material.dart'; +import '../constants.dart'; +import 'skeleton.dart'; + +class VideoIntroSkeleton extends StatelessWidget { + const VideoIntroSkeleton({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + Color bgColor = Theme.of(context).colorScheme.onInverseSurface; + return Skeleton( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: StyleString.safeSpace), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox(height: 18), + Container( + width: double.infinity, + height: 20, + margin: const EdgeInsets.only(bottom: 6), + color: bgColor, + ), + Container( + width: 220, + height: 20, + margin: const EdgeInsets.only(bottom: 12), + color: bgColor, + ), + Row( + children: [ + Container( + width: 45, + height: 14, + color: bgColor, + ), + const SizedBox(width: 8), + Container( + width: 45, + height: 14, + color: bgColor, + ), + const SizedBox(width: 8), + Container( + width: 45, + height: 14, + color: bgColor, + ), + const Spacer(), + Container( + width: 35, + height: 14, + color: bgColor, + ), + const SizedBox(width: 4), + ], + ), + const SizedBox(height: 30), + LayoutBuilder(builder: (context, constraints) { + // 并列5个正方形 + double width = (constraints.maxWidth - 30) / 5; + return Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: List.generate(5, (index) { + return Container( + width: width - 24, + height: width - 24, + decoration: BoxDecoration( + color: bgColor, + borderRadius: BorderRadius.circular(16), + ), + ); + }), + ); + }), + const SizedBox(height: 20), + Container( + width: double.infinity, + height: 30, + margin: const EdgeInsets.symmetric(horizontal: 6), + decoration: BoxDecoration( + color: bgColor, + borderRadius: BorderRadius.circular(8), + ), + ), + const SizedBox(height: 20), + Row( + children: [ + ClipOval( + child: Container( + width: 44, + height: 44, + color: bgColor, + ), + ), + const SizedBox(width: 12), + Container( + width: 50, + height: 14, + color: bgColor, + ), + const SizedBox(width: 8), + Container( + width: 35, + height: 14, + color: bgColor, + ), + const Spacer(), + Container( + width: 55, + height: 30, + decoration: BoxDecoration( + color: bgColor, + borderRadius: BorderRadius.circular(16), + ), + ), + const SizedBox(width: 2) + ], + ), + const SizedBox(height: 10), + ], + ), + ), + ); + } +} diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index 451173dd..d283d086 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -6,8 +6,8 @@ import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:get/get.dart'; import 'package:flutter/material.dart'; import 'package:hive/hive.dart'; -import 'package:lottie/lottie.dart'; import 'package:pilipala/common/constants.dart'; +import 'package:pilipala/common/skeleton/video_intro.dart'; import 'package:pilipala/common/widgets/http_error.dart'; import 'package:pilipala/pages/video/detail/index.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; @@ -76,10 +76,8 @@ class _VideoIntroPanelState extends State future: _futureBuilderFuture, builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.done) { - if (snapshot.data == null) { - return const SliverToBoxAdapter(child: SizedBox()); - } - if (snapshot.data['status']) { + Map? data = snapshot.data; + if (data != null && data['status']) { // 请求成功 return Obx( () => VideoInfo( @@ -91,25 +89,16 @@ class _VideoIntroPanelState extends State } else { // 请求错误 return HttpError( - errMsg: snapshot.data['msg'], - btnText: snapshot.data['code'] == -404 || - snapshot.data['code'] == 62002 + errMsg: data?['msg'] ?? '请求异常', + btnText: (data?['code'] == -404 || data?['code'] == 62002) ? '返回上一页' : null, fn: () => Get.back(), ); } } else { - return SliverToBoxAdapter( - child: SizedBox( - height: 100, - child: Center( - child: Lottie.asset( - 'assets/loading.json', - width: 200, - ), - ), - ), + return const SliverToBoxAdapter( + child: VideoIntroSkeleton(), ); } }, From 79cd211976e05bc027e1951e71af632e2cedc110 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 23 Nov 2024 15:28:26 +0800 Subject: [PATCH 5/6] feat: back home when autoplay is off --- lib/pages/video/detail/view.dart | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 0fb97066..488ed14d 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -909,6 +909,21 @@ class _VideoDetailPageState extends State icon: const Icon(FontAwesomeIcons.arrowLeft, size: 15), fuc: () => Get.back(), ), + const SizedBox(width: 8), + ComBtn( + icon: const Icon( + FontAwesomeIcons.house, + size: 15, + color: Colors.white, + ), + fuc: () async { + await vdCtr.plPlayerController.dispose(type: 'all'); + if (mounted) { + Navigator.popUntil( + context, (Route route) => route.isFirst); + } + }, + ), const Spacer(), ComBtn( icon: const Icon(Icons.history_outlined, size: 22), From e8166c9f829e9887fef01680ebbea0f90eaf8f16 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 23 Nov 2024 22:48:09 +0800 Subject: [PATCH 6/6] opt: toggle showSubtitleBtn --- lib/pages/video/detail/controller.dart | 9 +++++++++ lib/pages/video/detail/widgets/header_control.dart | 6 +++--- lib/plugin/pl_player/controller.dart | 5 +++++ 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/lib/pages/video/detail/controller.dart b/lib/pages/video/detail/controller.dart index da3b0cbc..ca5a73be 100644 --- a/lib/pages/video/detail/controller.dart +++ b/lib/pages/video/detail/controller.dart @@ -480,6 +480,15 @@ class VideoDetailController extends GetxController getDanmaku(subtitles); } } + headerControl = HeaderControl( + controller: plPlayerController, + videoDetailCtr: this, + floating: floating, + bvid: bvid, + videoType: videoType, + showSubtitleBtn: result['status'] && result['data'].subtitles.isNotEmpty, + ); + plPlayerController.setHeaderControl(headerControl); } // 获取弹幕 diff --git a/lib/pages/video/detail/widgets/header_control.dart b/lib/pages/video/detail/widgets/header_control.dart index f22bf846..d563396d 100644 --- a/lib/pages/video/detail/widgets/header_control.dart +++ b/lib/pages/video/detail/widgets/header_control.dart @@ -30,7 +30,7 @@ class HeaderControl extends StatefulWidget implements PreferredSizeWidget { this.floating, this.bvid, this.videoType, - this.showSubtitleBtn, + this.showSubtitleBtn = true, super.key, }); final PlPlayerController? controller; @@ -38,7 +38,7 @@ class HeaderControl extends StatefulWidget implements PreferredSizeWidget { final Floating? floating; final String? bvid; final SearchType? videoType; - final bool? showSubtitleBtn; + final bool showSubtitleBtn; @override State createState() => _HeaderControlState(); @@ -1326,7 +1326,7 @@ class _HeaderControlState extends State { ], /// 字幕 - if (widget.showSubtitleBtn ?? true) + if (widget.showSubtitleBtn) ComBtn( icon: const Icon( Icons.closed_caption_off, diff --git a/lib/plugin/pl_player/controller.dart b/lib/plugin/pl_player/controller.dart index de4cd9df..d1305aa7 100644 --- a/lib/plugin/pl_player/controller.dart +++ b/lib/plugin/pl_player/controller.dart @@ -929,6 +929,11 @@ class PlPlayerController { showControls.value = !val; } + /// 设置/更新顶部控制栏 + void setHeaderControl(PreferredSizeWidget? widget) { + headerControl = widget; + } + void toggleFullScreen(bool val) { _isFullScreen.value = val; }