From 4d1e4511a3f47826b998b17b1aff2e5c8f52e20a Mon Sep 17 00:00:00 2001 From: orz12 Date: Wed, 10 Jan 2024 02:25:21 +0800 Subject: [PATCH 01/10] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E5=AE=9A?= =?UTF-8?q?=E6=97=B6=E5=85=B3=E9=97=AD=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/common/widgets/content_container.dart | 2 +- lib/pages/video/detail/view.dart | 4 +- .../video/detail/widgets/header_control.dart | 160 ++++++++++++++++-- lib/services/shutdown_timer_service.dart | 140 +++++++++++++++ 4 files changed, 291 insertions(+), 15 deletions(-) create mode 100644 lib/services/shutdown_timer_service.dart diff --git a/lib/common/widgets/content_container.dart b/lib/common/widgets/content_container.dart index 076a02e9..a9a1bf12 100644 --- a/lib/common/widgets/content_container.dart +++ b/lib/common/widgets/content_container.dart @@ -20,7 +20,7 @@ class ContentContainer extends StatelessWidget { builder: (BuildContext context, BoxConstraints constraints) { return SingleChildScrollView( clipBehavior: childClipBehavior ?? Clip.hardEdge, - physics: isScrollable ? null : NeverScrollableScrollPhysics(), + physics: isScrollable ? null : const NeverScrollableScrollPhysics(), child: ConstrainedBox( constraints: constraints.copyWith( minHeight: constraints.maxHeight, diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 38b81378..f8d00fb2 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -24,6 +24,7 @@ import 'package:pilipala/services/service_locator.dart'; import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/plugin/pl_player/utils/fullscreen.dart'; +import '../../../services/shutdown_timer_service.dart'; import 'widgets/header_control.dart'; class VideoDetailPage extends StatefulWidget { @@ -120,7 +121,7 @@ class _VideoDetailPageState extends State if (autoExitFullcreen) { plPlayerController!.triggerFullScreen(status: false); } - + shutdownTimerService.handleWaitingFinished(); /// 顺序播放 列表循环 if (plPlayerController!.playRepeat != PlayRepeat.pause && plPlayerController!.playRepeat != PlayRepeat.singleCycle) { @@ -187,6 +188,7 @@ class _VideoDetailPageState extends State @override void dispose() { + shutdownTimerService.handleWaitingFinished(); if (plPlayerController != null) { plPlayerController!.removeStatusLister(playerListener); plPlayerController!.dispose(); diff --git a/lib/pages/video/detail/widgets/header_control.dart b/lib/pages/video/detail/widgets/header_control.dart index db6aeb3c..f2e38870 100644 --- a/lib/pages/video/detail/widgets/header_control.dart +++ b/lib/pages/video/detail/widgets/header_control.dart @@ -17,6 +17,7 @@ import 'package:pilipala/plugin/pl_player/index.dart'; import 'package:pilipala/plugin/pl_player/models/play_repeat.dart'; import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/http/danmaku.dart'; +import 'package:pilipala/services/shutdown_timer_service.dart'; class HeaderControl extends StatefulWidget implements PreferredSizeWidget { const HeaderControl({ @@ -39,14 +40,13 @@ class HeaderControl extends StatefulWidget implements PreferredSizeWidget { class _HeaderControlState extends State { late PlayUrlModel videoInfo; List playSpeed = PlaySpeed.values; - TextStyle subTitleStyle = const TextStyle(fontSize: 12); - TextStyle titleStyle = const TextStyle(fontSize: 14); + static const TextStyle subTitleStyle = TextStyle(fontSize: 12); + static const TextStyle titleStyle = TextStyle(fontSize: 14); Size get preferredSize => const Size(double.infinity, kToolbarHeight); final Box localCache = GStrorage.localCache; final Box videoStorage = GStrorage.video; late List speedsList; double buttonSpace = 8; - @override void initState() { super.initState(); @@ -63,7 +63,7 @@ class _HeaderControlState extends State { builder: (_) { return Container( width: double.infinity, - height: 440, + height: 460, clipBehavior: Clip.hardEdge, decoration: BoxDecoration( color: Theme.of(context).colorScheme.background, @@ -125,13 +125,20 @@ class _HeaderControlState extends State { }, dense: true, leading: const Icon(Icons.watch_later_outlined, size: 20), - title: Text('添加至「稍后再看」', style: titleStyle), + title: const Text('添加至「稍后再看」', style: titleStyle), + ), + ListTile( + onTap: () => {Get.back(), scheduleExit()}, + dense: true, + leading: + const Icon(Icons.hourglass_top_outlined, size: 20), + title: const Text('定时关闭(测试)', style: titleStyle), ), ListTile( onTap: () => {Get.back(), showSetVideoQa()}, dense: true, leading: const Icon(Icons.play_circle_outline, size: 20), - title: Text('选择画质', style: titleStyle), + title: const Text('选择画质', style: titleStyle), subtitle: Text( '当前画质 ${widget.videoDetailCtr!.currentVideoQa.description}', style: subTitleStyle), @@ -141,7 +148,7 @@ class _HeaderControlState extends State { onTap: () => {Get.back(), showSetAudioQa()}, dense: true, leading: const Icon(Icons.album_outlined, size: 20), - title: Text('选择音质', style: titleStyle), + title: const Text('选择音质', style: titleStyle), subtitle: Text( '当前音质 ${widget.videoDetailCtr!.currentAudioQa!.description}', style: subTitleStyle), @@ -150,7 +157,7 @@ class _HeaderControlState extends State { onTap: () => {Get.back(), showSetDecodeFormats()}, dense: true, leading: const Icon(Icons.av_timer_outlined, size: 20), - title: Text('解码格式', style: titleStyle), + title: const Text('解码格式', style: titleStyle), subtitle: Text( '当前解码格式 ${widget.videoDetailCtr!.currentDecodeFormats.description}', style: subTitleStyle), @@ -159,7 +166,7 @@ class _HeaderControlState extends State { onTap: () => {Get.back(), showSetRepeat()}, dense: true, leading: const Icon(Icons.repeat, size: 20), - title: Text('播放顺序', style: titleStyle), + title: const Text('播放顺序', style: titleStyle), subtitle: Text(widget.controller!.playRepeat.description, style: subTitleStyle), ), @@ -167,7 +174,7 @@ class _HeaderControlState extends State { onTap: () => {Get.back(), showSetDanmaku()}, dense: true, leading: const Icon(Icons.subtitles_outlined, size: 20), - title: Text('弹幕设置', style: titleStyle), + title: const Text('弹幕设置', style: titleStyle), ), ], ), @@ -263,6 +270,133 @@ class _HeaderControlState extends State { ); } + /// 定时关闭 + void scheduleExit() async { + const List scheduleTimeChoices = [ + -1, + 15, + 30, + 60, + ]; + showModalBottomSheet( + context: context, + elevation: 0, + backgroundColor: Colors.transparent, + builder: (BuildContext context) { + return StatefulBuilder( + builder: (BuildContext context, StateSetter setState) { + return Container( + width: double.infinity, + height: 500, + clipBehavior: Clip.hardEdge, + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.background, + borderRadius: const BorderRadius.all(Radius.circular(12)), + ), + margin: const EdgeInsets.all(12), + padding: const EdgeInsets.only(left: 14, right: 14), + child: SingleChildScrollView( + child: Padding( + padding: + const EdgeInsets.symmetric(vertical: 0, horizontal: 20), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox(height: 30), + const Center(child: Text('定时关闭', style: titleStyle)), + const SizedBox(height: 10), + for (final int choice in scheduleTimeChoices) ...[ + ListTile( + onTap: () { + shutdownTimerService.scheduledExitInMinutes = + choice; + shutdownTimerService.startShutdownTimer(); + Get.back(); + }, + contentPadding: const EdgeInsets.only(), + dense: true, + title: Text(choice == -1 ? "禁用" : "$choice分钟后"), + trailing: shutdownTimerService + .scheduledExitInMinutes == + choice + ? Icon( + Icons.done, + color: Theme.of(context).colorScheme.primary, + ) + : const SizedBox(), + ) + ], + const SizedBox(height: 6), + const Center( + child: SizedBox( + width: 100, + child: Divider(height: 1), + )), + const SizedBox(height: 10), + ListTile( + onTap: () { + shutdownTimerService.waitForPlayingCompleted = + !shutdownTimerService.waitForPlayingCompleted; + setState(() {}); + }, + dense: true, + contentPadding: const EdgeInsets.only(), + title: + const Text("额外等待视频播放完毕", style: titleStyle), + trailing: Switch( + // thumb color (round icon) + activeColor: Theme.of(context).colorScheme.primary, + activeTrackColor: + Theme.of(context).colorScheme.primaryContainer, + inactiveThumbColor: + Theme.of(context).colorScheme.primaryContainer, + inactiveTrackColor: + Theme.of(context).colorScheme.background, + splashRadius: 10.0, + // boolean variable value + value: shutdownTimerService.waitForPlayingCompleted, + // changes the state of the switch + onChanged: (value) => setState(() => + shutdownTimerService.waitForPlayingCompleted = + value), + ), + ), + const SizedBox(height: 10), + Row( + children: [ + const Text('倒计时结束:', style: titleStyle), + const Spacer(), + ActionRowLineItem( + onTap: () { + shutdownTimerService.exitApp = false; + setState(() {}); + // Get.back(); + }, + text: " 暂停视频 ", + selectStatus: !shutdownTimerService.exitApp, + ), + const Spacer(), + // const SizedBox(width: 10), + ActionRowLineItem( + onTap: () { + shutdownTimerService.exitApp = true; + setState(() {}); + // Get.back(); + }, + text: " 退出APP ", + selectStatus: shutdownTimerService.exitApp, + ) + ], + ), + ]), + ), + ), + ); + }); + }, + ); + } + /// 选择倍速 void showSetSpeedSheet() { final double currentSpeed = widget.controller!.playbackSpeed; @@ -367,7 +501,7 @@ class _HeaderControlState extends State { child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - Text('选择画质', style: titleStyle), + const Text('选择画质', style: titleStyle), SizedBox(width: buttonSpace), Icon( Icons.info_outline, @@ -448,7 +582,7 @@ class _HeaderControlState extends State { margin: const EdgeInsets.all(12), child: Column( children: [ - SizedBox( + const SizedBox( height: 45, child: Center(child: Text('选择音质', style: titleStyle))), Expanded( @@ -614,7 +748,7 @@ class _HeaderControlState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - SizedBox( + const SizedBox( height: 45, child: Center(child: Text('弹幕设置', style: titleStyle)), ), diff --git a/lib/services/shutdown_timer_service.dart b/lib/services/shutdown_timer_service.dart new file mode 100644 index 00000000..aa9c5ceb --- /dev/null +++ b/lib/services/shutdown_timer_service.dart @@ -0,0 +1,140 @@ +// 定时关闭服务 +import 'dart:async'; +import 'dart:io'; +import 'package:flutter/material.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; + +import '../plugin/pl_player/controller.dart'; + +class ShutdownTimerService { + static final ShutdownTimerService _instance = + ShutdownTimerService._internal(); + Timer? _shutdownTimer; + Timer? _autoCloseDialogTimer; + //定时退出 + int scheduledExitInMinutes = -1; + bool exitApp = false; + bool waitForPlayingCompleted = false; + bool isWaiting = false; + + factory ShutdownTimerService() => _instance; + + ShutdownTimerService._internal(); + + void startShutdownTimer() { + cancelShutdownTimer(); // Cancel any previous timer + if (scheduledExitInMinutes == -1) { + //使用toast提示用户已取消 + SmartDialog.showToast("取消定时关闭"); + return; + } + SmartDialog.showToast("设置 $scheduledExitInMinutes 分钟后定时关闭"); + _shutdownTimer = Timer(Duration(minutes: scheduledExitInMinutes), + () => _shutdownDecider()); + } + + void _showTimeUpButPauseDialog() { + SmartDialog.show( + builder: (BuildContext dialogContext) { + return AlertDialog( + title: const Text('定时关闭'), + content: const Text('时间到啦!'), + actions: [ + TextButton( + child: const Text('确认'), + onPressed: () { + cancelShutdownTimer(); + SmartDialog.dismiss(); + }, + ), + ], + ); + }, + ); + } + + void _showShutdownDialog() { + SmartDialog.show( + builder: (BuildContext dialogContext) { + // Start the 10-second timer to auto close the dialog + _autoCloseDialogTimer?.cancel(); + _autoCloseDialogTimer = Timer(const Duration(seconds: 10), () { + SmartDialog.dismiss();// Close the dialog + _executeShutdown(); + }); + return AlertDialog( + title: const Text('定时关闭'), + content: const Text('将在10秒后执行,是否需要取消?'), + actions: [ + TextButton( + child: const Text('取消关闭'), + onPressed: () { + _autoCloseDialogTimer?.cancel(); // Cancel the auto-close timer + cancelShutdownTimer(); // Cancel the shutdown timer + SmartDialog.dismiss(); // Close the dialog + }, + ), + ], + ); + }, + ).then((_) { + // Cleanup when the dialog is dismissed + _autoCloseDialogTimer?.cancel(); + }); + } + + void _shutdownDecider() { + if (exitApp && !waitForPlayingCompleted) { + _showShutdownDialog(); + return; + } + PlPlayerController plPlayerController = PlPlayerController.getInstance(); + if (!exitApp && !waitForPlayingCompleted) { + if (!plPlayerController.playerStatus.playing) { + //仅提示用户 + _showTimeUpButPauseDialog(); + } else { + _showShutdownDialog(); + } + return; + } + //waitForPlayingCompleted + if (!plPlayerController.playerStatus.playing) { + _showShutdownDialog(); + return; + } + SmartDialog.showToast("定时关闭时间已到,等待当前视频播放完成"); + //监听播放完成 + //该方法依赖耦合实现,不够优雅 + isWaiting = true; + } + void handleWaitingFinished(){ + if(isWaiting){ + _showShutdownDialog(); + isWaiting = false; + } + } + void _executeShutdown() { + if (exitApp) { + //退出app + exit(0); + } else { + //暂停播放 + PlPlayerController plPlayerController = PlPlayerController.getInstance(); + if (plPlayerController.playerStatus.playing) { + plPlayerController.pause(); + waitForPlayingCompleted = true; + SmartDialog.showToast("已暂停播放"); + } else { + SmartDialog.showToast("当前未播放"); + } + } + } + + void cancelShutdownTimer() { + isWaiting = false; + _shutdownTimer?.cancel(); + } +} + +final shutdownTimerService = ShutdownTimerService(); From c9fd6304fd27ed3b7cb64097b239e0f2600d7999 Mon Sep 17 00:00:00 2001 From: orz12 Date: Tue, 9 Jan 2024 15:52:22 +0800 Subject: [PATCH 02/10] =?UTF-8?q?=E5=B0=9D=E8=AF=95=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E5=A4=9A=E5=B1=82=E5=BC=B9=E5=B9=95=E6=89=80=E5=8D=A0=E8=B5=84?= =?UTF-8?q?=E6=BA=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/danmaku/view.dart | 4 ++-- lib/pages/video/detail/view.dart | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/pages/danmaku/view.dart b/lib/pages/danmaku/view.dart index 5c3f97d0..83d6020e 100644 --- a/lib/pages/danmaku/view.dart +++ b/lib/pages/danmaku/view.dart @@ -125,7 +125,7 @@ class _PlDanmakuState extends State { duration: const Duration(milliseconds: 100), child: DanmakuView( createdController: (DanmakuController e) async { - widget.playerController.danmakuController = _controller = e; + playerController.danmakuController = _controller = e; }, option: DanmakuOption( fontSize: 15 * fontSizeVal, @@ -135,7 +135,7 @@ class _PlDanmakuState extends State { hideScroll: blockTypes.contains(2), hideBottom: blockTypes.contains(4), duration: - danmakuDurationVal / widget.playerController.playbackSpeed, + danmakuDurationVal / playerController.playbackSpeed, // initDuration / // (danmakuSpeedVal * widget.playerController.playbackSpeed), ), diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 38b81378..ceaa2648 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -213,6 +213,8 @@ class _VideoDetailPageState extends State videoIntroController.isPaused = true; plPlayerController!.removeStatusLister(playerListener); plPlayerController!.pause(); + plPlayerController!.danmakuController?.pause(); + plPlayerController!.danmakuController?.clear(); } super.didPushNext(); } From 0cc25203b141fbf7c83377e0b0d952233b4c9339 Mon Sep 17 00:00:00 2001 From: orz12 Date: Mon, 15 Jan 2024 00:58:52 +0800 Subject: [PATCH 03/10] =?UTF-8?q?fix:=20=E8=AE=BE=E7=BD=AE=E8=87=AA?= =?UTF-8?q?=E5=AE=9A=E4=B9=89=E5=80=8D=E9=80=9F=E5=90=8E=E7=99=BD=E5=B1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 原因:List并非List,赋值会产生错误 --- lib/plugin/pl_player/controller.dart | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/plugin/pl_player/controller.dart b/lib/plugin/pl_player/controller.dart index 16d5befd..fd3f02c9 100644 --- a/lib/plugin/pl_player/controller.dart +++ b/lib/plugin/pl_player/controller.dart @@ -289,9 +289,8 @@ class PlPlayerController { _longPressSpeed.value = videoStorage .get(VideoBoxKey.longPressSpeedDefault, defaultValue: 2.0); } - final List speedsListTemp = videoStorage - .get(VideoBoxKey.customSpeedsList, defaultValue: []); - speedsList = List.from(speedsListTemp); + speedsList = List.from(videoStorage + .get(VideoBoxKey.customSpeedsList, defaultValue: [])); for (final PlaySpeed i in PlaySpeed.values) { speedsList.add(i.value); } From 6654094480e86c7f61b2ef130aa086d4e376c6a4 Mon Sep 17 00:00:00 2001 From: orz12 Date: Mon, 15 Jan 2024 00:59:19 +0800 Subject: [PATCH 04/10] =?UTF-8?q?opt:=20=E4=BB=A3=E7=A0=81=E4=BC=98?= =?UTF-8?q?=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/common/widgets/content_container.dart | 2 +- lib/http/html.dart | 2 +- lib/http/init.dart | 2 +- lib/pages/whisper/view.dart | 4 ++-- lib/utils/wbi_sign.dart | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/common/widgets/content_container.dart b/lib/common/widgets/content_container.dart index 076a02e9..982a29c6 100644 --- a/lib/common/widgets/content_container.dart +++ b/lib/common/widgets/content_container.dart @@ -34,7 +34,7 @@ class ContentContainer extends StatelessWidget { child: contentWidget!, ) else - Spacer(), + const Spacer(), if (bottomWidget != null) bottomWidget!, ], ), diff --git a/lib/http/html.dart b/lib/http/html.dart index eb1401c9..100887e5 100644 --- a/lib/http/html.dart +++ b/lib/http/html.dart @@ -15,7 +15,7 @@ class HtmlHttp { Match match = regex.firstMatch(response.data)!; String matchedString = match.group(0)!; response = await Request().get( - 'https:$matchedString' + '/', + 'https:$matchedString/', extra: {'ua': 'pc'}, ); } diff --git a/lib/http/init.dart b/lib/http/init.dart index d446f86d..7840499f 100644 --- a/lib/http/init.dart +++ b/lib/http/init.dart @@ -6,7 +6,7 @@ import 'package:cookie_jar/cookie_jar.dart'; import 'package:dio/dio.dart'; import 'package:dio/io.dart'; import 'package:dio_cookie_manager/dio_cookie_manager.dart'; -import 'package:dio_http2_adapter/dio_http2_adapter.dart'; +// import 'package:dio_http2_adapter/dio_http2_adapter.dart'; import 'package:hive/hive.dart'; import '../utils/storage.dart'; import '../utils/utils.dart'; diff --git a/lib/pages/whisper/view.dart b/lib/pages/whisper/view.dart index 4de197ca..f2779a17 100644 --- a/lib/pages/whisper/view.dart +++ b/lib/pages/whisper/view.dart @@ -210,11 +210,11 @@ class _WhisperPageState extends State { ); } else { // 请求错误 - return SizedBox(); + return const SizedBox(); } } else { // 骨架屏 - return SizedBox(); + return const SizedBox(); } }, ) diff --git a/lib/utils/wbi_sign.dart b/lib/utils/wbi_sign.dart index 68e2bf0f..4f831f16 100644 --- a/lib/utils/wbi_sign.dart +++ b/lib/utils/wbi_sign.dart @@ -80,7 +80,7 @@ class WbiSign { String getMixinKey(String orig) { String temp = ''; for (int i = 0; i < mixinKeyEncTab.length; i++) { - temp += orig.split('')[mixinKeyEncTab[i] as int]; + temp += orig.split('')[mixinKeyEncTab[i]]; } return temp.substring(0, 32); } From 96737ded5bd542f7b4e4b03dd0f8d28174fc6ad6 Mon Sep 17 00:00:00 2001 From: orz12 Date: Mon, 15 Jan 2024 02:14:43 +0800 Subject: [PATCH 05/10] =?UTF-8?q?fix:=20=E4=B8=93=E6=A0=8F=E6=97=B6?= =?UTF-8?q?=E9=97=B4=E6=B2=A1=E6=9C=89=E5=B9=B4=E4=BB=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/member_seasons/widgets/item.dart | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/pages/member_seasons/widgets/item.dart b/lib/pages/member_seasons/widgets/item.dart index 157adb66..6398c5eb 100644 --- a/lib/pages/member_seasons/widgets/item.dart +++ b/lib/pages/member_seasons/widgets/item.dart @@ -25,7 +25,7 @@ class MemberSeasonsItem extends StatelessWidget { child: InkWell( onTap: () async { int cid = - await SearchHttp.ab2c(aid: seasonItem.aid, bvid: seasonItem.bvid); + await SearchHttp.ab2c(aid: seasonItem.aid, bvid: seasonItem.bvid); Get.toNamed('/video?bvid=${seasonItem.bvid}&cid=$cid', arguments: {'videoItem': seasonItem, 'heroTag': heroTag}); }, @@ -46,12 +46,13 @@ class MemberSeasonsItem extends StatelessWidget { height: maxHeight, ), ), - if (seasonItem.duration != null) + if (seasonItem.pubdate != null) PBadge( bottom: 6, right: 6, type: 'gray', - text: Utils.timeFormat(seasonItem.duration), + text: Utils.CustomStamp_str( + timestamp: seasonItem.pubdate, date: 'YY-MM-DD'), ) ], ); @@ -78,7 +79,7 @@ class MemberSeasonsItem extends StatelessWidget { const Spacer(), Text( Utils.CustomStamp_str( - timestamp: seasonItem.pubdate, date: 'MM-DD'), + timestamp: seasonItem.pubdate, date: 'YY-MM-DD'), style: TextStyle( fontSize: 11, color: Theme.of(context).colorScheme.outline, From 02bdb466256e3992ab19e4be85b21f5a56c8fa75 Mon Sep 17 00:00:00 2001 From: orz12 Date: Mon, 15 Jan 2024 20:27:51 +0800 Subject: [PATCH 06/10] =?UTF-8?q?opt:=20=E3=80=90=E5=85=B3=E6=B3=A8?= =?UTF-8?q?=E3=80=91=E6=8C=89=E9=92=AE=E4=B8=8E=E8=87=AA=E5=8A=A8=E6=92=AD?= =?UTF-8?q?=E6=94=BE=E7=95=8C=E9=9D=A2=E8=B7=B3=E5=8F=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 获得数据后再淡入显示,避免关注按钮跳变(如已关注状态下,会先经历默认的高亮未关注状态,再跳变为暗色已关注) 自动播放开启时取消显示封面(与官方一致),避免黑屏-亮屏(加载出封面)-黑屏(缓冲视频流)-亮屏(显示视频)的闪烁过程 --- lib/pages/video/detail/introduction/view.dart | 26 +-- lib/pages/video/detail/view.dart | 177 +++++++++--------- 2 files changed, 99 insertions(+), 104 deletions(-) diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index 85a24cc5..0a3ac934 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -414,14 +414,18 @@ class _VideoInfoState extends State with TickerProviderStateMixin { ), ), const Spacer(), - AnimatedOpacity( - opacity: loadingStatus ? 0 : 1, - duration: const Duration(milliseconds: 150), - child: SizedBox( - height: 32, - child: Obx( - () => - videoIntroController.followStatus.isNotEmpty + Obx(() => AnimatedOpacity( + opacity: loadingStatus || + videoIntroController + .followStatus.isEmpty + ? 0 + : 1, + duration: const Duration(milliseconds: 50), + child: SizedBox( + height: 32, + child: Obx( + () => videoIntroController + .followStatus.isNotEmpty ? TextButton( onPressed: videoIntroController .actionRelationMod, @@ -453,9 +457,9 @@ class _VideoInfoState extends State with TickerProviderStateMixin { .actionRelationMod, child: const Text('关注'), ), - ), - ), - ), + ), + ), + )), ], ), ), diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 38b81378..b26f07e5 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -388,107 +388,98 @@ class _VideoDetailPageState extends State }, ), - 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 && - !videoDetailController - .autoPlay.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( - side: MaterialStateProperty - .resolveWith( - (states) { - return BorderSide( - color: Theme.of(context) - .colorScheme - .primary - .withOpacity(0.5), - width: 1); - }), + 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 Theme.of(context) - .colorScheme - .background - .withOpacity(0.6); - }), + 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 4000e1b9dc6440ef708cb9c56df88b1b1cc7fc56 Mon Sep 17 00:00:00 2001 From: orz12 Date: Mon, 15 Jan 2024 21:02:46 +0800 Subject: [PATCH 07/10] =?UTF-8?q?fix:=20up=E8=AE=BE=E7=BD=AE=E5=88=86?= =?UTF-8?q?=E7=BB=84=E6=B2=A1=E6=9C=89=E6=97=A5=E9=97=B4=E6=A8=A1=E5=BC=8F?= =?UTF-8?q?=EF=BC=8C=E4=BB=A5=E5=8F=8A=E9=A2=9C=E8=89=B2=E4=B8=8D=E7=BB=9F?= =?UTF-8?q?=E4=B8=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/main.dart | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index 09dc3184..4e1482d7 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -113,6 +113,13 @@ class MyApp extends StatelessWidget { ? darkColorScheme : lightColorScheme, useMaterial3: true, + snackBarTheme: SnackBarThemeData( + actionTextColor: lightColorScheme.primary, + backgroundColor: lightColorScheme.secondaryContainer, + closeIconColor: lightColorScheme.secondary, + contentTextStyle: TextStyle(color: lightColorScheme.secondary), + elevation: 20, + ), pageTransitionsTheme: const PageTransitionsTheme( builders: { TargetPlatform.android: ZoomPageTransitionsBuilder( @@ -127,10 +134,11 @@ class MyApp extends StatelessWidget { ? lightColorScheme : darkColorScheme, useMaterial3: true, - snackBarTheme: const SnackBarThemeData( - actionTextColor: Colors.white, - backgroundColor: Colors.black, - contentTextStyle: TextStyle(color: Colors.white), + snackBarTheme: SnackBarThemeData( + actionTextColor: darkColorScheme.primary, + backgroundColor: darkColorScheme.secondaryContainer, + closeIconColor: darkColorScheme.secondary, + contentTextStyle: TextStyle(color: darkColorScheme.secondary), elevation: 20, ), ), From a418f457f5cfcc0307f99de67963ce02b8beb750 Mon Sep 17 00:00:00 2001 From: orz12 Date: Tue, 16 Jan 2024 01:32:55 +0800 Subject: [PATCH 08/10] =?UTF-8?q?mod:=20=E5=8D=87=E7=BA=A7web=E7=AB=AF?= =?UTF-8?q?=E6=8E=A8=E8=8D=90=E8=87=B3V8=EF=BC=8C=E5=AF=B9=E9=BD=90?= =?UTF-8?q?=E5=AE=98=E7=BD=91=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/video.dart | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/lib/http/video.dart b/lib/http/video.dart index 0f9ecfb3..a6a23f83 100644 --- a/lib/http/video.dart +++ b/lib/http/video.dart @@ -32,20 +32,27 @@ class VideoHttp { Api.recommendListWeb, data: { 'version': 1, - 'feed_version': 'V3', + 'feed_version': 'V8', + 'homepage_ver': 1, 'ps': ps, 'fresh_idx': freshIdx, - 'fresh_type': 999999 + 'brush': freshIdx, + 'fresh_type': 4 }, ); if (res.data['code'] == 0) { List list = []; + List blackMidsList = + setting.get(SettingBoxKey.blackMidsList, defaultValue: [-1]); for (var i in res.data['data']['item']) { - list.add(RecVideoItemModel.fromJson(i)); + //过滤掉live与ad,以及拉黑用户 + if (i['goto'] == 'av' && !blackMidsList.contains(i['owner']['mid'])) { + list.add(RecVideoItemModel.fromJson(i)); + } } return {'status': true, 'data': list}; } else { - return {'status': false, 'data': [], 'msg': ''}; + return {'status': false, 'data': [], 'msg': res.data['message']}; } } catch (err) { return {'status': false, 'data': [], 'msg': err.toString()}; From 931a513ac55a1dfa5f5e23be25479d58270fcfd1 Mon Sep 17 00:00:00 2001 From: orz12 Date: Thu, 18 Jan 2024 13:59:20 +0800 Subject: [PATCH 09/10] =?UTF-8?q?feat:=20=E6=8E=A8=E8=8D=90=E3=80=81?= =?UTF-8?q?=E7=9B=B8=E5=85=B3=E8=A7=86=E9=A2=91=E5=8D=A1=E7=89=87=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E6=8B=89=E9=BB=91Up=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + lib/common/widgets/video_card_h.dart | 59 ++++++++++++++++++- lib/common/widgets/video_card_v.dart | 85 +++++++++++++++++++++------- 3 files changed, 121 insertions(+), 24 deletions(-) diff --git a/.gitignore b/.gitignore index 24476c5d..7bc3f845 100644 --- a/.gitignore +++ b/.gitignore @@ -42,3 +42,4 @@ app.*.map.json /android/app/debug /android/app/profile /android/app/release +/*/flutter/[Gg]enerated*[Pp]lugin* diff --git a/lib/common/widgets/video_card_h.dart b/lib/common/widgets/video_card_h.dart index c4472b42..b00f1759 100644 --- a/lib/common/widgets/video_card_h.dart +++ b/lib/common/widgets/video_card_h.dart @@ -3,6 +3,7 @@ import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import '../../http/search.dart'; import '../../http/user.dart'; +import '../../http/video.dart'; import '../../utils/utils.dart'; import '../constants.dart'; import 'badge.dart'; @@ -265,7 +266,6 @@ class VideoContent extends StatelessWidget { height: 24, child: PopupMenuButton( padding: EdgeInsets.zero, - tooltip: '稍后再看', icon: Icon( Icons.more_vert_outlined, color: Theme.of(context).colorScheme.outline, @@ -280,10 +280,10 @@ class VideoContent extends StatelessWidget { onTap: () async { var res = await UserHttp.toViewLater( bvid: videoItem.bvid as String); - SmartDialog.showToast(res['msg'] as String); + SmartDialog.showToast(res['msg']); }, value: 'pause', - height: 35, + height: 40, child: const Row( children: [ Icon(Icons.watch_later_outlined, size: 16), @@ -292,6 +292,59 @@ class VideoContent extends StatelessWidget { ], ), ), + const PopupMenuDivider(), + PopupMenuItem( + onTap: () async { + SmartDialog.show( + useSystem: true, + animationType: + SmartAnimationType.centerFade_otherSlide, + builder: (BuildContext context) { + return AlertDialog( + title: const Text('提示'), + content: Text( + '确定拉黑:${videoItem.owner.name}(${videoItem.owner.mid})?' + '\n\n注:被拉黑的Up可以在隐私设置-黑名单管理中解除'), + actions: [ + TextButton( + onPressed: () => SmartDialog.dismiss(), + child: Text( + '点错了', + style: TextStyle( + color: Theme.of(context) + .colorScheme + .outline), + ), + ), + TextButton( + onPressed: () async { + var res = await VideoHttp.relationMod( + mid: videoItem.owner.mid, + act: 5, + reSrc: 11, + ); + SmartDialog.dismiss(); + SmartDialog.showToast( + res['msg'] ?? '成功'); + }, + child: const Text('确认'), + ) + ], + ); + }, + ); + }, + value: 'pause', + height: 40, + child: Row( + children: [ + const Icon(Icons.block, size: 16), + const SizedBox(width: 6), + Text('拉黑:${videoItem.owner.name}', + style: const TextStyle(fontSize: 13)) + ], + ), + ), ], ), ), diff --git a/lib/common/widgets/video_card_v.dart b/lib/common/widgets/video_card_v.dart index 1b6edbbf..c5577af3 100644 --- a/lib/common/widgets/video_card_v.dart +++ b/lib/common/widgets/video_card_v.dart @@ -4,6 +4,7 @@ import 'package:get/get.dart'; import '../../http/dynamics.dart'; import '../../http/search.dart'; import '../../http/user.dart'; +import '../../http/video.dart'; import '../../models/common/search_type.dart'; import '../../utils/id_utils.dart'; import '../../utils/utils.dart'; @@ -215,15 +216,10 @@ class VideoContent extends StatelessWidget { ), if (videoItem.goto == 'av' && crossAxisCount == 1) ...[ const SizedBox(width: 10), - WatchLater( + VideoPopupMenu( size: 32, iconSize: 18, - callFn: () async { - int aid = videoItem.param; - var res = - await UserHttp.toViewLater(bvid: IdUtils.av2bv(aid)); - SmartDialog.showToast(res['msg']); - }, + videoItem: videoItem, ), ], ], @@ -299,15 +295,10 @@ class VideoContent extends StatelessWidget { const Spacer(), ], if (videoItem.goto == 'av' && crossAxisCount != 1) ...[ - WatchLater( + VideoPopupMenu( size: 24, iconSize: 14, - callFn: () async { - int aid = videoItem.param; - var res = - await UserHttp.toViewLater(bvid: IdUtils.av2bv(aid)); - SmartDialog.showToast(res['msg']); - }, + videoItem: videoItem, ), ] else ...[ const SizedBox(height: 24) @@ -349,16 +340,16 @@ class VideoStat extends StatelessWidget { } } -class WatchLater extends StatelessWidget { +class VideoPopupMenu extends StatelessWidget { final double? size; final double? iconSize; - final Function? callFn; + final dynamic videoItem; - const WatchLater({ + const VideoPopupMenu({ Key? key, required this.size, required this.iconSize, - this.callFn, + required this.videoItem, }) : super(key: key); @override @@ -368,7 +359,6 @@ class WatchLater extends StatelessWidget { height: size, child: PopupMenuButton( padding: EdgeInsets.zero, - tooltip: '稍后再看', icon: Icon( Icons.more_vert_outlined, color: Theme.of(context).colorScheme.outline, @@ -379,9 +369,13 @@ class WatchLater extends StatelessWidget { onSelected: (String type) {}, itemBuilder: (BuildContext context) => >[ PopupMenuItem( - onTap: () => callFn!(), + onTap: () async { + var res = + await UserHttp.toViewLater(bvid: videoItem.bvid as String); + SmartDialog.showToast(res['msg']); + }, value: 'pause', - height: 35, + height: 40, child: const Row( children: [ Icon(Icons.watch_later_outlined, size: 16), @@ -390,6 +384,55 @@ class WatchLater extends StatelessWidget { ], ), ), + const PopupMenuDivider(), + PopupMenuItem( + onTap: () async { + SmartDialog.show( + useSystem: true, + animationType: SmartAnimationType.centerFade_otherSlide, + builder: (BuildContext context) { + return AlertDialog( + title: const Text('提示'), + content: Text( + '确定拉黑:${videoItem.owner.name}(${videoItem.owner.mid})?' + '\n\n注:被拉黑的Up可以在隐私设置-黑名单管理中解除'), + actions: [ + TextButton( + onPressed: () => SmartDialog.dismiss(), + child: Text( + '点错了', + style: TextStyle( + color: Theme.of(context).colorScheme.outline), + ), + ), + TextButton( + onPressed: () async { + var res = await VideoHttp.relationMod( + mid: videoItem.owner.mid, + act: 5, + reSrc: 11, + ); + SmartDialog.dismiss(); + SmartDialog.showToast(res['msg'] ?? '成功'); + }, + child: const Text('确认'), + ) + ], + ); + }, + ); + }, + value: 'pause', + height: 40, + child: Row( + children: [ + const Icon(Icons.block, size: 16), + const SizedBox(width: 6), + Text('拉黑:${videoItem.owner.name}', + style: const TextStyle(fontSize: 13)) + ], + ), + ), ], ), ); From 70cf27789f715b4aa6083dc9f7de716427154082 Mon Sep 17 00:00:00 2001 From: orz12 Date: Fri, 19 Jan 2024 13:53:17 +0800 Subject: [PATCH 10/10] =?UTF-8?q?fix:=20=E5=85=8D=E7=99=BB=E5=BD=95?= =?UTF-8?q?=E7=9C=8B1080p?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/video.dart | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/http/video.dart b/lib/http/video.dart index a6a23f83..923e93a2 100644 --- a/lib/http/video.dart +++ b/lib/http/video.dart @@ -140,6 +140,12 @@ class VideoHttp { data['bvid'] = bvid; } + // 免登录查看1080p + if (userInfoCache.get('userInfoCache') == null && + setting.get(SettingBoxKey.p1080, defaultValue: true)) { + data['try_look'] = 1; + } + Map params = await WbiSign().makSign({ ...data, 'fourk': 1, @@ -148,11 +154,6 @@ class VideoHttp { 'web_location': 1550101, }); - // 免登录查看1080p - if (userInfoCache.get('userInfoCache') == null && - setting.get(SettingBoxKey.p1080, defaultValue: true)) { - data['try_look'] = 1; - } try { var res = await Request().get(Api.videoUrl, data: params); if (res.data['code'] == 0) {