├── .github
└── workflows
│ └── release.yaml
├── .gitignore
├── .metadata
├── .vscode
├── launch.json
└── settings.json
├── LICENSE
├── README.md
├── analysis_options.yaml
├── android
├── .gitignore
├── app
│ ├── build.gradle
│ └── src
│ │ ├── debug
│ │ └── AndroidManifest.xml
│ │ ├── main
│ │ ├── AndroidManifest.xml
│ │ ├── kotlin
│ │ │ └── com
│ │ │ │ └── example
│ │ │ │ └── bilineo
│ │ │ │ └── MainActivity.kt
│ │ └── res
│ │ │ ├── drawable-hdpi
│ │ │ └── ic_launcher_foreground.png
│ │ │ ├── drawable-mdpi
│ │ │ └── ic_launcher_foreground.png
│ │ │ ├── drawable-v21
│ │ │ └── launch_background.xml
│ │ │ ├── drawable-xhdpi
│ │ │ └── ic_launcher_foreground.png
│ │ │ ├── drawable-xxhdpi
│ │ │ └── ic_launcher_foreground.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
├── images
│ ├── big-vip.png
│ ├── live.png
│ ├── loading.png
│ ├── logo
│ │ └── logo_android_2.png
│ ├── lv
│ │ ├── lv0.png
│ │ ├── lv1.png
│ │ ├── lv2.png
│ │ ├── lv3.png
│ │ ├── lv4.png
│ │ ├── lv5.png
│ │ └── lv6.png
│ └── noface.jpeg
├── logo
│ ├── logo_android.png
│ └── logo_ios.png
└── plugins
│ └── girigirilove.json
├── devtools_options.yaml
├── ios
├── .gitignore
├── Flutter
│ ├── AppFrameworkInfo.plist
│ ├── Debug.xcconfig
│ └── Release.xcconfig
├── 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
├── app_module.dart
├── app_widget.dart
├── bean
│ ├── bangumi
│ │ ├── bangumi_info.dart
│ │ └── bangumi_list.dart
│ ├── danmaku
│ │ ├── dm.pb.dart
│ │ ├── dm.pbenum.dart
│ │ ├── dm.pbjson.dart
│ │ ├── dm.pbserver.dart
│ │ └── dm.proto
│ └── settings
│ │ └── settings.dart
├── main.dart
├── pages
│ ├── card
│ │ ├── bangumi_card.dart
│ │ ├── bangumi_panel.dart
│ │ ├── network_img_layer.dart
│ │ └── pbadge.dart
│ ├── danmaku
│ │ └── controller.dart
│ ├── error
│ │ └── http_error.dart
│ ├── index_module.dart
│ ├── index_page.dart
│ ├── init_page.dart
│ ├── menu
│ │ └── menu.dart
│ ├── my
│ │ ├── my_controller.dart
│ │ ├── my_controller.g.dart
│ │ ├── my_module.dart
│ │ ├── my_page.dart
│ │ ├── user_info.dart
│ │ └── user_info.g.dart
│ ├── player
│ │ ├── play_quality.dart
│ │ ├── player_controller.dart
│ │ ├── player_controller.g.dart
│ │ ├── player_datasource.dart
│ │ ├── player_item.dart
│ │ └── player_url.dart
│ ├── popular
│ │ ├── popular_controller.dart
│ │ ├── popular_controller.g.dart
│ │ ├── popular_module.dart
│ │ └── popular_page.dart
│ ├── rating
│ │ ├── rating_module.dart
│ │ └── rating_page.dart
│ ├── router.dart
│ ├── search
│ │ ├── search_controller.dart
│ │ ├── search_controller.g.dart
│ │ ├── search_module.dart
│ │ ├── search_page.dart
│ │ ├── search_result.dart
│ │ ├── search_suggest.dart
│ │ └── search_type.dart
│ ├── search_result
│ │ ├── results_controller.dart
│ │ ├── results_controller.g.dart
│ │ ├── results_item.dart
│ │ ├── results_module.dart
│ │ └── results_page.dart
│ ├── video
│ │ ├── video_controller.dart
│ │ ├── video_controller.g.dart
│ │ ├── video_module.dart
│ │ └── video_page.dart
│ ├── webview
│ │ ├── webview_controller.dart
│ │ ├── webview_controller.g.dart
│ │ ├── webview_module.dart
│ │ └── webview_page.dart
│ └── webview_desktop
│ │ ├── webview_desktop_module.dart
│ │ └── webview_desktop_page.dart
├── request
│ ├── api.dart
│ ├── bangumi.dart
│ ├── constants.dart
│ ├── cookie.dart
│ ├── danmaku.dart
│ ├── interceptor.dart
│ ├── request.dart
│ ├── search.dart
│ ├── user.dart
│ └── video.dart
└── utils
│ ├── constans.dart
│ ├── em.dart
│ ├── extension.dart
│ ├── id.dart
│ ├── storage.dart
│ ├── utils.dart
│ ├── video.dart
│ └── wbisign.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
├── .gitignore
├── Flutter
│ ├── Flutter-Debug.xcconfig
│ ├── Flutter-Release.xcconfig
│ └── GeneratedPluginRegistrant.swift
├── 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
├── test
└── widget_test.dart
├── 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
/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .atom/
8 | .buildlog/
9 | .history
10 | .svn/
11 | migrate_working_dir/
12 |
13 | # IntelliJ related
14 | *.iml
15 | *.ipr
16 | *.iws
17 | .idea/
18 |
19 | # The .vscode folder contains launch configuration and tasks you configure in
20 | # VS Code which you may wish to be included in version control, so this line
21 | # is commented out by default.
22 | #.vscode/
23 |
24 | # Flutter/Dart/Pub related
25 | **/doc/api/
26 | **/ios/Flutter/.last_build_id
27 | .dart_tool/
28 | .flutter-plugins
29 | .flutter-plugins-dependencies
30 | .pub-cache/
31 | .pub/
32 | /build/
33 |
34 | # Symbolication related
35 | app.*.symbols
36 |
37 | # Obfuscation related
38 | app.*.map.json
39 |
40 | # Android Studio will place build artifacts here
41 | /android/app/debug
42 | /android/app/profile
43 | /android/app/release
44 |
--------------------------------------------------------------------------------
/.metadata:
--------------------------------------------------------------------------------
1 | # This file tracks properties of this Flutter project.
2 | # Used by Flutter tool to assess capabilities and perform upgrades etc.
3 | #
4 | # This file should be version controlled and should not be manually edited.
5 |
6 | version:
7 | revision: "41456452f29d64e8deb623a3c927524bcf9f111b"
8 | channel: "stable"
9 |
10 | project_type: app
11 |
12 | # Tracks metadata for the flutter migrate command
13 | migration:
14 | platforms:
15 | - platform: root
16 | create_revision: 41456452f29d64e8deb623a3c927524bcf9f111b
17 | base_revision: 41456452f29d64e8deb623a3c927524bcf9f111b
18 | - platform: android
19 | create_revision: 41456452f29d64e8deb623a3c927524bcf9f111b
20 | base_revision: 41456452f29d64e8deb623a3c927524bcf9f111b
21 | - platform: ios
22 | create_revision: 41456452f29d64e8deb623a3c927524bcf9f111b
23 | base_revision: 41456452f29d64e8deb623a3c927524bcf9f111b
24 | - platform: linux
25 | create_revision: 41456452f29d64e8deb623a3c927524bcf9f111b
26 | base_revision: 41456452f29d64e8deb623a3c927524bcf9f111b
27 | - platform: macos
28 | create_revision: 41456452f29d64e8deb623a3c927524bcf9f111b
29 | base_revision: 41456452f29d64e8deb623a3c927524bcf9f111b
30 | - platform: web
31 | create_revision: 41456452f29d64e8deb623a3c927524bcf9f111b
32 | base_revision: 41456452f29d64e8deb623a3c927524bcf9f111b
33 | - platform: windows
34 | create_revision: 41456452f29d64e8deb623a3c927524bcf9f111b
35 | base_revision: 41456452f29d64e8deb623a3c927524bcf9f111b
36 |
37 | # User provided section
38 |
39 | # List of Local paths (relative to this file) that should be
40 | # ignored by the migrate tool.
41 | #
42 | # Files that are not part of the templates will be ignored by default.
43 | unmanaged_files:
44 | - 'lib/main.dart'
45 | - 'ios/Runner.xcodeproj/project.pbxproj'
46 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "name": "bilineo",
9 | "request": "launch",
10 | "type": "dart"
11 | },
12 | {
13 | "name": "bilineo (profile mode)",
14 | "request": "launch",
15 | "type": "dart",
16 | "flutterMode": "profile"
17 | },
18 | {
19 | "name": "bilineo (release mode)",
20 | "request": "launch",
21 | "type": "dart",
22 | "flutterMode": "release"
23 | }
24 | ]
25 | }
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "cmake.configureOnOpen": false,
3 | "java.configuration.updateBuildConfiguration": "interactive"
4 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # bilineo
2 |
3 | 又一个 Bilibili 第三方客户端,仅支持番剧相关功能,项目使用 flutter 构建。本项目目的为个人学习与测试 Flutter 开发,所用API为B站公开API的封装,无任何破解行为。
4 |
5 | ## 支持平台
6 |
7 | - Android 10 及以上
8 | - Windows 10 1809 及以上
9 | - Linux (实验性)
10 |
11 | ## 功能 / 开发计划
12 |
13 | - [x] 番剧目录
14 | - [x] 番剧搜索
15 | - [x] 番剧字幕
16 | - [x] 番剧弹幕
17 | - [x] 港澳台番剧
18 | - [x] 视频播放器
19 | - [x] 硬件加速
20 | - [x] 在线更新
21 | - [x] 倍速播放
22 | - [ ] 新番动态
23 | - [ ] 番剧时间表
24 | - [ ] 追番列表
25 | - [ ] 番剧下载
26 | - [ ] 番剧评论
27 | - [ ] 还有更多 (/・ω・\)
28 |
29 | ## Q&A
30 |
31 | #### Q: 为什么我找不到 xxx 番剧? 为什么新番缺了那么多?
32 | A: 由于众所周知的问题,B站近年即使在港澳台区,购买番剧版权的策略也倾向于保守,如果没有您要找的番剧,可以试试作者的另一个项目 [oneAnime](https://github.com/Predidit/oneAnime)
33 |
34 | #### Q: 为什么 xxx 番剧没有字幕?
35 | A: B站部分番剧使用外挂字幕而非内嵌字幕,对应接口只支持包含有效用户签名的调用,对于此类番剧,需要您登录账号以获得字幕。
36 |
37 | #### Q: 我在尝试自行编译该项目,但是编译不通过。
38 |
39 | A: flutter 项目编译需要良好的网络环境,如果您位于中国大陆,可能需要设置恰当的镜像地址。如果您在编译 `Linux` 版本, 需要运行如下命令来安装编译需要的依赖。
40 | `sudo apt-get install -y clang cmake libgtk-3-dev ninja-build libayatana-appindicator3-dev mpv libmpv-dev libasound2-dev`
41 |
42 | ## 致谢
43 |
44 | 特别感谢 [pilipala](https://github.com/guozhigq/pilipala) 本项目使用了来自 pilipala 的代码。
45 |
46 | 感谢 [bilibili-API-collect](https://github.com/SocialSisterYi/bilibili-API-collect) 该项目收集的公开API使第三方客户端成为可能。
47 |
48 | 感谢 [bilibili-helper](https://github.com/ipcjs/bilibili-helper) 提供了解析 Bilibili 港澳台的相关思路。
49 |
--------------------------------------------------------------------------------
/analysis_options.yaml:
--------------------------------------------------------------------------------
1 | # This file configures the analyzer, which statically analyzes Dart code to
2 | # check for errors, warnings, and lints.
3 | #
4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled
5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
6 | # invoked from the command line by running `flutter analyze`.
7 |
8 | # The following line activates a set of recommended lints for Flutter apps,
9 | # packages, and plugins designed to encourage good coding practices.
10 | include: package:flutter_lints/flutter.yaml
11 |
12 | linter:
13 | # The lint rules applied to this project can be customized in the
14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml`
15 | # included above or to enable additional rules. A list of all available lints
16 | # and their documentation is published at https://dart.dev/lints.
17 | #
18 | # Instead of disabling a lint rule for the entire project in the
19 | # section below, it can also be suppressed for a single line of code
20 | # or a specific dart file by using the `// ignore: name_of_lint` and
21 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file
22 | # producing the lint.
23 | rules:
24 | # avoid_print: false # Uncomment to disable the `avoid_print` rule
25 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
26 |
27 | # Additional information about this file can be found at
28 | # https://dart.dev/guides/language/analysis-options
29 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id "com.android.application"
3 | id "kotlin-android"
4 | id "dev.flutter.flutter-gradle-plugin"
5 | }
6 |
7 | def localProperties = new Properties()
8 | def localPropertiesFile = rootProject.file('local.properties')
9 | if (localPropertiesFile.exists()) {
10 | localPropertiesFile.withReader('UTF-8') { reader ->
11 | localProperties.load(reader)
12 | }
13 | }
14 |
15 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
16 | if (flutterVersionCode == null) {
17 | flutterVersionCode = '1'
18 | }
19 |
20 | def flutterVersionName = localProperties.getProperty('flutter.versionName')
21 | if (flutterVersionName == null) {
22 | flutterVersionName = '1.0'
23 | }
24 |
25 | android {
26 | namespace "com.example.bilineo"
27 | compileSdkVersion flutter.compileSdkVersion
28 | ndkVersion flutter.ndkVersion
29 |
30 | compileOptions {
31 | sourceCompatibility JavaVersion.VERSION_1_8
32 | targetCompatibility JavaVersion.VERSION_1_8
33 | }
34 |
35 | kotlinOptions {
36 | jvmTarget = '1.8'
37 | }
38 |
39 | sourceSets {
40 | main.java.srcDirs += 'src/main/kotlin'
41 | }
42 |
43 | defaultConfig {
44 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
45 | applicationId "com.predidit.bilineo"
46 | // You can update the following values to match your application needs.
47 | // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
48 | minSdkVersion flutter.minSdkVersion
49 | targetSdkVersion flutter.targetSdkVersion
50 | versionCode flutterVersionCode.toInteger()
51 | versionName flutterVersionName
52 | }
53 |
54 | buildTypes {
55 | release {
56 | // TODO: Add your own signing config for the release build.
57 | // Signing with the debug keys for now, so `flutter run --release` works.
58 | signingConfig signingConfigs.debug
59 | }
60 | }
61 | }
62 |
63 | flutter {
64 | source '../..'
65 | }
66 |
67 | dependencies {}
68 |
--------------------------------------------------------------------------------
/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
15 |
19 |
23 |
24 |
25 |
26 |
27 |
28 |
30 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/android/app/src/main/kotlin/com/example/bilineo/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.example.bilineo
2 |
3 | import io.flutter.embedding.android.FlutterActivity
4 |
5 | class MainActivity: FlutterActivity() {
6 | }
7 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Predidit/BiliNeo/0e441fa786151a47405fa052d6125f5f37c28893/android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Predidit/BiliNeo/0e441fa786151a47405fa052d6125f5f37c28893/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/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Predidit/BiliNeo/0e441fa786151a47405fa052d6125f5f37c28893/android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Predidit/BiliNeo/0e441fa786151a47405fa052d6125f5f37c28893/android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Predidit/BiliNeo/0e441fa786151a47405fa052d6125f5f37c28893/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 |
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Predidit/BiliNeo/0e441fa786151a47405fa052d6125f5f37c28893/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Predidit/BiliNeo/0e441fa786151a47405fa052d6125f5f37c28893/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Predidit/BiliNeo/0e441fa786151a47405fa052d6125f5f37c28893/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Predidit/BiliNeo/0e441fa786151a47405fa052d6125f5f37c28893/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Predidit/BiliNeo/0e441fa786151a47405fa052d6125f5f37c28893/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/values-night/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/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 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/android/app/src/profile/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | ext.kotlin_version = '1.7.10'
3 | repositories {
4 | google()
5 | mavenCentral()
6 | }
7 |
8 | dependencies {
9 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
10 | }
11 | }
12 |
13 | allprojects {
14 | repositories {
15 | google()
16 | mavenCentral()
17 | }
18 | }
19 |
20 | rootProject.buildDir = '../build'
21 | subprojects {
22 | project.buildDir = "${rootProject.buildDir}/${project.name}"
23 | }
24 | subprojects {
25 | project.evaluationDependsOn(':app')
26 | }
27 |
28 | tasks.register("clean", Delete) {
29 | delete rootProject.buildDir
30 | }
31 |
--------------------------------------------------------------------------------
/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx4G
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 | pluginManagement {
2 | def flutterSdkPath = {
3 | def properties = new Properties()
4 | file("local.properties").withInputStream { properties.load(it) }
5 | def flutterSdkPath = properties.getProperty("flutter.sdk")
6 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
7 | return flutterSdkPath
8 | }
9 | settings.ext.flutterSdkPath = flutterSdkPath()
10 |
11 | includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle")
12 |
13 | repositories {
14 | google()
15 | mavenCentral()
16 | gradlePluginPortal()
17 | }
18 |
19 | plugins {
20 | id "dev.flutter.flutter-gradle-plugin" version "1.0.0" apply false
21 | }
22 | }
23 |
24 | plugins {
25 | id "dev.flutter.flutter-plugin-loader" version "1.0.0"
26 | id "com.android.application" version "7.3.0" apply false
27 | }
28 |
29 | include ":app"
30 |
--------------------------------------------------------------------------------
/assets/images/big-vip.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Predidit/BiliNeo/0e441fa786151a47405fa052d6125f5f37c28893/assets/images/big-vip.png
--------------------------------------------------------------------------------
/assets/images/live.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Predidit/BiliNeo/0e441fa786151a47405fa052d6125f5f37c28893/assets/images/live.png
--------------------------------------------------------------------------------
/assets/images/loading.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Predidit/BiliNeo/0e441fa786151a47405fa052d6125f5f37c28893/assets/images/loading.png
--------------------------------------------------------------------------------
/assets/images/logo/logo_android_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Predidit/BiliNeo/0e441fa786151a47405fa052d6125f5f37c28893/assets/images/logo/logo_android_2.png
--------------------------------------------------------------------------------
/assets/images/lv/lv0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Predidit/BiliNeo/0e441fa786151a47405fa052d6125f5f37c28893/assets/images/lv/lv0.png
--------------------------------------------------------------------------------
/assets/images/lv/lv1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Predidit/BiliNeo/0e441fa786151a47405fa052d6125f5f37c28893/assets/images/lv/lv1.png
--------------------------------------------------------------------------------
/assets/images/lv/lv2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Predidit/BiliNeo/0e441fa786151a47405fa052d6125f5f37c28893/assets/images/lv/lv2.png
--------------------------------------------------------------------------------
/assets/images/lv/lv3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Predidit/BiliNeo/0e441fa786151a47405fa052d6125f5f37c28893/assets/images/lv/lv3.png
--------------------------------------------------------------------------------
/assets/images/lv/lv4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Predidit/BiliNeo/0e441fa786151a47405fa052d6125f5f37c28893/assets/images/lv/lv4.png
--------------------------------------------------------------------------------
/assets/images/lv/lv5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Predidit/BiliNeo/0e441fa786151a47405fa052d6125f5f37c28893/assets/images/lv/lv5.png
--------------------------------------------------------------------------------
/assets/images/lv/lv6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Predidit/BiliNeo/0e441fa786151a47405fa052d6125f5f37c28893/assets/images/lv/lv6.png
--------------------------------------------------------------------------------
/assets/images/noface.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Predidit/BiliNeo/0e441fa786151a47405fa052d6125f5f37c28893/assets/images/noface.jpeg
--------------------------------------------------------------------------------
/assets/logo/logo_android.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Predidit/BiliNeo/0e441fa786151a47405fa052d6125f5f37c28893/assets/logo/logo_android.png
--------------------------------------------------------------------------------
/assets/logo/logo_ios.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Predidit/BiliNeo/0e441fa786151a47405fa052d6125f5f37c28893/assets/logo/logo_ios.png
--------------------------------------------------------------------------------
/assets/plugins/girigirilove.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "GiriGiriLove",
3 | "version": "0.0.1",
4 | "muliSources": "false",
5 | "useWebview": "false",
6 | "userAgent": "",
7 | "searchURL":"https://anime.girigirilove.com/search/-------------/?wd=@keyword",
8 | "searchList": "//div[4]/div[1]/div[1]/div[1]",
9 | "searchName": ".thumb-txt cor4 hide",
10 | "searchResult": ".a@href",
11 | "chapterRoads": "//div[5]/div[2]/div[1]/div[1]",
12 | "chapterResult": ".anthology-list-play size"
13 | }
--------------------------------------------------------------------------------
/devtools_options.yaml:
--------------------------------------------------------------------------------
1 | extensions:
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 | 12.0
25 |
26 |
27 |
--------------------------------------------------------------------------------
/ios/Flutter/Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Generated.xcconfig"
2 |
--------------------------------------------------------------------------------
/ios/Flutter/Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Generated.xcconfig"
2 |
--------------------------------------------------------------------------------
/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.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
37 |
38 |
39 |
40 |
43 |
49 |
50 |
51 |
52 |
53 |
63 |
65 |
71 |
72 |
73 |
74 |
80 |
82 |
88 |
89 |
90 |
91 |
93 |
94 |
97 |
98 |
99 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/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/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "20x20",
5 | "idiom" : "iphone",
6 | "filename" : "Icon-App-20x20@2x.png",
7 | "scale" : "2x"
8 | },
9 | {
10 | "size" : "20x20",
11 | "idiom" : "iphone",
12 | "filename" : "Icon-App-20x20@3x.png",
13 | "scale" : "3x"
14 | },
15 | {
16 | "size" : "29x29",
17 | "idiom" : "iphone",
18 | "filename" : "Icon-App-29x29@1x.png",
19 | "scale" : "1x"
20 | },
21 | {
22 | "size" : "29x29",
23 | "idiom" : "iphone",
24 | "filename" : "Icon-App-29x29@2x.png",
25 | "scale" : "2x"
26 | },
27 | {
28 | "size" : "29x29",
29 | "idiom" : "iphone",
30 | "filename" : "Icon-App-29x29@3x.png",
31 | "scale" : "3x"
32 | },
33 | {
34 | "size" : "40x40",
35 | "idiom" : "iphone",
36 | "filename" : "Icon-App-40x40@2x.png",
37 | "scale" : "2x"
38 | },
39 | {
40 | "size" : "40x40",
41 | "idiom" : "iphone",
42 | "filename" : "Icon-App-40x40@3x.png",
43 | "scale" : "3x"
44 | },
45 | {
46 | "size" : "60x60",
47 | "idiom" : "iphone",
48 | "filename" : "Icon-App-60x60@2x.png",
49 | "scale" : "2x"
50 | },
51 | {
52 | "size" : "60x60",
53 | "idiom" : "iphone",
54 | "filename" : "Icon-App-60x60@3x.png",
55 | "scale" : "3x"
56 | },
57 | {
58 | "size" : "20x20",
59 | "idiom" : "ipad",
60 | "filename" : "Icon-App-20x20@1x.png",
61 | "scale" : "1x"
62 | },
63 | {
64 | "size" : "20x20",
65 | "idiom" : "ipad",
66 | "filename" : "Icon-App-20x20@2x.png",
67 | "scale" : "2x"
68 | },
69 | {
70 | "size" : "29x29",
71 | "idiom" : "ipad",
72 | "filename" : "Icon-App-29x29@1x.png",
73 | "scale" : "1x"
74 | },
75 | {
76 | "size" : "29x29",
77 | "idiom" : "ipad",
78 | "filename" : "Icon-App-29x29@2x.png",
79 | "scale" : "2x"
80 | },
81 | {
82 | "size" : "40x40",
83 | "idiom" : "ipad",
84 | "filename" : "Icon-App-40x40@1x.png",
85 | "scale" : "1x"
86 | },
87 | {
88 | "size" : "40x40",
89 | "idiom" : "ipad",
90 | "filename" : "Icon-App-40x40@2x.png",
91 | "scale" : "2x"
92 | },
93 | {
94 | "size" : "76x76",
95 | "idiom" : "ipad",
96 | "filename" : "Icon-App-76x76@1x.png",
97 | "scale" : "1x"
98 | },
99 | {
100 | "size" : "76x76",
101 | "idiom" : "ipad",
102 | "filename" : "Icon-App-76x76@2x.png",
103 | "scale" : "2x"
104 | },
105 | {
106 | "size" : "83.5x83.5",
107 | "idiom" : "ipad",
108 | "filename" : "Icon-App-83.5x83.5@2x.png",
109 | "scale" : "2x"
110 | },
111 | {
112 | "size" : "1024x1024",
113 | "idiom" : "ios-marketing",
114 | "filename" : "Icon-App-1024x1024@1x.png",
115 | "scale" : "1x"
116 | }
117 | ],
118 | "info" : {
119 | "version" : 1,
120 | "author" : "xcode"
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Predidit/BiliNeo/0e441fa786151a47405fa052d6125f5f37c28893/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/Predidit/BiliNeo/0e441fa786151a47405fa052d6125f5f37c28893/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/Predidit/BiliNeo/0e441fa786151a47405fa052d6125f5f37c28893/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/Predidit/BiliNeo/0e441fa786151a47405fa052d6125f5f37c28893/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/Predidit/BiliNeo/0e441fa786151a47405fa052d6125f5f37c28893/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/Predidit/BiliNeo/0e441fa786151a47405fa052d6125f5f37c28893/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/Predidit/BiliNeo/0e441fa786151a47405fa052d6125f5f37c28893/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/Predidit/BiliNeo/0e441fa786151a47405fa052d6125f5f37c28893/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/Predidit/BiliNeo/0e441fa786151a47405fa052d6125f5f37c28893/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/Predidit/BiliNeo/0e441fa786151a47405fa052d6125f5f37c28893/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/Predidit/BiliNeo/0e441fa786151a47405fa052d6125f5f37c28893/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/Predidit/BiliNeo/0e441fa786151a47405fa052d6125f5f37c28893/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/Predidit/BiliNeo/0e441fa786151a47405fa052d6125f5f37c28893/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/Predidit/BiliNeo/0e441fa786151a47405fa052d6125f5f37c28893/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/Predidit/BiliNeo/0e441fa786151a47405fa052d6125f5f37c28893/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/Predidit/BiliNeo/0e441fa786151a47405fa052d6125f5f37c28893/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/Predidit/BiliNeo/0e441fa786151a47405fa052d6125f5f37c28893/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/Predidit/BiliNeo/0e441fa786151a47405fa052d6125f5f37c28893/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/Predidit/BiliNeo/0e441fa786151a47405fa052d6125f5f37c28893/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/Predidit/BiliNeo/0e441fa786151a47405fa052d6125f5f37c28893/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/Predidit/BiliNeo/0e441fa786151a47405fa052d6125f5f37c28893/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/Predidit/BiliNeo/0e441fa786151a47405fa052d6125f5f37c28893/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Predidit/BiliNeo/0e441fa786151a47405fa052d6125f5f37c28893/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Predidit/BiliNeo/0e441fa786151a47405fa052d6125f5f37c28893/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/LaunchScreen.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 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/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/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleDisplayName
8 | Bilineo
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | bilineo
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | $(FLUTTER_BUILD_NAME)
21 | CFBundleSignature
22 | ????
23 | CFBundleVersion
24 | $(FLUTTER_BUILD_NUMBER)
25 | LSRequiresIPhoneOS
26 |
27 | UILaunchStoryboardName
28 | LaunchScreen
29 | UIMainStoryboardFile
30 | Main
31 | UISupportedInterfaceOrientations
32 |
33 | UIInterfaceOrientationPortrait
34 | UIInterfaceOrientationLandscapeLeft
35 | UIInterfaceOrientationLandscapeRight
36 |
37 | UISupportedInterfaceOrientations~ipad
38 |
39 | UIInterfaceOrientationPortrait
40 | UIInterfaceOrientationPortraitUpsideDown
41 | UIInterfaceOrientationLandscapeLeft
42 | UIInterfaceOrientationLandscapeRight
43 |
44 | CADisableMinimumFrameDurationOnPhone
45 |
46 | UIApplicationSupportsIndirectInputEvents
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/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/app_module.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_modular/flutter_modular.dart';
2 | import 'package:bilineo/pages/index_module.dart';
3 |
4 | class AppModule extends Module {
5 | @override
6 | void binds(i) {
7 |
8 | }
9 |
10 | @override
11 | void routes(r) {
12 | r.module("/", module: IndexModule());
13 | }
14 | }
--------------------------------------------------------------------------------
/lib/app_widget.dart:
--------------------------------------------------------------------------------
1 | import 'dart:io';
2 | import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
3 | import 'package:flutter/material.dart';
4 | import 'package:flutter_localizations/flutter_localizations.dart';
5 | import 'package:flutter_modular/flutter_modular.dart';
6 | import 'package:adaptive_theme/adaptive_theme.dart';
7 | import 'package:flutter_displaymode/flutter_displaymode.dart';
8 |
9 | class AppWidget extends StatefulWidget {
10 | const AppWidget({super.key});
11 |
12 | @override
13 | State createState() => _AppWidgetState();
14 | }
15 |
16 | class _AppWidgetState extends State {
17 | @override
18 | Widget build(BuildContext context) {
19 | var app = AdaptiveTheme(
20 | light: ThemeData(
21 | useMaterial3: true,
22 | brightness: Brightness.light,
23 | ),
24 | dark: ThemeData(
25 | useMaterial3: true,
26 | brightness: Brightness.dark,
27 | ),
28 | initial: AdaptiveThemeMode.system,
29 | builder: (theme, darkTheme) => MaterialApp.router(
30 | title: "BiliNeo",
31 | localizationsDelegates: GlobalMaterialLocalizations.delegates,
32 | supportedLocales: const [
33 | Locale.fromSubtags(
34 | languageCode: 'zh', scriptCode: 'Hans', countryCode: "CN")
35 | ],
36 | locale: const Locale.fromSubtags(
37 | languageCode: 'zh', scriptCode: 'Hans', countryCode: "CN"),
38 | theme: theme,
39 | darkTheme: darkTheme,
40 | routerConfig: Modular.routerConfig,
41 | builder: FlutterSmartDialog.init(),
42 | // navigatorObservers: [Asuka.asukaHeroController],
43 | ),
44 | );
45 | Modular.setObservers([FlutterSmartDialog.observer]);
46 |
47 | // 强制设置高帧率
48 | if (Platform.isAndroid) {
49 | try {
50 | late List modes;
51 | FlutterDisplayMode.supported.then((value) {
52 | modes = value;
53 | DisplayMode f = DisplayMode.auto;
54 | DisplayMode preferred = modes.toList().firstWhere((el) => el == f);
55 | FlutterDisplayMode.setPreferredMode(preferred);
56 | });
57 | } catch (e) {
58 | debugPrint('高帧率设置失败 ${e.toString()}');
59 | }
60 | }
61 |
62 | return app;
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/lib/bean/bangumi/bangumi_list.dart:
--------------------------------------------------------------------------------
1 | class BangumiListDataModel {
2 | BangumiListDataModel({
3 | this.hasNext,
4 | this.list,
5 | this.num,
6 | this.size,
7 | this.total,
8 | });
9 |
10 | int? hasNext;
11 | List? list;
12 | int? num;
13 | int? size;
14 | int? total;
15 |
16 | BangumiListDataModel.fromJson(Map json) {
17 | hasNext = json['has_next'];
18 | list = json['list'] != null
19 | ? json['list']
20 | .map((e) => BangumiListItemModel.fromJson(e))
21 | .toList()
22 | : [];
23 | num = json['num'];
24 | size = json['size'];
25 | total = json['total'];
26 | }
27 | }
28 |
29 | class BangumiListItemModel {
30 | BangumiListItemModel({
31 | this.badge,
32 | this.badgeType,
33 | this.cover,
34 | // this.firstEp,
35 | this.indexShow,
36 | this.isFinish,
37 | this.link,
38 | this.mediaId,
39 | this.order,
40 | this.orderType,
41 | this.score,
42 | this.seasonId,
43 | this.seaconStatus,
44 | this.seasonType,
45 | this.subTitle,
46 | this.title,
47 | this.titleIcon,
48 | this.progress,
49 | });
50 |
51 | String? badge;
52 | int? badgeType;
53 | String? cover;
54 | String? indexShow;
55 | int? isFinish;
56 | String? link;
57 | int? mediaId;
58 | String? order;
59 | String? orderType;
60 | String? score;
61 | int? seasonId;
62 | int? seaconStatus;
63 | int? seasonType;
64 | String? subTitle;
65 | String? title;
66 | String? titleIcon;
67 |
68 | String? progress;
69 |
70 | BangumiListItemModel.fromJson(Map json) {
71 | badge = json['badge'] == '' ? null : json['badge'];
72 | badgeType = json['badge_type'];
73 | cover = json['cover'];
74 | indexShow = json['index_show'];
75 | isFinish = json['is_finish'];
76 | link = json['link'];
77 | mediaId = json['media_id'];
78 | order = json['order'];
79 | orderType = json['order_type'];
80 | score = json['score'];
81 | seasonId = json['season_id'];
82 | seaconStatus = json['seacon_status'];
83 | seasonType = json['season_type'];
84 | subTitle = json['sub_title'];
85 | title = json['title'];
86 | titleIcon = json['title_icon'];
87 |
88 | progress = json['progress'];
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/lib/bean/danmaku/dm.pbserver.dart:
--------------------------------------------------------------------------------
1 | ///
2 | // Generated code. Do not modify.
3 | // source: dm.proto
4 | //
5 | // @dart = 2.12
6 | // ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,deprecated_member_use_from_same_package,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name, avoid_renaming_method_parameters
7 |
8 | import 'dart:async' as $async;
9 |
10 | import 'package:protobuf/protobuf.dart' as $pb;
11 |
12 | import 'dart:core' as $core;
13 | import 'dm.pb.dart' as $0;
14 | import 'dm.pbjson.dart';
15 |
16 | export 'dm.pb.dart';
17 |
18 | abstract class DMServiceBase extends $pb.GeneratedService {
19 | $async.Future<$0.DmSegMobileReply> dmSegMobile(
20 | $pb.ServerContext ctx, $0.DmSegMobileReq request);
21 | $async.Future<$0.DmViewReply> dmView(
22 | $pb.ServerContext ctx, $0.DmViewReq request);
23 | $async.Future<$0.Response> dmPlayerConfig(
24 | $pb.ServerContext ctx, $0.DmPlayerConfigReq request);
25 | $async.Future<$0.DmSegOttReply> dmSegOtt(
26 | $pb.ServerContext ctx, $0.DmSegOttReq request);
27 | $async.Future<$0.DmSegSDKReply> dmSegSDK(
28 | $pb.ServerContext ctx, $0.DmSegSDKReq request);
29 | $async.Future<$0.DmExpoReportRes> dmExpoReport(
30 | $pb.ServerContext ctx, $0.DmExpoReportReq request);
31 |
32 | $pb.GeneratedMessage createRequest($core.String method) {
33 | switch (method) {
34 | case 'DmSegMobile':
35 | return $0.DmSegMobileReq();
36 | case 'DmView':
37 | return $0.DmViewReq();
38 | case 'DmPlayerConfig':
39 | return $0.DmPlayerConfigReq();
40 | case 'DmSegOtt':
41 | return $0.DmSegOttReq();
42 | case 'DmSegSDK':
43 | return $0.DmSegSDKReq();
44 | case 'DmExpoReport':
45 | return $0.DmExpoReportReq();
46 | default:
47 | throw $core.ArgumentError('Unknown method: $method');
48 | }
49 | }
50 |
51 | $async.Future<$pb.GeneratedMessage> handleCall($pb.ServerContext ctx,
52 | $core.String method, $pb.GeneratedMessage request) {
53 | switch (method) {
54 | case 'DmSegMobile':
55 | return this.dmSegMobile(ctx, request as $0.DmSegMobileReq);
56 | case 'DmView':
57 | return this.dmView(ctx, request as $0.DmViewReq);
58 | case 'DmPlayerConfig':
59 | return this.dmPlayerConfig(ctx, request as $0.DmPlayerConfigReq);
60 | case 'DmSegOtt':
61 | return this.dmSegOtt(ctx, request as $0.DmSegOttReq);
62 | case 'DmSegSDK':
63 | return this.dmSegSDK(ctx, request as $0.DmSegSDKReq);
64 | case 'DmExpoReport':
65 | return this.dmExpoReport(ctx, request as $0.DmExpoReportReq);
66 | default:
67 | throw $core.ArgumentError('Unknown method: $method');
68 | }
69 | }
70 |
71 | $core.Map<$core.String, $core.dynamic> get $json => DMServiceBase$json;
72 | $core.Map<$core.String, $core.Map<$core.String, $core.dynamic>>
73 | get $messageJson => DMServiceBase$messageJson;
74 | }
75 |
--------------------------------------------------------------------------------
/lib/bean/settings/settings.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
3 | import 'package:hive/hive.dart';
4 | import 'package:bilineo/utils/storage.dart';
5 | import 'package:bilineo/utils/utils.dart';
6 |
7 | class SetSwitchItem extends StatefulWidget {
8 | final String? title;
9 | final String? subTitle;
10 | final String? setKey;
11 | final bool? defaultVal;
12 | final Function? callFn;
13 | final bool? needReboot;
14 |
15 | const SetSwitchItem({
16 | this.title,
17 | this.subTitle,
18 | this.setKey,
19 | this.defaultVal,
20 | this.callFn,
21 | this.needReboot,
22 | Key? key,
23 | }) : super(key: key);
24 |
25 | @override
26 | State createState() => _SetSwitchItemState();
27 | }
28 |
29 | class _SetSwitchItemState extends State {
30 | // ignore: non_constant_identifier_names
31 | Box Setting = GStorage.setting;
32 | late bool val;
33 |
34 | @override
35 | void initState() {
36 | super.initState();
37 | val = Setting.get(widget.setKey, defaultValue: widget.defaultVal ?? false);
38 | }
39 |
40 | void switchChange(value) async {
41 | val = value ?? !val;
42 | await Setting.put(widget.setKey, val);
43 | if (widget.setKey == SettingBoxKey.autoUpdate && value == true) {
44 | Utils.checkUpdata();
45 | }
46 | if (widget.callFn != null) {
47 | widget.callFn!.call(val);
48 | }
49 | if (widget.needReboot != null && widget.needReboot!) {
50 | SmartDialog.showToast('重启生效');
51 | }
52 | setState(() {});
53 | }
54 |
55 | @override
56 | Widget build(BuildContext context) {
57 | TextStyle titleStyle = Theme.of(context).textTheme.titleMedium!;
58 | TextStyle subTitleStyle = Theme.of(context)
59 | .textTheme
60 | .labelMedium!
61 | .copyWith(color: Theme.of(context).colorScheme.outline);
62 | return ListTile(
63 | enableFeedback: true,
64 | onTap: () => switchChange(null),
65 | title: Text(widget.title!, style: titleStyle),
66 | subtitle: widget.subTitle != null
67 | ? Text(widget.subTitle!, style: subTitleStyle)
68 | : null,
69 | trailing: Transform.scale(
70 | alignment: Alignment.centerRight, // 缩放Switch的大小后保持右侧对齐, 避免右侧空隙过大
71 | scale: 0.8,
72 | child: Switch(
73 | value: val,
74 | onChanged: (val) => switchChange(val),
75 | ),
76 | ),
77 | );
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/lib/main.dart:
--------------------------------------------------------------------------------
1 | import 'dart:io';
2 |
3 | import 'package:flutter/material.dart';
4 | import 'package:bilineo/app_module.dart';
5 | import 'package:bilineo/app_widget.dart';
6 | import 'package:flutter_modular/flutter_modular.dart';
7 | import 'package:media_kit/media_kit.dart';
8 | import 'package:bilineo/utils/storage.dart';
9 | import 'package:bilineo/request/request.dart';
10 | import 'package:window_manager/window_manager.dart';
11 | // import 'package:webview_flutter/webview_flutter.dart';
12 |
13 | void main() async {
14 | WidgetsFlutterBinding.ensureInitialized();
15 | if (Platform.isWindows || Platform.isLinux) {
16 | await windowManager.ensureInitialized();
17 | WindowOptions windowOptions = const WindowOptions(
18 | size: Size(1280, 830),
19 | center: true,
20 | // backgroundColor: Colors.white,
21 | skipTaskbar: false,
22 | titleBarStyle: TitleBarStyle.normal,
23 | );
24 | windowManager.waitUntilReadyToShow(windowOptions, () async {
25 | await windowManager.show();
26 | await windowManager.focus();
27 | });
28 | // windowManager.setMaximizable(false);
29 | }
30 | MediaKit.ensureInitialized();
31 | await GStorage.init();
32 | Request();
33 | await Request.setCookie();
34 | runApp(ModularApp(
35 | module: AppModule(),
36 | child: const AppWidget(),
37 | ));
38 | }
39 |
--------------------------------------------------------------------------------
/lib/pages/card/pbadge.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class PBadge extends StatelessWidget {
4 | final String? text;
5 | final double? top;
6 | final double? right;
7 | final double? bottom;
8 | final double? left;
9 | final String? type;
10 | final String? size;
11 | final String? stack;
12 | final double? fs;
13 |
14 | const PBadge({
15 | super.key,
16 | this.text,
17 | this.top,
18 | this.right,
19 | this.bottom,
20 | this.left,
21 | this.type = 'primary',
22 | this.size = 'medium',
23 | this.stack = 'position',
24 | this.fs = 11,
25 | });
26 |
27 | @override
28 | Widget build(BuildContext context) {
29 | ColorScheme t = Theme.of(context).colorScheme;
30 | // 背景色
31 | Color bgColor = t.primary;
32 | // 前景色
33 | Color color = t.onPrimary;
34 | // 边框色
35 | Color borderColor = Colors.transparent;
36 | if (type == 'gray') {
37 | bgColor = Colors.black54.withOpacity(0.4);
38 | color = Colors.white;
39 | }
40 | if (type == 'color') {
41 | bgColor = t.primaryContainer.withOpacity(0.6);
42 | color = t.primary;
43 | }
44 | if (type == 'line') {
45 | bgColor = Colors.transparent;
46 | color = t.primary;
47 | borderColor = t.primary;
48 | }
49 |
50 | EdgeInsets paddingStyle =
51 | const EdgeInsets.symmetric(vertical: 1, horizontal: 6);
52 | double fontSize = 11;
53 | BorderRadius br = BorderRadius.circular(4);
54 |
55 | if (size == 'small') {
56 | paddingStyle = const EdgeInsets.symmetric(vertical: 0, horizontal: 3);
57 | fontSize = 11;
58 | br = BorderRadius.circular(3);
59 | }
60 |
61 | Widget content = Container(
62 | padding: paddingStyle,
63 | decoration: BoxDecoration(
64 | borderRadius: br,
65 | color: bgColor,
66 | border: Border.all(color: borderColor),
67 | ),
68 | child: Text(
69 | text!,
70 | style: TextStyle(fontSize: fs ?? fontSize, color: color),
71 | ),
72 | );
73 | if (stack == 'position') {
74 | return Positioned(
75 | top: top,
76 | left: left,
77 | right: right,
78 | bottom: bottom,
79 | child: content,
80 | );
81 | } else {
82 | return Padding(
83 | padding: const EdgeInsets.only(right: 5),
84 | child: content,
85 | );
86 | }
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/lib/pages/danmaku/controller.dart:
--------------------------------------------------------------------------------
1 | import 'package:bilineo/request/danmaku.dart';
2 | import 'package:bilineo/bean/danmaku/dm.pb.dart';
3 |
4 | class PlDanmakuController {
5 | PlDanmakuController(this.cid);
6 | final int cid;
7 | Map> dmSegMap = {};
8 | // 已请求的段落标记
9 | List requestedSeg = [];
10 |
11 | bool get initiated => requestedSeg.isNotEmpty;
12 |
13 | static int segmentLength = 60 * 6 * 1000;
14 |
15 | void initiate(int videoDuration, int progress) {
16 | if (requestedSeg.isEmpty) {
17 | int segCount = (videoDuration / segmentLength).ceil();
18 | requestedSeg = List.generate(segCount, (index) => false);
19 | }
20 | queryDanmaku(calcSegment(progress));
21 | }
22 |
23 | void dispose() {
24 | dmSegMap.clear();
25 | requestedSeg.clear();
26 | }
27 |
28 | int calcSegment(int progress) {
29 | return progress ~/ segmentLength;
30 | }
31 |
32 | void queryDanmaku(int segmentIndex) async {
33 | assert(requestedSeg[segmentIndex] == false);
34 | requestedSeg[segmentIndex] = true;
35 | final DmSegMobileReply result = await DanmakaHttp.queryDanmaku(
36 | cid: cid, segmentIndex: segmentIndex + 1);
37 | if (result.elems.isNotEmpty) {
38 | for (var element in result.elems) {
39 | int pos = element.progress ~/ 100; //每0.1秒存储一次
40 | if (dmSegMap[pos] == null) {
41 | dmSegMap[pos] = [];
42 | }
43 | dmSegMap[pos]!.add(element);
44 | }
45 | }
46 | }
47 |
48 | List? getCurrentDanmaku(int progress) {
49 | int segmentIndex = calcSegment(progress);
50 | if (!requestedSeg[segmentIndex]) {
51 | queryDanmaku(segmentIndex);
52 | }
53 | return dmSegMap[progress ~/ 100];
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/lib/pages/error/http_error.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class HttpError extends StatelessWidget {
4 | const HttpError(
5 | {required this.errMsg, required this.fn, this.btnText, super.key});
6 |
7 | final String? errMsg;
8 | final Function()? fn;
9 | final String? btnText;
10 |
11 | @override
12 | Widget build(BuildContext context) {
13 | return SliverToBoxAdapter(
14 | child: SizedBox(
15 | height: 400,
16 | child: Column(
17 | crossAxisAlignment: CrossAxisAlignment.center,
18 | mainAxisAlignment: MainAxisAlignment.center,
19 | children: [
20 | const SizedBox(height: 30),
21 | Text(
22 | errMsg ?? '请求异常',
23 | textAlign: TextAlign.center,
24 | style: Theme.of(context).textTheme.titleSmall,
25 | ),
26 | const SizedBox(height: 20),
27 | FilledButton.tonal(
28 | onPressed: () {
29 | fn!();
30 | },
31 | style: ButtonStyle(
32 | backgroundColor: MaterialStateProperty.resolveWith((states) {
33 | return Theme.of(context).colorScheme.primary.withAlpha(20);
34 | }),
35 | ),
36 | child: Text(
37 | btnText ?? '点击重试',
38 | style: TextStyle(color: Theme.of(context).colorScheme.primary),
39 | ),
40 | ),
41 | ],
42 | ),
43 | ),
44 | );
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/lib/pages/index_module.dart:
--------------------------------------------------------------------------------
1 | import 'package:bilineo/pages/index_page.dart';
2 | import 'package:flutter_modular/flutter_modular.dart';
3 | import 'package:bilineo/pages/router.dart';
4 | import 'package:bilineo/pages/init_page.dart';
5 | import 'package:flutter/material.dart';
6 | import 'package:bilineo/pages/popular/popular_controller.dart';
7 | import 'package:bilineo/pages/video/video_controller.dart';
8 | import 'package:bilineo/pages/my/my_controller.dart';
9 | import 'package:bilineo/pages/my/user_info.dart';
10 | import 'package:bilineo/pages/webview/webview_controller.dart';
11 | import 'package:bilineo/pages/search/search_controller.dart';
12 | import 'package:bilineo/pages/search_result/results_controller.dart';
13 |
14 |
15 | class IndexModule extends Module {
16 | @override
17 | List get imports => menu.moduleList;
18 |
19 | @override
20 | void binds(i) {
21 | i.addSingleton(PopularController.new);
22 | i.addSingleton(VideoController.new);
23 | i.addSingleton(MyController.new);
24 | i.addSingleton(UserInfoData.new);
25 | i.addSingleton(WebviewController.new);
26 | i.addSingleton(MySearchController.new);
27 | i.addSingleton(SearchResultController.new);
28 | // i.addSingleton(GStorage.new);
29 | // i.addSingleton(PlayerController.new);
30 | }
31 |
32 | @override
33 | void routes(r) {
34 | r.child("/",
35 | child: (_) => const InitPage(),
36 | children: [
37 | ChildRoute(
38 | "/error",
39 | child: (_) => Scaffold(
40 | appBar: AppBar (title: const Text("BiliNeo")),
41 | body: const Center(child: Text("初始化失败")),
42 | ),
43 | ),
44 | ],
45 | transition: TransitionType.noTransition);
46 | r.child("/tab", child: (_) {
47 | return const IndexPage();
48 | }, children: menu.routes, transition: TransitionType.noTransition);
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/lib/pages/index_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_modular/flutter_modular.dart';
3 | import 'package:bilineo/pages/menu/menu.dart';
4 |
5 |
6 | class IndexPage extends StatefulWidget {
7 | //const IndexPage({super.key});
8 | const IndexPage({Key? key}) : super(key: key);
9 |
10 | @override
11 | State createState() => _IndexPageState();
12 | }
13 |
14 | class _IndexPageState extends State with WidgetsBindingObserver {
15 |
16 | final PageController _page = PageController();
17 |
18 | /// 统一处理前后台改变
19 | void appListener(bool state) {
20 | if (state) {
21 | print("应用前台");
22 | } else {
23 | print("应用后台");
24 | }
25 | }
26 |
27 | @override
28 | Widget build(BuildContext context) {
29 | // return Row(children: [
30 | // AppDrawer(page: _page),
31 | // Expanded(
32 | // child: PageView.builder(
33 | // controller: _page,
34 | // itemCount: menu.size,
35 | // onPageChanged: (i) => Modular.to.navigate("/tab${menu.getPath(i)}/"),
36 | // itemBuilder: (_, __) => const RouterOutlet(),
37 | // ),
38 | // )
39 | // ]);
40 | return const BottomMenu();
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/lib/pages/init_page.dart:
--------------------------------------------------------------------------------
1 | import 'dart:io';
2 |
3 | import 'package:flutter/material.dart';
4 | import 'package:flutter_modular/flutter_modular.dart';
5 |
6 |
7 |
8 | class InitPage extends StatefulWidget {
9 | const InitPage({super.key});
10 |
11 | @override
12 | State createState() => _InitPageState();
13 | }
14 |
15 | class _InitPageState extends State {
16 |
17 | @override
18 | void initState() {
19 | _init();
20 | super.initState();
21 | }
22 |
23 | _init() {
24 | Modular.to.navigate('/tab/popular/');
25 | }
26 |
27 | @override
28 | Widget build(BuildContext context) {
29 | return const RouterOutlet();
30 | }
31 | }
32 |
33 | class LoadingWidget extends StatelessWidget {
34 | const LoadingWidget({super.key, required this.value});
35 |
36 | final double value;
37 |
38 | @override
39 | Widget build(BuildContext context) {
40 | var size = MediaQuery.of(context).size;
41 | return Scaffold(
42 | appBar: AppBar(title: const Text("BiliNeo")),
43 | body: Center(
44 | child: SizedBox(
45 | height: 200,
46 | child: Flex(
47 | direction: Axis.vertical,
48 | mainAxisAlignment: MainAxisAlignment.spaceEvenly,
49 | children: [
50 | SizedBox(
51 | width: size.width * 0.6,
52 | child: LinearProgressIndicator(
53 | value: value,
54 | backgroundColor: Colors.black12,
55 | minHeight: 10,
56 | ),
57 | ),
58 | const Text("初始化中"),
59 | ],
60 | ),
61 | ),
62 | ),
63 | );
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/lib/pages/my/my_controller.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'my_controller.dart';
4 |
5 | // **************************************************************************
6 | // StoreGenerator
7 | // **************************************************************************
8 |
9 | // ignore_for_file: non_constant_identifier_names, unnecessary_brace_in_string_interps, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic, no_leading_underscores_for_local_identifiers
10 |
11 | mixin _$MyController on _MyController, Store {
12 | late final _$userLoginAtom =
13 | Atom(name: '_MyController.userLogin', context: context);
14 |
15 | @override
16 | bool get userLogin {
17 | _$userLoginAtom.reportRead();
18 | return super.userLogin;
19 | }
20 |
21 | @override
22 | set userLogin(bool value) {
23 | _$userLoginAtom.reportWrite(value, super.userLogin, () {
24 | super.userLogin = value;
25 | });
26 | }
27 |
28 | late final _$userFaceAtom =
29 | Atom(name: '_MyController.userFace', context: context);
30 |
31 | @override
32 | String get userFace {
33 | _$userFaceAtom.reportRead();
34 | return super.userFace;
35 | }
36 |
37 | @override
38 | set userFace(String value) {
39 | _$userFaceAtom.reportWrite(value, super.userFace, () {
40 | super.userFace = value;
41 | });
42 | }
43 |
44 | late final _$unameAtom = Atom(name: '_MyController.uname', context: context);
45 |
46 | @override
47 | String get uname {
48 | _$unameAtom.reportRead();
49 | return super.uname;
50 | }
51 |
52 | @override
53 | set uname(String value) {
54 | _$unameAtom.reportWrite(value, super.uname, () {
55 | super.uname = value;
56 | });
57 | }
58 |
59 | late final _$currentLevelAtom =
60 | Atom(name: '_MyController.currentLevel', context: context);
61 |
62 | @override
63 | int get currentLevel {
64 | _$currentLevelAtom.reportRead();
65 | return super.currentLevel;
66 | }
67 |
68 | @override
69 | set currentLevel(int value) {
70 | _$currentLevelAtom.reportWrite(value, super.currentLevel, () {
71 | super.currentLevel = value;
72 | });
73 | }
74 |
75 | @override
76 | String toString() {
77 | return '''
78 | userLogin: ${userLogin},
79 | userFace: ${userFace},
80 | uname: ${uname},
81 | currentLevel: ${currentLevel}
82 | ''';
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/lib/pages/my/my_module.dart:
--------------------------------------------------------------------------------
1 | import 'package:bilineo/pages/my/my_page.dart';
2 | import 'package:flutter_modular/flutter_modular.dart';
3 | // import 'package:bilineo/pages/webview/webview_controller.dart';
4 | // import 'package:bilineo/pages/my/my_controller.dart';
5 |
6 | class MyModule extends Module {
7 | @override
8 | void binds(i) {
9 | // i.addSingleton(WebviewController.new);
10 | // i.addSingleton(MyController.new);
11 | }
12 |
13 | @override
14 | void routes(r) {
15 | r.child("/", child: (_) => const MyPage());
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/lib/pages/player/play_quality.dart:
--------------------------------------------------------------------------------
1 | // ignore_for_file: constant_identifier_names
2 |
3 | enum VideoQuality {
4 | speed240,
5 | flunt360,
6 | clear480,
7 | high720,
8 | high72060,
9 | high1080,
10 | high1080plus,
11 | high108060,
12 | super4K,
13 | hdr,
14 | dolbyVision,
15 | super8k
16 | }
17 |
18 | extension VideoQualityCode on VideoQuality {
19 | static final List _codeList = [
20 | 6,
21 | 16,
22 | 32,
23 | 64,
24 | 74,
25 | 80,
26 | 112,
27 | 116,
28 | 120,
29 | 125,
30 | 126,
31 | 127,
32 | ];
33 | int get code => _codeList[index];
34 |
35 | static VideoQuality? fromCode(int code) {
36 | final index = _codeList.indexOf(code);
37 | if (index != -1) {
38 | return VideoQuality.values[index];
39 | }
40 | return null;
41 | }
42 | }
43 |
44 | extension VideoQualityDesc on VideoQuality {
45 | static final List _descList = [
46 | '240P 极速',
47 | '360P 流畅',
48 | '480P 清晰',
49 | '720P 高清',
50 | '720P60 高帧率',
51 | '1080P 高清',
52 | '1080P+ 高码率',
53 | '1080P60 高帧率',
54 | '4K 超清',
55 | 'HDR 真彩色',
56 | '杜比视界',
57 | '8K 超高清'
58 | ];
59 | get description => _descList[index];
60 | }
61 |
62 | ///
63 | enum AudioQuality { k64, k132, k192, dolby, hiRes }
64 |
65 | extension AudioQualityCode on AudioQuality {
66 | static final List _codeList = [
67 | 30216,
68 | 30232,
69 | 30280,
70 | 30250,
71 | 30251,
72 | ];
73 | int get code => _codeList[index];
74 |
75 | static AudioQuality? fromCode(int code) {
76 | final index = _codeList.indexOf(code);
77 | if (index != -1) {
78 | return AudioQuality.values[index];
79 | }
80 | return null;
81 | }
82 | }
83 |
84 | extension AudioQualityDesc on AudioQuality {
85 | static final List _descList = [
86 | '64K',
87 | '132K',
88 | '192K',
89 | '杜比全景声',
90 | 'Hi-Res无损',
91 | ];
92 | get description => _descList[index];
93 | }
94 |
95 | enum VideoDecodeFormats {
96 | DVH1,
97 | AV1,
98 | HEVC,
99 | AVC,
100 | }
101 |
102 | extension VideoDecodeFormatsDesc on VideoDecodeFormats {
103 | static final List _descList = ['DVH1', 'AV1', 'HEVC', 'AVC'];
104 | get description => _descList[index];
105 | }
106 |
107 | extension VideoDecodeFormatsCode on VideoDecodeFormats {
108 | static final List _codeList = ['dvh1', 'av01', 'hev1', 'avc1'];
109 | get code => _codeList[index];
110 |
111 | static VideoDecodeFormats? fromCode(String code) {
112 | final index = _codeList.indexOf(code);
113 | if (index != -1) {
114 | return VideoDecodeFormats.values[index];
115 | }
116 | return null;
117 | }
118 |
119 | static VideoDecodeFormats? fromString(String val) {
120 | var result = VideoDecodeFormats.values.first;
121 | for (var i in _codeList) {
122 | if (val.startsWith(i)) {
123 | result = VideoDecodeFormats.values[_codeList.indexOf(i)];
124 | break;
125 | }
126 | }
127 | return result;
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/lib/pages/player/player_datasource.dart:
--------------------------------------------------------------------------------
1 | import 'dart:io';
2 |
3 | /// The way in which the video was originally loaded.
4 | ///
5 | /// This has nothing to do with the video's file type. It's just the place
6 | /// from which the video is fetched from.
7 | enum DataSourceType {
8 | /// The video was included in the app's asset files.
9 | asset,
10 |
11 | /// The video was downloaded from the internet.
12 | network,
13 |
14 | /// The video was loaded off of the local filesystem.
15 | file,
16 |
17 | /// The video is available via contentUri. Android only.
18 | contentUri,
19 | }
20 |
21 | class DataSource {
22 | File? file;
23 | String? videoSource;
24 | String? audioSource;
25 | String? subFiles;
26 | DataSourceType type;
27 | Map? httpHeaders; // for headers
28 | DataSource({
29 | this.file,
30 | this.videoSource,
31 | this.audioSource,
32 | this.subFiles,
33 | required this.type,
34 | this.httpHeaders,
35 | }) : assert((type == DataSourceType.file && file != null) ||
36 | videoSource != null);
37 |
38 | DataSource copyWith({
39 | File? file,
40 | String? videoSource,
41 | String? audioSource,
42 | String? subFiles,
43 | DataSourceType? type,
44 | Map? httpHeaders,
45 | }) {
46 | return DataSource(
47 | file: file ?? this.file,
48 | videoSource: videoSource ?? this.videoSource,
49 | audioSource: audioSource ?? this.audioSource,
50 | subFiles: subFiles ?? this.subFiles,
51 | type: type ?? this.type,
52 | httpHeaders: httpHeaders ?? this.httpHeaders,
53 | );
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/lib/pages/player/player_item.dart:
--------------------------------------------------------------------------------
1 | import 'dart:io';
2 |
3 | import 'package:bilineo/pages/player/player_controller.dart';
4 | import 'package:flutter/material.dart';
5 | import 'package:flutter_mobx/flutter_mobx.dart';
6 | import 'package:media_kit/media_kit.dart';
7 | import 'package:media_kit_video/media_kit_video.dart';
8 | import 'package:flutter_modular/flutter_modular.dart';
9 | import 'package:bilineo/pages/video/video_controller.dart' as videoPage;
10 |
11 | class PlayerItem extends StatefulWidget {
12 | const PlayerItem({super.key});
13 |
14 | @override
15 | State createState() => _PlayerItemState();
16 | }
17 |
18 | class _PlayerItemState extends State {
19 | final PlayerController playerController = Modular.get();
20 | final videoPage.VideoController videoPageController = Modular.get();
21 | @override
22 | void initState() {
23 | super.initState();
24 | // debugPrint('在小部件中初始化');
25 | // playerController.init;
26 | }
27 |
28 | @override
29 | void dispose() {
30 | //player.dispose();
31 | super.dispose();
32 | }
33 |
34 | @override
35 | Widget build(BuildContext context) {
36 | debugPrint('播放器获取到的bv是 ${playerController.bvid}');
37 | return Column(
38 | children: [
39 | // const Text('Video Player Test'),
40 | Observer(builder: (context) {
41 | return SizedBox(
42 | width: Platform.isWindows || Platform.isLinux
43 | ? MediaQuery.of(context).size.width
44 | : ((!videoPageController.androidFullscreen) ? MediaQuery.of(context).size.width : (MediaQuery.of(context).size.height * 16.0 / 9.0)),
45 | height: Platform.isWindows || Platform.isLinux
46 | ? (MediaQuery.of(context).size.width * 9.0 / (16.0))
47 | : ((!videoPageController.androidFullscreen) ? MediaQuery.of(context).size.width * 9.0 / 16.0 : MediaQuery.of(context).size.height),
48 | child: playerController.dataStatus == 'loaded'
49 | ? Video(
50 | controller: playerController.videoController,
51 | subtitleViewConfiguration: SubtitleViewConfiguration(
52 | style: TextStyle(
53 | color: Colors.pink, // 深粉色字体
54 | fontSize: 48.0, // 较大的字号
55 | background: Paint()..color = Colors.transparent, // 背景透明
56 | decoration: TextDecoration.none, // 无下划线
57 | fontWeight: FontWeight.bold, // 字体加粗
58 | shadows: const [
59 | // 显眼的包边
60 | Shadow(
61 | offset: Offset(1.0, 1.0),
62 | blurRadius: 3.0,
63 | color: Color.fromARGB(255, 255, 255, 255),
64 | ),
65 | Shadow(
66 | offset: Offset(-1.0, -1.0),
67 | blurRadius: 3.0,
68 | color: Color.fromARGB(125, 255, 255, 255),
69 | ),
70 | ],
71 | ),
72 | textAlign: TextAlign.center,
73 | padding: const EdgeInsets.all(24.0),
74 | ),
75 | )
76 | : SizedBox(
77 | child: Center(
78 | child: CircularProgressIndicator(),
79 | ),
80 | height: Platform.isWindows || Platform.isLinux
81 | ? MediaQuery.of(context).size.width * 9.0 / 32.0
82 | : MediaQuery.of(context).size.width * 9.0 / 16.0,
83 | width: Platform.isWindows || Platform.isLinux
84 | ? MediaQuery.of(context).size.width / 2
85 | : MediaQuery.of(context).size.width,
86 | ),
87 | );
88 | }),
89 | ],
90 | );
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/lib/pages/popular/popular_controller.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_modular/flutter_modular.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:bilineo/request/bangumi.dart';
4 | import 'package:bilineo/bean/bangumi/bangumi_list.dart';
5 | import 'package:mobx/mobx.dart';
6 |
7 | part 'popular_controller.g.dart';
8 |
9 | class PopularController = _PopularController with _$PopularController;
10 |
11 | abstract class _PopularController with Store {
12 | final ScrollController scrollController = ScrollController();
13 |
14 | @observable
15 | ObservableList bangumiList = ObservableList.of([BangumiListItemModel()]);
16 |
17 | List _items = [];
18 | List get items => _items;
19 | List get listValue => bangumiList.toList();
20 |
21 | int _currentPage = 1;
22 | double scrollOffset = 0.0;
23 | bool isLoadingMore = true;
24 |
25 | @action
26 | Future queryBangumiListFeed({type = 'init'}) async {
27 | if (type == 'init') {
28 | _currentPage = 1;
29 | }
30 | var result = await BangumiHttp.bangumiList(page: _currentPage);
31 | if (result['status']) {
32 | if (type == 'init') {
33 | bangumiList.clear();
34 | }
35 | bangumiList.addAll(result['data'].list);
36 | _currentPage += 1;
37 | } else {}
38 | isLoadingMore = false;
39 | return result;
40 | }
41 |
42 | Future onLoad() async {
43 | debugPrint('Popular正在加载更多内容');
44 | queryBangumiListFeed(type: 'onLoad');
45 | }
46 | }
--------------------------------------------------------------------------------
/lib/pages/popular/popular_controller.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'popular_controller.dart';
4 |
5 | // **************************************************************************
6 | // StoreGenerator
7 | // **************************************************************************
8 |
9 | // ignore_for_file: non_constant_identifier_names, unnecessary_brace_in_string_interps, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic, no_leading_underscores_for_local_identifiers
10 |
11 | mixin _$PopularController on _PopularController, Store {
12 | late final _$bangumiListAtom =
13 | Atom(name: '_PopularController.bangumiList', context: context);
14 |
15 | @override
16 | ObservableList get bangumiList {
17 | _$bangumiListAtom.reportRead();
18 | return super.bangumiList;
19 | }
20 |
21 | @override
22 | set bangumiList(ObservableList value) {
23 | _$bangumiListAtom.reportWrite(value, super.bangumiList, () {
24 | super.bangumiList = value;
25 | });
26 | }
27 |
28 | late final _$queryBangumiListFeedAsyncAction =
29 | AsyncAction('_PopularController.queryBangumiListFeed', context: context);
30 |
31 | @override
32 | Future queryBangumiListFeed({dynamic type = 'init'}) {
33 | return _$queryBangumiListFeedAsyncAction
34 | .run(() => super.queryBangumiListFeed(type: type));
35 | }
36 |
37 | @override
38 | String toString() {
39 | return '''
40 | bangumiList: ${bangumiList}
41 | ''';
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/lib/pages/popular/popular_module.dart:
--------------------------------------------------------------------------------
1 | import 'package:bilineo/pages/popular/popular_page.dart';
2 | import 'package:flutter_modular/flutter_modular.dart';
3 | import 'package:bilineo/pages/popular/popular_controller.dart';
4 |
5 | class PopularModule extends Module {
6 | @override
7 | void binds(i) {
8 | //i.addSingleton(PopularController.new);
9 | }
10 |
11 | @override
12 | void routes(r) {
13 | r.child("/", child: (_) => const PopularPage());
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/lib/pages/rating/rating_module.dart:
--------------------------------------------------------------------------------
1 | import 'package:bilineo/pages/rating/rating_page.dart';
2 | import 'package:flutter_modular/flutter_modular.dart';
3 |
4 | class RatingModule extends Module {
5 | @override
6 | void routes(r) {
7 | r.child("/", child: (_) => const RatingPage());
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/lib/pages/rating/rating_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_modular/flutter_modular.dart';
3 |
4 | class RatingPage extends StatefulWidget {
5 | const RatingPage({super.key});
6 |
7 | @override
8 | State createState() => _RatingPageState();
9 | }
10 |
11 | class _RatingPageState extends State {
12 |
13 | @override
14 | Widget build(BuildContext context) {
15 | // This method is rerun every time setState is called, for instance as done
16 | // by the _incrementCounter method above.
17 | //
18 | // The Flutter framework has been optimized to make rerunning build methods
19 | // fast, so that you can just rebuild anything that needs updating rather
20 | // than having to individually change instances of widgets.
21 | return Scaffold(
22 | appBar: AppBar(title: const Text('BiliNeo Rating Test Page')),
23 | body: Center(
24 | child: TextButton(
25 | onPressed: () {
26 |
27 | },
28 | child: const Text('测试'),
29 | ),
30 | ),
31 | );
32 | }
33 | }
--------------------------------------------------------------------------------
/lib/pages/router.dart:
--------------------------------------------------------------------------------
1 | import 'package:bilineo/pages/my/my_module.dart';
2 | import 'package:bilineo/pages/rating/rating_module.dart';
3 | import 'package:bilineo/pages/popular/popular_module.dart';
4 | import 'package:bilineo/pages/video/video_module.dart';
5 | import 'package:flutter_modular/flutter_modular.dart';
6 | import 'package:bilineo/pages/webview/webview_module.dart';
7 | import 'package:bilineo/pages/webview_desktop/webview_desktop_module.dart';
8 | import 'package:bilineo/pages/search/search_module.dart';
9 | import 'package:bilineo/pages/search_result/results_module.dart';
10 |
11 | class MenuRouteItem {
12 | final String path;
13 | final Module module;
14 |
15 | const MenuRouteItem({
16 | required this.path,
17 | required this.module,
18 | });
19 | }
20 |
21 | class MenuRoute {
22 | final List menuList;
23 |
24 | const MenuRoute(this.menuList);
25 |
26 | int get size => menuList.length;
27 |
28 | List get moduleList {
29 | return menuList.map((e) => e.module).toList();
30 | }
31 |
32 | List get routes {
33 | return menuList.map((e) => ModuleRoute(e.path, module: e.module)).toList();
34 | }
35 |
36 | getPath(int index) {
37 | return menuList[index].path;
38 | }
39 | }
40 |
41 | final MenuRoute menu = MenuRoute([
42 | MenuRouteItem(
43 | path: "/popular",
44 | module: PopularModule(),
45 | ),
46 | MenuRouteItem(
47 | path: "/rating",
48 | module: RatingModule(),
49 | ),
50 | MenuRouteItem(
51 | path: "/my",
52 | module: MyModule(),
53 | ),
54 | MenuRouteItem(
55 | path: "/video",
56 | module: VideoModule(),
57 | ),
58 | MenuRouteItem(
59 | path: '/webview',
60 | module: WebviewMoudle(),
61 | ),
62 | MenuRouteItem(
63 | path: '/webviewdesktop',
64 | module: WebviewDesktopMoudle(),
65 | ),
66 | MenuRouteItem(
67 | path: '/search',
68 | module: SearchModule(),
69 | ),
70 | MenuRouteItem(
71 | path: '/searchresult',
72 | module: SearchResultModule(),
73 | ),
74 | ]);
75 |
--------------------------------------------------------------------------------
/lib/pages/search/search_controller.dart:
--------------------------------------------------------------------------------
1 | import 'package:bilineo/pages/search/search_suggest.dart';
2 | import 'package:flutter_modular/flutter_modular.dart';
3 | import 'package:mobx/mobx.dart';
4 | import 'package:flutter/material.dart';
5 | import 'package:bilineo/pages/search/search_controller.dart';
6 | import 'package:bilineo/request/search.dart';
7 | import 'package:bilineo/pages/search_result/results_controller.dart';
8 |
9 | part 'search_controller.g.dart';
10 |
11 | class MySearchController = _MySearchController with _$MySearchController;
12 |
13 | abstract class _MySearchController with Store {
14 | @observable
15 | String searchKeyWord = '';
16 |
17 | @observable
18 | List searchSuggestList = [SearchSuggestItem()];
19 |
20 | @observable
21 | TextEditingController controller = TextEditingController();
22 |
23 | void onChange(value) {
24 | searchKeyWord = value;
25 | debugPrint('搜索框内容变动,当前内容 $value');
26 | if (value == '') {
27 | searchSuggestList = [];
28 | return;
29 | }
30 | querySearchSuggest(value);
31 | }
32 |
33 | onSelect(word) {
34 | searchKeyWord = word;
35 | controller.text = word;
36 | submit();
37 | }
38 |
39 | Future querySearchSuggest(String value) async {
40 | var result = await SearchHttp.searchSuggest(term: value);
41 | if (result['status']) {
42 | if (result['data'] is SearchSuggestModel) {
43 | debugPrint('来自服务器的搜索建议响应 ${result['data'].tag}');
44 | searchSuggestList = result['data'].tag;
45 | // return searchSuggestList;
46 | }
47 | }
48 | }
49 |
50 | void onClickKeyword(String keyword) {
51 | searchKeyWord = keyword;
52 | controller.text = keyword;
53 | // 移动光标
54 | controller.selection = TextSelection.fromPosition(
55 | TextPosition(offset: controller.value.text.length),
56 | );
57 | submit();
58 | }
59 |
60 | void submit() {
61 | // ignore: unrelated_type_equality_checks
62 | if (searchKeyWord == '') {
63 | return;
64 | }
65 | debugPrint('Todo 搜索结果 $searchKeyWord');
66 | // final MySearchController mySearchController =
67 | // Modular.get();
68 | // final SearchResultController searchResultController =
69 | // Modular.get();
70 | // searchResultController.searchKeyWord = mySearchController.searchKeyWord;
71 | // searchResultController.onSearch();
72 | Modular.to.navigate('/tab/searchresult/');
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/lib/pages/search/search_controller.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'search_controller.dart';
4 |
5 | // **************************************************************************
6 | // StoreGenerator
7 | // **************************************************************************
8 |
9 | // ignore_for_file: non_constant_identifier_names, unnecessary_brace_in_string_interps, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic, no_leading_underscores_for_local_identifiers
10 |
11 | mixin _$MySearchController on _MySearchController, Store {
12 | late final _$searchKeyWordAtom =
13 | Atom(name: '_MySearchController.searchKeyWord', context: context);
14 |
15 | @override
16 | String get searchKeyWord {
17 | _$searchKeyWordAtom.reportRead();
18 | return super.searchKeyWord;
19 | }
20 |
21 | @override
22 | set searchKeyWord(String value) {
23 | _$searchKeyWordAtom.reportWrite(value, super.searchKeyWord, () {
24 | super.searchKeyWord = value;
25 | });
26 | }
27 |
28 | late final _$searchSuggestListAtom =
29 | Atom(name: '_MySearchController.searchSuggestList', context: context);
30 |
31 | @override
32 | List get searchSuggestList {
33 | _$searchSuggestListAtom.reportRead();
34 | return super.searchSuggestList;
35 | }
36 |
37 | @override
38 | set searchSuggestList(List value) {
39 | _$searchSuggestListAtom.reportWrite(value, super.searchSuggestList, () {
40 | super.searchSuggestList = value;
41 | });
42 | }
43 |
44 | late final _$controllerAtom =
45 | Atom(name: '_MySearchController.controller', context: context);
46 |
47 | @override
48 | TextEditingController get controller {
49 | _$controllerAtom.reportRead();
50 | return super.controller;
51 | }
52 |
53 | @override
54 | set controller(TextEditingController value) {
55 | _$controllerAtom.reportWrite(value, super.controller, () {
56 | super.controller = value;
57 | });
58 | }
59 |
60 | @override
61 | String toString() {
62 | return '''
63 | searchKeyWord: ${searchKeyWord},
64 | searchSuggestList: ${searchSuggestList},
65 | controller: ${controller}
66 | ''';
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/lib/pages/search/search_module.dart:
--------------------------------------------------------------------------------
1 | import 'package:bilineo/pages/search/search_page.dart';
2 | import 'package:flutter_modular/flutter_modular.dart';
3 |
4 | class SearchModule extends Module {
5 | @override
6 | void routes(r) {
7 | r.child("/", child: (_) => const SearchPage());
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/lib/pages/search/search_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_mobx/flutter_mobx.dart';
3 | import 'package:flutter_modular/flutter_modular.dart';
4 | import 'package:bilineo/pages/search/search_controller.dart';
5 | import 'package:provider/provider.dart';
6 | import 'package:bilineo/pages/menu/menu.dart';
7 |
8 | class SearchPage extends StatefulWidget {
9 | const SearchPage({super.key});
10 |
11 | @override
12 | State createState() => _SearchPageState();
13 | }
14 |
15 | class _SearchPageState extends State {
16 | bool isDark = false;
17 | final MySearchController mySearchController =
18 | Modular.get();
19 | late Iterable _lastOptions = [];
20 | FocusNode barFocusNode = FocusNode();
21 | FocusNode suggestFocusNode = FocusNode();
22 |
23 | void onBackPressed(BuildContext context) {
24 | final navigationBarState = Provider.of(context, listen: false);
25 | navigationBarState.showNavigate();
26 | navigationBarState.updateSelectedIndex(0);
27 | Modular.to.navigate('/tab/popular/');
28 | }
29 |
30 | @override
31 | Widget build(BuildContext context) {
32 | return PopScope(
33 | canPop: false,
34 | onPopInvoked: (bool didPop) async {
35 | onBackPressed(context);
36 | },
37 | child: Scaffold(
38 | appBar: AppBar(title: const Text('')),
39 | body: Observer(builder: (context) {
40 | String _searchingWithQuery = mySearchController.searchKeyWord;
41 | return Center(
42 | child: SearchAnchor(
43 | builder: (BuildContext context, SearchController controller) {
44 | return SearchBar(
45 | focusNode: barFocusNode,
46 | controller: controller,
47 | padding: const MaterialStatePropertyAll(
48 | EdgeInsets.symmetric(horizontal: 16.0)),
49 | onTap: () {
50 | debugPrint('搜索框点击事件');
51 | // Panic, maybe due to Focus
52 | controller.openView();
53 | },
54 | // onChanged: (value) {
55 | // debugPrint('搜索建议获得焦点,当前值为 $value');
56 | // mySearchController.querySearchSuggest(controller.text);
57 | // // barFocusNode.requestFocus();
58 | // // suggestFocusNode.requestFocus();
59 | // },
60 | onSubmitted: (value) {
61 | debugPrint('提交 ${controller.text}');
62 | mySearchController.onSelect(controller.text);
63 | },
64 | leading: const Icon(Icons.search),
65 | );
66 | }, suggestionsBuilder:
67 | (BuildContext context, SearchController controller) async {
68 | _searchingWithQuery = controller.text;
69 | if (_searchingWithQuery == '') {
70 | debugPrint('搜索框内容为空');
71 | return [];
72 | }
73 | debugPrint('提交到搜索建议API的搜索内容为 $_searchingWithQuery');
74 | await mySearchController.querySearchSuggest(_searchingWithQuery);
75 |
76 | if (_searchingWithQuery != controller.text) {
77 | return _lastOptions;
78 | }
79 |
80 | _lastOptions = List.generate(
81 | mySearchController.searchSuggestList.length, (int index) {
82 | // debugPrint('搜索框获得焦点');
83 | // barFocusNode.requestFocus();
84 | return ListTile(
85 | focusNode: suggestFocusNode,
86 | title: mySearchController.searchSuggestList[index].textRich,
87 | onTap: () {
88 | controller.text =
89 | mySearchController.searchSuggestList[index].term ?? '';
90 | mySearchController.onSelect(controller.text);
91 | },
92 | );
93 | });
94 |
95 | return _lastOptions;
96 | }),
97 | );
98 | }),
99 | ),
100 | );
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/lib/pages/search/search_suggest.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class SearchSuggestModel {
4 | SearchSuggestModel({
5 | this.tag,
6 | this.term,
7 | });
8 |
9 | List? tag;
10 | String? term;
11 |
12 | SearchSuggestModel.fromJson(Map json) {
13 | tag = json['tag']
14 | .map(
15 | (e) => SearchSuggestItem.fromJson(e, json['term']))
16 | .toList();
17 | }
18 | }
19 |
20 | class SearchSuggestItem {
21 | SearchSuggestItem({
22 | this.value,
23 | this.term,
24 | this.spid,
25 | this.textRich,
26 | });
27 |
28 | String? value;
29 | String? term;
30 | int? spid;
31 | Widget? textRich;
32 |
33 | SearchSuggestItem.fromJson(Map json, String inputTerm) {
34 | value = json['value'];
35 | term = json['term'];
36 | textRich = highlightText(json['name']);
37 | }
38 | }
39 |
40 | Widget highlightText(String str) {
41 | // 创建正则表达式,匹配 ... 格式的文本
42 | RegExp regex = RegExp(r'(.*?)<\/em>');
43 |
44 | // 用于存储每个匹配项的列表
45 | List children = [];
46 |
47 | // 获取所有匹配项
48 | Iterable matches = regex.allMatches(str);
49 |
50 | // 当前索引位置
51 | int currentIndex = 0;
52 |
53 | // 遍历每个匹配项
54 | for (var match in matches) {
55 | // 获取当前匹配项之前的普通文本部分
56 | String normalText = str.substring(currentIndex, match.start);
57 |
58 | // 获取需要高亮显示的文本部分
59 | String highlightedText = match.group(1)!;
60 |
61 | // 如果普通文本部分不为空,则将其添加到 children 列表中
62 | if (normalText.isNotEmpty) {
63 | children.add(TextSpan(
64 | text: normalText,
65 | ));
66 | }
67 |
68 | // 将需要高亮显示的文本部分添加到 children 列表中,并设置相应样式
69 | children.add(TextSpan(
70 | text: highlightedText,
71 | style: TextStyle(
72 | fontWeight: FontWeight.bold,
73 | ),
74 | ));
75 |
76 | // 更新当前索引位置
77 | currentIndex = match.end;
78 | }
79 |
80 | // 如果当前索引位置小于文本长度,表示还有剩余的普通文本部分
81 | if (currentIndex < str.length) {
82 | String remainingText = str.substring(currentIndex);
83 |
84 | // 将剩余的普通文本部分添加到 children 列表中
85 | children.add(TextSpan(
86 | text: remainingText,
87 | ));
88 | }
89 |
90 | // 使用 Text.rich 创建包含高亮显示的富文本小部件,并返回
91 | return Text.rich(TextSpan(children: children));
92 | }
93 |
--------------------------------------------------------------------------------
/lib/pages/search/search_type.dart:
--------------------------------------------------------------------------------
1 | // ignore_for_file: constant_identifier_names
2 | enum SearchType {
3 | // 视频:video
4 | video,
5 | // 番剧:media_bangumi,
6 | media_bangumi,
7 | // 影视:media_ft
8 | // media_ft,
9 | // 直播间及主播:live
10 | // live,
11 | // 直播间:live_room
12 | live_room,
13 | // 主播:live_user
14 | // live_user,
15 | // 话题:topic
16 | // topic,
17 | // 用户:bili_user
18 | bili_user,
19 | // 专栏:article
20 | article,
21 | // 相簿:photo
22 | // photo
23 | }
24 |
25 | extension SearchTypeExtension on SearchType {
26 | String get type =>
27 | ['video', 'media_bangumi', 'live_room', 'bili_user', 'article'][index];
28 | String get label => ['视频', '番剧', '直播间', '用户', '专栏'][index];
29 | }
30 |
31 | // 搜索类型为视频、专栏及相簿时
32 | enum ArchiveFilterType {
33 | totalrank,
34 | click,
35 | pubdate,
36 | dm,
37 | stow,
38 | scores,
39 | // 专栏
40 | // attention,
41 | }
42 |
43 | extension ArchiveFilterTypeExtension on ArchiveFilterType {
44 | String get description =>
45 | ['默认排序', '播放多', '新发布', '弹幕多', '收藏多', '评论多', '最多喜欢'][index];
46 | }
47 |
--------------------------------------------------------------------------------
/lib/pages/search_result/results_controller.dart:
--------------------------------------------------------------------------------
1 | import 'package:mobx/mobx.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:bilineo/request/search.dart';
4 | import 'package:bilineo/pages/search/search_type.dart';
5 | import 'package:bilineo/utils/utils.dart';
6 | import 'package:bilineo/utils/id.dart';
7 | import 'package:flutter_modular/flutter_modular.dart';
8 | import 'package:bilineo/pages/video/video_controller.dart';
9 |
10 | part 'results_controller.g.dart';
11 |
12 | class SearchResultController = _SearchResultController
13 | with _$SearchResultController;
14 |
15 | abstract class _SearchResultController with Store {
16 | ScrollController scrollController = ScrollController();
17 | final VideoController videoController = Modular.get();
18 |
19 | @observable
20 | String searchKeyWord = '';
21 |
22 | @observable
23 | int page = 1;
24 |
25 | @observable
26 | List resultList = [];
27 |
28 | SearchType searchType = SearchType.media_bangumi;
29 |
30 | Future onSearch({type = 'init'}) async {
31 | if (type == 'init') {
32 | page = 1;
33 | }
34 | var result = await SearchHttp.searchByType(
35 | searchType: searchType,
36 | keyword: searchKeyWord,
37 | page: page,
38 | order: null,
39 | duration: null);
40 | if (result['status']) {
41 | if (type == 'onRefresh' || type == 'init') {
42 | resultList = result['data'].list;
43 | } else {
44 | resultList.addAll(result['data'].list);
45 | }
46 | page++;
47 | onPushDetail(searchKeyWord, resultList);
48 | }
49 | return result;
50 | }
51 |
52 | Future onRefresh() async {
53 | page = 1;
54 | await onSearch(type: 'onRefresh');
55 | }
56 |
57 | void onPushDetail(keyword, resultList) async {
58 | // 匹配输入内容,如果是AV、BV号且有结果 直接跳转详情页
59 | Map matchRes = IdUtils.matchAvorBv(input: keyword);
60 | List matchKeys = matchRes.keys.toList();
61 | String? bvid;
62 | try {
63 | bvid = resultList.first.bvid;
64 | } catch (_) {
65 | bvid = null;
66 | }
67 | // keyword 可能输入纯数字
68 | int? aid;
69 | try {
70 | aid = resultList.first.aid;
71 | } catch (_) {
72 | aid = null;
73 | }
74 | if (matchKeys.isNotEmpty && searchType == SearchType.video ||
75 | aid.toString() == keyword) {
76 | String heroTag = Utils.makeHeroTag(bvid);
77 | int cid = await SearchHttp.ab2c(aid: aid, bvid: bvid);
78 | if (matchKeys.isNotEmpty &&
79 | matchKeys.first == 'BV' &&
80 | matchRes[matchKeys.first] == bvid ||
81 | matchKeys.isNotEmpty &&
82 | matchKeys.first == 'AV' &&
83 | matchRes[matchKeys.first] == aid ||
84 | aid.toString() == keyword) {
85 | // Get.toNamed(
86 | // '/video?bvid=$bvid&cid=$cid',
87 | // arguments: {'videoItem': resultList.first, 'heroTag': heroTag},
88 | // );
89 | videoController.bvid = bvid ?? '';
90 | videoController.cid = cid;
91 | videoController.heroTag = heroTag;
92 | videoController.videoType = SearchType.media_bangumi;
93 | // videoController.bangumiItem = res['data'];
94 | Modular.to.navigate('/tab/video/');
95 | }
96 | }
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/lib/pages/search_result/results_controller.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'results_controller.dart';
4 |
5 | // **************************************************************************
6 | // StoreGenerator
7 | // **************************************************************************
8 |
9 | // ignore_for_file: non_constant_identifier_names, unnecessary_brace_in_string_interps, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic, no_leading_underscores_for_local_identifiers
10 |
11 | mixin _$SearchResultController on _SearchResultController, Store {
12 | late final _$searchKeyWordAtom =
13 | Atom(name: '_SearchResultController.searchKeyWord', context: context);
14 |
15 | @override
16 | String get searchKeyWord {
17 | _$searchKeyWordAtom.reportRead();
18 | return super.searchKeyWord;
19 | }
20 |
21 | @override
22 | set searchKeyWord(String value) {
23 | _$searchKeyWordAtom.reportWrite(value, super.searchKeyWord, () {
24 | super.searchKeyWord = value;
25 | });
26 | }
27 |
28 | late final _$pageAtom =
29 | Atom(name: '_SearchResultController.page', context: context);
30 |
31 | @override
32 | int get page {
33 | _$pageAtom.reportRead();
34 | return super.page;
35 | }
36 |
37 | @override
38 | set page(int value) {
39 | _$pageAtom.reportWrite(value, super.page, () {
40 | super.page = value;
41 | });
42 | }
43 |
44 | late final _$resultListAtom =
45 | Atom(name: '_SearchResultController.resultList', context: context);
46 |
47 | @override
48 | List get resultList {
49 | _$resultListAtom.reportRead();
50 | return super.resultList;
51 | }
52 |
53 | @override
54 | set resultList(List value) {
55 | _$resultListAtom.reportWrite(value, super.resultList, () {
56 | super.resultList = value;
57 | });
58 | }
59 |
60 | @override
61 | String toString() {
62 | return '''
63 | searchKeyWord: ${searchKeyWord},
64 | page: ${page},
65 | resultList: ${resultList}
66 | ''';
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/lib/pages/search_result/results_module.dart:
--------------------------------------------------------------------------------
1 | import 'package:bilineo/pages/search_result/results_page.dart';
2 | import 'package:flutter_modular/flutter_modular.dart';
3 |
4 | class SearchResultModule extends Module {
5 | @override
6 | void routes(r) {
7 | r.child("/", child: (_) => const SearchResultPage());
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/lib/pages/search_result/results_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:bilineo/pages/search/search_type.dart';
3 | import 'package:flutter_modular/flutter_modular.dart';
4 | import 'package:bilineo/pages/search/search_controller.dart';
5 | import 'package:bilineo/pages/search_result/results_item.dart';
6 | import 'package:bilineo/pages/search_result/results_controller.dart';
7 | import 'package:bilineo/pages/menu/menu.dart';
8 | import 'package:provider/provider.dart';
9 |
10 | class SearchResultPage extends StatefulWidget {
11 | const SearchResultPage({super.key});
12 |
13 | @override
14 | State createState() => _SearchResultPageState();
15 | }
16 |
17 | class _SearchResultPageState extends State
18 | with TickerProviderStateMixin {
19 | final MySearchController mySearchController =
20 | Modular.get();
21 |
22 | final SearchResultController searchResultController = Modular.get();
23 |
24 | @override
25 | void initState() {
26 | super.initState();
27 | }
28 |
29 | @override
30 | void dispose() {
31 | // searchResultController.resultList = [];
32 | // searchResultController.searchKeyWord = '';
33 | // debugPrint('搜索缓存已经清空');
34 | super.dispose();
35 | }
36 |
37 | void onBackPressed(BuildContext context) {
38 | final navigationBarState = Provider.of(context, listen: false);
39 | navigationBarState.showNavigate();
40 | navigationBarState.updateSelectedIndex(1);
41 | Modular.to.navigate('/tab/search/');
42 | }
43 |
44 | @override
45 | Widget build(BuildContext context) {
46 | return PopScope(
47 | canPop: false,
48 | onPopInvoked: (bool didPop) async {
49 | onBackPressed(context);
50 | },
51 | child: Scaffold(
52 | appBar: AppBar(
53 | title: Text(mySearchController.searchKeyWord),
54 | leading: IconButton(
55 | icon: const Icon(Icons.arrow_back),
56 | onPressed: () {
57 | Modular.to.navigate('/tab/search/');
58 | //Modular.to.pop();
59 | },
60 | ),
61 | ),
62 | body: Column(
63 | children: [
64 | Expanded(
65 | child: SearchPanel(
66 | keyword: mySearchController.searchKeyWord,
67 | searchType: SearchType.media_bangumi,
68 | tag: DateTime.now().millisecondsSinceEpoch.toString(),
69 | )),
70 | ],
71 | ),
72 | ),
73 | );
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/lib/pages/video/video_controller.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:mobx/mobx.dart';
3 | import 'package:bilineo/pages/search/search_type.dart';
4 | import 'package:bilineo/pages/player/player_controller.dart';
5 | import 'package:flutter_modular/flutter_modular.dart';
6 | import 'package:bilineo/bean/bangumi/bangumi_info.dart';
7 |
8 | part 'video_controller.g.dart';
9 |
10 | class VideoController = _VideoController with _$VideoController;
11 |
12 | abstract class _VideoController with Store {
13 | @observable
14 | late String bvid;
15 |
16 | @observable
17 | late int cid;
18 |
19 | @observable
20 | late String pic;
21 |
22 | @observable
23 | late String heroTag;
24 |
25 | @observable
26 | late dynamic videoType;
27 |
28 | @observable
29 | late BangumiInfoModel? bangumiItem;
30 |
31 | String from = '/tab/popular/';
32 |
33 | // 界面管理
34 | @observable
35 | bool showPositioned = false;
36 | @observable
37 | bool showPosition = false;
38 | @observable
39 | bool showBrightness = false;
40 | @observable
41 | bool showVolume = false;
42 |
43 | @observable
44 | bool playing = false;
45 | @observable
46 | bool isBuffering = true;
47 | @observable
48 | Duration currentPosition = Duration.zero;
49 | @observable
50 | Duration buffer = Duration.zero;
51 | @observable
52 | Duration duration = Duration.zero;
53 |
54 | // 视频音量/亮度
55 | @observable
56 | double volume = 0;
57 | @observable
58 | double brightness = 0;
59 |
60 | // 播放器倍速
61 | @observable
62 | double playerSpeed = 1.0;
63 |
64 | // 弹幕开关
65 | @observable
66 | bool danmakuOn = false;
67 |
68 | // 安卓全屏状态
69 | @observable
70 | bool androidFullscreen = false;
71 |
72 | // final PlayerController playerController = Modular.get();
73 |
74 | Future init(PlayerController playerController) async {
75 | playerController.bvid = bvid;
76 | playerController.cid = cid;
77 | playerController.pic = pic;
78 | playerController.heroTag = heroTag;
79 | playerController.videoType = SearchType.media_bangumi;
80 | await playerController.init();
81 | debugPrint('PlayerContriller 初始化成功');
82 | return 'PlayerContriller Init Success';
83 | }
84 |
85 | // 修改分P或番剧分集
86 | Future changeSeasonOrbangu(bvidS, cidS, PlayerController playerController) async {
87 | // 重新获取视频资源
88 | bvid = bvidS;
89 | cid = cidS;
90 | playerController.bvid = bvidS;
91 | playerController.cid = cidS;
92 | await playerController.init();
93 | debugPrint('PlayController 重新初始化成功');
94 | }
95 |
96 | Future setPlaybackSpeed(double playerSpeed) async {
97 | final PlayerController playerController = Modular.get();
98 | try {
99 | playerController.mediaPlayer.setRate(playerSpeed);
100 | } catch(e) {
101 | debugPrint(e.toString());
102 | }
103 | this.playerSpeed = playerSpeed;
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/lib/pages/video/video_module.dart:
--------------------------------------------------------------------------------
1 | import 'package:bilineo/pages/video/video_page.dart';
2 | import 'package:bilineo/pages/video/video_controller.dart';
3 | import 'package:bilineo/pages/player/player_controller.dart';
4 | import 'package:flutter_modular/flutter_modular.dart';
5 |
6 | class VideoModule extends Module {
7 | @override
8 | void binds(i) {
9 | //i.addSingleton(VideoController.new);
10 | i.addSingleton(PlayerController.new);
11 | }
12 |
13 | @override
14 | void routes(r) {
15 | r.child("/", child: (_) => const VideoPage());
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/lib/pages/webview/webview_controller.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'webview_controller.dart';
4 |
5 | // **************************************************************************
6 | // StoreGenerator
7 | // **************************************************************************
8 |
9 | // ignore_for_file: non_constant_identifier_names, unnecessary_brace_in_string_interps, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic, no_leading_underscores_for_local_identifiers
10 |
11 | mixin _$WebviewController on _WebviewController, Store {
12 | late final _$typeAtom =
13 | Atom(name: '_WebviewController.type', context: context);
14 |
15 | @override
16 | String get type {
17 | _$typeAtom.reportRead();
18 | return super.type;
19 | }
20 |
21 | @override
22 | set type(String value) {
23 | _$typeAtom.reportWrite(value, super.type, () {
24 | super.type = value;
25 | });
26 | }
27 |
28 | late final _$loadProgressAtom =
29 | Atom(name: '_WebviewController.loadProgress', context: context);
30 |
31 | @override
32 | int get loadProgress {
33 | _$loadProgressAtom.reportRead();
34 | return super.loadProgress;
35 | }
36 |
37 | @override
38 | set loadProgress(int value) {
39 | _$loadProgressAtom.reportWrite(value, super.loadProgress, () {
40 | super.loadProgress = value;
41 | });
42 | }
43 |
44 | late final _$loadShowAtom =
45 | Atom(name: '_WebviewController.loadShow', context: context);
46 |
47 | @override
48 | bool get loadShow {
49 | _$loadShowAtom.reportRead();
50 | return super.loadShow;
51 | }
52 |
53 | @override
54 | set loadShow(bool value) {
55 | _$loadShowAtom.reportWrite(value, super.loadShow, () {
56 | super.loadShow = value;
57 | });
58 | }
59 |
60 | @override
61 | String toString() {
62 | return '''
63 | type: ${type},
64 | loadProgress: ${loadProgress},
65 | loadShow: ${loadShow}
66 | ''';
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/lib/pages/webview/webview_module.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_modular/flutter_modular.dart';
2 | import 'package:bilineo/pages/webview/webview_page.dart';
3 |
4 | class WebviewMoudle extends Module {
5 | @override
6 | void binds(i) {
7 | // i.addSingleton();
8 | }
9 |
10 | @override
11 | void routes(r) {
12 | r.child('/', child: (_) => const WebviewPage());
13 | }
14 | }
--------------------------------------------------------------------------------
/lib/pages/webview/webview_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_mobx/flutter_mobx.dart';
3 | import 'package:url_launcher/url_launcher.dart';
4 | import 'package:flutter_modular/flutter_modular.dart';
5 | import 'package:bilineo/pages/webview/webview_controller.dart';
6 | import 'package:webview_flutter/webview_flutter.dart';
7 |
8 | class WebviewPage extends StatefulWidget {
9 | const WebviewPage({super.key});
10 |
11 | @override
12 | State createState() => _WebviewPageState();
13 | }
14 |
15 | class _WebviewPageState extends State {
16 | final _webviewController = Modular.get();
17 |
18 | @override
19 | Widget build(BuildContext context) {
20 | return Scaffold(
21 | appBar: AppBar(
22 | centerTitle: false,
23 | titleSpacing: 0,
24 | leading: IconButton(
25 | icon: const Icon(Icons.arrow_back),
26 | onPressed: () {
27 | Modular.to.navigate('/tab/my/');
28 | //Modular.to.pop();
29 | },
30 | ),
31 | title: Text(
32 | _webviewController.pageTitle,
33 | style: Theme.of(context).textTheme.titleMedium,
34 | ),
35 | actions: [
36 | const SizedBox(width: 4),
37 | IconButton(
38 | onPressed: () {
39 | _webviewController.controller.reload();
40 | },
41 | icon: Icon(Icons.refresh_outlined,
42 | color: Theme.of(context).colorScheme.primary),
43 | ),
44 | IconButton(
45 | onPressed: () {
46 | launchUrl(Uri.parse(_webviewController.url));
47 | },
48 | icon: Icon(Icons.open_in_browser_outlined,
49 | color: Theme.of(context).colorScheme.primary),
50 | ),
51 | Observer(builder: (context) {
52 | return _webviewController.type == 'login'
53 | ? TextButton(
54 | onPressed: () => _webviewController.confirmLogin(null),
55 | child: const Text('刷新登录状态'),
56 | )
57 | : const SizedBox();
58 | }),
59 | const SizedBox(width: 12)
60 | ],
61 | ),
62 | body: Column(
63 | children: [
64 | Observer(builder: (context) {
65 | return AnimatedContainer(
66 | curve: Curves.easeInOut,
67 | duration: const Duration(milliseconds: 350),
68 | height: _webviewController.loadShow ? 4 : 0,
69 | child: LinearProgressIndicator(
70 | key: ValueKey(_webviewController.loadProgress),
71 | value: _webviewController.loadProgress / 100,
72 | ),
73 | );
74 | }),
75 | if (_webviewController.type == 'login')
76 | Container(
77 | width: double.infinity,
78 | color: Theme.of(context).colorScheme.onInverseSurface,
79 | padding: const EdgeInsets.only(
80 | left: 12, right: 12, top: 6, bottom: 6),
81 | child: const Text('登录成功未自动跳转? 请点击右上角「刷新登录状态」'),
82 | ),
83 | Expanded(
84 | child: WebViewWidget(controller: _webviewController.controller),
85 | ),
86 | ],
87 | ));
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/lib/pages/webview_desktop/webview_desktop_module.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_modular/flutter_modular.dart';
2 | import 'package:bilineo/pages/webview_desktop/webview_desktop_page.dart';
3 |
4 | class WebviewDesktopMoudle extends Module {
5 | @override
6 | void binds(i) {
7 | // i.addSingleton();
8 | }
9 |
10 | @override
11 | void routes(r) {
12 | r.child('/', child: (_) => const WebviewDesktopPage());
13 | }
14 | }
--------------------------------------------------------------------------------
/lib/request/bangumi.dart:
--------------------------------------------------------------------------------
1 | import 'package:bilineo/request/request.dart';
2 | import 'package:bilineo/request/api.dart';
3 | import 'package:bilineo/bean/bangumi/bangumi_list.dart';
4 | import 'package:bilineo/bean/bangumi/bangumi_info.dart';
5 |
6 | class BangumiHttp {
7 | static Future bangumiList({int? page}) async {
8 | var res = await Request().get(Api.bangumiList, data: {'page': page});
9 | if (res.data['code'] == 0) {
10 | return {
11 | 'status': true,
12 | 'data': BangumiListDataModel.fromJson(res.data['data'])
13 | };
14 | } else {
15 | return {
16 | 'status': false,
17 | 'data': [],
18 | 'msg': res.data['message'],
19 | };
20 | }
21 | }
22 |
23 | static Future