├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.yaml │ └── feature_request.yaml └── workflows │ └── main.yml ├── .gitignore ├── .metadata ├── LICENSE ├── README.md ├── analysis_options.yaml ├── android ├── .gitignore ├── app │ ├── build.gradle │ └── src │ │ ├── debug │ │ └── AndroidManifest.xml │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── kotlin │ │ │ └── com │ │ │ │ └── lucinhu │ │ │ │ └── bili_you │ │ │ │ └── MainActivity.kt │ │ └── res │ │ │ ├── drawable-hdpi-v26 │ │ │ └── ic_launcher_monochrome.png │ │ │ ├── drawable-hdpi │ │ │ └── ic_launcher_foreground.png │ │ │ ├── drawable-mdpi-v26 │ │ │ └── ic_launcher_monochrome.png │ │ │ ├── drawable-mdpi │ │ │ └── ic_launcher_foreground.png │ │ │ ├── drawable-v21 │ │ │ └── launch_background.xml │ │ │ ├── drawable-xhdpi-v26 │ │ │ └── ic_launcher_monochrome.png │ │ │ ├── drawable-xhdpi │ │ │ └── ic_launcher_foreground.png │ │ │ ├── drawable-xxhdpi-v26 │ │ │ └── ic_launcher_monochrome.png │ │ │ ├── drawable-xxhdpi │ │ │ └── ic_launcher_foreground.png │ │ │ ├── drawable-xxxhdpi-v26 │ │ │ └── ic_launcher_monochrome.png │ │ │ ├── drawable-xxxhdpi │ │ │ └── ic_launcher_foreground.png │ │ │ ├── drawable │ │ │ └── launch_background.xml │ │ │ ├── mipmap-anydpi-v26 │ │ │ └── ic_launcher.xml │ │ │ ├── mipmap-hdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-mdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxxhdpi │ │ │ └── ic_launcher.png │ │ │ ├── values-night │ │ │ └── styles.xml │ │ │ └── values │ │ │ ├── colors.xml │ │ │ └── styles.xml │ │ └── profile │ │ └── AndroidManifest.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties └── settings.gradle ├── assets ├── icon │ └── bili.png └── screenshot │ ├── v1.0.1 │ ├── main_page.png │ ├── reply.png │ ├── reply_reply.png │ ├── search_page.png │ ├── sms_login.png │ ├── user_info.png │ ├── video_fullscreen.png │ ├── video_part.png │ ├── video_play.png │ └── video_search.png │ └── v1.0.3 │ ├── about_page.png │ ├── bangumi_play.png │ ├── bangumi_search.png │ ├── main_page.png │ ├── reply.png │ ├── reply_reply.png │ ├── search_page.png │ ├── user_info.png │ ├── video_play.png │ └── video_search.png ├── fastlane └── metadata │ └── android │ ├── en-US │ ├── full_description.txt │ ├── images │ │ └── phoneScreenshots │ │ │ ├── about_page.png │ │ │ ├── bangumi_play.png │ │ │ ├── bangumi_search.png │ │ │ ├── main_page.png │ │ │ ├── reply.png │ │ │ ├── reply_reply.png │ │ │ ├── search_page.png │ │ │ ├── user_info.png │ │ │ ├── video_play.png │ │ │ └── video_search.png │ └── short_description.txt │ └── zh-CN │ ├── full_description.txt │ └── short_description.txt ├── ios ├── .gitignore ├── Flutter │ ├── AppFrameworkInfo.plist │ ├── Debug.xcconfig │ └── Release.xcconfig ├── Podfile ├── Podfile.lock ├── Runner.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── WorkspaceSettings.xcsettings │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings ├── Runner │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ ├── 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-50x50@1x.png │ │ │ ├── Icon-App-50x50@2x.png │ │ │ ├── Icon-App-57x57@1x.png │ │ │ ├── Icon-App-57x57@2x.png │ │ │ ├── Icon-App-60x60@2x.png │ │ │ ├── Icon-App-60x60@3x.png │ │ │ ├── Icon-App-72x72@1x.png │ │ │ ├── Icon-App-72x72@2x.png │ │ │ ├── Icon-App-76x76@1x.png │ │ │ ├── Icon-App-76x76@2x.png │ │ │ └── Icon-App-83.5x83.5@2x.png │ │ └── LaunchImage.imageset │ │ │ ├── Contents.json │ │ │ ├── LaunchImage.png │ │ │ ├── LaunchImage@2x.png │ │ │ ├── LaunchImage@3x.png │ │ │ └── README.md │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── Info.plist │ └── Runner-Bridging-Header.h └── RunnerTests │ └── RunnerTests.swift ├── lib ├── common │ ├── api │ │ ├── api_constants.dart │ │ ├── bangumi_api.dart │ │ ├── danmaku_api.dart │ │ ├── dynamic_api.dart │ │ ├── github_api.dart │ │ ├── history_api.dart │ │ ├── home_api.dart │ │ ├── index.dart │ │ ├── live_api.dart │ │ ├── login_api.dart │ │ ├── realtions_api.dart │ │ ├── related_video_api.dart │ │ ├── reply_api.dart │ │ ├── reply_operation_api.dart │ │ ├── search_api.dart │ │ ├── user_space_api.dart │ │ ├── video_info_api.dart │ │ ├── video_operation_api.dart │ │ ├── video_play_api.dart │ │ └── wbi.dart │ ├── index.dart │ ├── models │ │ ├── index.dart │ │ ├── local │ │ │ ├── bangumi │ │ │ │ ├── bangumi_info.dart │ │ │ │ └── episode_info.dart │ │ │ ├── dynamic │ │ │ │ ├── dynamic_author.dart │ │ │ │ ├── dynamic_content.dart │ │ │ │ ├── dynamic_item.dart │ │ │ │ └── dynamic_stat.dart │ │ │ ├── history │ │ │ │ └── video_view_history_item.dart │ │ │ ├── home │ │ │ │ └── recommend_item_info.dart │ │ │ ├── live │ │ │ │ └── live_room_card_info.dart │ │ │ ├── login │ │ │ │ ├── level_info.dart │ │ │ │ ├── login_qrcode_info.dart │ │ │ │ ├── login_qrcode_stat.dart │ │ │ │ ├── login_user_info.dart │ │ │ │ └── login_user_stat.dart │ │ │ ├── reply │ │ │ │ ├── add_reply_result.dart │ │ │ │ ├── official_verify.dart │ │ │ │ ├── reply_add_like_result.dart │ │ │ │ ├── reply_content.dart │ │ │ │ ├── reply_info.dart │ │ │ │ ├── reply_item.dart │ │ │ │ ├── reply_member.dart │ │ │ │ ├── reply_reply_info.dart │ │ │ │ └── vip.dart │ │ │ ├── search │ │ │ │ ├── default_search_word.dart │ │ │ │ ├── hot_word_item.dart │ │ │ │ ├── search_bangumi_item.dart │ │ │ │ ├── search_suggest_item.dart │ │ │ │ ├── search_user_item.dart │ │ │ │ └── search_video_item.dart │ │ │ ├── user_space │ │ │ │ └── user_video_search.dart │ │ │ ├── video │ │ │ │ ├── audio_play_item.dart │ │ │ │ ├── click_add_coin_result.dart │ │ │ │ ├── click_add_share_result.dart │ │ │ │ ├── click_like_result.dart │ │ │ │ ├── part_info.dart │ │ │ │ ├── video_info.dart │ │ │ │ ├── video_play_info.dart │ │ │ │ └── video_play_item.dart │ │ │ └── video_tile │ │ │ │ └── video_tile_info.dart │ │ └── network │ │ │ ├── bangumi │ │ │ └── bangumi_info.dart │ │ │ ├── dynamic │ │ │ └── dynamic.dart │ │ │ ├── github │ │ │ └── github_releases_item.dart │ │ │ ├── history │ │ │ └── report_history.dart │ │ │ ├── home │ │ │ └── recommend_video.dart │ │ │ ├── login │ │ │ ├── captcha_data.dart │ │ │ ├── captcha_result.dart │ │ │ ├── password_login_key_hash.dart │ │ │ ├── password_login_result.dart │ │ │ ├── post_sms_login.dart │ │ │ └── post_sms_require.dart │ │ │ ├── proto │ │ │ └── danmaku │ │ │ │ ├── danmaku.pb.dart │ │ │ │ ├── danmaku.pbenum.dart │ │ │ │ ├── danmaku.pbjson.dart │ │ │ │ ├── danmaku.pbserver.dart │ │ │ │ └── danmaku.proto │ │ │ ├── related_video │ │ │ └── related_video.dart │ │ │ ├── reply │ │ │ ├── reply.dart │ │ │ └── reply_reply.dart │ │ │ ├── search │ │ │ ├── default_search_word.dart │ │ │ ├── hot_words.dart │ │ │ ├── search_bangumi.dart │ │ │ ├── search_suggest.dart │ │ │ └── search_video.dart │ │ │ ├── user │ │ │ ├── user_info.dart │ │ │ └── user_stat.dart │ │ │ ├── user_relations │ │ │ ├── user_realtion.dart │ │ │ └── user_relation_types.dart │ │ │ ├── user_space │ │ │ └── user_video_search.dart │ │ │ ├── video_info │ │ │ ├── video_info.dart │ │ │ └── video_parts.dart │ │ │ └── video_play │ │ │ └── video_play.dart │ ├── utils │ │ ├── bili_you_storage.dart │ │ ├── bvid_avid_util.dart │ │ ├── cache_util.dart │ │ ├── cookie_util.dart │ │ ├── fullscreen.dart │ │ ├── http_utils.dart │ │ ├── index.dart │ │ ├── settings.dart │ │ ├── show_dialog.dart │ │ └── string_format_utils.dart │ ├── values │ │ ├── coutry_id.dart │ │ ├── hero_tag_id.dart │ │ └── index.dart │ └── widget │ │ ├── avatar.dart │ │ ├── bangumi_tile_item.dart │ │ ├── bili_url_scheme.dart │ │ ├── cached_network_image.dart │ │ ├── foldable_text.dart │ │ ├── icon_text_button.dart │ │ ├── index.dart │ │ ├── live_room_card.dart │ │ ├── radio_list_dialog.dart │ │ ├── settings_label.dart │ │ ├── settings_radios_tile.dart │ │ ├── settings_slider_tile.dart │ │ ├── settings_switch_tile.dart │ │ ├── simple_easy_refresher.dart │ │ ├── slider_dialog.dart │ │ ├── tag.dart │ │ ├── user_tile_item.dart │ │ ├── video_audio_player.dart │ │ ├── video_tile_item.dart │ │ └── video_view_history_tile.dart ├── index.dart ├── main.dart └── pages │ ├── about │ ├── about_page.dart │ └── index.dart │ ├── bili_live │ ├── controller.dart │ └── view.dart │ ├── bili_video │ ├── controller.dart │ ├── index.dart │ ├── view.dart │ └── widgets │ │ ├── bili_video_player │ │ ├── bili_danmaku.dart │ │ ├── bili_video_player.dart │ │ └── bili_video_player_panel.dart │ │ ├── introduction │ │ ├── controller.dart │ │ ├── index.dart │ │ └── view.dart │ │ └── reply │ │ ├── add_reply_util.dart │ │ ├── controller.dart │ │ ├── index.dart │ │ ├── view.dart │ │ └── widgets │ │ ├── reply_item.dart │ │ └── reply_reply_page.dart │ ├── bili_video1 │ ├── bili_media_content.dart │ ├── bili_media_content_cubit.dart │ ├── bili_media_cubit.dart │ ├── bili_video_page.dart │ └── bili_video_player.dart │ ├── dynamic │ ├── controller.dart │ ├── index.dart │ ├── view.dart │ └── widget │ │ ├── dynamic_article.dart │ │ ├── dynamic_author_filter.dart │ │ ├── dynamic_draw.dart │ │ ├── dynamic_item_card.dart │ │ └── dynamic_video_card.dart │ ├── history │ ├── history_controller.dart │ └── history_page.dart │ ├── home │ ├── controller.dart │ ├── index.dart │ ├── view.dart │ └── widgets │ │ └── user_menu │ │ ├── controller.dart │ │ ├── index.dart │ │ └── view.dart │ ├── live_tab_page │ ├── controller.dart │ └── view.dart │ ├── login │ ├── controller.dart │ ├── password_login │ │ ├── controller.dart │ │ ├── index.dart │ │ └── view.dart │ ├── qrcode_login │ │ ├── controller.dart │ │ └── view.dart │ ├── sms_login │ │ ├── controller.dart │ │ ├── index.dart │ │ ├── view.dart │ │ └── widgets │ │ │ └── error │ │ │ ├── controller.dart │ │ │ ├── index.dart │ │ │ └── view.dart │ └── web_login │ │ └── view.dart │ ├── main │ ├── controller.dart │ ├── index.dart │ └── view.dart │ ├── popular_video │ ├── controller.dart │ └── view.dart │ ├── recommend │ ├── controller.dart │ ├── index.dart │ ├── view.dart │ └── widgets │ │ └── recommend_card.dart │ ├── relation │ ├── controller.dart │ ├── index.dart │ └── view.dart │ ├── search_input │ ├── controller.dart │ ├── index.dart │ └── view.dart │ ├── search_result │ ├── controller.dart │ ├── index.dart │ └── view.dart │ ├── search_tab_view │ ├── controller.dart │ └── view.dart │ ├── settings_page │ ├── appearance_settings_page.dart │ ├── cache_management_page.dart │ ├── common_settings_page.dart │ ├── others_settings_page.dart │ └── settings_page.dart │ ├── splash │ ├── controller.dart │ ├── index.dart │ └── view.dart │ ├── ui_test │ ├── controller.dart │ ├── index.dart │ ├── test_widget │ │ └── media_kit_test_page.dart │ └── view.dart │ ├── user_space │ ├── controller.dart │ └── view.dart │ └── webview │ └── browser.dart ├── linux ├── .gitignore ├── CMakeLists.txt ├── flutter │ ├── CMakeLists.txt │ ├── generated_plugin_registrant.cc │ ├── generated_plugin_registrant.h │ └── generated_plugins.cmake ├── main.cc ├── my_application.cc └── my_application.h ├── macos ├── -configuration ├── .gitignore ├── .xcodeproj ├── Flutter │ ├── Flutter-Debug.xcconfig │ ├── Flutter-Release.xcconfig │ └── GeneratedPluginRegistrant.swift ├── Podfile ├── Podfile.lock ├── Runner.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── Runner │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ └── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── app_icon_1024.png │ │ │ ├── app_icon_128.png │ │ │ ├── app_icon_16.png │ │ │ ├── app_icon_256.png │ │ │ ├── app_icon_32.png │ │ │ ├── app_icon_512.png │ │ │ └── app_icon_64.png │ ├── Base.lproj │ │ └── MainMenu.xib │ ├── Configs │ │ ├── AppInfo.xcconfig │ │ ├── Debug.xcconfig │ │ ├── Release.xcconfig │ │ └── Warnings.xcconfig │ ├── DebugProfile.entitlements │ ├── Info.plist │ ├── MainFlutterWindow.swift │ └── Release.entitlements └── RunnerTests │ └── RunnerTests.swift ├── pubspec.lock ├── pubspec.yaml ├── scripts ├── bili_you.AppDir │ ├── AppRun │ └── bili_you.desktop ├── build.sh ├── build_linux_appimage.sh └── run_flutter_laucher_icons.sh ├── web ├── favicon.png ├── icons │ ├── Icon-192.png │ ├── Icon-512.png │ ├── Icon-maskable-192.png │ └── Icon-maskable-512.png ├── index.html └── manifest.json └── windows ├── .gitignore ├── CMakeLists.txt ├── flutter ├── CMakeLists.txt ├── generated_plugin_registrant.cc ├── generated_plugin_registrant.h └── generated_plugins.cmake └── runner ├── CMakeLists.txt ├── Runner.rc ├── flutter_window.cpp ├── flutter_window.h ├── main.cpp ├── resource.h ├── resources └── app_icon.ico ├── runner.exe.manifest ├── utils.cpp ├── utils.h ├── win32_window.cpp └── win32_window.h /.github/ISSUE_TEMPLATE/bug_report.yaml: -------------------------------------------------------------------------------- 1 | name: "Bug report" 2 | description: 创建问题报告以帮助 bili_you 更好 3 | labels: 4 | - bug 5 | assignees: lucinhu 6 | body: 7 | - type: textarea 8 | attributes: 9 | label: Bug 描述 10 | description: 简洁而有力的描述 Bug 11 | validations: 12 | required: true 13 | - type: textarea 14 | attributes: 15 | label: 复现步骤 16 | description: 如何复现你描述的 Bug ? 17 | validations: 18 | required: true 19 | - type: textarea 20 | attributes: 21 | label: 预期行为 22 | description: 若无 Bug 你认为 bili_you 改如何工作 23 | - type: textarea 24 | attributes: 25 | label: 屏幕捕获 26 | description: 如果可用,添加一些截图帮助排查问题 27 | - type: textarea 28 | attributes: 29 | label: 版本信息 30 | description: 填写一些版本信息 31 | value: | 32 | 1. Android 版本: 33 | 2. Webview 版本: 34 | 3. 设备型号: 35 | 4. bili_you 版本号: 36 | validations: 37 | required: true 38 | - type: textarea 39 | attributes: 40 | label: 补充说明 41 | description: 如果有任何补充,填写此字段 42 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.yaml: -------------------------------------------------------------------------------- 1 | name: "Feature request" 2 | description: 关于新功能的建议 3 | labels: 4 | - enhancement 5 | assignees: lucinhu 6 | body: 7 | - type: textarea 8 | attributes: 9 | label: 预期行为 10 | description: 你提出的功能会解决预期中的什么问题 11 | validations: 12 | required: true 13 | - type: textarea 14 | attributes: 15 | label: 功能描述 16 | description: 简洁而有力的描述你想象中的新功能 17 | validations: 18 | required: true 19 | - type: textarea 20 | attributes: 21 | label: 补充说明 22 | description: 如果有任何补充,填写此字段 -------------------------------------------------------------------------------- /.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 | .json/ 24 | 25 | # Flutter/Dart/Pub related 26 | **/doc/api/ 27 | **/ios/Flutter/.last_build_id 28 | .dart_tool/ 29 | .flutter-plugins 30 | .flutter-plugins-dependencies 31 | .packages 32 | .pub-cache/ 33 | .pub/ 34 | /build/ 35 | 36 | # Symbolication related 37 | app.*.symbols 38 | 39 | # Obfuscation related 40 | app.*.map.json 41 | 42 | # Android Studio will place build artifacts here 43 | /android/app/debug 44 | /android/app/profile 45 | /android/app/release 46 | -------------------------------------------------------------------------------- /.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. 5 | 6 | version: 7 | revision: 835b892d7fbe808be92d5eeee7b31c7bb7a3b1eb 8 | channel: master 9 | 10 | project_type: app 11 | 12 | # Tracks metadata for the flutter migrate command 13 | migration: 14 | platforms: 15 | - platform: root 16 | create_revision: 835b892d7fbe808be92d5eeee7b31c7bb7a3b1eb 17 | base_revision: 835b892d7fbe808be92d5eeee7b31c7bb7a3b1eb 18 | - platform: macos 19 | create_revision: 835b892d7fbe808be92d5eeee7b31c7bb7a3b1eb 20 | base_revision: 835b892d7fbe808be92d5eeee7b31c7bb7a3b1eb 21 | 22 | # User provided section 23 | 24 | # List of Local paths (relative to this file) that should be 25 | # ignored by the migrate tool. 26 | # 27 | # Files that are not part of the templates will be ignored by default. 28 | unmanaged_files: 29 | - 'lib/main.dart' 30 | - 'ios/Runner.xcodeproj/project.pbxproj' 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Bili You 2 | 3 |
4 | 5 | 6 | ![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/lucinhu/bili_you/main.yml?color=%238BC34A&style=for-the-badge) 7 | [![GitHub release (latest SemVer including pre-releases)](https://img.shields.io/github/v/release/lucinhu/bili_you?include_prereleases&style=for-the-badge)](https://github.com/lucinhu/bili_you/releases) 8 | [![GitHub all releases](https://img.shields.io/github/downloads/lucinhu/bili_you/total?color=%234CAF50&style=for-the-badge)](https://github.com/lucinhu/bili_you/releases) 9 | ![GitHub Repo stars](https://img.shields.io/github/stars/lucinhu/bili_you?color=%23FFC107&style=for-the-badge) 10 | 11 |
12 | 13 | 一个用flutter制作的第三方B站客户端. 14 | 15 | ## 功能实现 16 | 17 | - [x] 主页视频推荐 18 | - [x] 视频搜索 19 | - [x] 评论区 20 | - [x] 评论区楼中楼 21 | - [x] 评论图片及笔记 22 | - [x] 相关视频 23 | - [x] 热搜 24 | - [x] 视频播放 25 | - [x] 弹幕 26 | - [ ] 直播 27 | - [x] 动态(未完善) 28 | - [x] 用户投稿 29 | - [x] 番剧搜索 30 | - [x] 番剧播放 31 | 32 | ## 截图 33 | 34 |
35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 |
46 | 47 | ## 交流 48 | 49 | Discord:[https://discord.gg/tsFh7Hdb4s](https://discord.gg/tsFh7Hdb4s) 50 | 51 | ## 声明 52 | 53 | - 此项目是个人为了兴趣而开发, 仅供学习交流使用, 无任何商业用途. 54 | - 资源版权仍归原网站或其作者所有. 55 | - 所用API皆从官方网站收集, 不含任何非法及破解内容. 56 | 57 | ## 感谢 58 | 59 | - [bilibili-API-collect](https://github.com/SocialSisterYi/bilibili-API-collect): 哔哩哔哩API收集,感谢@SocialSisterYi及各位贡献者的维护! 60 | - [flutter_ns_danmaku](https://github.com/xiaoyaocz/flutter_ns_danmaku): @xiaoyaocz大佬制作的flutter弹幕插件,非常感谢! 61 | - [media_kit](https://github.com/alexmercerind/media_kit): 感谢@alexmercerind大佬制作的media_kit播放器! 62 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:flutter_lints/flutter.yaml 2 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | 9 | # Remember to never publicly share your keystore. 10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app 11 | key.properties 12 | **/*.keystore 13 | **/*.jks 14 | **/keystore.properties 15 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/lucinhu/bili_you/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.lucinhu.bili_you 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-hdpi-v26/ic_launcher_monochrome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/android/app/src/main/res/drawable-hdpi-v26/ic_launcher_monochrome.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-mdpi-v26/ic_launcher_monochrome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/android/app/src/main/res/drawable-mdpi-v26/ic_launcher_monochrome.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xhdpi-v26/ic_launcher_monochrome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/android/app/src/main/res/drawable-xhdpi-v26/ic_launcher_monochrome.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxhdpi-v26/ic_launcher_monochrome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/android/app/src/main/res/drawable-xxhdpi-v26/ic_launcher_monochrome.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxxhdpi-v26/ic_launcher_monochrome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/android/app/src/main/res/drawable-xxxhdpi-v26/ic_launcher_monochrome.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 16 | 20 | 21 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #ffffff 4 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 16 | 20 | 21 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.7.10' 3 | repositories { 4 | google() 5 | mavenCentral() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:7.2.0' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | } 12 | } 13 | 14 | allprojects { 15 | repositories { 16 | google() 17 | mavenCentral() 18 | } 19 | } 20 | 21 | rootProject.buildDir = '../build' 22 | subprojects { 23 | project.buildDir = "${rootProject.buildDir}/${project.name}" 24 | } 25 | subprojects { 26 | project.evaluationDependsOn(':app') 27 | } 28 | 29 | tasks.register("clean", Delete) { 30 | delete rootProject.buildDir 31 | } 32 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /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-7.5-all.zip 6 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties") 4 | def properties = new Properties() 5 | 6 | assert localPropertiesFile.exists() 7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } 8 | 9 | def flutterSdkPath = properties.getProperty("flutter.sdk") 10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" 12 | -------------------------------------------------------------------------------- /assets/icon/bili.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/assets/icon/bili.png -------------------------------------------------------------------------------- /assets/screenshot/v1.0.1/main_page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/assets/screenshot/v1.0.1/main_page.png -------------------------------------------------------------------------------- /assets/screenshot/v1.0.1/reply.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/assets/screenshot/v1.0.1/reply.png -------------------------------------------------------------------------------- /assets/screenshot/v1.0.1/reply_reply.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/assets/screenshot/v1.0.1/reply_reply.png -------------------------------------------------------------------------------- /assets/screenshot/v1.0.1/search_page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/assets/screenshot/v1.0.1/search_page.png -------------------------------------------------------------------------------- /assets/screenshot/v1.0.1/sms_login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/assets/screenshot/v1.0.1/sms_login.png -------------------------------------------------------------------------------- /assets/screenshot/v1.0.1/user_info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/assets/screenshot/v1.0.1/user_info.png -------------------------------------------------------------------------------- /assets/screenshot/v1.0.1/video_fullscreen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/assets/screenshot/v1.0.1/video_fullscreen.png -------------------------------------------------------------------------------- /assets/screenshot/v1.0.1/video_part.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/assets/screenshot/v1.0.1/video_part.png -------------------------------------------------------------------------------- /assets/screenshot/v1.0.1/video_play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/assets/screenshot/v1.0.1/video_play.png -------------------------------------------------------------------------------- /assets/screenshot/v1.0.1/video_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/assets/screenshot/v1.0.1/video_search.png -------------------------------------------------------------------------------- /assets/screenshot/v1.0.3/about_page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/assets/screenshot/v1.0.3/about_page.png -------------------------------------------------------------------------------- /assets/screenshot/v1.0.3/bangumi_play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/assets/screenshot/v1.0.3/bangumi_play.png -------------------------------------------------------------------------------- /assets/screenshot/v1.0.3/bangumi_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/assets/screenshot/v1.0.3/bangumi_search.png -------------------------------------------------------------------------------- /assets/screenshot/v1.0.3/main_page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/assets/screenshot/v1.0.3/main_page.png -------------------------------------------------------------------------------- /assets/screenshot/v1.0.3/reply.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/assets/screenshot/v1.0.3/reply.png -------------------------------------------------------------------------------- /assets/screenshot/v1.0.3/reply_reply.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/assets/screenshot/v1.0.3/reply_reply.png -------------------------------------------------------------------------------- /assets/screenshot/v1.0.3/search_page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/assets/screenshot/v1.0.3/search_page.png -------------------------------------------------------------------------------- /assets/screenshot/v1.0.3/user_info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/assets/screenshot/v1.0.3/user_info.png -------------------------------------------------------------------------------- /assets/screenshot/v1.0.3/video_play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/assets/screenshot/v1.0.3/video_play.png -------------------------------------------------------------------------------- /assets/screenshot/v1.0.3/video_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/assets/screenshot/v1.0.3/video_search.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/full_description.txt: -------------------------------------------------------------------------------- 1 | Features 2 | 3 | 17 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/about_page.png: -------------------------------------------------------------------------------- 1 | ../../../../../../assets/screenshot/v1.0.3/about_page.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/bangumi_play.png: -------------------------------------------------------------------------------- 1 | ../../../../../../assets/screenshot/v1.0.3/bangumi_play.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/bangumi_search.png: -------------------------------------------------------------------------------- 1 | ../../../../../../assets/screenshot/v1.0.3/bangumi_search.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/main_page.png: -------------------------------------------------------------------------------- 1 | ../../../../../../assets/screenshot/v1.0.3/main_page.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/reply.png: -------------------------------------------------------------------------------- 1 | ../../../../../../assets/screenshot/v1.0.3/reply.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/reply_reply.png: -------------------------------------------------------------------------------- 1 | ../../../../../../assets/screenshot/v1.0.3/reply_reply.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/search_page.png: -------------------------------------------------------------------------------- 1 | ../../../../../../assets/screenshot/v1.0.3/search_page.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/user_info.png: -------------------------------------------------------------------------------- 1 | ../../../../../../assets/screenshot/v1.0.3/user_info.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/video_play.png: -------------------------------------------------------------------------------- 1 | ../../../../../../assets/screenshot/v1.0.3/video_play.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/video_search.png: -------------------------------------------------------------------------------- 1 | ../../../../../../assets/screenshot/v1.0.3/video_search.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/short_description.txt: -------------------------------------------------------------------------------- 1 | A third-party Bilibili client 2 | -------------------------------------------------------------------------------- /fastlane/metadata/android/zh-CN/full_description.txt: -------------------------------------------------------------------------------- 1 | 功能 2 | 3 | 17 | 18 | -------------------------------------------------------------------------------- /fastlane/metadata/android/zh-CN/short_description.txt: -------------------------------------------------------------------------------- 1 | 第三方 Bilibili 客户端 2 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | 11.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | platform :ios, '13.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 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.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 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/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/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/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/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/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/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/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/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/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/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/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/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/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/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/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/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/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/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/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/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /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. -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /lib/common/api/bangumi_api.dart: -------------------------------------------------------------------------------- 1 | import 'package:bili_you/common/api/api_constants.dart'; 2 | import 'package:bili_you/common/models/local/bangumi/bangumi_info.dart'; 3 | import 'package:bili_you/common/models/local/bangumi/episode_info.dart'; 4 | import 'package:bili_you/common/models/network/bangumi/bangumi_info.dart'; 5 | import 'package:bili_you/common/utils/http_utils.dart'; 6 | 7 | class BangumiApi { 8 | //ssid(seaseon_id)或者epid都可以 9 | static Future _requestBangumiInfo( 10 | {int? ssid, int? epid}) async { 11 | var response = await HttpUtils().get( 12 | ApiConstants.bangumiInfo, 13 | queryParameters: {"season_id": ssid, "ep_id": epid}, 14 | ); 15 | return BangumiInfoResponse.fromJson(response.data); 16 | } 17 | 18 | //获取番剧信息 19 | static Future getBangumiInfo({int? ssid, int? epid}) async { 20 | if (ssid == null && epid == null) { 21 | throw "getBangumiInfo: ssid和epid不能同时为空"; 22 | } 23 | var response = await _requestBangumiInfo(ssid: ssid, epid: epid); 24 | if (response.code != 0) { 25 | throw "getBangumiInfo: code:${response.code}, message:${response.message}"; 26 | } 27 | List episodes = []; 28 | for (var i in response.result?.episodes ?? []) { 29 | episodes.add(EpisodeInfo( 30 | title: i.longTitle ?? "", bvid: i.bvid ?? "", cid: i.cid ?? 0)); 31 | } 32 | return BangumiInfo( 33 | title: response.result?.title ?? "", 34 | ssid: response.result?.seasonId ?? 0, 35 | episodes: episodes); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /lib/common/api/danmaku_api.dart: -------------------------------------------------------------------------------- 1 | import 'package:bili_you/common/api/api_constants.dart'; 2 | import 'package:bili_you/common/models/network/proto/danmaku/danmaku.pb.dart'; 3 | import 'package:bili_you/common/utils/http_utils.dart'; 4 | import 'package:dio/dio.dart'; 5 | import 'package:flutter/foundation.dart'; 6 | 7 | class DanmakuApi { 8 | static Future requestDanmaku( 9 | {int type = 1, required int cid, required int segmentIndex}) async { 10 | var ret = await compute((Map params) async { 11 | var response = await HttpUtils().get(ApiConstants.danmaku, 12 | queryParameters: params, 13 | options: Options(responseType: ResponseType.bytes)); 14 | return DmSegMobileReply.fromBuffer(response.data); 15 | }, {'type': type, 'oid': cid, 'segment_index': segmentIndex}); 16 | return ret; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /lib/common/api/github_api.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:bili_you/common/utils/http_utils.dart'; 4 | import 'package:dio/dio.dart'; 5 | 6 | import '../models/network/github/github_releases_item.dart'; 7 | import 'api_constants.dart'; 8 | 9 | class GithubApi { 10 | static Future requestLatestRelease() async { 11 | var response = await HttpUtils().get(ApiConstants.githubLatestRelease, 12 | options: Options(headers: { 13 | "Authorization": base64 14 | .decode( 15 | "dG9rZW4gZ2hwX05ia0huNm9aRlRJN0ZyTzFPb1R4MkF3U21oTFN0OTBhN1lQVQ==") 16 | .toString() 17 | })); 18 | return GithubReleasesItemModel.fromJson(response.data); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /lib/common/api/history_api.dart: -------------------------------------------------------------------------------- 1 | import 'package:bili_you/common/api/index.dart'; 2 | import 'package:bili_you/common/models/local/history/video_view_history_item.dart'; 3 | import 'package:bili_you/common/models/network/history/report_history.dart'; 4 | import 'package:bili_you/common/utils/http_utils.dart'; 5 | 6 | import '../utils/cookie_util.dart'; 7 | 8 | class HistoryApi { 9 | ///max是上一个历史记录的id,或者是oid 10 | ///viewAt是上一条记录的最后观看时间 11 | ///可以两者都设,也可任意设置一个 12 | static Future> getVideoViewHistory( 13 | {int? max, int? viewAt}) async { 14 | var response = 15 | await HttpUtils().get(ApiConstants.viewHistory, queryParameters: { 16 | 'type': 'archive', 17 | 'business': 'archive', 18 | 'ps': 20, 19 | if (max != null) 'max': max, 20 | if (viewAt != null) 'view_at': viewAt 21 | }); 22 | if (response.data['code'] != 0) { 23 | throw 'getVideoViewHistory:code:${response.data['code']},message:${response.data['message']}'; 24 | } 25 | List list = []; 26 | for (Map i in response.data['data']['list']) { 27 | list.add(VideoViewHistoryItem( 28 | oid: i['history']['oid'], 29 | title: i['title'], 30 | cover: i['cover'], 31 | bvid: i['history']['bvid'], 32 | cid: i['history']['cid'], 33 | epid: i['history']['epid'], 34 | page: i['history']['page'], 35 | authorName: i['author_name'], 36 | viewAt: i['view_at'], 37 | progress: i['progress'], 38 | duration: i['duration'], 39 | isFinished: i['is_finish'] != 0 || i['progress'] < 0)); 40 | } 41 | return list; 42 | } 43 | 44 | /// 汇报历史记录 45 | static Future reportVideoViewHistory( 46 | {required int aid, required int cid, int? progress}) async { 47 | var response = 48 | await HttpUtils().post(ApiConstants.reportHistory, queryParameters: { 49 | "aid": aid, 50 | "cid": cid, 51 | "progress": progress ?? 0, 52 | "platform": "android", 53 | "csrf": await CookieUtils.getCsrf() 54 | }); 55 | var json = ReportHistory.fromJson(response.data); 56 | if (json.code != 0) { 57 | throw 'report_history:code:${json.code},raw:"${response.data}"'; 58 | } 59 | return true; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /lib/common/api/index.dart: -------------------------------------------------------------------------------- 1 | export 'api_constants.dart'; 2 | export 'bangumi_api.dart'; 3 | export 'danmaku_api.dart'; 4 | export 'github_api.dart'; 5 | export 'home_api.dart'; 6 | export 'login_api.dart'; 7 | export 'related_video_api.dart'; 8 | export 'search_api.dart'; 9 | export 'user_space_api.dart'; 10 | export 'video_info_api.dart'; 11 | export 'video_play_api.dart'; 12 | export 'reply_api.dart'; 13 | -------------------------------------------------------------------------------- /lib/common/api/live_api.dart: -------------------------------------------------------------------------------- 1 | import 'package:bili_you/common/api/index.dart'; 2 | import 'package:bili_you/common/models/local/live/live_room_card_info.dart'; 3 | import 'package:bili_you/common/utils/http_utils.dart'; 4 | 5 | class LiveApi { 6 | static Future> getUserRecommendLive( 7 | {required int pageNum, required int pageSize}) async { 8 | var response = await HttpUtils().get(ApiConstants.userRecommendLive, 9 | queryParameters: { 10 | 'page': pageNum, 11 | 'page_size': pageSize, 12 | 'platform': 'web' 13 | }); 14 | if (response.data['code'] != 0) { 15 | throw "getUserRecommendLive: code:${response.data['code']}, message:${response.data['message']}"; 16 | } 17 | 18 | return [ 19 | for (Map i in response.data['data']['list']) 20 | LiveRoomCardInfo( 21 | roomId: i['roomid'] ?? 0, 22 | uid: i['uid'] ?? 0, 23 | title: i['title'] ?? '', 24 | uname: i['uname'] ?? '', 25 | cover: i['cover'] ?? '', 26 | userFace: i['face'] ?? '', 27 | parentId: i['parent_id'] ?? 0, 28 | parentName: i['parent_name'] ?? '', 29 | areaId: i['area_id'] ?? 0, 30 | areaName: i['area_name'] ?? '', 31 | watchNum: i['watched_show']?['num'] ?? 0) 32 | ]; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /lib/common/api/realtions_api.dart: -------------------------------------------------------------------------------- 1 | import 'package:bili_you/common/api/api_constants.dart'; 2 | import 'package:bili_you/common/models/network/user_relations/user_realtion.dart'; 3 | import 'package:bili_you/common/utils/http_utils.dart'; 4 | 5 | class RelationApi { 6 | static Future> getFollowingList( 7 | {required int? vmid, 8 | String? orderType, 9 | required int pn, 10 | required int ps}) async { 11 | return await _request( 12 | apiurl: ApiConstants.followings, vmid: vmid, pn: pn, ps: ps); 13 | } 14 | 15 | static Future> getFollowersList( 16 | {required int? vmid, 17 | String? orderType, 18 | required int pn, 19 | required int ps}) async { 20 | return await _request( 21 | apiurl: ApiConstants.followers, 22 | orderType: orderType, 23 | vmid: vmid, 24 | pn: pn, 25 | ps: ps); 26 | } 27 | 28 | static Future> _request( 29 | {required String apiurl, 30 | required int? vmid, 31 | String? orderType, 32 | required int pn, 33 | required int ps}) async { 34 | var response0 = await HttpUtils().get( 35 | apiurl, 36 | queryParameters: { 37 | "vmid": vmid, 38 | "order_type": orderType, 39 | "pn": pn, 40 | "ps": ps 41 | }, 42 | ); 43 | var response = response0.data; 44 | 45 | if (response["code"] != 0) { 46 | throw "getRelationList: code:${response["code"]}, message:${response["message"]}"; 47 | } 48 | 49 | List relations = []; 50 | for (var rel in response["data"]["list"]) { 51 | relations.add(UserRelation.fromJson(rel)); 52 | } 53 | return relations; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /lib/common/api/related_video_api.dart: -------------------------------------------------------------------------------- 1 | import 'package:bili_you/common/api/api_constants.dart'; 2 | import 'package:bili_you/common/models/local/video_tile/video_tile_info.dart'; 3 | import 'package:bili_you/common/models/network/related_video/related_video.dart'; 4 | import 'package:bili_you/common/utils/http_utils.dart'; 5 | 6 | class RelatedVideoApi { 7 | static Future _requestRelatedVideo( 8 | {required String bvid}) async { 9 | var response = await HttpUtils().get( 10 | ApiConstants.relatedVideo, 11 | queryParameters: {'bvid': bvid}, 12 | ); 13 | return RelatedVideoResponse.fromJson(response.data); 14 | } 15 | 16 | ///获取相关视频 17 | static Future> getRelatedVideo( 18 | {required String bvid}) async { 19 | List list = []; 20 | var response = await _requestRelatedVideo(bvid: bvid); 21 | if (response.code != 0) { 22 | throw "getRelatedVideo: code:${response.code}, message:${response.message}"; 23 | } 24 | if (response.data == null) { 25 | return list; 26 | } 27 | for (var i in response.data!) { 28 | list.add(VideoTileInfo( 29 | coverUrl: i.pic ?? "", 30 | bvid: i.bvid ?? "", 31 | cid: i.cid ?? 0, 32 | title: i.title ?? "", 33 | upName: i.owner?.name ?? "", 34 | timeLength: i.duration ?? 0, 35 | playNum: i.stat?.view ?? 0, 36 | pubDate: i.pubdate ?? 0)); 37 | } 38 | return list; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /lib/common/api/user_space_api.dart: -------------------------------------------------------------------------------- 1 | import 'package:bili_you/common/api/api_constants.dart'; 2 | import 'package:bili_you/common/api/wbi.dart'; 3 | import 'package:bili_you/common/models/local/user_space/user_video_search.dart'; 4 | import 'package:bili_you/common/models/network/user_space/user_video_search.dart'; 5 | import 'package:bili_you/common/utils/http_utils.dart'; 6 | 7 | class UserSpaceApi { 8 | static Future _requestUserVideoSearch({ 9 | required int mid, 10 | required int pageNum, 11 | String? keyword, 12 | }) async { 13 | //TODO order排序方式,tid分区筛选,keyword关键词筛选,ps每页项数, 14 | var response = await HttpUtils().get(ApiConstants.userVideoSearch, 15 | queryParameters: await WbiSign.encodeParams({ 16 | "mid": mid, 17 | "pn": pageNum, 18 | "ps": 30, 19 | "keyword": keyword ?? "", 20 | "order": "pubdate", 21 | "tid": 0, 22 | "platform": "web", 23 | })); 24 | return UserVideoSearchResponse.fromJson(response.data); 25 | } 26 | 27 | static Future getUserVideoSearch({ 28 | required int mid, 29 | required int pageNum, 30 | String? keyword, 31 | }) async { 32 | var response = await _requestUserVideoSearch( 33 | mid: mid, pageNum: pageNum, keyword: keyword); 34 | if (response.code != 0) { 35 | throw "getUserVideoSearch: code:${response.code}, message:${response.message}"; 36 | } 37 | 38 | if (response.data == null || 39 | response.data?.list == null || 40 | response.data?.list?.vlist == null) { 41 | return UserVideoSearch.zero; 42 | } 43 | List videos = []; 44 | for (var i in response.data!.list!.vlist!) { 45 | videos.add(UserVideoItem( 46 | author: i.author ?? "", 47 | title: i.title ?? "", 48 | mid: i.mid ?? 0, 49 | bvid: i.bvid ?? "", 50 | coverUrl: i.pic ?? "", 51 | danmakuCount: i.videoReview ?? 0, 52 | description: i.description ?? "", 53 | isUnionVideo: i.isUnionVideo == 1, 54 | playCount: i.play ?? 0, 55 | duration: i.length ?? "--:--", 56 | pubDate: i.created ?? 0, 57 | replyCount: i.comment ?? 0)); 58 | } 59 | return UserVideoSearch(videos: videos); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /lib/common/index.dart: -------------------------------------------------------------------------------- 1 | export 'api/index.dart'; 2 | export 'models/index.dart'; 3 | export 'utils/index.dart'; 4 | export 'values/index.dart'; 5 | export 'widget/index.dart'; 6 | 7 | -------------------------------------------------------------------------------- /lib/common/models/index.dart: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /lib/common/models/local/bangumi/bangumi_info.dart: -------------------------------------------------------------------------------- 1 | import 'package:bili_you/common/models/local/bangumi/episode_info.dart'; 2 | 3 | class BangumiInfo { 4 | BangumiInfo( 5 | {required this.title, required this.ssid, required this.episodes}); 6 | static BangumiInfo get zero => BangumiInfo(title: "", ssid: 0, episodes: []); 7 | String title; 8 | int ssid; 9 | List episodes; 10 | } 11 | -------------------------------------------------------------------------------- /lib/common/models/local/bangumi/episode_info.dart: -------------------------------------------------------------------------------- 1 | class EpisodeInfo { 2 | EpisodeInfo({required this.title, required this.bvid, required this.cid}); 3 | static EpisodeInfo get zero => EpisodeInfo(title: "", bvid: "", cid: 0); 4 | String title; 5 | String bvid; 6 | int cid; 7 | } 8 | -------------------------------------------------------------------------------- /lib/common/models/local/dynamic/dynamic_author.dart: -------------------------------------------------------------------------------- 1 | import '../reply/official_verify.dart'; 2 | import '../reply/vip.dart'; 3 | 4 | class DynamicAuthor { 5 | DynamicAuthor( 6 | {required this.mid, 7 | required this.name, 8 | required this.avatarUrl, 9 | required this.officialVerify, 10 | required this.vip, 11 | required this.pubTime, 12 | required this.pubAction, 13 | this.hasUpdate = false}); 14 | static DynamicAuthor get zero => DynamicAuthor( 15 | mid: 0, 16 | name: "", 17 | avatarUrl: "", 18 | officialVerify: OfficialVerify.zero, 19 | vip: Vip.zero, 20 | pubTime: "", 21 | pubAction: ""); 22 | int mid; 23 | String name; 24 | String avatarUrl; 25 | OfficialVerify officialVerify; 26 | Vip vip; 27 | String pubTime; 28 | String pubAction; 29 | bool hasUpdate; 30 | } 31 | -------------------------------------------------------------------------------- /lib/common/models/local/dynamic/dynamic_item.dart: -------------------------------------------------------------------------------- 1 | import 'package:bili_you/common/models/local/reply/reply_item.dart'; 2 | 3 | import 'dynamic_author.dart'; 4 | import 'dynamic_content.dart'; 5 | import 'dynamic_stat.dart'; 6 | 7 | class DynamicItem { 8 | DynamicItem( 9 | {required this.replyId, 10 | required this.replyType, 11 | required this.author, 12 | required this.type, 13 | required this.content, 14 | required this.stat}); 15 | static DynamicItem get zero => DynamicItem( 16 | replyId: '', 17 | replyType: ReplyType.unkown, 18 | author: DynamicAuthor.zero, 19 | type: DynamicItemType.unkown, 20 | content: WordDynamicContent.zero, 21 | stat: DynamicStat.zero); 22 | 23 | ///评论区id 24 | String replyId; 25 | ReplyType replyType; 26 | DynamicAuthor author; 27 | DynamicItemType type; 28 | DynamicContent content; 29 | DynamicStat stat; 30 | } 31 | 32 | enum DynamicItemType { 33 | //未知 34 | unkown, 35 | 36 | ///消息 37 | //DYNAMIC_TYPE_WORD 38 | word, 39 | 40 | ///文章 41 | //DYNAMIC_TYPE_ARTICLE 42 | article, 43 | 44 | ///视频投稿 45 | //DYNAMIC_TYPE_AV 46 | av, 47 | 48 | ///抽签or互动? 49 | //DYNAMIC_TYPE_DRAW 50 | draw, 51 | 52 | ///直播推荐 53 | //DYNAMIC_TYPE_LIVE_RCMD 54 | liveRecommend, 55 | 56 | ///转发动态 57 | //DYNAMIC_TYPE_FORWARD 58 | forward, 59 | } 60 | 61 | extension DynamicItemTypeCode on DynamicItemType { 62 | static const List _codeList = [ 63 | "UNKOWN", 64 | "DYNAMIC_TYPE_WORD", 65 | "DYNAMIC_TYPE_ARTICLE", 66 | "DYNAMIC_TYPE_AV", 67 | "DYNAMIC_TYPE_DRAW", 68 | "DYNAMIC_TYPE_LIVE_RCMD", 69 | "DYNAMIC_TYPE_FORWARD", 70 | ]; 71 | static DynamicItemType fromCode(String code) { 72 | int index = _codeList.indexOf(code); 73 | if (index == -1) { 74 | return DynamicItemType.unkown; 75 | } 76 | return DynamicItemType.values[index]; 77 | } 78 | 79 | String get code => _codeList[index]; 80 | } 81 | -------------------------------------------------------------------------------- /lib/common/models/local/dynamic/dynamic_stat.dart: -------------------------------------------------------------------------------- 1 | class DynamicStat { 2 | DynamicStat( 3 | {required this.shareCount, 4 | required this.replyCount, 5 | required this.likeCount}); 6 | static DynamicStat get zero => 7 | DynamicStat(shareCount: 0, replyCount: 0, likeCount: 0); 8 | int shareCount; 9 | int replyCount; 10 | int likeCount; 11 | } 12 | -------------------------------------------------------------------------------- /lib/common/models/local/history/video_view_history_item.dart: -------------------------------------------------------------------------------- 1 | class VideoViewHistoryItem { 2 | VideoViewHistoryItem( 3 | {required this.oid, 4 | required this.title, 5 | required this.cover, 6 | required this.bvid, 7 | required this.cid, 8 | required this.epid, 9 | required this.page, 10 | required this.authorName, 11 | required this.viewAt, 12 | required this.progress, 13 | required this.duration, 14 | required this.isFinished}); 15 | static VideoViewHistoryItem get zero => VideoViewHistoryItem( 16 | oid: 0, 17 | title: '', 18 | cover: '', 19 | bvid: '', 20 | cid: 0, 21 | epid: 0, 22 | page: 1, 23 | authorName: '', 24 | viewAt: 0, 25 | progress: 0, 26 | duration: 0, 27 | isFinished: false); 28 | int oid; 29 | String title; 30 | String cover; 31 | String bvid; 32 | int cid; 33 | int epid; 34 | int page; 35 | String authorName; 36 | int viewAt; 37 | int progress; 38 | int duration; 39 | bool isFinished; 40 | } 41 | -------------------------------------------------------------------------------- /lib/common/models/local/home/recommend_item_info.dart: -------------------------------------------------------------------------------- 1 | class RecommendVideoItemInfo { 2 | RecommendVideoItemInfo( 3 | {required this.coverUrl, 4 | required this.danmakuNum, 5 | required this.playNum, 6 | required this.timeLength, 7 | required this.title, 8 | required this.upName, 9 | required this.bvid, 10 | required this.cid}); 11 | static RecommendVideoItemInfo get zero => RecommendVideoItemInfo( 12 | coverUrl: "", 13 | danmakuNum: 0, 14 | playNum: 0, 15 | timeLength: 0, 16 | title: "", 17 | upName: "", 18 | bvid: "", 19 | cid: 0); 20 | String coverUrl; 21 | int danmakuNum; 22 | int playNum; 23 | int timeLength; 24 | String title; 25 | String upName; 26 | String bvid; 27 | int cid; 28 | } 29 | -------------------------------------------------------------------------------- /lib/common/models/local/live/live_room_card_info.dart: -------------------------------------------------------------------------------- 1 | class LiveRoomCardInfo { 2 | LiveRoomCardInfo( 3 | {required this.roomId, 4 | required this.uid, 5 | required this.title, 6 | required this.uname, 7 | required this.cover, 8 | required this.userFace, 9 | required this.parentId, 10 | required this.parentName, 11 | required this.areaId, 12 | required this.areaName, 13 | required this.watchNum}); 14 | int roomId; 15 | int uid; 16 | String title; 17 | String uname; 18 | String cover; 19 | String userFace; 20 | int parentId; 21 | String parentName; 22 | int areaId; 23 | String areaName; 24 | int watchNum; 25 | } 26 | -------------------------------------------------------------------------------- /lib/common/models/local/login/level_info.dart: -------------------------------------------------------------------------------- 1 | class LevelInfo { 2 | LevelInfo( 3 | {required this.currentLevel, 4 | required this.currentExp, 5 | required this.currentMin, 6 | required this.nextExp}); 7 | static LevelInfo get zero => 8 | LevelInfo(currentLevel: 0, currentExp: 0, currentMin: 0, nextExp: 0); 9 | 10 | ///当前等级 11 | int currentLevel; 12 | 13 | ///升到当前等级所需要的全部经验值 14 | int currentMin; 15 | 16 | ///当前经验值 17 | int currentExp; 18 | 19 | ///升到下一级所需要的全部经验值 20 | int nextExp; 21 | } 22 | -------------------------------------------------------------------------------- /lib/common/models/local/login/login_qrcode_info.dart: -------------------------------------------------------------------------------- 1 | class LoginQRcodeInfo { 2 | ///以该url作为内容生成二维码 3 | String url = ''; 4 | 5 | ///用来作为检查扫码登陆状态的函数参数 6 | String qrcodeKey = ''; 7 | } 8 | -------------------------------------------------------------------------------- /lib/common/models/local/login/login_qrcode_stat.dart: -------------------------------------------------------------------------------- 1 | enum LoginQrcodeStat { 2 | ///登录成功 3 | loginSuccess, 4 | 5 | ///未扫码 6 | hasNotScan, 7 | 8 | ///扫码了但没确认 9 | hasNotAccept, 10 | 11 | ///二维码失效 12 | qrcodeInvalid, 13 | 14 | ///其他状态 15 | other, 16 | } 17 | 18 | extension LoginQrcodeStatExtension on LoginQrcodeStat { 19 | static const map = { 20 | 0: '登录成功', 21 | 86101: '未扫码', 22 | 86090: '已扫码但未确认', 23 | 86038: "二维码已失效", 24 | -1: "登录失败" 25 | }; 26 | int get code => map.keys.toList()[index]; 27 | String get message => map.values.toList()[index]; 28 | static LoginQrcodeStat fromCode(int code) { 29 | var index = map.keys.toList().indexOf(code); 30 | if (index == -1) { 31 | //找不到对应的就返回 其他状态 32 | return LoginQrcodeStat.other; 33 | } else { 34 | return LoginQrcodeStat.values[index]; 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /lib/common/models/local/login/login_user_info.dart: -------------------------------------------------------------------------------- 1 | import 'package:bili_you/common/models/local/reply/official_verify.dart'; 2 | import 'package:bili_you/common/models/local/reply/vip.dart'; 3 | 4 | import 'level_info.dart'; 5 | 6 | ///TODO 修改支持Hive 7 | class LoginUserInfo { 8 | LoginUserInfo( 9 | {required this.mid, 10 | required this.name, 11 | required this.avatarUrl, 12 | required this.levelInfo, 13 | required this.officialVerify, 14 | required this.vip, 15 | required this.isLogin}); 16 | static LoginUserInfo get zero => LoginUserInfo( 17 | mid: 0, 18 | name: "", 19 | avatarUrl: "", 20 | levelInfo: LevelInfo.zero, 21 | officialVerify: OfficialVerify.zero, 22 | vip: Vip.zero, 23 | isLogin: false); 24 | int mid; 25 | String name; 26 | String avatarUrl; 27 | LevelInfo levelInfo; 28 | OfficialVerify officialVerify; 29 | Vip vip; 30 | bool isLogin; 31 | } 32 | -------------------------------------------------------------------------------- /lib/common/models/local/login/login_user_stat.dart: -------------------------------------------------------------------------------- 1 | class LoginUserStat { 2 | LoginUserStat( 3 | {required this.followerCount, 4 | required this.followingCount, 5 | required this.dynamicCount}); 6 | static LoginUserStat get zero => 7 | LoginUserStat(followerCount: 0, followingCount: 0, dynamicCount: 0); 8 | int followerCount; 9 | int followingCount; 10 | int dynamicCount; 11 | } 12 | -------------------------------------------------------------------------------- /lib/common/models/local/reply/add_reply_result.dart: -------------------------------------------------------------------------------- 1 | import 'package:bili_you/common/models/local/reply/reply_item.dart'; 2 | 3 | class AddReplyResult { 4 | AddReplyResult( 5 | {required this.isSuccess, required this.error, required this.replyItem}); 6 | bool isSuccess; 7 | String error; 8 | ReplyItem replyItem; 9 | } 10 | -------------------------------------------------------------------------------- /lib/common/models/local/reply/official_verify.dart: -------------------------------------------------------------------------------- 1 | ///官方认证信息 2 | class OfficialVerify { 3 | OfficialVerify({required this.type, required this.description}); 4 | static OfficialVerify get zero => 5 | OfficialVerify(type: OfficialVerifyType.none, description: ""); 6 | OfficialVerifyType type; 7 | String description; 8 | } 9 | 10 | ///官方认证类型 11 | enum OfficialVerifyType { 12 | none, 13 | 14 | person, 15 | 16 | organization 17 | } 18 | 19 | extension OfficialVerifyTypeCode on OfficialVerifyType { 20 | static OfficialVerifyType fromCode(int code) { 21 | switch (code) { 22 | case -1: 23 | return OfficialVerifyType.none; 24 | case 0: 25 | return OfficialVerifyType.person; 26 | case 1: 27 | return OfficialVerifyType.organization; 28 | default: 29 | return OfficialVerifyType.none; 30 | } 31 | } 32 | 33 | get code => [-1, 0, 1][index]; 34 | } 35 | -------------------------------------------------------------------------------- /lib/common/models/local/reply/reply_add_like_result.dart: -------------------------------------------------------------------------------- 1 | class ReplyAddLikeResult { 2 | ReplyAddLikeResult({required this.isSuccess, required this.error}); 3 | static ReplyAddLikeResult get zero => 4 | ReplyAddLikeResult(isSuccess: false, error: ''); 5 | bool isSuccess; 6 | String error; 7 | } 8 | -------------------------------------------------------------------------------- /lib/common/models/local/reply/reply_content.dart: -------------------------------------------------------------------------------- 1 | import 'package:bili_you/common/models/local/reply/reply_member.dart'; 2 | 3 | class ReplyContent { 4 | ReplyContent( 5 | {required this.message, 6 | required this.atMembers, 7 | required this.emotes, 8 | required this.jumpUrls, 9 | required this.pictures}); 10 | static get zero { 11 | return ReplyContent( 12 | message: "", atMembers: [], emotes: [], jumpUrls: [], pictures: []); 13 | } 14 | 15 | ///评论 16 | String message; 17 | 18 | ///@到的用户 19 | List atMembers; 20 | 21 | ///需要渲染的表情转义和所表示的表情信息 22 | List emotes; 23 | 24 | ///跳转链接 25 | List jumpUrls; 26 | 27 | ///图片 28 | List pictures; 29 | } 30 | 31 | class ReplyJumpUrl { 32 | ReplyJumpUrl({required this.url, required this.title}); 33 | static ReplyJumpUrl get zero => ReplyJumpUrl(url: '', title: ''); 34 | String url; 35 | String title; 36 | } 37 | 38 | class ReplyPicture { 39 | ReplyPicture( 40 | {required this.url, 41 | required this.width, 42 | required this.height, 43 | required this.size}); 44 | static ReplyPicture get zero => 45 | ReplyPicture(url: '', width: 0, height: 0, size: 1); 46 | String url; 47 | int width; 48 | int height; 49 | double size; 50 | } 51 | 52 | class Emote { 53 | Emote({required this.text, required this.url, required this.size}); 54 | static Emote get zero => Emote(text: "", url: "", size: EmoteSize.small); 55 | 56 | ///表情转义符 57 | String text; 58 | 59 | ///表情图片url 60 | String url; 61 | 62 | ///表情大小 63 | EmoteSize size; 64 | } 65 | 66 | enum EmoteSize { 67 | ///1 68 | small, 69 | 70 | ///2 71 | big, 72 | } 73 | 74 | extension EmoteSizeCode on EmoteSize { 75 | int get code => [1, 2][index]; 76 | } 77 | -------------------------------------------------------------------------------- /lib/common/models/local/reply/reply_info.dart: -------------------------------------------------------------------------------- 1 | import 'package:bili_you/common/models/local/reply/reply_item.dart'; 2 | 3 | class ReplyInfo { 4 | ReplyInfo( 5 | {required this.replies, 6 | required this.topReplies, 7 | required this.upperMid, 8 | required this.replyCount}); 9 | static ReplyInfo get zero => 10 | ReplyInfo(replies: [], topReplies: [], upperMid: 0, replyCount: 0); 11 | 12 | ///评论 13 | List replies; 14 | 15 | ///置顶评论 16 | List topReplies; 17 | 18 | ///up主的mid 19 | int upperMid; 20 | 21 | ///评论总数 22 | int replyCount; 23 | } 24 | -------------------------------------------------------------------------------- /lib/common/models/local/reply/reply_member.dart: -------------------------------------------------------------------------------- 1 | import 'official_verify.dart'; 2 | import 'vip.dart'; 3 | 4 | class ReplyMember { 5 | ReplyMember( 6 | {required this.mid, 7 | required this.name, 8 | required this.gender, 9 | required this.avatarUrl, 10 | required this.level, 11 | required this.officialVerify, 12 | required this.vip}); 13 | static ReplyMember get zero => ReplyMember( 14 | mid: 0, 15 | name: "", 16 | gender: Gender.secret, 17 | avatarUrl: "", 18 | level: 0, 19 | officialVerify: OfficialVerify.zero, 20 | vip: Vip.zero); 21 | int mid; 22 | String name; 23 | String avatarUrl; 24 | int level; 25 | OfficialVerify officialVerify; 26 | Vip vip; 27 | 28 | ///性别 29 | Gender gender; 30 | } 31 | 32 | enum Gender { man, woman, secret } 33 | 34 | extension GenderText on Gender { 35 | static Gender fromText(String text) { 36 | switch (text) { 37 | case '男': 38 | return Gender.man; 39 | case '女': 40 | return Gender.woman; 41 | case '保密': 42 | return Gender.secret; 43 | default: 44 | return Gender.secret; 45 | } 46 | } 47 | 48 | get text => ['男', '女', '保密'][index]; 49 | } 50 | -------------------------------------------------------------------------------- /lib/common/models/local/reply/reply_reply_info.dart: -------------------------------------------------------------------------------- 1 | import 'package:bili_you/common/models/local/reply/reply_item.dart'; 2 | 3 | class ReplyReplyInfo { 4 | ReplyReplyInfo( 5 | {required this.replies, 6 | required this.rootReply, 7 | required this.upperMid, 8 | required this.replyCount}); 9 | static ReplyReplyInfo get zero => ReplyReplyInfo( 10 | replies: [], rootReply: ReplyItem.zero, upperMid: 0, replyCount: 0); 11 | List replies; 12 | ReplyItem rootReply; 13 | int upperMid; 14 | 15 | ///评论总数 16 | int replyCount; 17 | } 18 | -------------------------------------------------------------------------------- /lib/common/models/local/reply/vip.dart: -------------------------------------------------------------------------------- 1 | ///Vip信息 2 | class Vip { 3 | Vip({required this.isVip, required this.type}); 4 | static Vip get zero => Vip(isVip: false, type: VipType.none); 5 | bool isVip; 6 | VipType type; 7 | } 8 | 9 | ///Vip类型 10 | enum VipType { none, month, year } 11 | 12 | extension VipTypeCode on VipType { 13 | static VipType fromCode(int code) { 14 | return VipType.values[code]; 15 | } 16 | 17 | int get code => [0, 1, 2][index]; 18 | } 19 | -------------------------------------------------------------------------------- /lib/common/models/local/search/default_search_word.dart: -------------------------------------------------------------------------------- 1 | class DefaultSearchWord { 2 | DefaultSearchWord({required this.showName, required this.name}); 3 | static DefaultSearchWord get zero => 4 | DefaultSearchWord(showName: "", name: ""); 5 | String showName; 6 | String name; 7 | } 8 | -------------------------------------------------------------------------------- /lib/common/models/local/search/hot_word_item.dart: -------------------------------------------------------------------------------- 1 | class HotWordItem { 2 | HotWordItem({required this.keyWord, required this.showWord}); 3 | static HotWordItem get zero => HotWordItem(keyWord: "", showWord: ""); 4 | String keyWord; 5 | String showWord; 6 | } 7 | -------------------------------------------------------------------------------- /lib/common/models/local/search/search_bangumi_item.dart: -------------------------------------------------------------------------------- 1 | class SearchBangumiItem { 2 | SearchBangumiItem( 3 | {required this.coverUrl, 4 | required this.title, 5 | required this.describe, 6 | required this.score, 7 | required this.ssid}); 8 | static SearchBangumiItem get zero => SearchBangumiItem( 9 | coverUrl: "", title: "", describe: "", score: 0, ssid: 0); 10 | String coverUrl; 11 | String title; 12 | String describe; 13 | double score; 14 | int ssid; 15 | } 16 | -------------------------------------------------------------------------------- /lib/common/models/local/search/search_suggest_item.dart: -------------------------------------------------------------------------------- 1 | class SearchSuggestItem { 2 | SearchSuggestItem({required this.showWord, required this.realWord}); 3 | static SearchSuggestItem get zero => 4 | SearchSuggestItem(showWord: "", realWord: ""); 5 | String showWord; 6 | String realWord; 7 | } 8 | -------------------------------------------------------------------------------- /lib/common/models/local/search/search_user_item.dart: -------------------------------------------------------------------------------- 1 | import 'package:bili_you/common/models/local/reply/official_verify.dart'; 2 | import 'package:bili_you/common/models/local/reply/reply_member.dart'; 3 | 4 | class SearchUserItem { 5 | SearchUserItem( 6 | {required this.mid, 7 | required this.name, 8 | required this.face, 9 | required this.sign, 10 | required this.fansCount, 11 | required this.videoCount, 12 | required this.level, 13 | required this.gender, 14 | required this.isUpper, 15 | required this.isLive, 16 | required this.roomId, 17 | required this.officialVerify}); 18 | static SearchUserItem get zero => SearchUserItem( 19 | mid: 0, 20 | name: '', 21 | face: '', 22 | sign: '', 23 | fansCount: 0, 24 | videoCount: 0, 25 | level: 0, 26 | gender: Gender.secret, 27 | isUpper: false, 28 | isLive: false, 29 | roomId: 0, 30 | officialVerify: OfficialVerify.zero); 31 | int mid; 32 | String name; 33 | String face; 34 | String sign; 35 | int fansCount; 36 | int videoCount; 37 | int level; 38 | Gender gender; 39 | bool isUpper; 40 | bool isLive; 41 | int roomId; 42 | OfficialVerify officialVerify; 43 | } 44 | -------------------------------------------------------------------------------- /lib/common/models/local/search/search_video_item.dart: -------------------------------------------------------------------------------- 1 | class SearchVideoItem { 2 | SearchVideoItem( 3 | {required this.coverUrl, 4 | required this.bvid, 5 | required this.title, 6 | required this.upName, 7 | required this.timeLength, 8 | required this.playNum, 9 | required this.pubDate}); 10 | static SearchVideoItem get zero => SearchVideoItem( 11 | coverUrl: "", 12 | bvid: "", 13 | title: "", 14 | upName: "", 15 | timeLength: 0, 16 | playNum: 0, 17 | pubDate: 0); 18 | String coverUrl; 19 | String bvid; 20 | String title; 21 | String upName; 22 | int timeLength; 23 | int playNum; 24 | int pubDate; 25 | } 26 | -------------------------------------------------------------------------------- /lib/common/models/local/user_space/user_video_search.dart: -------------------------------------------------------------------------------- 1 | class UserVideoSearch { 2 | UserVideoSearch({required this.videos}); 3 | static UserVideoSearch get zero => UserVideoSearch(videos: []); 4 | List videos; 5 | 6 | ///TODO 该用户所有投稿视频分区列表 7 | } 8 | 9 | class UserVideoItem { 10 | UserVideoItem({ 11 | required this.author, 12 | required this.title, 13 | required this.mid, 14 | required this.bvid, 15 | required this.coverUrl, 16 | required this.danmakuCount, 17 | required this.description, 18 | required this.isUnionVideo, 19 | required this.playCount, 20 | required this.duration, 21 | required this.pubDate, 22 | required this.replyCount, 23 | // required this.typeid 24 | }); 25 | static UserVideoItem get zero => UserVideoItem( 26 | author: "", 27 | title: "", 28 | mid: 0, 29 | bvid: "", 30 | coverUrl: "", 31 | danmakuCount: 0, 32 | description: "", 33 | isUnionVideo: false, 34 | playCount: 0, 35 | duration: "--:--", 36 | pubDate: 0, 37 | replyCount: 0, 38 | // typeid: 0 39 | ); 40 | 41 | ///评论数 42 | int replyCount; 43 | 44 | // ///分区id 45 | // int typeid; 46 | 47 | ///播放量 48 | int playCount; 49 | 50 | ///封面 51 | String coverUrl; 52 | 53 | ///简介 54 | String description; 55 | 56 | ///标题 57 | String title; 58 | 59 | ///up主名字,如果是合作视频的话是第一个人的名字 60 | String author; 61 | 62 | ///up主mid 63 | int mid; 64 | 65 | ///投稿时间戳 66 | int pubDate; 67 | 68 | ///弹幕量 69 | int danmakuCount; 70 | 71 | ///视频时长,分:秒的形式 72 | String duration; 73 | 74 | ///bvid 75 | String bvid; 76 | 77 | ///是否是合作视频 78 | bool isUnionVideo; 79 | } 80 | -------------------------------------------------------------------------------- /lib/common/models/local/video/audio_play_item.dart: -------------------------------------------------------------------------------- 1 | class AudioPlayItem { 2 | AudioPlayItem({ 3 | required this.urls, 4 | required this.quality, 5 | required this.bandWidth, 6 | required this.codecs, 7 | // required this.mimeType, 8 | // required this.segmentBase, 9 | // required this.timeLength 10 | }); 11 | static AudioPlayItem get zero => AudioPlayItem( 12 | urls: [], quality: AudioQuality.unknown, bandWidth: 0, codecs: "" 13 | // , 14 | // mimeType: '', 15 | // segmentBase: SegmentBase(initialization: '', indexRange: ''), 16 | // timeLength: 0 17 | ); 18 | List urls; 19 | AudioQuality quality; 20 | int bandWidth; 21 | String codecs; 22 | // String mimeType; 23 | // SegmentBase segmentBase; 24 | // //时长,秒为单位 25 | // int timeLength; 26 | } 27 | 28 | enum AudioQuality { 29 | unknown, 30 | audio64k, 31 | audio132k, 32 | audio192k, 33 | dolby, 34 | hiRes, 35 | } 36 | 37 | extension AudioQualityCode on AudioQuality { 38 | static final List _codeList = [ 39 | -1, 40 | 30216, 41 | 30232, 42 | 30280, 43 | 30250, 44 | 30251, 45 | ]; 46 | 47 | static AudioQuality fromCode(int code) { 48 | var index = _codeList.indexOf(code); 49 | if (index == -1) { 50 | return AudioQuality.unknown; 51 | } 52 | return AudioQuality.values[index]; 53 | } 54 | 55 | get code => _codeList[index]; 56 | } 57 | 58 | extension AudioQualityDescription on AudioQuality { 59 | get description => ['未知', '64K', '132K', '192K', '杜比全景声', 'Hi-Res无损'][index]; 60 | } 61 | -------------------------------------------------------------------------------- /lib/common/models/local/video/click_add_coin_result.dart: -------------------------------------------------------------------------------- 1 | class ClickAddCoinResult { 2 | ClickAddCoinResult({required this.isSuccess, required this.error}); 3 | static ClickAddCoinResult get zero => 4 | ClickAddCoinResult(isSuccess: false, error: ''); 5 | bool isSuccess; 6 | String error; 7 | } 8 | -------------------------------------------------------------------------------- /lib/common/models/local/video/click_add_share_result.dart: -------------------------------------------------------------------------------- 1 | class ClickAddShareResult { 2 | ClickAddShareResult( 3 | {required this.isSuccess, 4 | required this.error, 5 | required this.currentShareNum}); 6 | static ClickAddShareResult get zero => 7 | ClickAddShareResult(isSuccess: false, error: '', currentShareNum: 0); 8 | bool isSuccess; 9 | String error; 10 | int currentShareNum; 11 | } 12 | -------------------------------------------------------------------------------- /lib/common/models/local/video/click_like_result.dart: -------------------------------------------------------------------------------- 1 | ///点赞后的结果 2 | class ClickLikeResult { 3 | ClickLikeResult( 4 | {required this.isSuccess, required this.error, required this.haslike}); 5 | 6 | ///操作是否成功 7 | bool isSuccess; 8 | 9 | ///错误信息 10 | String error; 11 | 12 | ///成功后的状态 13 | ///true为已点赞 14 | ///false则是未点赞 15 | bool haslike; 16 | } 17 | -------------------------------------------------------------------------------- /lib/common/models/local/video/part_info.dart: -------------------------------------------------------------------------------- 1 | class PartInfo { 2 | PartInfo({required this.title, required this.cid}); 3 | static PartInfo get zero => PartInfo(title: "", cid: 0); 4 | String title; 5 | int cid; 6 | } 7 | -------------------------------------------------------------------------------- /lib/common/models/local/video/video_info.dart: -------------------------------------------------------------------------------- 1 | import 'package:bili_you/common/models/local/video/part_info.dart'; 2 | 3 | class VideoInfo { 4 | VideoInfo( 5 | {required this.title, 6 | required this.describe, 7 | required this.bvid, 8 | required this.cid, 9 | required this.copyRight, 10 | required this.pubDate, 11 | required this.playNum, 12 | required this.danmaukuNum, 13 | required this.coinNum, 14 | required this.favariteNum, 15 | required this.likeNum, 16 | required this.shareNum, 17 | required this.ownerFace, 18 | required this.ownerMid, 19 | required this.ownerName, 20 | required this.parts, 21 | required this.hasLike, 22 | required this.hasAddCoin, 23 | required this.hasFavourite}); 24 | static VideoInfo get zero => VideoInfo( 25 | title: "", 26 | describe: "", 27 | bvid: "", 28 | cid: 0, 29 | copyRight: "", 30 | pubDate: 0, 31 | playNum: 0, 32 | danmaukuNum: 0, 33 | coinNum: 0, 34 | favariteNum: 0, 35 | likeNum: 0, 36 | shareNum: 0, 37 | ownerFace: "", 38 | ownerMid: 0, 39 | ownerName: "", 40 | parts: [], 41 | hasLike: false, 42 | hasAddCoin: false, 43 | hasFavourite: false); 44 | String title; 45 | String describe; 46 | String bvid; 47 | int cid; 48 | String copyRight; 49 | int playNum; 50 | int danmaukuNum; 51 | int pubDate; 52 | int likeNum; 53 | int coinNum; 54 | int favariteNum; 55 | int shareNum; 56 | String ownerFace; 57 | String ownerName; 58 | int ownerMid; 59 | List parts; 60 | bool hasLike; 61 | bool hasAddCoin; 62 | bool hasFavourite; 63 | } 64 | -------------------------------------------------------------------------------- /lib/common/models/local/video/video_play_info.dart: -------------------------------------------------------------------------------- 1 | import 'package:bili_you/common/models/local/video/audio_play_item.dart'; 2 | import 'package:bili_you/common/models/local/video/video_play_item.dart'; 3 | 4 | class VideoPlayInfo { 5 | VideoPlayInfo( 6 | { 7 | // required this.defualtVideoQuality, 8 | required this.supportVideoQualities, 9 | required this.supportAudioQualities, 10 | required this.timeLength, 11 | required this.videos, 12 | required this.audios, 13 | required this.lastPlayCid, 14 | required this.lastPlayTime}); 15 | static VideoPlayInfo get zero => VideoPlayInfo( 16 | supportVideoQualities: [], 17 | supportAudioQualities: [], 18 | timeLength: 0, 19 | videos: [], 20 | audios: [], 21 | lastPlayCid: 0, 22 | lastPlayTime: Duration.zero); 23 | // VideoQuality defualtVideoQuality ; 24 | ///支持的视频质量 25 | /// 26 | ///不是所有的质量都能播放,因为有一些是需要Vip的,不支持的质量在videos列表中会找不到 27 | List supportVideoQualities; 28 | // AudioQuality defualtAudioQuality ; 29 | ///支持的音频质量 30 | List supportAudioQualities; 31 | int timeLength; 32 | List videos; 33 | List audios; 34 | Duration lastPlayTime; 35 | int lastPlayCid; 36 | } 37 | 38 | extension VideoPlayInfoMethods on VideoPlayInfo { 39 | ///通过画质查找视频 40 | List findVideoByVideoQuality(VideoQuality videoQuality) { 41 | List list = []; 42 | for (var i in videos) { 43 | if (i.quality == videoQuality) { 44 | list.add(i); 45 | } 46 | } 47 | return list; 48 | } 49 | 50 | ///通过音质查找视频 51 | List findAudioByAudioQuality(AudioQuality audioQuality) { 52 | List list = []; 53 | for (var i in audios) { 54 | if (i.quality == audioQuality) { 55 | list.add(i); 56 | } 57 | } 58 | return list; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /lib/common/models/local/video_tile/video_tile_info.dart: -------------------------------------------------------------------------------- 1 | class VideoTileInfo { 2 | VideoTileInfo( 3 | {required this.coverUrl, 4 | required this.bvid, 5 | required this.cid, 6 | required this.title, 7 | required this.upName, 8 | required this.timeLength, 9 | required this.playNum, 10 | required this.pubDate}); 11 | static VideoTileInfo get zero => VideoTileInfo( 12 | coverUrl: "", 13 | bvid: "", 14 | cid: 0, 15 | title: "", 16 | upName: "", 17 | timeLength: 0, 18 | playNum: 0, 19 | pubDate: 0); 20 | String coverUrl; 21 | String bvid; 22 | int cid; 23 | String title; 24 | String upName; 25 | int timeLength; 26 | int playNum; 27 | int pubDate; 28 | } 29 | -------------------------------------------------------------------------------- /lib/common/models/network/history/report_history.dart: -------------------------------------------------------------------------------- 1 | class ReportHistory { 2 | ReportHistory({ 3 | this.code, 4 | this.message, 5 | this.ttl, 6 | }); 7 | 8 | ReportHistory.fromJson(dynamic json) { 9 | code = json['code']; 10 | message = json['message']; 11 | ttl = json['ttl']; 12 | } 13 | int? code; 14 | String? message; 15 | int? ttl; 16 | 17 | Map toJson() { 18 | final map = {}; 19 | map['code'] = code; 20 | map['message'] = message; 21 | map['ttl'] = ttl; 22 | return map; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/common/models/network/login/captcha_result.dart: -------------------------------------------------------------------------------- 1 | import 'captcha_data.dart'; 2 | 3 | class CaptchaResultModel { 4 | String validate; 5 | String seccode; 6 | 7 | CaptchaDataResponse captchaData; 8 | CaptchaResultModel( 9 | {this.validate = "", this.seccode = "", required this.captchaData}); 10 | } 11 | -------------------------------------------------------------------------------- /lib/common/models/network/login/password_login_key_hash.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | class PasswordLoginKeyHashResponse { 4 | PasswordLoginKeyHashResponse({ 5 | this.code, 6 | this.message, 7 | this.ttl, 8 | this.data, 9 | }); 10 | 11 | int? code; 12 | String? message; 13 | int? ttl; 14 | PasswordLoginKeyHashResponseData? data; 15 | 16 | factory PasswordLoginKeyHashResponse.fromRawJson(String str) => 17 | PasswordLoginKeyHashResponse.fromJson(json.decode(str)); 18 | 19 | String toRawJson() => json.encode(toJson()); 20 | 21 | factory PasswordLoginKeyHashResponse.fromJson(Map json) => 22 | PasswordLoginKeyHashResponse( 23 | code: json["code"], 24 | message: json["message"], 25 | ttl: json["ttl"], 26 | data: json["data"] == null 27 | ? null 28 | : PasswordLoginKeyHashResponseData.fromJson(json["data"]), 29 | ); 30 | 31 | Map toJson() => { 32 | "code": code, 33 | "message": message, 34 | "ttl": ttl, 35 | "data": data?.toJson(), 36 | }; 37 | } 38 | 39 | class PasswordLoginKeyHashResponseData { 40 | PasswordLoginKeyHashResponseData({ 41 | this.hash, 42 | this.key, 43 | }); 44 | 45 | String? hash; 46 | String? key; 47 | 48 | factory PasswordLoginKeyHashResponseData.fromRawJson(String str) => 49 | PasswordLoginKeyHashResponseData.fromJson(json.decode(str)); 50 | 51 | String toRawJson() => json.encode(toJson()); 52 | 53 | factory PasswordLoginKeyHashResponseData.fromJson( 54 | Map json) => 55 | PasswordLoginKeyHashResponseData( 56 | hash: json["hash"], 57 | key: json["key"], 58 | ); 59 | 60 | Map toJson() => { 61 | "hash": hash, 62 | "key": key, 63 | }; 64 | } 65 | -------------------------------------------------------------------------------- /lib/common/models/network/login/password_login_result.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | class PostPasswordLoginResponse { 4 | PostPasswordLoginResponse({ 5 | this.code, 6 | this.message, 7 | this.data, 8 | }); 9 | 10 | int? code; 11 | String? message; 12 | PostPasswordLoginResponseData? data; 13 | 14 | factory PostPasswordLoginResponse.fromRawJson(String str) => 15 | PostPasswordLoginResponse.fromJson(json.decode(str)); 16 | 17 | String toRawJson() => json.encode(toJson()); 18 | 19 | factory PostPasswordLoginResponse.fromJson(Map json) => 20 | PostPasswordLoginResponse( 21 | code: json["code"], 22 | message: json["message"], 23 | data: json["data"] == null 24 | ? null 25 | : PostPasswordLoginResponseData.fromJson(json["data"]), 26 | ); 27 | 28 | Map toJson() => { 29 | "code": code, 30 | "message": message, 31 | "data": data?.toJson(), 32 | }; 33 | } 34 | 35 | class PostPasswordLoginResponseData { 36 | PostPasswordLoginResponseData({ 37 | this.status, 38 | }); 39 | 40 | int? status; 41 | 42 | factory PostPasswordLoginResponseData.fromRawJson(String str) => 43 | PostPasswordLoginResponseData.fromJson(json.decode(str)); 44 | 45 | String toRawJson() => json.encode(toJson()); 46 | 47 | factory PostPasswordLoginResponseData.fromJson(Map json) => 48 | PostPasswordLoginResponseData( 49 | status: json["status"], 50 | ); 51 | 52 | Map toJson() => { 53 | "status": status, 54 | }; 55 | } 56 | -------------------------------------------------------------------------------- /lib/common/models/network/login/post_sms_login.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | class PostSmsLoginResponse { 4 | PostSmsLoginResponse({ 5 | this.code, 6 | this.message, 7 | this.data, 8 | }); 9 | 10 | int? code; 11 | String? message; 12 | PostSmsLoginResponseData? data; 13 | 14 | factory PostSmsLoginResponse.fromRawJson(String str) => 15 | PostSmsLoginResponse.fromJson(json.decode(str)); 16 | 17 | String toRawJson() => json.encode(toJson()); 18 | 19 | factory PostSmsLoginResponse.fromJson(Map json) => 20 | PostSmsLoginResponse( 21 | code: json["code"], 22 | message: json["message"], 23 | data: json["data"] == null 24 | ? null 25 | : PostSmsLoginResponseData.fromJson(json["data"]), 26 | ); 27 | 28 | Map toJson() => { 29 | "code": code, 30 | "message": message, 31 | "data": data?.toJson(), 32 | }; 33 | } 34 | 35 | class PostSmsLoginResponseData { 36 | PostSmsLoginResponseData({ 37 | this.isNew, 38 | this.status, 39 | }); 40 | 41 | bool? isNew; 42 | int? status; 43 | 44 | factory PostSmsLoginResponseData.fromRawJson(String str) => 45 | PostSmsLoginResponseData.fromJson(json.decode(str)); 46 | 47 | String toRawJson() => json.encode(toJson()); 48 | 49 | factory PostSmsLoginResponseData.fromJson(Map json) => 50 | PostSmsLoginResponseData( 51 | isNew: json["is_new"], 52 | status: json["status"], 53 | ); 54 | 55 | Map toJson() => { 56 | "is_new": isNew, 57 | "status": status, 58 | }; 59 | } 60 | -------------------------------------------------------------------------------- /lib/common/models/network/login/post_sms_require.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | class PostSmsRequireResponse { 4 | PostSmsRequireResponse({ 5 | this.code, 6 | this.message, 7 | this.data, 8 | }); 9 | 10 | int? code; 11 | String? message; 12 | PostSmsRequireResponseData? data; 13 | 14 | factory PostSmsRequireResponse.fromRawJson(String str) => 15 | PostSmsRequireResponse.fromJson(json.decode(str)); 16 | 17 | String toRawJson() => json.encode(toJson()); 18 | 19 | factory PostSmsRequireResponse.fromJson(Map json) => 20 | PostSmsRequireResponse( 21 | code: json["code"], 22 | message: json["message"], 23 | data: json["data"] == null 24 | ? null 25 | : PostSmsRequireResponseData.fromJson(json["data"]), 26 | ); 27 | 28 | Map toJson() => { 29 | "code": code, 30 | "message": message, 31 | "data": data?.toJson(), 32 | }; 33 | } 34 | 35 | class PostSmsRequireResponseData { 36 | PostSmsRequireResponseData({ 37 | this.captchaKey, 38 | }); 39 | 40 | String? captchaKey; 41 | 42 | factory PostSmsRequireResponseData.fromRawJson(String str) => 43 | PostSmsRequireResponseData.fromJson(json.decode(str)); 44 | 45 | String toRawJson() => json.encode(toJson()); 46 | 47 | factory PostSmsRequireResponseData.fromJson(Map json) => 48 | PostSmsRequireResponseData( 49 | captchaKey: json["captcha_key"], 50 | ); 51 | 52 | Map toJson() => { 53 | "captcha_key": captchaKey, 54 | }; 55 | } 56 | -------------------------------------------------------------------------------- /lib/common/models/network/user/user_stat.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | class LoginUserStatResponse { 4 | LoginUserStatResponse({ 5 | this.code, 6 | this.message, 7 | this.ttl, 8 | this.data, 9 | }); 10 | 11 | int? code; 12 | String? message; 13 | int? ttl; 14 | UserStatResponseData? data; 15 | 16 | factory LoginUserStatResponse.fromRawJson(String str) => 17 | LoginUserStatResponse.fromJson(json.decode(str)); 18 | 19 | String toRawJson() => json.encode(toJson()); 20 | 21 | factory LoginUserStatResponse.fromJson(Map json) => 22 | LoginUserStatResponse( 23 | code: json["code"], 24 | message: json["message"], 25 | ttl: json["ttl"], 26 | data: json["data"] == null 27 | ? null 28 | : UserStatResponseData.fromJson(json["data"]), 29 | ); 30 | 31 | Map toJson() => { 32 | "code": code, 33 | "message": message, 34 | "ttl": ttl, 35 | "data": data?.toJson(), 36 | }; 37 | } 38 | 39 | class UserStatResponseData { 40 | UserStatResponseData({ 41 | this.following, 42 | this.follower, 43 | this.dynamicCount, 44 | }); 45 | 46 | int? following; 47 | int? follower; 48 | int? dynamicCount; 49 | 50 | factory UserStatResponseData.fromRawJson(String str) => 51 | UserStatResponseData.fromJson(json.decode(str)); 52 | 53 | String toRawJson() => json.encode(toJson()); 54 | 55 | factory UserStatResponseData.fromJson(Map json) => 56 | UserStatResponseData( 57 | following: json["following"], 58 | follower: json["follower"], 59 | dynamicCount: json["dynamic_count"], 60 | ); 61 | 62 | Map toJson() => { 63 | "following": following, 64 | "follower": follower, 65 | "dynamic_count": dynamicCount, 66 | }; 67 | } 68 | -------------------------------------------------------------------------------- /lib/common/models/network/user_relations/user_realtion.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:bili_you/common/models/network/user/user_info.dart'; 4 | 5 | class UserRelation { 6 | int? mid; 7 | int? attribute; 8 | int? mtime; 9 | dynamic tag; 10 | int? special; 11 | Object? contractInfo; 12 | String? uname; 13 | String? face; 14 | int? faceNft; 15 | String? sign; 16 | OfficialVerify? officialVerify; 17 | Vip? vip; 18 | String? nftIcon; 19 | String? recReason; 20 | String? trackId; 21 | 22 | UserRelation({ 23 | this.mid, 24 | this.attribute, 25 | this.mtime, 26 | this.tag, 27 | this.special, 28 | this.contractInfo, 29 | this.uname, 30 | this.face, 31 | this.faceNft, 32 | this.sign, 33 | this.officialVerify, 34 | this.vip, 35 | this.nftIcon, 36 | this.recReason, 37 | this.trackId, 38 | }); 39 | 40 | factory UserRelation.fromRawJson(String str) => json.decode(str); 41 | factory UserRelation.fromJson(Map json) => UserRelation( 42 | mid: json["mid"], 43 | attribute: json["attribute"], 44 | mtime: json["mtime"], 45 | tag: json["tag"], 46 | special: json["special"], 47 | contractInfo: json["contract_info"], 48 | uname: json["uname"], 49 | face: json["face"], 50 | faceNft: json["face_nft"], 51 | sign: json["sign"], 52 | officialVerify: OfficialVerify.fromJson(json["official_verify"]), 53 | vip: Vip.fromJson(json["vip"]), 54 | nftIcon: json["nft_icon"], 55 | recReason: json["rec_reason"], 56 | trackId: json["track_id"]); 57 | 58 | Map toJson() => { 59 | "mid": mid, 60 | "attribute": attribute, 61 | "mtime": mtime, 62 | "tag": tag, 63 | "special": special, 64 | "contract_info": contractInfo, 65 | "uname": uname, 66 | "face": face, 67 | "face_nft": faceNft, 68 | "sign": sign, 69 | "official_verify": officialVerify?.toJson(), 70 | "vip": vip?.toJson(), 71 | "nft_icon": nftIcon, 72 | "rec_reason": recReason, 73 | "track_id": trackId, 74 | }; 75 | 76 | String toRawJson() => json.encode(toJson()); 77 | } 78 | -------------------------------------------------------------------------------- /lib/common/models/network/user_relations/user_relation_types.dart: -------------------------------------------------------------------------------- 1 | //对于查询者,被查询者的身份 2 | enum UserRelationType{ 3 | following, //关注 4 | follower, //粉丝 5 | } -------------------------------------------------------------------------------- /lib/common/utils/bvid_avid_util.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:flutter/material.dart'; 4 | 5 | class BvidAvidUtil { 6 | static const String table = 7 | "fZodR9XQDSUm21yCkr6zBqiveYah8bt4xsWpHnJE7jL5VG3guMTKNPAwcF"; 8 | static const List seqArray = [11, 10, 3, 8, 4, 6]; 9 | static const int xOr = 177451812; 10 | static const int xAdd = 8728348608; 11 | static const List defaultBvid = [ 12 | 'B', 13 | 'V', 14 | '1', 15 | '', 16 | '', 17 | '4', 18 | '', 19 | '1', 20 | '', 21 | '7', 22 | '', 23 | '' 24 | ]; 25 | 26 | ///avid转bvid 27 | static String av2Bvid(int av) { 28 | int newAvId = (av ^ xOr) + xAdd; 29 | List defaultBv = []; 30 | defaultBv.addAll(BvidAvidUtil.defaultBvid); 31 | for (int i = 0; i < seqArray.length; i++) { 32 | defaultBv[seqArray[i]] = table.characters 33 | .elementAt((newAvId / pow(58, i).toInt() % 58).toInt()); 34 | } 35 | return defaultBv.join(); 36 | } 37 | 38 | ///bvid转avid 39 | static int bvid2Av(String bvid) { 40 | int newAvId = 0; 41 | for (int i = 0; i < seqArray.length; i++) { 42 | newAvId += 43 | (table.indexOf(bvid.characters.elementAt(seqArray[i])) * pow(58, i)) 44 | .toInt(); 45 | } 46 | int av = (newAvId - xAdd) ^ xOr; 47 | return av; 48 | } 49 | 50 | ///判断是否是bvid 51 | ///只是简单的通过文本判断 52 | static bool isBvid(String bvid) { 53 | bvid = bvid.toUpperCase(); 54 | if (bvid.length != defaultBvid.length) return false; 55 | for (int i = 0; i < bvid.length; i++) { 56 | if (defaultBvid[i] == '') continue; 57 | if (bvid.characters.elementAt(i) != defaultBvid[i]) return false; 58 | } 59 | return true; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /lib/common/utils/cache_util.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_cache_manager/flutter_cache_manager.dart'; 2 | 3 | class CacheUtils { 4 | static const String userFaceKey = 'userFace'; 5 | static const String othersFaceKey = 'othersFace'; 6 | static const String recommendItemCoverKey = 'recommendItemCover'; 7 | static const String searchResultItemCoverKey = 'searchResultItemCover'; 8 | static const String relatedVideosItemCoverKey = 'relatedVideosItemCover'; 9 | static const String dynamicVideoItemCoverKey = 'dynamicVideoItemCover'; 10 | static const String emoteKey = 'emote'; 11 | static const String bigImageKey = 'bigImageKey'; 12 | 13 | static final CacheManager userFaceCacheManager = 14 | CacheManager(Config(userFaceKey)); 15 | static final CacheManager othersFaceCacheManager = 16 | CacheManager(Config(othersFaceKey)); 17 | static final CacheManager recommendItemCoverCacheManager = 18 | CacheManager(Config(recommendItemCoverKey)); 19 | static final CacheManager searchResultItemCoverCacheManager = 20 | CacheManager(Config(searchResultItemCoverKey)); 21 | static final CacheManager relatedVideosItemCoverCacheManager = 22 | CacheManager(Config(relatedVideosItemCoverKey)); 23 | static final CacheManager dynamicVideoItemCoverCacheManager = 24 | CacheManager(Config(dynamicVideoItemCoverKey)); 25 | static final CacheManager emoteCacheManager = CacheManager(Config(emoteKey)); 26 | static final CacheManager bigImageCacheManager = 27 | CacheManager(Config(bigImageKey)); 28 | static final List cacheMangerList = [ 29 | userFaceCacheManager, 30 | othersFaceCacheManager, 31 | recommendItemCoverCacheManager, 32 | searchResultItemCoverCacheManager, 33 | relatedVideosItemCoverCacheManager, 34 | dynamicVideoItemCoverCacheManager, 35 | emoteCacheManager, 36 | bigImageCacheManager 37 | ]; 38 | 39 | ///释放所有图像内存 40 | static void clearAllCacheImageMem() { 41 | for (var cacheManager in cacheMangerList) { 42 | cacheManager.store.emptyMemoryCache(); 43 | } 44 | } 45 | 46 | ///删除所有图片缓存(除了用户头像缓存) 47 | static Future deleteAllCacheImage() async { 48 | for (var cacheManager in cacheMangerList) { 49 | //不删除用户头像 50 | if (cacheManager == userFaceCacheManager) continue; 51 | await cacheManager.store.emptyCache(); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /lib/common/utils/cookie_util.dart: -------------------------------------------------------------------------------- 1 | import 'package:bili_you/common/api/api_constants.dart'; 2 | 3 | import 'http_utils.dart'; 4 | 5 | class CookieUtils { 6 | static Future getCsrf() async { 7 | //从cookie中获取csrf需要的数据 8 | for (var i in (await HttpUtils.cookieManager.cookieJar 9 | .loadForRequest(Uri.parse(ApiConstants.bilibiliBase)))) { 10 | if (i.name == 'bili_jct') { 11 | return i.value; 12 | } 13 | } 14 | return ''; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /lib/common/utils/fullscreen.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:auto_orientation/auto_orientation.dart'; 4 | import 'package:device_info_plus/device_info_plus.dart'; 5 | import 'package:flutter/services.dart'; 6 | 7 | //进入全屏显示 8 | Future enterFullScreen() async { 9 | await SystemChrome.setEnabledSystemUIMode( 10 | SystemUiMode.immersiveSticky, 11 | ); 12 | } 13 | 14 | //退出全屏显示 15 | Future exitFullScreen() async { 16 | late SystemUiMode mode; 17 | if ((Platform.isAndroid && 18 | (await DeviceInfoPlugin().androidInfo).version.sdkInt >= 29) || 19 | !Platform.isAndroid) { 20 | mode = SystemUiMode.edgeToEdge; 21 | } else { 22 | mode = SystemUiMode.manual; 23 | } 24 | await SystemChrome.setEnabledSystemUIMode(mode, 25 | overlays: [SystemUiOverlay.top, SystemUiOverlay.bottom]); 26 | } 27 | 28 | //横屏 29 | Future landScape() async { 30 | if (Platform.isAndroid || Platform.isIOS) { 31 | await AutoOrientation.landscapeAutoMode(forceSensor: true); 32 | } 33 | } 34 | 35 | //竖屏 36 | Future portraitUp() async { 37 | await SystemChrome.setPreferredOrientations([ 38 | DeviceOrientation.portraitUp, 39 | ]); 40 | } 41 | -------------------------------------------------------------------------------- /lib/common/utils/index.dart: -------------------------------------------------------------------------------- 1 | export 'bili_you_storage.dart'; 2 | export 'fullscreen.dart'; 3 | export 'settings.dart'; 4 | export 'string_format_utils.dart'; 5 | -------------------------------------------------------------------------------- /lib/common/values/hero_tag_id.dart: -------------------------------------------------------------------------------- 1 | class HeroTagId { 2 | static int id = 0; 3 | static int lastId = 0; 4 | } 5 | -------------------------------------------------------------------------------- /lib/common/values/index.dart: -------------------------------------------------------------------------------- 1 | export '../utils/cache_util.dart'; 2 | export 'coutry_id.dart'; 3 | -------------------------------------------------------------------------------- /lib/common/widget/icon_text_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class IconTextButton extends StatelessWidget { 4 | const IconTextButton({ 5 | super.key, 6 | required this.onPressed, 7 | required this.icon, 8 | required this.text, 9 | this.selected = false, 10 | }); 11 | final Function()? onPressed; 12 | final Widget icon; 13 | final Text? text; 14 | 15 | ///是否被选上 16 | final bool selected; 17 | @override 18 | Widget build(BuildContext context) { 19 | return ElevatedButton( 20 | style: ButtonStyle( 21 | visualDensity: VisualDensity.comfortable, 22 | foregroundColor: selected 23 | ? MaterialStatePropertyAll(Theme.of(context).colorScheme.onPrimary) 24 | : null, 25 | backgroundColor: selected 26 | ? MaterialStatePropertyAll(Theme.of(context).colorScheme.primary) 27 | : null, 28 | elevation: const MaterialStatePropertyAll(0), 29 | padding: const MaterialStatePropertyAll( 30 | EdgeInsets.only(left: 10, right: 10, bottom: 0, top: 0)), 31 | minimumSize: const MaterialStatePropertyAll(Size(10, 10)), 32 | ), 33 | onPressed: onPressed ?? () {}, 34 | child: FittedBox( 35 | child: Column( 36 | mainAxisAlignment: MainAxisAlignment.center, 37 | children: [icon, if (text != null) text!], 38 | ), 39 | ), 40 | ); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /lib/common/widget/index.dart: -------------------------------------------------------------------------------- 1 | export 'bangumi_tile_item.dart'; 2 | export 'video_audio_player.dart'; 3 | export 'video_tile_item.dart'; 4 | 5 | -------------------------------------------------------------------------------- /lib/common/widget/radio_list_dialog.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class RadioListDialog extends StatefulWidget { 4 | const RadioListDialog( 5 | {super.key, 6 | required this.title, 7 | required this.itemNameValueMap, 8 | required this.groupValue, 9 | this.onChanged}); 10 | final String title; 11 | final Map itemNameValueMap; 12 | final T groupValue; 13 | final Function(T? value)? onChanged; 14 | 15 | @override 16 | State> createState() => _RadioListDialogState(); 17 | } 18 | 19 | class _RadioListDialogState extends State> { 20 | late List items; 21 | @override 22 | void initState() { 23 | items = []; 24 | widget.itemNameValueMap.forEach((title, value) { 25 | items.add(RadioListTile( 26 | value: value, 27 | groupValue: widget.groupValue, 28 | title: Text(title), 29 | onChanged: (value) { 30 | widget.onChanged?.call(value); 31 | setState(() {}); 32 | }, 33 | )); 34 | }); 35 | super.initState(); 36 | } 37 | 38 | @override 39 | Widget build(BuildContext context) { 40 | return AlertDialog( 41 | scrollable: true, 42 | title: Text(widget.title), 43 | content: Column(children: items), 44 | actions: [ 45 | TextButton( 46 | onPressed: () => Navigator.of(context).pop(), 47 | child: const Text('取消')) 48 | ], 49 | ); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /lib/common/widget/settings_label.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class SettingsLabel extends StatelessWidget { 4 | const SettingsLabel({super.key, required this.text}); 5 | final String text; 6 | 7 | @override 8 | Widget build(BuildContext context) { 9 | return Padding( 10 | padding: const EdgeInsets.only(left: 16, right: 16), 11 | child: Text( 12 | text, 13 | style: TextStyle(color: Theme.of(context).colorScheme.primary), 14 | ), 15 | ); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /lib/common/widget/settings_radios_tile.dart: -------------------------------------------------------------------------------- 1 | import 'package:bili_you/common/widget/radio_list_dialog.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | class SettingsRadiosTile extends StatefulWidget { 5 | const SettingsRadiosTile( 6 | {super.key, 7 | required this.title, 8 | required this.subTitle, 9 | required this.buildTrailingText, 10 | required this.itemNameValue, 11 | required this.buildGroupValue, 12 | required this.applyValue}); 13 | final String title; 14 | final String subTitle; 15 | 16 | ///当前已选择项的名称 17 | final String Function() buildTrailingText; 18 | 19 | ///项的(名称--值)数据表 20 | final Map itemNameValue; 21 | 22 | ///当前选择项的值 23 | final T Function() buildGroupValue; 24 | 25 | ///使当前选择值生效的回调函数 26 | final Function(T value) applyValue; 27 | 28 | @override 29 | State> createState() => _SettingsRadiosTileState(); 30 | } 31 | 32 | class _SettingsRadiosTileState extends State> { 33 | @override 34 | Widget build(BuildContext context) { 35 | return ListTile( 36 | title: Text(widget.title), 37 | subtitle: Text(widget.subTitle), 38 | trailing: Text(widget.buildTrailingText()), 39 | onTap: () { 40 | showDialog( 41 | context: context, 42 | builder: (context) => RadioListDialog( 43 | title: widget.title, 44 | itemNameValueMap: widget.itemNameValue, 45 | groupValue: widget.buildGroupValue(), 46 | onChanged: (value) { 47 | if (value != null) widget.applyValue(value); 48 | Navigator.of(context).pop(); 49 | setState( 50 | () {}, 51 | ); 52 | }, 53 | ), 54 | ); 55 | }, 56 | ); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /lib/common/widget/settings_slider_tile.dart: -------------------------------------------------------------------------------- 1 | import 'package:bili_you/common/utils/index.dart'; 2 | import 'package:bili_you/common/widget/slider_dialog.dart'; 3 | import 'package:flutter/material.dart'; 4 | 5 | class SettingsSliderTile extends StatefulWidget { 6 | const SettingsSliderTile( 7 | {super.key, 8 | required this.title, 9 | required this.subTitle, 10 | required this.settingsKey, 11 | required this.defualtValue, 12 | required this.min, 13 | required this.max, 14 | this.divisions, 15 | required this.buildLabel}); 16 | final String title; 17 | final String subTitle; 18 | final String settingsKey; 19 | final double defualtValue; 20 | 21 | final double min; 22 | final double max; 23 | final int? divisions; 24 | 25 | ///构建trailing和slider的label 26 | final String Function(double selectingValue)? buildLabel; 27 | 28 | @override 29 | State createState() => _SettingsSliderTileState(); 30 | } 31 | 32 | class _SettingsSliderTileState extends State { 33 | @override 34 | Widget build(BuildContext context) { 35 | double initValue = SettingsUtil.getValue(widget.settingsKey, 36 | defaultValue: widget.defualtValue); 37 | return ListTile( 38 | title: Text(widget.title), 39 | subtitle: Text(widget.subTitle), 40 | trailing: widget.buildLabel != null 41 | ? StatefulBuilder(builder: (context, setState) { 42 | return Text(widget.buildLabel!.call(initValue)); 43 | }) 44 | : null, 45 | onTap: () { 46 | showDialog( 47 | context: context, 48 | builder: (context) => SliderDialog( 49 | title: widget.title, 50 | initValue: initValue, 51 | min: widget.min, 52 | max: widget.max, 53 | divisions: widget.divisions, 54 | buildLabel: widget.buildLabel, 55 | onOk: (selectingValue) async { 56 | await SettingsUtil.setValue(widget.settingsKey, selectingValue); 57 | setState(() {}); 58 | }, 59 | ), 60 | ); 61 | }, 62 | ); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /lib/common/widget/settings_switch_tile.dart: -------------------------------------------------------------------------------- 1 | import 'package:bili_you/common/utils/settings.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | class SettingsSwitchTile extends StatelessWidget { 5 | const SettingsSwitchTile( 6 | {super.key, 7 | required this.title, 8 | required this.subTitle, 9 | required this.settingsKey, 10 | required this.defualtValue, 11 | this.apply}); 12 | final String title; 13 | final String subTitle; 14 | final String settingsKey; 15 | final bool defualtValue; 16 | 17 | ///在开关切换且设置保存后进行调用,提供给外部进行应用该设置项 18 | final Function()? apply; 19 | 20 | @override 21 | Widget build(BuildContext context) { 22 | return ListTile( 23 | title: Text(title), 24 | subtitle: Text(subTitle), 25 | trailing: StatefulBuilder(builder: (context, setState) { 26 | return Switch( 27 | value: SettingsUtil.getValue(settingsKey, defaultValue: defualtValue), 28 | onChanged: (value) async { 29 | await SettingsUtil.setValue(settingsKey, value); 30 | setState(() {}); 31 | apply?.call(); 32 | }, 33 | ); 34 | }), 35 | ); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /lib/common/widget/tag.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class TextTag extends StatelessWidget { 4 | final String text; 5 | 6 | const TextTag({Key? key, required this.text}) : super(key: key); 7 | 8 | @override 9 | Widget build(BuildContext context) { 10 | return Card( 11 | color: Theme.of(context).colorScheme.secondary, 12 | child: Text( 13 | ' $text ', 14 | style: TextStyle( 15 | color: Theme.of(context).colorScheme.onSecondary, 16 | fontSize: 10, 17 | fontWeight: FontWeight.bold, 18 | ), 19 | ), 20 | ); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /lib/index.dart: -------------------------------------------------------------------------------- 1 | export 'common/index.dart'; 2 | export 'main.dart'; 3 | -------------------------------------------------------------------------------- /lib/pages/about/index.dart: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/lib/pages/about/index.dart -------------------------------------------------------------------------------- /lib/pages/bili_live/controller.dart: -------------------------------------------------------------------------------- 1 | import 'dart:developer'; 2 | 3 | import 'package:bili_you/common/api/index.dart'; 4 | import 'package:bili_you/common/utils/http_utils.dart'; 5 | import 'package:bili_you/common/widget/video_audio_player.dart'; 6 | import 'package:get/get.dart'; 7 | import 'package:media_kit/media_kit.dart'; 8 | 9 | class BiliLivePageController extends GetxController { 10 | BiliLivePageController({required this.roomId}); 11 | final int roomId; 12 | Future init() async { 13 | await PlayersSingleton().dispose(); 14 | await PlayersSingleton().init(); 15 | PlayersSingleton().count++; 16 | await onRefresh(); 17 | } 18 | 19 | Future onRefresh() async { 20 | var response = 21 | await HttpUtils().get(ApiConstants.livePlayUrl, queryParameters: { 22 | 'room_id': roomId, 23 | 'protocol': '0, 1', 24 | 'format': '0, 1, 2', 25 | 'codec': '0, 1', 26 | 'qn': 250, 27 | 'platform': 'web', 28 | 'ptype': 8, 29 | 'dolby': 5, 30 | 'panorama': 1 31 | }); 32 | log(response.data['code'].toString()); 33 | log(response.data['message']); 34 | String baseUrl = response.data['data']['playurl_info']['playurl']['stream'] 35 | [0]['format'][0]['codec'][0]['base_url']; 36 | String host = response.data['data']['playurl_info']['playurl']['stream'][0] 37 | ['format'][0]['codec'][0]['url_info'][0]['host']; 38 | String extra = response.data['data']['playurl_info']['playurl']['stream'][0] 39 | ['format'][0]['codec'][0]['url_info'][0]['extra']; 40 | 41 | await PlayersSingleton().player!.open( 42 | Media(host + baseUrl + extra, 43 | httpHeaders: {'referer': 'https://live.bilibili.com'}), 44 | ); 45 | await PlayersSingleton().player!.play(); 46 | } 47 | 48 | @override 49 | void onClose() { 50 | PlayersSingleton().dispose(); 51 | super.onClose(); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /lib/pages/bili_live/view.dart: -------------------------------------------------------------------------------- 1 | import 'package:bili_you/common/index.dart'; 2 | import 'package:bili_you/common/models/local/live/live_room_card_info.dart'; 3 | import 'package:bili_you/pages/bili_live/controller.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:get/get.dart'; 6 | import 'package:media_kit_video/media_kit_video.dart'; 7 | 8 | class BiliLivePage extends StatefulWidget { 9 | const BiliLivePage({super.key, required this.info}); 10 | final LiveRoomCardInfo info; 11 | 12 | @override 13 | State createState() => _BiliLivePageState(); 14 | } 15 | 16 | class _BiliLivePageState extends State { 17 | late final BiliLivePageController controller; 18 | @override 19 | void initState() { 20 | controller = Get.put(BiliLivePageController(roomId: widget.info.roomId)); 21 | super.initState(); 22 | } 23 | 24 | @override 25 | void dispose() { 26 | controller.dispose(); 27 | super.dispose(); 28 | } 29 | 30 | @override 31 | Widget build(BuildContext context) { 32 | return Scaffold( 33 | appBar: AppBar( 34 | title: Text(widget.info.title), 35 | ), 36 | body: Center( 37 | child: FutureBuilder( 38 | future: controller.init(), 39 | builder: (context, snapshot) { 40 | if (snapshot.connectionState == ConnectionState.done) { 41 | return Video( 42 | controller: VideoController( 43 | PlayersSingleton().player!, 44 | configuration: VideoControllerConfiguration( 45 | enableHardwareAcceleration: SettingsUtil.getValue( 46 | SettingsStorageKeys.isHardwareDecode, 47 | defaultValue: true)), 48 | ), 49 | ); 50 | } else { 51 | return const CircularProgressIndicator(); 52 | } 53 | }, 54 | ))); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /lib/pages/bili_video/controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:bili_you/pages/bili_video/widgets/bili_video_player/bili_danmaku.dart'; 2 | import 'package:bili_you/pages/bili_video/widgets/bili_video_player/bili_video_player.dart'; 3 | import 'package:bili_you/pages/bili_video/widgets/bili_video_player/bili_video_player_panel.dart'; 4 | import 'package:bili_you/pages/bili_video/widgets/reply/index.dart'; 5 | import 'package:flutter/material.dart'; 6 | import 'package:get/get.dart'; 7 | 8 | class BiliVideoController extends GetxController 9 | with GetTickerProviderStateMixin { 10 | BiliVideoController({ 11 | required this.bvid, 12 | required this.cid, 13 | this.ssid, 14 | this.progress, 15 | required this.isBangumi, 16 | }); 17 | String bvid; 18 | late String oldBvid; 19 | int cid; 20 | int? ssid; 21 | int? progress; 22 | bool isBangumi; 23 | 24 | late BiliVideoPlayerController biliVideoPlayerController; 25 | late BiliVideoPlayerPanelController biliVideoPlayerPanelController; 26 | late BiliDanmakuController biliDanmakuController; 27 | late final TabController tabController; 28 | 29 | Future changeVideoPart(String bvid, int cid) async { 30 | this.cid = cid; 31 | this.bvid = bvid; 32 | biliVideoPlayerController.bvid = bvid; 33 | biliVideoPlayerController.cid = cid; 34 | await biliVideoPlayerController.changeCid(bvid, cid); 35 | } 36 | 37 | refreshReply() { 38 | Get.find(tag: 'ReplyPage:$oldBvid').bvid = bvid; 39 | Get.find(tag: 'ReplyPage:$oldBvid') 40 | .refreshController 41 | .callRefresh(); 42 | } 43 | 44 | @override 45 | void onInit() { 46 | oldBvid = bvid; 47 | tabController = TabController( 48 | length: 2, 49 | vsync: this, 50 | animationDuration: const Duration(milliseconds: 200)); 51 | biliVideoPlayerController = BiliVideoPlayerController( 52 | bvid: bvid, 53 | cid: cid, 54 | initVideoPosition: 55 | progress != null ? Duration(seconds: progress!) : Duration.zero); 56 | biliVideoPlayerPanelController = 57 | BiliVideoPlayerPanelController(biliVideoPlayerController); 58 | biliDanmakuController = BiliDanmakuController(biliVideoPlayerController); 59 | super.onInit(); 60 | } 61 | 62 | @override 63 | void onClose() { 64 | biliVideoPlayerController.dispose(); 65 | super.onClose(); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /lib/pages/bili_video/index.dart: -------------------------------------------------------------------------------- 1 | library video_play; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/bili_video/widgets/introduction/index.dart: -------------------------------------------------------------------------------- 1 | library introduction; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/bili_video/widgets/reply/index.dart: -------------------------------------------------------------------------------- 1 | library reply; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/bili_video1/bili_media_content.dart: -------------------------------------------------------------------------------- 1 | import 'package:bili_you/common/models/local/video/video_play_info.dart'; 2 | 3 | class BiliMediaContent extends VideoPlayInfo { 4 | BiliMediaContent( 5 | {required super.supportVideoQualities, 6 | required super.supportAudioQualities, 7 | required super.timeLength, 8 | required super.videos, 9 | required super.audios, 10 | required super.lastPlayCid, 11 | required super.lastPlayTime, 12 | required super.minBufferTime}); 13 | } 14 | -------------------------------------------------------------------------------- /lib/pages/bili_video1/bili_media_content_cubit.dart: -------------------------------------------------------------------------------- 1 | import 'package:bili_you/pages/bili_video1/bili_media_content.dart'; 2 | import 'package:flutter_bloc/flutter_bloc.dart'; 3 | 4 | class BiliMediaContentCubit extends Cubit { 5 | BiliMediaContentCubit(super.initialState); 6 | } 7 | -------------------------------------------------------------------------------- /lib/pages/bili_video1/bili_media_cubit.dart: -------------------------------------------------------------------------------- 1 | import 'package:bili_you/common/api/index.dart'; 2 | import 'package:bili_you/common/utils/index.dart'; 3 | 4 | import 'package:flutter_bloc/flutter_bloc.dart'; 5 | 6 | import 'bili_media_content.dart'; 7 | 8 | class BiliMedia { 9 | String bvid; 10 | int cid; 11 | int? ssid; 12 | int? epid; 13 | bool isBangumi; 14 | int? progress; 15 | String? cover; 16 | 17 | BiliMedia( 18 | {required this.bvid, 19 | required this.cid, 20 | this.ssid, 21 | this.epid, 22 | this.isBangumi = false, 23 | this.progress, 24 | this.cover}); 25 | } 26 | 27 | class BiliMediaCubit extends Cubit { 28 | BiliMediaCubit(super.initialState); 29 | Future getVideoPlayInfo() async { 30 | var videoPlayInfo = 31 | await VideoPlayApi.getVideoPlay(bvid: state.bvid, cid: state.cid); 32 | //找出最符合设置的清晰度/解码格式 33 | var seletedVideoQuality = SettingsUtil.getPreferVideoQuality(); 34 | var seletedAudioQuality = SettingsUtil.getPreferAudioQuality(); 35 | var seletedVideoCodec = SettingsUtil.getPreferVideoCodec(); 36 | for (var i in videoPlayInfo.videos) {} 37 | return videoPlayInfo as BiliMediaContent; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/pages/dynamic/index.dart: -------------------------------------------------------------------------------- 1 | library dynamic; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/dynamic/view.dart: -------------------------------------------------------------------------------- 1 | import 'package:bili_you/common/widget/simple_easy_refresher.dart'; 2 | import 'package:bili_you/pages/dynamic/widget/dynamic_author_filter.dart'; 3 | import 'package:bili_you/pages/dynamic/widget/dynamic_item_card.dart'; 4 | import 'package:easy_refresh/easy_refresh.dart'; 5 | import 'package:flutter/material.dart'; 6 | import 'package:get/get.dart'; 7 | 8 | import 'index.dart'; 9 | 10 | class DynamicPage extends StatefulWidget { 11 | const DynamicPage({Key? key}) : super(key: key); 12 | 13 | @override 14 | State createState() => _DynamicPageState(); 15 | } 16 | 17 | class _DynamicPageState extends State { 18 | late final DynamicController controller; 19 | @override 20 | void initState() { 21 | controller = Get.put(DynamicController()); 22 | super.initState(); 23 | } 24 | 25 | @override 26 | void dispose() { 27 | controller.dispose(); 28 | super.dispose(); 29 | } 30 | 31 | @override 32 | Widget build(BuildContext context) { 33 | return Scaffold( 34 | appBar: AppBar(title: const Text("动态")), 35 | body: SimpleEasyRefresher( 36 | easyRefreshController: controller.refreshController, 37 | indicatorPosition: IndicatorPosition.locator, 38 | childBuilder: (context, physics) => CustomScrollView( 39 | cacheExtent: MediaQuery.of(context).size.height, 40 | controller: controller.scrollController, 41 | physics: physics, 42 | slivers: [ 43 | //up主面板 44 | DynamicAuthorFilter( 45 | authors: controller.dynamicAuthorList, 46 | onAuthorFilterApplied: controller.applyAuthorFilter, 47 | ), 48 | const HeaderLocator.sliver(), 49 | //動態内容卡片 50 | SliverList.builder( 51 | itemCount: controller.dynamicItems.length, 52 | itemBuilder: (context, index) => 53 | DynamicItemCard(dynamicItem: controller.dynamicItems[index]), 54 | ), 55 | const FooterLocator.sliver(), 56 | ], 57 | ), 58 | onLoad: controller.onLoad, 59 | onRefresh: controller.onRefresh, 60 | )); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /lib/pages/dynamic/widget/dynamic_article.dart: -------------------------------------------------------------------------------- 1 | import 'package:bili_you/common/models/local/dynamic/dynamic_content.dart'; 2 | import 'package:bili_you/common/widget/foldable_text.dart'; 3 | import 'package:bili_you/pages/dynamic/widget/dynamic_draw.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | class DynamicArticleWidget extends StatelessWidget { 7 | const DynamicArticleWidget({super.key, required this.content}); 8 | final ArticleDynamicContent content; 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | ///TODO: 跳转到文章 13 | return Column( 14 | crossAxisAlignment: CrossAxisAlignment.start, 15 | children: [ 16 | Text(content.title, 17 | style: const TextStyle(fontSize: 15, fontWeight: FontWeight.bold)), 18 | if ((content.title.isNotEmpty && content.text.isNotEmpty)) 19 | const Padding(padding: EdgeInsets.only(top: 4)), 20 | SelectableRegion( 21 | magnifierConfiguration: const TextMagnifierConfiguration(), 22 | focusNode: FocusNode(), 23 | selectionControls: MaterialTextSelectionControls(), 24 | child: FoldableText.rich( 25 | TextSpan(text: content.text), 26 | maxLines: 6, 27 | folderTextStyle: 28 | TextStyle(color: Theme.of(context).colorScheme.primary), 29 | style: const TextStyle(fontSize: 15), 30 | )), 31 | if ((content.title.isNotEmpty || content.text.isNotEmpty) && 32 | content.draws.isNotEmpty) 33 | const Padding(padding: EdgeInsets.only(top: 4)), 34 | DynamicDrawWidget( 35 | content: DrawDynamicContent( 36 | description: content.description, 37 | emotes: content.emotes, 38 | draws: content.draws)), 39 | ], 40 | ); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /lib/pages/history/history_controller.dart: -------------------------------------------------------------------------------- 1 | import 'dart:developer'; 2 | 3 | import 'package:bili_you/common/api/history_api.dart'; 4 | import 'package:bili_you/common/models/local/history/video_view_history_item.dart'; 5 | import 'package:bili_you/common/values/index.dart'; 6 | import 'package:easy_refresh/easy_refresh.dart'; 7 | import 'package:flutter/material.dart'; 8 | import 'package:flutter_cache_manager/flutter_cache_manager.dart'; 9 | import 'package:get/get.dart'; 10 | 11 | class HistoryController extends GetxController { 12 | HistoryController(); 13 | EasyRefreshController easyRefreshController = EasyRefreshController( 14 | controlFinishLoad: true, controlFinishRefresh: true); 15 | int max = 0; 16 | int viewAt = 0; 17 | ScrollController scrollController = ScrollController(); 18 | List videoViewHistoryItems = []; 19 | CacheManager cacheManager = CacheUtils.searchResultItemCoverCacheManager; 20 | 21 | Future _loadVideoViewHistoryItemList() async { 22 | late List list; 23 | try { 24 | if (max == 0 && viewAt == 0) { 25 | list = await HistoryApi.getVideoViewHistory(); 26 | } else { 27 | list = await HistoryApi.getVideoViewHistory(max: max, viewAt: viewAt); 28 | } 29 | if (list.isNotEmpty) { 30 | max = list.last.oid; 31 | viewAt = list.last.viewAt; 32 | } 33 | } catch (e) { 34 | log(e.toString()); 35 | return false; 36 | } 37 | videoViewHistoryItems.addAll(list); 38 | return true; 39 | } 40 | Future onLoad() async { 41 | if (await _loadVideoViewHistoryItemList()) { 42 | easyRefreshController.finishLoad(IndicatorResult.success); 43 | easyRefreshController.resetFooter(); 44 | } else { 45 | easyRefreshController.finishLoad(IndicatorResult.fail); 46 | } 47 | } 48 | 49 | Future onRefresh() async { 50 | videoViewHistoryItems.clear(); 51 | max = 0; 52 | viewAt = 0; 53 | await cacheManager.emptyCache(); 54 | if (await _loadVideoViewHistoryItemList()) { 55 | easyRefreshController.finishRefresh(IndicatorResult.success); 56 | } else { 57 | easyRefreshController.finishRefresh(IndicatorResult.fail); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /lib/pages/history/history_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:bili_you/common/values/hero_tag_id.dart'; 2 | import 'package:bili_you/common/widget/simple_easy_refresher.dart'; 3 | import 'package:bili_you/common/widget/video_view_history_tile.dart'; 4 | import 'package:bili_you/pages/history/history_controller.dart'; 5 | import 'package:flutter/material.dart'; 6 | import 'package:get/get.dart'; 7 | 8 | class HistoryPage extends StatefulWidget { 9 | const HistoryPage({super.key}); 10 | 11 | @override 12 | State createState() => _HistoryPageState(); 13 | } 14 | 15 | class _HistoryPageState extends State { 16 | late HistoryController controller; 17 | @override 18 | void initState() { 19 | controller = Get.put(HistoryController()); 20 | super.initState(); 21 | } 22 | 23 | @override 24 | void dispose() { 25 | controller.dispose(); 26 | super.dispose(); 27 | } 28 | 29 | @override 30 | Widget build(BuildContext context) { 31 | return Scaffold( 32 | appBar: AppBar(title: const Text('历史记录')), 33 | body: SimpleEasyRefresher( 34 | onLoad: controller.onLoad, //加載更多 35 | onRefresh: controller.onRefresh, //刷新 36 | easyRefreshController: controller.easyRefreshController, 37 | childBuilder: (context, physics) => ListView.builder( 38 | padding: const EdgeInsets.all(12), 39 | addAutomaticKeepAlives: false, 40 | addRepaintBoundaries: false, 41 | physics: physics, 42 | itemCount: controller.videoViewHistoryItems.length, 43 | controller: controller.scrollController, 44 | itemBuilder: (context, index) => Padding( 45 | padding: const EdgeInsets.only(bottom: 10), 46 | child: VideoViewHistoryTile( 47 | videoViewHistoryItem: controller.videoViewHistoryItems[index], 48 | cacheManager: controller.cacheManager, 49 | heroTagId: HeroTagId.id++), 50 | ), 51 | ), 52 | ), 53 | ); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /lib/pages/home/index.dart: -------------------------------------------------------------------------------- 1 | library home; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/home/widgets/user_menu/index.dart: -------------------------------------------------------------------------------- 1 | library user_face; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/live_tab_page/controller.dart: -------------------------------------------------------------------------------- 1 | import 'dart:developer'; 2 | 3 | import 'package:bili_you/common/api/live_api.dart'; 4 | import 'package:bili_you/common/models/local/live/live_room_card_info.dart'; 5 | import 'package:bili_you/common/utils/bili_you_storage.dart'; 6 | import 'package:bili_you/common/utils/settings.dart'; 7 | import 'package:easy_refresh/easy_refresh.dart'; 8 | import 'package:flutter/material.dart'; 9 | import 'package:get/get.dart'; 10 | 11 | class LiveTabPageController extends GetxController { 12 | var refreshController = EasyRefreshController( 13 | controlFinishLoad: true, controlFinishRefresh: true); 14 | var scrollController = ScrollController(); 15 | int currentPageNum = 1; 16 | RxList infoList = [].obs; 17 | int columnCount = 2; 18 | @override 19 | void onInit() { 20 | columnCount = SettingsUtil.getValue( 21 | SettingsStorageKeys.recommendColumnCount, 22 | defaultValue: 2); 23 | super.onInit(); 24 | } 25 | 26 | void animateToTop() { 27 | scrollController.animateTo(0, 28 | duration: const Duration(milliseconds: 500), curve: Curves.linear); 29 | } 30 | 31 | Future _loadInfos() async { 32 | try { 33 | infoList.addAll(await LiveApi.getUserRecommendLive( 34 | pageNum: currentPageNum, pageSize: 30)); 35 | currentPageNum++; 36 | return true; 37 | } catch (e) { 38 | log("LiveTabPageController:$e"); 39 | } 40 | return false; 41 | } 42 | 43 | Future onRefresh() async { 44 | infoList.clear(); 45 | currentPageNum = 1; 46 | if (await _loadInfos()) { 47 | refreshController.finishRefresh(IndicatorResult.success); 48 | } else { 49 | refreshController.finishRefresh(IndicatorResult.fail); 50 | } 51 | } 52 | 53 | Future onLoad() async { 54 | if (await _loadInfos()) { 55 | refreshController.finishLoad(IndicatorResult.success); 56 | } else { 57 | refreshController.finishLoad(IndicatorResult.fail); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /lib/pages/live_tab_page/view.dart: -------------------------------------------------------------------------------- 1 | import 'package:bili_you/common/values/hero_tag_id.dart'; 2 | import 'package:bili_you/common/widget/live_room_card.dart'; 3 | import 'package:bili_you/common/widget/simple_easy_refresher.dart'; 4 | import 'package:bili_you/pages/live_tab_page/controller.dart'; 5 | import 'package:flutter/widgets.dart'; 6 | import 'package:get/get.dart'; 7 | 8 | class LiveTabPage extends StatefulWidget { 9 | const LiveTabPage({super.key}); 10 | 11 | @override 12 | State createState() => _LiveTabPageState(); 13 | } 14 | 15 | class _LiveTabPageState extends State 16 | with AutomaticKeepAliveClientMixin { 17 | late LiveTabPageController controller; 18 | @override 19 | void initState() { 20 | controller = Get.put(LiveTabPageController()); 21 | super.initState(); 22 | } 23 | 24 | @override 25 | void dispose() { 26 | controller.dispose(); 27 | super.dispose(); 28 | } 29 | 30 | @override 31 | Widget build(BuildContext context) { 32 | super.build(context); 33 | return SimpleEasyRefresher( 34 | onRefresh: controller.onRefresh, 35 | onLoad: controller.onLoad, 36 | easyRefreshController: controller.refreshController, 37 | childBuilder: (context, physics) => GridView.builder( 38 | controller: controller.scrollController, 39 | physics: physics, 40 | padding: const EdgeInsets.all(12), 41 | gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( 42 | crossAxisSpacing: 12, 43 | mainAxisSpacing: 12, 44 | crossAxisCount: controller.columnCount, 45 | mainAxisExtent: (MediaQuery.of(context).size.width / 46 | controller.columnCount) * 47 | 10 / 48 | 16 + 49 | 45 * MediaQuery.of(context).textScaleFactor), 50 | itemCount: controller.infoList.length, 51 | itemBuilder: (context, index) => LiveRoomCard( 52 | info: controller.infoList[index], heroTagId: HeroTagId.id++), 53 | )); 54 | } 55 | 56 | @override 57 | bool get wantKeepAlive => true; 58 | } 59 | -------------------------------------------------------------------------------- /lib/pages/login/password_login/index.dart: -------------------------------------------------------------------------------- 1 | library password_login; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/login/password_login/view.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:get/get.dart'; 3 | 4 | import 'index.dart'; 5 | 6 | class PasswordLoginPage extends GetView { 7 | const PasswordLoginPage({Key? key}) : super(key: key); 8 | 9 | // 主视图 10 | Widget _buildView(context) { 11 | var outlineInputBorder = OutlineInputBorder( 12 | borderRadius: const BorderRadius.all(Radius.circular(10)), 13 | borderSide: BorderSide(color: Theme.of(context).colorScheme.outline)); 14 | return Padding( 15 | padding: const EdgeInsets.all(20), 16 | child: Center( 17 | child: Column(children: [ 18 | const SizedBox( 19 | height: 20, 20 | ), 21 | TextField( 22 | onChanged: (value) { 23 | controller.account = value; 24 | }, 25 | decoration: 26 | InputDecoration(labelText: "账号", border: outlineInputBorder), 27 | ), 28 | const SizedBox( 29 | height: 20, 30 | ), 31 | TextField( 32 | onChanged: (value) { 33 | controller.password = value; 34 | }, 35 | decoration: 36 | InputDecoration(labelText: "密码", border: outlineInputBorder), 37 | ), 38 | const SizedBox( 39 | height: 20, 40 | ), 41 | OutlinedButton( 42 | onPressed: () { 43 | controller.startLogin(); 44 | }, 45 | child: const Text("登录")) 46 | ]), 47 | ), 48 | ); 49 | } 50 | 51 | @override 52 | Widget build(BuildContext context) { 53 | return GetBuilder( 54 | init: PasswordLoginController(), 55 | id: "password_login", 56 | builder: (_) { 57 | return Scaffold( 58 | appBar: AppBar(title: const Text("密码登录")), 59 | body: _buildView(context), 60 | ); 61 | }, 62 | ); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /lib/pages/login/qrcode_login/controller.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:developer'; 3 | 4 | import 'package:bili_you/common/api/index.dart'; 5 | import 'package:bili_you/common/models/local/login/login_qrcode_info.dart'; 6 | import 'package:bili_you/common/models/local/login/login_qrcode_stat.dart'; 7 | import 'package:bili_you/pages/login/controller.dart'; 8 | import 'package:flutter/material.dart'; 9 | import 'package:get/get.dart'; 10 | 11 | class QRcodeLoginController extends GetxController { 12 | late LoginQrcodeStat stat; 13 | late LoginQRcodeInfo info; 14 | Timer? timer; 15 | 16 | Future getQRcodeInfo() async { 17 | timer?.cancel(); 18 | timer = null; 19 | info = await LoginApi.getQRcode(); 20 | 21 | ///每秒钟检查是否扫码登录成功 22 | timer = Timer.periodic(const Duration(seconds: 1), (timer) async { 23 | log('a'); 24 | stat = await LoginApi.checkQRcodeLogin(qrcodeKey: info.qrcodeKey); 25 | if (stat == LoginQrcodeStat.loginSuccess) { 26 | //成功时 27 | await onLoginSuccess(await LoginApi.getLoginUserInfo(), 28 | await LoginApi.getLoginUserStat()); 29 | Navigator.of(Get.context!).popUntil((route) => route.isFirst); 30 | await Get.rawSnackbar(message: "登录成功!").show(); 31 | } else if (stat == LoginQrcodeStat.qrcodeInvalid) { 32 | //二维码失效时,弹出提示 33 | await Get.rawSnackbar(message: "二维码失效!请点击二维码进行刷新!").show(); 34 | } 35 | }); 36 | } 37 | 38 | ///手动检查登录状态 39 | Future checkQRcodeLoginStat() async { 40 | stat = await LoginApi.checkQRcodeLogin(qrcodeKey: info.qrcodeKey); 41 | if (stat == LoginQrcodeStat.loginSuccess) { 42 | await onLoginSuccess( 43 | await LoginApi.getLoginUserInfo(), await LoginApi.getLoginUserStat()); 44 | Navigator.of(Get.context!).popUntil((route) => route.isFirst); 45 | await Get.rawSnackbar(message: "登录成功!").show(); 46 | } else { 47 | await Get.rawSnackbar(message: stat.message).show(); 48 | } 49 | } 50 | 51 | @override 52 | void onClose() { 53 | timer?.cancel(); 54 | timer = null; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /lib/pages/login/qrcode_login/view.dart: -------------------------------------------------------------------------------- 1 | import 'package:bili_you/pages/login/qrcode_login/controller.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:get/get.dart'; 4 | import 'package:qr_flutter/qr_flutter.dart'; 5 | 6 | class QrcodeLogin extends StatefulWidget { 7 | const QrcodeLogin({super.key}); 8 | 9 | @override 10 | State createState() => _QrcodeLoginState(); 11 | } 12 | 13 | class _QrcodeLoginState extends State { 14 | late final QRcodeLoginController controller; 15 | @override 16 | void initState() { 17 | controller = Get.put(QRcodeLoginController()); 18 | super.initState(); 19 | } 20 | 21 | @override 22 | void dispose() { 23 | controller.dispose(); 24 | super.dispose(); 25 | } 26 | 27 | @override 28 | Widget build(BuildContext context) { 29 | return Scaffold( 30 | appBar: AppBar( 31 | title: const Text('二维码登录'), 32 | actions: [ 33 | TextButton( 34 | onPressed: () async { 35 | await controller.checkQRcodeLoginStat(); 36 | }, 37 | child: const Text("手动检查登录状态")), 38 | ], 39 | ), 40 | body: FutureBuilder( 41 | future: controller.getQRcodeInfo(), 42 | builder: (context, snapshot) { 43 | if (snapshot.connectionState == ConnectionState.done) { 44 | return GestureDetector( 45 | child: QrImageView( 46 | data: controller.info.url, 47 | backgroundColor: Colors.white, 48 | ), 49 | onTap: () { 50 | setState(() {}); 51 | }, 52 | ); 53 | } else { 54 | return const CircularProgressIndicator(); 55 | } 56 | }, 57 | ), 58 | ); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /lib/pages/login/sms_login/index.dart: -------------------------------------------------------------------------------- 1 | library phone_login; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/login/sms_login/widgets/error/controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:get/get.dart'; 2 | 3 | class ErrorController extends GetxController { 4 | ErrorController(); 5 | 6 | _initData() { 7 | update(["error"]); 8 | } 9 | 10 | void onTap() {} 11 | 12 | // @override 13 | // void onInit() { 14 | // super.onInit(); 15 | // } 16 | 17 | @override 18 | void onReady() { 19 | super.onReady(); 20 | _initData(); 21 | } 22 | 23 | // @override 24 | // void onClose() { 25 | // super.onClose(); 26 | // } 27 | } 28 | -------------------------------------------------------------------------------- /lib/pages/login/sms_login/widgets/error/index.dart: -------------------------------------------------------------------------------- 1 | library error; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/login/sms_login/widgets/error/view.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:get/get.dart'; 3 | 4 | import 'index.dart'; 5 | 6 | class ErrorPage extends GetView { 7 | const ErrorPage({Key? key, required this.message}) : super(key: key); 8 | final String message; 9 | 10 | // 主视图 11 | Widget _buildView() { 12 | return Center( 13 | child: Text(message), 14 | ); 15 | } 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | return GetBuilder( 20 | init: ErrorController(), 21 | id: "error", 22 | builder: (_) { 23 | return Scaffold( 24 | appBar: AppBar(title: const Text("错误")), 25 | body: SafeArea( 26 | child: _buildView(), 27 | ), 28 | ); 29 | }, 30 | ); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /lib/pages/main/controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:get/get.dart'; 3 | import '../home/view.dart'; 4 | import '../dynamic/view.dart'; 5 | 6 | class MainController extends GetxController { 7 | MainController(); 8 | var selectedIndex = 0.obs; 9 | 10 | List pages = [ 11 | const HomePage(), 12 | const DynamicPage(), 13 | ]; 14 | 15 | _initData() { 16 | // update(["main"]); 17 | } 18 | 19 | void onTap() {} 20 | 21 | // @override 22 | // void onInit() { 23 | // super.onInit(); 24 | // } 25 | 26 | @override 27 | void onReady() { 28 | super.onReady(); 29 | _initData(); 30 | } 31 | 32 | // @override 33 | // void onClose() { 34 | // super.onClose(); 35 | // } 36 | } 37 | -------------------------------------------------------------------------------- /lib/pages/main/index.dart: -------------------------------------------------------------------------------- 1 | library main; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/popular_video/controller.dart: -------------------------------------------------------------------------------- 1 | import 'dart:developer'; 2 | 3 | import 'package:bili_you/common/api/index.dart'; 4 | import 'package:bili_you/common/models/local/video_tile/video_tile_info.dart'; 5 | import 'package:easy_refresh/easy_refresh.dart'; 6 | import 'package:flutter/material.dart'; 7 | import 'package:get/get.dart'; 8 | 9 | class PopularVideoController extends GetxController { 10 | PopularVideoController(); 11 | var refreshController = EasyRefreshController( 12 | controlFinishLoad: true, controlFinishRefresh: true); 13 | var scrollController = ScrollController(); 14 | int currentPageNum = 1; 15 | RxList infoList = [].obs; 16 | 17 | void animateToTop() { 18 | scrollController.animateTo(0, 19 | duration: const Duration(milliseconds: 500), curve: Curves.linear); 20 | } 21 | 22 | Future _loadInfos() async { 23 | try { 24 | infoList.addAll(await HomeApi.getPopularVideos(pageNum: currentPageNum)); 25 | currentPageNum++; 26 | return true; 27 | } catch (e) { 28 | log("PopularVideoController:$e"); 29 | } 30 | return false; 31 | } 32 | 33 | Future onRefresh() async { 34 | infoList.clear(); 35 | currentPageNum = 1; 36 | if (await _loadInfos()) { 37 | refreshController.finishRefresh(IndicatorResult.success); 38 | } else { 39 | refreshController.finishRefresh(IndicatorResult.fail); 40 | } 41 | } 42 | 43 | Future onLoad() async { 44 | if (await _loadInfos()) { 45 | refreshController.finishLoad(IndicatorResult.success); 46 | } else { 47 | refreshController.finishLoad(IndicatorResult.fail); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /lib/pages/popular_video/view.dart: -------------------------------------------------------------------------------- 1 | import 'package:bili_you/common/values/hero_tag_id.dart'; 2 | import 'package:bili_you/common/widget/simple_easy_refresher.dart'; 3 | import 'package:bili_you/index.dart'; 4 | import 'package:bili_you/pages/bili_video/view.dart'; 5 | import 'package:bili_you/pages/popular_video/controller.dart'; 6 | import 'package:flutter/material.dart'; 7 | import 'package:get/get.dart'; 8 | 9 | class PopularVideoPage extends StatefulWidget { 10 | const PopularVideoPage({super.key}); 11 | 12 | @override 13 | State createState() => _PopularVideoPageState(); 14 | } 15 | 16 | class _PopularVideoPageState extends State 17 | with AutomaticKeepAliveClientMixin { 18 | late PopularVideoController controller; 19 | 20 | @override 21 | void initState() { 22 | controller = Get.put(PopularVideoController()); 23 | super.initState(); 24 | } 25 | 26 | @override 27 | void dispose() { 28 | controller.dispose(); 29 | super.dispose(); 30 | } 31 | 32 | @override 33 | Widget build(BuildContext context) { 34 | super.build(context); 35 | return SimpleEasyRefresher( 36 | onRefresh: controller.onRefresh, 37 | onLoad: controller.onLoad, 38 | easyRefreshController: controller.refreshController, 39 | childBuilder: (context, physics) => ListView.builder( 40 | padding: const EdgeInsets.all(12), 41 | controller: controller.scrollController, 42 | itemCount: controller.infoList.length, 43 | physics: physics, 44 | itemBuilder: (context, index) { 45 | var i = controller.infoList[index]; 46 | var heroTagId = HeroTagId.id++; 47 | return Padding( 48 | padding: const EdgeInsets.only(bottom: 10), 49 | child: VideoTileItem.fromVideoTileInfo(i, 50 | cacheManager: CacheUtils.relatedVideosItemCoverCacheManager, 51 | heroTagId: heroTagId, onTap: (context) { 52 | HeroTagId.lastId = heroTagId; 53 | Navigator.of(context).push(GetPageRoute( 54 | page: () => BiliVideoPage( 55 | key: ValueKey("BiliVideoPage:${i.bvid}"), 56 | bvid: i.bvid, 57 | cid: i.cid, 58 | ), 59 | )); 60 | }), 61 | ); 62 | }, 63 | ), 64 | ); 65 | } 66 | 67 | @override 68 | bool get wantKeepAlive => true; 69 | } 70 | -------------------------------------------------------------------------------- /lib/pages/recommend/controller.dart: -------------------------------------------------------------------------------- 1 | import 'dart:developer'; 2 | 3 | import 'package:bili_you/common/models/local/home/recommend_item_info.dart'; 4 | import 'package:bili_you/common/utils/index.dart'; 5 | import 'package:bili_you/common/utils/cache_util.dart'; 6 | import 'package:easy_refresh/easy_refresh.dart'; 7 | import 'package:flutter/widgets.dart'; 8 | import 'package:flutter_cache_manager/flutter_cache_manager.dart'; 9 | import 'package:get/get.dart'; 10 | import 'package:bili_you/common/api/home_api.dart'; 11 | 12 | class RecommendController extends GetxController { 13 | RecommendController(); 14 | List recommendItems = []; 15 | 16 | ScrollController scrollController = ScrollController(); 17 | EasyRefreshController refreshController = EasyRefreshController( 18 | controlFinishLoad: true, controlFinishRefresh: true); 19 | int refreshIdx = 0; 20 | CacheManager cacheManager = CacheUtils.recommendItemCoverCacheManager; 21 | int recommendColumnCount = 2; 22 | 23 | @override 24 | void onInit() { 25 | recommendColumnCount = SettingsUtil.getValue( 26 | SettingsStorageKeys.recommendColumnCount, 27 | defaultValue: 2); 28 | super.onInit(); 29 | } 30 | 31 | void animateToTop() { 32 | scrollController.animateTo(0, 33 | duration: const Duration(milliseconds: 500), curve: Curves.linear); 34 | } 35 | 36 | //加载并追加视频推荐 37 | Future _addRecommendItems() async { 38 | try { 39 | recommendItems.addAll(await HomeApi.getRecommendVideoItems( 40 | num: 30, refreshIdx: refreshIdx)); 41 | } catch (e) { 42 | log("加载推荐视频失败:${e.toString()}"); 43 | return false; 44 | } 45 | refreshIdx += 1; 46 | return true; 47 | } 48 | 49 | Future onRefresh() async { 50 | recommendItems.clear(); 51 | await cacheManager.emptyCache(); 52 | if (await _addRecommendItems()) { 53 | refreshController.finishRefresh(IndicatorResult.success); 54 | } else { 55 | refreshController.finishRefresh(IndicatorResult.fail); 56 | } 57 | } 58 | 59 | Future onLoad() async { 60 | if (await _addRecommendItems()) { 61 | refreshController.finishLoad(IndicatorResult.success); 62 | refreshController.resetFooter(); 63 | } else { 64 | refreshController.finishLoad(IndicatorResult.fail); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /lib/pages/recommend/index.dart: -------------------------------------------------------------------------------- 1 | library recommend; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/relation/controller.dart: -------------------------------------------------------------------------------- 1 | import 'dart:developer'; 2 | 3 | import 'package:bili_you/common/api/realtions_api.dart'; 4 | import 'package:bili_you/common/models/network/user_relations/user_realtion.dart'; 5 | import 'package:bili_you/common/models/network/user_relations/user_relation_types.dart'; 6 | import 'package:bili_you/common/values/index.dart'; 7 | import 'package:easy_refresh/easy_refresh.dart'; 8 | import 'package:flutter/material.dart'; 9 | import 'package:flutter_cache_manager/flutter_cache_manager.dart'; 10 | import 'package:get/get.dart'; 11 | 12 | class RealtionController extends GetxController { 13 | RealtionController({required this.mid, required this.type}); 14 | final int mid; 15 | final UserRelationType type; 16 | 17 | EasyRefreshController easyRefreshController = EasyRefreshController( 18 | controlFinishLoad: true, controlFinishRefresh: true); 19 | 20 | int ps = 20; 21 | int pn = 1; 22 | 23 | ScrollController scrollController = ScrollController(); 24 | List relationsItems = []; 25 | CacheManager cacheManager = CacheUtils.searchResultItemCoverCacheManager; 26 | 27 | Future _loadList() async { 28 | late List list; 29 | try { 30 | switch (type) { 31 | case UserRelationType.following: 32 | list = await RelationApi.getFollowingList(vmid: mid, ps: ps, pn: pn); 33 | break; 34 | case UserRelationType.follower: 35 | list = await RelationApi.getFollowersList(vmid: mid, ps: ps, pn: pn); 36 | } 37 | 38 | pn++; 39 | } catch (e) { 40 | log(e.toString()); 41 | return false; 42 | } 43 | relationsItems.addAll(list); 44 | return true; 45 | } 46 | 47 | Future onLoad() async { 48 | if (await _loadList()) { 49 | easyRefreshController.finishLoad(IndicatorResult.success); 50 | easyRefreshController.resetFooter(); 51 | } else { 52 | easyRefreshController.finishLoad(IndicatorResult.fail); 53 | } 54 | } 55 | 56 | Future onRefresh() async { 57 | relationsItems.clear(); 58 | pn = 1; 59 | await cacheManager.emptyCache(); 60 | if (await _loadList()) { 61 | easyRefreshController.finishRefresh(IndicatorResult.success); 62 | } else { 63 | easyRefreshController.finishRefresh(IndicatorResult.fail); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /lib/pages/relation/index.dart: -------------------------------------------------------------------------------- 1 | library following; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; -------------------------------------------------------------------------------- /lib/pages/search_input/index.dart: -------------------------------------------------------------------------------- 1 | library search; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/search_result/controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:bili_you/common/api/search_api.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:get/get.dart'; 4 | 5 | class SearchResultController extends GetxController 6 | with GetSingleTickerProviderStateMixin { 7 | SearchResultController({required this.keyWord}); 8 | late TabController tabController; 9 | static int searchResultCount = 0; 10 | //tagId用来给tab使用,使同一个搜索界面下tab的tagName可以确定,以便使用Get.find获得对应的controller 11 | final tagId = searchResultCount++; 12 | int currentSelectedTabIndex = 0; 13 | String keyWord; 14 | 15 | //根据tab的index获取该搜索页面下该tab的tagName 16 | String getTabTagNameByIndex(int index) { 17 | return '${SearchType.values[index].name}:$tagId'; 18 | } 19 | 20 | @override 21 | void onInit() { 22 | tabController = TabController( 23 | length: 5, vsync: this, initialIndex: currentSelectedTabIndex); 24 | super.onInit(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/pages/search_result/index.dart: -------------------------------------------------------------------------------- 1 | library search_result; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/settings_page/others_settings_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:bili_you/common/widget/settings_label.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:get/get.dart'; 4 | 5 | import 'cache_management_page.dart'; 6 | 7 | class OthersSettingsPage extends StatelessWidget { 8 | const OthersSettingsPage({super.key}); 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | return Scaffold( 13 | appBar: AppBar( 14 | title: const Text("其他设置"), 15 | ), 16 | body: ListView(children: [ 17 | const SettingsLabel( 18 | text: '缓存', 19 | ), 20 | ListTile( 21 | title: const Text( 22 | "缓存管理", 23 | ), 24 | onTap: () { 25 | Navigator.of(context).push(GetPageRoute( 26 | page: () => const CacheManagementPage(), 27 | )); 28 | }, 29 | ) 30 | ]), 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /lib/pages/settings_page/settings_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:bili_you/pages/settings_page/appearance_settings_page.dart'; 2 | import 'package:bili_you/pages/settings_page/common_settings_page.dart'; 3 | import 'package:bili_you/pages/settings_page/others_settings_page.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:get/get.dart'; 6 | 7 | class SettingsPage extends StatelessWidget { 8 | const SettingsPage({super.key}); 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | Color iconColor = Theme.of(context).colorScheme.primary; 13 | return Scaffold( 14 | appBar: AppBar(title: const Text("设置")), 15 | body: ListView(children: [ 16 | ListTile( 17 | leading: Icon(Icons.tune_outlined, color: iconColor), 18 | title: const Text("通用"), 19 | onTap: () => Navigator.of(context).push(GetPageRoute( 20 | page: () => const CommonSettingsPage(), 21 | )), 22 | ), 23 | ListTile( 24 | leading: Icon(Icons.color_lens_outlined, color: iconColor), 25 | title: const Text("外观"), 26 | onTap: () => Navigator.of(context) 27 | .push(GetPageRoute(page: () => const AppearanceSettingsPage())), 28 | ), 29 | ListTile( 30 | leading: Icon(Icons.more_horiz_outlined, color: iconColor), 31 | title: const Text("其他"), 32 | onTap: () => Navigator.of(context).push(GetPageRoute( 33 | page: () => const OthersSettingsPage(), 34 | ))), 35 | ]), 36 | ); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /lib/pages/splash/controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:get/get.dart'; 2 | 3 | class SplashController extends GetxController { 4 | SplashController(); 5 | 6 | _initData() { 7 | // update(["splash"]); 8 | } 9 | 10 | void onTap() {} 11 | 12 | // @override 13 | // void onInit() { 14 | // super.onInit(); 15 | // } 16 | 17 | @override 18 | void onReady() { 19 | super.onReady(); 20 | _initData(); 21 | } 22 | 23 | // @override 24 | // void onClose() { 25 | // super.onClose(); 26 | // } 27 | } 28 | -------------------------------------------------------------------------------- /lib/pages/splash/index.dart: -------------------------------------------------------------------------------- 1 | library splash; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/splash/view.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:get/get.dart'; 3 | import 'package:bili_you/pages/main/view.dart'; 4 | 5 | import 'index.dart'; 6 | 7 | class SplashPage extends GetView { 8 | const SplashPage({Key? key}) : super(key: key); 9 | 10 | Widget _buildView() { 11 | //暂时先不写splash屏画面,让它直接跳转到主页面 12 | return const MainPage(); 13 | } 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return GetBuilder( 18 | init: SplashController(), 19 | id: "splash", 20 | builder: (splashController) { 21 | return _buildView(); 22 | }, 23 | ); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lib/pages/ui_test/index.dart: -------------------------------------------------------------------------------- 1 | library ui_test; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/ui_test/test_widget/media_kit_test_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:bili_you/common/index.dart'; 2 | import 'package:bili_you/common/utils/bvid_avid_util.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:media_kit/media_kit.dart'; 5 | import 'package:media_kit_video/media_kit_video.dart'; 6 | 7 | class BiliVideoPlayer extends StatefulWidget { 8 | const BiliVideoPlayer({super.key}); 9 | 10 | @override 11 | State createState() => _BiliVideoPlayerState(); 12 | } 13 | 14 | class _BiliVideoPlayerState extends State { 15 | final Player videopPlayer = 16 | Player(configuration: const PlayerConfiguration()); 17 | VideoController? controller; 18 | @override 19 | void initState() { 20 | Future.microtask(() async { 21 | String bvid = BvidAvidUtil.av2Bvid(170001); 22 | var videoPlayerInfo = await VideoPlayApi.getVideoPlay( 23 | bvid: bvid, cid: await VideoInfoApi.getFirstCid(bvid: bvid)); 24 | const String name = 'http-header-fields'; 25 | var kvArray = []; 26 | VideoPlayApi.videoPlayerHttpHeaders 27 | .forEach((key, value) => kvArray.add('$key: $value')); 28 | final data = kvArray.join(','); 29 | if (videopPlayer.platform is libmpvPlayer) { 30 | await (videopPlayer.platform as libmpvPlayer).setProperty(name, data); 31 | await (videopPlayer.platform as libmpvPlayer).setProperty('audio-files', 32 | videoPlayerInfo.audios.first.urls.first.replaceAll(':', '\\:')); 33 | await videopPlayer.setVolume(100); 34 | await videopPlayer.open(Media(videoPlayerInfo.videos.first.urls.first)); 35 | } 36 | controller = VideoController( 37 | videopPlayer, 38 | ); 39 | await videopPlayer.play(); 40 | setState(() {}); 41 | }); 42 | super.initState(); 43 | } 44 | 45 | @override 46 | void dispose() { 47 | Future.microtask(() async { 48 | await videopPlayer.dispose(); 49 | // controller?.dispose(); 50 | }); 51 | super.dispose(); 52 | } 53 | 54 | @override 55 | Widget build(BuildContext context) { 56 | return Video(controller: controller!); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /lib/pages/ui_test/view.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:get/get.dart'; 3 | 4 | import 'index.dart'; 5 | 6 | class UiTestPage extends StatefulWidget { 7 | const UiTestPage({Key? key}) : super(key: key); 8 | 9 | @override 10 | State createState() => _UiTestPageState(); 11 | } 12 | 13 | class _UiTestPageState extends State { 14 | late final UiTestController controller; 15 | @override 16 | void initState() { 17 | controller = Get.put(UiTestController()); 18 | super.initState(); 19 | } 20 | 21 | @override 22 | void dispose() { 23 | controller.dispose(); 24 | super.dispose(); 25 | } 26 | 27 | Widget _buildView(BuildContext context) { 28 | return ListView.builder( 29 | itemCount: controller.listTiles.length, 30 | itemBuilder: (context, index) => controller.listTiles[index], 31 | ); 32 | } 33 | 34 | @override 35 | Widget build(BuildContext context) { 36 | return Scaffold( 37 | appBar: AppBar(title: const Text("ui_test")), 38 | body: _buildView(context), 39 | ); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lib/pages/user_space/controller.dart: -------------------------------------------------------------------------------- 1 | import 'dart:developer'; 2 | 3 | import 'package:bili_you/common/api/user_space_api.dart'; 4 | import 'package:bili_you/common/models/local/user_space/user_video_search.dart'; 5 | import 'package:bili_you/common/utils/cache_util.dart'; 6 | import 'package:easy_refresh/easy_refresh.dart'; 7 | import 'package:flutter_cache_manager/flutter_cache_manager.dart'; 8 | import 'package:get/get.dart'; 9 | 10 | class UserSpacePageController extends GetxController { 11 | UserSpacePageController({required this.mid}); 12 | EasyRefreshController refreshController = EasyRefreshController( 13 | controlFinishLoad: true, controlFinishRefresh: true); 14 | CacheManager cacheManager = CacheUtils.searchResultItemCoverCacheManager; 15 | final int mid; 16 | int currentPage = 1; 17 | List searchItems = []; 18 | 19 | Future loadVideoItemWidgtLists() async { 20 | late UserVideoSearch userVideoSearch; 21 | try { 22 | userVideoSearch = 23 | await UserSpaceApi.getUserVideoSearch(mid: mid, pageNum: currentPage); 24 | } catch (e) { 25 | log("loadVideoItemWidgtLists:$e"); 26 | return false; 27 | } 28 | searchItems.addAll(userVideoSearch.videos); 29 | currentPage++; 30 | return true; 31 | } 32 | 33 | Future onLoad() async { 34 | if (await loadVideoItemWidgtLists()) { 35 | refreshController.finishLoad(IndicatorResult.success); 36 | refreshController.resetFooter(); 37 | } else { 38 | refreshController.finishLoad(IndicatorResult.fail); 39 | } 40 | } 41 | 42 | Future onRefresh() async { 43 | await cacheManager.emptyCache(); 44 | searchItems.clear(); 45 | currentPage = 1; 46 | bool success = await loadVideoItemWidgtLists(); 47 | if (success) { 48 | refreshController.finishRefresh(IndicatorResult.success); 49 | } else { 50 | refreshController.finishRefresh(IndicatorResult.fail); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /linux/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ephemeral 2 | -------------------------------------------------------------------------------- /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 | 14 | void fl_register_plugins(FlPluginRegistry* registry) { 15 | g_autoptr(FlPluginRegistrar) dynamic_color_registrar = 16 | fl_plugin_registry_get_registrar_for_plugin(registry, "DynamicColorPlugin"); 17 | dynamic_color_plugin_register_with_registrar(dynamic_color_registrar); 18 | g_autoptr(FlPluginRegistrar) media_kit_libs_linux_registrar = 19 | fl_plugin_registry_get_registrar_for_plugin(registry, "MediaKitLibsLinuxPlugin"); 20 | media_kit_libs_linux_plugin_register_with_registrar(media_kit_libs_linux_registrar); 21 | g_autoptr(FlPluginRegistrar) media_kit_video_registrar = 22 | fl_plugin_registry_get_registrar_for_plugin(registry, "MediaKitVideoPlugin"); 23 | media_kit_video_plugin_register_with_registrar(media_kit_video_registrar); 24 | g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = 25 | fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); 26 | url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); 27 | } 28 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /linux/flutter/generated_plugins.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Generated file, do not edit. 3 | # 4 | 5 | list(APPEND FLUTTER_PLUGIN_LIST 6 | dynamic_color 7 | media_kit_libs_linux 8 | media_kit_video 9 | url_launcher_linux 10 | ) 11 | 12 | list(APPEND FLUTTER_FFI_PLUGIN_LIST 13 | media_kit_native_event_loop 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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/-configuration: -------------------------------------------------------------------------------- 1 | Command line invocation: 2 | /Applications/Xcode.app/Contents/Developer/usr/bin/xcodebuild -project Runner.xcodeproj -scheme bili_you Release 3 | 4 | User defaults from command line: 5 | IDEPackageSupportUseBuiltinSCM = YES 6 | 7 | -------------------------------------------------------------------------------- /macos/.gitignore: -------------------------------------------------------------------------------- 1 | # Flutter-related 2 | **/Flutter/ephemeral/ 3 | **/Pods/ 4 | 5 | # Xcode-related 6 | **/dgph 7 | **/xcuserdata/ 8 | -------------------------------------------------------------------------------- /macos/.xcodeproj: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/macos/.xcodeproj -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /macos/Flutter/GeneratedPluginRegistrant.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | import FlutterMacOS 6 | import Foundation 7 | 8 | import connectivity_plus 9 | import device_info_plus 10 | import dynamic_color 11 | import media_kit_libs_macos_video 12 | import media_kit_video 13 | import package_info_plus 14 | import path_provider_foundation 15 | import screen_brightness_macos 16 | import share_plus 17 | import sqflite 18 | import url_launcher_macos 19 | import wakelock_plus 20 | 21 | func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { 22 | ConnectivityPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlugin")) 23 | DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) 24 | DynamicColorPlugin.register(with: registry.registrar(forPlugin: "DynamicColorPlugin")) 25 | MediaKitLibsMacosVideoPlugin.register(with: registry.registrar(forPlugin: "MediaKitLibsMacosVideoPlugin")) 26 | MediaKitVideoPlugin.register(with: registry.registrar(forPlugin: "MediaKitVideoPlugin")) 27 | FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin")) 28 | PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) 29 | ScreenBrightnessMacosPlugin.register(with: registry.registrar(forPlugin: "ScreenBrightnessMacosPlugin")) 30 | SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) 31 | SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) 32 | UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) 33 | WakelockPlusMacosPlugin.register(with: registry.registrar(forPlugin: "WakelockPlusMacosPlugin")) 34 | } 35 | -------------------------------------------------------------------------------- /macos/Podfile: -------------------------------------------------------------------------------- 1 | platform :osx, '11.0' 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 | -------------------------------------------------------------------------------- /macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /macos/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info": { 3 | "version": 1, 4 | "author": "xcode" 5 | }, 6 | "images": [ 7 | { 8 | "size": "16x16", 9 | "idiom": "mac", 10 | "filename": "app_icon_16.png", 11 | "scale": "1x" 12 | }, 13 | { 14 | "size": "16x16", 15 | "idiom": "mac", 16 | "filename": "app_icon_32.png", 17 | "scale": "2x" 18 | }, 19 | { 20 | "size": "32x32", 21 | "idiom": "mac", 22 | "filename": "app_icon_32.png", 23 | "scale": "1x" 24 | }, 25 | { 26 | "size": "32x32", 27 | "idiom": "mac", 28 | "filename": "app_icon_64.png", 29 | "scale": "2x" 30 | }, 31 | { 32 | "size": "128x128", 33 | "idiom": "mac", 34 | "filename": "app_icon_128.png", 35 | "scale": "1x" 36 | }, 37 | { 38 | "size": "128x128", 39 | "idiom": "mac", 40 | "filename": "app_icon_256.png", 41 | "scale": "2x" 42 | }, 43 | { 44 | "size": "256x256", 45 | "idiom": "mac", 46 | "filename": "app_icon_256.png", 47 | "scale": "1x" 48 | }, 49 | { 50 | "size": "256x256", 51 | "idiom": "mac", 52 | "filename": "app_icon_512.png", 53 | "scale": "2x" 54 | }, 55 | { 56 | "size": "512x512", 57 | "idiom": "mac", 58 | "filename": "app_icon_512.png", 59 | "scale": "1x" 60 | }, 61 | { 62 | "size": "512x512", 63 | "idiom": "mac", 64 | "filename": "app_icon_1024.png", 65 | "scale": "2x" 66 | } 67 | ] 68 | } -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png -------------------------------------------------------------------------------- /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_you 9 | 10 | // The application's bundle identifier 11 | PRODUCT_BUNDLE_IDENTIFIER = com.lucinhu.biliYou 12 | 13 | // The copyright displayed in application information 14 | PRODUCT_COPYRIGHT = Copyright © 2023 com.lucinhu. All rights reserved. 15 | -------------------------------------------------------------------------------- /macos/Runner/Configs/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Debug.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /macos/Runner/Configs/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Release.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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.client 10 | 11 | com.apple.security.network.server 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /macos/Runner/Release.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.network.client 8 | 9 | com.apple.security.network.server 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 | -------------------------------------------------------------------------------- /scripts/bili_you.AppDir/AppRun: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | cd "$(dirname "$0")" 3 | exec ./opt/bili_you 4 | -------------------------------------------------------------------------------- /scripts/bili_you.AppDir/bili_you.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Categories=AudioVideo; 3 | Comment[zh_CN]=第三方b站客户端 4 | Comment=Three party bilibili client. 5 | Exec=/opt/bili_you 6 | Icon=/opt/data/flutter_assets/assets/icon/bili 7 | Name[zh_CN]=BiliYou 8 | Name=BiliYou 9 | Terminal=false 10 | Type=Application 11 | -------------------------------------------------------------------------------- /scripts/build.sh: -------------------------------------------------------------------------------- 1 | flutter build apk --target-platform android-arm64 --split-per-abi -------------------------------------------------------------------------------- /scripts/build_linux_appimage.sh: -------------------------------------------------------------------------------- 1 | # 该脚本需要在项目根目录执行 2 | echo 编译linux版AppImage包到build目录... 3 | flutter build linux --release 4 | rm -rf scripts/bili_you.AppDir/opt/ 5 | cp -r build/linux/x64/release/bundle/ scripts/bili_you.AppDir/opt/ 6 | appimagetool scripts/bili_you.AppDir build/BiliYou-x86_64.AppImage 7 | rm -rf scripts/bili_you.AppDir/opt/ 8 | rm scripts/bili_you.AppDir/.DirIcon 9 | -------------------------------------------------------------------------------- /scripts/run_flutter_laucher_icons.sh: -------------------------------------------------------------------------------- 1 | flutter pub run flutter_launcher_icons -------------------------------------------------------------------------------- /web/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/web/favicon.png -------------------------------------------------------------------------------- /web/icons/Icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/web/icons/Icon-192.png -------------------------------------------------------------------------------- /web/icons/Icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/web/icons/Icon-512.png -------------------------------------------------------------------------------- /web/icons/Icon-maskable-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/web/icons/Icon-maskable-192.png -------------------------------------------------------------------------------- /web/icons/Icon-maskable-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/web/icons/Icon-maskable-512.png -------------------------------------------------------------------------------- /web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | bili_you 33 | 34 | 35 | 39 | 40 | 41 | 42 | 43 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /web/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bili_you", 3 | "short_name": "bili_you", 4 | "start_url": ".", 5 | "display": "standalone", 6 | "background_color": "#hexcode", 7 | "theme_color": "#hexcode", 8 | "description": "A new Flutter project.", 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 | } -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | ConnectivityPlusWindowsPluginRegisterWithRegistrar( 19 | registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin")); 20 | DynamicColorPluginCApiRegisterWithRegistrar( 21 | registry->GetRegistrarForPlugin("DynamicColorPluginCApi")); 22 | MediaKitLibsWindowsVideoPluginCApiRegisterWithRegistrar( 23 | registry->GetRegistrarForPlugin("MediaKitLibsWindowsVideoPluginCApi")); 24 | MediaKitVideoPluginCApiRegisterWithRegistrar( 25 | registry->GetRegistrarForPlugin("MediaKitVideoPluginCApi")); 26 | ScreenBrightnessWindowsPluginRegisterWithRegistrar( 27 | registry->GetRegistrarForPlugin("ScreenBrightnessWindowsPlugin")); 28 | SharePlusWindowsPluginCApiRegisterWithRegistrar( 29 | registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi")); 30 | UrlLauncherWindowsRegisterWithRegistrar( 31 | registry->GetRegistrarForPlugin("UrlLauncherWindows")); 32 | } 33 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /windows/flutter/generated_plugins.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Generated file, do not edit. 3 | # 4 | 5 | list(APPEND FLUTTER_PLUGIN_LIST 6 | connectivity_plus 7 | dynamic_color 8 | media_kit_libs_windows_video 9 | media_kit_video 10 | screen_brightness_windows 11 | share_plus 12 | url_launcher_windows 13 | ) 14 | 15 | list(APPEND FLUTTER_FFI_PLUGIN_LIST 16 | media_kit_native_event_loop 17 | ) 18 | 19 | set(PLUGIN_BUNDLED_LIBRARIES) 20 | 21 | foreach(plugin ${FLUTTER_PLUGIN_LIST}) 22 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) 23 | target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) 24 | list(APPEND PLUGIN_BUNDLED_LIBRARIES $) 25 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) 26 | endforeach(plugin) 27 | 28 | foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) 29 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) 30 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) 31 | endforeach(ffi_plugin) 32 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | return true; 35 | } 36 | 37 | void FlutterWindow::OnDestroy() { 38 | if (flutter_controller_) { 39 | flutter_controller_ = nullptr; 40 | } 41 | 42 | Win32Window::OnDestroy(); 43 | } 44 | 45 | LRESULT 46 | FlutterWindow::MessageHandler(HWND hwnd, UINT const message, 47 | WPARAM const wparam, 48 | LPARAM const lparam) noexcept { 49 | // Give Flutter, including plugins, an opportunity to handle window messages. 50 | if (flutter_controller_) { 51 | std::optional result = 52 | flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, 53 | lparam); 54 | if (result) { 55 | return *result; 56 | } 57 | } 58 | 59 | switch (message) { 60 | case WM_FONTCHANGE: 61 | flutter_controller_->engine()->ReloadSystemFonts(); 62 | break; 63 | } 64 | 65 | return Win32Window::MessageHandler(hwnd, message, wparam, lparam); 66 | } 67 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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_you", 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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /windows/runner/resources/app_icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucinhu/bili_you/ea34e1a06b6947e62473a107454d0f273a391498/windows/runner/resources/app_icon.ico -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | std::string utf8_string; 52 | if (target_length == 0 || target_length > utf8_string.max_size()) { 53 | return utf8_string; 54 | } 55 | utf8_string.resize(target_length); 56 | int converted_length = ::WideCharToMultiByte( 57 | CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, 58 | -1, utf8_string.data(), 59 | target_length, nullptr, nullptr); 60 | if (converted_length == 0) { 61 | return std::string(); 62 | } 63 | return utf8_string; 64 | } 65 | -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------