├── lib ├── pages │ ├── main_search │ │ ├── index.dart │ │ └── controller.dart │ ├── user │ │ └── fav_list │ │ │ ├── index.dart │ │ │ └── controller.dart │ ├── main │ │ ├── bili_music │ │ │ ├── index.dart │ │ │ ├── controller.dart │ │ │ └── view.dart │ │ ├── user_info │ │ │ ├── index.dart │ │ │ └── controller.dart │ │ ├── video_music │ │ │ └── index.dart │ │ └── home │ │ │ └── view.dart │ └── setting │ │ └── setting.dart ├── common │ ├── weight │ │ ├── qr_login_dialog │ │ │ ├── index.dart │ │ │ ├── controller.dart │ │ │ └── view.dart │ │ ├── hots_tag_shimmer.dart │ │ ├── shimmer │ │ │ ├── search_squear_list_shimmer.dart │ │ │ ├── user_page_shimmer.dart │ │ │ ├── video_card_grid_view_shimmer.dart │ │ │ └── fav_list_card_shimmer.dart │ │ ├── assemble_animated_opacity.dart │ │ ├── router │ │ │ └── search_page_router.dart │ │ ├── search │ │ │ └── search_text.dart │ │ ├── video_cover_image.dart │ │ ├── common_error.dart │ │ ├── polygon_button.dart │ │ ├── song_sheet_card.dart │ │ ├── colorful_text_widget.dart │ │ ├── badge.dart │ │ ├── video_card_dot_page.dart │ │ └── lyric │ │ │ └── single_line_lyric.dart │ ├── api │ │ ├── res.dart │ │ ├── user_login_api.dart │ │ ├── fav_api.dart │ │ ├── interceptor │ │ │ └── login_api_interceptor.dart │ │ ├── user_info_api.dart │ │ ├── search_info_api.dart │ │ ├── api_path.dart │ │ ├── video_music_api.dart │ │ └── video_api.dart │ ├── router │ │ ├── router.dart │ │ └── b_v_t_page.dart │ ├── storage │ │ └── setting_storage.dart │ ├── StyleString.dart │ ├── model │ │ ├── local │ │ │ ├── lyric_data.dart │ │ │ └── isar │ │ │ │ ├── setting.dart │ │ │ │ └── video_audio_player_task.dart │ │ └── network │ │ │ ├── home │ │ │ └── search_criteria_emnu.dart │ │ │ ├── user │ │ │ ├── login_qrcode_info.dart │ │ │ └── login_poll_info.dart │ │ │ └── video_music │ │ │ └── host_info.dart │ ├── utils │ │ ├── screen_utils.dart │ │ ├── event_bus.dart │ │ ├── utils.dart │ │ ├── extends.dart │ │ └── http_utils.dart │ ├── di │ │ └── database_model.dart │ └── controller │ │ └── user_controller.dart ├── main_controller.dart ├── services │ ├── service_locator.dart │ └── bili_audio_service.dart └── firebase_options.dart ├── linux ├── .gitignore ├── main.cc ├── flutter │ ├── generated_plugin_registrant.h │ ├── generated_plugins.cmake │ ├── generated_plugin_registrant.cc │ └── CMakeLists.txt └── my_application.h ├── android ├── app │ ├── src │ │ ├── main │ │ │ ├── res │ │ │ │ ├── values │ │ │ │ │ ├── strings.xml │ │ │ │ │ ├── ic_launcher_background.xml │ │ │ │ │ └── styles.xml │ │ │ │ ├── mipmap-hdpi │ │ │ │ │ ├── ic_launcher.webp │ │ │ │ │ └── ic_launcher_round.webp │ │ │ │ ├── mipmap-mdpi │ │ │ │ │ ├── ic_launcher.webp │ │ │ │ │ └── ic_launcher_round.webp │ │ │ │ ├── mipmap-xhdpi │ │ │ │ │ ├── ic_launcher.webp │ │ │ │ │ └── ic_launcher_round.webp │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ │ ├── ic_launcher.webp │ │ │ │ │ └── ic_launcher_round.webp │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ │ ├── ic_launcher.webp │ │ │ │ │ └── ic_launcher_round.webp │ │ │ │ ├── raw │ │ │ │ │ └── keep.xml │ │ │ │ ├── xml │ │ │ │ │ └── network_security_config.xml │ │ │ │ ├── mipmap-anydpi-v26 │ │ │ │ │ ├── ic_launcher_round.xml │ │ │ │ │ └── ic_launcher.xml │ │ │ │ ├── drawable │ │ │ │ │ ├── launch_background.xml │ │ │ │ │ └── ic_launcher_monochrome.xml │ │ │ │ ├── drawable-v21 │ │ │ │ │ └── launch_background.xml │ │ │ │ ├── values-night │ │ │ │ │ └── styles.xml │ │ │ │ └── layout │ │ │ │ │ └── activity_main.xml │ │ │ ├── kotlin │ │ │ │ └── com │ │ │ │ │ └── imcys │ │ │ │ │ └── bilivideotunes │ │ │ │ │ └── bili_video_tunes │ │ │ │ │ ├── MainActivity.kt │ │ │ │ │ └── openApp │ │ │ │ │ ├── OpenAppPlugins.kt │ │ │ │ │ └── SaveFilePlugins.kt │ │ │ ├── java │ │ │ │ └── io │ │ │ │ │ └── flutter │ │ │ │ │ └── app │ │ │ │ │ └── FlutterMultiDexApplication.java │ │ │ └── AndroidManifest.xml │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ └── profile │ │ │ └── AndroidManifest.xml │ ├── google-services.json │ └── build.gradle.kts ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties ├── .gitignore ├── build.gradle.kts └── settings.gradle.kts ├── ios ├── Runner │ ├── Runner-Bridging-Header.h │ ├── Assets.xcassets │ │ ├── LaunchImage.imageset │ │ │ ├── LaunchImage.png │ │ │ ├── LaunchImage@2x.png │ │ │ ├── LaunchImage@3x.png │ │ │ ├── README.md │ │ │ └── Contents.json │ │ └── AppIcon.appiconset │ │ │ ├── Icon-App-20x20@1x.png │ │ │ ├── Icon-App-20x20@2x.png │ │ │ ├── Icon-App-20x20@3x.png │ │ │ ├── Icon-App-29x29@1x.png │ │ │ ├── Icon-App-29x29@2x.png │ │ │ ├── Icon-App-29x29@3x.png │ │ │ ├── Icon-App-40x40@1x.png │ │ │ ├── Icon-App-40x40@2x.png │ │ │ ├── Icon-App-40x40@3x.png │ │ │ ├── Icon-App-60x60@2x.png │ │ │ ├── Icon-App-60x60@3x.png │ │ │ ├── Icon-App-76x76@1x.png │ │ │ ├── Icon-App-76x76@2x.png │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ ├── Icon-App-83.5x83.5@2x.png │ │ │ └── Contents.json │ ├── AppDelegate.swift │ ├── GoogleService-Info.plist │ ├── Base.lproj │ │ ├── Main.storyboard │ │ └── LaunchScreen.storyboard │ └── Info.plist ├── Flutter │ ├── Debug.xcconfig │ ├── Release.xcconfig │ └── AppFrameworkInfo.plist ├── Runner.xcodeproj │ └── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ ├── WorkspaceSettings.xcsettings │ │ └── IDEWorkspaceChecks.plist ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── WorkspaceSettings.xcsettings │ │ └── IDEWorkspaceChecks.plist ├── firebase_app_id_file.json ├── RunnerTests │ └── RunnerTests.swift ├── .gitignore └── Podfile ├── web ├── favicon.png ├── icons │ ├── Icon-192.png │ ├── Icon-512.png │ ├── Icon-maskable-192.png │ └── Icon-maskable-512.png ├── manifest.json └── index.html ├── assets └── images │ └── app │ └── bili_load.png ├── macos ├── Runner │ ├── Configs │ │ ├── Debug.xcconfig │ │ ├── Release.xcconfig │ │ ├── Warnings.xcconfig │ │ └── AppInfo.xcconfig │ ├── Assets.xcassets │ │ └── AppIcon.appiconset │ │ │ ├── app_icon_16.png │ │ │ ├── app_icon_32.png │ │ │ ├── app_icon_64.png │ │ │ ├── app_icon_1024.png │ │ │ ├── app_icon_128.png │ │ │ ├── app_icon_256.png │ │ │ ├── app_icon_512.png │ │ │ └── Contents.json │ ├── AppDelegate.swift │ ├── Release.entitlements │ ├── DebugProfile.entitlements │ ├── MainFlutterWindow.swift │ ├── GoogleService-Info.plist │ └── Info.plist ├── .gitignore ├── Flutter │ ├── Flutter-Debug.xcconfig │ ├── Flutter-Release.xcconfig │ └── GeneratedPluginRegistrant.swift ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── firebase_app_id_file.json ├── Runner.xcodeproj │ └── project.xcworkspace │ │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── RunnerTests │ └── RunnerTests.swift └── Podfile ├── windows ├── runner │ ├── resources │ │ └── app_icon.ico │ ├── resource.h │ ├── utils.h │ ├── runner.exe.manifest │ ├── flutter_window.h │ ├── main.cpp │ ├── CMakeLists.txt │ ├── utils.cpp │ ├── flutter_window.cpp │ ├── Runner.rc │ └── win32_window.h ├── .gitignore └── flutter │ ├── generated_plugin_registrant.h │ ├── generated_plugins.cmake │ └── generated_plugin_registrant.cc ├── .github ├── ISSUE_TEMPLATE │ ├── 功能需求.md │ └── 错误报告.md ├── CONTRIBUTING.md └── workflows │ └── flutter.yml ├── firebase.json ├── .gitignore ├── analysis_options.yaml ├── .metadata ├── test └── widget_test.dart ├── integration_test └── app_test.dart └── README.md /lib/pages/main_search/index.dart: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /linux/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ephemeral 2 | -------------------------------------------------------------------------------- /lib/pages/user/fav_list/index.dart: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /lib/common/weight/qr_login_dialog/index.dart: -------------------------------------------------------------------------------- 1 | export 'controller.dart'; 2 | export 'view.dart'; -------------------------------------------------------------------------------- /web/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1250422131/BiliVideoTunes/HEAD/web/favicon.png -------------------------------------------------------------------------------- /lib/common/api/res.dart: -------------------------------------------------------------------------------- 1 | 2 | const String imageAVIF = '@672w_378h_1c_!web-home-common-cover.avif'; -------------------------------------------------------------------------------- /web/icons/Icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1250422131/BiliVideoTunes/HEAD/web/icons/Icon-192.png -------------------------------------------------------------------------------- /web/icons/Icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1250422131/BiliVideoTunes/HEAD/web/icons/Icon-512.png -------------------------------------------------------------------------------- /lib/pages/main/bili_music/index.dart: -------------------------------------------------------------------------------- 1 | library user_info; 2 | 3 | export 'controller.dart'; 4 | export 'view.dart'; -------------------------------------------------------------------------------- /lib/pages/main/user_info/index.dart: -------------------------------------------------------------------------------- 1 | library bili_music; 2 | 3 | export 'controller.dart'; 4 | export 'view.dart'; -------------------------------------------------------------------------------- /lib/pages/main/video_music/index.dart: -------------------------------------------------------------------------------- 1 | library video_music; 2 | 3 | export 'controller.dart'; 4 | export 'view.dart'; -------------------------------------------------------------------------------- /assets/images/app/bili_load.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1250422131/BiliVideoTunes/HEAD/assets/images/app/bili_load.png -------------------------------------------------------------------------------- /macos/Runner/Configs/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Debug.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /web/icons/Icon-maskable-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1250422131/BiliVideoTunes/HEAD/web/icons/Icon-maskable-192.png -------------------------------------------------------------------------------- /web/icons/Icon-maskable-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1250422131/BiliVideoTunes/HEAD/web/icons/Icon-maskable-512.png -------------------------------------------------------------------------------- /macos/Runner/Configs/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Release.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /macos/.gitignore: -------------------------------------------------------------------------------- 1 | # Flutter-related 2 | **/Flutter/ephemeral/ 3 | **/Pods/ 4 | 5 | # Xcode-related 6 | **/dgph 7 | **/xcuserdata/ 8 | -------------------------------------------------------------------------------- /windows/runner/resources/app_icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1250422131/BiliVideoTunes/HEAD/windows/runner/resources/app_icon.ico -------------------------------------------------------------------------------- /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /lib/pages/main/bili_music/controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:get/get.dart'; 2 | 3 | class BiLiMusicPageController extends GetxController { 4 | 5 | 6 | 7 | } 8 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1250422131/BiliVideoTunes/HEAD/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1250422131/BiliVideoTunes/HEAD/android/app/src/main/res/mipmap-mdpi/ic_launcher.webp -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1250422131/BiliVideoTunes/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1250422131/BiliVideoTunes/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1250422131/BiliVideoTunes/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /android/app/src/main/res/raw/keep.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx4G -XX:MaxMetaspaceSize=2G -XX:+HeapDumpOnOutOfMemoryError 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /macos/Flutter/Flutter-Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "ephemeral/Flutter-Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /macos/Flutter/Flutter-Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "ephemeral/Flutter-Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1250422131/BiliVideoTunes/HEAD/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1250422131/BiliVideoTunes/HEAD/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1250422131/BiliVideoTunes/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1250422131/BiliVideoTunes/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1250422131/BiliVideoTunes/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1250422131/BiliVideoTunes/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1250422131/BiliVideoTunes/HEAD/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1250422131/BiliVideoTunes/HEAD/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1250422131/BiliVideoTunes/HEAD/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png -------------------------------------------------------------------------------- /android/app/src/main/res/values/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #1A73E8 4 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1250422131/BiliVideoTunes/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1250422131/BiliVideoTunes/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1250422131/BiliVideoTunes/HEAD/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1250422131/BiliVideoTunes/HEAD/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1250422131/BiliVideoTunes/HEAD/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1250422131/BiliVideoTunes/HEAD/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1250422131/BiliVideoTunes/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1250422131/BiliVideoTunes/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1250422131/BiliVideoTunes/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1250422131/BiliVideoTunes/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1250422131/BiliVideoTunes/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1250422131/BiliVideoTunes/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1250422131/BiliVideoTunes/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1250422131/BiliVideoTunes/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1250422131/BiliVideoTunes/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1250422131/BiliVideoTunes/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1250422131/BiliVideoTunes/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1250422131/BiliVideoTunes/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1250422131/BiliVideoTunes/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1250422131/BiliVideoTunes/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1250422131/BiliVideoTunes/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /linux/main.cc: -------------------------------------------------------------------------------- 1 | #include "my_application.h" 2 | 3 | int main(int argc, char** argv) { 4 | g_autoptr(MyApplication) app = my_application_new(); 5 | return g_application_run(G_APPLICATION(app), argc, argv); 6 | } 7 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /lib/main_controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:get/get.dart'; 2 | 3 | class MainController extends GetxController { 4 | RxDouble panelPosition = 1.0.obs; 5 | 6 | RxInt currentPage = 0.obs; 7 | 8 | RxBool showBottomNav = true.obs; 9 | 10 | } 11 | -------------------------------------------------------------------------------- /lib/common/router/router.dart: -------------------------------------------------------------------------------- 1 | import 'package:bili_video_tunes/pages/main/home/view.dart'; 2 | import 'package:flutter/cupertino.dart'; 3 | 4 | import 'b_v_t_page.dart'; 5 | 6 | const rootNavigator = Navigator( 7 | pages: [ 8 | 9 | ], 10 | ); 11 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip 6 | -------------------------------------------------------------------------------- /lib/common/storage/setting_storage.dart: -------------------------------------------------------------------------------- 1 | import 'package:bili_video_tunes/common/model/local/isar/setting.dart'; 2 | import 'package:get/get.dart'; 3 | import 'package:isar/isar.dart'; 4 | 5 | class SettingStorage { 6 | IsarCollection setting = Get.find().settings; 7 | } 8 | -------------------------------------------------------------------------------- /macos/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | 4 | @NSApplicationMain 5 | class AppDelegate: FlutterAppDelegate { 6 | override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { 7 | return true 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /android/app/src/main/res/xml/network_security_config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 127.0.0.1 5 | 6 | -------------------------------------------------------------------------------- /macos/Runner/Release.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/firebase_app_id_file.json: -------------------------------------------------------------------------------- 1 | { 2 | "file_generated_by": "FlutterFire CLI", 3 | "purpose": "FirebaseAppID & ProjectID for this Firebase app in this directory", 4 | "GOOGLE_APP_ID": "1:1095515545580:ios:dd7abea4ba9216cb472ac9", 5 | "FIREBASE_PROJECT_ID": "bilivideotunes", 6 | "GCM_SENDER_ID": "1095515545580" 7 | } -------------------------------------------------------------------------------- /macos/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /macos/firebase_app_id_file.json: -------------------------------------------------------------------------------- 1 | { 2 | "file_generated_by": "FlutterFire CLI", 3 | "purpose": "FirebaseAppID & ProjectID for this Firebase app in this directory", 4 | "GOOGLE_APP_ID": "1:1095515545580:ios:aef0ba89c8a11c2e472ac9", 5 | "FIREBASE_PROJECT_ID": "bilivideotunes", 6 | "GCM_SENDER_ID": "1095515545580" 7 | } -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/功能需求.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 功能需求 3 | about: 为项目提供一个好的想法! 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## 您的功能请求是否与问题相关? 11 | 12 | 例如:我总是在[...]时感到很麻烦或者不符合正常逻辑。 13 | 14 | 15 | ## 你希望的解决方案 16 | 对你希望的功能或者解决方案的清晰和简洁的描述。 17 | 18 | 19 | ## 描述您考虑过的替代方案 20 | 如果您考虑过替代方案,请描述它们。 21 | 22 | ## 其他 23 | 在这里添加有关功能请求的任何其他备注或截图。 24 | -------------------------------------------------------------------------------- /ios/RunnerTests/RunnerTests.swift: -------------------------------------------------------------------------------- 1 | import Flutter 2 | import UIKit 3 | import XCTest 4 | 5 | class RunnerTests: XCTestCase { 6 | 7 | func testExample() { 8 | // If you add code to the Runner application, consider adding tests here. 9 | // See https://developer.apple.com/documentation/xctest for more information about using XCTest. 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /macos/RunnerTests/RunnerTests.swift: -------------------------------------------------------------------------------- 1 | import FlutterMacOS 2 | import Cocoa 3 | import XCTest 4 | 5 | class RunnerTests: XCTestCase { 6 | 7 | func testExample() { 8 | // If you add code to the Runner application, consider adding tests here. 9 | // See https://developer.apple.com/documentation/xctest for more information about using XCTest. 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | /.kotlin 9 | /app/.cxx 10 | # Remember to never publicly share your keystore. 11 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app 12 | key.properties 13 | **/*.keystore 14 | **/*.jks 15 | -------------------------------------------------------------------------------- /windows/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ephemeral/ 2 | 3 | # Visual Studio user-specific files. 4 | *.suo 5 | *.user 6 | *.userosscache 7 | *.sln.docstates 8 | 9 | # Visual Studio build-related files. 10 | x64/ 11 | x86/ 12 | 13 | # Visual Studio cache files 14 | # files ending in .cache can be ignored 15 | *.[Cc]ache 16 | # but keep track of directories ending in .cache 17 | !*.[Cc]ache/ 18 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /lib/common/StyleString.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | // 摘抄:https://github.com/guozhigq/pilipala/ 4 | class StyleString { 5 | static const double cardSpace = 8; 6 | static const double safeSpace = 12; 7 | static BorderRadius mdRadius = BorderRadius.circular(10); 8 | static const Radius imgRadius = Radius.circular(10); 9 | static const double aspectRatio = 16 / 10; 10 | } -------------------------------------------------------------------------------- /linux/flutter/generated_plugin_registrant.h: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #ifndef GENERATED_PLUGIN_REGISTRANT_ 8 | #define GENERATED_PLUGIN_REGISTRANT_ 9 | 10 | #include 11 | 12 | // Registers Flutter plugins. 13 | void fl_register_plugins(FlPluginRegistry* registry); 14 | 15 | #endif // GENERATED_PLUGIN_REGISTRANT_ 16 | -------------------------------------------------------------------------------- /windows/flutter/generated_plugin_registrant.h: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #ifndef GENERATED_PLUGIN_REGISTRANT_ 8 | #define GENERATED_PLUGIN_REGISTRANT_ 9 | 10 | #include 11 | 12 | // Registers Flutter plugins. 13 | void RegisterPlugins(flutter::PluginRegistry* registry); 14 | 15 | #endif // GENERATED_PLUGIN_REGISTRANT_ 16 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /macos/Runner/DebugProfile.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.cs.allow-jit 8 | 9 | com.apple.security.network.server 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /lib/common/model/local/lyric_data.dart: -------------------------------------------------------------------------------- 1 | /// 歌词Model 2 | /// 特别的,此Model需要由其他Model转换而来 3 | class LyricData { 4 | final String lyric; 5 | final String? translate; 6 | final num starTime; 7 | final num endTime; 8 | final num duration; 9 | 10 | LyricData( 11 | {required this.lyric, 12 | this.translate, 13 | required this.duration, 14 | required this.starTime, 15 | required this.endTime}); 16 | } 17 | -------------------------------------------------------------------------------- /lib/common/utils/screen_utils.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | 4 | class ScreenSize { 5 | static const double Small = 200; 6 | static const double Normal = 450; 7 | static const double Large = 900; 8 | static const double ExtraLarge = 1350; 9 | } 10 | 11 | 12 | double getWindowsWidth(BuildContext context) => MediaQuery.of(context).size.width; 13 | 14 | double getWindowsHeight(BuildContext context) => MediaQuery.of(context).size.height; -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @UIApplicationMain 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /linux/my_application.h: -------------------------------------------------------------------------------- 1 | #ifndef FLUTTER_MY_APPLICATION_H_ 2 | #define FLUTTER_MY_APPLICATION_H_ 3 | 4 | #include 5 | 6 | G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, 7 | GtkApplication) 8 | 9 | /** 10 | * my_application_new: 11 | * 12 | * Creates a new Flutter-based application. 13 | * 14 | * Returns: a new #MyApplication. 15 | */ 16 | MyApplication* my_application_new(); 17 | 18 | #endif // FLUTTER_MY_APPLICATION_H_ 19 | -------------------------------------------------------------------------------- /macos/Runner/MainFlutterWindow.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | 4 | class MainFlutterWindow: NSWindow { 5 | override func awakeFromNib() { 6 | let flutterViewController = FlutterViewController() 7 | let windowFrame = self.frame 8 | self.contentViewController = flutterViewController 9 | self.setFrame(windowFrame, display: true) 10 | 11 | RegisterGeneratedPlugins(registry: flutterViewController) 12 | 13 | super.awakeFromNib() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /lib/common/model/local/isar/setting.dart: -------------------------------------------------------------------------------- 1 | import 'package:isar/isar.dart'; 2 | 3 | part 'generate/setting.g.dart'; 4 | 5 | @collection 6 | class Setting { 7 | Id id = Isar.autoIncrement; 8 | 9 | bool autoCheckUpdate; 10 | 11 | ///显示搜索默认词 12 | bool showSearchDefaultWord; 13 | 14 | ///显示搜索历史 15 | bool showSearchHistory; 16 | 17 | Setting( 18 | {this.autoCheckUpdate = true, 19 | this.showSearchDefaultWord = true, 20 | this.showSearchHistory = true}); 21 | } 22 | -------------------------------------------------------------------------------- /lib/common/di/database_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:bili_video_tunes/common/model/local/isar/setting.dart'; 2 | import 'package:bili_video_tunes/common/model/local/isar/video_audio_player_task.dart'; 3 | import 'package:isar/isar.dart'; 4 | import 'package:path_provider/path_provider.dart'; 5 | 6 | Future initDatabase() async { 7 | final dir = await getApplicationDocumentsDirectory(); 8 | return Isar.open( 9 | [VideoAudioPlayerTaskSchema, SettingSchema], 10 | directory: dir.path, 11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /windows/runner/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by Runner.rc 4 | // 5 | #define IDI_APP_ICON 101 6 | 7 | // Next default values for new objects 8 | // 9 | #ifdef APSTUDIO_INVOKED 10 | #ifndef APSTUDIO_READONLY_SYMBOLS 11 | #define _APS_NEXT_RESOURCE_VALUE 102 12 | #define _APS_NEXT_COMMAND_VALUE 40001 13 | #define _APS_NEXT_CONTROL_VALUE 1001 14 | #define _APS_NEXT_SYMED_VALUE 101 15 | #endif 16 | #endif 17 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /firebase.json: -------------------------------------------------------------------------------- 1 | {"flutter":{"platforms":{"android":{"default":{"projectId":"bilivideotunes","appId":"1:1095515545580:android:dc9144987f5d765b472ac9","fileOutput":"android/app/google-services.json"}},"dart":{"lib/firebase_options.dart":{"projectId":"bilivideotunes","configurations":{"android":"1:1095515545580:android:dc9144987f5d765b472ac9","ios":"1:1095515545580:ios:dd7abea4ba9216cb472ac9","macos":"1:1095515545580:ios:dd7abea4ba9216cb472ac9","web":"1:1095515545580:web:35536197b380219d472ac9","windows":"1:1095515545580:web:cad2f28e694fb59f472ac9"}}}}}} -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/错误报告.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 错误报告 3 | about: 创建一个错误报告,方便我们找到问题! 4 | title: BUG 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## 描述错误 11 | 12 | 你在做什么的时候遇到了什么问题?请用简洁的话描述发送了什么事情。 13 | 14 | ## 重现步骤 15 | 16 | 能否做到最小重现步骤,这对我们排除问题非常有帮助! 17 | 1. 点击...... 18 | 2. 滚动到...... 19 | 3. 滚动到...... 20 | 21 | ## 预期行为 22 | 23 | 正常这个时候应该发送什么?简洁的描述一下吧! 24 | 25 | ## 截图 26 | 27 | 如果这个问题可以截图或者录屏请在这里加上,这会非常好的帮助我们找到问题。 28 | 29 | ## 系统与版本 30 | 31 | - 设备系统:android x.x.x 32 | - 网络环境:wifi/数据流量 33 | - 网络运营商:电信/移动/联通/xx 34 | - 软件版本:x.x.x 35 | 36 | ## 其他 37 | 38 | 如果还有其他的信息可以在这里加上。 39 | -------------------------------------------------------------------------------- /lib/pages/main/bili_music/view.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:flutter/material.dart'; 3 | 4 | class BiLiMusicPage extends StatefulWidget{ 5 | const BiLiMusicPage({super.key}); 6 | 7 | @override 8 | State createState() => _BiLiMusicPageState(); 9 | 10 | } 11 | 12 | class _BiLiMusicPageState extends State{ 13 | 14 | @override 15 | Widget build(BuildContext context) { 16 | 17 | return const Column( 18 | mainAxisAlignment: MainAxisAlignment.center, 19 | children: [ 20 | Text("音频界面") 21 | ], 22 | ); 23 | 24 | } 25 | 26 | } 27 | 28 | -------------------------------------------------------------------------------- /lib/services/service_locator.dart: -------------------------------------------------------------------------------- 1 | import 'package:audio_service/audio_service.dart'; 2 | import 'package:bili_video_tunes/common/handler/bili_audio_handler.dart'; 3 | 4 | 5 | 6 | 7 | Future initAudioService() async { 8 | return await AudioService.init( 9 | builder: () => BiliAudioHandler(), 10 | config: const AudioServiceConfig( 11 | androidNotificationChannelId: 'com.imcys.bilivideotunes.biliAudio', 12 | androidNotificationChannelName: 'BiliVideoTunes Service', 13 | androidNotificationOngoing: true, 14 | androidStopForegroundOnPause: true, 15 | ), 16 | ); 17 | } 18 | -------------------------------------------------------------------------------- /macos/Runner/Configs/Warnings.xcconfig: -------------------------------------------------------------------------------- 1 | WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings 2 | GCC_WARN_UNDECLARED_SELECTOR = YES 3 | CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES 4 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE 5 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES 6 | CLANG_WARN_PRAGMA_PACK = YES 7 | CLANG_WARN_STRICT_PROTOTYPES = YES 8 | CLANG_WARN_COMMA = YES 9 | GCC_WARN_STRICT_SELECTOR_MATCH = YES 10 | CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES 11 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES 12 | GCC_WARN_SHADOW = YES 13 | CLANG_WARN_UNREACHABLE_CODE = YES 14 | -------------------------------------------------------------------------------- /android/build.gradle.kts: -------------------------------------------------------------------------------- 1 | allprojects { 2 | repositories { 3 | maven { setUrl("https://jitpack.io") } 4 | google() 5 | mavenCentral() 6 | } 7 | } 8 | 9 | val newBuildDir: Directory = rootProject.layout.buildDirectory.dir("../../build").get() 10 | rootProject.layout.buildDirectory.value(newBuildDir) 11 | 12 | subprojects { 13 | val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name) 14 | project.layout.buildDirectory.value(newSubprojectBuildDir) 15 | } 16 | subprojects { 17 | project.evaluationDependsOn(":app") 18 | } 19 | 20 | tasks.register("clean") { 21 | delete(rootProject.layout.buildDirectory) 22 | } 23 | -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/imcys/bilivideotunes/bili_video_tunes/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.imcys.bilivideotunes.bili_video_tunes 2 | 3 | import com.imcys.bilivideotunes.bili_video_tunes.openApp.OpenAppPlugins 4 | import com.imcys.bilivideotunes.bili_video_tunes.openApp.SaveFilePlugins 5 | import com.ryanheise.audioservice.AudioServiceActivity 6 | import io.flutter.embedding.engine.FlutterEngine 7 | 8 | class MainActivity: AudioServiceActivity() { 9 | 10 | override fun configureFlutterEngine(flutterEngine: FlutterEngine) { 11 | flutterEngine.plugins.add(OpenAppPlugins()) 12 | flutterEngine.plugins.add(SaveFilePlugins()) 13 | 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /macos/Runner/Configs/AppInfo.xcconfig: -------------------------------------------------------------------------------- 1 | // Application-level settings for the Runner target. 2 | // 3 | // This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the 4 | // future. If not, the values below would default to using the project name when this becomes a 5 | // 'flutter create' template. 6 | 7 | // The application's name. By default this is also the title of the Flutter window. 8 | PRODUCT_NAME = bili_video_tunes 9 | 10 | // The application's bundle identifier 11 | PRODUCT_BUNDLE_IDENTIFIER = com.imcys.bilivideotunes.biliVideoTunes 12 | 13 | // The copyright displayed in application information 14 | PRODUCT_COPYRIGHT = Copyright © 2024 com.imcys.bilivideotunes. All rights reserved. 15 | -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | **/dgph 2 | *.mode1v3 3 | *.mode2v3 4 | *.moved-aside 5 | *.pbxuser 6 | *.perspectivev3 7 | **/*sync/ 8 | .sconsign.dblite 9 | .tags* 10 | **/.vagrant/ 11 | **/DerivedData/ 12 | Icon? 13 | **/Pods/ 14 | **/.symlinks/ 15 | profile 16 | xcuserdata 17 | **/.generated/ 18 | Flutter/App.framework 19 | Flutter/Flutter.framework 20 | Flutter/Flutter.podspec 21 | Flutter/Generated.xcconfig 22 | Flutter/ephemeral/ 23 | Flutter/app.flx 24 | Flutter/app.zip 25 | Flutter/flutter_assets/ 26 | Flutter/flutter_export_environment.sh 27 | ServiceDefinitions.json 28 | Runner/GeneratedPluginRegistrant.* 29 | 30 | # Exceptions to above rules. 31 | !default.mode1v3 32 | !default.mode2v3 33 | !default.pbxuser 34 | !default.perspectivev3 35 | -------------------------------------------------------------------------------- /lib/common/model/network/home/search_criteria_emnu.dart: -------------------------------------------------------------------------------- 1 | class SearchType { 2 | static const String all = 'all'; 3 | static const String video = 'video'; 4 | static const String mediaBangumi = 'media_bangumi'; 5 | static const String live = 'live'; 6 | static const String article = 'article'; 7 | static const String topic = 'topic'; 8 | static const String biliUser = 'bili_user'; 9 | } 10 | 11 | 12 | 13 | class SearchOrder { 14 | static const String totalRank = 'totalrank'; 15 | static const String click = 'click'; 16 | static const String pubDate = 'pubdate'; 17 | static const String dm = 'dm'; 18 | static const String stow = 'stow'; 19 | static const String scores = 'scores'; 20 | static const String defaultOrder = totalRank; 21 | } 22 | -------------------------------------------------------------------------------- /windows/runner/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_UTILS_H_ 2 | #define RUNNER_UTILS_H_ 3 | 4 | #include 5 | #include 6 | 7 | // Creates a console for the process, and redirects stdout and stderr to 8 | // it for both the runner and the Flutter library. 9 | void CreateAndAttachConsole(); 10 | 11 | // Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string 12 | // encoded in UTF-8. Returns an empty std::string on failure. 13 | std::string Utf8FromUtf16(const wchar_t* utf16_string); 14 | 15 | // Gets the command line arguments passed in as a std::vector, 16 | // encoded in UTF-8. Returns an empty std::vector on failure. 17 | std::vector GetCommandLineArguments(); 18 | 19 | #endif // RUNNER_UTILS_H_ 20 | -------------------------------------------------------------------------------- /lib/common/controller/user_controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:bili_video_tunes/common/api/api_path.dart'; 2 | import 'package:bili_video_tunes/common/api/user_info_api.dart'; 3 | import 'package:bili_video_tunes/common/model/network/user/login_user_info.dart'; 4 | import 'package:bili_video_tunes/common/utils/http_utils.dart'; 5 | import 'package:get/get.dart'; 6 | 7 | class UserController extends GetxController { 8 | Rx loginUserData = Rx(null); 9 | 10 | Future initLoginUserData() async { 11 | // 获取webi 12 | dioClient.get(bliUrl); 13 | final result = await UserInfoApi.getLoginUserInfo(); 14 | if (result.code == 0) { 15 | loginUserData.value = result.data; 16 | } else { 17 | loginUserData.value = null; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /lib/common/api/user_login_api.dart: -------------------------------------------------------------------------------- 1 | import 'package:bili_video_tunes/common/model/network/user/login_poll_info.dart'; 2 | import 'package:bili_video_tunes/common/utils/http_utils.dart'; 3 | 4 | import '../model/network/user/login_qrcode_info.dart'; 5 | import 'api_path.dart'; 6 | 7 | class UserLoginApi { 8 | static Future getLoginQrcodeInfo() async { 9 | final result = await dioClient.get(loginQrcodeGeneratePath); 10 | return LoginQrcodeInfo.fromJson(result.data); 11 | } 12 | 13 | static Future getLoginPollInfo( 14 | {required String qrcodeKey}) async { 15 | final result = await dioClient 16 | .get(loginQrcodePollPath, queryParameters: {"qrcode_key": qrcodeKey}); 17 | return LoginPollInfo.fromJson(result.data); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /lib/common/weight/hots_tag_shimmer.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:shimmer/shimmer.dart'; 3 | 4 | class HotsTagShimmerWrap extends StatelessWidget { 5 | final int mockNum; 6 | final int spacing; 7 | 8 | const HotsTagShimmerWrap( 9 | {required this.mockNum, required this.spacing, super.key}); 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | return Wrap( 14 | spacing: 10, 15 | children: List.generate( 16 | 5, 17 | (index) => Shimmer.fromColors( 18 | baseColor: Colors.grey.shade300, 19 | highlightColor: Colors.grey.shade100, 20 | child: 21 | const ChoiceChip(label: Text("XXXXX"), selected: false), 22 | ))); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /android/app/google-services.json: -------------------------------------------------------------------------------- 1 | { 2 | "project_info": { 3 | "project_number": "1095515545580", 4 | "project_id": "bilivideotunes", 5 | "storage_bucket": "bilivideotunes.appspot.com" 6 | }, 7 | "client": [ 8 | { 9 | "client_info": { 10 | "mobilesdk_app_id": "1:1095515545580:android:dc9144987f5d765b472ac9", 11 | "android_client_info": { 12 | "package_name": "com.imcys.bilivideotunes" 13 | } 14 | }, 15 | "oauth_client": [], 16 | "api_key": [ 17 | { 18 | "current_key": "AIzaSyBZ4wo9fGJPU77cBH2w8k2jh9C05OXSEyc" 19 | } 20 | ], 21 | "services": { 22 | "appinvite_service": { 23 | "other_platform_oauth_client": [] 24 | } 25 | } 26 | } 27 | ], 28 | "configuration_version": "1" 29 | } -------------------------------------------------------------------------------- /android/app/src/main/java/io/flutter/app/FlutterMultiDexApplication.java: -------------------------------------------------------------------------------- 1 | // Generated file. 2 | // 3 | // If you wish to remove Flutter's multidex support, delete this entire file. 4 | // 5 | // Modifications to this file should be done in a copy under a different name 6 | // as this file may be regenerated. 7 | 8 | package io.flutter.app; 9 | 10 | import android.app.Application; 11 | import android.content.Context; 12 | import androidx.annotation.CallSuper; 13 | import androidx.multidex.MultiDex; 14 | 15 | /** 16 | * Extension of {@link android.app.Application}, adding multidex support. 17 | */ 18 | public class FlutterMultiDexApplication extends Application { 19 | @Override 20 | @CallSuper 21 | protected void attachBaseContext(Context base) { 22 | super.attachBaseContext(base); 23 | MultiDex.install(this); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | migrate_working_dir/ 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | 19 | # The .vscode folder contains launch configuration and tasks you configure in 20 | # VS Code which you may wish to be included in version control, so this line 21 | # is commented out by default. 22 | #.vscode/ 23 | 24 | # Flutter/Dart/Pub related 25 | **/doc/api/ 26 | **/ios/Flutter/.last_build_id 27 | .dart_tool/ 28 | .flutter-plugins 29 | .flutter-plugins-dependencies 30 | .pub-cache/ 31 | .pub/ 32 | /build/ 33 | 34 | # Symbolication related 35 | app.*.symbols 36 | 37 | # Obfuscation related 38 | app.*.map.json 39 | 40 | # Android Studio will place build artifacts here 41 | /android/app/debug 42 | /android/app/profile 43 | /android/app/release 44 | -------------------------------------------------------------------------------- /ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 12.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /lib/common/api/fav_api.dart: -------------------------------------------------------------------------------- 1 | import 'package:bili_video_tunes/common/api/api_path.dart'; 2 | import 'package:bili_video_tunes/common/model/network/user/fav_resource_list_info.dart'; 3 | import 'package:bili_video_tunes/common/utils/http_utils.dart'; 4 | 5 | class FavApi { 6 | static Future getFavResourceInfoList({ 7 | required int mediaId, 8 | int? tid, 9 | String? keyword, 10 | String? order, 11 | int? type, 12 | int? ps, 13 | int? pn, 14 | String platform = "web", 15 | }) async { 16 | final result = await dioClient.get(favResourceListPath, queryParameters: { 17 | "media_id": mediaId, 18 | "tid": tid, 19 | "keyword": keyword, 20 | "order": order, 21 | "type": type, 22 | "ps": ps, 23 | "pn": pn, 24 | "platform": platform, 25 | }); 26 | 27 | return FavResourceListInfo.fromJson(result.data); 28 | 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /windows/runner/runner.exe.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PerMonitorV2 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /linux/flutter/generated_plugins.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Generated file, do not edit. 3 | # 4 | 5 | list(APPEND FLUTTER_PLUGIN_LIST 6 | dynamic_color 7 | isar_flutter_libs 8 | screen_retriever_linux 9 | url_launcher_linux 10 | window_manager 11 | ) 12 | 13 | list(APPEND FLUTTER_FFI_PLUGIN_LIST 14 | ) 15 | 16 | set(PLUGIN_BUNDLED_LIBRARIES) 17 | 18 | foreach(plugin ${FLUTTER_PLUGIN_LIST}) 19 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) 20 | target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) 21 | list(APPEND PLUGIN_BUNDLED_LIBRARIES $) 22 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) 23 | endforeach(plugin) 24 | 25 | foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) 26 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) 27 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) 28 | endforeach(ffi_plugin) 29 | -------------------------------------------------------------------------------- /windows/flutter/generated_plugins.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Generated file, do not edit. 3 | # 4 | 5 | list(APPEND FLUTTER_PLUGIN_LIST 6 | dynamic_color 7 | firebase_core 8 | isar_flutter_libs 9 | just_audio_windows 10 | screen_retriever_windows 11 | url_launcher_windows 12 | window_manager 13 | ) 14 | 15 | list(APPEND FLUTTER_FFI_PLUGIN_LIST 16 | ) 17 | 18 | set(PLUGIN_BUNDLED_LIBRARIES) 19 | 20 | foreach(plugin ${FLUTTER_PLUGIN_LIST}) 21 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) 22 | target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) 23 | list(APPEND PLUGIN_BUNDLED_LIBRARIES $) 24 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) 25 | endforeach(plugin) 26 | 27 | foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) 28 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) 29 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) 30 | endforeach(ffi_plugin) 31 | -------------------------------------------------------------------------------- /ios/Runner/GoogleService-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | API_KEY 6 | AIzaSyAnVzFsqaX6IMbsuC_z3eVuOIGHZh1B468 7 | GCM_SENDER_ID 8 | 1095515545580 9 | PLIST_VERSION 10 | 1 11 | BUNDLE_ID 12 | com.imcys.bilivideotunes.biliVideoTunes 13 | PROJECT_ID 14 | bilivideotunes 15 | STORAGE_BUCKET 16 | bilivideotunes.appspot.com 17 | IS_ADS_ENABLED 18 | 19 | IS_ANALYTICS_ENABLED 20 | 21 | IS_APPINVITE_ENABLED 22 | 23 | IS_GCM_ENABLED 24 | 25 | IS_SIGNIN_ENABLED 26 | 27 | GOOGLE_APP_ID 28 | 1:1095515545580:ios:dd7abea4ba9216cb472ac9 29 | 30 | -------------------------------------------------------------------------------- /macos/Runner/GoogleService-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | API_KEY 6 | AIzaSyAnVzFsqaX6IMbsuC_z3eVuOIGHZh1B468 7 | GCM_SENDER_ID 8 | 1095515545580 9 | PLIST_VERSION 10 | 1 11 | BUNDLE_ID 12 | com.imcys.bilivideotunes.biliVideoTunes.RunnerTests 13 | PROJECT_ID 14 | bilivideotunes 15 | STORAGE_BUCKET 16 | bilivideotunes.appspot.com 17 | IS_ADS_ENABLED 18 | 19 | IS_ANALYTICS_ENABLED 20 | 21 | IS_APPINVITE_ENABLED 22 | 23 | IS_GCM_ENABLED 24 | 25 | IS_SIGNIN_ENABLED 26 | 27 | GOOGLE_APP_ID 28 | 1:1095515545580:ios:aef0ba89c8a11c2e472ac9 29 | 30 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/ic_launcher_monochrome.xml: -------------------------------------------------------------------------------- 1 | 6 | 10 | 13 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /windows/runner/flutter_window.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_FLUTTER_WINDOW_H_ 2 | #define RUNNER_FLUTTER_WINDOW_H_ 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include "win32_window.h" 10 | 11 | // A window that does nothing but host a Flutter view. 12 | class FlutterWindow : public Win32Window { 13 | public: 14 | // Creates a new FlutterWindow hosting a Flutter view running |project|. 15 | explicit FlutterWindow(const flutter::DartProject& project); 16 | virtual ~FlutterWindow(); 17 | 18 | protected: 19 | // Win32Window: 20 | bool OnCreate() override; 21 | void OnDestroy() override; 22 | LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, 23 | LPARAM const lparam) noexcept override; 24 | 25 | private: 26 | // The project to run. 27 | flutter::DartProject project_; 28 | 29 | // The Flutter instance hosted by this window. 30 | std::unique_ptr flutter_controller_; 31 | }; 32 | 33 | #endif // RUNNER_FLUTTER_WINDOW_H_ 34 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /web/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bili_video_tunes", 3 | "short_name": "bili_video_tunes", 4 | "start_url": ".", 5 | "display": "standalone", 6 | "background_color": "#0175C2", 7 | "theme_color": "#0175C2", 8 | "description": "BVideoTunes,是视频曲调解析器,主要工作是将B站的视频", 9 | "orientation": "portrait-primary", 10 | "prefer_related_applications": false, 11 | "icons": [ 12 | { 13 | "src": "icons/Icon-192.png", 14 | "sizes": "192x192", 15 | "type": "image/png" 16 | }, 17 | { 18 | "src": "icons/Icon-512.png", 19 | "sizes": "512x512", 20 | "type": "image/png" 21 | }, 22 | { 23 | "src": "icons/Icon-maskable-192.png", 24 | "sizes": "192x192", 25 | "type": "image/png", 26 | "purpose": "maskable" 27 | }, 28 | { 29 | "src": "icons/Icon-maskable-512.png", 30 | "sizes": "512x512", 31 | "type": "image/png", 32 | "purpose": "maskable" 33 | } 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /lib/common/weight/shimmer/search_squear_list_shimmer.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:shimmer/shimmer.dart'; 3 | 4 | class SearchSquareListShimmer extends StatelessWidget { 5 | const SearchSquareListShimmer({super.key}); 6 | 7 | @override 8 | Widget build(BuildContext context) { 9 | return GridView( 10 | gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( 11 | crossAxisCount: 2, 12 | childAspectRatio: 4 / 1, 13 | crossAxisSpacing: 5, 14 | mainAxisSpacing: 2, 15 | ), 16 | children: List.generate( 17 | 10, 18 | (index) => Shimmer.fromColors( 19 | baseColor: Colors.grey.shade300, 20 | highlightColor: Colors.grey.shade100, 21 | child: Container( 22 | width: 40, 23 | height: 10, 24 | margin: const EdgeInsets.all(10), 25 | decoration: BoxDecoration( 26 | borderRadius: BorderRadius.circular(10), 27 | color: Colors.grey, 28 | ), 29 | ), 30 | )), 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /lib/common/weight/assemble_animated_opacity.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | 3 | class AssembleAutoAnimatedOpacity extends StatefulWidget { 4 | final Widget? child; 5 | final bool alwaysIncludeSemantics; 6 | final Duration duration; 7 | 8 | const AssembleAutoAnimatedOpacity( 9 | {super.key, 10 | this.child, 11 | required this.duration, 12 | this.alwaysIncludeSemantics = false}); 13 | 14 | @override 15 | State createState() => _AssembleAutoAnimatedOpacity(); 16 | } 17 | 18 | class _AssembleAutoAnimatedOpacity extends State { 19 | double _opacity = 0.0; 20 | 21 | @override 22 | void initState() { 23 | super.initState(); 24 | Future.delayed(const Duration(milliseconds: 100), () { 25 | setState(() { 26 | _opacity = 1.0; 27 | }); 28 | }); 29 | } 30 | 31 | @override 32 | Widget build(BuildContext context) { 33 | return AnimatedOpacity( 34 | opacity: _opacity, 35 | alwaysIncludeSemantics: widget.alwaysIncludeSemantics, 36 | duration: widget.duration, 37 | child: widget.child, 38 | ); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /android/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | val flutterSdkPath = run { 3 | val properties = java.util.Properties() 4 | file("local.properties").inputStream().use { properties.load(it) } 5 | val flutterSdkPath = properties.getProperty("flutter.sdk") 6 | require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" } 7 | flutterSdkPath 8 | } 9 | 10 | includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") 11 | 12 | repositories { 13 | google() 14 | mavenCentral() 15 | gradlePluginPortal() 16 | jcenter() 17 | } 18 | } 19 | 20 | plugins { 21 | id("dev.flutter.flutter-plugin-loader") version "1.0.0" 22 | id("com.android.application") version "8.7.3" apply false 23 | // START: FlutterFire Configuration 24 | id ("com.google.gms.google-services") version "4.3.15" apply false 25 | id ("com.google.firebase.firebase-perf") version "1.4.1" apply false 26 | id ("com.google.firebase.crashlytics") version "2.8.1" apply false 27 | id ("org.jetbrains.kotlin.android") version "2.0.20" apply false 28 | // END: FlutterFire Configuration 29 | } 30 | 31 | include(":app") 32 | -------------------------------------------------------------------------------- /macos/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSMinimumSystemVersion 24 | $(MACOSX_DEPLOYMENT_TARGET) 25 | NSHumanReadableCopyright 26 | $(PRODUCT_COPYRIGHT) 27 | NSMainNibFile 28 | MainMenu 29 | NSPrincipalClass 30 | NSApplication 31 | 32 | 33 | -------------------------------------------------------------------------------- /lib/pages/user/fav_list/controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:bili_video_tunes/common/api/fav_api.dart'; 2 | import 'package:bili_video_tunes/common/model/network/user/fav_resource_list_info.dart'; 3 | import 'package:get/get.dart'; 4 | 5 | class FavListController extends GetxController { 6 | final favListInfo = FavResourceListInfo().obs; 7 | final favList = [].obs; 8 | 9 | Future loadFavList({ 10 | required int mediaId, 11 | int? tid, 12 | String? keyword, 13 | String? order, 14 | int? type, 15 | int ps = 20, 16 | int pn = 1, 17 | String platform = "web", 18 | isClear = false, 19 | }) async { 20 | if (isClear) { 21 | favList.clear(); 22 | } 23 | 24 | final result = await FavApi.getFavResourceInfoList( 25 | mediaId: mediaId, 26 | tid: tid, 27 | keyword: keyword, 28 | order: order, 29 | type: type, 30 | ps: ps, 31 | pn: pn, 32 | platform: platform, 33 | ); 34 | 35 | favListInfo.value = result; 36 | favList.addAll(result.data?.medias ?? []); 37 | } 38 | 39 | Future initFavList({required int mediaId}) async { 40 | await loadFavList(mediaId: mediaId, isClear: true); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /lib/common/utils/event_bus.dart: -------------------------------------------------------------------------------- 1 | //订阅者回调签名 2 | typedef void EventCallback(arg); 3 | 4 | /// 候选事件总线,需要评估是否需要引入。 5 | class EventBus { 6 | //私有构造函数 7 | EventBus._internal(); 8 | 9 | //保存单例 10 | static final EventBus _singleton = EventBus._internal(); 11 | 12 | //工厂构造函数 13 | factory EventBus() => _singleton; 14 | 15 | //保存事件订阅者队列,key:事件名(id),value: 对应事件的订阅者队列 16 | final _emap = ?>{}; 17 | 18 | //添加订阅者 19 | void on(eventName, EventCallback f) { 20 | _emap[eventName] ??= []; 21 | _emap[eventName]!.add(f); 22 | } 23 | 24 | //移除订阅者 25 | void off(eventName, [EventCallback? f]) { 26 | var list = _emap[eventName]; 27 | if (eventName == null || list == null) return; 28 | if (f == null) { 29 | _emap[eventName] = null; 30 | } else { 31 | list.remove(f); 32 | } 33 | } 34 | 35 | //触发事件,事件触发后该事件所有订阅者会被调用 36 | void emit(eventName, [arg]) { 37 | var list = _emap[eventName]; 38 | if (list == null) return; 39 | int len = list.length - 1; 40 | //反向遍历,防止订阅者在回调中移除自身带来的下标错位 41 | for (var i = len; i > -1; --i) { 42 | list[i](arg); 43 | } 44 | } 45 | } 46 | 47 | //定义一个top-level(全局)变量,页面引入该文件后可以直接使用bus 48 | var bus = EventBus(); 49 | -------------------------------------------------------------------------------- /lib/services/bili_audio_service.dart: -------------------------------------------------------------------------------- 1 | import 'package:bili_video_tunes/common/controller/audio_controller.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:get/get.dart'; 4 | import 'package:get/get_rx/get_rx.dart'; 5 | import 'package:just_audio/just_audio.dart'; 6 | 7 | class BiliAudioService extends GetxService { 8 | // 播放任务列表 9 | RxList playerList = [].obs; 10 | 11 | // 播放状态 12 | Rx playerState = PlayerState(false, ProcessingState.idle).obs; 13 | 14 | // 当前播放进度 15 | Rx currentPosition = (Duration.zero).obs; 16 | 17 | // 总播放进度 18 | Rx totalDuration = Rx(null); 19 | 20 | // 播放指标 21 | Rx playerIndex = Rx(null); 22 | 23 | // 循环模式 24 | Rx loopModel = (LoopMode.off).obs; 25 | 26 | // 歌词位置 27 | Rx lyricLineIndex = 0.obs; 28 | 29 | // 歌曲主题 30 | Rx audioLightColorScheme = 31 | (ColorScheme.fromSeed(seedColor: Colors.deepPurple)).obs; 32 | 33 | Rx audioDarkColorScheme = (ColorScheme.fromSeed( 34 | seedColor: Colors.deepPurple, brightness: Brightness.dark)) 35 | .obs; 36 | 37 | Rx singleLyricAnimationController = Rx(null); 38 | } 39 | -------------------------------------------------------------------------------- /lib/common/weight/router/search_page_router.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | 3 | class SearchPageRouter extends PageRoute { 4 | SearchPageRouter({ 5 | required this.builder, 6 | this.transitionDuration = const Duration(milliseconds: 300), 7 | this.opaque = true, 8 | this.barrierDismissible = false, 9 | this.barrierColor, 10 | this.barrierLabel, 11 | this.maintainState = true, 12 | }); 13 | 14 | final WidgetBuilder builder; 15 | 16 | @override 17 | final Duration transitionDuration; 18 | 19 | @override 20 | final bool opaque; 21 | 22 | @override 23 | final bool barrierDismissible; 24 | 25 | @override 26 | final Color? barrierColor; 27 | 28 | @override 29 | final String? barrierLabel; 30 | 31 | @override 32 | final bool maintainState; 33 | 34 | @override 35 | Widget buildPage(BuildContext context, Animation animation, 36 | Animation secondaryAnimation) => 37 | builder(context); 38 | 39 | @override 40 | Widget buildTransitions(BuildContext context, Animation animation, 41 | Animation secondaryAnimation, Widget child) { 42 | return FadeTransition( 43 | opacity: animation, 44 | child: builder(context), 45 | ); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /lib/common/api/interceptor/login_api_interceptor.dart: -------------------------------------------------------------------------------- 1 | import 'package:bili_video_tunes/common/api/api_path.dart'; 2 | import 'package:dio/dio.dart'; 3 | 4 | /// 此监听器用于拦截替换账户登录的接口 5 | class LoginApiInterceptor implements Interceptor{ 6 | 7 | @override 8 | void onError(DioException err, ErrorInterceptorHandler handler) { 9 | return handler.next(err); 10 | } 11 | 12 | @override 13 | void onRequest( 14 | RequestOptions options, RequestInterceptorHandler handler) async { 15 | // 替换BaseUrl 16 | if (options.uri.path.contains("passport-login")) { 17 | options.baseUrl = passportBaseUrl; 18 | }else if(options.uri.path.contains(suggestSearchInfo)){ 19 | // 替换搜索接口 20 | options.baseUrl = searchUrl; 21 | } else if (options.uri.path == bliUrl) { 22 | // 替换B站主站结课 23 | options.contentType = ""; 24 | options.baseUrl = bliUrl; 25 | options.path = ""; 26 | options.responseType = ResponseType.bytes; 27 | } else if (options.uri.path.contains("ai_subtitle/prod")) { 28 | // 替换AI字幕网络请求 29 | options.baseUrl = aiSubtitleUrl; 30 | } 31 | 32 | return handler.next(options); 33 | } 34 | 35 | @override 36 | void onResponse(Response response, ResponseInterceptorHandler handler) { 37 | return handler.next(response); 38 | } 39 | 40 | } -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # 欢迎使用 BiliVideoTunes 文档贡献指南 2 | 3 | 非常感谢您花费时间为 BiliVideoTunes 进行贡献,您的任何贡献都将在 APP 当中实际显示! 4 | 5 | 加入 [QQ 频道]() 你可以更好的和其他开发者进行交流,如果你不是中国的贡献者请联系我的邮箱:[123456789@qq.com]() ,我会帮助你加入。 6 | 7 | ## 欢迎 8 | 9 | BiliVideoTunes 开发团队 由许多的开源贡献者和捐赠者组成,你有非常多的方式参与贡献,比如在 GitHub 上提交问题、编写代码、在 QQ 群或者频道帮助那些遇到问题的人并且天添加到帮助文档、或者任何其他形式提供的帮助! 10 | 11 | 在开始提交代码之前,希望你可以阅读下面的一些内容,其中阐述了一些社区的规范内容: 12 | 13 | 1. [行为准则](),这里我们明确规定了每位贡献者必须亲谦虚、尊重他人、当然还有一些其他遇到冲突时的解决方案。 14 | 15 | 2. [核心价值观](),这对我们来说很重要! 16 | 17 | ## 在错误跟踪提供问题 18 | 19 | BiliVideoTunes 采用了 [Firbase](https://firebase.google.cn/) 来统计奔溃问题,但者仅仅是发现了问题,我们需要对问题进行跟踪。你可以加入 Firbase 的运维,将 Firbase 发现的问题移交到 GitHub,并且将修复的问题在 Firbase 上进行关闭。 20 | 21 | ## 在问题分类上提供帮助 22 | 23 | BiliVideoTunes 可能会遇到非常多的问题,这些问题难免会存在重复性和类似的问题,将他们分类很有必要,在 GitHub 上找寻相同的问题将他们归档在一起,帮助开发者们快速的定位问题。 24 | 25 | ## 质量审查 26 | 27 | 质量审查是非常重要的,我们会在每个提交的代码中进行质量审查,通过质量审查的代码将会被合并到主分支当中。 28 | 29 | PR合并的代码,包括已经提交的代码,代码的健壮性无法保证,你可以试着审阅现在的代码和等待合并的代码,修复和指出代码的问题,这将能保证我们走的更远。 30 | 31 | 其次我们还有一些自动化测试的想法,你可以试试看做一些`integration_test`或者`单元测试`,这有助于我们每次发布新版本时检查APP的基本质量。 32 | 33 | ## 编码 34 | 35 | 如果你很喜欢开发,并且有在使用 [Flutter](https://flutter.dev/) 开发,那么你可以在[问题列表](https://github.com/1250422131/BiliVideoTunes/issues)当中寻找可以解决的问题,帮助我们修复这个问题。 36 | 37 | 如果你对新规划感兴趣那么可以在群聊里联系我们进一步讨论。 38 | 39 | 40 | ## 发布 41 | 42 | 如果你有兴趣参与我们的发版计划,那么可以在社群里联系我们! 43 | -------------------------------------------------------------------------------- /lib/pages/main/home/view.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | 3 | import '../../../common/router/b_v_t_page.dart'; 4 | import '../../../common/utils/screen_utils.dart'; 5 | import '../user_info/view.dart'; 6 | import '../video_music/view.dart'; 7 | 8 | final PageController pageController = PageController(); 9 | 10 | class HomePage extends StatefulWidget { 11 | const HomePage({super.key}); 12 | 13 | @override 14 | State createState() => _HomePageState(); 15 | } 16 | 17 | class NavInfo { 18 | final String title; 19 | final IconData icon; 20 | final IconData selectedIcon; 21 | 22 | const NavInfo( 23 | {required this.title, required this.icon, required this.selectedIcon}); 24 | } 25 | 26 | class _HomePageState extends State { 27 | @override 28 | Widget build(BuildContext context) { 29 | return PageView( 30 | controller: pageController, 31 | // 添加页面滑动改变后,去改变索引变量刷新页面来更新底部导航 32 | onPageChanged: (int index) {}, 33 | physics: const NeverScrollableScrollPhysics(), 34 | scrollDirection: getWindowsWidth(context) > ScreenSize.Normal 35 | ? Axis.vertical 36 | : Axis.horizontal, 37 | children: const [ 38 | VideoMusicPage(), 39 | // BiLiMusicPage(), 40 | UserInfoPage() 41 | ], 42 | ); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /windows/runner/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "flutter_window.h" 6 | #include "utils.h" 7 | 8 | int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, 9 | _In_ wchar_t *command_line, _In_ int show_command) { 10 | // Attach to console when present (e.g., 'flutter run') or create a 11 | // new console when running with a debugger. 12 | if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { 13 | CreateAndAttachConsole(); 14 | } 15 | 16 | // Initialize COM, so that it is available for use in the library and/or 17 | // plugins. 18 | ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); 19 | 20 | flutter::DartProject project(L"data"); 21 | 22 | std::vector command_line_arguments = 23 | GetCommandLineArguments(); 24 | 25 | project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); 26 | 27 | FlutterWindow window(project); 28 | Win32Window::Point origin(10, 10); 29 | Win32Window::Size size(1280, 720); 30 | if (!window.Create(L"bili_video_tunes", origin, size)) { 31 | return EXIT_FAILURE; 32 | } 33 | window.SetQuitOnClose(true); 34 | 35 | ::MSG msg; 36 | while (::GetMessage(&msg, nullptr, 0, 0)) { 37 | ::TranslateMessage(&msg); 38 | ::DispatchMessage(&msg); 39 | } 40 | 41 | ::CoUninitialize(); 42 | return EXIT_SUCCESS; 43 | } 44 | -------------------------------------------------------------------------------- /lib/common/weight/search/search_text.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | // 摘抄自:https://github.com/guozhigq/pilipala/blob/main/lib/pages/search/widgets/search_text.dar 4 | // 作者:guozhigq 5 | class SearchText extends StatelessWidget { 6 | final String? searchText; 7 | final Function? onSelect; 8 | final int? searchTextIdx; 9 | final Function? onLongSelect; 10 | const SearchText({ 11 | super.key, 12 | this.searchText, 13 | this.onSelect, 14 | this.searchTextIdx, 15 | this.onLongSelect, 16 | }); 17 | 18 | @override 19 | Widget build(BuildContext context) { 20 | return Material( 21 | color: Theme.of(context).colorScheme.surfaceVariant.withOpacity(0.5), 22 | borderRadius: BorderRadius.circular(6), 23 | child: Padding( 24 | padding: EdgeInsets.zero, 25 | child: InkWell( 26 | onTap: () { 27 | onSelect!(searchText); 28 | }, 29 | onLongPress: () { 30 | onLongSelect!(searchText); 31 | }, 32 | borderRadius: BorderRadius.circular(6), 33 | child: Padding( 34 | padding: 35 | const EdgeInsets.only(top: 5, bottom: 5, left: 11, right: 11), 36 | child: Text( 37 | searchText!, 38 | style: TextStyle( 39 | color: Theme.of(context).colorScheme.onSurfaceVariant), 40 | ), 41 | ), 42 | ), 43 | ), 44 | ); 45 | } 46 | } -------------------------------------------------------------------------------- /lib/common/api/user_info_api.dart: -------------------------------------------------------------------------------- 1 | import 'package:bili_video_tunes/common/api/api_path.dart'; 2 | import 'package:bili_video_tunes/common/model/network/user/login_user_info.dart'; 3 | import 'package:bili_video_tunes/common/model/network/user/player_history_info.dart'; 4 | import 'package:bili_video_tunes/common/utils/http_utils.dart'; 5 | 6 | import '../model/network/user/user_created_folder_info.dart'; 7 | 8 | class UserInfoApi { 9 | // 获取登录用户信息 10 | static Future getLoginUserInfo() async { 11 | final loginUserInfo = await dioClient.get(loginUserInfoPath); 12 | return LoginUserInfo.fromJson(loginUserInfo.data); 13 | } 14 | 15 | static Future getPlayerHistoryInfo( 16 | {int? max, int? viewAt, String? business = "archive"}) async { 17 | final playerHistoryInfo = 18 | await dioClient.get(playerHistoryPath, queryParameters: { 19 | "max": max, 20 | "view_at": viewAt, 21 | "business": business, 22 | }); 23 | return PlayerHistoryInfo.fromJson(playerHistoryInfo.data); 24 | } 25 | 26 | static Future getUserCreatedFolderInfo({ 27 | required num mid, 28 | int pn = 1, 29 | int ps = 10, 30 | }) async { 31 | final userCreatedFolderInfo = 32 | await dioClient.get(userCreateFolderListPath, queryParameters: { 33 | "up_mid": mid, 34 | "pn": pn, 35 | "ps": ps, 36 | }); 37 | return UserCreatedFolderInfo.fromJson(userCreatedFolderInfo.data); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/common/weight/video_cover_image.dart: -------------------------------------------------------------------------------- 1 | import 'package:cached_network_image/cached_network_image.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:get/get_state_manager/src/simple/list_notifier.dart'; 4 | 5 | class VideoCoverImage extends StatefulWidget { 6 | final double? width; 7 | final double? height; 8 | final String coverUrl; 9 | 10 | const VideoCoverImage( 11 | {Key? key, required this.coverUrl, this.width, this.height}) 12 | : super(key: key); 13 | 14 | @override 15 | State createState() => _VideoCoverImage(); 16 | } 17 | 18 | class _VideoCoverImage extends State{ 19 | 20 | @override 21 | void initState() { 22 | super.initState(); 23 | } 24 | 25 | @override 26 | Widget build(BuildContext context) { 27 | return CachedNetworkImage( 28 | imageUrl: widget.coverUrl, 29 | width: widget.width, 30 | height: widget.height, 31 | placeholder: (context, url) { 32 | return Container( 33 | width: widget.width, 34 | height: widget.height, 35 | color: Colors.grey.shade400, 36 | child: Align( 37 | alignment: Alignment.center, 38 | child: Image.asset( 39 | "assets/images/app/bili_load.png", 40 | width: 50, 41 | height: 50, 42 | ), 43 | ), 44 | ); 45 | }, 46 | filterQuality: FilterQuality.none, 47 | fit: BoxFit.cover, 48 | ); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /windows/flutter/generated_plugin_registrant.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #include "generated_plugin_registrant.h" 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | void RegisterPlugins(flutter::PluginRegistry* registry) { 18 | DynamicColorPluginCApiRegisterWithRegistrar( 19 | registry->GetRegistrarForPlugin("DynamicColorPluginCApi")); 20 | FirebaseCorePluginCApiRegisterWithRegistrar( 21 | registry->GetRegistrarForPlugin("FirebaseCorePluginCApi")); 22 | IsarFlutterLibsPluginRegisterWithRegistrar( 23 | registry->GetRegistrarForPlugin("IsarFlutterLibsPlugin")); 24 | JustAudioWindowsPluginRegisterWithRegistrar( 25 | registry->GetRegistrarForPlugin("JustAudioWindowsPlugin")); 26 | ScreenRetrieverWindowsPluginCApiRegisterWithRegistrar( 27 | registry->GetRegistrarForPlugin("ScreenRetrieverWindowsPluginCApi")); 28 | UrlLauncherWindowsRegisterWithRegistrar( 29 | registry->GetRegistrarForPlugin("UrlLauncherWindows")); 30 | WindowManagerPluginRegisterWithRegistrar( 31 | registry->GetRegistrarForPlugin("WindowManagerPlugin")); 32 | } 33 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the analyzer, which statically analyzes Dart code to 2 | # check for errors, warnings, and lints. 3 | # 4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled 5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be 6 | # invoked from the command line by running `flutter analyze`. 7 | 8 | # The following line activates a set of recommended lints for Flutter apps, 9 | # packages, and plugins designed to encourage good coding practices. 10 | include: package:flutter_lints/flutter.yaml 11 | 12 | linter: 13 | # The lint rules applied to this project can be customized in the 14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml` 15 | # included above or to enable additional rules. A list of all available lints 16 | # and their documentation is published at https://dart.dev/lints. 17 | # 18 | # Instead of disabling a lint rule for the entire project in the 19 | # section below, it can also be suppressed for a single line of code 20 | # or a specific dart file by using the `// ignore: name_of_lint` and 21 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file 22 | # producing the lint. 23 | rules: 24 | # avoid_print: false # Uncomment to disable the `avoid_print` rule 25 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule 26 | 27 | # Additional information about this file can be found at 28 | # https://dart.dev/guides/language/analysis-options 29 | -------------------------------------------------------------------------------- /lib/common/router/b_v_t_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:bili_video_tunes/pages/setting/setting.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:get/get.dart'; 4 | 5 | import '../../main_controller.dart'; 6 | import '../../pages/main/home/view.dart'; 7 | import '../../pages/user/fav_list/view.dart'; 8 | 9 | 10 | class BVDNavigatorObserver extends NavigatorObserver { 11 | /// 内存泄漏 12 | final MainController _controller = Get.find(); 13 | 14 | @override 15 | void didPush(Route route, Route? previousRoute) { 16 | _controller.showBottomNav.value = route.settings.name == rootPagePath; 17 | } 18 | 19 | @override 20 | void didPop(Route route, Route? previousRoute) { 21 | if (previousRoute?.settings.name == rootPagePath) { 22 | _controller.showBottomNav.value = true; 23 | } 24 | } 25 | } 26 | 27 | 28 | Route onGenerateRoute(RouteSettings settings) { 29 | late Widget page; 30 | switch (settings.name) { 31 | case rootPagePath: 32 | page = const HomePage(); 33 | break; 34 | case favPagePath: 35 | page = FavListPage( 36 | oid: (settings.arguments as Map)["oid"]!, 37 | ); 38 | break; 39 | case settingPath: 40 | page = const Setting(); 41 | } 42 | return MaterialPageRoute( 43 | settings: settings, 44 | builder: (BuildContext context) { 45 | return page; 46 | }); 47 | } 48 | 49 | 50 | const rootPagePath = "/"; 51 | 52 | const favPagePath = "/fav"; 53 | 54 | const settingPath = "/setting"; 55 | -------------------------------------------------------------------------------- /lib/common/utils/utils.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:dio/dio.dart'; 4 | import 'package:flutter/services.dart'; 5 | 6 | String formatSeconds(int seconds) { 7 | int hours = seconds ~/ 3600; 8 | int minutes = (seconds % 3600) ~/ 60; 9 | int remainingSeconds = seconds % 60; 10 | 11 | String hoursStr = hours.toString().padLeft(2, '0'); 12 | String minutesStr = minutes.toString().padLeft(2, '0'); 13 | String secondsStr = remainingSeconds.toString().padLeft(2, '0'); 14 | 15 | if (hours > 0) { 16 | return '$hoursStr:$minutesStr:$secondsStr'; 17 | } else { 18 | return '$minutesStr:$secondsStr'; 19 | } 20 | } 21 | 22 | Future goToApp( 23 | {required String name, 24 | required String package, 25 | required String path}) async { 26 | const methodChannel = MethodChannel('openAppChannel'); 27 | var map = { 28 | 'name': name, 29 | 'package': package, 30 | 'path': path, 31 | }; 32 | return await methodChannel.invokeMethod('openAppChannel', map); 33 | } 34 | 35 | Future saveNetworkImage(String imageUrl) async { 36 | const MethodChannel channel = MethodChannel('SaveFileChannel'); 37 | 38 | var response = await Dio() 39 | .get(imageUrl, options: Options(responseType: ResponseType.bytes)); 40 | 41 | final result = await channel.invokeMethod( 42 | 'saveImageToGallery', { 43 | 'imageBytes': Uint8List.fromList(response.data), 44 | 'quality': 60, 45 | 'name': "bvd_qrcode", 46 | 'child':'bvd', 47 | }); 48 | 49 | 50 | return result; 51 | } 52 | -------------------------------------------------------------------------------- /macos/Podfile: -------------------------------------------------------------------------------- 1 | platform :osx, '10.14' 2 | 3 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 4 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 5 | 6 | project 'Runner', { 7 | 'Debug' => :debug, 8 | 'Profile' => :release, 9 | 'Release' => :release, 10 | } 11 | 12 | def flutter_root 13 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__) 14 | unless File.exist?(generated_xcode_build_settings_path) 15 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first" 16 | end 17 | 18 | File.foreach(generated_xcode_build_settings_path) do |line| 19 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 20 | return matches[1].strip if matches 21 | end 22 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\"" 23 | end 24 | 25 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 26 | 27 | flutter_macos_podfile_setup 28 | 29 | target 'Runner' do 30 | use_frameworks! 31 | use_modular_headers! 32 | 33 | flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) 34 | target 'RunnerTests' do 35 | inherit! :search_paths 36 | end 37 | end 38 | 39 | post_install do |installer| 40 | installer.pods_project.targets.each do |target| 41 | flutter_additional_macos_build_settings(target) 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '12.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def flutter_root 14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) 15 | unless File.exist?(generated_xcode_build_settings_path) 16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" 17 | end 18 | 19 | File.foreach(generated_xcode_build_settings_path) do |line| 20 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 21 | return matches[1].strip if matches 22 | end 23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" 24 | end 25 | 26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 27 | 28 | flutter_ios_podfile_setup 29 | 30 | target 'Runner' do 31 | use_frameworks! 32 | use_modular_headers! 33 | 34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) 35 | target 'RunnerTests' do 36 | inherit! :search_paths 37 | end 38 | end 39 | 40 | post_install do |installer| 41 | installer.pods_project.targets.each do |target| 42 | flutter_additional_ios_build_settings(target) 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /lib/pages/main/user_info/controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:bili_video_tunes/common/model/network/user/player_history_info.dart'; 2 | import 'package:get/get.dart'; 3 | import 'package:bili_video_tunes/common/api/user_info_api.dart'; 4 | import 'package:bili_video_tunes/common/model/network/user/login_user_info.dart' 5 | as user_info; 6 | import 'package:bili_video_tunes/common/model/network/user/user_created_folder_info.dart' 7 | as user_folder_info; 8 | 9 | class UserInfoPageController extends GetxController { 10 | Rx myUserData = Rx(null); 11 | 12 | RxList playerHistoryList = [].obs; 13 | 14 | RxList favorites = 15 | [].obs; 16 | 17 | Future initMeUserData() async { 18 | final result = await UserInfoApi.getLoginUserInfo(); 19 | if (result.code == 0) { 20 | myUserData.value = result.data; 21 | } else { 22 | myUserData.value = null; 23 | } 24 | } 25 | 26 | Future initPlayerHistory() async { 27 | final result = await UserInfoApi.getPlayerHistoryInfo(); 28 | if (result.code == 0) { 29 | playerHistoryList.value = result.data!.list ?? []; 30 | } 31 | } 32 | 33 | Future initUserVideoFolder() async { 34 | if (myUserData.value != null) { 35 | final result = await UserInfoApi.getUserCreatedFolderInfo( 36 | mid: myUserData.value!.mid!); 37 | if (result.code == 0) { 38 | favorites.value = result.data!.list ?? []; 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /lib/common/weight/common_error.dart: -------------------------------------------------------------------------------- 1 | import 'package:bili_video_tunes/common/weight/assemble_animated_opacity.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | typedef Retry = void Function(); 5 | 6 | class CommonError extends StatelessWidget { 7 | final String tip; 8 | final IconData iconData; 9 | final String retryTip; 10 | final Retry retry; 11 | 12 | const CommonError({ 13 | super.key, 14 | required this.tip, 15 | required this.iconData, 16 | required this.retryTip, 17 | required this.retry, 18 | }); 19 | 20 | @override 21 | Widget build(BuildContext context) { 22 | return AssembleAutoAnimatedOpacity(duration: const Duration(milliseconds: 300),child: Center( 23 | child: Column( 24 | mainAxisAlignment: MainAxisAlignment.center, 25 | children: [ 26 | Icon( 27 | iconData, 28 | color: Theme.of(context).colorScheme.onErrorContainer, 29 | size: 120, 30 | ), 31 | const SizedBox( 32 | height: 32, 33 | ), 34 | Text( 35 | tip, 36 | style: const TextStyle( 37 | fontSize: 18, 38 | ), 39 | ), 40 | const SizedBox( 41 | height: 32, 42 | ), 43 | TextButton( 44 | onPressed: () { 45 | retry(); 46 | }, 47 | child: Text(retryTip, 48 | style: TextStyle( 49 | color: Theme.of(context).colorScheme.onErrorContainer, 50 | ))) 51 | ], 52 | ), 53 | ),); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "16x16", 5 | "idiom" : "mac", 6 | "filename" : "app_icon_16.png", 7 | "scale" : "1x" 8 | }, 9 | { 10 | "size" : "16x16", 11 | "idiom" : "mac", 12 | "filename" : "app_icon_32.png", 13 | "scale" : "2x" 14 | }, 15 | { 16 | "size" : "32x32", 17 | "idiom" : "mac", 18 | "filename" : "app_icon_32.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "32x32", 23 | "idiom" : "mac", 24 | "filename" : "app_icon_64.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "128x128", 29 | "idiom" : "mac", 30 | "filename" : "app_icon_128.png", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "size" : "128x128", 35 | "idiom" : "mac", 36 | "filename" : "app_icon_256.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "256x256", 41 | "idiom" : "mac", 42 | "filename" : "app_icon_256.png", 43 | "scale" : "1x" 44 | }, 45 | { 46 | "size" : "256x256", 47 | "idiom" : "mac", 48 | "filename" : "app_icon_512.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "512x512", 53 | "idiom" : "mac", 54 | "filename" : "app_icon_512.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "512x512", 59 | "idiom" : "mac", 60 | "filename" : "app_icon_1024.png", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /linux/flutter/generated_plugin_registrant.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #include "generated_plugin_registrant.h" 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | void fl_register_plugins(FlPluginRegistry* registry) { 16 | g_autoptr(FlPluginRegistrar) dynamic_color_registrar = 17 | fl_plugin_registry_get_registrar_for_plugin(registry, "DynamicColorPlugin"); 18 | dynamic_color_plugin_register_with_registrar(dynamic_color_registrar); 19 | g_autoptr(FlPluginRegistrar) isar_flutter_libs_registrar = 20 | fl_plugin_registry_get_registrar_for_plugin(registry, "IsarFlutterLibsPlugin"); 21 | isar_flutter_libs_plugin_register_with_registrar(isar_flutter_libs_registrar); 22 | g_autoptr(FlPluginRegistrar) screen_retriever_linux_registrar = 23 | fl_plugin_registry_get_registrar_for_plugin(registry, "ScreenRetrieverLinuxPlugin"); 24 | screen_retriever_linux_plugin_register_with_registrar(screen_retriever_linux_registrar); 25 | g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = 26 | fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); 27 | url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); 28 | g_autoptr(FlPluginRegistrar) window_manager_registrar = 29 | fl_plugin_registry_get_registrar_for_plugin(registry, "WindowManagerPlugin"); 30 | window_manager_plugin_register_with_registrar(window_manager_registrar); 31 | } 32 | -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /lib/common/utils/extends.dart: -------------------------------------------------------------------------------- 1 | 2 | 3 | import 'package:audio_service/audio_service.dart'; 4 | import 'package:bili_video_tunes/common/controller/audio_controller.dart'; 5 | 6 | extension AlsoExtension on T { 7 | T also(void Function(T) block) { 8 | block(this); 9 | return this; 10 | } 11 | } 12 | 13 | extension LetExtension on T { 14 | R let(R Function(T) block) { 15 | return block(this); 16 | } 17 | } 18 | 19 | extension ListExtension on List{ 20 | bool containsByToString(T element){ 21 | int length = this.length; 22 | for (int i = 0; i < length; i++) { 23 | if (this[i].toString() == element.toString()) return true; 24 | if (length != this.length) { 25 | throw ConcurrentModificationError(this); 26 | } 27 | } 28 | return false; 29 | } 30 | } 31 | 32 | 33 | extension AudioMediaItemExtension on AudioMediaItem{ 34 | 35 | MediaItem toMediaItem(){ 36 | return MediaItem( 37 | id: bvId!, 38 | title: title, 39 | displayTitle: title, 40 | displaySubtitle: bvId, 41 | displayDescription: description, 42 | artUri:Uri.parse(coverImageUrl), 43 | ); 44 | } 45 | } 46 | 47 | extension TimeFormatSeconds on int{ 48 | String formatSeconds(){ 49 | int hours = this ~/ 3600; 50 | int minutes = (this % 3600) ~/ 60; 51 | int remainingSeconds = this % 60; 52 | 53 | String hoursStr = hours.toString().padLeft(2, '0'); 54 | String minutesStr = minutes.toString().padLeft(2, '0'); 55 | String secondsStr = remainingSeconds.toString().padLeft(2, '0'); 56 | 57 | if (hours > 0) { 58 | return '$hoursStr:$minutesStr:$secondsStr'; 59 | } else { 60 | return '$minutesStr:$secondsStr'; 61 | } 62 | } 63 | } 64 | 65 | -------------------------------------------------------------------------------- /.github/workflows/flutter.yml: -------------------------------------------------------------------------------- 1 | name: Build and Release apk 2 | 3 | on: [push, pull_request, workflow_dispatch] 4 | 5 | jobs: 6 | flutter-build-apk: 7 | name: Build APK 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: Clone repository 11 | uses: actions/checkout@v4 12 | 13 | - name: Set up Java 14 | uses: actions/setup-java@v3 15 | with: 16 | distribution: 'zulu' 17 | java-version: '21' 18 | 19 | - name: Setup Gradle 20 | uses: gradle/gradle-build-action@v2 21 | 22 | - name: Set up Flutter 23 | uses: subosito/flutter-action@v2 24 | with: 25 | channel: stable 26 | flutter-version: 3.29.0 27 | 28 | - name: Cache Gradle packages 29 | uses: actions/cache@v3 30 | with: 31 | path: | 32 | ~/.gradle/caches 33 | ~/.gradle/wrapper 34 | key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} 35 | restore-keys: | 36 | ${{ runner.os }}-gradle- 37 | 38 | - name: Cache pub dependencies 39 | uses: actions/cache@v3 40 | with: 41 | path: ${{ env.PUB_CACHE }} 42 | key: ${{ runner.os }}-pub-${{ hashFiles('**/pubspec.lock') }} 43 | restore-keys: | 44 | ${{ runner.os }}-pub- 45 | 46 | - run: flutter pub get 47 | # - run: flutter test 48 | - run: flutter build apk --debug 49 | # - run: flutter build appbundle 50 | 51 | - name: Upload build artifacts 52 | uses: actions/upload-artifact@v4 53 | with: 54 | name: Build artifacts 55 | path: | 56 | ./build/app/outputs/flutter-apk/*.apk 57 | # ./build/app/outputs/bundle/release/*.aab -------------------------------------------------------------------------------- /macos/Flutter/GeneratedPluginRegistrant.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | import FlutterMacOS 6 | import Foundation 7 | 8 | import audio_service 9 | import audio_session 10 | import dynamic_color 11 | import firebase_analytics 12 | import firebase_core 13 | import firebase_crashlytics 14 | import isar_flutter_libs 15 | import just_audio 16 | import path_provider_foundation 17 | import screen_retriever_macos 18 | import sqflite_darwin 19 | import url_launcher_macos 20 | import window_manager 21 | 22 | func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { 23 | AudioServicePlugin.register(with: registry.registrar(forPlugin: "AudioServicePlugin")) 24 | AudioSessionPlugin.register(with: registry.registrar(forPlugin: "AudioSessionPlugin")) 25 | DynamicColorPlugin.register(with: registry.registrar(forPlugin: "DynamicColorPlugin")) 26 | FLTFirebaseAnalyticsPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseAnalyticsPlugin")) 27 | FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin")) 28 | FLTFirebaseCrashlyticsPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCrashlyticsPlugin")) 29 | IsarFlutterLibsPlugin.register(with: registry.registrar(forPlugin: "IsarFlutterLibsPlugin")) 30 | JustAudioPlugin.register(with: registry.registrar(forPlugin: "JustAudioPlugin")) 31 | PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) 32 | ScreenRetrieverMacosPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverMacosPlugin")) 33 | SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) 34 | UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) 35 | WindowManagerPlugin.register(with: registry.registrar(forPlugin: "WindowManagerPlugin")) 36 | } 37 | -------------------------------------------------------------------------------- /lib/common/weight/polygon_button.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | 4 | import 'package:flutter/material.dart'; 5 | 6 | 7 | class WaveCirclePainter extends CustomPainter { 8 | final int waveCount; 9 | final double amplitude; 10 | final double frequency; 11 | final Color color; 12 | 13 | WaveCirclePainter({ 14 | this.waveCount = 12, 15 | this.amplitude = 10.0, 16 | this.frequency = 1.0, 17 | this.color = Colors.black, 18 | }); 19 | 20 | @override 21 | void paint(Canvas canvas, Size size) { 22 | final paint = Paint() 23 | ..color = color 24 | ..style = PaintingStyle.stroke 25 | ..strokeWidth = 2.0; 26 | 27 | final center = Offset(size.width / 2, size.height / 2); 28 | final radius = min(size.width / 2, size.height / 2); 29 | 30 | final path = Path(); 31 | 32 | for (int i = 0; i < waveCount; i++) { 33 | final startAngle = 2 * pi * i / waveCount; 34 | final endAngle = 2 * pi * (i + 1) / waveCount; 35 | final midAngle = (startAngle + endAngle) / 2; 36 | 37 | final startX = center.dx + radius * cos(startAngle); 38 | final startY = center.dy + radius * sin(startAngle); 39 | final endX = center.dx + radius * cos(endAngle); 40 | final endY = center.dy + radius * sin(endAngle); 41 | final midX = center.dx + (radius + amplitude * sin(frequency * i)) * cos(midAngle); 42 | final midY = center.dy + (radius + amplitude * sin(frequency * i)) * sin(midAngle); 43 | 44 | if (i == 0) { 45 | path.moveTo(startX, startY); 46 | } else { 47 | path.lineTo(startX, startY); 48 | } 49 | path.quadraticBezierTo(midX, midY, endX, endY); 50 | } 51 | path.close(); 52 | 53 | canvas.drawPath(path, paint); 54 | } 55 | 56 | @override 57 | bool shouldRepaint(WaveCirclePainter oldDelegate) => false; 58 | } -------------------------------------------------------------------------------- /lib/common/model/local/isar/video_audio_player_task.dart: -------------------------------------------------------------------------------- 1 | import 'package:bili_video_tunes/common/controller/audio_controller.dart'; 2 | import 'package:isar/isar.dart'; 3 | 4 | part 'generate/video_audio_player_task.g.dart'; 5 | 6 | @collection 7 | class VideoAudioPlayerTask { 8 | Id id = Isar.autoIncrement; 9 | 10 | final String title; 11 | final String description; 12 | final String coverImageUrl; 13 | 14 | @enumerated 15 | final AudioMediaType type; 16 | 17 | String? mediaUrl; 18 | String? bvId; 19 | 20 | //设置音频开始和结束时间 21 | int? startTime; 22 | int? endTime; 23 | 24 | final int totalDuration; 25 | 26 | VideoAudioPlayerTask( 27 | {required this.title, 28 | this.mediaUrl, 29 | this.bvId, 30 | this.startTime, 31 | this.endTime, 32 | required this.description, 33 | required this.coverImageUrl, 34 | required this.type, 35 | required this.totalDuration}); 36 | } 37 | 38 | extension AudioMediaItemExtension on AudioMediaItem { 39 | VideoAudioPlayerTask toVideoAudioPlayerTask() { 40 | return VideoAudioPlayerTask( 41 | title: title, 42 | description: description, 43 | coverImageUrl: coverImageUrl, 44 | mediaUrl: mediaUrl, 45 | type: type, 46 | bvId: bvId, 47 | startTime: startTime, 48 | endTime: endTime, 49 | totalDuration: totalDuration, 50 | ); 51 | } 52 | } 53 | 54 | extension AVideoAudioPlayerTaskExtension on VideoAudioPlayerTask { 55 | AudioMediaItem toAudioMediaItem() { 56 | return AudioMediaItem( 57 | title: title, 58 | description: description, 59 | coverImageUrl: coverImageUrl, 60 | mediaUrl: mediaUrl, 61 | type: type, 62 | bvId: bvId, 63 | startTime: startTime, 64 | endTime: endTime, 65 | totalDuration: totalDuration, 66 | ); 67 | } 68 | } 69 | 70 | -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: "c519ee916eaeb88923e67befb89c0f1dabfa83e6" 8 | channel: "stable" 9 | 10 | project_type: app 11 | 12 | # Tracks metadata for the flutter migrate command 13 | migration: 14 | platforms: 15 | - platform: root 16 | create_revision: c519ee916eaeb88923e67befb89c0f1dabfa83e6 17 | base_revision: c519ee916eaeb88923e67befb89c0f1dabfa83e6 18 | - platform: android 19 | create_revision: c519ee916eaeb88923e67befb89c0f1dabfa83e6 20 | base_revision: c519ee916eaeb88923e67befb89c0f1dabfa83e6 21 | - platform: ios 22 | create_revision: c519ee916eaeb88923e67befb89c0f1dabfa83e6 23 | base_revision: c519ee916eaeb88923e67befb89c0f1dabfa83e6 24 | - platform: linux 25 | create_revision: c519ee916eaeb88923e67befb89c0f1dabfa83e6 26 | base_revision: c519ee916eaeb88923e67befb89c0f1dabfa83e6 27 | - platform: macos 28 | create_revision: c519ee916eaeb88923e67befb89c0f1dabfa83e6 29 | base_revision: c519ee916eaeb88923e67befb89c0f1dabfa83e6 30 | - platform: web 31 | create_revision: c519ee916eaeb88923e67befb89c0f1dabfa83e6 32 | base_revision: c519ee916eaeb88923e67befb89c0f1dabfa83e6 33 | - platform: windows 34 | create_revision: c519ee916eaeb88923e67befb89c0f1dabfa83e6 35 | base_revision: c519ee916eaeb88923e67befb89c0f1dabfa83e6 36 | 37 | # User provided section 38 | 39 | # List of Local paths (relative to this file) that should be 40 | # ignored by the migrate tool. 41 | # 42 | # Files that are not part of the templates will be ignored by default. 43 | unmanaged_files: 44 | - 'lib/main.dart' 45 | - 'ios/Runner.xcodeproj/project.pbxproj' 46 | -------------------------------------------------------------------------------- /lib/common/weight/shimmer/user_page_shimmer.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:flutter/cupertino.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:shimmer/shimmer.dart'; 5 | 6 | class UserPageShimmer extends StatelessWidget{ 7 | const UserPageShimmer({super.key}); 8 | 9 | @override 10 | Widget build(BuildContext context) { 11 | return Center( 12 | child: Column( 13 | mainAxisAlignment: MainAxisAlignment.center, 14 | children: [ 15 | Shimmer.fromColors( 16 | baseColor: Colors.grey.shade300, 17 | highlightColor: Colors.grey.shade100, 18 | child: ClipOval( 19 | child: Container( 20 | color: Colors.grey, 21 | width: 120, 22 | height: 120, 23 | ), 24 | ), 25 | ), 26 | const SizedBox(height: 20,), 27 | Shimmer.fromColors( 28 | baseColor: Colors.grey.shade300, 29 | highlightColor: Colors.grey.shade100, 30 | child: Container( 31 | width: 120, 32 | height: 20, 33 | margin: const EdgeInsets.all(10), 34 | decoration: BoxDecoration( 35 | borderRadius: BorderRadius.circular(10), 36 | color: Colors.grey, 37 | ), 38 | ), 39 | ), 40 | Shimmer.fromColors( 41 | baseColor: Colors.grey.shade300, 42 | highlightColor: Colors.grey.shade100, 43 | child: Container( 44 | width: 140, 45 | height: 20, 46 | margin: const EdgeInsets.all(10), 47 | decoration: BoxDecoration( 48 | borderRadius: BorderRadius.circular(10), 49 | color: Colors.grey, 50 | ), 51 | ), 52 | ), 53 | 54 | ], 55 | ), 56 | ); 57 | } 58 | 59 | } -------------------------------------------------------------------------------- /lib/pages/setting/setting.dart: -------------------------------------------------------------------------------- 1 | import 'package:bili_video_tunes/common/storage/setting_storage.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:get/get.dart'; 4 | import 'package:url_launcher/url_launcher.dart'; 5 | 6 | class Setting extends StatefulWidget { 7 | const Setting({super.key}); 8 | 9 | @override 10 | State createState() => _Setting(); 11 | } 12 | 13 | class _Setting extends State { 14 | final SettingStorage storage = Get.put(SettingStorage()); 15 | 16 | @override 17 | Widget build(BuildContext context) { 18 | return Scaffold( 19 | appBar: AppBar( 20 | title: const Text('设置'), 21 | ), 22 | body: ListView( 23 | children: [ 24 | // ListTile( 25 | // title: Text("title"), 26 | // subtitle: Text("subTitle"), 27 | // trailing: StatefulBuilder(builder: (context, setState) { 28 | // return Switch( 29 | // value: true, 30 | // onChanged: (value) async { 31 | // }, 32 | // ); 33 | // }), 34 | // ), 35 | const ListTile(title:Text("关于项目")), 36 | ListTile( 37 | leading: const Icon(Icons.code_rounded), 38 | title: const Text("GitHub"), 39 | subtitle: const Text("反馈问题并成为贡献者"), 40 | onTap: () async { 41 | final url = Uri.parse("https://github.com/1250422131/BiliVideoTunes"); 42 | if (!await launchUrl(url)) { 43 | throw Exception('Could not launch $url'); 44 | } 45 | }, 46 | ), 47 | ListTile( 48 | leading: const Icon(Icons.local_police_rounded), 49 | title: const Text("隐私政策"), 50 | subtitle: const Text("了解我们如何处理您的信息"), 51 | onTap: (){}, 52 | ), 53 | ], 54 | ), 55 | ); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /windows/runner/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | project(runner LANGUAGES CXX) 3 | 4 | # Define the application target. To change its name, change BINARY_NAME in the 5 | # top-level CMakeLists.txt, not the value here, or `flutter run` will no longer 6 | # work. 7 | # 8 | # Any new source files that you add to the application should be added here. 9 | add_executable(${BINARY_NAME} WIN32 10 | "flutter_window.cpp" 11 | "main.cpp" 12 | "utils.cpp" 13 | "win32_window.cpp" 14 | "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" 15 | "Runner.rc" 16 | "runner.exe.manifest" 17 | ) 18 | 19 | # Apply the standard set of build settings. This can be removed for applications 20 | # that need different build settings. 21 | apply_standard_settings(${BINARY_NAME}) 22 | 23 | # Add preprocessor definitions for the build version. 24 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"") 25 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}") 26 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}") 27 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}") 28 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}") 29 | 30 | # Disable Windows macros that collide with C++ standard library functions. 31 | target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") 32 | 33 | # Add dependency libraries and include directories. Add any application-specific 34 | # dependencies here. 35 | target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) 36 | target_link_libraries(${BINARY_NAME} PRIVATE "dwmapi.lib") 37 | target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") 38 | 39 | # Run the Flutter tool portions of the build. This must not be removed. 40 | add_dependencies(${BINARY_NAME} flutter_assemble) 41 | -------------------------------------------------------------------------------- /ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | Bili Video Tunes 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | bili_video_tunes 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | $(FLUTTER_BUILD_NUMBER) 25 | LSRequiresIPhoneOS 26 | 27 | UILaunchStoryboardName 28 | LaunchScreen 29 | UIMainStoryboardFile 30 | Main 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | CADisableMinimumFrameDurationOnPhone 45 | 46 | UIApplicationSupportsIndirectInputEvents 47 | 48 | UIBackgroundModes 49 | 50 | audio 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /lib/common/utils/http_utils.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:bili_video_tunes/common/api/api_path.dart'; 4 | import 'package:bili_video_tunes/common/api/interceptor/dio_firebase_performance.dart'; 5 | import 'package:bili_video_tunes/common/api/interceptor/login_api_interceptor.dart'; 6 | import 'package:cookie_jar/cookie_jar.dart'; 7 | import 'package:dio/dio.dart'; 8 | import 'package:dio_cookie_manager/dio_cookie_manager.dart'; 9 | import 'package:flutter/foundation.dart'; 10 | import 'package:path_provider/path_provider.dart'; 11 | import 'package:pretty_dio_logger/pretty_dio_logger.dart'; 12 | 13 | final Dio dioClient = Dio(BaseOptions( 14 | baseUrl: baseUrl, 15 | connectTimeout: const Duration(seconds: 5), 16 | receiveTimeout: const Duration(seconds: 5), 17 | contentType: Headers.jsonContentType, 18 | persistentConnection: true, 19 | )) 20 | ..transformer = BackgroundTransformer() 21 | ..interceptors.add(LoginApiInterceptor()) 22 | ..interceptors.add(DioFirebasePerformanceInterceptor()) 23 | ..interceptors.add(PrettyDioLogger( 24 | requestHeader: true, 25 | requestBody: true, 26 | responseBody: false, 27 | responseHeader: false, 28 | error: true, 29 | compact: true, 30 | maxWidth: 90)); 31 | 32 | late PersistCookieJar _cookieJar; 33 | 34 | Future initCookieJar() async { 35 | 36 | final Directory appDocumentsDir = await getApplicationDocumentsDirectory(); 37 | final mCookieJar = PersistCookieJar( 38 | storage: FileStorage("${appDocumentsDir.path}/user/cookies"), 39 | ignoreExpires: true, 40 | ); 41 | 42 | // WEB需要移除,Web交由浏览器自行管理Cookie 43 | if(!kIsWeb){ 44 | dioClient.interceptors.add(CookieManager(mCookieJar)); 45 | _cookieJar = mCookieJar; 46 | } 47 | 48 | } 49 | 50 | 51 | Future findCookieValueByName(String name) async{ 52 | final cookies = await _cookieJar.loadForRequest(Uri.parse(bliUrl)); 53 | return cookies.where((element) => element.name == name).firstOrNull?.value; 54 | } -------------------------------------------------------------------------------- /lib/common/api/search_info_api.dart: -------------------------------------------------------------------------------- 1 | import 'package:bili_video_tunes/common/model/network/home/search_by_type_result_info.dart'; 2 | import 'package:bili_video_tunes/common/model/network/home/search_criteria_emnu.dart'; 3 | import 'package:bili_video_tunes/common/model/network/home/search_default_info.dart'; 4 | import 'package:bili_video_tunes/common/model/network/home/search_square_info.dart'; 5 | import 'package:bili_video_tunes/common/model/network/home/search_suggest_info.dart'; 6 | import 'package:bili_video_tunes/common/utils/http_utils.dart'; 7 | 8 | import 'api_path.dart'; 9 | 10 | class SearchInfoApi { 11 | 12 | static Future getSearchDefaultInfo() async { 13 | final response = await dioClient.get(searchDefaultInfo); 14 | return SearchDefaultInfo.fromJson(response.data); 15 | } 16 | 17 | static Future getSearchSquareInfo({ 18 | int limit = 10, 19 | String platform = "web", 20 | }) async { 21 | final response = await dioClient.get(searchRankInfo, queryParameters: { 22 | "limit": limit, 23 | "platform": platform, 24 | "wts": DateTime.now().millisecondsSinceEpoch 25 | }); 26 | return SearchSquareInfo.fromJson(response.data); 27 | } 28 | 29 | static Future getSearchByTypeResultInfo({ 30 | String searchType = SearchType.video, 31 | String order = SearchOrder.defaultOrder, 32 | required String keyword, 33 | int page = 1, 34 | }) async { 35 | final response = await dioClient.get(searchByTypeInfo, queryParameters: { 36 | "search_type": searchType, 37 | "order": order, 38 | "keyword": keyword, 39 | "page": page, 40 | }); 41 | return SearchByTypeResultInfo.fromJson(response.data); 42 | } 43 | 44 | static Future getSearchSuggestInfo({ 45 | String? term, 46 | }) async { 47 | final response = await dioClient.get(suggestSearchInfo, queryParameters: { 48 | "term": term, 49 | }); 50 | return SearchSuggestInfo.fromJson(response.data); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /lib/common/weight/song_sheet_card.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class SongSheetCard extends StatefulWidget { 4 | final num id; 5 | final String title; 6 | final String cover; 7 | 8 | const SongSheetCard( 9 | {Key? key, required this.id, required this.title, required this.cover}) 10 | : super(key: key); 11 | 12 | @override 13 | State createState() => _SongSheetCardState(); 14 | } 15 | 16 | class _SongSheetCardState extends State { 17 | @override 18 | Widget build(BuildContext context) { 19 | return Column( 20 | mainAxisAlignment: MainAxisAlignment.center, 21 | children: [ 22 | ClipRRect( 23 | borderRadius: const BorderRadius.all(Radius.circular(10)), 24 | child: widget.cover != "" 25 | ? Image.network( 26 | widget.cover, 27 | width: 115, 28 | height: 115, 29 | fit: BoxFit.cover, 30 | // color: Theme.of(context).colorScheme.primary.withOpacity(0.1), 31 | ) 32 | : Container( 33 | width: 115, 34 | height: 115, 35 | color: Theme.of(context).colorScheme.primary.withOpacity(0.1), 36 | child: Stack( 37 | alignment: Alignment.center, 38 | children: [ 39 | Text(widget.title.substring(0,1),style: const TextStyle( 40 | fontSize: 30, 41 | fontWeight: FontWeight.bold 42 | ),) 43 | ], 44 | ), 45 | ), 46 | ), 47 | const SizedBox( 48 | height: 5, 49 | ), 50 | SizedBox( 51 | width: 115, 52 | child: Text( 53 | widget.title, 54 | textAlign: TextAlign.center, 55 | overflow: TextOverflow.ellipsis, 56 | maxLines: 1, 57 | softWrap: false, 58 | ), 59 | ) 60 | ], 61 | ); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // 3 | // To perform an interaction with a widget in your test, use the WidgetTester 4 | // utility in the flutter_test package. For example, you can send tap and scroll 5 | // gestures. You can also use WidgetTester to find child widgets in the widget 6 | // tree, read text, and verify that the values of widget properties are correct. 7 | 8 | import 'package:bili_video_tunes/common/controller/audio_controller.dart'; 9 | import 'package:bili_video_tunes/common/controller/user_controller.dart'; 10 | import 'package:bili_video_tunes/common/handler/bili_audio_handler.dart'; 11 | import 'package:bili_video_tunes/common/weight/shimmer/user_page_shimmer.dart'; 12 | import 'package:bili_video_tunes/common/weight/shimmer/video_card_grid_view_shimmer.dart'; 13 | import 'package:bili_video_tunes/main.dart'; 14 | import 'package:bili_video_tunes/services/bili_audio_service.dart'; 15 | import 'package:bili_video_tunes/services/service_locator.dart'; 16 | import 'package:flutter_test/flutter_test.dart'; 17 | import 'package:get/get.dart'; 18 | 19 | void main() { 20 | // testWidgets('Counter increments smoke test', (WidgetTester tester) async { 21 | // // 全局支持音乐播放器和对应控制器 22 | // Get.put(UserController()); 23 | // Get.put(BiliAudioService()); 24 | // Get.put(await initAudioService()); 25 | // Get.put(AudioController()); 26 | // 27 | // await tester.pumpWidget(const UserPageShimmer()); 28 | // await Future.delayed(const Duration(seconds: 5)); 29 | // 30 | // 31 | // expect(find.text('0'), findsOneWidget); 32 | // expect(find.text('1'), findsNothing); 33 | // 34 | // await Future.delayed(const Duration(seconds: 5)); 35 | // 36 | // // Tap the '+' icon and trigger a frame. 37 | // await tester.tap(find.byIcon(Icons.add)); 38 | // await tester.pump(); 39 | // 40 | // // Verify that our counter has incremented. 41 | // expect(find.text('0'), findsNothing); 42 | // expect(find.text('1'), findsOneWidget); 43 | // 44 | // }); 45 | } 46 | -------------------------------------------------------------------------------- /windows/runner/utils.cpp: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | void CreateAndAttachConsole() { 11 | if (::AllocConsole()) { 12 | FILE *unused; 13 | if (freopen_s(&unused, "CONOUT$", "w", stdout)) { 14 | _dup2(_fileno(stdout), 1); 15 | } 16 | if (freopen_s(&unused, "CONOUT$", "w", stderr)) { 17 | _dup2(_fileno(stdout), 2); 18 | } 19 | std::ios::sync_with_stdio(); 20 | FlutterDesktopResyncOutputStreams(); 21 | } 22 | } 23 | 24 | std::vector GetCommandLineArguments() { 25 | // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. 26 | int argc; 27 | wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); 28 | if (argv == nullptr) { 29 | return std::vector(); 30 | } 31 | 32 | std::vector command_line_arguments; 33 | 34 | // Skip the first argument as it's the binary name. 35 | for (int i = 1; i < argc; i++) { 36 | command_line_arguments.push_back(Utf8FromUtf16(argv[i])); 37 | } 38 | 39 | ::LocalFree(argv); 40 | 41 | return command_line_arguments; 42 | } 43 | 44 | std::string Utf8FromUtf16(const wchar_t* utf16_string) { 45 | if (utf16_string == nullptr) { 46 | return std::string(); 47 | } 48 | int target_length = ::WideCharToMultiByte( 49 | CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, 50 | -1, nullptr, 0, nullptr, nullptr) 51 | -1; // remove the trailing null character 52 | int input_length = (int)wcslen(utf16_string); 53 | std::string utf8_string; 54 | if (target_length <= 0 || target_length > utf8_string.max_size()) { 55 | return utf8_string; 56 | } 57 | utf8_string.resize(target_length); 58 | int converted_length = ::WideCharToMultiByte( 59 | CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, 60 | input_length, utf8_string.data(), target_length, nullptr, nullptr); 61 | if (converted_length == 0) { 62 | return std::string(); 63 | } 64 | return utf8_string; 65 | } 66 | -------------------------------------------------------------------------------- /web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | bili_video_tunes 33 | 34 | 35 | 39 | 40 | 41 | 42 | 43 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /lib/pages/main_search/controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:bili_video_tunes/common/api/search_info_api.dart'; 2 | import 'package:bili_video_tunes/common/model/network/home/search_by_type_result_info.dart'; 3 | import 'package:bili_video_tunes/common/model/network/home/search_criteria_emnu.dart'; 4 | import 'package:bili_video_tunes/common/model/network/home/search_square_info.dart'; 5 | import 'package:bili_video_tunes/common/utils/extends.dart'; 6 | import 'package:flutter/widgets.dart'; 7 | import 'package:get/get.dart'; 8 | import 'package:get/get_rx/get_rx.dart'; 9 | import 'package:get/get_rx/src/rx_types/rx_types.dart'; 10 | 11 | class MainSearchController extends GetxController { 12 | Rx searchQueryInfo = SearchSquareInfo().obs; 13 | Rx searchResultInfo = SearchByTypeResultInfo().obs; 14 | 15 | RxList searchResultList = RxList.empty(); 16 | 17 | Future loadSearchQueryInfo() async { 18 | final searchQueryInfo = await SearchInfoApi.getSearchSquareInfo(); 19 | this.searchQueryInfo.value = searchQueryInfo; 20 | } 21 | 22 | Future loadSearchByTypeResultInfo( 23 | {String searchType = SearchType.video, 24 | String order = SearchOrder.defaultOrder, 25 | required String keyword, 26 | int page = 1, 27 | bool isClear = false}) async { 28 | if (isClear) { 29 | searchResultList.clear(); 30 | } 31 | 32 | final searchQueryInfo = await SearchInfoApi.getSearchByTypeResultInfo( 33 | searchType: searchType, 34 | order: order, 35 | keyword: keyword, 36 | page: page, 37 | ); 38 | 39 | searchResultInfo.value = searchQueryInfo; 40 | 41 | searchQueryInfo.data?.result?.also((it) { 42 | searchResultList.addAll(it); 43 | }); 44 | } 45 | 46 | void closeData() { 47 | // 销毁时清理资源 48 | debugPrint("MainSearchController closeData"); 49 | searchQueryInfo.value = SearchSquareInfo(); 50 | searchResultInfo.value = SearchByTypeResultInfo(); 51 | searchResultList.clear(); 52 | } 53 | 54 | @override 55 | void dispose() { 56 | super.dispose(); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /lib/common/weight/shimmer/video_card_grid_view_shimmer.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:flutter/material.dart'; 3 | import 'package:shimmer/shimmer.dart'; 4 | 5 | class VideoCardGridViewShimmer extends StatelessWidget{ 6 | const VideoCardGridViewShimmer({super.key}); 7 | 8 | 9 | @override 10 | Widget build(BuildContext context) { 11 | return GridView.count( 12 | // 定义列数 13 | crossAxisCount: 2, 14 | // 定义列边距 15 | crossAxisSpacing: 5, 16 | // 定义行边距 17 | mainAxisSpacing: 5, 18 | // 定义内边距 19 | padding: const EdgeInsets.all(5), 20 | children: List.generate( 21 | 10, 22 | (index) => Card( 23 | shape: RoundedRectangleBorder( 24 | borderRadius: BorderRadius.circular(10), 25 | ), 26 | clipBehavior: Clip.antiAlias, 27 | //<--- 裁剪行为 28 | child: Shimmer.fromColors( 29 | baseColor: Colors.grey.shade300, 30 | highlightColor: Colors.grey.shade100, 31 | child: Column( 32 | crossAxisAlignment: CrossAxisAlignment.start, 33 | children: [ 34 | Container( 35 | height: 100, 36 | margin: const EdgeInsets.all(10), 37 | decoration: BoxDecoration( 38 | borderRadius: BorderRadius.circular(10), 39 | color: Colors.grey, 40 | ), 41 | ), 42 | Expanded( 43 | child: Container( 44 | height: 10, 45 | margin: const EdgeInsets.all(7), 46 | decoration: BoxDecoration( 47 | borderRadius: BorderRadius.circular(5), 48 | color: Colors.grey, 49 | ), 50 | ), 51 | ) 52 | ], 53 | ), 54 | ))), 55 | ); 56 | } 57 | 58 | } -------------------------------------------------------------------------------- /lib/common/api/api_path.dart: -------------------------------------------------------------------------------- 1 | const String baseUrl = "https://api.bilibili.com/"; 2 | 3 | const String passportBaseUrl = "https://passport.bilibili.com/"; 4 | 5 | const String videoMusicHotsPath = "x/tag/hots"; 6 | 7 | const String videoNewRankPath = "x/web-interface/newlist_rank"; 8 | 9 | const String newVideoDynamicPath = "x/web-interface/dynamic/region"; 10 | 11 | // 携带Tag 12 | const String newVideoDynamicTagePath = "x/web-interface/dynamic/tag"; 13 | 14 | // 获取视频基础信息 15 | const String getVideoDataPath = "x/web-interface/view"; 16 | 17 | const String videoPlayerDataPath = "x/player/playurl"; 18 | 19 | // 视频更多信息 20 | const String morePlayerIntoPath = "x/player/wbi/v2"; 21 | 22 | // 登录用户信息 23 | const String loginUserInfoPath = "x/web-interface/nav"; 24 | 25 | // 播放历史 26 | const String playerHistoryPath = "x/web-interface/history/cursor"; 27 | 28 | // 播放日志上报 29 | const String historyReportPath = "x/v2/history/report"; 30 | 31 | // 播放心跳 32 | const String playerHeartbeatPath = "x/click-interface/web/heartbeat"; 33 | 34 | // 获取登录QR码 35 | const String loginQrcodeGeneratePath = "x/passport-login/web/qrcode/generate"; 36 | 37 | // 检查登录状态 38 | const String loginQrcodePollPath = "x/passport-login/web/qrcode/poll"; 39 | 40 | const String userCreateFolderListPath = "/x/v3/fav/folder/created/list"; 41 | 42 | // 默认提示搜索 43 | const String searchDefaultInfo = "x/web-interface/wbi/search/default"; 44 | 45 | // 搜索排行 46 | const String searchRankInfo = "x/web-interface/wbi/search/square"; 47 | 48 | const String searchByTypeInfo = "x/web-interface/wbi/search/type"; 49 | 50 | const String suggestSearchInfo = "main/suggest"; 51 | 52 | const String favResourceListPath = "x/v3/fav/resource/list"; 53 | 54 | const String browserUserAgent = 55 | "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36 Edg/108.0.1462.76"; 56 | 57 | const String userAgent = "User-Agent"; 58 | 59 | const String bliUrl = "https://www.bilibili.com"; 60 | 61 | const String aiSubtitleUrl = "https://aisubtitle.hdslb.com"; 62 | 63 | const String searchUrl = "https://s.search.bilibili.com"; 64 | 65 | const String referer = "Referer"; 66 | -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/imcys/bilivideotunes/bili_video_tunes/openApp/OpenAppPlugins.kt: -------------------------------------------------------------------------------- 1 | package com.imcys.bilivideotunes.bili_video_tunes.openApp 2 | 3 | import android.content.Context 4 | import android.content.Intent 5 | import android.net.Uri 6 | import io.flutter.embedding.engine.plugins.FlutterPlugin 7 | import io.flutter.plugin.common.MethodCall 8 | import io.flutter.plugin.common.MethodChannel 9 | 10 | 11 | class OpenAppPlugins : MethodChannel.MethodCallHandler, FlutterPlugin { 12 | 13 | private lateinit var binding: FlutterPlugin.FlutterPluginBinding 14 | 15 | private lateinit var channel: MethodChannel 16 | 17 | private lateinit var context: Context 18 | 19 | override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) { 20 | 21 | when (call.method) { 22 | "openAppChannel" -> { 23 | val packageName = call.argument("package") 24 | val path = call.argument("path") 25 | if (isInstallApp(context, packageName)) { 26 | Intent(Intent.ACTION_VIEW, Uri.parse(path)).also { 27 | it.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) 28 | context.startActivity(it) 29 | } 30 | return result.success(true) 31 | } 32 | return result.success(false) 33 | } 34 | 35 | else -> result.notImplemented() 36 | } 37 | 38 | 39 | } 40 | 41 | override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { 42 | context = flutterPluginBinding.applicationContext 43 | channel = 44 | MethodChannel(flutterPluginBinding.binaryMessenger, "openAppChannel") 45 | channel.setMethodCallHandler(this) 46 | this.binding = flutterPluginBinding 47 | } 48 | 49 | override fun onDetachedFromEngine(p0: FlutterPlugin.FlutterPluginBinding) { 50 | channel.setMethodCallHandler(null) 51 | } 52 | 53 | private fun isInstallApp(context: Context, appPackageName: String?): Boolean { 54 | return context.packageManager.getLaunchIntentForPackage(appPackageName!!) != null 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /lib/common/api/video_music_api.dart: -------------------------------------------------------------------------------- 1 | import 'package:bili_video_tunes/common/api/api_path.dart'; 2 | import 'package:bili_video_tunes/common/model/network/video_music/new_list_rank_info.dart'; 3 | import 'package:bili_video_tunes/common/model/network/video_music/new_video_dynamic_info.dart'; 4 | import 'package:bili_video_tunes/common/utils/http_utils.dart'; 5 | 6 | import '../model/network/video_music/host_info.dart'; 7 | 8 | class VideoMusicApi { 9 | static Future getHostInfo( 10 | {required num rid, required num type}) async { 11 | final result = await dioClient 12 | .get(videoMusicHotsPath, queryParameters: {"rid": rid, "type": type}); 13 | 14 | return HostInfo.fromJson(result.data); 15 | } 16 | 17 | static Future getNewVideoDynamicInfo({ 18 | required num rid, 19 | required num pn, 20 | required num ps, 21 | }) async { 22 | final result = await dioClient.get(newVideoDynamicPath, 23 | queryParameters: {"rid": rid, "pn": pn, "ps": ps}); 24 | 25 | return NewVideoDynamicInfo.fromJson(result.data); 26 | } 27 | 28 | static Future getNewVideoDynamicTagInfo({ 29 | required num rid, 30 | required num tagId, 31 | required num pn, 32 | required num ps, 33 | }) async { 34 | final result = await dioClient.get(newVideoDynamicTagePath, 35 | queryParameters: {"rid": rid, "pn": pn, "ps": ps, "tag_id": tagId}); 36 | 37 | return NewVideoDynamicInfo.fromJson(result.data); 38 | } 39 | 40 | static Future getNewRankInfo( 41 | {required num cateId, 42 | required num page, 43 | required num pageSize, 44 | required num timeFrom, 45 | required num timeTo, 46 | String? keyword}) async { 47 | final result = await dioClient.get(videoNewRankPath, queryParameters: { 48 | "main_ver": "v3", 49 | "search_type": "video", 50 | "view_type": "hot_rank", 51 | "order": "click", 52 | "copy_right": -1, 53 | "new_web_tag": 1, 54 | "cate_id": cateId, 55 | "page": page, 56 | "pagesize": pageSize, 57 | "time_from": timeFrom, 58 | "time_to": timeTo, 59 | "keyword": keyword 60 | }); 61 | 62 | return NewListRankInfo.fromJson(result.data); 63 | } 64 | 65 | 66 | 67 | 68 | 69 | } 70 | -------------------------------------------------------------------------------- /lib/common/weight/colorful_text_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class ColorfulTextWidget extends StatefulWidget { 4 | final String text; 5 | final Duration duration; 6 | final bool shouldStartAnimation; // 这里添加了一个用于控制动画开始的布尔值 7 | 8 | 9 | 10 | const ColorfulTextWidget({super.key, 11 | required this.text, 12 | required this.duration, 13 | this.shouldStartAnimation = false, 14 | }); 15 | 16 | @override 17 | createState() => _ColorfulTextWidgetState(); 18 | } 19 | 20 | class _ColorfulTextWidgetState extends State with SingleTickerProviderStateMixin { 21 | late AnimationController _controller; 22 | 23 | 24 | 25 | @override 26 | void initState() { 27 | super.initState(); 28 | 29 | 30 | _controller = AnimationController( 31 | duration: widget.duration, 32 | vsync: this, 33 | ); 34 | 35 | // 如果条件满足,开始动画 36 | if (widget.shouldStartAnimation) { 37 | _controller.forward(); 38 | } 39 | } 40 | 41 | @override 42 | void didUpdateWidget(ColorfulTextWidget oldWidget) { 43 | super.didUpdateWidget(oldWidget); 44 | // 当条件变为true时,启动动画 45 | if (widget.shouldStartAnimation && !oldWidget.shouldStartAnimation) { 46 | _controller.forward(); 47 | } 48 | } 49 | 50 | @override 51 | void dispose() { 52 | _controller.dispose(); 53 | super.dispose(); 54 | } 55 | 56 | @override 57 | Widget build(BuildContext context) { 58 | return AnimatedBuilder( 59 | animation: _controller, 60 | builder: (context, child) { 61 | return RichText( 62 | textAlign:TextAlign.center , 63 | text: TextSpan( 64 | children: _buildTextSpans(), 65 | style: DefaultTextStyle.of(context).style, 66 | ), 67 | ); 68 | }, 69 | ); 70 | } 71 | 72 | List _buildTextSpans() { 73 | // 上色进度,_controller.value是一个0-1变动的值,这个值乘以总数目,就可以获得需要上色的字数 74 | int coloredCharCount = (_controller.value * widget.text.length).round(); 75 | List spans = []; 76 | 77 | for (int i = 0; i < widget.text.length; i++) { 78 | 79 | final color = i < coloredCharCount && widget.shouldStartAnimation ? Colors.blue : Colors.black; 80 | spans.add(TextSpan(text: widget.text[i], style: TextStyle(color: color,fontSize: 16))); 81 | 82 | } 83 | 84 | return spans; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /windows/runner/flutter_window.cpp: -------------------------------------------------------------------------------- 1 | #include "flutter_window.h" 2 | 3 | #include 4 | 5 | #include "flutter/generated_plugin_registrant.h" 6 | 7 | FlutterWindow::FlutterWindow(const flutter::DartProject& project) 8 | : project_(project) {} 9 | 10 | FlutterWindow::~FlutterWindow() {} 11 | 12 | bool FlutterWindow::OnCreate() { 13 | if (!Win32Window::OnCreate()) { 14 | return false; 15 | } 16 | 17 | RECT frame = GetClientArea(); 18 | 19 | // The size here must match the window dimensions to avoid unnecessary surface 20 | // creation / destruction in the startup path. 21 | flutter_controller_ = std::make_unique( 22 | frame.right - frame.left, frame.bottom - frame.top, project_); 23 | // Ensure that basic setup of the controller was successful. 24 | if (!flutter_controller_->engine() || !flutter_controller_->view()) { 25 | return false; 26 | } 27 | RegisterPlugins(flutter_controller_->engine()); 28 | SetChildContent(flutter_controller_->view()->GetNativeWindow()); 29 | 30 | flutter_controller_->engine()->SetNextFrameCallback([&]() { 31 | this->Show(); 32 | }); 33 | 34 | // Flutter can complete the first frame before the "show window" callback is 35 | // registered. The following call ensures a frame is pending to ensure the 36 | // window is shown. It is a no-op if the first frame hasn't completed yet. 37 | flutter_controller_->ForceRedraw(); 38 | 39 | return true; 40 | } 41 | 42 | void FlutterWindow::OnDestroy() { 43 | if (flutter_controller_) { 44 | flutter_controller_ = nullptr; 45 | } 46 | 47 | Win32Window::OnDestroy(); 48 | } 49 | 50 | LRESULT 51 | FlutterWindow::MessageHandler(HWND hwnd, UINT const message, 52 | WPARAM const wparam, 53 | LPARAM const lparam) noexcept { 54 | // Give Flutter, including plugins, an opportunity to handle window messages. 55 | if (flutter_controller_) { 56 | std::optional result = 57 | flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, 58 | lparam); 59 | if (result) { 60 | return *result; 61 | } 62 | } 63 | 64 | switch (message) { 65 | case WM_FONTCHANGE: 66 | flutter_controller_->engine()->ReloadSystemFonts(); 67 | break; 68 | } 69 | 70 | return Win32Window::MessageHandler(hwnd, message, wparam, lparam); 71 | } 72 | -------------------------------------------------------------------------------- /android/app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 18 | 19 |