From d0590933e071d55563ed9a166f3bcd7245a6f791 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 17 Oct 2023 23:00:48 +0800 Subject: [PATCH 01/47] =?UTF-8?q?fix:=20=E9=95=BF=E6=8C=89=E5=8F=96?= =?UTF-8?q?=E6=B6=88=E5=90=8E=E5=BC=B9=E5=B9=95=E9=80=9F=E5=BA=A6=E5=BC=82?= =?UTF-8?q?=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/plugin/pl_player/controller.dart | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/lib/plugin/pl_player/controller.dart b/lib/plugin/pl_player/controller.dart index 2c116b16..07fabb76 100644 --- a/lib/plugin/pl_player/controller.dart +++ b/lib/plugin/pl_player/controller.dart @@ -214,6 +214,8 @@ class PlPlayerController { late double fontSizeVal; late double danmakuSpeedVal; late List speedsList; + // 缓存 + double? defaultDuration; // 播放顺序相关 PlayRepeat playRepeat = PlayRepeat.pause; @@ -573,8 +575,9 @@ class PlPlayerController { await _videoPlayerController?.setRate(speed); try { DanmakuOption currentOption = danmakuController!.option; + defaultDuration ??= currentOption.duration; DanmakuOption updatedOption = currentOption.copyWith( - duration: (currentOption.duration / speed) * playbackSpeed); + duration: (defaultDuration! / speed) * playbackSpeed); danmakuController!.updateOption(updatedOption); } catch (_) {} // fix 长按倍速后放开不恢复 @@ -582,16 +585,16 @@ class PlPlayerController { } /// 设置倍速 - Future togglePlaybackSpeed() async { - List allowedSpeeds = - PlaySpeed.values.map((e) => e.value).toList(); - int index = allowedSpeeds.indexOf(_playbackSpeed.value); - if (index < allowedSpeeds.length - 1) { - setPlaybackSpeed(allowedSpeeds[index + 1]); - } else { - setPlaybackSpeed(allowedSpeeds[0]); - } - } + // Future togglePlaybackSpeed() async { + // List allowedSpeeds = + // PlaySpeed.values.map((e) => e.value).toList(); + // int index = allowedSpeeds.indexOf(_playbackSpeed.value); + // if (index < allowedSpeeds.length - 1) { + // setPlaybackSpeed(allowedSpeeds[index + 1]); + // } else { + // setPlaybackSpeed(allowedSpeeds[0]); + // } + // } /// 播放视频 Future play({bool repeat = false, bool hideControls = true}) async { From a97f57642eb372eca2a4b48b600a795d612e7dc9 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 17 Oct 2023 23:20:27 +0800 Subject: [PATCH 02/47] =?UTF-8?q?fix:=20=E9=A6=96=E9=A1=B5=E5=8A=A8?= =?UTF-8?q?=E6=80=81=E8=B7=B3=E8=BD=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/common/widgets/video_card_v.dart | 64 ++++++++++++++++------------ 1 file changed, 36 insertions(+), 28 deletions(-) diff --git a/lib/common/widgets/video_card_v.dart b/lib/common/widgets/video_card_v.dart index 0c315f7b..39358fda 100644 --- a/lib/common/widgets/video_card_v.dart +++ b/lib/common/widgets/video_card_v.dart @@ -70,36 +70,44 @@ class VideoCardV extends StatelessWidget { break; // 动态 case 'picture': - String dynamicType = 'picture'; - String uri = videoItem.uri; - if (videoItem.uri.contains('bilibili://article/')) { - dynamicType = 'article'; - RegExp regex = RegExp(r'\d+'); - Match match = regex.firstMatch(videoItem.uri)!; - String matchedNumber = match.group(0)!; - videoItem.param = 'cv' + matchedNumber; - } - if (uri.startsWith('http')) { - String path = Uri.parse(uri).path; - if (isStringNumeric(path.split('/')[1])) { - // 请求接口 - var res = await DynamicsHttp.dynamicDetail(id: path.split('/')[1]); - if (res['status']) { - Get.toNamed('/dynamicDetail', arguments: { - 'item': res['data'], - 'floor': 1, - 'action': 'detail' - }); - } - return; + try { + String dynamicType = 'picture'; + String uri = videoItem.uri; + String id = ''; + if (videoItem.uri.startsWith('bilibili://article/')) { + // https://www.bilibili.com/read/cv27063554 + dynamicType = 'read'; + RegExp regex = RegExp(r'\d+'); + Match match = regex.firstMatch(videoItem.uri)!; + String matchedNumber = match.group(0)!; + videoItem.param = int.parse(matchedNumber); + id = 'cv${videoItem.param}'; } + if (uri.startsWith('http')) { + String path = Uri.parse(uri).path; + if (isStringNumeric(path.split('/')[1])) { + // 请求接口 + var res = + await DynamicsHttp.dynamicDetail(id: path.split('/')[1]); + if (res['status']) { + Get.toNamed('/dynamicDetail', arguments: { + 'item': res['data'], + 'floor': 1, + 'action': 'detail' + }); + } + return; + } + } + Get.toNamed('/htmlRender', parameters: { + 'url': uri, + 'title': videoItem.title, + 'id': id, + 'dynamicType': dynamicType + }); + } catch (err) { + SmartDialog.showToast(err.toString()); } - Get.toNamed('/htmlRender', parameters: { - 'url': uri, - 'title': videoItem.title, - 'id': videoItem.param.toString(), - 'dynamicType': dynamicType - }); break; default: SmartDialog.showToast(videoItem.goto); From 9744ec88a0aff9cf93321a0be080ef9aac047f62 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 21 Oct 2023 01:11:38 +0800 Subject: [PATCH 03/47] =?UTF-8?q?mod:=20CDN=E4=BC=98=E5=8C=96=20issues=20#?= =?UTF-8?q?70?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Release-iphonesimulator/FMDB.build/dgph | Bin 93 -> 0 bytes .../Flutter.build/dgph | Bin 93 -> 0 bytes .../Pods-Runner.build/dgph | Bin 93 -> 0 bytes .../ReachabilitySwift.build/dgph | Bin 93 -> 0 bytes .../connectivity_plus.build/dgph | Bin 93 -> 0 bytes .../device_info_plus.build/dgph | Bin 93 -> 0 bytes .../image_gallery_saver.build/dgph | Bin 93 -> 0 bytes .../media_kit_libs_ios_video.build/dgph | Bin 93 -> 0 bytes .../media_kit_native_event_loop.build/dgph | Bin 93 -> 0 bytes .../media_kit_video.build/dgph | Bin 93 -> 0 bytes .../package_info_plus.build/dgph | Bin 93 -> 0 bytes .../path_provider_foundation.build/dgph | Bin 93 -> 0 bytes .../permission_handler_apple.build/dgph | Bin 93 -> 0 bytes .../screen_brightness_ios.build/dgph | Bin 93 -> 0 bytes .../share_plus.build/dgph | Bin 93 -> 0 bytes .../sqflite.build/dgph | Bin 93 -> 0 bytes .../url_launcher_ios.build/dgph | Bin 93 -> 0 bytes .../volume_controller.build/dgph | Bin 93 -> 0 bytes .../wakelock_plus.build/dgph | Bin 93 -> 0 bytes .../webview_cookie_manager.build/dgph | Bin 93 -> 0 bytes .../webview_flutter_wkwebview.build/dgph | Bin 93 -> 0 bytes lib/pages/setting/play_setting.dart | 6 +++ lib/pages/video/detail/controller.dart | 46 +++++++++++------- lib/pages/video/detail/view.dart | 4 -- lib/plugin/pl_player/controller.dart | 22 ++++----- lib/utils/storage.dart | 1 + lib/utils/video_utils.dart | 36 ++++++++++++++ pubspec.lock | 8 +-- pubspec.yaml | 4 +- 29 files changed, 89 insertions(+), 38 deletions(-) delete mode 100644 android/build/ios/Pods.build/Release-iphonesimulator/FMDB.build/dgph delete mode 100644 android/build/ios/Pods.build/Release-iphonesimulator/Flutter.build/dgph delete mode 100644 android/build/ios/Pods.build/Release-iphonesimulator/Pods-Runner.build/dgph delete mode 100644 android/build/ios/Pods.build/Release-iphonesimulator/ReachabilitySwift.build/dgph delete mode 100644 android/build/ios/Pods.build/Release-iphonesimulator/connectivity_plus.build/dgph delete mode 100644 android/build/ios/Pods.build/Release-iphonesimulator/device_info_plus.build/dgph delete mode 100644 android/build/ios/Pods.build/Release-iphonesimulator/image_gallery_saver.build/dgph delete mode 100644 android/build/ios/Pods.build/Release-iphonesimulator/media_kit_libs_ios_video.build/dgph delete mode 100644 android/build/ios/Pods.build/Release-iphonesimulator/media_kit_native_event_loop.build/dgph delete mode 100644 android/build/ios/Pods.build/Release-iphonesimulator/media_kit_video.build/dgph delete mode 100644 android/build/ios/Pods.build/Release-iphonesimulator/package_info_plus.build/dgph delete mode 100644 android/build/ios/Pods.build/Release-iphonesimulator/path_provider_foundation.build/dgph delete mode 100644 android/build/ios/Pods.build/Release-iphonesimulator/permission_handler_apple.build/dgph delete mode 100644 android/build/ios/Pods.build/Release-iphonesimulator/screen_brightness_ios.build/dgph delete mode 100644 android/build/ios/Pods.build/Release-iphonesimulator/share_plus.build/dgph delete mode 100644 android/build/ios/Pods.build/Release-iphonesimulator/sqflite.build/dgph delete mode 100644 android/build/ios/Pods.build/Release-iphonesimulator/url_launcher_ios.build/dgph delete mode 100644 android/build/ios/Pods.build/Release-iphonesimulator/volume_controller.build/dgph delete mode 100644 android/build/ios/Pods.build/Release-iphonesimulator/wakelock_plus.build/dgph delete mode 100644 android/build/ios/Pods.build/Release-iphonesimulator/webview_cookie_manager.build/dgph delete mode 100644 android/build/ios/Pods.build/Release-iphonesimulator/webview_flutter_wkwebview.build/dgph create mode 100644 lib/utils/video_utils.dart diff --git a/android/build/ios/Pods.build/Release-iphonesimulator/FMDB.build/dgph b/android/build/ios/Pods.build/Release-iphonesimulator/FMDB.build/dgph deleted file mode 100644 index c431c0f700e593ccf8a91f54e0d541715cb02730..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 93 zcmZ>95AZP5Gce(HEGSYiG*>V(Ff!sWw6rp{urf5_U|?X>XJBLvElw>eW?*6}Dq>(} vcS$YIF3B%oU|~tlPf2B9u>SX``tG^Ua diff --git a/android/build/ios/Pods.build/Release-iphonesimulator/Flutter.build/dgph b/android/build/ios/Pods.build/Release-iphonesimulator/Flutter.build/dgph deleted file mode 100644 index c431c0f700e593ccf8a91f54e0d541715cb02730..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 93 zcmZ>95AZP5Gce(HEGSYiG*>V(Ff!sWw6rp{urf5_U|?X>XJBLvElw>eW?*6}Dq>(} vcS$YIF3B%oU|~tlPf2B9u>SX``tG^Ua diff --git a/android/build/ios/Pods.build/Release-iphonesimulator/Pods-Runner.build/dgph b/android/build/ios/Pods.build/Release-iphonesimulator/Pods-Runner.build/dgph deleted file mode 100644 index c431c0f700e593ccf8a91f54e0d541715cb02730..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 93 zcmZ>95AZP5Gce(HEGSYiG*>V(Ff!sWw6rp{urf5_U|?X>XJBLvElw>eW?*6}Dq>(} vcS$YIF3B%oU|~tlPf2B9u>SX``tG^Ua diff --git a/android/build/ios/Pods.build/Release-iphonesimulator/ReachabilitySwift.build/dgph b/android/build/ios/Pods.build/Release-iphonesimulator/ReachabilitySwift.build/dgph deleted file mode 100644 index c431c0f700e593ccf8a91f54e0d541715cb02730..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 93 zcmZ>95AZP5Gce(HEGSYiG*>V(Ff!sWw6rp{urf5_U|?X>XJBLvElw>eW?*6}Dq>(} vcS$YIF3B%oU|~tlPf2B9u>SX``tG^Ua diff --git a/android/build/ios/Pods.build/Release-iphonesimulator/connectivity_plus.build/dgph b/android/build/ios/Pods.build/Release-iphonesimulator/connectivity_plus.build/dgph deleted file mode 100644 index c431c0f700e593ccf8a91f54e0d541715cb02730..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 93 zcmZ>95AZP5Gce(HEGSYiG*>V(Ff!sWw6rp{urf5_U|?X>XJBLvElw>eW?*6}Dq>(} vcS$YIF3B%oU|~tlPf2B9u>SX``tG^Ua diff --git a/android/build/ios/Pods.build/Release-iphonesimulator/device_info_plus.build/dgph b/android/build/ios/Pods.build/Release-iphonesimulator/device_info_plus.build/dgph deleted file mode 100644 index c431c0f700e593ccf8a91f54e0d541715cb02730..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 93 zcmZ>95AZP5Gce(HEGSYiG*>V(Ff!sWw6rp{urf5_U|?X>XJBLvElw>eW?*6}Dq>(} vcS$YIF3B%oU|~tlPf2B9u>SX``tG^Ua diff --git a/android/build/ios/Pods.build/Release-iphonesimulator/image_gallery_saver.build/dgph b/android/build/ios/Pods.build/Release-iphonesimulator/image_gallery_saver.build/dgph deleted file mode 100644 index c431c0f700e593ccf8a91f54e0d541715cb02730..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 93 zcmZ>95AZP5Gce(HEGSYiG*>V(Ff!sWw6rp{urf5_U|?X>XJBLvElw>eW?*6}Dq>(} vcS$YIF3B%oU|~tlPf2B9u>SX``tG^Ua diff --git a/android/build/ios/Pods.build/Release-iphonesimulator/media_kit_libs_ios_video.build/dgph b/android/build/ios/Pods.build/Release-iphonesimulator/media_kit_libs_ios_video.build/dgph deleted file mode 100644 index c431c0f700e593ccf8a91f54e0d541715cb02730..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 93 zcmZ>95AZP5Gce(HEGSYiG*>V(Ff!sWw6rp{urf5_U|?X>XJBLvElw>eW?*6}Dq>(} vcS$YIF3B%oU|~tlPf2B9u>SX``tG^Ua diff --git a/android/build/ios/Pods.build/Release-iphonesimulator/media_kit_native_event_loop.build/dgph b/android/build/ios/Pods.build/Release-iphonesimulator/media_kit_native_event_loop.build/dgph deleted file mode 100644 index c431c0f700e593ccf8a91f54e0d541715cb02730..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 93 zcmZ>95AZP5Gce(HEGSYiG*>V(Ff!sWw6rp{urf5_U|?X>XJBLvElw>eW?*6}Dq>(} vcS$YIF3B%oU|~tlPf2B9u>SX``tG^Ua diff --git a/android/build/ios/Pods.build/Release-iphonesimulator/media_kit_video.build/dgph b/android/build/ios/Pods.build/Release-iphonesimulator/media_kit_video.build/dgph deleted file mode 100644 index c431c0f700e593ccf8a91f54e0d541715cb02730..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 93 zcmZ>95AZP5Gce(HEGSYiG*>V(Ff!sWw6rp{urf5_U|?X>XJBLvElw>eW?*6}Dq>(} vcS$YIF3B%oU|~tlPf2B9u>SX``tG^Ua diff --git a/android/build/ios/Pods.build/Release-iphonesimulator/package_info_plus.build/dgph b/android/build/ios/Pods.build/Release-iphonesimulator/package_info_plus.build/dgph deleted file mode 100644 index c431c0f700e593ccf8a91f54e0d541715cb02730..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 93 zcmZ>95AZP5Gce(HEGSYiG*>V(Ff!sWw6rp{urf5_U|?X>XJBLvElw>eW?*6}Dq>(} vcS$YIF3B%oU|~tlPf2B9u>SX``tG^Ua diff --git a/android/build/ios/Pods.build/Release-iphonesimulator/path_provider_foundation.build/dgph b/android/build/ios/Pods.build/Release-iphonesimulator/path_provider_foundation.build/dgph deleted file mode 100644 index c431c0f700e593ccf8a91f54e0d541715cb02730..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 93 zcmZ>95AZP5Gce(HEGSYiG*>V(Ff!sWw6rp{urf5_U|?X>XJBLvElw>eW?*6}Dq>(} vcS$YIF3B%oU|~tlPf2B9u>SX``tG^Ua diff --git a/android/build/ios/Pods.build/Release-iphonesimulator/permission_handler_apple.build/dgph b/android/build/ios/Pods.build/Release-iphonesimulator/permission_handler_apple.build/dgph deleted file mode 100644 index c431c0f700e593ccf8a91f54e0d541715cb02730..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 93 zcmZ>95AZP5Gce(HEGSYiG*>V(Ff!sWw6rp{urf5_U|?X>XJBLvElw>eW?*6}Dq>(} vcS$YIF3B%oU|~tlPf2B9u>SX``tG^Ua diff --git a/android/build/ios/Pods.build/Release-iphonesimulator/screen_brightness_ios.build/dgph b/android/build/ios/Pods.build/Release-iphonesimulator/screen_brightness_ios.build/dgph deleted file mode 100644 index c431c0f700e593ccf8a91f54e0d541715cb02730..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 93 zcmZ>95AZP5Gce(HEGSYiG*>V(Ff!sWw6rp{urf5_U|?X>XJBLvElw>eW?*6}Dq>(} vcS$YIF3B%oU|~tlPf2B9u>SX``tG^Ua diff --git a/android/build/ios/Pods.build/Release-iphonesimulator/share_plus.build/dgph b/android/build/ios/Pods.build/Release-iphonesimulator/share_plus.build/dgph deleted file mode 100644 index c431c0f700e593ccf8a91f54e0d541715cb02730..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 93 zcmZ>95AZP5Gce(HEGSYiG*>V(Ff!sWw6rp{urf5_U|?X>XJBLvElw>eW?*6}Dq>(} vcS$YIF3B%oU|~tlPf2B9u>SX``tG^Ua diff --git a/android/build/ios/Pods.build/Release-iphonesimulator/sqflite.build/dgph b/android/build/ios/Pods.build/Release-iphonesimulator/sqflite.build/dgph deleted file mode 100644 index c431c0f700e593ccf8a91f54e0d541715cb02730..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 93 zcmZ>95AZP5Gce(HEGSYiG*>V(Ff!sWw6rp{urf5_U|?X>XJBLvElw>eW?*6}Dq>(} vcS$YIF3B%oU|~tlPf2B9u>SX``tG^Ua diff --git a/android/build/ios/Pods.build/Release-iphonesimulator/url_launcher_ios.build/dgph b/android/build/ios/Pods.build/Release-iphonesimulator/url_launcher_ios.build/dgph deleted file mode 100644 index c431c0f700e593ccf8a91f54e0d541715cb02730..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 93 zcmZ>95AZP5Gce(HEGSYiG*>V(Ff!sWw6rp{urf5_U|?X>XJBLvElw>eW?*6}Dq>(} vcS$YIF3B%oU|~tlPf2B9u>SX``tG^Ua diff --git a/android/build/ios/Pods.build/Release-iphonesimulator/volume_controller.build/dgph b/android/build/ios/Pods.build/Release-iphonesimulator/volume_controller.build/dgph deleted file mode 100644 index c431c0f700e593ccf8a91f54e0d541715cb02730..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 93 zcmZ>95AZP5Gce(HEGSYiG*>V(Ff!sWw6rp{urf5_U|?X>XJBLvElw>eW?*6}Dq>(} vcS$YIF3B%oU|~tlPf2B9u>SX``tG^Ua diff --git a/android/build/ios/Pods.build/Release-iphonesimulator/wakelock_plus.build/dgph b/android/build/ios/Pods.build/Release-iphonesimulator/wakelock_plus.build/dgph deleted file mode 100644 index c431c0f700e593ccf8a91f54e0d541715cb02730..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 93 zcmZ>95AZP5Gce(HEGSYiG*>V(Ff!sWw6rp{urf5_U|?X>XJBLvElw>eW?*6}Dq>(} vcS$YIF3B%oU|~tlPf2B9u>SX``tG^Ua diff --git a/android/build/ios/Pods.build/Release-iphonesimulator/webview_cookie_manager.build/dgph b/android/build/ios/Pods.build/Release-iphonesimulator/webview_cookie_manager.build/dgph deleted file mode 100644 index c431c0f700e593ccf8a91f54e0d541715cb02730..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 93 zcmZ>95AZP5Gce(HEGSYiG*>V(Ff!sWw6rp{urf5_U|?X>XJBLvElw>eW?*6}Dq>(} vcS$YIF3B%oU|~tlPf2B9u>SX``tG^Ua diff --git a/android/build/ios/Pods.build/Release-iphonesimulator/webview_flutter_wkwebview.build/dgph b/android/build/ios/Pods.build/Release-iphonesimulator/webview_flutter_wkwebview.build/dgph deleted file mode 100644 index c431c0f700e593ccf8a91f54e0d541715cb02730..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 93 zcmZ>95AZP5Gce(HEGSYiG*>V(Ff!sWw6rp{urf5_U|?X>XJBLvElw>eW?*6}Dq>(} vcS$YIF3B%oU|~tlPf2B9u>SX``tG^Ua diff --git a/lib/pages/setting/play_setting.dart b/lib/pages/setting/play_setting.dart index cc3f3b88..6606f3c4 100644 --- a/lib/pages/setting/play_setting.dart +++ b/lib/pages/setting/play_setting.dart @@ -67,6 +67,12 @@ class _PlaySettingState extends State { setKey: SettingBoxKey.p1080, defaultVal: true, ), + const SetSwitchItem( + title: 'CDN优化', + subTitle: '使用优质CDN线路', + setKey: SettingBoxKey.enableCDN, + defaultVal: true, + ), const SetSwitchItem( title: '自动播放', subTitle: '进入详情页自动播放', diff --git a/lib/pages/video/detail/controller.dart b/lib/pages/video/detail/controller.dart index cbf08162..6fa0df6f 100644 --- a/lib/pages/video/detail/controller.dart +++ b/lib/pages/video/detail/controller.dart @@ -16,6 +16,7 @@ import 'package:pilipala/pages/video/detail/replyReply/index.dart'; import 'package:pilipala/plugin/pl_player/index.dart'; import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/utils.dart'; +import 'package:pilipala/utils/video_utils.dart'; import 'package:screen_brightness/screen_brightness.dart'; import 'widgets/header_control.dart'; @@ -83,6 +84,11 @@ class VideoDetailController extends GetxController Floating? floating; late PreferredSizeWidget headerControl; + late bool enableCDN; + late int? cacheVideoQa; + late String cacheDecode; + late int cacheAudioQa; + @override void onInit() { super.onInit(); @@ -120,6 +126,15 @@ class VideoDetailController extends GetxController videoDetailCtr: this, floating: floating, ); + // CDN优化 + enableCDN = setting.get(SettingBoxKey.enableCDN, defaultValue: true); + // 预设的画质 + cacheVideoQa = setting.get(SettingBoxKey.defaultVideoQa); + // 预设的解码格式 + cacheDecode = setting.get(SettingBoxKey.defaultDecode, + defaultValue: VideoDecodeFormats.values.last.code); + cacheAudioQa = setting.get(SettingBoxKey.defaultAudioQa, + defaultValue: AudioQuality.hiRes.code); } showReplyReplyPanel() { @@ -231,22 +246,19 @@ class VideoDetailController extends GetxController var result = await VideoHttp.videoUrl(cid: cid.value, bvid: bvid); if (result['status']) { data = result['data']; - List allVideosList = data.dash!.video!; - try { // 当前可播放的最高质量视频 int currentHighVideoQa = allVideosList.first.quality!.code; - // 使用预设的画质 | 当前可用的最高质量 - int cacheVideoQa = setting.get(SettingBoxKey.defaultVideoQa, - defaultValue: currentHighVideoQa); + // 预设的画质为null,则当前可用的最高质量 + cacheVideoQa ??= currentHighVideoQa; int resVideoQa = currentHighVideoQa; - if (cacheVideoQa <= currentHighVideoQa) { + if (cacheVideoQa! <= currentHighVideoQa) { // 如果预设的画质低于当前最高 List numbers = data.acceptQuality! .where((e) => e <= currentHighVideoQa) .toList(); - resVideoQa = Utils.findClosestNumber(cacheVideoQa, numbers); + resVideoQa = Utils.findClosestNumber(cacheVideoQa!, numbers); } currentVideoQa = VideoQualityCode.fromCode(resVideoQa)!; @@ -260,9 +272,7 @@ class VideoDetailController extends GetxController List supportDecodeFormats = supportFormats.firstWhere((e) => e.quality == resVideoQa).codecs!; // 默认从设置中取AVC - currentDecodeFormats = VideoDecodeFormatsCode.fromString(setting.get( - SettingBoxKey.defaultDecode, - defaultValue: VideoDecodeFormats.values.last.code))!; + currentDecodeFormats = VideoDecodeFormatsCode.fromString(cacheDecode)!; try { // 当前视频没有对应格式返回第一个 bool flag = false; @@ -285,7 +295,9 @@ class VideoDetailController extends GetxController } catch (_) { firstVideo = videosList.first; } - videoUrl = firstVideo.baseUrl!; + videoUrl = enableCDN + ? VideoUtils.getCdnUrl(firstVideo) + : (firstVideo.backupUrl ?? firstVideo.baseUrl!); } catch (err) { SmartDialog.showToast('firstVideo error: $err'); } @@ -295,8 +307,6 @@ class VideoDetailController extends GetxController List audiosList = data.dash!.audio!; try { - int resultAudioQa = setting.get(SettingBoxKey.defaultAudioQa, - defaultValue: AudioQuality.hiRes.code); if (data.dash!.dolby?.audio?.isNotEmpty == true) { // 杜比 audiosList.insert(0, data.dash!.dolby!.audio!.first); @@ -309,9 +319,9 @@ class VideoDetailController extends GetxController if (audiosList.isNotEmpty) { List numbers = audiosList.map((map) => map.id!).toList(); - int closestNumber = Utils.findClosestNumber(resultAudioQa, numbers); - if (!numbers.contains(resultAudioQa) && - numbers.any((e) => e > resultAudioQa)) { + int closestNumber = Utils.findClosestNumber(cacheAudioQa, numbers); + if (!numbers.contains(cacheAudioQa) && + numbers.any((e) => e > cacheAudioQa)) { closestNumber = 30280; } firstAudio = audiosList.firstWhere((e) => e.id == closestNumber); @@ -323,7 +333,9 @@ class VideoDetailController extends GetxController SmartDialog.showToast('firstAudio error: $err'); } - audioUrl = firstAudio.baseUrl ?? ''; + audioUrl = enableCDN + ? VideoUtils.getCdnUrl(firstAudio) + : (firstAudio.backupUrl ?? firstAudio.baseUrl!); // if (firstAudio.id != null) { currentAudioQa = AudioQualityCode.fromCode(firstAudio.id!)!; diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 568a4bee..0debd259 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -3,18 +3,15 @@ import 'dart:io'; import 'package:extended_nested_scroll_view/extended_nested_scroll_view.dart'; import 'package:floating/floating.dart'; -import 'package:flutter/services.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:flutter/material.dart'; import 'package:hive/hive.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; -import 'package:pilipala/common/widgets/sliver_header.dart'; import 'package:pilipala/http/user.dart'; import 'package:pilipala/models/common/search_type.dart'; import 'package:pilipala/pages/bangumi/introduction/index.dart'; import 'package:pilipala/pages/danmaku/view.dart'; -import 'package:pilipala/pages/video/detail/introduction/widgets/menu_row.dart'; import 'package:pilipala/pages/video/detail/reply/index.dart'; import 'package:pilipala/pages/video/detail/controller.dart'; import 'package:pilipala/pages/video/detail/introduction/index.dart'; @@ -23,7 +20,6 @@ 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 'widgets/app_bar.dart'; import 'widgets/header_control.dart'; class VideoDetailPage extends StatefulWidget { diff --git a/lib/plugin/pl_player/controller.dart b/lib/plugin/pl_player/controller.dart index 2c116b16..537dde58 100644 --- a/lib/plugin/pl_player/controller.dart +++ b/lib/plugin/pl_player/controller.dart @@ -332,12 +332,11 @@ class PlPlayerController { // 数据加载完成 dataStatus.status.value = DataStatus.loaded; - await _initializePlayer(seekTo: seekTo); - // listen the video player events if (!_listenersInitialized) { startListeners(); } + await _initializePlayer(seekTo: seekTo); bool autoEnterFullcreen = setting.get(SettingBoxKey.enableAutoEnter, defaultValue: false); if (autoEnterFullcreen && _isFirstTime) { @@ -407,6 +406,7 @@ class PlPlayerController { player, configuration: VideoControllerConfiguration( enableHardwareAcceleration: enableHA, + androidAttachSurfaceAfterVideoParameters: false, ), ); @@ -542,6 +542,7 @@ class PlPlayerController { } _position.value = position; _heartDuration = position.inSeconds; + print('seek duration: $duration'); if (duration.value.inSeconds != 0) { if (type != 'slider') { /// 拖动进度条调节时,不等待第一帧,防止抖动 @@ -552,17 +553,19 @@ class PlPlayerController { // play(); // } } else { + print('seek duration else'); _timerForSeek?.cancel(); _timerForSeek = Timer.periodic(const Duration(milliseconds: 200), (Timer t) async { //_timerForSeek = null; if (duration.value.inSeconds != 0) { + await _videoPlayerController!.stream.buffer.first; await _videoPlayerController?.seek(position); - // if (playerStatus.stopped) { - // play(); - // } + if (playerStatus.status.value == PlayerStatus.paused) { + play(); + } t.cancel(); - //_timerForSeek = null; + _timerForSeek = null; } }); } @@ -595,6 +598,8 @@ class PlPlayerController { /// 播放视频 Future play({bool repeat = false, bool hideControls = true}) async { + // 播放时自动隐藏控制条 + controls = !hideControls; // repeat为true,将从头播放 if (repeat) { await seekTo(Duration.zero); @@ -606,11 +611,6 @@ class PlPlayerController { playerStatus.status.value = PlayerStatus.playing; // screenManager.setOverlays(false); - - // 播放时自动隐藏控制条 - if (hideControls) { - _hideTaskControls(); - } } /// 暂停播放 diff --git a/lib/utils/storage.dart b/lib/utils/storage.dart index 8d31726c..4689d7f2 100644 --- a/lib/utils/storage.dart +++ b/lib/utils/storage.dart @@ -105,6 +105,7 @@ class SettingBoxKey { static const String enableAutoEnter = 'enableAutoEnter'; static const String enableAutoExit = 'enableAutoExit'; static const String p1080 = 'p1080'; + static const String enableCDN = 'enableCDN'; // youtube 双击快进快退 static const String enableQuickDouble = 'enableQuickDouble'; diff --git a/lib/utils/video_utils.dart b/lib/utils/video_utils.dart new file mode 100644 index 00000000..88faba3c --- /dev/null +++ b/lib/utils/video_utils.dart @@ -0,0 +1,36 @@ +import 'package:pilipala/models/video/play/url.dart'; + +class VideoUtils { + static String getCdnUrl(dynamic item) { + var backupUrl = ""; + var videoUrl = ""; + + /// 先获取backupUrl 一般是upgcxcode地址 播放更稳定 + if (item is VideoItem) { + backupUrl = item.backupUrl ?? ""; + videoUrl = backupUrl.contains("http") ? backupUrl : (item.baseUrl ?? ""); + } else if (item is AudioItem) { + backupUrl = item.backupUrl ?? ""; + videoUrl = backupUrl.contains("http") ? backupUrl : (item.baseUrl ?? ""); + } else { + return ""; + } + + /// issues #70 + if (videoUrl.contains(".mcdn.bilivideo") || + videoUrl.contains("/upgcxcode/")) { + //CDN列表 + var cdnList = { + 'ali': 'upos-sz-mirrorali.bilivideo.com', + 'cos': 'upos-sz-mirrorcos.bilivideo.com', + 'hw': 'upos-sz-mirrorhw.bilivideo.com', + }; + //取一个CDN + var cdn = cdnList['ali'] ?? ""; + var reg = RegExp(r'(http|https)://(.*?)/upgcxcode/'); + videoUrl = videoUrl.replaceAll(reg, "https://$cdn/upgcxcode/"); + } + + return videoUrl; + } +} diff --git a/pubspec.lock b/pubspec.lock index 55c4c0a6..8354dd69 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -761,10 +761,10 @@ packages: dependency: "direct main" description: name: media_kit - sha256: d652c2bdb0cd876bf1046e24d0b614651fefe59f7c3a2d9b7ed57217b9e7db94 + sha256: "3dffc6d0c19117d51fbc42a7f89612e0595665800a596289ab7a80bdd93e0ad1" url: "https://pub.dev" source: hosted - version: "1.1.8+1" + version: "1.1.9" media_kit_libs_android_video: dependency: transitive description: @@ -825,10 +825,10 @@ packages: dependency: "direct main" description: name: media_kit_video - sha256: b1a427f0540c5f052dfab73e4b76a5eb8efa7ebb5d83179cb23fc3932afc315a + sha256: b8df9cf97aba1861af83b00ac16f5cac536debe0781a934a554b77c157a8f7e8 url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.2.2" meta: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index e2f00f2c..2b8fc115 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -84,8 +84,8 @@ dependencies: crypto: ^3.0.3 # 视频播放器 - media_kit: ^1.1.8 # Primary package. - media_kit_video: ^1.2.1 # For video rendering. + media_kit: ^1.1.9 # Primary package. + media_kit_video: ^1.2.2 # For video rendering. media_kit_libs_video: ^1.0.3 # 音量、亮度、屏幕控制 From b71558173e1667c2498cfb02dd3602162af77f53 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 21 Oct 2023 13:00:52 +0800 Subject: [PATCH 04/47] =?UTF-8?q?mod:=20=E5=8F=96=E6=B6=88=E8=87=AA?= =?UTF-8?q?=E5=8A=A8=E5=A4=87=E4=BB=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- android/app/src/main/AndroidManifest.xml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 55667534..dc980d0f 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -39,7 +39,11 @@ android:label="PiliPala" android:name="${applicationName}" android:icon="@mipmap/ic_launcher" - android:enableOnBackInvokedCallback="true"> + xmlns:tools="http://schemas.android.com/tools" + android:enableOnBackInvokedCallback="true" + android:allowBackup="false" + android:fullBackupContent="false" + tools:replace="android:allowBackup"> Date: Sat, 21 Oct 2023 21:00:22 +0800 Subject: [PATCH 05/47] =?UTF-8?q?fix:=20=E5=B0=9D=E8=AF=95=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E8=AE=B0=E5=BF=86=E6=92=AD=E6=94=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/plugin/pl_player/controller.dart | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/lib/plugin/pl_player/controller.dart b/lib/plugin/pl_player/controller.dart index 537dde58..c44dd99b 100644 --- a/lib/plugin/pl_player/controller.dart +++ b/lib/plugin/pl_player/controller.dart @@ -437,11 +437,6 @@ class PlPlayerController { Future _initializePlayer({ Duration seekTo = Duration.zero, }) async { - // 跳转播放 - if (seekTo != Duration.zero) { - await this.seekTo(seekTo); - } - // 设置倍速 if (_playbackSpeed.value != 1.0) { await setPlaybackSpeed(_playbackSpeed.value); @@ -453,6 +448,11 @@ class PlPlayerController { // await setLooping(_looping); // } + // 跳转播放 + if (seekTo != Duration.zero) { + await this.seekTo(seekTo); + } + // 自动播放 if (_autoPlay) { await play(); @@ -542,7 +542,6 @@ class PlPlayerController { } _position.value = position; _heartDuration = position.inSeconds; - print('seek duration: $duration'); if (duration.value.inSeconds != 0) { if (type != 'slider') { /// 拖动进度条调节时,不等待第一帧,防止抖动 @@ -561,9 +560,9 @@ class PlPlayerController { if (duration.value.inSeconds != 0) { await _videoPlayerController!.stream.buffer.first; await _videoPlayerController?.seek(position); - if (playerStatus.status.value == PlayerStatus.paused) { - play(); - } + // if (playerStatus.status.value == PlayerStatus.paused) { + // play(); + // } t.cancel(); _timerForSeek = null; } From b4cc542a4d8f040e857906ca52ce4569d88a1a4e Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 21 Oct 2023 21:02:39 +0800 Subject: [PATCH 06/47] =?UTF-8?q?mod:=20=E6=92=AD=E6=94=BE=E5=99=A8?= =?UTF-8?q?=E6=8F=92=E4=BB=B6=E5=8D=87=E7=BA=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pubspec.lock | 16 ++++++++-------- pubspec.yaml | 6 +++--- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index 8354dd69..bc6d573d 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -761,18 +761,18 @@ packages: dependency: "direct main" description: name: media_kit - sha256: "3dffc6d0c19117d51fbc42a7f89612e0595665800a596289ab7a80bdd93e0ad1" + sha256: "3289062540e3b8b9746e5c50d95bd78a9289826b7227e253dff806d002b9e67a" url: "https://pub.dev" source: hosted - version: "1.1.9" + version: "1.1.10+1" media_kit_libs_android_video: dependency: transitive description: name: media_kit_libs_android_video - sha256: a7ef60926ac528e2fabe9ee7084e648e385422a881ba914c978a7a81e6595dee + sha256: "9dd8012572e4aff47516e55f2597998f0a378e3d588d0fad0ca1f11a53ae090c" url: "https://pub.dev" source: hosted - version: "1.3.5" + version: "1.3.6" media_kit_libs_ios_video: dependency: transitive description: @@ -801,10 +801,10 @@ packages: dependency: "direct main" description: name: media_kit_libs_video - sha256: f130964bd4c0907d0af645ba03c8080a914776bfd2e23761a5e22ac3c0c0906a + sha256: "3688e0c31482074578652bf038ce6301a5d21e1eda6b54fc3117ffeb4bdba067" url: "https://pub.dev" source: hosted - version: "1.0.3" + version: "1.0.4" media_kit_libs_windows_video: dependency: transitive description: @@ -825,10 +825,10 @@ packages: dependency: "direct main" description: name: media_kit_video - sha256: b8df9cf97aba1861af83b00ac16f5cac536debe0781a934a554b77c157a8f7e8 + sha256: c048d11a19e379aebbe810647636e3fc6d18374637e2ae12def4ff8a4b99a882 url: "https://pub.dev" source: hosted - version: "1.2.2" + version: "1.2.4" meta: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 2b8fc115..b522bb5c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -84,9 +84,9 @@ dependencies: crypto: ^3.0.3 # 视频播放器 - media_kit: ^1.1.9 # Primary package. - media_kit_video: ^1.2.2 # For video rendering. - media_kit_libs_video: ^1.0.3 + media_kit: ^1.1.10 # Primary package. + media_kit_video: ^1.2.4 # For video rendering. + media_kit_libs_video: ^1.0.4 # 音量、亮度、屏幕控制 flutter_volume_controller: ^1.2.7 From eec052c47e5c2e66144d74091e25fb204d600d5c Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 21 Oct 2023 21:37:22 +0800 Subject: [PATCH 07/47] =?UTF-8?q?mod:=20=E9=9F=B3=E9=87=8F=E4=BA=AE?= =?UTF-8?q?=E5=BA=A6=E6=BB=91=E5=8A=A8=E6=8E=A7=E5=88=B6=E6=9D=A1=E8=B0=83?= =?UTF-8?q?=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/plugin/pl_player/view.dart | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/lib/plugin/pl_player/view.dart b/lib/plugin/pl_player/view.dart index 9605daf1..7fe72dd6 100644 --- a/lib/plugin/pl_player/view.dart +++ b/lib/plugin/pl_player/view.dart @@ -74,6 +74,7 @@ class _PLVideoPlayerState extends State late int defaultBtmProgressBehavior; late bool enableQuickDouble; late bool enableBackgroundPlay; + late double screenWidth; void onDoubleTapSeekBackward() { _ctr.onDoubleTapSeekBackward(); @@ -116,6 +117,7 @@ class _PLVideoPlayerState extends State @override void initState() { super.initState(); + screenWidth = Get.size.width; animationController = AnimationController( vsync: this, duration: const Duration(milliseconds: 300)); videoController = widget.controller.videoController!; @@ -128,7 +130,6 @@ class _PLVideoPlayerState extends State setting.get(SettingBoxKey.enableQuickDouble, defaultValue: true); enableBackgroundPlay = setting.get(SettingBoxKey.enableBackgroundPlay, defaultValue: false); - Future.microtask(() async { try { FlutterVolumeController.showSystemUI = true; @@ -508,7 +509,11 @@ class _PLVideoPlayerState extends State } if (tapPosition < sectionWidth) { // 左边区域 👈 - final brightness = _ctr.brightnessValue.value - delta / 100.0; + double level = (_.isFullScreen.value + ? Get.size.height + : screenWidth * 9 / 16) * + 3; + final brightness = _ctr.brightnessValue.value - delta / level; final result = brightness.clamp(0.0, 1.0); setBrightness(result); } else if (tapPosition < sectionWidth * 2) { @@ -531,7 +536,11 @@ class _PLVideoPlayerState extends State _distance = dy; } else { // 右边区域 👈 - final volume = _ctr.volumeValue.value - delta / 100.0; + double level = (_.isFullScreen.value + ? Get.size.height + : screenWidth * 9 / 16) * + 3; + final volume = _ctr.volumeValue.value - delta / level; final result = volume.clamp(0.0, 1.0); setVolume(result); } From f30fb7a71c67cfe2b0c9b092e2b79b2a90ab7824 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 21 Oct 2023 21:50:38 +0800 Subject: [PATCH 08/47] =?UTF-8?q?mod:=20=E8=A7=86=E9=A2=91=E8=AF=A6?= =?UTF-8?q?=E6=83=85=E9=A1=B5=E6=A0=B7=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/introduction/view.dart | 4 ++-- lib/pages/video/detail/widgets/header_control.dart | 11 ++++++----- lib/plugin/pl_player/widgets/bottom_control.dart | 4 ++-- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index d07b30fd..ef8ab928 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -248,8 +248,8 @@ class _VideoInfoState extends State with TickerProviderStateMixin { ? widget.videoDetail!.title : videoItem['title'], style: const TextStyle( - fontSize: 16, - fontWeight: FontWeight.w500, + fontSize: 17, + fontWeight: FontWeight.bold, ), maxLines: 2, overflow: TextOverflow.ellipsis, diff --git a/lib/pages/video/detail/widgets/header_control.dart b/lib/pages/video/detail/widgets/header_control.dart index e1e94bd9..85adfc25 100644 --- a/lib/pages/video/detail/widgets/header_control.dart +++ b/lib/pages/video/detail/widgets/header_control.dart @@ -43,6 +43,7 @@ class _HeaderControlState extends State { Box localCache = GStrorage.localCache; Box videoStorage = GStrorage.video; late List speedsList; + double buttonSpace = 8; @override void initState() { @@ -276,7 +277,7 @@ class _HeaderControlState extends State { mainAxisAlignment: MainAxisAlignment.center, children: [ Text('选择画质', style: titleStyle), - const SizedBox(width: 4), + SizedBox(width: buttonSpace), Icon( Icons.info_outline, size: 16, @@ -793,7 +794,7 @@ class _HeaderControlState extends State { ), fuc: () => Get.back(), ), - const SizedBox(width: 4), + SizedBox(width: buttonSpace), ComBtn( icon: const Icon( FontAwesomeIcons.house, @@ -838,7 +839,7 @@ class _HeaderControlState extends State { ), ), ), - const SizedBox(width: 4), + SizedBox(width: buttonSpace), if (Platform.isAndroid) ...[ SizedBox( width: 34, @@ -870,7 +871,7 @@ class _HeaderControlState extends State { ), ), ), - const SizedBox(width: 4), + SizedBox(width: buttonSpace), ], Obx( () => SizedBox( @@ -888,7 +889,7 @@ class _HeaderControlState extends State { ), ), ), - const SizedBox(width: 4), + SizedBox(width: buttonSpace), ComBtn( icon: const Icon( FontAwesomeIcons.sliders, diff --git a/lib/plugin/pl_player/widgets/bottom_control.dart b/lib/plugin/pl_player/widgets/bottom_control.dart index fb0f42b6..5a1d3cf3 100644 --- a/lib/plugin/pl_player/widgets/bottom_control.dart +++ b/lib/plugin/pl_player/widgets/bottom_control.dart @@ -123,7 +123,7 @@ class BottomControl extends StatelessWidget implements PreferredSizeWidget { ), fuc: () => _.toggleVideoFit(), ), - const SizedBox(width: 4), + const SizedBox(width: 10), // 全屏 Obx( () => ComBtn( @@ -139,7 +139,7 @@ class BottomControl extends StatelessWidget implements PreferredSizeWidget { ), ], ), - const SizedBox(height: 10), + const SizedBox(height: 12), ], ), ); From aa95d9020d5ca9ae1ba9b9710565ee79d1a4528d Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 21 Oct 2023 22:15:18 +0800 Subject: [PATCH 09/47] =?UTF-8?q?mod:=20=E7=94=BB=E9=9D=A2=E6=AF=94?= =?UTF-8?q?=E4=BE=8B=E8=B0=83=E6=95=B4=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/plugin/pl_player/controller.dart | 33 +++++++++---------- .../pl_player/widgets/bottom_control.dart | 21 ++++++++---- lib/utils/storage.dart | 2 ++ 3 files changed, 32 insertions(+), 24 deletions(-) diff --git a/lib/plugin/pl_player/controller.dart b/lib/plugin/pl_player/controller.dart index c44dd99b..5bc3fc32 100644 --- a/lib/plugin/pl_player/controller.dart +++ b/lib/plugin/pl_player/controller.dart @@ -73,6 +73,7 @@ class PlPlayerController { Rx videoFitChanged = false.obs; final Rx _videoFit = Rx(BoxFit.contain); + final Rx _videoFitDesc = Rx('包含'); /// // ignore: prefer_final_fields @@ -183,6 +184,7 @@ class PlPlayerController { /// 视频比例 Rx get videoFit => _videoFit; + Rx get videoFitDEsc => _videoFitDesc; /// 是否长按倍速 Rx get doubleSpeedStatus => _doubleSpeedStatus; @@ -443,7 +445,7 @@ class PlPlayerController { } else { await setPlaybackSpeed(1.0); } - + getVideoFit(); // if (_looping) { // await setLooping(_looping); // } @@ -731,37 +733,34 @@ class PlPlayerController { if (attrs.indexOf(_videoFit.value) < attrs.length - 1) { int index = attrs.indexOf(_videoFit.value); _videoFit.value = attrs[index + 1]; - print(videoFitType[index + 1]['desc']); + _videoFitDesc.value = videoFitType[index + 1]['desc']; SmartDialog.showToast(videoFitType[index + 1]['desc']); } else { // 默认 contain _videoFit.value = videoFitType.first['attr']; + _videoFitDesc.value = videoFitType.first['desc']; SmartDialog.showToast(videoFitType.first['desc']); } videoFitChangedTimer = Timer(const Duration(seconds: 1), () { videoFitChangedTimer = null; videoFitChanged.value = false; }); - print(_videoFit.value); - } - - /// Change Video Fit accordingly - void onVideoFitChange(BoxFit fit) { - _videoFit.value = fit; + setVideoFit(); } /// 缓存fit - // Future setVideoFit() async { - // videoStorage.put(VideoBoxKey.videoBrightness, _videoFit.value.name); - // } + Future setVideoFit() async { + List attrs = videoFitType.map((e) => e['attr']).toList(); + int index = attrs.indexOf(_videoFit.value); + videoStorage.put(VideoBoxKey.cacheVideoFit, index); + } /// 读取fit - // Future getVideoFit() async { - // String fitValue = - // videoStorage.get(VideoBoxKey.videoBrightness, defaultValue: 'contain'); - // _videoFit.value = videoFitType - // .firstWhere((element) => element['attr'] == fitValue)['attr']; - // } + Future getVideoFit() async { + int fitValue = videoStorage.get(VideoBoxKey.cacheVideoFit, defaultValue: 0); + _videoFit.value = videoFitType[fitValue]['attr']; + _videoFitDesc.value = videoFitType[fitValue]['desc']; + } /// 读取亮度 // Future getVideoBrightness() async { diff --git a/lib/plugin/pl_player/widgets/bottom_control.dart b/lib/plugin/pl_player/widgets/bottom_control.dart index fb0f42b6..dbd93b08 100644 --- a/lib/plugin/pl_player/widgets/bottom_control.dart +++ b/lib/plugin/pl_player/widgets/bottom_control.dart @@ -115,13 +115,20 @@ class BottomControl extends StatelessWidget implements PreferredSizeWidget { // ), // ), // ), - ComBtn( - icon: const Icon( - Icons.settings_overscan_outlined, - size: 18, - color: Colors.white, + SizedBox( + height: 30, + child: TextButton( + onPressed: () => _.toggleVideoFit(), + style: ButtonStyle( + padding: MaterialStateProperty.all(EdgeInsets.zero), + ), + child: Obx( + () => Text( + _.videoFitDEsc.value, + style: const TextStyle(color: Colors.white, fontSize: 13), + ), + ), ), - fuc: () => _.toggleVideoFit(), ), const SizedBox(width: 4), // 全屏 @@ -139,7 +146,7 @@ class BottomControl extends StatelessWidget implements PreferredSizeWidget { ), ], ), - const SizedBox(height: 10), + const SizedBox(height: 8), ], ), ); diff --git a/lib/utils/storage.dart b/lib/utils/storage.dart index 4689d7f2..ea30253b 100644 --- a/lib/utils/storage.dart +++ b/lib/utils/storage.dart @@ -170,4 +170,6 @@ class VideoBoxKey { static const String longPressSpeedDefault = 'longPressSpeedDefault'; // 自定义倍速集合 static const String customSpeedsList = 'customSpeedsList'; + // 画面填充比例 + static const String cacheVideoFit = 'cacheVideoFit'; } From 88e6eb607ce926ab5fa367a9b7e52fa1540b5e23 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 21 Oct 2023 22:40:05 +0800 Subject: [PATCH 10/47] =?UTF-8?q?mod:=20=E6=92=AD=E6=94=BE=E9=80=9F?= =?UTF-8?q?=E5=BA=A6=E8=B0=83=E8=8A=82=E4=BC=98=E5=8C=96=20issues=20#201?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../video/detail/widgets/header_control.dart | 18 +++++++++++++----- lib/plugin/pl_player/controller.dart | 13 ++++++++++++- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/lib/pages/video/detail/widgets/header_control.dart b/lib/pages/video/detail/widgets/header_control.dart index 85adfc25..a6f98d4a 100644 --- a/lib/pages/video/detail/widgets/header_control.dart +++ b/lib/pages/video/detail/widgets/header_control.dart @@ -197,12 +197,20 @@ class _HeaderControlState extends State { for (var i in speedsList) ...[ if (i == currentSpeed) ...[ FilledButton( - onPressed: () => {setState(() => currentSpeed = i)}, + onPressed: () async { + // setState(() => currentSpeed = i), + await widget.controller!.setPlaybackSpeed(i); + SmartDialog.dismiss(); + }, child: Text(i.toString()), ), ] else ...[ FilledButton.tonal( - onPressed: () => {setState(() => currentSpeed = i)}, + onPressed: () async { + // setState(() => currentSpeed = i), + await widget.controller!.setPlaybackSpeed(i); + SmartDialog.dismiss(); + }, child: Text(i.toString()), ), ] @@ -220,10 +228,10 @@ class _HeaderControlState extends State { ), TextButton( onPressed: () async { - await SmartDialog.dismiss(); - widget.controller!.setPlaybackSpeed(currentSpeed); + await widget.controller!.setDefaultSpeed(); + SmartDialog.dismiss(); }, - child: const Text('确定'), + child: const Text('默认速度'), ), ], ); diff --git a/lib/plugin/pl_player/controller.dart b/lib/plugin/pl_player/controller.dart index dd097212..c5d515a2 100644 --- a/lib/plugin/pl_player/controller.dart +++ b/lib/plugin/pl_player/controller.dart @@ -585,7 +585,17 @@ class PlPlayerController { danmakuController!.updateOption(updatedOption); } catch (_) {} // fix 长按倍速后放开不恢复 - // _playbackSpeed.value = speed; + if (!doubleSpeedStatus.value) { + _playbackSpeed.value = speed; + } + } + + // 还原默认速度 + Future setDefaultSpeed() async { + double speed = + videoStorage.get(VideoBoxKey.playSpeedDefault, defaultValue: 1.0); + await _videoPlayerController?.setRate(speed); + _playbackSpeed.value = speed; } /// 设置倍速 @@ -796,6 +806,7 @@ class PlPlayerController { if (val) { setPlaybackSpeed(longPressSpeed); } else { + print(playbackSpeed); setPlaybackSpeed(playbackSpeed); } } From 5c6b8624d71821c67142b5a76507204cffd9ec53 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 21 Oct 2023 23:01:24 +0800 Subject: [PATCH 11/47] =?UTF-8?q?feat:=20=E5=B7=B2=E5=85=B3=E6=B3=A8up?= =?UTF-8?q?=E5=88=86=E7=BB=84=20issues=20#203?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/follow/widgets/follow_item.dart | 25 ++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/lib/pages/follow/widgets/follow_item.dart b/lib/pages/follow/widgets/follow_item.dart index cae72f4c..d367b8d4 100644 --- a/lib/pages/follow/widgets/follow_item.dart +++ b/lib/pages/follow/widgets/follow_item.dart @@ -1,7 +1,9 @@ import 'package:flutter/material.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; import 'package:pilipala/models/follow/result.dart'; +import 'package:pilipala/pages/video/detail/introduction/widgets/group_panel.dart'; import 'package:pilipala/utils/feed_back.dart'; import 'package:pilipala/utils/utils.dart'; @@ -39,7 +41,28 @@ class FollowItem extends StatelessWidget { overflow: TextOverflow.ellipsis, ), dense: true, - trailing: const SizedBox(width: 6), + trailing: SizedBox( + height: 34, + child: TextButton( + onPressed: () async { + await Get.bottomSheet( + GroupPanel(mid: item.mid!), + isScrollControlled: true, + ); + SmartDialog.showToast('重进页面查看效果'); + }, + style: TextButton.styleFrom( + padding: const EdgeInsets.fromLTRB(15, 0, 15, 0), + foregroundColor: Theme.of(context).colorScheme.outline, + backgroundColor: + Theme.of(context).colorScheme.onInverseSurface, // 设置按钮背景色 + ), + child: const Text( + '已关注', + style: TextStyle(fontSize: 12), + ), + ), + ), ); } } From ab7dd149d3686812bc4667117a4fb704755afec0 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 22 Oct 2023 09:43:27 +0800 Subject: [PATCH 12/47] =?UTF-8?q?mod:=20=E5=88=87=E6=8D=A2=E8=87=B3?= =?UTF-8?q?=E5=89=8D=E5=8F=B0=E7=BB=A7=E7=BB=AD=E6=92=AD=E6=94=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/plugin/pl_player/view.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/plugin/pl_player/view.dart b/lib/plugin/pl_player/view.dart index 7fe72dd6..8b8b5780 100644 --- a/lib/plugin/pl_player/view.dart +++ b/lib/plugin/pl_player/view.dart @@ -218,6 +218,7 @@ class _PLVideoPlayerState extends State controller: videoController, controls: NoVideoControls, pauseUponEnteringBackgroundMode: !enableBackgroundPlay, + resumeUponEnteringForegroundMode: true, subtitleViewConfiguration: SubtitleViewConfiguration( style: subTitleStyle, textAlign: TextAlign.center, From 9fd5193259efce8b9dda4e62bcee1f21516bec85 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 22 Oct 2023 10:21:00 +0800 Subject: [PATCH 13/47] =?UTF-8?q?fix:=20=E5=8A=A8=E6=80=81=E9=87=8D?= =?UTF-8?q?=E5=A4=8D=E5=8A=A0=E8=BD=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/member/dynamic/controller.dart | 5 +++++ lib/pages/member/dynamic/view.dart | 13 ++++++++----- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/lib/pages/member/dynamic/controller.dart b/lib/pages/member/dynamic/controller.dart index 480afd09..056240ad 100644 --- a/lib/pages/member/dynamic/controller.dart +++ b/lib/pages/member/dynamic/controller.dart @@ -6,6 +6,7 @@ class MemberDynamicPanelController extends GetxController { int? mid; String offset = ''; int count = 0; + bool hasMore = true; @override void onInit() { @@ -14,12 +15,16 @@ class MemberDynamicPanelController extends GetxController { } Future getMemberDynamic() async { + if (!hasMore) { + return {'status': false}; + } var res = await MemberHttp.memberDynamic( offset: offset, mid: mid, ); if (res['status']) { offset = res['data'].offset; + hasMore = res['data'].hasMore; } return res; } diff --git a/lib/pages/member/dynamic/view.dart b/lib/pages/member/dynamic/view.dart index 6ceb3d14..15d7376e 100644 --- a/lib/pages/member/dynamic/view.dart +++ b/lib/pages/member/dynamic/view.dart @@ -139,11 +139,14 @@ class LoadMoreListSource extends LoadingMoreBase { if (res['status']) { addAll(res['data'].items); } - if (res['data'].hasMore) { - isSuccess = true; - } else { - isSuccess = false; - } + try { + if (res['data'].hasMore) { + isSuccess = true; + } else { + isSuccess = false; + } + } catch (_) {} + return isSuccess; } } From 445a37d305e925adb2bd22e2c674d692e8d417a2 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 22 Oct 2023 10:32:03 +0800 Subject: [PATCH 14/47] =?UTF-8?q?mod:=20=E9=A6=96=E9=A1=B5=E5=A4=9A?= =?UTF-8?q?=E5=88=97=E5=8A=A0=E8=BD=BD=E6=9B=B4=E5=A4=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/bangumi/view.dart | 2 +- lib/pages/live/view.dart | 24 ++---------------------- lib/pages/rcmd/view.dart | 22 +++++++++++++++------- 3 files changed, 18 insertions(+), 30 deletions(-) diff --git a/lib/pages/bangumi/view.dart b/lib/pages/bangumi/view.dart index 78d4a2a9..e48715eb 100644 --- a/lib/pages/bangumi/view.dart +++ b/lib/pages/bangumi/view.dart @@ -201,7 +201,7 @@ class _BangumiPageState extends State }, ), ), - const LoadingMore() + LoadingMore() ], ), ); diff --git a/lib/pages/live/view.dart b/lib/pages/live/view.dart index fc29dea7..1fbff63c 100644 --- a/lib/pages/live/view.dart +++ b/lib/pages/live/view.dart @@ -10,6 +10,7 @@ import 'package:pilipala/common/widgets/animated_dialog.dart'; import 'package:pilipala/common/widgets/http_error.dart'; import 'package:pilipala/common/widgets/overlay_pop.dart'; import 'package:pilipala/pages/main/index.dart'; +import 'package:pilipala/pages/rcmd/index.dart'; import 'controller.dart'; import 'widgets/live_item.dart'; @@ -118,7 +119,7 @@ class _LivePageState extends State }, ), ), - const LoadingMore() + LoadingMore(ctr: _liveController) ], ), ), @@ -180,24 +181,3 @@ class _LivePageState extends State ); } } - -class LoadingMore extends StatelessWidget { - const LoadingMore({super.key}); - - @override - Widget build(BuildContext context) { - return SliverToBoxAdapter( - child: Container( - height: MediaQuery.of(context).padding.bottom + 80, - padding: EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom), - child: Center( - child: Text( - '加载中...', - style: TextStyle( - color: Theme.of(context).colorScheme.outline, fontSize: 13), - ), - ), - ), - ); - } -} diff --git a/lib/pages/rcmd/view.dart b/lib/pages/rcmd/view.dart index 165613a6..51771d3c 100644 --- a/lib/pages/rcmd/view.dart +++ b/lib/pages/rcmd/view.dart @@ -125,7 +125,7 @@ class _RcmdPageState extends State }, ), ), - const LoadingMore() + LoadingMore(ctr: _rcmdController) ], ), ), @@ -191,7 +191,8 @@ class _RcmdPageState extends State } class LoadingMore extends StatelessWidget { - const LoadingMore({super.key}); + dynamic ctr; + LoadingMore({super.key, this.ctr}); @override Widget build(BuildContext context) { @@ -199,11 +200,18 @@ class LoadingMore extends StatelessWidget { child: Container( height: MediaQuery.of(context).padding.bottom + 80, padding: EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom), - child: Center( - child: Text( - '加载中...', - style: TextStyle( - color: Theme.of(context).colorScheme.outline, fontSize: 13), + child: GestureDetector( + onTap: () { + if (ctr != null) { + ctr!.onLoad(); + } + }, + child: Center( + child: Text( + '加载更多 👇', + style: TextStyle( + color: Theme.of(context).colorScheme.outline, fontSize: 13), + ), ), ), ), From 8aa38a36c6e5eb870be7d8fab506b14a797aa4f1 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 22 Oct 2023 11:24:51 +0800 Subject: [PATCH 15/47] =?UTF-8?q?mod:=20=E4=BF=AE=E6=94=B9=E5=B7=B2?= =?UTF-8?q?=E5=85=B3=E6=B3=A8upup=E5=88=86=E7=BB=84=E4=BB=85=E8=87=AA?= =?UTF-8?q?=E5=B7=B1=E5=8F=AF=E8=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/follow/widgets/follow_item.dart | 51 ++++++++++--------- lib/pages/follow/widgets/follow_list.dart | 5 +- .../follow/widgets/owner_follow_list.dart | 5 +- 3 files changed, 35 insertions(+), 26 deletions(-) diff --git a/lib/pages/follow/widgets/follow_item.dart b/lib/pages/follow/widgets/follow_item.dart index d367b8d4..3f9e4f3c 100644 --- a/lib/pages/follow/widgets/follow_item.dart +++ b/lib/pages/follow/widgets/follow_item.dart @@ -3,17 +3,19 @@ import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; import 'package:pilipala/models/follow/result.dart'; +import 'package:pilipala/pages/follow/index.dart'; import 'package:pilipala/pages/video/detail/introduction/widgets/group_panel.dart'; import 'package:pilipala/utils/feed_back.dart'; import 'package:pilipala/utils/utils.dart'; class FollowItem extends StatelessWidget { final FollowItemModel item; - const FollowItem({super.key, required this.item}); + final FollowController? ctr; + const FollowItem({super.key, required this.item, this.ctr}); @override Widget build(BuildContext context) { - String heroTag = Utils.makeHeroTag(item!.mid); + String heroTag = Utils.makeHeroTag(item.mid); return ListTile( onTap: () { feedBack(); @@ -41,28 +43,29 @@ class FollowItem extends StatelessWidget { overflow: TextOverflow.ellipsis, ), dense: true, - trailing: SizedBox( - height: 34, - child: TextButton( - onPressed: () async { - await Get.bottomSheet( - GroupPanel(mid: item.mid!), - isScrollControlled: true, - ); - SmartDialog.showToast('重进页面查看效果'); - }, - style: TextButton.styleFrom( - padding: const EdgeInsets.fromLTRB(15, 0, 15, 0), - foregroundColor: Theme.of(context).colorScheme.outline, - backgroundColor: - Theme.of(context).colorScheme.onInverseSurface, // 设置按钮背景色 - ), - child: const Text( - '已关注', - style: TextStyle(fontSize: 12), - ), - ), - ), + trailing: ctr!.isOwner.value + ? SizedBox( + height: 34, + child: TextButton( + onPressed: () async { + await Get.bottomSheet( + GroupPanel(mid: item.mid!), + isScrollControlled: true, + ); + }, + style: TextButton.styleFrom( + padding: const EdgeInsets.fromLTRB(15, 0, 15, 0), + foregroundColor: Theme.of(context).colorScheme.outline, + backgroundColor: + Theme.of(context).colorScheme.onInverseSurface, // 设置按钮背景色 + ), + child: const Text( + '已关注', + style: TextStyle(fontSize: 12), + ), + ), + ) + : const SizedBox(), ); } } diff --git a/lib/pages/follow/widgets/follow_list.dart b/lib/pages/follow/widgets/follow_list.dart index 73535e2a..d198bec2 100644 --- a/lib/pages/follow/widgets/follow_list.dart +++ b/lib/pages/follow/widgets/follow_list.dart @@ -84,7 +84,10 @@ class _FollowListState extends State { ), ); } else { - return FollowItem(item: list[index]); + return FollowItem( + item: list[index], + ctr: widget.ctr, + ); } }, ) diff --git a/lib/pages/follow/widgets/owner_follow_list.dart b/lib/pages/follow/widgets/owner_follow_list.dart index 13a1d0b3..e622bd5a 100644 --- a/lib/pages/follow/widgets/owner_follow_list.dart +++ b/lib/pages/follow/widgets/owner_follow_list.dart @@ -101,7 +101,10 @@ class _OwnerFollowListState extends State MediaQuery.of(context).padding.bottom), ); } else { - return FollowItem(item: followList[index]); + return FollowItem( + item: followList[index], + ctr: widget.ctr, + ); } }, ) From 48030d5ee724834a74ec0231708a2ffedfaebb1c Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 22 Oct 2023 14:11:43 +0800 Subject: [PATCH 16/47] =?UTF-8?q?fix:=20=E5=B8=A6=E9=97=AE=E5=8F=B7?= =?UTF-8?q?=E7=9A=84=E8=AF=84=E8=AE=BA=E5=86=85=E5=AE=B9=E5=8C=B9=E9=85=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../detail/reply/widgets/reply_item.dart | 90 +++++++++++-------- 1 file changed, 51 insertions(+), 39 deletions(-) diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index 0c2328fc..58acd8ab 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -669,58 +669,70 @@ InlineSpan buildContent( String matchUrl = matchMember; if (content.jumpUrl.isNotEmpty && hasMatchMember) { List urlKeys = content.jumpUrl.keys.toList().reversed.toList(); + for (var index = 0; index < urlKeys.length; index++) { + var i = urlKeys[index]; + if (i.contains('?')) { + urlKeys[index] = i.replaceAll('?', '\\?'); + } + } matchUrl = matchMember.splitMapJoin( /// RegExp.escape() 转义特殊字符 RegExp(urlKeys.map((key) => key).join("|")), - // RegExp(RegExp.escape(urlKeys.join("|"))), + // RegExp('What does the fox say\\?'), onMatch: (Match match) { String matchStr = match[0]!; - String appUrlSchema = content.jumpUrl[matchStr]['app_url_schema']; + String appUrlSchema = ''; + if (content.jumpUrl[matchStr] != null) { + appUrlSchema = content.jumpUrl[matchStr]['app_url_schema']; + } // 默认不显示关键词 bool enableWordRe = setting.get(SettingBoxKey.enableWordRe, defaultValue: false); - spanChilds.add( - TextSpan( - text: content.jumpUrl[matchStr]['title'], - style: TextStyle( - color: enableWordRe - ? Theme.of(context).colorScheme.primary - : null, - ), - recognizer: TapGestureRecognizer() - ..onTap = () { - if (appUrlSchema == '') { - String str = Uri.parse(matchStr).pathSegments[0]; - Map matchRes = IdUtils.matchAvorBv(input: str); - List matchKeys = matchRes.keys.toList(); - if (matchKeys.isNotEmpty) { - if (matchKeys.first == 'BV') { + if (content.jumpUrl[matchStr] != null) { + spanChilds.add( + TextSpan( + text: content.jumpUrl[matchStr]['title'], + style: TextStyle( + color: enableWordRe + ? Theme.of(context).colorScheme.primary + : null, + ), + recognizer: TapGestureRecognizer() + ..onTap = () { + if (appUrlSchema == '') { + String str = Uri.parse(matchStr).pathSegments[0]; + Map matchRes = IdUtils.matchAvorBv(input: str); + List matchKeys = matchRes.keys.toList(); + if (matchKeys.isNotEmpty) { + if (matchKeys.first == 'BV') { + Get.toNamed( + '/searchResult', + parameters: {'keyword': matchRes['BV']}, + ); + } + } else { Get.toNamed( - '/searchResult', - parameters: {'keyword': matchRes['BV']}, + '/webview', + parameters: { + 'url': matchStr, + 'type': 'url', + 'pageTitle': '' + }, ); } } else { - Get.toNamed( - '/webview', - parameters: { - 'url': matchStr, - 'type': 'url', - 'pageTitle': '' - }, - ); + if (appUrlSchema.startsWith('bilibili://search') && + enableWordRe) { + Get.toNamed('/searchResult', parameters: { + 'keyword': content.jumpUrl[matchStr]['title'] + }); + } } - } else { - if (appUrlSchema.startsWith('bilibili://search') && - enableWordRe) { - Get.toNamed('/searchResult', parameters: { - 'keyword': content.jumpUrl[matchStr]['title'] - }); - } - } - }, - ), - ); + }, + ), + ); + } + if (appUrlSchema.startsWith('bilibili://search') && enableWordRe) { spanChilds.add( WidgetSpan( From 50b5f221e861ba0d4557a0229c26876294ed934b Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 22 Oct 2023 16:19:06 +0800 Subject: [PATCH 17/47] =?UTF-8?q?fix:=20=E9=A1=B5=E9=9D=A2=E5=BF=AB?= =?UTF-8?q?=E9=80=9F=E8=BF=94=E5=9B=9E=E6=97=B6=E5=BC=82=E5=B8=B8=20issues?= =?UTF-8?q?=20#175?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/introduction/controller.dart | 5 ++++- lib/plugin/pl_player/controller.dart | 5 +++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/pages/video/detail/introduction/controller.dart b/lib/pages/video/detail/introduction/controller.dart index 6c32dc33..225fea53 100644 --- a/lib/pages/video/detail/introduction/controller.dart +++ b/lib/pages/video/detail/introduction/controller.dart @@ -61,12 +61,15 @@ class VideoIntroController extends GetxController { RxString total = '1'.obs; Timer? timer; bool isPaused = false; - String heroTag = Get.arguments['heroTag']; + String heroTag = ''; @override void onInit() { super.onInit(); userInfo = userInfoCache.get('userInfoCache'); + try { + heroTag = Get.arguments['heroTag']; + } catch (_) {} if (Get.arguments.isNotEmpty) { if (Get.arguments.containsKey('videoItem')) { preRender = true; diff --git a/lib/plugin/pl_player/controller.dart b/lib/plugin/pl_player/controller.dart index c5d515a2..ee4f3545 100644 --- a/lib/plugin/pl_player/controller.dart +++ b/lib/plugin/pl_player/controller.dart @@ -328,6 +328,9 @@ class PlPlayerController { await pause(notify: false); } + if (_playerCount.value == 0) { + return; + } // 配置Player 音轨、字幕等等 _videoPlayerController = await _createVideoController( dataSource, _looping, enableHA, width, height); @@ -992,6 +995,8 @@ class PlPlayerController { localCache.put(LocalCacheKey.danmakuFontScale, fontSizeVal); localCache.put(LocalCacheKey.danmakuSpeed, danmakuSpeedVal); + var pp = _videoPlayerController!.platform as NativePlayer; + await pp.setProperty('audio-files', ''); removeListeners(); await _videoPlayerController?.dispose(); _videoPlayerController = null; From dc2bd04143496c4195a936ec44fd15931d36c068 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 22 Oct 2023 17:54:34 +0800 Subject: [PATCH 18/47] =?UTF-8?q?feat:=20ai=E6=80=BB=E7=BB=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/images/ai.png | Bin 0 -> 7926 bytes lib/http/api.dart | 9 + lib/http/video.dart | 21 ++ lib/models/video/ai.dart | 80 ++++++ .../video/detail/introduction/controller.dart | 23 ++ lib/pages/video/detail/introduction/view.dart | 151 ++++++----- lib/pages/video/detail/widgets/ai_detail.dart | 236 ++++++++++++++++++ lib/utils/utils.dart | 11 + 8 files changed, 472 insertions(+), 59 deletions(-) create mode 100644 assets/images/ai.png create mode 100644 lib/models/video/ai.dart create mode 100644 lib/pages/video/detail/widgets/ai_detail.dart diff --git a/assets/images/ai.png b/assets/images/ai.png new file mode 100644 index 0000000000000000000000000000000000000000..19f11915cb9f429d720b44fdac48f2140099a31f GIT binary patch literal 7926 zcmWkzc|26_7ryp=nNVYAETIxgj4aVejU~p~L}6rUvJTnzZOHmXlC3ag8A}XFwwAGF zR5C_Y4S0C3&8jX;5S!~b8b%;2}W zNzfvAW5ns*v0w#1A*@b#aLwj*`yLJe6s7)u(FG86o`Qc0`s&;GntM9?`aAeM0{s2` z72Q0rIE;hWBSlXim)uoNApj7=-9hMD1mvycg=dOdX07dNw^uBVxBUqn{IgJUhgDkg zhW~9=NnTcZ1$v$|8jcgY|6H?6@Okl@0oweLhkwI^W-wME`!tDL ztP(m53s-sd^ltyiFBxoK9jMUI**TmUe;<1I)PLUos%NWeFmds{pT}@HPJ8&e%0ZUgTu|Gyn1ZHD*?#K?a!zGcE;M@KN_yK3z+PP93`E-{D6~%M=Z~G zB|Sa;j@z#%PkWKRPoMsv?wKhEu!b2eKo*K6sD*fqeeHmV|ZEX^dV@ro9v`B z{mSjy(wM7RiPzjmKL-Wx9+BbxIXG^-h`PazzeN)3+5vz3PPhG~q@-S$ltF-3m6g~t zFG93a0YFq#l&@M%U7ZyGa*Ukybl?8ExV^i3^I9&!Gi)>EGJ2`)Gn5_{?R1&(bec4L zl6{|rJh?2YNg6qhp;qM$M>f@T!Um{ethyxcGR=-r5S zu*OLFpuMT{6iwgxZ#9sdRD1_Cnj9*dp|Msh-s+6iE-ZuN=Du}zv&yX4`unSGZ*PA$ zic~%8g)|-1RUy~fD#r6@HS9|{K*M$DR0Wg}JD*2MrwdXj_FIk_AfB5ys+Sa>g9953U!<~=u zVrvK@v~pd29HUBS(fX2F^KbxAP|WUkuCuxLRx5a?HkRX@qjq6ey!gp+p4xc8l0Izs z=VTP=8n}twp}RF7Hnf%?>G!Y@018ClxZ$)xg+8=lQeBCchpQaT^Vi1m-71nHACnQI z)h`^S@OY$)`XB|Lf?HLmDMmi;o1L$ytbUsiY{F9Yz@ev1URTE5yr?u;SZ!l&&bfj5 z)ZsJ`H4lBiS8$P`WViv%)}2q!h^-IG zpvRgWG1jn9eo7#`HCFoE8^R(YzWx3E(lK5I zv}Q(|AJ2`MI9=!!a{H&-LJtEJvp=Pza61zpeQ``Tln>2D()4|Ef zSEfjB*ck%q4x?@ddu)_1Ix+$lCyr*5CL$WGc4ugbShy5v1kaA)rQgL!}4}c-N4LCU223 zBUua!3yUc5uCn@l@7!kK=!d|~pB-mx4Csf($0@fA3)yoh zswBcxRsI{$-27^I`XmWHdz+mvY7O1Iu@x>2vvT(w@$G#})q|3a#~NEt1Yql_ujFo* z9kWoh{P*4Sa9n^8;H!~OrH(=&ehOJXjE#+jAj#CxgSHwzdycagz4k*2DC#qu*9B_X zYEu|1Oy#=OQ<|x0W?c!;grDe2^%N~yo)4v5Jtd2ZPEO)~9~A5#GKkI27J7E_#xh`Fw~_(y)t0i=JmZuY&Nj-I@6Wy({KFZOJf~Qg*R~9>k@^1Tq{8{$!%p#PXgVjRm-;s z7J`5t!QxG3Vw40vWFj>SrIFqDf0rzkaOGb2uNH+uUwsz+M;7*UN;tIh8?t=oK6Wi^ zZ{1-vP)v=C_i4UJqQ14c7(|9npqIM5mvv6|X0VFw8@&RR1L4Djh0?969E*@rw+snm zZ*{>@ixnw4$j^h|cy5v3$CC^^M~yNWZMnDy>`||b5N77)=JSVPBKqkjwqTUz_RY%C zF$3HQ_@NeYpcYJT5P_Wd^--*n9I;X7^vz5`=!R{W^Vfz zayp5f(1$pR@sAmCF|o!!fBwYyPlT$i&ChqZk2QK@I5#y@ty@^Y%~r@ITJY=ZigcBi z6^0#r#Omxct?9J7Ea>BBT2F6Dn6I4I%{?=n`Bhf%<2xT&he`&tur6<;A9uHEJ?mV1*5?xK$T*%R> zAhqo7Z^WO|P5F)Tc8?Y4h5Mm`S!ud=2Y%E){m^!EUM}d4ygqQFZS6YtQmn&muv*JV zGD0AkeUGNaPuAo2IWB+bNMMbYus7pRuvIMMHT&xW7YB|E#6!323XebP*!q7w+VA_X z<@t>qb~JrS-J~8qLEv1G)xxO%6WO%VxL*OHWGJiR<3}??!d<)>!rfp;^LulnF`ElM zycAfVN=`#1#_(FKaGH}=U?Ho`G^ZbL?qk12dpHkFK$yFOjv}<`f1vUev zDEXu!d~iV*iK>SsepK3;wmBVod&SyTTj0&D+bvI5t)WWabb#hM5ro;%IR+-vK#lYP+Ehme-N=s5 zWzCKC;`Kf8#v29(EEmzfKK-iLfmUcVGK&}O^62i!+t~zNnOQ6qeLkiZ@wa#O?b70+ z4;4LW{VSVQm6f7S_>NprYmLHUb)}Sk?J0_?0){<>BCwBH7(|c zd%wBt8<+|Fa`L)tY;Cm2%rlw))q4G##ywpdZykk~eN@UsW2-au0Q&`Bp?ezn3X z-oG2NUJ})@`8oJ<^Oi4cx`0-v#QE#>&G;Bv;0q)njewHh(&PbQOz!M6Th>k@X}W0l zgFl>RWJ0JoR}c;n8{0aZvD@``Y9HGQ-dp*U8kI(0f|4j<{K6zxenXzA@A>72kCW2P z_Q$^+`OfP=FV1PZ@YYI;B&p(wu z92fPc=Z&h;n?^ls%#?t?kJX39`B)i@CG^yY zcc?dvXE%KsZ`u*-syJC_^jr%InC7_))GU2wmyg78_ve(}E8ag+!Ej2!6bXodO(o8o zPQ}H=Ri<)E7)}`M+02Xfor9%wPA^sJ>16%Yo7!r(1BSO>j66SIP<^5>y(f3WYcg-9 zqMlMf%j3g$7s)w#i{OAaavXpSzy4QvS3Yj~r%ah2^{(+y%T#4(jaa*o*v;V8=5qk= zg_&E-9V2^6D=~p~I6U6EZ26rsa~7&Q6ZIxj4|~Y~Axm(or};hwjI0DOoXjloDgIii z38ESSwWGRBH#)p+l5`u1?pBtTztPXiE!d~kZ$Y}0=>Z$8atg+S_{e=l)O}lU_`r(M zR58oh;suA|dtUAJ*E}D=MD`jMors=#t;6%z2zSMHH=#W`@H{1+C45sjU~9_2+4n)S zh@OangnG?S9190`nu727AZJKGNdSOPv&ebaYTiu@=YZ48`~Mk1>R{GnAl>;V zo!0i&;8dyS1bZIH#cfCjB71+ks-r&*;a^}L5r0{|JEnw3nw!{x%mu3$axRI}j^(oE z?$KD@J!PyYtE8-!r=cn#H(A+M{!0}}j(~{h?Zz|d0#q~t;hZ$3%X`&$^6qhFb)XYe!BDH+5JxRb;%VQ`doJY8?*8YRNBO^H$@NbA$vQ}eHC1q zDapxoj&8KWxbL5vu)(;{*LuRoVwmUXJE;m2Wq4URQ zDap=D!LT3`Cr=Lp&(W~-3SC7nJz$EABt3^fR8opg_Me8%sF}B%76eZ%EwyW0s--?z zGP9QblUZ=~r@zO^*`qB39qmQU^XS>YR{K0S?FEjTM7_wC>Ih^(<%z15x9XKrk}&y2 zi(}Qrb%(3XE@*r4wjC)0yDXk+yF(h2r7Giek5Ma*n@D;?65G8%vpsV&u{-Ui-$qH@ zYaMj(%8>PI3x&H#&)%!zeh+fyzLs1iV&<1tv=%>*iAAL`mV`|^v$GxQ=OQ%Js?^9# zh9*WkYmC$dEv@ytr#>ttgXL@svp#}hB!^FCvrNGHQgArazO9Ge%S?8U)|r;tY0+c$ zADM&hj2)14AqGp?P8f`le*DD)w!)6r+-^Gi_^>V3UZu%>#=uF{YRQ-wgqWf zBWVL_y^c+!u$KkmD)H2RW)H3X^?0_o+upYO5o&u-)*_k*4g<@jxp9MGm zI>p)KXnd5H)h(;rN#O=VZ0#nl4`B-0r@h;A_Y-bFGuR@lqDi!-jlJ;r^R{XC-8|NR z$AHHHrO9FZXn#$8?Q@!P+>nF@hBMXy6c4H-&p?xuJJemWri|THj@~Ff{TDpJb%}V1 z??>;f%F*$5;Ymi=DBsf3O6)+$QAS1tVNm|v-;KT$jduB4G9OIk7%A{<=@L{qQpzgd z&u@rN^d~*8{47YI>hA6@>3J z7gu~Xs;X%^r|tJ=qI0QcB|?sUDqE*rSZsec-o;muCX)Fd2@@slIuc2;YHDiYk)6r` z05GIuGXGPjIT1+|xC>Fn-FG=qC4zYo&+5M7ZYsy0YQqbQ8f*6x{(V9mODSDl>wd7Z z6rCC}|FT%ShCpIcr%hITS51%v)UjYilPtQ4s3et^mdZM!kQwZmRWci0hYv**lcYI-Lm@%n%%BBQpf>^ggl>Tig4yl2 zZuqO(p8d_?{}PYk%XveUkDm!gxPDwraI6b>H~REGhBL5$HmxpuHcpaF@*)f^8FLeH zDsZ>cXOT-W@pJ6k#b71q|Re2`cz(?EG^+L;5;>zkOnjgd=##!+KdLv4l36E3+ zK~=?#D8E1bEs}v{(fYOp45i@nD5USF|F9zW)b5D~cpWlq&k@AyUjj{!&l5M6)&QymodYR?>@ac=9K3 zrZao^^sajFQ16V*O0gP&|F`d;^(z92V3qorsT@75#2Nn}a5jskP6PY!TqNNlq*;O$ zpM{h`mJL!iH9x`Cl9Z)l@Vm=_n*!D>E_!6dNuSWeqZ1Z2OQ7K#7ael7- zP({5$T`48fP@>t}Z)L1$7cPnT3cZqtn+YuNrZj_M&c!U`Jwhh&sR=O?#bT;Qneh zN0!%IP9OPP(t9V;B_lu|p`@f#>qn#H4^?OQ>KjP{9%f zMMYKdP+?5b;oAP}&BB9anhB2bPg?Oxb6P#~ZLeA?)wy!QZ;Hw5d+b_;RG|mAb~2Ab{clmwmnE1@dT zv-1lJaRmi~WmUCQp^zuiV2l6Hs%i*Mw zBX4D%)H1U7W2V>T4WW}!Rp!2@=UPp0aB!g!0R_Q;s;8Ts=t(n3?Y{Seq#~iMO&b8- zQZ*>ekCJjw<@^j0O5~W+L(28=;OT_(mo*&5**NlXlT~zBCL3&|_k-s95jP?xy+YVL z2xqucobqt4o`p;+E885ZrC~w$4xiex@kz*Vu$BInmFqP*2y=CvC*AjGo~7yX#sIuh zj#Z><1+;ruV&AOK&*5Q~ke>f!E@t#}jEq1Z6D1R2&rQm_eSP`iv?>CDa2m-X4%G_p zf4OSG>jb5BES+VjJs_Mlo#@03o(t>kSH1e~ie_0`r^m?R;Q2<<%zf>`wbLKjZCe7A zeQttu&H=U7oD8AQU!(yVf((oks{uotB?rUeKU=Jh z4@4>&jc3wiE9K<}L0!J-x7R1GiKL==OZi7^5GjHMC@a+es&Y=s`Bm>C41nsGm`B^b za~A}1C5Lkd6xF9wEP4bgVr+P`zdSn^h$JZR%w0&_c21=P-R{Qx^ngqCMWB+2o!KM?6k#D@?`;M$ECZL8R{zF4VK>)X$SrPzIN z>JcoU1={3t>AAX|l%3{Wk4nCrQ`sjE_lU4KZB>>9$1uZ+o>*SS!;;KL zh>I5pdI-)els9ONG~p^0-4Cq?1pwJZ*znMh{#cV|SwVqV1u>oZ%|*FF?iXu^uoMg= z?llP11yrWF>}7 z3?t?F;$Ax>{j#jI${8OYzw-*P-v}06j0`O30(HmO%>QB?GnL2!p}&qAygg`KFPfcaieb>Q+O;=e%Gj3Ooh>5q$xY&IiF!yn#0#y4ZlqE_))e zzsZuu7@_QZVH(B)q$oQ>zC+Sy^RO_Lp@tXA&VraalmZnk42q_K1=q-~lx7b%w*)@b zSs(b=_wNqL%3!GuMwt2?_ejx$dIZSjJwNfHScPgfLr1@?yTe?@cJxU?@)LW|!oI2qJzW z<)iWM*#~_neqdbF0UW@!dtVz|zr#2H#r>iGa~C7lkQZl0z%YDWQ^V8bIY9v-#vEA( ziWa4rj%-&Y8T)9P^pL=2+p`F1-t;Tv0KXyTyD9m&ujsyfToNvYihd9p$zxc=RB0zr zQNaPlP|${m%noFnGsUt%>YikbB{JvlyV3Hi2&XGKNy_ARj3|*V#oYe)>M3@0HojU~ zTGcHrx!0bI0E3ap-?ihitvX9DvDFR$kkDIEQ=AL9`}$@!kw|qR+5?dEF7K}&m5l7Y zDe#%RD@0XVUcIy{1>Kj2ORBiMa);llrfTs>bV%37@(UbG%s`DU9^5cMP~k426HQ2Y zYoadEa-Y^8flX1_D`F+K1O%)0ZO&P%$+Ojx9SvH~{7N-^6E*;DbU7~uvoY&}+R z9k@OH8~Ulj?7I>2Ua+VIeNCMbn+O@IOumgZ)?@907Blh!H+UJT&>3}FW{*>qGFh@{ sTQjwRs>n+JIOb~p%TjufE``%MUUT`qI-1H4&ZvPq`X-2KJ^QHt0m5uIvH$=8 literal 0 HcmV?d00001 diff --git a/lib/http/api.dart b/lib/http/api.dart index 14f55319..f2f06007 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -327,4 +327,13 @@ class Api { // id=849312409672744983 // features=itemOpusStyle static const String dynamicDetail = '/x/polymer/web-dynamic/v1/detail'; + + // AI总结 + /// https://api.bilibili.com/x/web-interface/view/conclusion/get? + /// bvid=BV1ju4y1s7kn& + /// cid=1296086601& + /// up_mid=4641697& + /// w_rid=1607c6c5a4a35a1297e31992220900ae& + /// wts=1697033079 + static const String aiConclusion = '/x/web-interface/view/conclusion/get'; } diff --git a/lib/http/video.dart b/lib/http/video.dart index 5ca8a280..9429a04b 100644 --- a/lib/http/video.dart +++ b/lib/http/video.dart @@ -9,9 +9,11 @@ import 'package:pilipala/models/home/rcmd/result.dart'; import 'package:pilipala/models/model_hot_video_item.dart'; import 'package:pilipala/models/model_rec_video_item.dart'; import 'package:pilipala/models/user/fav_folder.dart'; +import 'package:pilipala/models/video/ai.dart'; import 'package:pilipala/models/video/play/url.dart'; import 'package:pilipala/models/video_detail_res.dart'; import 'package:pilipala/utils/storage.dart'; +import 'package:pilipala/utils/wbi_sign.dart'; /// res.data['code'] == 0 请求正常返回结果 /// res.data['data'] 为结果 @@ -420,4 +422,23 @@ class VideoHttp { return {'status': true, 'data': res.data['data']}; } } + + static Future aiConclusion({ + String? bvid, + int? cid, + int? upMid, + }) async { + Map params = await WbiSign().makSign({ + 'bvid': bvid, + 'cid': cid, + 'up_mid': upMid, + }); + var res = await Request().get(Api.aiConclusion, data: params); + if (res.data['code'] == 0) { + return { + 'status': true, + 'data': AiConclusionModel.fromJson(res.data['data']), + }; + } + } } diff --git a/lib/models/video/ai.dart b/lib/models/video/ai.dart new file mode 100644 index 00000000..a06fa79d --- /dev/null +++ b/lib/models/video/ai.dart @@ -0,0 +1,80 @@ +class AiConclusionModel { + AiConclusionModel({ + this.code, + this.modelResult, + this.stid, + this.status, + this.likeNum, + this.dislikeNum, + }); + + int? code; + ModelResult? modelResult; + String? stid; + int? status; + int? likeNum; + int? dislikeNum; + + AiConclusionModel.fromJson(Map json) { + code = json['code']; + modelResult = ModelResult.fromJson(json['model_result']); + stid = json['stid']; + status = json['status']; + likeNum = json['like_num']; + dislikeNum = json['dislike_num']; + } +} + +class ModelResult { + ModelResult({ + this.resultType, + this.summary, + this.outline, + }); + + int? resultType; + String? summary; + List? outline; + + ModelResult.fromJson(Map json) { + resultType = json['result_type']; + summary = json['summary']; + outline = json['result_type'] == 2 + ? json['outline'] + .map((e) => OutlineItem.fromJson(e)) + .toList() + : []; + } +} + +class OutlineItem { + OutlineItem({ + this.title, + this.partOutline, + }); + + String? title; + List? partOutline; + + OutlineItem.fromJson(Map json) { + title = json['title']; + partOutline = json['part_outline'] + .map((e) => PartOutline.fromJson(e)) + .toList(); + } +} + +class PartOutline { + PartOutline({ + this.timestamp, + this.content, + }); + + int? timestamp; + String? content; + + PartOutline.fromJson(Map json) { + timestamp = json['timestamp']; + content = json['content']; + } +} diff --git a/lib/pages/video/detail/introduction/controller.dart b/lib/pages/video/detail/introduction/controller.dart index 6c32dc33..5c959116 100644 --- a/lib/pages/video/detail/introduction/controller.dart +++ b/lib/pages/video/detail/introduction/controller.dart @@ -8,6 +8,7 @@ import 'package:pilipala/http/constants.dart'; import 'package:pilipala/http/user.dart'; import 'package:pilipala/http/video.dart'; import 'package:pilipala/models/user/fav_folder.dart'; +import 'package:pilipala/models/video/ai.dart'; import 'package:pilipala/models/video_detail_res.dart'; import 'package:pilipala/pages/video/detail/controller.dart'; import 'package:pilipala/pages/video/detail/reply/index.dart'; @@ -62,6 +63,7 @@ class VideoIntroController extends GetxController { Timer? timer; bool isPaused = false; String heroTag = Get.arguments['heroTag']; + late ModelResult modelResult; @override void onInit() { @@ -561,4 +563,25 @@ class VideoIntroController extends GetxController { isScrollControlled: true, ); } + + // ai总结 + Future aiConclusion() async { + SmartDialog.showLoading(msg: '正在生产ai总结'); + var res = await VideoHttp.aiConclusion( + bvid: bvid, + cid: lastPlayCid.value, + upMid: videoDetail.value.owner!.mid!, + ); + if (res['status']) { + if (res['data'].modelResult.resultType == 0) { + SmartDialog.showToast('该视频不支持ai总结'); + } + if (res['data'].modelResult.resultType == 2 || + res['data'].modelResult.resultType == 1) { + modelResult = res['data'].modelResult; + } + } + SmartDialog.dismiss(); + return res; + } } diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index ef8ab928..46d641a2 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -11,6 +11,7 @@ import 'package:pilipala/common/widgets/stat/danmu.dart'; import 'package:pilipala/common/widgets/stat/view.dart'; import 'package:pilipala/models/video_detail_res.dart'; import 'package:pilipala/pages/video/detail/introduction/controller.dart'; +import 'package:pilipala/pages/video/detail/widgets/ai_detail.dart'; import 'package:pilipala/utils/feed_back.dart'; import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/utils.dart'; @@ -226,6 +227,17 @@ class _VideoInfoState extends State with TickerProviderStateMixin { arguments: {'face': face, 'heroTag': memberHeroTag}); } + // ai总结 + showAiBottomSheet() { + showBottomSheet( + context: context, + enableDrag: true, + builder: (BuildContext context) { + return AiDetail(modelResult: videoIntroController.modelResult); + }, + ); + } + @override Widget build(BuildContext context) { ThemeData t = Theme.of(context); @@ -238,70 +250,91 @@ class _VideoInfoState extends State with TickerProviderStateMixin { ? Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - GestureDetector( - behavior: HitTestBehavior.translucent, - onTap: () => showIntroDetail(), - child: Padding( - padding: const EdgeInsets.only(bottom: 6), - child: Text( - !loadingStatus - ? widget.videoDetail!.title - : videoItem['title'], - style: const TextStyle( - fontSize: 17, - fontWeight: FontWeight.bold, - ), - maxLines: 2, - overflow: TextOverflow.ellipsis, - ), - )), GestureDetector( behavior: HitTestBehavior.translucent, onTap: () => showIntroDetail(), - child: Row( - children: [ - StatView( - theme: 'gray', - view: !widget.loadingStatus - ? widget.videoDetail!.stat!.view - : videoItem['stat'].view, - size: 'medium', - ), - const SizedBox(width: 10), - StatDanMu( - theme: 'gray', - danmu: !widget.loadingStatus - ? widget.videoDetail!.stat!.danmaku - : videoItem['stat'].danmaku, - size: 'medium', - ), - const SizedBox(width: 10), - Text( - Utils.dateFormat( - !widget.loadingStatus - ? widget.videoDetail!.pubdate - : videoItem['pubdate'], - formatType: 'detail'), - style: TextStyle( - fontSize: 12, - color: t.colorScheme.outline, - ), - ), - const SizedBox(width: 10), - if (videoIntroController.isShowOnlineTotal) - Obx( - () => Text( - '${videoIntroController.total.value}人在看', - style: TextStyle( - fontSize: 12, - color: t.colorScheme.outline, - ), - ), - ), - ], + child: Text( + !loadingStatus + ? widget.videoDetail!.title + : videoItem['title'], + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), + maxLines: 2, + overflow: TextOverflow.ellipsis, ), ), - const SizedBox(height: 7), + Stack( + children: [ + GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () => showIntroDetail(), + child: Padding( + padding: const EdgeInsets.only(top: 7, bottom: 6), + child: Row( + children: [ + StatView( + theme: 'gray', + view: !widget.loadingStatus + ? widget.videoDetail!.stat!.view + : videoItem['stat'].view, + size: 'medium', + ), + const SizedBox(width: 10), + StatDanMu( + theme: 'gray', + danmu: !widget.loadingStatus + ? widget.videoDetail!.stat!.danmaku + : videoItem['stat'].danmaku, + size: 'medium', + ), + const SizedBox(width: 10), + Text( + Utils.dateFormat( + !widget.loadingStatus + ? widget.videoDetail!.pubdate + : videoItem['pubdate'], + formatType: 'detail'), + style: TextStyle( + fontSize: 12, + color: t.colorScheme.outline, + ), + ), + const SizedBox(width: 10), + if (videoIntroController.isShowOnlineTotal) + Obx( + () => Text( + '${videoIntroController.total.value}人在看', + style: TextStyle( + fontSize: 12, + color: t.colorScheme.outline, + ), + ), + ), + ], + ), + ), + ), + Positioned( + right: 10, + top: 6, + child: GestureDetector( + onTap: () async { + var res = await videoIntroController.aiConclusion(); + if (res['status']) { + if (res['data'].modelResult.resultType == 2 || + res['data'].modelResult.resultType == 1) { + showAiBottomSheet(); + } + } + }, + child: + Image.asset('assets/images/ai.png', height: 22), + ), + ) + ], + ), // 点赞收藏转发 布局样式1 // SingleChildScrollView( // padding: const EdgeInsets.only(top: 7, bottom: 7), diff --git a/lib/pages/video/detail/widgets/ai_detail.dart b/lib/pages/video/detail/widgets/ai_detail.dart new file mode 100644 index 00000000..fb280d91 --- /dev/null +++ b/lib/pages/video/detail/widgets/ai_detail.dart @@ -0,0 +1,236 @@ +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; +import 'package:get/get.dart'; +import 'package:hive/hive.dart'; +import 'package:pilipala/models/video/ai.dart'; +import 'package:pilipala/pages/video/detail/index.dart'; +import 'package:pilipala/utils/storage.dart'; +import 'package:pilipala/utils/utils.dart'; + +Box localCache = GStrorage.localCache; +late double sheetHeight; + +class AiDetail extends StatelessWidget { + final ModelResult? modelResult; + + const AiDetail({ + Key? key, + this.modelResult, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + sheetHeight = localCache.get('sheetHeight'); + return Container( + color: Theme.of(context).colorScheme.background, + padding: const EdgeInsets.only(left: 14, right: 14), + height: sheetHeight, + child: Column( + children: [ + InkWell( + onTap: () => Get.back(), + child: Container( + height: 35, + padding: const EdgeInsets.only(bottom: 2), + child: Center( + child: Container( + width: 32, + height: 3, + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.primary, + borderRadius: const BorderRadius.all(Radius.circular(3)), + ), + ), + ), + ), + ), + Expanded( + child: SingleChildScrollView( + child: Column( + children: [ + Text( + modelResult!.summary!, + style: const TextStyle( + fontSize: 15, + fontWeight: FontWeight.bold, + height: 1.5, + ), + ), + const SizedBox(height: 20), + ListView.builder( + shrinkWrap: true, + itemCount: modelResult!.outline!.length, + physics: const NeverScrollableScrollPhysics(), + itemBuilder: (context, index) { + return Column( + children: [ + Text( + modelResult!.outline![index].title!, + style: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.bold, + height: 1.5, + ), + ), + const SizedBox(height: 6), + ListView.builder( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemCount: modelResult! + .outline![index].partOutline!.length, + itemBuilder: (context, i) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Wrap( + children: [ + RichText( + text: TextSpan( + style: TextStyle( + fontSize: 13, + color: Theme.of(context) + .colorScheme + .onBackground, + height: 1.5, + ), + children: [ + TextSpan( + text: Utils.tampToSeektime( + modelResult! + .outline![index] + .partOutline![i] + .timestamp!), + style: TextStyle( + color: Theme.of(context) + .colorScheme + .primary, + ), + recognizer: TapGestureRecognizer() + ..onTap = () { + // 跳转到指定位置 + try { + Get.find( + tag: Get.arguments[ + 'heroTag']) + .plPlayerController + .seekTo( + Duration( + seconds: + Utils.duration( + Utils.tampToSeektime(modelResult! + .outline![ + index] + .partOutline![ + i] + .timestamp!) + .toString(), + ), + ), + ); + } catch (_) {} + }, + ), + const TextSpan(text: ' '), + TextSpan( + text: modelResult! + .outline![index] + .partOutline![i] + .content!), + ], + ), + ), + ], + ), + ], + ); + }, + ), + const SizedBox(height: 20), + ], + ); + }, + ) + ], + ), + ), + ), + ], + ), + ); + } + + InlineSpan buildContent(BuildContext context, content) { + List descV2 = content.descV2; + // type + // 1 普通文本 + // 2 @用户 + List spanChilds = List.generate(descV2.length, (index) { + final currentDesc = descV2[index]; + switch (currentDesc.type) { + case 1: + List spanChildren = []; + RegExp urlRegExp = RegExp(r'https?://\S+\b'); + Iterable matches = urlRegExp.allMatches(currentDesc.rawText); + + int previousEndIndex = 0; + for (Match match in matches) { + if (match.start > previousEndIndex) { + spanChildren.add(TextSpan( + text: currentDesc.rawText + .substring(previousEndIndex, match.start))); + } + spanChildren.add( + TextSpan( + text: match.group(0), + style: TextStyle( + color: Theme.of(context).colorScheme.primary), // 设置颜色为蓝色 + recognizer: TapGestureRecognizer() + ..onTap = () { + // 处理点击事件 + try { + Get.toNamed( + '/webview', + parameters: { + 'url': match.group(0)!, + 'type': 'url', + 'pageTitle': match.group(0)!, + }, + ); + } catch (err) { + SmartDialog.showToast(err.toString()); + } + }, + ), + ); + previousEndIndex = match.end; + } + + if (previousEndIndex < currentDesc.rawText.length) { + spanChildren.add(TextSpan( + text: currentDesc.rawText.substring(previousEndIndex))); + } + + TextSpan result = TextSpan(children: spanChildren); + return result; + case 2: + final colorSchemePrimary = Theme.of(context).colorScheme.primary; + final heroTag = Utils.makeHeroTag(currentDesc.bizId); + return TextSpan( + text: '@${currentDesc.rawText}', + style: TextStyle(color: colorSchemePrimary), + recognizer: TapGestureRecognizer() + ..onTap = () { + Get.toNamed( + '/member?mid=${currentDesc.bizId}', + arguments: {'face': '', 'heroTag': heroTag}, + ); + }, + ); + default: + return const TextSpan(); + } + }); + return TextSpan(children: spanChilds); + } +} diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart index f571e10d..8982c178 100644 --- a/lib/utils/utils.dart +++ b/lib/utils/utils.dart @@ -286,4 +286,15 @@ class Utils { ); } } + + // 时间戳转时间 + static tampToSeektime(number) { + int hours = number ~/ 60; + int minutes = number % 60; + + String formattedHours = hours.toString().padLeft(2, '0'); + String formattedMinutes = minutes.toString().padLeft(2, '0'); + + return '$formattedHours:$formattedMinutes'; + } } From 59f7c52611c8e6d0085987ea8e99266dcf220d2c Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 22 Oct 2023 19:19:08 +0800 Subject: [PATCH 19/47] =?UTF-8?q?fix:=20=E9=9F=B3=E9=87=8F=E4=B8=8D?= =?UTF-8?q?=E4=B8=80=E8=87=B4=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/plugin/pl_player/controller.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/plugin/pl_player/controller.dart b/lib/plugin/pl_player/controller.dart index c5d515a2..5e483f0a 100644 --- a/lib/plugin/pl_player/controller.dart +++ b/lib/plugin/pl_player/controller.dart @@ -382,6 +382,8 @@ class PlPlayerController { var pp = player.platform as NativePlayer; // 解除倍速限制 await pp.setProperty("af", "scaletempo2=max-speed=8"); + // 音量不一致 + await pp.setProperty("audio", "--ao=audiotrack"); // 音轨 if (dataSource.audioSource != '' && dataSource.audioSource != null) { await pp.setProperty( From 5d79c7ebbf031b1e0f274f988f8d954eb24929b1 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 23 Oct 2023 22:42:28 +0800 Subject: [PATCH 20/47] =?UTF-8?q?mod:=20=E6=8F=92=E4=BB=B6=E5=8D=87?= =?UTF-8?q?=E7=BA=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pubspec.lock | 96 ++++++++++++++++++++++++---------------------------- pubspec.yaml | 24 ++++++------- 2 files changed, 56 insertions(+), 64 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index bc6d573d..4cbbd2e9 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -21,10 +21,10 @@ packages: dependency: "direct main" description: name: animations - sha256: fe8a6bdca435f718bb1dc8a11661b2c22504c6da40ef934cee8327ed77934164 + sha256: ef57563eed3620bd5d75ad96189846aca1e033c0c45fc9a7d26e80ab02b88a70 url: "https://pub.dev" source: hosted - version: "2.0.7" + version: "2.0.8" appscheme: dependency: "direct main" description: @@ -61,10 +61,10 @@ packages: dependency: "direct main" description: name: audio_video_progress_bar - sha256: "67f3a5ea70d48b48caaf29f5a0606284a6aa3a393736daf9e82bec985d2f9b70" + sha256: "3384875247cdbea748bd9ae8330631cd06a6cabfcda4945d45c9b406da92bc66" url: "https://pub.dev" source: hosted - version: "1.0.1" + version: "2.0.1" auto_orientation: dependency: "direct main" description: @@ -149,26 +149,26 @@ packages: dependency: "direct main" description: name: cached_network_image - sha256: fd3d0dc1d451f9a252b32d95d3f0c3c487bc41a75eba2e6097cb0b9c71491b15 + sha256: f98972704692ba679db144261172a8e20feb145636c617af0eb4022132a6797f url: "https://pub.dev" source: hosted - version: "3.2.3" + version: "3.3.0" cached_network_image_platform_interface: dependency: transitive description: name: cached_network_image_platform_interface - sha256: bb2b8403b4ccdc60ef5f25c70dead1f3d32d24b9d6117cfc087f496b178594a7 + sha256: "56aa42a7a01e3c9db8456d9f3f999931f1e05535b5a424271e9a38cabf066613" url: "https://pub.dev" source: hosted - version: "2.0.0" + version: "3.0.0" cached_network_image_web: dependency: transitive description: name: cached_network_image_web - sha256: b8eb814ebfcb4dea049680f8c1ffb2df399e4d03bf7a352c775e26fa06e02fa0 + sha256: "759b9a9f8f6ccbb66c185df805fac107f05730b1dab9c64626d1008cca532257" url: "https://pub.dev" source: hosted - version: "1.0.2" + version: "1.1.0" characters: dependency: transitive description: @@ -325,18 +325,18 @@ packages: dependency: "direct main" description: name: dio - sha256: ce75a1b40947fea0a0e16ce73337122a86762e38b982e1ccb909daa3b9bc4197 + sha256: "417e2a6f9d83ab396ec38ff4ea5da6c254da71e4db765ad737a42af6930140b7" url: "https://pub.dev" source: hosted - version: "5.3.2" + version: "5.3.3" dio_cookie_manager: dependency: "direct main" description: name: dio_cookie_manager - sha256: c4b7a693aa09efd694a5c5e12065daa5e026647b106245281ed1042b3ebefb8f + sha256: e79498b0f632897ff0c28d6e8178b4bc6e9087412401f618c31fa0904ace050d url: "https://pub.dev" source: hosted - version: "3.1.0" + version: "3.1.1" dio_http2_adapter: dependency: "direct main" description: @@ -357,10 +357,10 @@ packages: dependency: "direct main" description: name: dynamic_color - sha256: de4798a7069121aee12d5895315680258415de9b00e717723a1bd73d58f0126d + sha256: "8b8bd1d798bd393e11eddeaa8ae95b12ff028bf7d5998fc5d003488cd5f4ce2f" url: "https://pub.dev" source: hosted - version: "1.6.6" + version: "1.6.8" easy_debounce: dependency: "direct main" description: @@ -454,14 +454,6 @@ packages: description: flutter source: sdk version: "0.0.0" - flutter_blurhash: - dependency: transitive - description: - name: flutter_blurhash - sha256: "05001537bd3fac7644fa6558b09ec8c0a3f2eba78c0765f88912882b1331a5c6" - url: "https://pub.dev" - source: hosted - version: "0.7.0" flutter_cache_manager: dependency: transitive description: @@ -519,10 +511,10 @@ packages: dependency: "direct main" description: name: flutter_smart_dialog - sha256: "8ba9eeb5b0b380bec368c5c8a324e1dab0cd88965c2dd83e64237441140bc599" + sha256: "8ffa51d55591227dbfe9fc2b1ff396b37bec7d09c241d875b9b932db99d2d5ea" url: "https://pub.dev" source: hosted - version: "4.9.3+2" + version: "4.9.4" flutter_svg: dependency: "direct main" description: @@ -540,10 +532,10 @@ packages: dependency: "direct main" description: name: flutter_volume_controller - sha256: "7f88cb046b00fd80e98bcb7926b9e3879f004f30905109fdf6c5d09b8d28eb2e" + sha256: "1161957826183b46916adb4f1c9f91befce0d8415bd3fcd781f7faed9df62d46" url: "https://pub.dev" source: hosted - version: "1.2.7" + version: "1.3.0" flutter_web_plugins: dependency: transitive description: flutter @@ -866,10 +858,10 @@ packages: dependency: transitive description: name: octo_image - sha256: "107f3ed1330006a3bea63615e81cf637433f5135a52466c7caa0e7152bca9143" + sha256: "45b40f99622f11901238e18d48f5f12ea36426d8eced9f4cbf58479c7aa2430d" url: "https://pub.dev" source: hosted - version: "1.0.2" + version: "2.0.0" package_config: dependency: transitive description: @@ -914,66 +906,66 @@ packages: dependency: "direct main" description: name: path_provider - sha256: "3087813781ab814e4157b172f1a11c46be20179fcc9bea043e0fba36bc0acaa2" + sha256: a1aa8aaa2542a6bc57e381f132af822420216c80d4781f7aa085ca3229208aaa url: "https://pub.dev" source: hosted - version: "2.0.15" + version: "2.1.1" path_provider_android: dependency: transitive description: name: path_provider_android - sha256: "2cec049d282c7f13c594b4a73976b0b4f2d7a1838a6dd5aaf7bd9719196bee86" + sha256: "6b8b19bd80da4f11ce91b2d1fb931f3006911477cec227cce23d3253d80df3f1" url: "https://pub.dev" source: hosted - version: "2.0.27" + version: "2.2.0" path_provider_foundation: dependency: transitive description: name: path_provider_foundation - sha256: "916731ccbdce44d545414dd9961f26ba5fbaa74bcbb55237d8e65a623a8c7297" + sha256: "19314d595120f82aca0ba62787d58dde2cc6b5df7d2f0daf72489e38d1b57f2d" url: "https://pub.dev" source: hosted - version: "2.2.4" + version: "2.3.1" path_provider_linux: dependency: transitive description: name: path_provider_linux - sha256: ffbb8cc9ed2c9ec0e4b7a541e56fd79b138e8f47d2fb86815f15358a349b3b57 + sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 url: "https://pub.dev" source: hosted - version: "2.1.11" + version: "2.2.1" path_provider_platform_interface: dependency: transitive description: name: path_provider_platform_interface - sha256: "57585299a729335f1298b43245842678cb9f43a6310351b18fb577d6e33165ec" + sha256: "94b1e0dd80970c1ce43d5d4e050a9918fce4f4a775e6142424c30a29a363265c" url: "https://pub.dev" source: hosted - version: "2.0.6" + version: "2.1.1" path_provider_windows: dependency: transitive description: name: path_provider_windows - sha256: "1cb68ba4cd3a795033de62ba1b7b4564dace301f952de6bfb3cd91b202b6ee96" + sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170" url: "https://pub.dev" source: hosted - version: "2.1.7" + version: "2.2.1" permission_handler: dependency: "direct main" description: name: permission_handler - sha256: "63e5216aae014a72fe9579ccd027323395ce7a98271d9defa9d57320d001af81" + sha256: "284a66179cabdf942f838543e10413246f06424d960c92ba95c84439154fcac8" url: "https://pub.dev" source: hosted - version: "10.4.3" + version: "11.0.1" permission_handler_android: dependency: transitive description: name: permission_handler_android - sha256: "2ffaf52a21f64ac9b35fe7369bb9533edbd4f698e5604db8645b1064ff4cf221" + sha256: f9fddd3b46109bd69ff3f9efa5006d2d309b7aec0f3c1c5637a60a2d5659e76e url: "https://pub.dev" source: hosted - version: "10.3.3" + version: "11.1.0" permission_handler_apple: dependency: transitive description: @@ -986,10 +978,10 @@ packages: dependency: transitive description: name: permission_handler_platform_interface - sha256: "7c6b1500385dd1d2ca61bb89e2488ca178e274a69144d26bbd65e33eae7c02a9" + sha256: "6760eb5ef34589224771010805bea6054ad28453906936f843a8cc4d3a55c4a4" url: "https://pub.dev" source: hosted - version: "3.11.3" + version: "3.12.0" permission_handler_windows: dependency: transitive description: @@ -1098,10 +1090,10 @@ packages: dependency: "direct main" description: name: screen_brightness - sha256: "62fd61a64e68b32b98b840bad7d8b6822bbc40e63c2b569a5f85528484c86b41" + sha256: ed8da4a4511e79422fc1aa88138e920e4008cd312b72cdaa15ccb426c0faaedd url: "https://pub.dev" source: hosted - version: "0.2.2" + version: "0.2.2+1" screen_brightness_android: dependency: transitive description: @@ -1335,10 +1327,10 @@ packages: dependency: "direct main" description: name: url_launcher - sha256: "781bd58a1eb16069412365c98597726cd8810ae27435f04b3b4d3a470bacd61e" + sha256: "47e208a6711459d813ba18af120d9663c20bdf6985d6ad39fe165d2538378d27" url: "https://pub.dev" source: hosted - version: "6.1.12" + version: "6.1.14" url_launcher_android: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index b522bb5c..a676942b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -36,31 +36,31 @@ dependencies: # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.5 # 动态取色 - dynamic_color: ^1.6.6 + dynamic_color: ^1.6.8 get: ^4.6.5 # 网络 - dio: ^5.3.0 + dio: ^5.3.3 cookie_jar: ^4.0.8 - dio_cookie_manager: ^3.1.0 + dio_cookie_manager: ^3.1.1 connectivity_plus: ^4.0.1 dio_http2_adapter: ^2.3.1+1 # 图片 - cached_network_image: ^3.2.3 + cached_network_image: ^3.3.0 extended_image: ^8.0.2 saver_gallery: ^2.0.1 # 存储 - path_provider: ^2.0.14 + path_provider: ^2.1.1 hive: ^2.2.3 hive_flutter: ^1.1.0 # 设备信息 device_info_plus: ^9.0.2 # 权限 - permission_handler: ^10.4.3 + permission_handler: ^11.0.1 # 分享 share_plus: ^7.0.2 # cookie 管理 @@ -76,7 +76,7 @@ dependencies: # 图标 font_awesome_flutter: ^10.4.0 # toast - flutter_smart_dialog: ^4.9.3+2 + flutter_smart_dialog: ^4.9.4 # 下滑关闭 dismissible_page: ^1.0.2 custom_sliding_segmented_control: ^1.7.5 @@ -89,19 +89,19 @@ dependencies: media_kit_libs_video: ^1.0.4 # 音量、亮度、屏幕控制 - flutter_volume_controller: ^1.2.7 - screen_brightness: ^0.2.2 + flutter_volume_controller: ^1.3.0 + screen_brightness: ^0.2.2+1 wakelock_plus: ^1.1.1 universal_platform: ^1.0.0+1 # 进度条 - audio_video_progress_bar: ^1.0.1 + audio_video_progress_bar: ^2.0.1 auto_orientation: ^2.3.1 protobuf: ^3.0.0 - animations: ^2.0.7 + animations: ^2.0.8 # 获取appx信息 package_info_plus: ^4.1.0 - url_launcher: ^6.1.12 + url_launcher: ^6.1.14 flutter_svg: ^2.0.7 # 防抖节流 easy_debounce: ^2.0.3 From 8b28417962f4c2a9c3256ae91c199f5c04958f0f Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 23 Oct 2023 23:00:35 +0800 Subject: [PATCH 21/47] fix: issues #210 --- lib/pages/video/detail/reply/controller.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pages/video/detail/reply/controller.dart b/lib/pages/video/detail/reply/controller.dart index 6bd0866f..40c26875 100644 --- a/lib/pages/video/detail/reply/controller.dart +++ b/lib/pages/video/detail/reply/controller.dart @@ -92,11 +92,11 @@ class VideoReplyController extends GetxController { } } replies.insertAll(0, res['data'].topReplies); + count.value = res['data'].page.count; replyList.value = replies; } else { replyList.addAll(replies); } - count.value = res['data'].page.count; } isLoadingMore = false; return res; From 2cd8e86864d07a50f1ccfc22dd9c011b2b8b2bfc Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 23 Oct 2023 23:07:37 +0800 Subject: [PATCH 22/47] fix: issues #214 --- lib/pages/video/detail/widgets/header_control.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pages/video/detail/widgets/header_control.dart b/lib/pages/video/detail/widgets/header_control.dart index a6f98d4a..97502177 100644 --- a/lib/pages/video/detail/widgets/header_control.dart +++ b/lib/pages/video/detail/widgets/header_control.dart @@ -183,8 +183,8 @@ class _HeaderControlState extends State { /// 选择倍速 void showSetSpeedSheet() { double currentSpeed = widget.controller!.playbackSpeed; - SmartDialog.show( - animationType: SmartAnimationType.centerFade_otherSlide, + showDialog( + context: Get.context!, builder: (context) { return AlertDialog( title: const Text('播放速度'), From 81bf8d915cac2e48b6b0c1a94068a89d90bc42cc Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 23 Oct 2023 23:34:24 +0800 Subject: [PATCH 23/47] =?UTF-8?q?feat:=20=E5=BA=94=E7=94=A8=E5=88=87?= =?UTF-8?q?=E6=8D=A2=E8=87=B3=E5=90=8E=E5=8F=B0=E8=87=AA=E5=8A=A8=E7=94=BB?= =?UTF-8?q?=E4=B8=AD=E7=94=BB=20issues=20#212?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/setting/play_setting.dart | 6 ++++++ lib/pages/video/detail/view.dart | 21 ++++++++++++++++++++- lib/utils/storage.dart | 1 + 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/lib/pages/setting/play_setting.dart b/lib/pages/setting/play_setting.dart index 6606f3c4..82459be9 100644 --- a/lib/pages/setting/play_setting.dart +++ b/lib/pages/setting/play_setting.dart @@ -85,6 +85,12 @@ class _PlaySettingState extends State { setKey: SettingBoxKey.enableBackgroundPlay, defaultVal: false, ), + const SetSwitchItem( + title: '自动PiP播放', + subTitle: 'app切换至后台时画中画播放', + setKey: SettingBoxKey.autoPiP, + defaultVal: false, + ), const SetSwitchItem( title: '自动全屏', subTitle: '视频开始播放时进入全屏', diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 0debd259..75d91c23 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -32,7 +32,7 @@ class VideoDetailPage extends StatefulWidget { } class _VideoDetailPageState extends State - with TickerProviderStateMixin, RouteAware { + with TickerProviderStateMixin, RouteAware, WidgetsBindingObserver { late VideoDetailController videoDetailController; PlPlayerController? plPlayerController; final ScrollController _extendNestCtr = ScrollController(); @@ -52,6 +52,8 @@ class _VideoDetailPageState extends State // 自动退出全屏 late bool autoExitFullcreen; late bool autoPlayEnable; + late bool autoPiP; + final floating = Floating(); @override void initState() { @@ -65,8 +67,11 @@ class _VideoDetailPageState extends State setting.get(SettingBoxKey.enableAutoExit, defaultValue: false); autoPlayEnable = setting.get(SettingBoxKey.autoPlayEnable, defaultValue: true); + autoPiP = setting.get(SettingBoxKey.autoPiP, defaultValue: false); + videoSourceInit(); appbarStreamListen(); + WidgetsBinding.instance.addObserver(this); } // 获取视频资源,初始化播放器 @@ -149,6 +154,8 @@ class _VideoDetailPageState extends State if (videoDetailController.floating != null) { videoDetailController.floating!.dispose(); } + WidgetsBinding.instance.removeObserver(this); + floating.dispose(); super.dispose(); } @@ -195,6 +202,17 @@ class _VideoDetailPageState extends State .subscribe(this, ModalRoute.of(context) as PageRoute); } + @override + void didChangeAppLifecycleState(AppLifecycleState lifecycleState) { + if (lifecycleState == AppLifecycleState.inactive && autoPiP) { + floating.enable( + aspectRatio: Rational( + videoDetailController.data.dash!.video!.first.width!, + videoDetailController.data.dash!.video!.first.height!, + )); + } + } + @override Widget build(BuildContext context) { final videoHeight = MediaQuery.of(context).size.width * 9 / 16; @@ -493,6 +511,7 @@ class _VideoDetailPageState extends State return PiPSwitcher( childWhenDisabled: childWhenDisabled, childWhenEnabled: childWhenEnabled, + floating: floating, ); } else { return childWhenDisabled; diff --git a/lib/utils/storage.dart b/lib/utils/storage.dart index ea30253b..d49bd4ad 100644 --- a/lib/utils/storage.dart +++ b/lib/utils/storage.dart @@ -106,6 +106,7 @@ class SettingBoxKey { static const String enableAutoExit = 'enableAutoExit'; static const String p1080 = 'p1080'; static const String enableCDN = 'enableCDN'; + static const String autoPiP = 'autoPiP'; // youtube 双击快进快退 static const String enableQuickDouble = 'enableQuickDouble'; From 1c370fb224685fd53ae1c3e0d485990a32f35d10 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 24 Oct 2023 08:19:38 +0800 Subject: [PATCH 24/47] =?UTF-8?q?feat:=20=E5=BA=95=E6=A0=8F=E6=A0=B7?= =?UTF-8?q?=E5=BC=8F=20issues=20#189?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/main/view.dart | 48 ++++++++++++++++++++-------- lib/pages/setting/style_setting.dart | 7 +++- lib/utils/storage.dart | 1 + 3 files changed, 41 insertions(+), 15 deletions(-) diff --git a/lib/pages/main/view.dart b/lib/pages/main/view.dart index c744098e..ee8d3829 100644 --- a/lib/pages/main/view.dart +++ b/lib/pages/main/view.dart @@ -29,6 +29,8 @@ class _MainAppState extends State with SingleTickerProviderStateMixin { late Animation? _slideAnimation; int selectedIndex = 0; int? _lastSelectTime; //上次点击时间 + Box setting = GStrorage.setting; + late bool enableMYBar; @override void initState() { @@ -45,6 +47,7 @@ class _MainAppState extends State with SingleTickerProviderStateMixin { Tween(begin: 0.8, end: 1.0).animate(_animationController!); _lastSelectTime = DateTime.now().millisecondsSinceEpoch; _pageController = PageController(initialPage: selectedIndex); + enableMYBar = setting.get(SettingBoxKey.enableMYBar, defaultValue: true); } void setIndex(int value) async { @@ -144,21 +147,38 @@ class _MainAppState extends State with SingleTickerProviderStateMixin { builder: (context, AsyncSnapshot snapshot) { return AnimatedSlide( curve: Curves.easeInOutCubicEmphasized, - duration: const Duration(milliseconds: 1000), + duration: const Duration(milliseconds: 500), offset: Offset(0, snapshot.data ? 0 : 1), - child: NavigationBar( - onDestinationSelected: (value) => setIndex(value), - selectedIndex: selectedIndex, - destinations: [ - ..._mainController.navigationBars.map((e) { - return NavigationDestination( - icon: e['icon'], - selectedIcon: e['selectIcon'], - label: e['label'], - ); - }).toList(), - ], - ), + child: enableMYBar + ? NavigationBar( + onDestinationSelected: (value) => setIndex(value), + selectedIndex: selectedIndex, + destinations: [ + ..._mainController.navigationBars.map((e) { + return NavigationDestination( + icon: e['icon'], + selectedIcon: e['selectIcon'], + label: e['label'], + ); + }).toList(), + ], + ) + : BottomNavigationBar( + currentIndex: selectedIndex, + onTap: (value) => setIndex(value), + iconSize: 16, + selectedFontSize: 12, + unselectedFontSize: 12, + items: [ + ..._mainController.navigationBars.map((e) { + return BottomNavigationBarItem( + icon: e['icon'], + activeIcon: e['selectIcon'], + label: e['label'], + ); + }).toList(), + ], + ), ); }, ), diff --git a/lib/pages/setting/style_setting.dart b/lib/pages/setting/style_setting.dart index 9151d79d..2256b2fd 100644 --- a/lib/pages/setting/style_setting.dart +++ b/lib/pages/setting/style_setting.dart @@ -1,7 +1,6 @@ import 'dart:io'; import 'package:flutter/material.dart'; -import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; import 'package:pilipala/models/common/theme_type.dart'; @@ -78,6 +77,12 @@ class _StyleSettingState extends State { setKey: SettingBoxKey.iosTransition, defaultVal: false, ), + const SetSwitchItem( + title: 'MD3样式底栏', + subTitle: '符合Material You设计规范的底栏', + setKey: SettingBoxKey.enableMYBar, + defaultVal: true, + ), // SetSwitchItem( // title: '首页单列', // subTitle: '每行展示一个内容卡片', diff --git a/lib/utils/storage.dart b/lib/utils/storage.dart index ea30253b..dc00ec43 100644 --- a/lib/utils/storage.dart +++ b/lib/utils/storage.dart @@ -135,6 +135,7 @@ class SettingBoxKey { static const String enableSingleRow = 'enableSingleRow'; // 首页单列 static const String displayMode = 'displayMode'; static const String customRows = 'customRows'; // 自定义列 + static const String enableMYBar = 'enableMYBar'; } class LocalCacheKey { From c41679d6f51c00b70589174400f8979a57acdaa2 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 24 Oct 2023 23:00:47 +0800 Subject: [PATCH 25/47] =?UTF-8?q?fix:=20=E9=83=A8=E5=88=86=E6=9C=BA?= =?UTF-8?q?=E5=9E=8B=E9=9F=B3=E9=87=8F=E4=B8=8D=E4=B8=80=E8=87=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/plugin/pl_player/controller.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/plugin/pl_player/controller.dart b/lib/plugin/pl_player/controller.dart index 5e483f0a..d11dd23f 100644 --- a/lib/plugin/pl_player/controller.dart +++ b/lib/plugin/pl_player/controller.dart @@ -383,7 +383,9 @@ class PlPlayerController { // 解除倍速限制 await pp.setProperty("af", "scaletempo2=max-speed=8"); // 音量不一致 - await pp.setProperty("audio", "--ao=audiotrack"); + await pp.setProperty("volume-max", "100"); + await pp.setProperty("ao", "audiotrack,opensles"); + // 音轨 if (dataSource.audioSource != '' && dataSource.audioSource != null) { await pp.setProperty( From eda8a5c6a711e90bdb73bc0f47ef7eb60d518acb Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 24 Oct 2023 23:47:50 +0800 Subject: [PATCH 26/47] =?UTF-8?q?fix:=20=E5=85=B3=E6=B3=A8=E5=88=97?= =?UTF-8?q?=E8=A1=A8=E4=B8=8D=E8=B6=B3=E4=B8=80=E5=B1=8F=E6=97=B6=E6=97=A0?= =?UTF-8?q?=E6=B3=95=E4=B8=8B=E6=8B=89=E5=88=B7=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/follow/widgets/owner_follow_list.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/pages/follow/widgets/owner_follow_list.dart b/lib/pages/follow/widgets/owner_follow_list.dart index e622bd5a..0dcd785d 100644 --- a/lib/pages/follow/widgets/owner_follow_list.dart +++ b/lib/pages/follow/widgets/owner_follow_list.dart @@ -1,3 +1,5 @@ +import 'dart:math'; + import 'package:easy_debounce/easy_throttle.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -89,6 +91,7 @@ class _OwnerFollowListState extends State return Obx( () => followList.isNotEmpty ? ListView.builder( + physics: const AlwaysScrollableScrollPhysics(), controller: scrollController, itemCount: followList.length + 1, itemBuilder: (BuildContext context, int index) { From 0a5dea0535f272f5b6ddd87c5ae76abb70a4525f Mon Sep 17 00:00:00 2001 From: Riri Date: Wed, 25 Oct 2023 16:27:14 +0800 Subject: [PATCH 27/47] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E5=AA=92?= =?UTF-8?q?=E4=BD=93=E9=80=9A=E7=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- android/app/src/main/AndroidManifest.xml | 22 +++- .../drawable-xxhdpi-v26/ic_stat_replay_10.png | Bin 0 -> 2454 bytes .../res/drawable-xxhdpi/ic_stat_replay_10.png | Bin 0 -> 2454 bytes .../ic_stat_forward_10.png | Bin 0 -> 2882 bytes .../ic_stat_replay_10.png | Bin 0 -> 2933 bytes .../drawable-xxxhdpi/ic_stat_forward_10.png | Bin 0 -> 2882 bytes .../drawable-xxxhdpi/ic_stat_replay_10.png | Bin 0 -> 2933 bytes ios/Runner/Info.plist | 4 + lib/main.dart | 2 + lib/pages/video/detail/introduction/view.dart | 3 + lib/plugin/pl_player/controller.dart | 14 +++ lib/services/audio_handler.dart | 116 ++++++++++++++++++ lib/services/service_locator.dart | 8 ++ macos/Flutter/GeneratedPluginRegistrant.swift | 4 + pubspec.lock | 76 +++++++++--- pubspec.yaml | 7 +- 16 files changed, 235 insertions(+), 21 deletions(-) create mode 100644 android/app/src/main/res/drawable-xxhdpi-v26/ic_stat_replay_10.png create mode 100644 android/app/src/main/res/drawable-xxhdpi/ic_stat_replay_10.png create mode 100644 android/app/src/main/res/drawable-xxxhdpi-v26/ic_stat_forward_10.png create mode 100644 android/app/src/main/res/drawable-xxxhdpi-v26/ic_stat_replay_10.png create mode 100644 android/app/src/main/res/drawable-xxxhdpi/ic_stat_forward_10.png create mode 100644 android/app/src/main/res/drawable-xxxhdpi/ic_stat_replay_10.png create mode 100644 lib/services/audio_handler.dart create mode 100644 lib/services/service_locator.dart diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index dc980d0f..ebbfa2f1 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -45,7 +45,7 @@ android:fullBackupContent="false" tools:replace="android:allowBackup"> + + + + + + + + + + + + + diff --git a/android/app/src/main/res/raw/keep.xml b/android/app/src/main/res/raw/keep.xml new file mode 100644 index 00000000..0a4bfd53 --- /dev/null +++ b/android/app/src/main/res/raw/keep.xml @@ -0,0 +1,3 @@ + + \ No newline at end of file From 2348c140084600cbc2bf909027f6736f5d89760b Mon Sep 17 00:00:00 2001 From: Riri Date: Sat, 28 Oct 2023 14:52:29 +0800 Subject: [PATCH 37/47] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=E4=B8=8D?= =?UTF-8?q?=E5=BC=80=E5=90=AF=E5=90=8E=E5=8F=B0=E6=92=AD=E6=94=BE=E6=97=B6?= =?UTF-8?q?=E7=9A=84=E9=9F=B3=E9=A2=91=E6=89=93=E6=96=AD=20=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E6=9C=89=E9=80=9A=E7=9F=A5=E6=98=AF=E5=87=8F=E5=B0=8F?= =?UTF-8?q?=E9=9F=B3=E9=87=8F=E9=80=9A=E7=9F=A5=E7=BB=93=E6=9D=9F=E6=97=B6?= =?UTF-8?q?=E5=9B=9E=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/plugin/pl_player/controller.dart | 1 + lib/services/audio_handler.dart | 47 ------------------------ lib/services/audio_session.dart | 53 ++++++++++++++++++++++++++++ lib/services/service_locator.dart | 3 ++ 4 files changed, 57 insertions(+), 47 deletions(-) create mode 100644 lib/services/audio_session.dart diff --git a/lib/plugin/pl_player/controller.dart b/lib/plugin/pl_player/controller.dart index e4f1b3d3..ec862738 100644 --- a/lib/plugin/pl_player/controller.dart +++ b/lib/plugin/pl_player/controller.dart @@ -646,6 +646,7 @@ class PlPlayerController { playerStatus.status.value = PlayerStatus.playing; // screenManager.setOverlays(false); + audioSessionHandler.setActive(true); } /// 暂停播放 diff --git a/lib/services/audio_handler.dart b/lib/services/audio_handler.dart index 67b8cb89..61b32b96 100644 --- a/lib/services/audio_handler.dart +++ b/lib/services/audio_handler.dart @@ -5,7 +5,6 @@ import 'package:pilipala/models/video_detail_res.dart'; import 'package:get/get.dart'; import 'package:pilipala/plugin/pl_player/index.dart'; import 'package:pilipala/utils/storage.dart'; -import 'package:audio_session/audio_session.dart'; Future initAudioService() async { return await AudioService.init( @@ -27,49 +26,9 @@ class VideoPlayerServiceHandler extends BaseAudioHandler with SeekHandler { static final List _item = []; Box setting = GStrorage.setting; bool enableBackgroundPlay = false; - late AudioSession session; - bool _playInterrupted = false; // 暂时无用 VideoPlayerServiceHandler() { revalidateSetting(); - initSession(); - } - - Future initSession() async { - session = await AudioSession.instance; - session.configure(const AudioSessionConfiguration.speech()); - - session.interruptionEventStream.listen((event) { - if (event.begin) { - switch (event.type) { - case AudioInterruptionType.duck: - // duck or pause - // break; - case AudioInterruptionType.pause: - case AudioInterruptionType.unknown: - pause(); - _playInterrupted = true; - break; - } - } else { - switch (event.type) { - case AudioInterruptionType.duck: - // unduck - // break; - case AudioInterruptionType.pause: - if (_playInterrupted) play(); - break; - case AudioInterruptionType.unknown: - break; - } - _playInterrupted = false; - } - }); - - // 耳机拔出暂停 - session.becomingNoisyEventStream.listen((_) { - pause(); - }); } revalidateSetting() { @@ -105,12 +64,6 @@ class VideoPlayerServiceHandler extends BaseAudioHandler with SeekHandler { final AudioProcessingState processingState; final playing = status == PlayerStatus.playing; - if (playing) { - _playInterrupted = false; - session.setActive(true); - } else { - session.setActive(false); - } if (status == PlayerStatus.completed) { processingState = AudioProcessingState.completed; } else if (isBuffering) { diff --git a/lib/services/audio_session.dart b/lib/services/audio_session.dart new file mode 100644 index 00000000..3dd9db45 --- /dev/null +++ b/lib/services/audio_session.dart @@ -0,0 +1,53 @@ +import 'package:audio_session/audio_session.dart'; +import 'package:pilipala/plugin/pl_player/index.dart'; + +class AudioSessionHandler { + late AudioSession session; + bool _playInterrupted = false; + + setActive(bool active) { + session.setActive(active); + } + + AudioSessionHandler() { + initSession(); + } + + Future initSession() async { + session = await AudioSession.instance; + session.configure(const AudioSessionConfiguration.music()); + + session.interruptionEventStream.listen((event) { + final player = PlPlayerController.getInstance(); + if (event.begin) { + switch (event.type) { + case AudioInterruptionType.duck: + player.setVolume(player.volume.value * 0.5); + break; + case AudioInterruptionType.pause: + case AudioInterruptionType.unknown: + player.pause(); + _playInterrupted = true; + break; + } + } else { + switch (event.type) { + case AudioInterruptionType.duck: + player.setVolume(player.volume.value * 2); + break; + case AudioInterruptionType.pause: + if (_playInterrupted) PlPlayerController.getInstance().play(); + break; + case AudioInterruptionType.unknown: + break; + } + _playInterrupted = false; + } + }); + + // 耳机拔出暂停 + session.becomingNoisyEventStream.listen((_) { + PlPlayerController.getInstance().pause(); + }); + } +} diff --git a/lib/services/service_locator.dart b/lib/services/service_locator.dart index 8b9e5af0..e4497660 100644 --- a/lib/services/service_locator.dart +++ b/lib/services/service_locator.dart @@ -1,8 +1,11 @@ import 'audio_handler.dart'; +import 'audio_session.dart'; late VideoPlayerServiceHandler videoPlayerServiceHandler; +late AudioSessionHandler audioSessionHandler; Future setupServiceLocator() async { final audio = await initAudioService(); videoPlayerServiceHandler = audio; + audioSessionHandler = AudioSessionHandler(); } From e01292a8f9ca1342941559c4cce70c3e632e5f28 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 28 Oct 2023 23:51:24 +0800 Subject: [PATCH 38/47] =?UTF-8?q?fix:=20=E9=83=A8=E5=88=86=E6=8A=95?= =?UTF-8?q?=E7=A8=BF=E4=B8=8D=E8=BF=9E=E6=92=AD=20issues=20#217?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../video/detail/introduction/controller.dart | 25 +++++++------------ 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/lib/pages/video/detail/introduction/controller.dart b/lib/pages/video/detail/introduction/controller.dart index 4bc32d35..4590ec44 100644 --- a/lib/pages/video/detail/introduction/controller.dart +++ b/lib/pages/video/detail/introduction/controller.dart @@ -514,19 +514,7 @@ class VideoIntroController extends GetxController { /// 列表循环或者顺序播放时,自动播放下一个 void nextPlay() { late List episodes; - // if (videoDetail.value.ugcSeason != null) { - // UgcSeason ugcSeason = videoDetail.value.ugcSeason!; - // List sections = ugcSeason.sections!; - // for (int i = 0; i < sections.length; i++) { - // List episodesList = sections[i].episodes!; - // for (int j = 0; j < episodesList.length; j++) { - // if (episodesList[j].cid == lastPlayCid.value) { - // episodes = episodesList; - // continue; - // } - // } - // } - // } + bool isPages = false; if (videoDetail.value.ugcSeason != null) { UgcSeason ugcSeason = videoDetail.value.ugcSeason!; List sections = ugcSeason.sections!; @@ -536,6 +524,11 @@ class VideoIntroController extends GetxController { List episodesList = sections[i].episodes!; episodes.addAll(episodesList); } + } else if (videoDetail.value.pages != null) { + isPages = true; + List pages = videoDetail.value.pages!; + episodes = []; + episodes.addAll(pages); } int currentIndex = episodes.indexWhere((e) => e.cid == lastPlayCid.value); @@ -554,9 +547,9 @@ class VideoIntroController extends GetxController { } } int cid = episodes[nextIndex].cid!; - String bvid = episodes[nextIndex].bvid!; - int aid = episodes[nextIndex].aid!; - changeSeasonOrbangu(bvid, cid, aid); + String rBvid = isPages ? bvid : episodes[nextIndex].bvid; + int rAid = isPages ? IdUtils.bv2av(bvid) : episodes[nextIndex].aid!; + changeSeasonOrbangu(rBvid, cid, rAid); } // 设置关注分组 From fd4eb0fad1cb009abd35f3b6e2826df92fda9f85 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 29 Oct 2023 00:03:57 +0800 Subject: [PATCH 39/47] =?UTF-8?q?mod:=20=E4=BC=98=E5=8C=96=E5=BC=B9?= =?UTF-8?q?=E5=B9=95=E8=AF=B7=E6=B1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/danmaku/controller.dart | 2 ++ lib/pages/danmaku/view.dart | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/pages/danmaku/controller.dart b/lib/pages/danmaku/controller.dart index 576d4f3c..38d09e04 100644 --- a/lib/pages/danmaku/controller.dart +++ b/lib/pages/danmaku/controller.dart @@ -10,6 +10,8 @@ class PlDanmakuController { // 按 6min 分段 int segCount = 0; List dmSegList = []; + // 已请求的段落标记 + List hasrequestSeg = []; int currentSegIndex = 1; int currentDmIndex = 0; diff --git a/lib/pages/danmaku/view.dart b/lib/pages/danmaku/view.dart index ae699b1c..317d47e9 100644 --- a/lib/pages/danmaku/view.dart +++ b/lib/pages/danmaku/view.dart @@ -95,7 +95,9 @@ class _PlDanmakuState extends State { // 根据position判断是否有已缓存弹幕。没有则请求对应段 int segIndex = (currentPosition / (6 * 60 * 1000)).ceil(); segIndex = segIndex < 1 ? 1 : segIndex; - if (ctr.dmSegList[segIndex - 1].elems.isEmpty) { + if (ctr.dmSegList[segIndex - 1].elems.isEmpty && + !ctr.hasrequestSeg.contains(segIndex - 1)) { + ctr.hasrequestSeg.add(segIndex - 1); ctr.currentSegIndex = segIndex; EasyThrottle.throttle('follow', const Duration(seconds: 1), () { ctr.queryDanmaku(); From e844870c34d216cf3b32a09832781da6db8b7624 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 29 Oct 2023 00:38:10 +0800 Subject: [PATCH 40/47] =?UTF-8?q?fix:=20=E6=90=9C=E7=B4=A2=E5=BB=BA?= =?UTF-8?q?=E8=AE=AE=E5=BC=82=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/search.dart | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/lib/http/search.dart b/lib/http/search.dart index 7b21c2cd..b94ace2c 100644 --- a/lib/http/search.dart +++ b/lib/http/search.dart @@ -39,16 +39,25 @@ class SearchHttp { static Future searchSuggest({required term}) async { var res = await Request().get(Api.serachSuggest, data: {'term': term, 'main_ver': 'v1', 'highlight': term}); - if (res.data['code'] == 0) { - if (res.data['result'] is Map) { - res.data['result']['term'] = term; + if (res.data is String) { + Map resultMap = json.decode(res.data); + if (resultMap['code'] == 0) { + if (resultMap['result'] is Map) { + resultMap['result']['term'] = term; + } + return { + 'status': true, + 'data': resultMap['result'] is Map + ? SearchSuggestModel.fromJson(resultMap['result']) + : [], + }; + } else { + return { + 'status': false, + 'data': [], + 'msg': '请求错误 🙅', + }; } - return { - 'status': true, - 'data': res.data['result'] is Map - ? SearchSuggestModel.fromJson(res.data['result']) - : [], - }; } else { return { 'status': false, From 720e9f0040156d895c9059976dd8467087da790d Mon Sep 17 00:00:00 2001 From: Riri Date: Sun, 29 Oct 2023 01:57:39 +0800 Subject: [PATCH 41/47] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E4=B8=BB?= =?UTF-8?q?=E5=8A=A8=E6=9A=82=E5=81=9C=E5=90=8E=E5=85=B6=E4=BB=96=E9=9F=B3?= =?UTF-8?q?=E9=A2=91=E5=81=9C=E6=AD=A2=E5=AF=BC=E8=87=B4=E8=A7=86=E9=A2=91?= =?UTF-8?q?=E6=81=A2=E5=A4=8D=E6=92=AD=E6=94=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/plugin/pl_player/controller.dart | 7 ++++++- lib/services/audio_session.dart | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/plugin/pl_player/controller.dart b/lib/plugin/pl_player/controller.dart index ec862738..73229b72 100644 --- a/lib/plugin/pl_player/controller.dart +++ b/lib/plugin/pl_player/controller.dart @@ -650,9 +650,14 @@ class PlPlayerController { } /// 暂停播放 - Future pause({bool notify = true}) async { + Future pause({bool notify = true, bool isInterrupt = false}) async { await _videoPlayerController?.pause(); playerStatus.status.value = PlayerStatus.paused; + + // 主动暂停时让出音频焦点 + if (!isInterrupt) { + audioSessionHandler.setActive(false); + } } /// 更改播放状态 diff --git a/lib/services/audio_session.dart b/lib/services/audio_session.dart index 3dd9db45..98707652 100644 --- a/lib/services/audio_session.dart +++ b/lib/services/audio_session.dart @@ -26,7 +26,7 @@ class AudioSessionHandler { break; case AudioInterruptionType.pause: case AudioInterruptionType.unknown: - player.pause(); + player.pause(isInterrupt: true); _playInterrupted = true; break; } From eaff4def1c98493b5632014127c6be1299f7c6f4 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 4 Nov 2023 21:07:27 +0800 Subject: [PATCH 42/47] =?UTF-8?q?mod:=20=E7=94=BB=E9=9D=A2=E6=AF=94?= =?UTF-8?q?=E4=BE=8B=20issues=20#229?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../video/detail/widgets/header_control.dart | 6 +- lib/plugin/pl_player/controller.dart | 68 +++++++++++-------- 2 files changed, 43 insertions(+), 31 deletions(-) diff --git a/lib/pages/video/detail/widgets/header_control.dart b/lib/pages/video/detail/widgets/header_control.dart index 97502177..1d91bf50 100644 --- a/lib/pages/video/detail/widgets/header_control.dart +++ b/lib/pages/video/detail/widgets/header_control.dart @@ -200,7 +200,7 @@ class _HeaderControlState extends State { onPressed: () async { // setState(() => currentSpeed = i), await widget.controller!.setPlaybackSpeed(i); - SmartDialog.dismiss(); + Get.back(); }, child: Text(i.toString()), ), @@ -209,7 +209,7 @@ class _HeaderControlState extends State { onPressed: () async { // setState(() => currentSpeed = i), await widget.controller!.setPlaybackSpeed(i); - SmartDialog.dismiss(); + Get.back(); }, child: Text(i.toString()), ), @@ -229,7 +229,7 @@ class _HeaderControlState extends State { TextButton( onPressed: () async { await widget.controller!.setDefaultSpeed(); - SmartDialog.dismiss(); + Get.back(); }, child: const Text('默认速度'), ), diff --git a/lib/plugin/pl_player/controller.dart b/lib/plugin/pl_player/controller.dart index 73229b72..4d4d1a9b 100644 --- a/lib/plugin/pl_player/controller.dart +++ b/lib/plugin/pl_player/controller.dart @@ -270,14 +270,6 @@ class PlPlayerController { // 获取实例 传参 static PlPlayerController getInstance({ String videoType = 'archive', - List fits = const [ - BoxFit.contain, - BoxFit.cover, - BoxFit.fill, - BoxFit.fitHeight, - BoxFit.fitWidth, - BoxFit.scaleDown - ], }) { // 如果实例尚未创建,则创建一个新实例 _instance ??= PlPlayerController._(); @@ -766,26 +758,46 @@ class PlPlayerController { /// Toggle Change the videofit accordingly void toggleVideoFit() { - videoFitChangedTimer?.cancel(); - videoFitChanged.value = true; - // 范围内 - List attrs = videoFitType.map((e) => e['attr']).toList(); - if (attrs.indexOf(_videoFit.value) < attrs.length - 1) { - int index = attrs.indexOf(_videoFit.value); - _videoFit.value = attrs[index + 1]; - _videoFitDesc.value = videoFitType[index + 1]['desc']; - SmartDialog.showToast(videoFitType[index + 1]['desc']); - } else { - // 默认 contain - _videoFit.value = videoFitType.first['attr']; - _videoFitDesc.value = videoFitType.first['desc']; - SmartDialog.showToast(videoFitType.first['desc']); - } - videoFitChangedTimer = Timer(const Duration(seconds: 1), () { - videoFitChangedTimer = null; - videoFitChanged.value = false; - }); - setVideoFit(); + showDialog( + context: Get.context!, + builder: (context) { + return AlertDialog( + title: const Text('画面比例'), + content: StatefulBuilder(builder: (context, StateSetter setState) { + return Wrap( + alignment: WrapAlignment.start, + spacing: 8, + runSpacing: 2, + children: [ + for (var i in videoFitType) ...[ + if (_videoFit.value == i['attr']) ...[ + FilledButton( + onPressed: () async { + _videoFit.value = i['attr']; + _videoFitDesc.value = i['desc']; + setVideoFit(); + Get.back(); + }, + child: Text(i['desc']), + ), + ] else ...[ + FilledButton.tonal( + onPressed: () async { + _videoFit.value = i['attr']; + _videoFitDesc.value = i['desc']; + setVideoFit(); + Get.back(); + }, + child: Text(i['desc']), + ), + ] + ] + ], + ); + }), + ); + }, + ); } /// 缓存fit From 13ce50f730aba6a2984faada001749abca211f6f Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 6 Nov 2023 00:04:54 +0800 Subject: [PATCH 43/47] =?UTF-8?q?mod:=20=E4=BB=A3=E7=90=86=E8=AE=BE?= =?UTF-8?q?=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/init.dart | 36 +++++++++ lib/pages/setting/extra_setting.dart | 111 +++++++++++++++++++++++++++ lib/utils/proxy.dart | 28 +++++++ lib/utils/storage.dart | 5 ++ 4 files changed, 180 insertions(+) create mode 100644 lib/utils/proxy.dart diff --git a/lib/http/init.dart b/lib/http/init.dart index dbf189c6..e2b3cd1f 100644 --- a/lib/http/init.dart +++ b/lib/http/init.dart @@ -4,6 +4,7 @@ import 'dart:io'; import 'dart:async'; import 'package:dio/dio.dart'; import 'package:cookie_jar/cookie_jar.dart'; +import 'package:dio/io.dart'; import 'package:dio_http2_adapter/dio_http2_adapter.dart'; import 'package:hive/hive.dart'; import 'package:pilipala/utils/storage.dart'; @@ -17,6 +18,11 @@ class Request { static late CookieManager cookieManager; static late final Dio dio; factory Request() => _instance; + Box setting = GStrorage.setting; + static Box localCache = GStrorage.localCache; + late dynamic enableSystemProxy; + late String systemProxyHost; + late String systemProxyPort; /// 设置cookie static setCookie() async { @@ -92,6 +98,13 @@ class Request { headers: {}, ); + enableSystemProxy = + setting.get(SettingBoxKey.enableSystemProxy, defaultValue: false); + systemProxyHost = + localCache.get(LocalCacheKey.systemProxyHost, defaultValue: ''); + systemProxyPort = + localCache.get(LocalCacheKey.systemProxyPort, defaultValue: ''); + dio = Dio(options) /// fix 第三方登录 302重定向 跟iOS代理问题冲突 @@ -100,6 +113,29 @@ class Request { idleTimeout: const Duration(milliseconds: 10000), onClientCreate: (_, config) => config.onBadCertificate = (_) => true, ), + ) + + /// 设置代理 + ..httpClientAdapter = IOHttpClientAdapter( + createHttpClient: () { + final client = HttpClient(); + // Config the client. + client.findProxy = (uri) { + if (enableSystemProxy) { + print('🌹:$systemProxyHost'); + print('🌹:$systemProxyPort'); + + // return 'PROXY host:port'; + return 'PROXY $systemProxyHost:$systemProxyPort'; + } else { + // 不设置代理 + return 'DIRECT'; + } + }; + client.badCertificateCallback = + (X509Certificate cert, String host, int port) => true; + return client; + }, ); //添加拦截器 diff --git a/lib/pages/setting/extra_setting.dart b/lib/pages/setting/extra_setting.dart index 4bbb841c..6f1ff07e 100644 --- a/lib/pages/setting/extra_setting.dart +++ b/lib/pages/setting/extra_setting.dart @@ -16,8 +16,12 @@ class ExtraSetting extends StatefulWidget { class _ExtraSettingState extends State { Box setting = GStrorage.setting; + static Box localCache = GStrorage.localCache; late dynamic defaultReplySort; late dynamic defaultDynamicType; + late dynamic enableSystemProxy; + late String defaultSystemProxyHost; + late String defaultSystemProxyPort; @override void initState() { @@ -28,6 +32,86 @@ class _ExtraSettingState extends State { // 优先展示全部动态 all defaultDynamicType = setting.get(SettingBoxKey.defaultDynamicType, defaultValue: 0); + enableSystemProxy = + setting.get(SettingBoxKey.enableSystemProxy, defaultValue: false); + defaultSystemProxyHost = + localCache.get(LocalCacheKey.systemProxyHost, defaultValue: ''); + defaultSystemProxyPort = + localCache.get(LocalCacheKey.systemProxyPort, defaultValue: ''); + } + + // 设置代理 + void twoFADialog() { + var systemProxyHost = ''; + var systemProxyPort = ''; + + SmartDialog.show( + useSystem: true, + animationType: SmartAnimationType.centerFade_otherSlide, + builder: (BuildContext context) { + return AlertDialog( + title: const Text('设置代理'), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const SizedBox(height: 6), + TextField( + decoration: InputDecoration( + isDense: true, + labelText: defaultSystemProxyHost != '' + ? defaultSystemProxyHost + : '请输入Host,使用 . 分割', + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(6.0), + ), + hintText: defaultSystemProxyHost, + ), + onChanged: (e) { + systemProxyHost = e; + }, + ), + const SizedBox(height: 10), + TextField( + keyboardType: TextInputType.number, + decoration: InputDecoration( + isDense: true, + labelText: defaultSystemProxyPort != '' + ? defaultSystemProxyPort + : '请输入Port', + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(6.0), + ), + hintText: defaultSystemProxyPort, + ), + onChanged: (e) { + systemProxyPort = e; + }, + ), + ], + ), + actions: [ + TextButton( + onPressed: () async { + SmartDialog.dismiss(); + }, + child: Text( + '取消', + style: TextStyle(color: Theme.of(context).colorScheme.outline), + ), + ), + TextButton( + onPressed: () async { + localCache.put(LocalCacheKey.systemProxyHost, systemProxyHost); + localCache.put(LocalCacheKey.systemProxyPort, systemProxyPort); + SmartDialog.dismiss(); + // Request.dio; + }, + child: const Text('确认'), + ) + ], + ); + }, + ); } @override @@ -135,6 +219,33 @@ class _ExtraSettingState extends State { ], ), ), + ListTile( + enableFeedback: true, + onTap: () => twoFADialog(), + title: Text('设置代理', style: titleStyle), + subtitle: Text('设置代理 host:port', style: subTitleStyle), + trailing: Transform.scale( + scale: 0.8, + child: Switch( + thumbIcon: MaterialStateProperty.resolveWith( + (Set states) { + if (states.isNotEmpty && + states.first == MaterialState.selected) { + return const Icon(Icons.done); + } + return null; // All other states will use the default thumbIcon. + }), + value: enableSystemProxy, + onChanged: (val) { + setting.put( + SettingBoxKey.enableSystemProxy, !enableSystemProxy); + setState(() { + enableSystemProxy = !enableSystemProxy; + }); + }, + ), + ), + ), const SetSwitchItem( title: '检查更新', subTitle: '每次启动时检查是否需要更新', diff --git a/lib/utils/proxy.dart b/lib/utils/proxy.dart new file mode 100644 index 00000000..cba74c42 --- /dev/null +++ b/lib/utils/proxy.dart @@ -0,0 +1,28 @@ +import 'dart:io'; +import 'package:system_proxy/system_proxy.dart'; + +class CustomProxy { + init() async { + Map? proxy = await SystemProxy.getProxySettings(); + if (proxy != null) { + HttpOverrides.global = + ProxiedHttpOverrides(proxy['host']!, proxy['port']!); + } + } +} + +class ProxiedHttpOverrides extends HttpOverrides { + final String _port; + final String _host; + + ProxiedHttpOverrides(this._host, this._port); + + @override + HttpClient createHttpClient(SecurityContext? context) { + return super.createHttpClient(context) + // set proxy + ..findProxy = (uri) { + return "PROXY $_host:$_port;"; + }; + } +} diff --git a/lib/utils/storage.dart b/lib/utils/storage.dart index dc00ec43..e0e240a1 100644 --- a/lib/utils/storage.dart +++ b/lib/utils/storage.dart @@ -125,6 +125,7 @@ class SettingBoxKey { static const String enableSearchWord = 'enableSearchWord'; static const String enableRcmdDynamic = 'enableRcmdDynamic'; static const String enableSaveLastData = 'enableSaveLastData'; + static const String enableSystemProxy = 'enableSystemProxy'; /// 外观 static const String themeMode = 'themeMode'; @@ -154,6 +155,10 @@ class LocalCacheKey { static const String danmakuOpacity = 'danmakuOpacity'; static const String danmakuFontScale = 'danmakuFontScale'; static const String danmakuSpeed = 'danmakuSpeed'; + + // 代理host port + static const String systemProxyHost = 'systemProxyHost'; + static const String systemProxyPort = 'systemProxyPort'; } class VideoBoxKey { From d105718fbf1cd9bcec697d7eba12d16205adde1d Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 11 Nov 2023 23:18:19 +0800 Subject: [PATCH 44/47] =?UTF-8?q?feat:=20app=E7=AB=AF=E7=99=BB=E5=BD=95-?= =?UTF-8?q?=E5=BC=80=E5=8F=91=E4=B8=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ios/Podfile.lock | 16 +- ios/Runner.xcodeproj/project.pbxproj | 18 ++ lib/http/api.dart | 36 +++ lib/http/init.dart | 8 +- lib/http/login.dart | 177 +++++++++++++ lib/models/login/index.dart | 49 ++++ lib/pages/login/controller.dart | 204 +++++++++++++++ lib/pages/login/index.dart | 4 + lib/pages/login/view.dart | 362 +++++++++++++++++++++++++++ lib/pages/mine/controller.dart | 1 + lib/router/app_pages.dart | 3 + lib/utils/login.dart | 30 +++ pubspec.lock | 26 +- pubspec.yaml | 4 + 14 files changed, 928 insertions(+), 10 deletions(-) create mode 100644 lib/http/login.dart create mode 100644 lib/models/login/index.dart create mode 100644 lib/pages/login/controller.dart create mode 100644 lib/pages/login/index.dart create mode 100644 lib/pages/login/view.dart diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 3e0ed4ca..9d796293 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1,8 +1,6 @@ PODS: - appscheme (1.0.4): - Flutter - - auto_orientation (0.0.1): - - Flutter - connectivity_plus (0.0.1): - Flutter - ReachabilitySwift @@ -14,6 +12,10 @@ PODS: - FMDB (2.7.5): - FMDB/standard (= 2.7.5) - FMDB/standard (2.7.5) + - gt3_flutter_plugin (0.0.8): + - Flutter + - GT3Captcha-iOS + - GT3Captcha-iOS (0.15.8.3) - media_kit_libs_ios_video (1.0.4): - Flutter - media_kit_native_event_loop (1.0.0): @@ -54,11 +56,11 @@ PODS: DEPENDENCIES: - appscheme (from `.symlinks/plugins/appscheme/ios`) - - auto_orientation (from `.symlinks/plugins/auto_orientation/ios`) - connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`) - device_info_plus (from `.symlinks/plugins/device_info_plus/ios`) - Flutter (from `Flutter`) - flutter_volume_controller (from `.symlinks/plugins/flutter_volume_controller/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`) - media_kit_video (from `.symlinks/plugins/media_kit_video/ios`) @@ -80,13 +82,12 @@ DEPENDENCIES: SPEC REPOS: trunk: - FMDB + - GT3Captcha-iOS - ReachabilitySwift EXTERNAL SOURCES: appscheme: :path: ".symlinks/plugins/appscheme/ios" - auto_orientation: - :path: ".symlinks/plugins/auto_orientation/ios" connectivity_plus: :path: ".symlinks/plugins/connectivity_plus/ios" device_info_plus: @@ -95,6 +96,8 @@ EXTERNAL SOURCES: :path: Flutter flutter_volume_controller: :path: ".symlinks/plugins/flutter_volume_controller/ios" + gt3_flutter_plugin: + :path: ".symlinks/plugins/gt3_flutter_plugin/ios" media_kit_libs_ios_video: :path: ".symlinks/plugins/media_kit_libs_ios_video/ios" media_kit_native_event_loop: @@ -132,12 +135,13 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: appscheme: b1c3f8862331cb20430cf9e0e4af85dbc1572ad8 - auto_orientation: 102ed811a5938d52c86520ddd7ecd3a126b5d39d connectivity_plus: 07c49e96d7fc92bc9920617b83238c4d178b446a device_info_plus: 7545d84d8d1b896cb16a4ff98c19f07ec4b298ea Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 flutter_volume_controller: e4d5832f08008180f76e30faf671ffd5a425e529 FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a + gt3_flutter_plugin: bfa1f26e9a09dc00401514be5ed437f964cabf23 + GT3Captcha-iOS: 5e3b1077834d8a9d6f4d64a447a30af3e14affe6 media_kit_libs_ios_video: a5fe24bc7875ccd6378a0978c13185e1344651c1 media_kit_native_event_loop: e6b2ab20cf0746eb1c33be961fcf79667304fa2a media_kit_video: 5da63f157170e5bf303bf85453b7ef6971218a2e diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 6f46bb30..eeb03bef 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -140,6 +140,7 @@ 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 5A372F23F3CF0118D6526BAC /* [CP] Embed Pods Frameworks */, + B78851E7B29A4C3961AC483C /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -268,6 +269,23 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; }; + B78851E7B29A4C3961AC483C /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Copy Pods Resources"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ diff --git a/lib/http/api.dart b/lib/http/api.dart index f2f06007..042d8e11 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -336,4 +336,40 @@ class Api { /// w_rid=1607c6c5a4a35a1297e31992220900ae& /// wts=1697033079 static const String aiConclusion = '/x/web-interface/view/conclusion/get'; + + // captcha验证码 + static const String getCaptcha = + 'https://passport.bilibili.com/x/passport-login/captcha?source=main_web'; + + // web端短信验证码 + static const String smsCode = + 'https://passport.bilibili.com/x/passport-login/web/sms/send'; + + // web端验证码登录 + + // web端密码登录 + + // app端短信验证码 + static const String appSmsCode = + 'https://passport.bilibili.com/x/passport-login/sms/send'; + + // app端验证码登录 + + // 获取短信验证码 + // static const String appSafeSmsCode = + // 'https://passport.bilibili.com/x/safecenter/common/sms/send'; + + /// app端密码登录 + /// username + /// password + /// key + /// rhash + static const String loginInByPwdApi = + 'https://passport.bilibili.com/x/passport-login/oauth2/login'; + + /// 密码加密密钥 + /// disable_rcmd + /// local_id + static const getWebKey = + 'https://passport.bilibili.com/x/passport-login/web/key'; } diff --git a/lib/http/init.dart b/lib/http/init.dart index e2b3cd1f..6a60dca0 100644 --- a/lib/http/init.dart +++ b/lib/http/init.dart @@ -47,8 +47,8 @@ class Request { log("setCookie, ${e.toString()}"); } } - setOptionsHeaders(userInfo); } + setOptionsHeaders(userInfo, userInfo != null && userInfo.mid != null); if (cookie.isEmpty) { try { @@ -73,8 +73,10 @@ class Request { return token; } - static setOptionsHeaders(userInfo) { - dio.options.headers['x-bili-mid'] = userInfo.mid.toString(); + static setOptionsHeaders(userInfo, status) { + if (status) { + dio.options.headers['x-bili-mid'] = userInfo.mid.toString(); + } dio.options.headers['env'] = 'prod'; dio.options.headers['app-key'] = 'android64'; dio.options.headers['x-bili-aurora-eid'] = 'UlMFQVcABlAH'; diff --git a/lib/http/login.dart b/lib/http/login.dart new file mode 100644 index 00000000..8d2a254e --- /dev/null +++ b/lib/http/login.dart @@ -0,0 +1,177 @@ +import 'dart:convert'; +import 'dart:math'; +import 'package:crypto/crypto.dart'; + +import 'package:dio/dio.dart'; +import 'package:encrypt/encrypt.dart'; +import 'package:pilipala/http/index.dart'; +import 'package:pilipala/models/login/index.dart'; +import 'package:pilipala/utils/login.dart'; +import 'package:uuid/uuid.dart'; + +class LoginHttp { + static Future queryCaptcha() async { + var res = await Request().get(Api.getCaptcha); + if (res.data['code'] == 0) { + return { + 'status': true, + 'data': CaptchaDataModel.fromJson(res.data['data']), + }; + } else { + return {'status': false, 'data': res.message}; + } + } + + static Future sendSmsCode({ + int? cid, + required int tel, + required String token, + required String challenge, + required String validate, + required String seccode, + }) async { + var res = await Request().post( + Api.appSmsCode, + data: { + 'cid': cid, + 'tel': tel, + "source": "main_web", + 'token': token, + 'challenge': challenge, + 'validate': validate, + 'seccode': seccode, + }, + options: Options( + contentType: Headers.formUrlEncodedContentType, + // headers: {'user-agent': ApiConstants.userAgent} + ), + ); + print(res); + } + + // web端验证码 + static Future sendWebSmsCode({ + int? cid, + required int tel, + required String token, + required String challenge, + required String validate, + required String seccode, + }) async { + Map data = { + 'cid': cid, + 'tel': tel, + 'token': token, + 'challenge': challenge, + 'validate': validate, + 'seccode': seccode, + }; + FormData formData = FormData.fromMap({...data}); + var res = await Request().post( + Api.smsCode, + data: formData, + options: Options( + contentType: Headers.formUrlEncodedContentType, + ), + ); + print(res); + } + + // web端验证码登录 + static Future loginInByWebSmsCode() async {} + + // web端密码登录 + static Future liginInByWebPwd() async {} + + // app端验证码 + static Future sendAppSmsCode({ + int? cid, + required int tel, + required String token, + required String challenge, + required String validate, + required String seccode, + }) async { + Map data = { + 'cid': cid, + 'tel': tel, + 'login_session_id': const Uuid().v4().replaceAll('-', ''), + 'recaptcha_token': token, + 'gee_challenge': challenge, + 'gee_validate': validate, + 'gee_seccode': seccode, + 'channel': 'bili', + 'buvid': buvid(), + 'local_id': buvid(), + // 'ts': DateTime.now().millisecondsSinceEpoch ~/ 1000, + 'statistics': { + "appId": 1, + "platform": 3, + "version": "7.52.0", + "abtest": "" + }, + }; + // FormData formData = FormData.fromMap({...data}); + var res = await Request().post( + Api.appSmsCode, + data: data, + options: Options( + contentType: Headers.formUrlEncodedContentType, + ), + ); + print(res); + } + + static String buvid() { + var mac = []; + var random = Random(); + + for (var i = 0; i < 6; i++) { + var min = 0; + var max = 0xff; + var num = (random.nextInt(max - min + 1) + min).toRadixString(16); + mac.add(num); + } + + var md5Str = md5.convert(utf8.encode(mac.join(':'))).toString(); + var md5Arr = md5Str.split(''); + return 'XY${md5Arr[2]}${md5Arr[12]}${md5Arr[22]}$md5Str'; + } + + // 获取盐hash跟PubKey + static Future getWebKey() async { + var res = await Request().get(Api.getWebKey, + data: {'disable_rcmd': 0, 'local_id': LoginUtils.generateBuvid()}); + if (res.data['code'] == 0) { + return {'status': true, 'data': res.data['data']}; + } else { + return {'status': false, 'data': {}, 'msg': res.data['message']}; + } + } + + // app端密码登录 + static Future loginInByMobPwd({ + required String tel, + required String password, + required String key, + required String rhash, + }) async { + dynamic publicKey = RSAKeyParser().parse(key); + String passwordEncryptyed = + Encrypter(RSA(publicKey: publicKey)).encrypt(rhash + password).base64; + Map data = { + 'username': tel, + 'password': passwordEncryptyed, + 'local_id': LoginUtils.generateBuvid(), + 'disable_rcmd': "0", + }; + var res = await Request().post( + Api.loginInByPwdApi, + data: data, + options: Options( + contentType: Headers.formUrlEncodedContentType, + ), + ); + print(res); + } +} diff --git a/lib/models/login/index.dart b/lib/models/login/index.dart new file mode 100644 index 00000000..a4f2e3c0 --- /dev/null +++ b/lib/models/login/index.dart @@ -0,0 +1,49 @@ +class CaptchaDataModel { + CaptchaDataModel({ + this.type, + this.token, + this.geetest, + this.tencent, + this.validate, + this.seccode, + }); + + String? type; + String? token; + GeetestData? geetest; + Tencent? tencent; + String? validate; + String? seccode; + + CaptchaDataModel.fromJson(Map json) { + type = json["type"]; + token = json["token"]; + geetest = + json["geetest"] != null ? GeetestData.fromJson(json["geetest"]) : null; + tencent = + json["tencent"] != null ? Tencent.fromJson(json["tencent"]) : null; + } +} + +class GeetestData { + GeetestData({ + this.challenge, + this.gt, + }); + + String? challenge; + String? gt; + + GeetestData.fromJson(Map json) { + challenge = json["challenge"]; + gt = json["gt"]; + } +} + +class Tencent { + Tencent({this.appid}); + String? appid; + Tencent.fromJson(Map json) { + appid = json["appid"]; + } +} diff --git a/lib/pages/login/controller.dart b/lib/pages/login/controller.dart new file mode 100644 index 00000000..c002fdf9 --- /dev/null +++ b/lib/pages/login/controller.dart @@ -0,0 +1,204 @@ +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; +import 'package:get/get.dart'; +import 'package:pilipala/http/login.dart'; +import 'package:gt3_flutter_plugin/gt3_flutter_plugin.dart'; +import 'package:pilipala/models/login/index.dart'; + +class LoginPageController extends GetxController { + final GlobalKey mobFormKey = GlobalKey(); + final GlobalKey passwordFormKey = GlobalKey(); + final GlobalKey msgCodeFormKey = GlobalKey(); + + final TextEditingController mobTextController = TextEditingController(); + final TextEditingController passwordTextController = TextEditingController(); + final TextEditingController msgCodeTextController = TextEditingController(); + + final FocusNode mobTextFieldNode = FocusNode(); + final FocusNode passwordTextFieldNode = FocusNode(); + final FocusNode msgCodeTextFieldNode = FocusNode(); + + final PageController pageViewController = PageController(); + + RxInt currentIndex = 0.obs; + + final Gt3FlutterPlugin captcha = Gt3FlutterPlugin(); + + // 默认密码登录 + RxInt loginType = 0.obs; + + // 监听pageView切换 + void onPageChange(int index) { + currentIndex.value = index; + } + + // 输入手机号 下一页 + void nextStep() async { + if ((mobFormKey.currentState as FormState).validate()) { + await pageViewController.animateToPage( + 1, + duration: const Duration(microseconds: 3000), + curve: Curves.easeInOut, + ); + passwordTextFieldNode.requestFocus(); + } + } + + // 上一页 + void previousPage() async { + passwordTextFieldNode.unfocus(); + await Future.delayed(const Duration(milliseconds: 200)); + pageViewController.animateToPage( + 0, + duration: const Duration(microseconds: 300), + curve: Curves.easeInOut, + ); + } + + // 切换登录方式 + void changeLoginType() { + loginType.value = loginType.value == 0 ? 1 : 0; + if (loginType.value == 0) { + passwordTextFieldNode.requestFocus(); + } else { + msgCodeTextFieldNode.requestFocus(); + } + } + + // app端密码登录 + void loginInByAppPassword() async { + if ((passwordFormKey.currentState as FormState).validate()) { + var webKeyRes = await LoginHttp.getWebKey(); + if (webKeyRes['status']) { + String rhash = webKeyRes['data']['hash']; + String key = webKeyRes['data']['key']; + LoginHttp.loginInByMobPwd( + tel: mobTextController.text, + password: passwordTextController.text, + key: key, + rhash: rhash, + ); + } else { + SmartDialog.showToast(webKeyRes['msg']); + } + } + } + + // 验证码登录 + void loginInByCode() { + if ((msgCodeFormKey.currentState as FormState).validate()) {} + } + + // app端验证码 + void getMsgCode() async { + getCaptcha((data) async { + CaptchaDataModel captchaData = data; + var res = await LoginHttp.sendAppSmsCode( + cid: 86, + tel: 13734077064, + token: captchaData.token!, + challenge: captchaData.geetest!.challenge!, + validate: captchaData.validate!, + seccode: captchaData.seccode!, + ); + print(res); + }); + } + + // 申请极验验证码 + Future getCaptcha(oncall) async { + SmartDialog.showLoading(msg: '请求中...'); + var result = await LoginHttp.queryCaptcha(); + if (result['status']) { + CaptchaDataModel captchaData = result['data']; + var registerData = Gt3RegisterData( + challenge: captchaData.geetest!.challenge, + gt: captchaData.geetest!.gt!, + success: true, + ); + captcha.addEventHandler(onShow: (Map message) async { + SmartDialog.dismiss(); + }, onClose: (Map message) async { + SmartDialog.showToast('关闭验证'); + }, onResult: (Map message) async { + debugPrint("Captcha result: $message"); + String code = message["code"]; + if (code == "1") { + // 发送 message["result"] 中的数据向 B 端的业务服务接口进行查询 + SmartDialog.showToast('验证成功'); + captchaData.validate = message['result']['geetest_validate']; + captchaData.seccode = message['result']['geetest_seccode']; + captchaData.geetest!.challenge = + message['result']['geetest_challenge']; + oncall(captchaData); + } else { + // 终端用户完成验证失败,自动重试 If the verification fails, it will be automatically retried. + debugPrint("Captcha result code : $code"); + } + }, onError: (Map message) async { + String code = message["code"]; + + // 处理验证中返回的错误 Handling errors returned in verification + if (Platform.isAndroid) { + // Android 平台 + if (code == "-2") { + // Dart 调用异常 Call exception + } else if (code == "-1") { + // Gt3RegisterData 参数不合法 Parameter is invalid + } else if (code == "201") { + // 网络无法访问 Network inaccessible + } else if (code == "202") { + // Json 解析错误 Analysis error + } else if (code == "204") { + // WebView 加载超时,请检查是否混淆极验 SDK Load timed out + } else if (code == "204_1") { + // WebView 加载前端页面错误,请查看日志 Error loading front-end page, please check the log + } else if (code == "204_2") { + // WebView 加载 SSLError + } else if (code == "206") { + // gettype 接口错误或返回为 null API error or return null + } else if (code == "207") { + // getphp 接口错误或返回为 null API error or return null + } else if (code == "208") { + // ajax 接口错误或返回为 null API error or return null + } else { + // 更多错误码参考开发文档 More error codes refer to the development document + // https://docs.geetest.com/sensebot/apirefer/errorcode/android + } + } + + if (Platform.isIOS) { + // iOS 平台 + if (code == "-1009") { + // 网络无法访问 Network inaccessible + } else if (code == "-1004") { + // 无法查找到 HOST Unable to find HOST + } else if (code == "-1002") { + // 非法的 URL Illegal URL + } else if (code == "-1001") { + // 网络超时 Network timeout + } else if (code == "-999") { + // 请求被意外中断, 一般由用户进行取消操作导致 The interrupted request was usually caused by the user cancelling the operation + } else if (code == "-21") { + // 使用了重复的 challenge Duplicate challenges are used + // 检查获取 challenge 是否进行了缓存 Check if the fetch challenge is cached + } else if (code == "-20") { + // 尝试过多, 重新引导用户触发验证即可 Try too many times, lead the user to request verification again + } else if (code == "-10") { + // 预判断时被封禁, 不会再进行图形验证 Banned during pre-judgment, and no more image captcha verification + } else if (code == "-2") { + // Dart 调用异常 Call exception + } else if (code == "-1") { + // Gt3RegisterData 参数不合法 Parameter is invalid + } else { + // 更多错误码参考开发文档 More error codes refer to the development document + // https://docs.geetest.com/sensebot/apirefer/errorcode/ios + } + } + }); + captcha.startCaptcha(registerData); + } else {} + } +} diff --git a/lib/pages/login/index.dart b/lib/pages/login/index.dart new file mode 100644 index 00000000..cdc05abd --- /dev/null +++ b/lib/pages/login/index.dart @@ -0,0 +1,4 @@ +library login; + +export './controller.dart'; +export 'view.dart'; diff --git a/lib/pages/login/view.dart b/lib/pages/login/view.dart new file mode 100644 index 00000000..6521e9d9 --- /dev/null +++ b/lib/pages/login/view.dart @@ -0,0 +1,362 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +import 'controller.dart'; + +class LoginPage extends StatefulWidget { + const LoginPage({super.key}); + + @override + State createState() => _LoginPageState(); +} + +class _LoginPageState extends State { + final LoginPageController _loginPageCtr = Get.put(LoginPageController()); + + @override + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + leading: Obx( + () => _loginPageCtr.currentIndex.value == 0 + ? IconButton( + onPressed: () async { + _loginPageCtr.mobTextFieldNode.unfocus(); + await Future.delayed(const Duration(milliseconds: 200)); + Get.back(); + }, + icon: const Icon(Icons.close_outlined), + ) + : IconButton( + onPressed: () => _loginPageCtr.previousPage(), + icon: const Icon(Icons.arrow_back), + ), + ), + ), + body: PageView( + physics: const NeverScrollableScrollPhysics(), + controller: _loginPageCtr.pageViewController, + onPageChanged: (int index) => _loginPageCtr.onPageChange(index), + children: [ + Padding( + padding: EdgeInsets.only( + left: 20, + right: 20, + top: 10, + bottom: MediaQuery.of(context).padding.bottom + 10, + ), + child: Form( + key: _loginPageCtr.mobFormKey, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.max, + children: [ + Text( + '登录', + style: Theme.of(context).textTheme.titleLarge!.copyWith( + letterSpacing: 1, + height: 2.1, + fontSize: 34, + fontWeight: FontWeight.w500), + ), + Row( + children: [ + Text( + '请使用您的 BiliBili 账号登录。', + style: Theme.of(context).textTheme.titleSmall!, + ), + GestureDetector( + onTap: () {}, + child: const Icon(Icons.info_outline, size: 16), + ) + ], + ), + Container( + margin: const EdgeInsets.only(top: 38, bottom: 15), + child: TextFormField( + controller: _loginPageCtr.mobTextController, + focusNode: _loginPageCtr.mobTextFieldNode, + keyboardType: TextInputType.number, + decoration: InputDecoration( + isDense: true, + labelText: '输入手机号码', + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(6.0), + ), + ), + // 校验用户名 + validator: (v) { + return v!.trim().isNotEmpty ? null : "手机号码不能为空"; + }, + onSaved: (val) { + print(val); + }, + onEditingComplete: () { + _loginPageCtr.nextStep(); + }, + ), + ), + GestureDetector( + onTap: () { + Get.offNamed( + '/webview', + parameters: { + 'url': + 'https://passport.bilibili.com/h5-app/passport/login', + 'type': 'login', + 'pageTitle': '登录bilibili', + }, + ); + }, + child: Padding( + padding: const EdgeInsets.only(left: 2), + child: Text( + '使用网页端登录', + style: TextStyle( + color: Theme.of(context).colorScheme.primary), + ), + ), + ), + const Spacer(), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + TextButton(onPressed: () {}, child: const Text('中国大陆')), + TextButton( + style: TextButton.styleFrom( + padding: const EdgeInsets.fromLTRB(20, 0, 20, 0), + foregroundColor: + Theme.of(context).colorScheme.onPrimary, + backgroundColor: + Theme.of(context).colorScheme.primary, // 设置按钮背景色 + ), + onPressed: () => _loginPageCtr.nextStep(), + child: const Text('下一步'), + ) + ], + ), + ], + ), + ), + ), + Padding( + padding: EdgeInsets.only( + left: 20, + right: 20, + top: 10, + bottom: MediaQuery.of(context).padding.bottom + 10, + ), + child: Obx( + () => _loginPageCtr.loginType.value == 0 + ? Form( + key: _loginPageCtr.passwordFormKey, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.max, + children: [ + Row( + children: [ + Text( + '密码登录', + style: Theme.of(context) + .textTheme + .titleLarge! + .copyWith( + letterSpacing: 1, + height: 2.1, + fontSize: 34, + fontWeight: FontWeight.w500), + ), + const SizedBox(width: 4), + IconButton( + style: ButtonStyle( + backgroundColor: + MaterialStateProperty.resolveWith( + (states) { + return Theme.of(context) + .colorScheme + .primary + .withOpacity(0.1); + }), + ), + onPressed: () => + _loginPageCtr.changeLoginType(), + icon: const Icon(Icons.swap_vert_outlined), + ) + ], + ), + Text( + '请输入您的 BiliBili 密码。', + style: Theme.of(context).textTheme.titleSmall!, + ), + Container( + margin: const EdgeInsets.only(top: 38, bottom: 15), + child: TextFormField( + controller: _loginPageCtr.passwordTextController, + focusNode: _loginPageCtr.passwordTextFieldNode, + keyboardType: TextInputType.visiblePassword, + decoration: InputDecoration( + isDense: true, + labelText: '输入密码', + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(6.0), + ), + ), + // 校验用户名 + validator: (v) { + return v!.trim().isNotEmpty ? null : "密码不能为空"; + }, + onSaved: (val) { + print(val); + }, + ), + ), + const Spacer(), + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + TextButton( + onPressed: () => _loginPageCtr.previousPage(), + child: const Text('上一步'), + ), + const SizedBox(width: 15), + TextButton( + style: TextButton.styleFrom( + padding: + const EdgeInsets.fromLTRB(20, 0, 20, 0), + foregroundColor: + Theme.of(context).colorScheme.onPrimary, + backgroundColor: Theme.of(context) + .colorScheme + .primary, // 设置按钮背景色 + ), + onPressed: () => + _loginPageCtr.loginInByAppPassword(), + child: const Text('确认登录'), + ) + ], + ), + ], + ), + ) + : Form( + key: _loginPageCtr.msgCodeFormKey, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.max, + children: [ + Row( + children: [ + Text( + '验证码登录', + style: Theme.of(context) + .textTheme + .titleLarge! + .copyWith( + letterSpacing: 1, + height: 2.1, + fontSize: 34, + fontWeight: FontWeight.w500), + ), + const SizedBox(width: 4), + IconButton( + style: ButtonStyle( + backgroundColor: + MaterialStateProperty.resolveWith( + (states) { + return Theme.of(context) + .colorScheme + .primary + .withOpacity(0.1); + }), + ), + onPressed: () => + _loginPageCtr.changeLoginType(), + icon: const Icon(Icons.swap_vert_outlined), + ) + ], + ), + Text( + '请输入收到到验证码。', + style: Theme.of(context).textTheme.titleSmall!, + ), + Container( + margin: const EdgeInsets.only(top: 38, bottom: 15), + child: Stack( + children: [ + TextFormField( + controller: + _loginPageCtr.msgCodeTextController, + focusNode: _loginPageCtr.msgCodeTextFieldNode, + maxLength: 6, + keyboardType: TextInputType.number, + decoration: InputDecoration( + isDense: true, + labelText: '输入验证码', + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(6.0), + ), + ), + // 校验用户名 + validator: (v) { + return v!.trim().isNotEmpty + ? null + : "验证码不能为空"; + }, + onSaved: (val) { + print(val); + }, + ), + Positioned( + right: 8, + top: 4, + child: Center( + child: TextButton( + onPressed: () => + _loginPageCtr.getMsgCode(), + child: const Text('获取验证码'), + ), + ), + ), + ], + ), + ), + const Spacer(), + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + TextButton( + onPressed: () => _loginPageCtr.previousPage(), + child: const Text('上一步'), + ), + const SizedBox(width: 15), + TextButton( + style: TextButton.styleFrom( + padding: + const EdgeInsets.fromLTRB(20, 0, 20, 0), + foregroundColor: + Theme.of(context).colorScheme.onPrimary, + backgroundColor: Theme.of(context) + .colorScheme + .primary, // 设置按钮背景色 + ), + onPressed: () => _loginPageCtr.loginInByCode(), + child: const Text('确认登录'), + ) + ], + ), + ], + ), + ), + ), + ), + ], + ), + ); + } +} diff --git a/lib/pages/mine/controller.dart b/lib/pages/mine/controller.dart index 772ba06a..2b53850b 100644 --- a/lib/pages/mine/controller.dart +++ b/lib/pages/mine/controller.dart @@ -41,6 +41,7 @@ class MineController extends GetxController { 'pageTitle': '登录bilibili', }, ); + // Get.toNamed('/loginPage'); } else { int mid = userInfo.value.mid!; String face = userInfo.value.face!; diff --git a/lib/router/app_pages.dart b/lib/router/app_pages.dart index 544217f9..6accf4f1 100644 --- a/lib/router/app_pages.dart +++ b/lib/router/app_pages.dart @@ -19,6 +19,7 @@ import 'package:pilipala/pages/hot/index.dart'; import 'package:pilipala/pages/html/index.dart'; import 'package:pilipala/pages/later/index.dart'; import 'package:pilipala/pages/liveRoom/view.dart'; +import 'package:pilipala/pages/login/index.dart'; import 'package:pilipala/pages/member/index.dart'; import 'package:pilipala/pages/member_search/index.dart'; import 'package:pilipala/pages/preview/index.dart'; @@ -122,6 +123,8 @@ class Routes { CustomGetPage(name: '/playSpeedSet', page: () => const PlaySpeedPage()), // 收藏搜索 CustomGetPage(name: '/favSearch', page: () => const FavSearchPage()), + // 登录页面 + CustomGetPage(name: '/loginPage', page: () => const LoginPage()), ]; } diff --git a/lib/utils/login.dart b/lib/utils/login.dart index 54c03775..59c53027 100644 --- a/lib/utils/login.dart +++ b/lib/utils/login.dart @@ -1,9 +1,14 @@ +import 'dart:convert'; +import 'dart:math'; + +import 'package:crypto/crypto.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:pilipala/pages/dynamics/index.dart'; import 'package:pilipala/pages/home/index.dart'; import 'package:pilipala/pages/media/index.dart'; import 'package:pilipala/pages/mine/index.dart'; +import 'package:uuid/uuid.dart'; class LoginUtils { static Future refreshLoginStatus(bool status) async { @@ -27,4 +32,29 @@ class LoginUtils { SmartDialog.showToast('refreshLoginStatus error: ${err.toString()}'); } } + + static String buvid() { + var mac = []; + var random = Random(); + + for (var i = 0; i < 6; i++) { + var min = 0; + var max = 0xff; + var num = (random.nextInt(max - min + 1) + min).toRadixString(16); + mac.add(num); + } + + var md5Str = md5.convert(utf8.encode(mac.join(':'))).toString(); + var md5Arr = md5Str.split(''); + return 'XY${md5Arr[2]}${md5Arr[12]}${md5Arr[22]}$md5Str'; + } + + static String getUUID() { + return const Uuid().v4().replaceAll('-', ''); + } + + static String generateBuvid() { + String uuid = getUUID() + getUUID(); + return 'XY${uuid.substring(0, 35).toUpperCase()}'; + } } diff --git a/pubspec.lock b/pubspec.lock index 4cbbd2e9..a70c3553 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -49,6 +49,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.4.2" + asn1lib: + dependency: transitive + description: + name: asn1lib + sha256: "21afe4333076c02877d14f4a89df111e658a6d466cbfc802eb705eb91bd5adfd" + url: "https://pub.dev" + source: hosted + version: "1.5.0" async: dependency: transitive description: @@ -369,6 +377,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.3" + encrypt: + dependency: "direct main" + description: + name: encrypt + sha256: "62d9aa4670cc2a8798bab89b39fc71b6dfbacf615de6cf5001fb39f7e4a996a2" + url: "https://pub.dev" + source: hosted + version: "5.0.3" extended_image: dependency: "direct main" description: @@ -581,6 +597,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.1" + gt3_flutter_plugin: + dependency: "direct main" + description: + name: gt3_flutter_plugin + sha256: f12bff2bfbcf27467833f8d564dcc24ee2f1b3254a7c7cf5eb2c4590baf11cc1 + url: "https://pub.dev" + source: hosted + version: "0.0.8" hive: dependency: "direct main" description: @@ -1388,7 +1412,7 @@ packages: source: hosted version: "3.0.7" uuid: - dependency: transitive + dependency: "direct main" description: name: uuid sha256: "648e103079f7c64a36dc7d39369cabb358d377078a051d6ae2ad3aa539519313" diff --git a/pubspec.yaml b/pubspec.yaml index a676942b..090e8a38 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -82,6 +82,7 @@ dependencies: custom_sliding_segmented_control: ^1.7.5 # 加密 crypto: ^3.0.3 + encrypt: ^5.0.3 # 视频播放器 media_kit: ^1.1.10 # Primary package. @@ -124,6 +125,9 @@ dependencies: html: ^0.15.4 # html渲染 flutter_html: ^3.0.0-beta.2 + # 极验 + gt3_flutter_plugin: ^0.0.8 + uuid: ^3.0.7 dev_dependencies: From 96523a99ce0065af4d292b8b944b075b4d3f7de2 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 11 Nov 2023 23:39:54 +0800 Subject: [PATCH 45/47] =?UTF-8?q?feat:=20=E9=95=BF=E6=8C=89=E5=88=A0?= =?UTF-8?q?=E9=99=A4=E6=90=9C=E7=B4=A2=E8=AE=B0=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/search/controller.dart | 7 +++++ lib/pages/search/view.dart | 32 +++++++++++++---------- lib/pages/search/widgets/search_text.dart | 13 +++++++-- 3 files changed, 36 insertions(+), 16 deletions(-) diff --git a/lib/pages/search/controller.dart b/lib/pages/search/controller.dart index b95b048b..59d51a41 100644 --- a/lib/pages/search/controller.dart +++ b/lib/pages/search/controller.dart @@ -117,6 +117,13 @@ class SSearchController extends GetxController { submit(); } + onLongSelect(word) { + int index = historyList.indexOf(word); + historyList.value = historyList.removeAt(index); + historyList.refresh(); + histiryWord.put('cacheList', historyList); + } + onClearHis() { historyList.value = []; historyCacheList = []; diff --git a/lib/pages/search/view.dart b/lib/pages/search/view.dart index 81f56ce0..0ec910f1 100644 --- a/lib/pages/search/view.dart +++ b/lib/pages/search/view.dart @@ -299,20 +299,24 @@ class _SearchPageState extends State with RouteAware { ), ), // if (_searchController.historyList.isNotEmpty) - Wrap( - spacing: 8, - runSpacing: 8, - direction: Axis.horizontal, - textDirection: TextDirection.ltr, - children: [ - for (int i = 0; i < _searchController.historyList.length; i++) - SearchText( - searchText: _searchController.historyList[i], - searchTextIdx: i, - onSelect: (value) => _searchController.onSelect(value), - ) - ], - ), + Obx(() => Wrap( + spacing: 8, + runSpacing: 8, + direction: Axis.horizontal, + textDirection: TextDirection.ltr, + children: [ + for (int i = 0; + i < _searchController.historyList.length; + i++) + SearchText( + searchText: _searchController.historyList[i], + searchTextIdx: i, + onSelect: (value) => _searchController.onSelect(value), + onLongSelect: (value) => + _searchController.onLongSelect(value), + ) + ], + )), ], ), ), diff --git a/lib/pages/search/widgets/search_text.dart b/lib/pages/search/widgets/search_text.dart index 9f5f84c3..039a851b 100644 --- a/lib/pages/search/widgets/search_text.dart +++ b/lib/pages/search/widgets/search_text.dart @@ -4,8 +4,14 @@ class SearchText extends StatelessWidget { final String? searchText; final Function? onSelect; final int? searchTextIdx; - const SearchText( - {super.key, this.searchText, this.onSelect, this.searchTextIdx}); + final Function? onLongSelect; + const SearchText({ + super.key, + this.searchText, + this.onSelect, + this.searchTextIdx, + this.onLongSelect, + }); @override Widget build(BuildContext context) { @@ -18,6 +24,9 @@ class SearchText extends StatelessWidget { onTap: () { onSelect!(searchText); }, + onLongPress: () { + onLongSelect!(searchText); + }, borderRadius: BorderRadius.circular(6), child: Padding( padding: From 5edcc756a05890d5d504eb350c121f61e87eef5e Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 12 Nov 2023 00:10:24 +0800 Subject: [PATCH 46/47] =?UTF-8?q?fix:=20=E6=A8=AA=E5=B1=8F=E7=8A=B6?= =?UTF-8?q?=E6=80=81=E8=AE=BE=E7=BD=AE=E9=9D=A2=E6=9D=BF=E6=BB=91=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/widgets/header_control.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/pages/video/detail/widgets/header_control.dart b/lib/pages/video/detail/widgets/header_control.dart index 97502177..78182927 100644 --- a/lib/pages/video/detail/widgets/header_control.dart +++ b/lib/pages/video/detail/widgets/header_control.dart @@ -89,7 +89,6 @@ class _HeaderControlState extends State { Expanded( child: Material( child: ListView( - physics: const NeverScrollableScrollPhysics(), children: [ ListTile( onTap: () {}, From 27e268b2a074d04339555147f1cc9369a5a74976 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 12 Nov 2023 11:52:51 +0800 Subject: [PATCH 47/47] =?UTF-8?q?v1.0.11=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.11.1112.md | 26 ++++++++++++++++++++++++++ pubspec.yaml | 2 +- 2 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 change_log/1.0.11.1112.md diff --git a/change_log/1.0.11.1112.md b/change_log/1.0.11.1112.md new file mode 100644 index 00000000..4292048c --- /dev/null +++ b/change_log/1.0.11.1112.md @@ -0,0 +1,26 @@ +## 1.0.11 + +### 新功能 ++ 适配了原生媒体通知栏 @Daydreamer-riri ++ 视频主题图标 @Daydreamer-riri ++ 关闭软件后自动画中画播放 ++ UP主分组管理 ++ md2样式底栏 ++ + + +### 修复 ++ 历史记录记忆播放 ++ 部分类型视频连播 ++ 播放速度选择框不支持返回手势 ++ 播放速度选择框不支持返回手势 ++ 视频播放速度总是显示1.0X ++ 评论页面计数错误 ++ 退出视频还有声音 + + +### 优化 ++ 视频加载速度 + +更多更新日志可在Github上查看 +问题反馈、功能建议请查看「关于」页面。 \ No newline at end of file diff --git a/pubspec.yaml b/pubspec.yaml index 37320439..616e99e7 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.10 +version: 1.0.11 environment: sdk: ">=2.19.6 <3.0.0"