├── .gitignore ├── .metadata ├── .vscode └── settings.json ├── README.md ├── analysis_options.yaml ├── android ├── .gitignore ├── app │ ├── build.gradle │ └── src │ │ ├── debug │ │ └── AndroidManifest.xml │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── kotlin │ │ │ └── com │ │ │ │ └── heyanle │ │ │ │ └── easy_book │ │ │ │ └── MainActivity.kt │ │ └── res │ │ │ ├── drawable-v21 │ │ │ └── launch_background.xml │ │ │ ├── drawable │ │ │ └── launch_background.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 │ │ │ └── styles.xml │ │ └── profile │ │ └── AndroidManifest.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties └── settings.gradle ├── assets ├── sakiko_error.png ├── sakiko_loading.gif ├── sakiko_ok.png └── sakiko_pull.png ├── build.yaml ├── 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-60x60@2x.png │ │ │ ├── Icon-App-60x60@3x.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.dart ├── c.dart ├── database │ ├── dao │ │ ├── manga │ │ │ ├── manga_dao.dart │ │ │ └── manga_dao.g.dart │ │ └── novel │ │ │ ├── novel_dao.dart │ │ │ └── novel_dao.g.dart │ ├── database.dart │ ├── database.g.dart │ └── db │ │ ├── manga │ │ ├── manga_db.dart │ │ └── manga_db.g.dart │ │ └── novel │ │ ├── novel_db.dart │ │ └── novel_db.g.dart ├── entity │ ├── book │ │ ├── cover │ │ │ ├── book_cover.dart │ │ │ ├── book_cover.freezed.dart │ │ │ └── book_cover.g.dart │ │ └── home_tab │ │ │ ├── book_home_tab.dart │ │ │ ├── book_home_tab.freezed.dart │ │ │ └── book_home_tab.g.dart │ ├── extension │ │ ├── extension_data │ │ │ ├── extension_data.dart │ │ │ ├── extension_data.freezed.dart │ │ │ └── extension_data.g.dart │ │ └── extension_info │ │ │ ├── extension_info.dart │ │ │ ├── extension_info.freezed.dart │ │ │ └── extension_info.g.dart │ ├── manga │ │ ├── manga_chapter │ │ │ ├── manga_chapter.dart │ │ │ ├── manga_chapter.freezed.dart │ │ │ └── manga_chapter.g.dart │ │ ├── manga_cover │ │ │ ├── manga_cover.dart │ │ │ ├── manga_cover.freezed.dart │ │ │ └── manga_cover.g.dart │ │ ├── manga_detailed │ │ │ ├── manga_detailed.dart │ │ │ ├── manga_detailed.freezed.dart │ │ │ └── manga_detailed.g.dart │ │ ├── manga_enum.dart │ │ ├── manga_info │ │ │ └── manga_info.dart │ │ ├── manga_picture │ │ │ ├── manga_picture.dart │ │ │ ├── manga_picture.freezed.dart │ │ │ └── manga_picture.g.dart │ │ └── manga_summary │ │ │ ├── manga_summary.dart │ │ │ ├── manga_summary.freezed.dart │ │ │ └── manga_summary.g.dart │ ├── novel │ │ ├── novel_chapter │ │ │ ├── novel_chapter.dart │ │ │ ├── novel_chapter.freezed.dart │ │ │ └── novel_chapter.g.dart │ │ ├── novel_cover │ │ │ ├── novel_cover.dart │ │ │ ├── novel_cover.freezed.dart │ │ │ └── novel_cover.g.dart │ │ ├── novel_detailed │ │ │ ├── novel_detailed.dart │ │ │ ├── novel_detailed.freezed.dart │ │ │ └── novel_detailed.g.dart │ │ ├── novel_enum.dart │ │ ├── novel_info │ │ │ └── novel_info.dart │ │ ├── novel_summary │ │ │ ├── novel_summary.dart │ │ │ ├── novel_summary.freezed.dart │ │ │ └── novel_summary.g.dart │ │ └── novel_volume │ │ │ ├── novel_volume.dart │ │ │ ├── novel_volume.freezed.dart │ │ │ └── novel_volume.g.dart │ └── source │ │ ├── source_data │ │ └── source_data.dart │ │ └── source_info │ │ ├── source_info.dart │ │ ├── source_info.freezed.dart │ │ └── source_info.g.dart ├── hive_registrar.g.dart ├── l10n │ ├── arb │ │ └── intl_zh_CN.arb │ ├── intl │ │ ├── messages_all.dart │ │ └── messages_zh_CN.dart │ └── l10n.dart ├── plugin │ ├── component │ │ ├── api │ │ │ ├── component.dart │ │ │ ├── manga │ │ │ │ ├── detailed │ │ │ │ │ ├── manga_detailed_component.dart │ │ │ │ │ └── resp │ │ │ │ │ │ ├── detailed_resp.dart │ │ │ │ │ │ ├── detailed_resp.freezed.dart │ │ │ │ │ │ └── detailed_resp.g.dart │ │ │ │ ├── home │ │ │ │ │ ├── manga_home_component.dart │ │ │ │ │ ├── page │ │ │ │ │ │ ├── home_page.dart │ │ │ │ │ │ ├── home_page.freezed.dart │ │ │ │ │ │ └── home_page.g.dart │ │ │ │ │ ├── resp │ │ │ │ │ │ ├── home_resp.dart │ │ │ │ │ │ ├── home_resp.freezed.dart │ │ │ │ │ │ └── home_resp.g.dart │ │ │ │ │ └── tab │ │ │ │ │ │ ├── home_tab.dart │ │ │ │ │ │ ├── home_tab.freezed.dart │ │ │ │ │ │ └── home_tab.g.dart │ │ │ │ ├── read │ │ │ │ │ ├── manga_read_component.dart │ │ │ │ │ └── resp │ │ │ │ │ │ ├── read_resp.dart │ │ │ │ │ │ ├── read_resp.freezed.dart │ │ │ │ │ │ └── read_resp.g.dart │ │ │ │ └── search │ │ │ │ │ ├── manga_search_component.dart │ │ │ │ │ └── resp │ │ │ │ │ ├── search_resp.dart │ │ │ │ │ ├── search_resp.freezed.dart │ │ │ │ │ └── search_resp.g.dart │ │ │ ├── novel │ │ │ │ ├── detailed │ │ │ │ │ ├── novel_detailed_component.dart │ │ │ │ │ └── resp │ │ │ │ │ │ ├── detailed_resp.dart │ │ │ │ │ │ ├── detailed_resp.freezed.dart │ │ │ │ │ │ └── detailed_resp.g.dart │ │ │ │ ├── home │ │ │ │ │ ├── novel_home_component.dart │ │ │ │ │ ├── page │ │ │ │ │ │ ├── home_page.dart │ │ │ │ │ │ ├── home_page.freezed.dart │ │ │ │ │ │ └── home_page.g.dart │ │ │ │ │ ├── resp │ │ │ │ │ │ ├── home_resp.dart │ │ │ │ │ │ ├── home_resp.freezed.dart │ │ │ │ │ │ └── home_resp.g.dart │ │ │ │ │ └── tab │ │ │ │ │ │ ├── home_tab.dart │ │ │ │ │ │ ├── home_tab.freezed.dart │ │ │ │ │ │ └── home_tab.g.dart │ │ │ │ ├── read │ │ │ │ │ ├── novel_read_component.dart │ │ │ │ │ └── resp │ │ │ │ │ │ ├── read_resp.dart │ │ │ │ │ │ ├── read_resp.freezed.dart │ │ │ │ │ │ └── read_resp.g.dart │ │ │ │ └── search │ │ │ │ │ ├── novel_search_component.dart │ │ │ │ │ └── resp │ │ │ │ │ ├── search_resp.dart │ │ │ │ │ ├── search_resp.freezed.dart │ │ │ │ │ └── search_resp.g.dart │ │ │ └── payload │ │ │ │ ├── component_payload.dart │ │ │ │ ├── component_payload.freezed.dart │ │ │ │ └── component_payload.g.dart │ │ └── core │ │ │ └── js │ │ │ ├── js_component.dart │ │ │ ├── manga │ │ │ ├── detailed │ │ │ │ └── js_manga_detailed_component.dart │ │ │ ├── home │ │ │ │ └── js_manga_home_component.dart │ │ │ ├── read │ │ │ │ └── js_manga_read_component.dart │ │ │ └── search │ │ │ │ └── js_manga_search_component.dart │ │ │ ├── novel │ │ │ ├── detailed │ │ │ │ └── js_novel_detailed_component.dart │ │ │ ├── home │ │ │ │ └── js_novel_home_component.dart │ │ │ ├── read │ │ │ │ └── js_novel_read_component.dart │ │ │ └── search │ │ │ │ └── js_novel_search_component.dart │ │ │ └── utils │ │ │ └── js_component_utils.dart │ ├── extension │ │ ├── controller │ │ │ ├── extension_controller.dart │ │ │ ├── extension_controller.freezed.dart │ │ │ └── extension_controller.g.dart │ │ ├── loader │ │ │ ├── extension_loader.dart │ │ │ ├── inner │ │ │ │ └── inner_extension_loader.dart │ │ │ ├── js │ │ │ │ └── js_extension_loader.dart │ │ │ └── mygopack │ │ │ │ ├── manifest │ │ │ │ ├── mygopack_manifest_info.dart │ │ │ │ ├── mygopack_manifest_info.freezed.dart │ │ │ │ └── mygopack_manifest_info.g.dart │ │ │ │ └── mygopack_extension_loader.dart │ │ └── utils │ │ │ └── extension_utils.dart │ ├── inner │ │ ├── inner_source.dart │ │ └── test │ │ │ ├── component │ │ │ ├── manga │ │ │ │ └── manga_test_component.dart │ │ │ └── novel │ │ │ │ └── novel_test_component.dart │ │ │ └── test_inner_source.dart │ └── source │ │ ├── config_controller │ │ ├── source_config_controller.dart │ │ ├── source_config_controller.freezed.dart │ │ └── source_config_controller.g.dart │ │ ├── controller │ │ ├── source_controller.dart │ │ └── source_controller.g.dart │ │ ├── load_controller │ │ ├── source_load_controller.dart │ │ └── source_load_controller.g.dart │ │ ├── loader │ │ ├── inner │ │ │ └── inner_source_loader.dart │ │ ├── js │ │ │ ├── js_source_loader.dart │ │ │ └── js_source_utils.dart │ │ └── source_loader.dart │ │ └── utils │ │ ├── action │ │ ├── buffer_action.dart │ │ ├── entity │ │ │ ├── web_view_resp │ │ │ │ ├── web_view_resp.dart │ │ │ │ ├── web_view_resp.freezed.dart │ │ │ │ └── web_view_resp.g.dart │ │ │ └── web_view_strategy │ │ │ │ ├── web_view_strategy.dart │ │ │ │ ├── web_view_strategy.freezed.dart │ │ │ │ └── web_view_strategy.g.dart │ │ ├── html_action.dart │ │ ├── source_action.dart │ │ ├── web_view_action.dart │ │ └── xpath_action.dart │ │ ├── buffer_utils.dart │ │ └── source_utils_scope.dart ├── repository │ └── book_cover │ │ ├── book_cover_repository │ │ └── book_cover_repository.dart │ │ └── page │ │ ├── book_cover_page.dart │ │ ├── manga_cover_page.dart │ │ └── novel_cover_page.dart ├── router.dart ├── router.g.dart ├── theme │ ├── theme.dart │ ├── theme.freezed.dart │ └── theme.g.dart ├── ui │ ├── common │ │ ├── cover_card.dart │ │ ├── custom_sliver_grid.dart │ │ └── loading_icon.dart │ ├── main │ │ ├── history │ │ │ └── history.dart │ │ ├── home │ │ │ ├── home.dart │ │ │ ├── page │ │ │ │ ├── home_book_page.dart │ │ │ │ └── view_model │ │ │ │ │ ├── home_page_view_model.dart │ │ │ │ │ ├── home_page_view_model.freezed.dart │ │ │ │ │ └── home_page_view_model.g.dart │ │ │ └── view_model │ │ │ │ ├── home_view_model.dart │ │ │ │ ├── home_view_model.freezed.dart │ │ │ │ └── home_view_model.g.dart │ │ ├── library │ │ │ └── library.dart │ │ ├── main.dart │ │ └── more │ │ │ └── more.dart │ └── splash │ │ └── splash.dart └── utils │ ├── cancelable_task │ └── cancelable_task.dart │ ├── file_index │ ├── file_index_utils.dart │ └── item │ │ ├── file_index_item.dart │ │ ├── file_index_item.freezed.dart │ │ └── file_index_item.g.dart │ ├── hive │ └── hive.dart │ ├── json.dart │ ├── platform.dart │ └── zip │ └── zip_utils.dart ├── linux ├── .gitignore ├── CMakeLists.txt ├── flutter │ └── CMakeLists.txt ├── main.cc ├── my_application.cc └── my_application.h ├── macos ├── .gitignore ├── Flutter │ ├── Flutter-Debug.xcconfig │ └── Flutter-Release.xcconfig ├── 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 ├── make ├── README.md ├── bin │ └── make.dart └── pubspec.yaml ├── 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 └── 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 /.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: "0d074ced6cd64f39f586aaa115b2e4c993d74cb7" 8 | channel: "beta" 9 | 10 | project_type: app 11 | 12 | # Tracks metadata for the flutter migrate command 13 | migration: 14 | platforms: 15 | - platform: root 16 | create_revision: 0d074ced6cd64f39f586aaa115b2e4c993d74cb7 17 | base_revision: 0d074ced6cd64f39f586aaa115b2e4c993d74cb7 18 | - platform: android 19 | create_revision: 0d074ced6cd64f39f586aaa115b2e4c993d74cb7 20 | base_revision: 0d074ced6cd64f39f586aaa115b2e4c993d74cb7 21 | - platform: ios 22 | create_revision: 0d074ced6cd64f39f586aaa115b2e4c993d74cb7 23 | base_revision: 0d074ced6cd64f39f586aaa115b2e4c993d74cb7 24 | - platform: linux 25 | create_revision: 0d074ced6cd64f39f586aaa115b2e4c993d74cb7 26 | base_revision: 0d074ced6cd64f39f586aaa115b2e4c993d74cb7 27 | - platform: macos 28 | create_revision: 0d074ced6cd64f39f586aaa115b2e4c993d74cb7 29 | base_revision: 0d074ced6cd64f39f586aaa115b2e4c993d74cb7 30 | - platform: web 31 | create_revision: 0d074ced6cd64f39f586aaa115b2e4c993d74cb7 32 | base_revision: 0d074ced6cd64f39f586aaa115b2e4c993d74cb7 33 | - platform: windows 34 | create_revision: 0d074ced6cd64f39f586aaa115b2e4c993d74cb7 35 | base_revision: 0d074ced6cd64f39f586aaa115b2e4c993d74cb7 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/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmake.ignoreCMakeListsMissing": true 3 | } -------------------------------------------------------------------------------- /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 17 | # https://dart-lang.github.io/linter/lints/index.html. 18 | # 19 | # Instead of disabling a lint rule for the entire project in the 20 | # section below, it can also be suppressed for a single line of code 21 | # or a specific dart file by using the `// ignore: name_of_lint` and 22 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file 23 | # producing the lint. 24 | rules: 25 | # avoid_print: false # Uncomment to disable the `avoid_print` rule 26 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule 27 | analyzer: 28 | plugins: 29 | - custom_lint 30 | errors: 31 | invalid_annotation_target: ignore 32 | # Additional information about this file can be found at 33 | # https://dart.dev/guides/language/analysis-options 34 | -------------------------------------------------------------------------------- /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/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | 9 | 10 | 14 | 22 | 26 | 30 | 31 | 32 | 33 | 34 | 35 | 37 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/heyanle/easy_book/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.heyanle.easy_mygo 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /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/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easybangumiorg/EasyMyGo/8f9a7163c5d4c9744f32b1f2b8a68577fa8e3fce/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easybangumiorg/EasyMyGo/8f9a7163c5d4c9744f32b1f2b8a68577fa8e3fce/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easybangumiorg/EasyMyGo/8f9a7163c5d4c9744f32b1f2b8a68577fa8e3fce/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easybangumiorg/EasyMyGo/8f9a7163c5d4c9744f32b1f2b8a68577fa8e3fce/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easybangumiorg/EasyMyGo/8f9a7163c5d4c9744f32b1f2b8a68577fa8e3fce/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/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 'com.android.tools.build:gradle:7.3.0' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | } 12 | } 13 | 14 | allprojects { 15 | repositories { 16 | google() 17 | mavenCentral() 18 | } 19 | } 20 | 21 | rootProject.buildDir = '../build' 22 | subprojects { 23 | project.buildDir = "${rootProject.buildDir}/${project.name}" 24 | } 25 | subprojects { 26 | project.evaluationDependsOn(':app') 27 | } 28 | 29 | tasks.register("clean", Delete) { 30 | delete rootProject.buildDir 31 | } 32 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip 6 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties") 4 | def properties = new Properties() 5 | 6 | assert localPropertiesFile.exists() 7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } 8 | 9 | def flutterSdkPath = properties.getProperty("flutter.sdk") 10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" 12 | -------------------------------------------------------------------------------- /assets/sakiko_error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easybangumiorg/EasyMyGo/8f9a7163c5d4c9744f32b1f2b8a68577fa8e3fce/assets/sakiko_error.png -------------------------------------------------------------------------------- /assets/sakiko_loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easybangumiorg/EasyMyGo/8f9a7163c5d4c9744f32b1f2b8a68577fa8e3fce/assets/sakiko_loading.gif -------------------------------------------------------------------------------- /assets/sakiko_ok.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easybangumiorg/EasyMyGo/8f9a7163c5d4c9744f32b1f2b8a68577fa8e3fce/assets/sakiko_ok.png -------------------------------------------------------------------------------- /assets/sakiko_pull.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easybangumiorg/EasyMyGo/8f9a7163c5d4c9744f32b1f2b8a68577fa8e3fce/assets/sakiko_pull.png -------------------------------------------------------------------------------- /build.yaml: -------------------------------------------------------------------------------- 1 | targets: 2 | $default: 3 | builders: 4 | riverpod_generator: 5 | options: 6 | provider_name_suffix: "Pod" # (default) 7 | provider_family_name_suffix: "Provider" 8 | -------------------------------------------------------------------------------- /devtools_options.yaml: -------------------------------------------------------------------------------- 1 | description: This file stores settings for Dart & Flutter DevTools. 2 | documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states 3 | extensions: 4 | -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | **/dgph 2 | *.mode1v3 3 | *.mode2v3 4 | *.moved-aside 5 | *.pbxuser 6 | *.perspectivev3 7 | **/*sync/ 8 | .sconsign.dblite 9 | .tags* 10 | **/.vagrant/ 11 | **/DerivedData/ 12 | Icon? 13 | **/Pods/ 14 | **/.symlinks/ 15 | profile 16 | xcuserdata 17 | **/.generated/ 18 | Flutter/App.framework 19 | Flutter/Flutter.framework 20 | Flutter/Flutter.podspec 21 | Flutter/Generated.xcconfig 22 | Flutter/ephemeral/ 23 | Flutter/app.flx 24 | Flutter/app.zip 25 | Flutter/flutter_assets/ 26 | Flutter/flutter_export_environment.sh 27 | ServiceDefinitions.json 28 | Runner/GeneratedPluginRegistrant.* 29 | 30 | # Exceptions to above rules. 31 | !default.mode1v3 32 | !default.mode2v3 33 | !default.pbxuser 34 | !default.perspectivev3 35 | -------------------------------------------------------------------------------- /ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 11.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 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/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easybangumiorg/EasyMyGo/8f9a7163c5d4c9744f32b1f2b8a68577fa8e3fce/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/easybangumiorg/EasyMyGo/8f9a7163c5d4c9744f32b1f2b8a68577fa8e3fce/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/easybangumiorg/EasyMyGo/8f9a7163c5d4c9744f32b1f2b8a68577fa8e3fce/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/easybangumiorg/EasyMyGo/8f9a7163c5d4c9744f32b1f2b8a68577fa8e3fce/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/easybangumiorg/EasyMyGo/8f9a7163c5d4c9744f32b1f2b8a68577fa8e3fce/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/easybangumiorg/EasyMyGo/8f9a7163c5d4c9744f32b1f2b8a68577fa8e3fce/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/easybangumiorg/EasyMyGo/8f9a7163c5d4c9744f32b1f2b8a68577fa8e3fce/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/easybangumiorg/EasyMyGo/8f9a7163c5d4c9744f32b1f2b8a68577fa8e3fce/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/easybangumiorg/EasyMyGo/8f9a7163c5d4c9744f32b1f2b8a68577fa8e3fce/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/easybangumiorg/EasyMyGo/8f9a7163c5d4c9744f32b1f2b8a68577fa8e3fce/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easybangumiorg/EasyMyGo/8f9a7163c5d4c9744f32b1f2b8a68577fa8e3fce/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/easybangumiorg/EasyMyGo/8f9a7163c5d4c9744f32b1f2b8a68577fa8e3fce/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easybangumiorg/EasyMyGo/8f9a7163c5d4c9744f32b1f2b8a68577fa8e3fce/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/easybangumiorg/EasyMyGo/8f9a7163c5d4c9744f32b1f2b8a68577fa8e3fce/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/easybangumiorg/EasyMyGo/8f9a7163c5d4c9744f32b1f2b8a68577fa8e3fce/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/easybangumiorg/EasyMyGo/8f9a7163c5d4c9744f32b1f2b8a68577fa8e3fce/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easybangumiorg/EasyMyGo/8f9a7163c5d4c9744f32b1f2b8a68577fa8e3fce/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easybangumiorg/EasyMyGo/8f9a7163c5d4c9744f32b1f2b8a68577fa8e3fce/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | Easy Book 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | easy_mygo 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 | UIViewControllerBasedStatusBarAppearance 45 | 46 | CADisableMinimumFrameDurationOnPhone 47 | 48 | UIApplicationSupportsIndirectInputEvents 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /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/c.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'dart:io'; 3 | 4 | import 'package:path/path.dart'; 5 | import 'package:path_provider/path_provider.dart'; 6 | 7 | class EasyConstant { 8 | static const versionName = String.fromEnvironment("VERSION_NAME", defaultValue: "dev"); 9 | static final versionCode = int.tryParse(const String.fromEnvironment("VERSION_CODE", defaultValue: "0")) ?? 0; 10 | 11 | static final applicationPath = Future(() async { 12 | final rootPath = await getApplicationSupportDirectory(); 13 | final dir = Directory(join(rootPath.absolute.path, "files")); 14 | await dir.create(recursive: true); 15 | return dir; 16 | }); 17 | 18 | static final tempPath = Future(() async { 19 | final rootPath = await getApplicationSupportDirectory(); 20 | final dir = Directory(join(rootPath.absolute.path, "temp")); 21 | await dir.create(recursive: true); 22 | return dir; 23 | }); 24 | 25 | static final cachePath = Future(() async { 26 | final rootPath = await getApplicationSupportDirectory(); 27 | final dir = Directory(join(rootPath.absolute.path, "cache")); 28 | await dir.create(recursive: true); 29 | return dir; 30 | }); 31 | } 32 | -------------------------------------------------------------------------------- /lib/database/dao/manga/manga_dao.dart: -------------------------------------------------------------------------------- 1 | import 'package:drift/drift.dart'; 2 | import 'package:easy_mygo/database/db/manga/manga_db.dart'; 3 | import 'package:easy_mygo/entity/manga/manga_info/manga_info.dart'; 4 | 5 | part 'manga_dao.g.dart'; 6 | 7 | @DriftAccessor(tables: [MangaTable]) 8 | class MangaDao extends DatabaseAccessor with _$MangaDaoMixin { 9 | // Rthis constructor is required so that the main database can create an instance 10 | // of this object. 11 | MangaDao(super.db); 12 | 13 | Future> selectAll() async { 14 | return await select(mangaTable).get(); 15 | } 16 | 17 | Stream> watchAll() { 18 | return select(mangaTable).watch(); 19 | } 20 | 21 | Stream> watchStar() { 22 | return (select(mangaTable) 23 | ..where((tbl) => tbl.starTime.isBiggerThan(const Constant(0)))) 24 | .watch(); 25 | } 26 | 27 | Stream> watchHistory() { 28 | return (select(mangaTable) 29 | ..where((tbl) => tbl.lastHistoryTime.isBiggerThan(const Constant(0)))) 30 | .watch(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /lib/database/dao/manga/manga_dao.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'manga_dao.dart'; 4 | 5 | // ignore_for_file: type=lint 6 | mixin _$MangaDaoMixin on DatabaseAccessor { 7 | $MangaTableTable get mangaTable => attachedDatabase.mangaTable; 8 | } 9 | -------------------------------------------------------------------------------- /lib/database/dao/novel/novel_dao.dart: -------------------------------------------------------------------------------- 1 | import 'package:drift/drift.dart'; 2 | import 'package:easy_mygo/database/db/novel/novel_db.dart'; 3 | import 'package:easy_mygo/entity/novel/novel_info/novel_info.dart'; 4 | 5 | part 'novel_dao.g.dart'; 6 | 7 | @DriftAccessor(tables: [NovelTable]) 8 | class NovelDao extends DatabaseAccessor with _$NovelDaoMixin { 9 | // Rthis constructor is required so that the main database can create an instance 10 | // of this object. 11 | NovelDao(super.db); 12 | 13 | // Future> selectAll() async { 14 | // return await select(mangaTable).get(); 15 | // } 16 | // 17 | // Stream> watchAll() { 18 | // return select(mangaTable).watch(); 19 | // } 20 | // 21 | // Stream> watchStar() { 22 | // return (select(mangaTable) 23 | // ..where((tbl) => tbl.starTime.isBiggerThan(const Constant(0)))) 24 | // .watch(); 25 | // } 26 | // 27 | // Stream> watchHistory() { 28 | // return (select(mangaTable) 29 | // ..where((tbl) => tbl.lastHistoryTime.isBiggerThan(const Constant(0)))) 30 | // .watch(); 31 | // } 32 | } 33 | -------------------------------------------------------------------------------- /lib/database/dao/novel/novel_dao.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'novel_dao.dart'; 4 | 5 | // ignore_for_file: type=lint 6 | mixin _$NovelDaoMixin on DatabaseAccessor { 7 | $NovelTableTable get novelTable => attachedDatabase.novelTable; 8 | } 9 | -------------------------------------------------------------------------------- /lib/database/database.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'database.dart'; 4 | 5 | // ************************************************************************** 6 | // RiverpodGenerator 7 | // ************************************************************************** 8 | 9 | String _$databaseControllerHash() => 10 | r'c3b5726e447d53a150aeb916bc620e81ecab4695'; 11 | 12 | /// See also [DatabaseController]. 13 | @ProviderFor(DatabaseController) 14 | final databaseControllerPod = 15 | NotifierProvider.internal( 16 | DatabaseController.new, 17 | name: r'databaseControllerPod', 18 | debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') 19 | ? null 20 | : _$databaseControllerHash, 21 | dependencies: null, 22 | allTransitiveDependencies: null, 23 | ); 24 | 25 | typedef _$DatabaseController = Notifier; 26 | // ignore_for_file: type=lint 27 | // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member 28 | -------------------------------------------------------------------------------- /lib/database/db/manga/manga_db.dart: -------------------------------------------------------------------------------- 1 | import 'package:drift/drift.dart'; 2 | import 'package:drift/native.dart'; 3 | import 'package:easy_mygo/database/dao/manga/manga_dao.dart'; 4 | import 'package:easy_mygo/entity/manga/manga_enum.dart'; 5 | import 'package:easy_mygo/entity/manga/manga_info/manga_info.dart'; 6 | 7 | part 'manga_db.g.dart'; 8 | 9 | @DriftDatabase(tables: [MangaTable], daos: [MangaDao]) 10 | class MangaDB extends _$MangaDB { 11 | // we tell the database where to store the data with this constructor 12 | 13 | // you should bump this number whenever you change or add a table definition. Migrations 14 | // are covered later in this readme. 15 | @override 16 | int get schemaVersion => 1; 17 | 18 | @override 19 | MigrationStrategy get migration => MigrationStrategy(onCreate: (Migrator m) { 20 | return m.createAll(); 21 | }, onUpgrade: (Migrator m, int from, int to) async { 22 | // 版本更新 23 | }); 24 | 25 | MangaDB(NativeDatabase super.database); 26 | 27 | } 28 | -------------------------------------------------------------------------------- /lib/database/db/novel/novel_db.dart: -------------------------------------------------------------------------------- 1 | import 'package:drift/drift.dart'; 2 | import 'package:drift/native.dart'; 3 | import 'package:easy_mygo/database/dao/novel/novel_dao.dart'; 4 | import 'package:easy_mygo/entity/novel/novel_info/novel_info.dart'; 5 | import 'package:easy_mygo/entity/novel/novel_enum.dart'; 6 | 7 | part 'novel_db.g.dart'; 8 | 9 | @DriftDatabase(tables: [NovelTable], daos: [NovelDao]) 10 | class NovelDB extends _$NovelDB { 11 | // we tell the database where to store the data with this constructor 12 | 13 | // you should bump this number whenever you change or add a table definition. Migrations 14 | // are covered later in this readme. 15 | @override 16 | int get schemaVersion => 1; 17 | 18 | @override 19 | MigrationStrategy get migration => MigrationStrategy(onCreate: (Migrator m) { 20 | return m.createAll(); 21 | }, onUpgrade: (Migrator m, int from, int to) async { 22 | // 版本更新 23 | }); 24 | 25 | NovelDB(NativeDatabase super.database); 26 | 27 | } 28 | -------------------------------------------------------------------------------- /lib/entity/book/cover/book_cover.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:easy_mygo/entity/manga/manga_cover/manga_cover.dart'; 3 | import 'package:easy_mygo/entity/novel/novel_cover/novel_cover.dart'; 4 | import 'package:easy_mygo/entity/source/source_info/source_info.dart'; 5 | import 'package:freezed_annotation/freezed_annotation.dart'; 6 | 7 | part 'book_cover.g.dart'; 8 | part 'book_cover.freezed.dart'; 9 | 10 | // 一本书的封面,主要是把 MangaCover 和 NovelCover 合并一波 11 | // 在封面显示 12 | @freezed 13 | class BookCover with _$BookCover { 14 | 15 | factory BookCover({ 16 | required SourceType type, 17 | required String id, 18 | required String label, 19 | required String cover, 20 | required String intro, 21 | @JsonKey(name: "jump_url") required String jumpUrl, 22 | required String source, 23 | required String ext, 24 | }) = _BookCover; 25 | 26 | factory BookCover.fromJson(Map json) => _$BookCoverFromJson(json); 27 | 28 | factory BookCover.fromNovelCover(NovelCover novelCover) { 29 | return BookCover( 30 | type: SourceType.novel, 31 | id: novelCover.id, 32 | label: novelCover.label, 33 | cover: novelCover.cover, 34 | intro: novelCover.intro, 35 | jumpUrl: novelCover.jumpUrl, 36 | source: novelCover.source, 37 | ext: novelCover.ext, 38 | ); 39 | } 40 | 41 | factory BookCover.fromMangaCover(MangaCover mangaCover) { 42 | return BookCover( 43 | type: SourceType.manga, 44 | id: mangaCover.id, 45 | label: mangaCover.label, 46 | cover: mangaCover.cover, 47 | intro: mangaCover.intro, 48 | jumpUrl: mangaCover.jumpUrl, 49 | source: mangaCover.source, 50 | ext: mangaCover.ext, 51 | ); 52 | } 53 | 54 | } -------------------------------------------------------------------------------- /lib/entity/book/cover/book_cover.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'book_cover.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _$BookCoverImpl _$$BookCoverImplFromJson(Map json) => 10 | _$BookCoverImpl( 11 | type: $enumDecode(_$SourceTypeEnumMap, json['type']), 12 | id: json['id'] as String, 13 | label: json['label'] as String, 14 | cover: json['cover'] as String, 15 | intro: json['intro'] as String, 16 | jumpUrl: json['jump_url'] as String, 17 | source: json['source'] as String, 18 | ext: json['ext'] as String, 19 | ); 20 | 21 | Map _$$BookCoverImplToJson(_$BookCoverImpl instance) => 22 | { 23 | 'type': _$SourceTypeEnumMap[instance.type]!, 24 | 'id': instance.id, 25 | 'label': instance.label, 26 | 'cover': instance.cover, 27 | 'intro': instance.intro, 28 | 'jump_url': instance.jumpUrl, 29 | 'source': instance.source, 30 | 'ext': instance.ext, 31 | }; 32 | 33 | const _$SourceTypeEnumMap = { 34 | SourceType.manga: 'manga', 35 | SourceType.novel: 'novel', 36 | }; 37 | -------------------------------------------------------------------------------- /lib/entity/book/home_tab/book_home_tab.dart: -------------------------------------------------------------------------------- 1 | 2 | 3 | import 'package:freezed_annotation/freezed_annotation.dart'; 4 | 5 | part 'book_home_tab.freezed.dart'; 6 | part 'book_home_tab.g.dart'; 7 | 8 | @freezed 9 | class BookHomeTab with _$BookHomeTab { 10 | factory BookHomeTab({ 11 | required String id, 12 | required String label, 13 | // 是否有 二级 tab 14 | @JsonKey(name: "has_second_tab") required bool hasSecondTab, 15 | 16 | // 可以让源透传一些信息 17 | @Default('') String ext, 18 | }) = _BookHomeTab; 19 | 20 | factory BookHomeTab.fromJson(Map json) => 21 | _$BookHomeTabFromJson(json); 22 | } 23 | 24 | @freezed 25 | class BookSecondTab with _$BookSecondTab { 26 | factory BookSecondTab({ 27 | required String id, 28 | required String label, 29 | 30 | // 可以让源透传一些信息 31 | @Default('') String ext, 32 | }) = _BookSecondTab; 33 | 34 | factory BookSecondTab.fromJson(Map json) => 35 | _$BookSecondTabFromJson(json); 36 | } -------------------------------------------------------------------------------- /lib/entity/book/home_tab/book_home_tab.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'book_home_tab.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _$BookHomeTabImpl _$$BookHomeTabImplFromJson(Map json) => 10 | _$BookHomeTabImpl( 11 | id: json['id'] as String, 12 | label: json['label'] as String, 13 | hasSecondTab: json['has_second_tab'] as bool, 14 | ext: json['ext'] as String? ?? '', 15 | ); 16 | 17 | Map _$$BookHomeTabImplToJson(_$BookHomeTabImpl instance) => 18 | { 19 | 'id': instance.id, 20 | 'label': instance.label, 21 | 'has_second_tab': instance.hasSecondTab, 22 | 'ext': instance.ext, 23 | }; 24 | 25 | _$BookSecondTabImpl _$$BookSecondTabImplFromJson(Map json) => 26 | _$BookSecondTabImpl( 27 | id: json['id'] as String, 28 | label: json['label'] as String, 29 | ext: json['ext'] as String? ?? '', 30 | ); 31 | 32 | Map _$$BookSecondTabImplToJson(_$BookSecondTabImpl instance) => 33 | { 34 | 'id': instance.id, 35 | 'label': instance.label, 36 | 'ext': instance.ext, 37 | }; 38 | -------------------------------------------------------------------------------- /lib/entity/extension/extension_data/extension_data.dart: -------------------------------------------------------------------------------- 1 | import 'package:easy_mygo/entity/extension/extension_info/extension_info.dart'; 2 | import 'package:easy_mygo/entity/source/source_info/source_info.dart'; 3 | import 'package:freezed_annotation/freezed_annotation.dart'; 4 | 5 | part 'extension_data.g.dart'; 6 | 7 | part 'extension_data.freezed.dart'; 8 | 9 | /// ExtensionInfo 加载结果 10 | /// 拓展管理界面依赖的实体 11 | 12 | enum ExtensionLoadState { loaded, error } 13 | 14 | @freezed 15 | class ExtensionData with _$ExtensionData { 16 | factory ExtensionData({ 17 | required ExtensionInfo info, 18 | @Default(null) List? sources, 19 | @Default(ExtensionLoadState.loaded) ExtensionLoadState state, 20 | @JsonKey(name: "error_msg") @Default("") String errorMsg, 21 | // 该插件的文件夹 22 | @JsonKey(name: "folder_path") required String folderPath, 23 | }) = _ExtensionData; 24 | 25 | factory ExtensionData.fromJson(Map json) => 26 | _$ExtensionDataFromJson(json); 27 | } 28 | -------------------------------------------------------------------------------- /lib/entity/extension/extension_data/extension_data.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'extension_data.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _$ExtensionDataImpl _$$ExtensionDataImplFromJson(Map json) => 10 | _$ExtensionDataImpl( 11 | info: ExtensionInfo.fromJson(json['info'] as Map), 12 | sources: (json['sources'] as List?) 13 | ?.map((e) => SourceInfo.fromJson(e as Map)) 14 | .toList() ?? 15 | null, 16 | state: $enumDecodeNullable(_$ExtensionLoadStateEnumMap, json['state']) ?? 17 | ExtensionLoadState.loaded, 18 | errorMsg: json['error_msg'] as String? ?? "", 19 | folderPath: json['folder_path'] as String, 20 | ); 21 | 22 | Map _$$ExtensionDataImplToJson(_$ExtensionDataImpl instance) => 23 | { 24 | 'info': instance.info, 25 | 'sources': instance.sources, 26 | 'state': _$ExtensionLoadStateEnumMap[instance.state]!, 27 | 'error_msg': instance.errorMsg, 28 | 'folder_path': instance.folderPath, 29 | }; 30 | 31 | const _$ExtensionLoadStateEnumMap = { 32 | ExtensionLoadState.loaded: 'loaded', 33 | ExtensionLoadState.error: 'error', 34 | }; 35 | -------------------------------------------------------------------------------- /lib/entity/extension/extension_info/extension_info.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | part 'extension_info.freezed.dart'; 4 | 5 | part 'extension_info.g.dart'; 6 | 7 | enum ExtensionLoaderType { 8 | // 单个纯 js 文件 9 | js, 10 | // mygo 包,包里的文件格式交给 loader 去处理了 11 | mygopack, 12 | 13 | // 内置拓展,统一一个 14 | inner, 15 | } 16 | 17 | @freezed 18 | class ExtensionInfo with _$ExtensionInfo { 19 | factory ExtensionInfo({ 20 | // 包名唯一 21 | required String package, 22 | required String label, 23 | @JsonKey(name: 'version_name') required String versionName, 24 | @JsonKey(name: 'version_code') required int versionCode, 25 | @JsonKey(name: 'lib_version') required int libVersion, 26 | @JsonKey(name: 'extension_load_type') required ExtensionLoaderType loadType, 27 | required String path, 28 | @Default("") String readme, 29 | }) = _ExtensionInfo; 30 | 31 | factory ExtensionInfo.fromJson(Map json) => 32 | _$ExtensionInfoFromJson(json); 33 | } 34 | -------------------------------------------------------------------------------- /lib/entity/extension/extension_info/extension_info.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'extension_info.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _$ExtensionInfoImpl _$$ExtensionInfoImplFromJson(Map json) => 10 | _$ExtensionInfoImpl( 11 | package: json['package'] as String, 12 | label: json['label'] as String, 13 | versionName: json['version_name'] as String, 14 | versionCode: (json['version_code'] as num).toInt(), 15 | libVersion: (json['lib_version'] as num).toInt(), 16 | loadType: $enumDecode( 17 | _$ExtensionLoaderTypeEnumMap, json['extension_load_type']), 18 | path: json['path'] as String, 19 | readme: json['readme'] as String? ?? "", 20 | ); 21 | 22 | Map _$$ExtensionInfoImplToJson(_$ExtensionInfoImpl instance) => 23 | { 24 | 'package': instance.package, 25 | 'label': instance.label, 26 | 'version_name': instance.versionName, 27 | 'version_code': instance.versionCode, 28 | 'lib_version': instance.libVersion, 29 | 'extension_load_type': _$ExtensionLoaderTypeEnumMap[instance.loadType]!, 30 | 'path': instance.path, 31 | 'readme': instance.readme, 32 | }; 33 | 34 | const _$ExtensionLoaderTypeEnumMap = { 35 | ExtensionLoaderType.js: 'js', 36 | ExtensionLoaderType.mygopack: 'mygopack', 37 | ExtensionLoaderType.inner: 'inner', 38 | }; 39 | -------------------------------------------------------------------------------- /lib/entity/manga/manga_chapter/manga_chapter.dart: -------------------------------------------------------------------------------- 1 | import 'package:easy_mygo/entity/manga/manga_enum.dart'; 2 | import 'package:freezed_annotation/freezed_annotation.dart'; 3 | 4 | part 'manga_chapter.freezed.dart'; 5 | 6 | part 'manga_chapter.g.dart'; 7 | 8 | @freezed 9 | class MangaChapter with _$MangaChapter { 10 | factory MangaChapter({ 11 | // 章节名称 12 | required String label, 13 | 14 | // 章节 Id 15 | required String id, 16 | 17 | // 展示类型 18 | @Default(ChapterShowType.normal) 19 | @JsonKey(name: "show_type") 20 | ChapterShowType showType, 21 | 22 | // 最终会传递给阅读器的参数,这里先预埋 23 | @Default({}) Map parameter, 24 | 25 | // 交给源维护,可以用于透传一些东西 26 | @Default('') String ext, 27 | }) = _MangaChapter; 28 | 29 | factory MangaChapter.fromJson(Map json) => 30 | _$MangaChapterFromJson(json); 31 | } 32 | -------------------------------------------------------------------------------- /lib/entity/manga/manga_chapter/manga_chapter.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'manga_chapter.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _$MangaChapterImpl _$$MangaChapterImplFromJson(Map json) => 10 | _$MangaChapterImpl( 11 | label: json['label'] as String, 12 | id: json['id'] as String, 13 | showType: 14 | $enumDecodeNullable(_$ChapterShowTypeEnumMap, json['show_type']) ?? 15 | ChapterShowType.normal, 16 | parameter: (json['parameter'] as Map?)?.map( 17 | (k, e) => MapEntry(k, e as String), 18 | ) ?? 19 | const {}, 20 | ext: json['ext'] as String? ?? '', 21 | ); 22 | 23 | Map _$$MangaChapterImplToJson(_$MangaChapterImpl instance) => 24 | { 25 | 'label': instance.label, 26 | 'id': instance.id, 27 | 'show_type': _$ChapterShowTypeEnumMap[instance.showType]!, 28 | 'parameter': instance.parameter, 29 | 'ext': instance.ext, 30 | }; 31 | 32 | const _$ChapterShowTypeEnumMap = { 33 | ChapterShowType.normal: 'normal', 34 | ChapterShowType.singlePicture: 'singlePicture', 35 | }; 36 | -------------------------------------------------------------------------------- /lib/entity/manga/manga_cover/manga_cover.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:easy_mygo/entity/manga/manga_summary/manga_summary.dart'; 3 | import 'package:freezed_annotation/freezed_annotation.dart'; 4 | 5 | part 'manga_cover.g.dart'; 6 | part 'manga_cover.freezed.dart'; 7 | 8 | /// 漫画封面,一般在首页或者搜索页展示 9 | 10 | @freezed 11 | class MangaCover with _$MangaCover { 12 | 13 | factory MangaCover({ 14 | 15 | required String id, 16 | 17 | // cover 信息 18 | required String label, 19 | required String cover, 20 | required String intro, 21 | 22 | @JsonKey(name: "jump_url") 23 | required String jumpUrl, 24 | 25 | // 必要信息,但是由 Component 填充 26 | @Default("") String source, 27 | 28 | // 额外字段 29 | @Default("") String ext, 30 | }) = _MangaCover; 31 | 32 | factory MangaCover.fromJson(Map json) => 33 | _$MangaCoverFromJson(json); 34 | } 35 | 36 | extension MangaCoverExt on MangaCover { 37 | 38 | static final _identifyValues = Expando(); 39 | String get identify { 40 | return _identifyValues[this] ??= "$id-|-$source"; 41 | } 42 | 43 | static final _summaryValues = Expando(); 44 | MangaSummary get mangaSummary { 45 | return _summaryValues[this] ??= MangaSummary(source: source, id: id); 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /lib/entity/manga/manga_cover/manga_cover.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'manga_cover.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _$MangaCoverImpl _$$MangaCoverImplFromJson(Map json) => 10 | _$MangaCoverImpl( 11 | id: json['id'] as String, 12 | label: json['label'] as String, 13 | cover: json['cover'] as String, 14 | intro: json['intro'] as String, 15 | jumpUrl: json['jump_url'] as String, 16 | source: json['source'] as String? ?? "", 17 | ext: json['ext'] as String? ?? "", 18 | ); 19 | 20 | Map _$$MangaCoverImplToJson(_$MangaCoverImpl instance) => 21 | { 22 | 'id': instance.id, 23 | 'label': instance.label, 24 | 'cover': instance.cover, 25 | 'intro': instance.intro, 26 | 'jump_url': instance.jumpUrl, 27 | 'source': instance.source, 28 | 'ext': instance.ext, 29 | }; 30 | -------------------------------------------------------------------------------- /lib/entity/manga/manga_detailed/manga_detailed.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:easy_mygo/entity/manga/manga_enum.dart'; 3 | import 'package:easy_mygo/entity/manga/manga_summary/manga_summary.dart'; 4 | import 'package:freezed_annotation/freezed_annotation.dart'; 5 | 6 | part 'manga_detailed.g.dart'; 7 | part 'manga_detailed.freezed.dart'; 8 | 9 | /// 漫画详细信息,一般在漫画详情页展示 10 | 11 | @freezed 12 | class MangaDetailed with _$MangaDetailed { 13 | 14 | factory MangaDetailed({ 15 | required String id, 16 | 17 | // cover 信息 18 | required String label, 19 | required String cover, 20 | required String intro, 21 | @JsonKey(name: "jump_url") required String jumpUrl, 22 | 23 | // detailed 24 | @Default(false) @JsonKey(name: "is_detailed_load") bool isDetailedLoad, 25 | @Default("") String genre, 26 | @Default("") String description, 27 | @JsonKey(name: "update_strategy") @Default(MangaUpdateStrategy.always) MangaUpdateStrategy updateStrategy, 28 | @Default(false) bool isUpdate, 29 | @Default(MangaStatus.unknown) MangaStatus status, 30 | 31 | // 额外字段 32 | @Default("") String ext, 33 | 34 | // 必要信息,但是由 Component 填充 35 | @Default("") String source, 36 | }) = _MangaDetailed; 37 | 38 | factory MangaDetailed.fromJson(Map json) => 39 | _$MangaDetailedFromJson(json); 40 | } 41 | 42 | extension MangaDetailedExt on MangaDetailed { 43 | 44 | static final _identifyValues = Expando(); 45 | String get identify { 46 | return _identifyValues[this] ??= "$id-|-$source"; 47 | } 48 | 49 | static final _summaryValues = Expando(); 50 | MangaSummary get mangaSummary { 51 | return _summaryValues[this] ??= MangaSummary(source: source, id: id); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /lib/entity/manga/manga_detailed/manga_detailed.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'manga_detailed.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _$MangaDetailedImpl _$$MangaDetailedImplFromJson(Map json) => 10 | _$MangaDetailedImpl( 11 | id: json['id'] as String, 12 | label: json['label'] as String, 13 | cover: json['cover'] as String, 14 | intro: json['intro'] as String, 15 | jumpUrl: json['jump_url'] as String, 16 | isDetailedLoad: json['is_detailed_load'] as bool? ?? false, 17 | genre: json['genre'] as String? ?? "", 18 | description: json['description'] as String? ?? "", 19 | updateStrategy: $enumDecodeNullable( 20 | _$MangaUpdateStrategyEnumMap, json['update_strategy']) ?? 21 | MangaUpdateStrategy.always, 22 | isUpdate: json['isUpdate'] as bool? ?? false, 23 | status: $enumDecodeNullable(_$MangaStatusEnumMap, json['status']) ?? 24 | MangaStatus.unknown, 25 | ext: json['ext'] as String? ?? "", 26 | source: json['source'] as String? ?? "", 27 | ); 28 | 29 | Map _$$MangaDetailedImplToJson(_$MangaDetailedImpl instance) => 30 | { 31 | 'id': instance.id, 32 | 'label': instance.label, 33 | 'cover': instance.cover, 34 | 'intro': instance.intro, 35 | 'jump_url': instance.jumpUrl, 36 | 'is_detailed_load': instance.isDetailedLoad, 37 | 'genre': instance.genre, 38 | 'description': instance.description, 39 | 'update_strategy': _$MangaUpdateStrategyEnumMap[instance.updateStrategy]!, 40 | 'isUpdate': instance.isUpdate, 41 | 'status': _$MangaStatusEnumMap[instance.status]!, 42 | 'ext': instance.ext, 43 | 'source': instance.source, 44 | }; 45 | 46 | const _$MangaUpdateStrategyEnumMap = { 47 | MangaUpdateStrategy.always: 'always', 48 | MangaUpdateStrategy.onlyStrict: 'onlyStrict', 49 | MangaUpdateStrategy.never: 'never', 50 | }; 51 | 52 | const _$MangaStatusEnumMap = { 53 | MangaStatus.ongoing: 'ongoing', 54 | MangaStatus.complete: 'complete', 55 | MangaStatus.unknown: 'unknown', 56 | }; 57 | -------------------------------------------------------------------------------- /lib/entity/manga/manga_enum.dart: -------------------------------------------------------------------------------- 1 | 2 | enum MangaStatus { 3 | ongoing, 4 | complete, 5 | unknown 6 | } 7 | 8 | enum MangaUpdateStrategy { 9 | always, 10 | onlyStrict, 11 | never 12 | } 13 | 14 | // 章节展示的类型 15 | enum ChapterShowType { 16 | normal, // 普通类型,一个 Chapter 分为多个 Picture 17 | singlePicture // 单图片类型,一章只有一张大的 Picture,展示端需要适配(裁切或者只支持上下滑动模式) 18 | } 19 | 20 | // 图片加载类型,后续可能添加裁切功能? 21 | enum PictureLoadType { 22 | normal, // 普通类型,直接加载 MangaPicture 中 url 的图片(网络或本地) 23 | } -------------------------------------------------------------------------------- /lib/entity/manga/manga_picture/manga_picture.dart: -------------------------------------------------------------------------------- 1 | 2 | 3 | import 'package:easy_mygo/entity/manga/manga_enum.dart'; 4 | import 'package:freezed_annotation/freezed_annotation.dart'; 5 | 6 | part 'manga_picture.g.dart'; 7 | part 'manga_picture.freezed.dart'; 8 | 9 | /// 漫画某章节里的一页数据 10 | /// parameter 为额外参数,最终会传给特定 type 的加载器 11 | 12 | @freezed 13 | class MangaPicture with _$MangaPicture { 14 | factory MangaPicture({ 15 | // 图片 Url 16 | @Default("") String url, 17 | @Default(PictureLoadType.normal) PictureLoadType type, 18 | @Default({}) Map parameter, 19 | 20 | }) = _MangaPicture; 21 | 22 | factory MangaPicture.fromJson(Map json) 23 | => _$MangaPictureFromJson(json); 24 | } -------------------------------------------------------------------------------- /lib/entity/manga/manga_picture/manga_picture.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'manga_picture.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _$MangaPictureImpl _$$MangaPictureImplFromJson(Map json) => 10 | _$MangaPictureImpl( 11 | url: json['url'] as String? ?? "", 12 | type: $enumDecodeNullable(_$PictureLoadTypeEnumMap, json['type']) ?? 13 | PictureLoadType.normal, 14 | parameter: (json['parameter'] as Map?)?.map( 15 | (k, e) => MapEntry(k, e as String), 16 | ) ?? 17 | const {}, 18 | ); 19 | 20 | Map _$$MangaPictureImplToJson(_$MangaPictureImpl instance) => 21 | { 22 | 'url': instance.url, 23 | 'type': _$PictureLoadTypeEnumMap[instance.type]!, 24 | 'parameter': instance.parameter, 25 | }; 26 | 27 | const _$PictureLoadTypeEnumMap = { 28 | PictureLoadType.normal: 'normal', 29 | }; 30 | -------------------------------------------------------------------------------- /lib/entity/manga/manga_summary/manga_summary.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | part 'manga_summary.g.dart'; 4 | part 'manga_summary.freezed.dart'; 5 | 6 | /// 决定一部漫画的最小单元 7 | 8 | @freezed 9 | class MangaSummary with _$MangaSummary { 10 | factory MangaSummary({ 11 | // 必要信息 12 | required String source, 13 | required String id, 14 | 15 | // 额外字段 16 | @Default("") String ext, 17 | }) = _MangaSummary; 18 | 19 | factory MangaSummary.fromJson(Map json) => 20 | _$MangaSummaryFromJson(json); 21 | } 22 | -------------------------------------------------------------------------------- /lib/entity/manga/manga_summary/manga_summary.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'manga_summary.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _$MangaSummaryImpl _$$MangaSummaryImplFromJson(Map json) => 10 | _$MangaSummaryImpl( 11 | source: json['source'] as String, 12 | id: json['id'] as String, 13 | ext: json['ext'] as String? ?? "", 14 | ); 15 | 16 | Map _$$MangaSummaryImplToJson(_$MangaSummaryImpl instance) => 17 | { 18 | 'source': instance.source, 19 | 'id': instance.id, 20 | 'ext': instance.ext, 21 | }; 22 | -------------------------------------------------------------------------------- /lib/entity/novel/novel_chapter/novel_chapter.dart: -------------------------------------------------------------------------------- 1 | import 'package:easy_mygo/entity/novel/novel_enum.dart'; 2 | import 'package:freezed_annotation/freezed_annotation.dart'; 3 | 4 | part 'novel_chapter.freezed.dart'; 5 | 6 | part 'novel_chapter.g.dart'; 7 | 8 | @freezed 9 | class NovelChapter with _$NovelChapter { 10 | factory NovelChapter({ 11 | // 章节标题 12 | required String label, 13 | 14 | // 章节 id 15 | required String id, 16 | 17 | // 根据加载方式不同有不同含义 18 | required String src, 19 | 20 | // 加载方式 21 | @Default(NovelChapterLoadFrom.direct) 22 | @JsonKey(name: "load_from") 23 | NovelChapterLoadFrom loadFrom, 24 | 25 | // 加载类型 26 | @Default(NovelChapterLoadType.html) 27 | @JsonKey(name: "load_type") 28 | NovelChapterLoadType loadType, 29 | 30 | // 最终会传递给阅读器的参数,这里先预埋 31 | @Default({}) Map parameter, 32 | }) = _NovelChapter; 33 | 34 | factory NovelChapter.fromJson(Map json) => 35 | _$NovelChapterFromJson(json); 36 | } 37 | -------------------------------------------------------------------------------- /lib/entity/novel/novel_chapter/novel_chapter.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'novel_chapter.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _$NovelChapterImpl _$$NovelChapterImplFromJson(Map json) => 10 | _$NovelChapterImpl( 11 | label: json['label'] as String, 12 | id: json['id'] as String, 13 | src: json['src'] as String, 14 | loadFrom: $enumDecodeNullable( 15 | _$NovelChapterLoadFromEnumMap, json['load_from']) ?? 16 | NovelChapterLoadFrom.direct, 17 | loadType: $enumDecodeNullable( 18 | _$NovelChapterLoadTypeEnumMap, json['load_type']) ?? 19 | NovelChapterLoadType.html, 20 | parameter: (json['parameter'] as Map?)?.map( 21 | (k, e) => MapEntry(k, e as String), 22 | ) ?? 23 | const {}, 24 | ); 25 | 26 | Map _$$NovelChapterImplToJson(_$NovelChapterImpl instance) => 27 | { 28 | 'label': instance.label, 29 | 'id': instance.id, 30 | 'src': instance.src, 31 | 'load_from': _$NovelChapterLoadFromEnumMap[instance.loadFrom]!, 32 | 'load_type': _$NovelChapterLoadTypeEnumMap[instance.loadType]!, 33 | 'parameter': instance.parameter, 34 | }; 35 | 36 | const _$NovelChapterLoadFromEnumMap = { 37 | NovelChapterLoadFrom.url: 'url', 38 | NovelChapterLoadFrom.direct: 'direct', 39 | }; 40 | 41 | const _$NovelChapterLoadTypeEnumMap = { 42 | NovelChapterLoadType.epub: 'epub', 43 | NovelChapterLoadType.txt: 'txt', 44 | NovelChapterLoadType.pdf: 'pdf', 45 | NovelChapterLoadType.html: 'html', 46 | }; 47 | -------------------------------------------------------------------------------- /lib/entity/novel/novel_cover/novel_cover.dart: -------------------------------------------------------------------------------- 1 | import 'package:easy_mygo/entity/novel/novel_summary/novel_summary.dart'; 2 | import 'package:freezed_annotation/freezed_annotation.dart'; 3 | 4 | part 'novel_cover.g.dart'; 5 | part 'novel_cover.freezed.dart'; 6 | 7 | /// 小说封面,一般在首页或者搜索页展示 8 | 9 | @freezed 10 | class NovelCover with _$NovelCover { 11 | factory NovelCover({ 12 | // 必要信息 13 | required String id, 14 | 15 | // cover 信息 16 | required String label, 17 | required String cover, 18 | required String intro, 19 | @JsonKey(name: "jump_url") required String jumpUrl, 20 | 21 | // 必要信息,但是由 Component 填充 22 | @Default("") String source, 23 | 24 | // 额外字段 25 | @Default("") String ext, 26 | }) = _NovelCover; 27 | 28 | factory NovelCover.fromJson(Map json) => 29 | _$NovelCoverFromJson(json); 30 | } 31 | 32 | extension MangaCoverExt on NovelCover { 33 | static final _identifyValues = Expando(); 34 | String get identify { 35 | return _identifyValues[this] ??= "$id-|-$source"; 36 | } 37 | 38 | static final _summaryValues = Expando(); 39 | NovelSummary get mangaSummary { 40 | return _summaryValues[this] ??= NovelSummary(source: source, id: id); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /lib/entity/novel/novel_cover/novel_cover.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'novel_cover.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _$NovelCoverImpl _$$NovelCoverImplFromJson(Map json) => 10 | _$NovelCoverImpl( 11 | id: json['id'] as String, 12 | label: json['label'] as String, 13 | cover: json['cover'] as String, 14 | intro: json['intro'] as String, 15 | jumpUrl: json['jump_url'] as String, 16 | source: json['source'] as String? ?? "", 17 | ext: json['ext'] as String? ?? "", 18 | ); 19 | 20 | Map _$$NovelCoverImplToJson(_$NovelCoverImpl instance) => 21 | { 22 | 'id': instance.id, 23 | 'label': instance.label, 24 | 'cover': instance.cover, 25 | 'intro': instance.intro, 26 | 'jump_url': instance.jumpUrl, 27 | 'source': instance.source, 28 | 'ext': instance.ext, 29 | }; 30 | -------------------------------------------------------------------------------- /lib/entity/novel/novel_detailed/novel_detailed.dart: -------------------------------------------------------------------------------- 1 | import 'package:easy_mygo/entity/novel/novel_enum.dart'; 2 | import 'package:easy_mygo/entity/novel/novel_summary/novel_summary.dart'; 3 | import 'package:freezed_annotation/freezed_annotation.dart'; 4 | 5 | part 'novel_detailed.g.dart'; 6 | part 'novel_detailed.freezed.dart'; 7 | 8 | /// 小说详细信息,一般在详情页展示 9 | 10 | @freezed 11 | class NovelDetailed with _$NovelDetailed { 12 | factory NovelDetailed({ 13 | // 必要信息 14 | required String source, 15 | required String id, 16 | 17 | // cover 信息 18 | required String label, 19 | required String cover, 20 | required String intro, 21 | @JsonKey(name: "jump_url") required String jumpUrl, 22 | 23 | // detailed 24 | @Default(false) @JsonKey(name: "is_detailed_load") bool isDetailedLoad, 25 | @Default("") String genre, 26 | @Default("") String description, 27 | @JsonKey(name: "update_strategy") 28 | @Default(NovelUpdateStrategy.always) 29 | NovelUpdateStrategy updateStrategy, 30 | @Default(false) bool isUpdate, 31 | @Default(NovelStatus.unknown) NovelStatus status, 32 | 33 | // 额外字段 34 | @Default("") String ext, 35 | }) = _NovelDetailed; 36 | 37 | factory NovelDetailed.fromJson(Map json) => 38 | _$NovelDetailedFromJson(json); 39 | } 40 | 41 | extension MangaDetailedExt on NovelDetailed { 42 | static final _identifyValues = Expando(); 43 | String get identify { 44 | return _identifyValues[this] ??= "$id-|-$source"; 45 | } 46 | 47 | static final _summaryValues = Expando(); 48 | NovelSummary get mangaSummary { 49 | return _summaryValues[this] ??= NovelSummary(source: source, id: id); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /lib/entity/novel/novel_detailed/novel_detailed.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'novel_detailed.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _$NovelDetailedImpl _$$NovelDetailedImplFromJson(Map json) => 10 | _$NovelDetailedImpl( 11 | source: json['source'] as String, 12 | id: json['id'] as String, 13 | label: json['label'] as String, 14 | cover: json['cover'] as String, 15 | intro: json['intro'] as String, 16 | jumpUrl: json['jump_url'] as String, 17 | isDetailedLoad: json['is_detailed_load'] as bool? ?? false, 18 | genre: json['genre'] as String? ?? "", 19 | description: json['description'] as String? ?? "", 20 | updateStrategy: $enumDecodeNullable( 21 | _$NovelUpdateStrategyEnumMap, json['update_strategy']) ?? 22 | NovelUpdateStrategy.always, 23 | isUpdate: json['isUpdate'] as bool? ?? false, 24 | status: $enumDecodeNullable(_$NovelStatusEnumMap, json['status']) ?? 25 | NovelStatus.unknown, 26 | ext: json['ext'] as String? ?? "", 27 | ); 28 | 29 | Map _$$NovelDetailedImplToJson(_$NovelDetailedImpl instance) => 30 | { 31 | 'source': instance.source, 32 | 'id': instance.id, 33 | 'label': instance.label, 34 | 'cover': instance.cover, 35 | 'intro': instance.intro, 36 | 'jump_url': instance.jumpUrl, 37 | 'is_detailed_load': instance.isDetailedLoad, 38 | 'genre': instance.genre, 39 | 'description': instance.description, 40 | 'update_strategy': _$NovelUpdateStrategyEnumMap[instance.updateStrategy]!, 41 | 'isUpdate': instance.isUpdate, 42 | 'status': _$NovelStatusEnumMap[instance.status]!, 43 | 'ext': instance.ext, 44 | }; 45 | 46 | const _$NovelUpdateStrategyEnumMap = { 47 | NovelUpdateStrategy.always: 'always', 48 | NovelUpdateStrategy.onlyStrict: 'onlyStrict', 49 | NovelUpdateStrategy.never: 'never', 50 | }; 51 | 52 | const _$NovelStatusEnumMap = { 53 | NovelStatus.ongoing: 'ongoing', 54 | NovelStatus.complete: 'complete', 55 | NovelStatus.unknown: 'unknown', 56 | }; 57 | -------------------------------------------------------------------------------- /lib/entity/novel/novel_enum.dart: -------------------------------------------------------------------------------- 1 | enum NovelStatus { 2 | ongoing, 3 | complete, 4 | unknown, 5 | } 6 | 7 | enum NovelUpdateStrategy { 8 | // 总是检查更新,一般用于连载中 9 | always, 10 | // 只有严格模式(手动触发全体更新)才更新 11 | onlyStrict, 12 | // 永不更新 13 | never 14 | } 15 | 16 | enum NovelVolumeOrganizeType { 17 | // 最终返回的 List 是一个一个章节,一般用于 web 版, 18 | // 一个 Chapter 最终会被缓存成一个 txt,业务需要打包多个 txt 后再输入阅读器 19 | multiChapter, 20 | 21 | // 最终返回的 List 中只有一个 Chapter, 22 | // 一般用于 文库版,比如 epub 等电子书文件格式,Chapter 由文件本身区分 23 | singleChapter, 24 | } 25 | 26 | // 加载方式 27 | // url 文件或网络 28 | // 直出 29 | enum NovelChapterLoadFrom { 30 | url, 31 | direct, 32 | } 33 | 34 | // 加载类型(数据类型) 35 | enum NovelChapterLoadType { 36 | epub, // epub 37 | txt, // txt 38 | pdf, // pdf 39 | html, // 支持一些标签 40 | } 41 | -------------------------------------------------------------------------------- /lib/entity/novel/novel_summary/novel_summary.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | part 'novel_summary.g.dart'; 4 | part 'novel_summary.freezed.dart'; 5 | 6 | /// 决定一个小说的最小单元 7 | 8 | @freezed 9 | class NovelSummary with _$NovelSummary { 10 | factory NovelSummary({ 11 | // 必要信息 12 | required String source, 13 | required String id, 14 | 15 | // 额外字段 16 | @Default("") String ext, 17 | }) = _NovelSummary; 18 | 19 | factory NovelSummary.fromJson(Map json) => 20 | _$NovelSummaryFromJson(json); 21 | } 22 | -------------------------------------------------------------------------------- /lib/entity/novel/novel_summary/novel_summary.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'novel_summary.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _$NovelSummaryImpl _$$NovelSummaryImplFromJson(Map json) => 10 | _$NovelSummaryImpl( 11 | source: json['source'] as String, 12 | id: json['id'] as String, 13 | ext: json['ext'] as String? ?? "", 14 | ); 15 | 16 | Map _$$NovelSummaryImplToJson(_$NovelSummaryImpl instance) => 17 | { 18 | 'source': instance.source, 19 | 'id': instance.id, 20 | 'ext': instance.ext, 21 | }; 22 | -------------------------------------------------------------------------------- /lib/entity/novel/novel_volume/novel_volume.dart: -------------------------------------------------------------------------------- 1 | import 'package:easy_mygo/entity/novel/novel_enum.dart'; 2 | import 'package:freezed_annotation/freezed_annotation.dart'; 3 | 4 | part 'novel_volume.freezed.dart'; 5 | 6 | part 'novel_volume.g.dart'; 7 | 8 | @freezed 9 | class NovelVolume with _$NovelVolume { 10 | factory NovelVolume({ 11 | // 卷名称 12 | required String label, 13 | 14 | // 卷 id 15 | required String id, 16 | 17 | // 组织形式 18 | @Default(NovelVolumeOrganizeType.multiChapter) 19 | @JsonKey(name: "organize_type") 20 | organizeType, 21 | 22 | // 最终会传递给阅读器的参数,这里先预埋 23 | @Default({}) Map parameter, 24 | 25 | // 交给源维护,可以用于透传一些东西 26 | @Default('') String ext, 27 | }) = _NovelVolume; 28 | 29 | factory NovelVolume.fromJson(Map json) => 30 | _$NovelVolumeFromJson(json); 31 | } 32 | -------------------------------------------------------------------------------- /lib/entity/novel/novel_volume/novel_volume.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'novel_volume.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _$NovelVolumeImpl _$$NovelVolumeImplFromJson(Map json) => 10 | _$NovelVolumeImpl( 11 | label: json['label'] as String, 12 | id: json['id'] as String, 13 | organizeType: 14 | json['organize_type'] ?? NovelVolumeOrganizeType.multiChapter, 15 | parameter: (json['parameter'] as Map?)?.map( 16 | (k, e) => MapEntry(k, e as String), 17 | ) ?? 18 | const {}, 19 | ext: json['ext'] as String? ?? '', 20 | ); 21 | 22 | Map _$$NovelVolumeImplToJson(_$NovelVolumeImpl instance) => 23 | { 24 | 'label': instance.label, 25 | 'id': instance.id, 26 | 'organize_type': instance.organizeType, 27 | 'parameter': instance.parameter, 28 | 'ext': instance.ext, 29 | }; 30 | -------------------------------------------------------------------------------- /lib/entity/source/source_data/source_data.dart: -------------------------------------------------------------------------------- 1 | import 'package:easy_mygo/entity/source/source_info/source_info.dart'; 2 | import 'package:easy_mygo/plugin/component/api/component.dart'; 3 | 4 | 5 | enum SourceState { 6 | loaded, 7 | error 8 | } 9 | 10 | class SourceData { 11 | 12 | SourceInfo info; 13 | List? components; 14 | SourceState state; 15 | String errorMsg; 16 | 17 | SourceData({ 18 | required this.info, 19 | this.components, 20 | this.state = SourceState.loaded, 21 | this.errorMsg = "", 22 | }); 23 | 24 | SourceData copyWith({ 25 | SourceInfo? info, 26 | List? components, 27 | SourceState? state, 28 | String? errorMsg, 29 | }){ 30 | return SourceData( 31 | info: info ?? this.info, 32 | components: components ?? this.components, 33 | state: state ?? this.state, 34 | errorMsg: errorMsg ?? this.errorMsg, 35 | ); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /lib/entity/source/source_info/source_info.dart: -------------------------------------------------------------------------------- 1 | import 'package:easy_mygo/plugin/source/utils/source_utils_scope.dart'; 2 | import 'package:freezed_annotation/freezed_annotation.dart'; 3 | 4 | part 'source_info.freezed.dart'; 5 | 6 | part 'source_info.g.dart'; 7 | 8 | enum SourceType { manga, novel } 9 | enum SourceLoaderType { js, inner } 10 | 11 | @freezed 12 | class SourceInfo with _$SourceInfo { 13 | factory SourceInfo({ 14 | // 单个 Extension 里的所有 Source 的 key 需要唯一 15 | required String key, 16 | // 所在 Extension 的包名 17 | @JsonKey(name: 'from_package') required String fromPackage, 18 | required String label, 19 | required SourceType type, 20 | @JsonKey(name: 'version_name') required String versionName, 21 | @JsonKey(name: 'version_code') required int versionCode, 22 | required String path, 23 | @JsonKey(name: 'loader_type') required SourceLoaderType loaderType, 24 | @Default("") String description, 25 | @Default("") String header, 26 | }) = _SourceInfo; 27 | 28 | factory SourceInfo.fromJson(Map json) => 29 | _$SourceInfoFromJson(json); 30 | } 31 | 32 | extension SourceInfoExt on SourceInfo { 33 | static final _identifyValues = Expando(); 34 | 35 | String get identify { 36 | return _identifyValues[this] ??= "$fromPackage-|-$key"; 37 | } 38 | 39 | static final _sourceUtilsScopeValues = Expando(); 40 | SourceUtilsScope get sourceUtilsScope { 41 | return _sourceUtilsScopeValues[this] ??= SourceUtilsScope(this); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/entity/source/source_info/source_info.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'source_info.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _$SourceInfoImpl _$$SourceInfoImplFromJson(Map json) => 10 | _$SourceInfoImpl( 11 | key: json['key'] as String, 12 | fromPackage: json['from_package'] as String, 13 | label: json['label'] as String, 14 | type: $enumDecode(_$SourceTypeEnumMap, json['type']), 15 | versionName: json['version_name'] as String, 16 | versionCode: (json['version_code'] as num).toInt(), 17 | path: json['path'] as String, 18 | loaderType: $enumDecode(_$SourceLoaderTypeEnumMap, json['loader_type']), 19 | description: json['description'] as String? ?? "", 20 | header: json['header'] as String? ?? "", 21 | ); 22 | 23 | Map _$$SourceInfoImplToJson(_$SourceInfoImpl instance) => 24 | { 25 | 'key': instance.key, 26 | 'from_package': instance.fromPackage, 27 | 'label': instance.label, 28 | 'type': _$SourceTypeEnumMap[instance.type]!, 29 | 'version_name': instance.versionName, 30 | 'version_code': instance.versionCode, 31 | 'path': instance.path, 32 | 'loader_type': _$SourceLoaderTypeEnumMap[instance.loaderType]!, 33 | 'description': instance.description, 34 | 'header': instance.header, 35 | }; 36 | 37 | const _$SourceTypeEnumMap = { 38 | SourceType.manga: 'manga', 39 | SourceType.novel: 'novel', 40 | }; 41 | 42 | const _$SourceLoaderTypeEnumMap = { 43 | SourceLoaderType.js: 'js', 44 | SourceLoaderType.inner: 'inner', 45 | }; 46 | -------------------------------------------------------------------------------- /lib/hive_registrar.g.dart: -------------------------------------------------------------------------------- 1 | import 'package:hive_ce/hive.dart'; 2 | 3 | extension HiveRegistrar on HiveInterface { 4 | void registerAdapters() { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /lib/l10n/arb/intl_zh_CN.arb: -------------------------------------------------------------------------------- 1 | { 2 | "@@locale": "zh_CN", 3 | "app_name": "纯纯 Mygo", 4 | "@app_name": { 5 | "description": "软件名称" 6 | }, 7 | "home": "主页", 8 | "more": "更多", 9 | "history": "历史", 10 | "library": "书架", 11 | "pull_to_refresh": "下拉刷新", 12 | "release_ready": "释放刷新", 13 | "loading": "加载中...", 14 | "success": "成功", 15 | "last_update_time": "最后更新时间" 16 | } -------------------------------------------------------------------------------- /lib/l10n/intl/messages_all.dart: -------------------------------------------------------------------------------- 1 | // DO NOT EDIT. This is code generated via package:intl/generate_localized.dart 2 | // This is a library that looks up messages for specific locales by 3 | // delegating to the appropriate library. 4 | 5 | // Ignore issues from commonly used lints in this file. 6 | // ignore_for_file:implementation_imports, file_names, unnecessary_new 7 | // ignore_for_file:unnecessary_brace_in_string_interps, directives_ordering 8 | // ignore_for_file:argument_type_not_assignable, invalid_assignment 9 | // ignore_for_file:prefer_single_quotes, prefer_generic_function_type_aliases 10 | // ignore_for_file:comment_references 11 | 12 | import 'dart:async'; 13 | 14 | import 'package:flutter/foundation.dart'; 15 | import 'package:intl/intl.dart'; 16 | import 'package:intl/message_lookup_by_library.dart'; 17 | import 'package:intl/src/intl_helpers.dart'; 18 | 19 | import 'messages_zh_CN.dart' as messages_zh_cn; 20 | 21 | typedef Future LibraryLoader(); 22 | Map _deferredLibraries = { 23 | 'zh_CN': () => new SynchronousFuture(null), 24 | }; 25 | 26 | MessageLookupByLibrary? _findExact(String localeName) { 27 | switch (localeName) { 28 | case 'zh_CN': 29 | return messages_zh_cn.messages; 30 | default: 31 | return null; 32 | } 33 | } 34 | 35 | /// User programs should call this before using [localeName] for messages. 36 | Future initializeMessages(String localeName) { 37 | var availableLocale = Intl.verifiedLocale( 38 | localeName, (locale) => _deferredLibraries[locale] != null, 39 | onFailure: (_) => null); 40 | if (availableLocale == null) { 41 | return new SynchronousFuture(false); 42 | } 43 | var lib = _deferredLibraries[availableLocale]; 44 | lib == null ? new SynchronousFuture(false) : lib(); 45 | initializeInternalMessageLookup(() => new CompositeMessageLookup()); 46 | messageLookup.addLocale(availableLocale, _findGeneratedMessagesFor); 47 | return new SynchronousFuture(true); 48 | } 49 | 50 | bool _messagesExistFor(String locale) { 51 | try { 52 | return _findExact(locale) != null; 53 | } catch (e) { 54 | return false; 55 | } 56 | } 57 | 58 | MessageLookupByLibrary? _findGeneratedMessagesFor(String locale) { 59 | var actualLocale = 60 | Intl.verifiedLocale(locale, _messagesExistFor, onFailure: (_) => null); 61 | if (actualLocale == null) return null; 62 | return _findExact(actualLocale); 63 | } 64 | -------------------------------------------------------------------------------- /lib/l10n/intl/messages_zh_CN.dart: -------------------------------------------------------------------------------- 1 | // DO NOT EDIT. This is code generated via package:intl/generate_localized.dart 2 | // This is a library that provides messages for a zh_CN locale. All the 3 | // messages from the main program should be duplicated here with the same 4 | // function name. 5 | 6 | // Ignore issues from commonly used lints in this file. 7 | // ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new 8 | // ignore_for_file:prefer_single_quotes,comment_references, directives_ordering 9 | // ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases 10 | // ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes 11 | // ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes 12 | 13 | import 'package:intl/intl.dart'; 14 | import 'package:intl/message_lookup_by_library.dart'; 15 | 16 | final messages = new MessageLookup(); 17 | 18 | typedef String MessageIfAbsent(String messageStr, List args); 19 | 20 | class MessageLookup extends MessageLookupByLibrary { 21 | String get localeName => 'zh_CN'; 22 | 23 | final messages = _notInlinedMessages(_notInlinedMessages); 24 | static Map _notInlinedMessages(_) => { 25 | "app_name": MessageLookupByLibrary.simpleMessage("纯纯 Mygo"), 26 | "history": MessageLookupByLibrary.simpleMessage("历史"), 27 | "home": MessageLookupByLibrary.simpleMessage("主页"), 28 | "last_update_time": MessageLookupByLibrary.simpleMessage("最后更新时间"), 29 | "library": MessageLookupByLibrary.simpleMessage("书架"), 30 | "loading": MessageLookupByLibrary.simpleMessage("加载中..."), 31 | "more": MessageLookupByLibrary.simpleMessage("更多"), 32 | "pull_to_refresh": MessageLookupByLibrary.simpleMessage("下拉刷新"), 33 | "release_ready": MessageLookupByLibrary.simpleMessage("释放刷新"), 34 | "success": MessageLookupByLibrary.simpleMessage("成功") 35 | }; 36 | } 37 | -------------------------------------------------------------------------------- /lib/plugin/component/api/component.dart: -------------------------------------------------------------------------------- 1 | 2 | 3 | import 'package:easy_mygo/entity/source/source_info/source_info.dart'; 4 | 5 | 6 | 7 | 8 | class Component { 9 | 10 | final SourceInfo sourceInfo; 11 | Component(this.sourceInfo); 12 | } -------------------------------------------------------------------------------- /lib/plugin/component/api/manga/detailed/manga_detailed_component.dart: -------------------------------------------------------------------------------- 1 | import 'package:easy_mygo/entity/manga/manga_summary/manga_summary.dart'; 2 | import 'package:easy_mygo/plugin/component/api/component.dart'; 3 | import 'package:easy_mygo/plugin/component/api/manga/detailed/resp/detailed_resp.dart'; 4 | import 'package:easy_mygo/plugin/component/api/payload/component_payload.dart'; 5 | 6 | abstract class MangaDetailedComponent extends Component { 7 | MangaDetailedComponent(super.sourceInfo); 8 | 9 | Future getMangaDetailed(MangaSummary summary); 10 | 11 | Future performGetMangaDetailed( 12 | MangaSummary summary) async { 13 | try { 14 | return await getMangaDetailed(summary); 15 | } catch (e) { 16 | if (e is ComponentPayload) { 17 | return MangaDetailedResp(payload: e); 18 | } else { 19 | return MangaDetailedResp( 20 | payload: ComponentPayload( 21 | code: ComponentPayload.codeCallError, 22 | msg: e.toString(), 23 | raw: e)); 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lib/plugin/component/api/manga/detailed/resp/detailed_resp.dart: -------------------------------------------------------------------------------- 1 | 2 | 3 | import 'package:easy_mygo/entity/manga/manga_chapter/manga_chapter.dart'; 4 | import 'package:easy_mygo/entity/manga/manga_detailed/manga_detailed.dart'; 5 | import 'package:easy_mygo/plugin/component/api/payload/component_payload.dart'; 6 | import 'package:freezed_annotation/freezed_annotation.dart'; 7 | 8 | part 'detailed_resp.freezed.dart'; 9 | part 'detailed_resp.g.dart'; 10 | 11 | @freezed 12 | class MangaDetailedResp with _$MangaDetailedResp { 13 | 14 | factory MangaDetailedResp({ 15 | @Default(null) MangaDetailed? detailed, 16 | @Default(null) List? chapters, 17 | required ComponentPayload payload, 18 | }) = _MangaDetailedResp; 19 | 20 | factory MangaDetailedResp.fromJson(Map json) 21 | => _$MangaDetailedRespFromJson(json); 22 | } -------------------------------------------------------------------------------- /lib/plugin/component/api/manga/detailed/resp/detailed_resp.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'detailed_resp.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _$MangaDetailedRespImpl _$$MangaDetailedRespImplFromJson( 10 | Map json) => 11 | _$MangaDetailedRespImpl( 12 | detailed: json['detailed'] == null 13 | ? null 14 | : MangaDetailed.fromJson(json['detailed'] as Map), 15 | chapters: (json['chapters'] as List?) 16 | ?.map((e) => MangaChapter.fromJson(e as Map)) 17 | .toList() ?? 18 | null, 19 | payload: 20 | ComponentPayload.fromJson(json['payload'] as Map), 21 | ); 22 | 23 | Map _$$MangaDetailedRespImplToJson( 24 | _$MangaDetailedRespImpl instance) => 25 | { 26 | 'detailed': instance.detailed, 27 | 'chapters': instance.chapters, 28 | 'payload': instance.payload, 29 | }; 30 | -------------------------------------------------------------------------------- /lib/plugin/component/api/manga/home/manga_home_component.dart: -------------------------------------------------------------------------------- 1 | import 'package:easy_mygo/entity/book/home_tab/book_home_tab.dart'; 2 | import 'package:easy_mygo/plugin/component/api/component.dart'; 3 | import 'package:easy_mygo/plugin/component/api/manga/home/page/home_page.dart'; 4 | import 'package:easy_mygo/plugin/component/api/manga/home/resp/home_resp.dart'; 5 | 6 | abstract class MangaHomeComponent extends Component { 7 | MangaHomeComponent(super.sourceInfo); 8 | 9 | /// 获取一级 tab 列表 10 | Future getHomeTab(); 11 | 12 | /// 获取二级 tab 列表 13 | Future getSecondTab(BookHomeTab tab); 14 | 15 | /// 获取一级 tab 对应的页面(如果没有二级 tab 的话) 16 | Future getPageWithHomeTab(BookHomeTab tab); 17 | 18 | /// 获取二级 tab 对应的页面 19 | Future getPageWithSecondTab( 20 | BookHomeTab homeTab, BookSecondTab secondTab); 21 | 22 | /// 获取页面中某一页的漫画数据 23 | Future loadPageData(MangaHomePage page, String key); 24 | } 25 | -------------------------------------------------------------------------------- /lib/plugin/component/api/manga/home/page/home_page.dart: -------------------------------------------------------------------------------- 1 | 2 | 3 | import 'package:freezed_annotation/freezed_annotation.dart'; 4 | 5 | part 'home_page.freezed.dart'; 6 | part 'home_page.g.dart'; 7 | 8 | @freezed 9 | class MangaHomePage with _$MangaHomePage { 10 | factory MangaHomePage({ 11 | required String id, 12 | required String label, 13 | // 是否有 封面 14 | @JsonKey(name: "has_cover") @Default(true) bool hasCover, 15 | 16 | // 初始页面 key 17 | @JsonKey(name: "init_key") @Default('') String initKey, 18 | 19 | // 可以让源透传一些信息 20 | @Default('') String ext, 21 | }) = _MangaHomePage; 22 | 23 | factory MangaHomePage.fromJson(Map json) => 24 | _$MangaHomePageFromJson(json); 25 | } -------------------------------------------------------------------------------- /lib/plugin/component/api/manga/home/page/home_page.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'home_page.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _$MangaHomePageImpl _$$MangaHomePageImplFromJson(Map json) => 10 | _$MangaHomePageImpl( 11 | id: json['id'] as String, 12 | label: json['label'] as String, 13 | hasCover: json['has_cover'] as bool? ?? true, 14 | initKey: json['init_key'] as String? ?? '', 15 | ext: json['ext'] as String? ?? '', 16 | ); 17 | 18 | Map _$$MangaHomePageImplToJson(_$MangaHomePageImpl instance) => 19 | { 20 | 'id': instance.id, 21 | 'label': instance.label, 22 | 'has_cover': instance.hasCover, 23 | 'init_key': instance.initKey, 24 | 'ext': instance.ext, 25 | }; 26 | -------------------------------------------------------------------------------- /lib/plugin/component/api/manga/home/resp/home_resp.dart: -------------------------------------------------------------------------------- 1 | import 'package:easy_mygo/entity/book/home_tab/book_home_tab.dart'; 2 | import 'package:easy_mygo/entity/manga/manga_cover/manga_cover.dart'; 3 | import 'package:easy_mygo/plugin/component/api/manga/home/page/home_page.dart'; 4 | import 'package:easy_mygo/plugin/component/api/payload/component_payload.dart'; 5 | import 'package:freezed_annotation/freezed_annotation.dart'; 6 | 7 | part 'home_resp.g.dart'; 8 | part 'home_resp.freezed.dart'; 9 | 10 | /// 获取一级 tab 11 | @freezed 12 | class MangaGetHomeTabResp with _$MangaGetHomeTabResp { 13 | factory MangaGetHomeTabResp({ 14 | @JsonKey(name: "tab_list") @Default(null) List? tabList, 15 | required ComponentPayload payload, 16 | }) = _MangaGetHomeTabResp; 17 | 18 | factory MangaGetHomeTabResp.fromJson(Map json) => 19 | _$MangaGetHomeTabRespFromJson(json); 20 | } 21 | 22 | /// 获取二级 tab 23 | @freezed 24 | class MangaGetSecondTabResp with _$MangaGetSecondTabResp { 25 | factory MangaGetSecondTabResp({ 26 | @JsonKey(name: "tab_list") @Default(null) List? tabList, 27 | required ComponentPayload payload, 28 | }) = _MangaGetSecondTabResp; 29 | 30 | factory MangaGetSecondTabResp.fromJson(Map json) => 31 | _$MangaGetSecondTabRespFromJson(json); 32 | } 33 | 34 | /// 获取 tab 对应的 page 35 | @freezed 36 | class MangaGetHomePageResp with _$MangaGetHomePageResp { 37 | factory MangaGetHomePageResp({ 38 | @Default(null) MangaHomePage? page, 39 | required ComponentPayload payload, 40 | }) = _MangaGetHomePageResp; 41 | 42 | factory MangaGetHomePageResp.fromJson(Map json) => 43 | _$MangaGetHomePageRespFromJson(json); 44 | } 45 | 46 | /// 加载 page 里某一页数据 47 | @freezed 48 | class MangaGetHomeCoverResp with _$MangaGetHomeCoverResp { 49 | factory MangaGetHomeCoverResp({ 50 | @Default(null) List? data, 51 | 52 | // 下一页的 key,如果为空则代表最后一页 53 | @JsonKey(name: "next_key") @Default(null) String? nextKey, 54 | required ComponentPayload payload, 55 | }) = _MangaGetHomeCoverResp; 56 | 57 | factory MangaGetHomeCoverResp.fromJson(Map json) => 58 | _$MangaGetHomeCoverRespFromJson(json); 59 | } 60 | -------------------------------------------------------------------------------- /lib/plugin/component/api/manga/home/tab/home_tab.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | part 'home_tab.freezed.dart'; 4 | part 'home_tab.g.dart'; 5 | 6 | 7 | /// 一级 tab 8 | @freezed 9 | class MangaHomeTab with _$MangaHomeTab { 10 | factory MangaHomeTab({ 11 | required String id, 12 | required String label, 13 | // 是否有 二级 tab 14 | @JsonKey(name: "has_second_tab") required bool hasSecondTab, 15 | 16 | // 可以让源透传一些信息 17 | @Default('') String ext, 18 | }) = _MangaHomeTab; 19 | 20 | factory MangaHomeTab.fromJson(Map json) => 21 | _$MangaHomeTabFromJson(json); 22 | } 23 | 24 | /// 二级 tab 25 | @freezed 26 | class MangaHomeSecondTab with _$MangaHomeSecondTab { 27 | factory MangaHomeSecondTab({ 28 | required String id, 29 | required String label, 30 | @Default('') String ext, 31 | }) = _MangaHomeSecondTab; 32 | 33 | factory MangaHomeSecondTab.fromJson(Map json) => 34 | _$MangaHomeSecondTabFromJson(json); 35 | } 36 | -------------------------------------------------------------------------------- /lib/plugin/component/api/manga/home/tab/home_tab.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'home_tab.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _$MangaHomeTabImpl _$$MangaHomeTabImplFromJson(Map json) => 10 | _$MangaHomeTabImpl( 11 | id: json['id'] as String, 12 | label: json['label'] as String, 13 | hasSecondTab: json['has_second_tab'] as bool, 14 | ext: json['ext'] as String? ?? '', 15 | ); 16 | 17 | Map _$$MangaHomeTabImplToJson(_$MangaHomeTabImpl instance) => 18 | { 19 | 'id': instance.id, 20 | 'label': instance.label, 21 | 'has_second_tab': instance.hasSecondTab, 22 | 'ext': instance.ext, 23 | }; 24 | 25 | _$MangaHomeSecondTabImpl _$$MangaHomeSecondTabImplFromJson( 26 | Map json) => 27 | _$MangaHomeSecondTabImpl( 28 | id: json['id'] as String, 29 | label: json['label'] as String, 30 | ext: json['ext'] as String? ?? '', 31 | ); 32 | 33 | Map _$$MangaHomeSecondTabImplToJson( 34 | _$MangaHomeSecondTabImpl instance) => 35 | { 36 | 'id': instance.id, 37 | 'label': instance.label, 38 | 'ext': instance.ext, 39 | }; 40 | -------------------------------------------------------------------------------- /lib/plugin/component/api/manga/read/manga_read_component.dart: -------------------------------------------------------------------------------- 1 | import 'package:easy_mygo/entity/manga/manga_chapter/manga_chapter.dart'; 2 | import 'package:easy_mygo/entity/manga/manga_detailed/manga_detailed.dart'; 3 | import 'package:easy_mygo/plugin/component/api/component.dart'; 4 | import 'package:easy_mygo/plugin/component/api/manga/read/resp/read_resp.dart'; 5 | import 'package:easy_mygo/plugin/component/api/payload/component_payload.dart'; 6 | 7 | abstract class MangaReadComponent extends Component { 8 | MangaReadComponent(super.sourceInfo); 9 | 10 | /// throws: ComponentPayload 11 | Future getMangaPicture( 12 | MangaDetailed detailed, MangaChapter chapter); 13 | 14 | Future performGetMangaPicture( 15 | MangaDetailed detailed, MangaChapter chapter) async { 16 | try { 17 | return await getMangaPicture(detailed, chapter); 18 | } catch (e) { 19 | if (e is ComponentPayload) { 20 | return MangaReadResp(payload: e); 21 | } else { 22 | return MangaReadResp( 23 | payload: ComponentPayload( 24 | code: ComponentPayload.codeCallError, 25 | msg: e.toString(), 26 | raw: e)); 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lib/plugin/component/api/manga/read/resp/read_resp.dart: -------------------------------------------------------------------------------- 1 | import 'package:easy_mygo/entity/manga/manga_picture/manga_picture.dart'; 2 | import 'package:easy_mygo/plugin/component/api/payload/component_payload.dart'; 3 | import 'package:freezed_annotation/freezed_annotation.dart'; 4 | 5 | part 'read_resp.freezed.dart'; 6 | part 'read_resp.g.dart'; 7 | 8 | @freezed 9 | class MangaReadResp with _$MangaReadResp { 10 | factory MangaReadResp({ 11 | @Default(null) List? pictures, 12 | required ComponentPayload payload, 13 | }) = _MangaReadResp; 14 | 15 | factory MangaReadResp.fromJson(Map json) => 16 | _$MangaReadRespFromJson(json); 17 | } 18 | -------------------------------------------------------------------------------- /lib/plugin/component/api/manga/read/resp/read_resp.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'read_resp.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _$MangaReadRespImpl _$$MangaReadRespImplFromJson(Map json) => 10 | _$MangaReadRespImpl( 11 | pictures: (json['pictures'] as List?) 12 | ?.map((e) => MangaPicture.fromJson(e as Map)) 13 | .toList() ?? 14 | null, 15 | payload: 16 | ComponentPayload.fromJson(json['payload'] as Map), 17 | ); 18 | 19 | Map _$$MangaReadRespImplToJson(_$MangaReadRespImpl instance) => 20 | { 21 | 'pictures': instance.pictures, 22 | 'payload': instance.payload, 23 | }; 24 | -------------------------------------------------------------------------------- /lib/plugin/component/api/manga/search/manga_search_component.dart: -------------------------------------------------------------------------------- 1 | 2 | 3 | import 'package:easy_mygo/plugin/component/api/component.dart'; 4 | import 'package:easy_mygo/plugin/component/api/manga/search/resp/search_resp.dart'; 5 | import 'package:easy_mygo/plugin/component/api/payload/component_payload.dart'; 6 | 7 | abstract class MangaSearchComponent extends Component { 8 | 9 | MangaSearchComponent(super.sourceInfo); 10 | 11 | Future getInitKey(String key); 12 | 13 | Future search(String key, String keyword); 14 | 15 | Future performSearch(String key, String keyword) async { 16 | try { 17 | return await search(key, keyword); 18 | } catch (e) { 19 | if (e is ComponentPayload) { 20 | return MangaSearchResp(payload: e); 21 | } else { 22 | return MangaSearchResp( 23 | payload: ComponentPayload( 24 | code: ComponentPayload.codeCallError, 25 | msg: e.toString(), 26 | raw: e)); 27 | } 28 | } 29 | } 30 | 31 | } -------------------------------------------------------------------------------- /lib/plugin/component/api/manga/search/resp/search_resp.dart: -------------------------------------------------------------------------------- 1 | import 'package:easy_mygo/entity/manga/manga_cover/manga_cover.dart'; 2 | import 'package:easy_mygo/plugin/component/api/payload/component_payload.dart'; 3 | import 'package:freezed_annotation/freezed_annotation.dart'; 4 | 5 | part 'search_resp.g.dart'; 6 | part 'search_resp.freezed.dart'; 7 | 8 | @freezed 9 | class MangaSearchResp with _$MangaSearchResp { 10 | 11 | factory MangaSearchResp({ 12 | // 当页结果 13 | @Default(null) List? data, 14 | 15 | // 下一页的 key,为 null 则代表没有下一页 16 | @Default(null) @JsonKey(name: "next_key") String? nextKey, 17 | required ComponentPayload payload, 18 | }) = _MangaSearchResp; 19 | 20 | factory MangaSearchResp.fromJson(Map json) => 21 | _$MangaSearchRespFromJson(json); 22 | } 23 | -------------------------------------------------------------------------------- /lib/plugin/component/api/manga/search/resp/search_resp.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'search_resp.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _$MangaSearchRespImpl _$$MangaSearchRespImplFromJson( 10 | Map json) => 11 | _$MangaSearchRespImpl( 12 | data: (json['data'] as List?) 13 | ?.map((e) => MangaCover.fromJson(e as Map)) 14 | .toList() ?? 15 | null, 16 | nextKey: json['next_key'] as String? ?? null, 17 | payload: 18 | ComponentPayload.fromJson(json['payload'] as Map), 19 | ); 20 | 21 | Map _$$MangaSearchRespImplToJson( 22 | _$MangaSearchRespImpl instance) => 23 | { 24 | 'data': instance.data, 25 | 'next_key': instance.nextKey, 26 | 'payload': instance.payload, 27 | }; 28 | -------------------------------------------------------------------------------- /lib/plugin/component/api/novel/detailed/novel_detailed_component.dart: -------------------------------------------------------------------------------- 1 | import 'package:easy_mygo/entity/novel/novel_summary/novel_summary.dart'; 2 | import 'package:easy_mygo/plugin/component/api/component.dart'; 3 | import 'package:easy_mygo/plugin/component/api/novel/detailed/resp/detailed_resp.dart'; 4 | import 'package:easy_mygo/plugin/component/api/payload/component_payload.dart'; 5 | 6 | abstract class NovelDetailedComponent extends Component { 7 | NovelDetailedComponent(super.sourceInfo); 8 | 9 | Future getNovelDetailed(NovelSummary summary); 10 | 11 | Future performGetNovelDetailed( 12 | NovelSummary summary) async { 13 | try { 14 | return await getNovelDetailed(summary); 15 | } catch (e) { 16 | if (e is ComponentPayload) { 17 | return NovelDetailedResp(payload: e); 18 | } else { 19 | return NovelDetailedResp( 20 | payload: ComponentPayload( 21 | code: ComponentPayload.codeCallError, 22 | msg: e.toString(), 23 | raw: e)); 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lib/plugin/component/api/novel/detailed/resp/detailed_resp.dart: -------------------------------------------------------------------------------- 1 | import 'package:easy_mygo/entity/novel/novel_detailed/novel_detailed.dart'; 2 | import 'package:easy_mygo/entity/novel/novel_volume/novel_volume.dart'; 3 | import 'package:easy_mygo/plugin/component/api/payload/component_payload.dart'; 4 | import 'package:freezed_annotation/freezed_annotation.dart'; 5 | 6 | part 'detailed_resp.freezed.dart'; 7 | part 'detailed_resp.g.dart'; 8 | 9 | @freezed 10 | class NovelDetailedResp with _$NovelDetailedResp { 11 | factory NovelDetailedResp({ 12 | @Default(null) NovelDetailed? detailed, 13 | @Default(null) List? volumes, 14 | required ComponentPayload payload, 15 | }) = _NovelDetailedResp; 16 | 17 | factory NovelDetailedResp.fromJson(Map json) => 18 | _$NovelDetailedRespFromJson(json); 19 | } 20 | -------------------------------------------------------------------------------- /lib/plugin/component/api/novel/detailed/resp/detailed_resp.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'detailed_resp.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _$NovelDetailedRespImpl _$$NovelDetailedRespImplFromJson( 10 | Map json) => 11 | _$NovelDetailedRespImpl( 12 | detailed: json['detailed'] == null 13 | ? null 14 | : NovelDetailed.fromJson(json['detailed'] as Map), 15 | volumes: (json['volumes'] as List?) 16 | ?.map((e) => NovelVolume.fromJson(e as Map)) 17 | .toList() ?? 18 | null, 19 | payload: 20 | ComponentPayload.fromJson(json['payload'] as Map), 21 | ); 22 | 23 | Map _$$NovelDetailedRespImplToJson( 24 | _$NovelDetailedRespImpl instance) => 25 | { 26 | 'detailed': instance.detailed, 27 | 'volumes': instance.volumes, 28 | 'payload': instance.payload, 29 | }; 30 | -------------------------------------------------------------------------------- /lib/plugin/component/api/novel/home/novel_home_component.dart: -------------------------------------------------------------------------------- 1 | import 'package:easy_mygo/entity/book/home_tab/book_home_tab.dart'; 2 | import 'package:easy_mygo/plugin/component/api/component.dart'; 3 | import 'package:easy_mygo/plugin/component/api/novel/home/page/home_page.dart'; 4 | import 'package:easy_mygo/plugin/component/api/novel/home/resp/home_resp.dart'; 5 | 6 | abstract class NovelHomeComponent extends Component { 7 | NovelHomeComponent(super.sourceInfo); 8 | 9 | /// 获取一级 tab 列表 10 | Future getHomeTab(); 11 | 12 | /// 获取二级 tab 列表 13 | Future getSecondTab(BookHomeTab tab); 14 | 15 | /// 获取一级 tab 对应的页面(如果没有二级 tab 的话) 16 | Future getPageWithHomeTab(BookHomeTab tab); 17 | 18 | /// 获取二级 tab 对应的页面 19 | Future getPageWithSecondTab( 20 | BookHomeTab homeTab, BookSecondTab secondTab); 21 | 22 | /// 获取页面中某一页的漫画数据 23 | Future loadPageData(NovelHomePage page, String key); 24 | } 25 | -------------------------------------------------------------------------------- /lib/plugin/component/api/novel/home/page/home_page.dart: -------------------------------------------------------------------------------- 1 | 2 | 3 | import 'package:freezed_annotation/freezed_annotation.dart'; 4 | 5 | part 'home_page.freezed.dart'; 6 | part 'home_page.g.dart'; 7 | 8 | @freezed 9 | class NovelHomePage with _$NovelHomePage { 10 | factory NovelHomePage({ 11 | required String id, 12 | required String label, 13 | // 是否有 封面 14 | @JsonKey(name: "has_cover") @Default(true) bool hasCover, 15 | 16 | // 初始页面 key 17 | @JsonKey(name: "init_key") @Default('') String initKey, 18 | 19 | // 可以让源透传一些信息 20 | @Default('') String ext, 21 | }) = _NovelHomePage; 22 | 23 | factory NovelHomePage.fromJson(Map json) => 24 | _$NovelHomePageFromJson(json); 25 | 26 | } -------------------------------------------------------------------------------- /lib/plugin/component/api/novel/home/page/home_page.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'home_page.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _$NovelHomePageImpl _$$NovelHomePageImplFromJson(Map json) => 10 | _$NovelHomePageImpl( 11 | id: json['id'] as String, 12 | label: json['label'] as String, 13 | hasCover: json['has_cover'] as bool? ?? true, 14 | initKey: json['init_key'] as String? ?? '', 15 | ext: json['ext'] as String? ?? '', 16 | ); 17 | 18 | Map _$$NovelHomePageImplToJson(_$NovelHomePageImpl instance) => 19 | { 20 | 'id': instance.id, 21 | 'label': instance.label, 22 | 'has_cover': instance.hasCover, 23 | 'init_key': instance.initKey, 24 | 'ext': instance.ext, 25 | }; 26 | -------------------------------------------------------------------------------- /lib/plugin/component/api/novel/home/resp/home_resp.dart: -------------------------------------------------------------------------------- 1 | import 'package:easy_mygo/entity/book/home_tab/book_home_tab.dart'; 2 | import 'package:easy_mygo/entity/novel/novel_cover/novel_cover.dart'; 3 | import 'package:easy_mygo/plugin/component/api/novel/home/page/home_page.dart'; 4 | import 'package:easy_mygo/plugin/component/api/payload/component_payload.dart'; 5 | import 'package:freezed_annotation/freezed_annotation.dart'; 6 | 7 | part 'home_resp.g.dart'; 8 | part 'home_resp.freezed.dart'; 9 | 10 | /// 获取一级 tab 11 | @freezed 12 | class NovelGetHomeTabResp with _$NovelGetHomeTabResp { 13 | factory NovelGetHomeTabResp({ 14 | @JsonKey(name: "tab_list") @Default(null) List? tabList, 15 | required ComponentPayload payload, 16 | }) = _NovelGetHomeTabResp; 17 | 18 | factory NovelGetHomeTabResp.fromJson(Map json) => 19 | _$NovelGetHomeTabRespFromJson(json); 20 | } 21 | 22 | /// 获取二级 tab 23 | @freezed 24 | class NovelGetSecondTabResp with _$NovelGetSecondTabResp { 25 | factory NovelGetSecondTabResp({ 26 | @JsonKey(name: "tab_list") @Default(null) List? tabList, 27 | required ComponentPayload payload, 28 | }) = _NovelGetSecondTabResp; 29 | 30 | factory NovelGetSecondTabResp.fromJson(Map json) => 31 | _$NovelGetSecondTabRespFromJson(json); 32 | } 33 | 34 | /// 获取 tab 对应的 page 35 | @freezed 36 | class NovelGetHomePageResp with _$NovelGetHomePageResp { 37 | factory NovelGetHomePageResp({ 38 | @JsonKey(name: 'page') @Default(null) NovelHomePage? page, 39 | required ComponentPayload payload, 40 | }) = _NovelGetHomePageResp; 41 | 42 | factory NovelGetHomePageResp.fromJson(Map json) => 43 | _$NovelGetHomePageRespFromJson(json); 44 | } 45 | 46 | /// 加载 page 里某一页数据 47 | @freezed 48 | class NovelGetHomeCoverResp with _$NovelGetHomeCoverResp { 49 | factory NovelGetHomeCoverResp({ 50 | @Default(null) List? data, 51 | 52 | // 下一页的 key,如果为空则代表最后一页 53 | @JsonKey(name: "next_key") @Default(null) String? nextKey, 54 | required ComponentPayload payload, 55 | }) = _NovelGetHomeCoverResp; 56 | 57 | factory NovelGetHomeCoverResp.fromJson(Map json) => 58 | _$NovelGetHomeCoverRespFromJson(json); 59 | } 60 | -------------------------------------------------------------------------------- /lib/plugin/component/api/novel/home/tab/home_tab.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | part 'home_tab.freezed.dart'; 4 | part 'home_tab.g.dart'; 5 | 6 | 7 | /// 一级 tab 8 | @freezed 9 | class NovelHomeTab with _$NovelHomeTab { 10 | factory NovelHomeTab({ 11 | required String id, 12 | required String label, 13 | // 是否有 二级 tab 14 | @JsonKey(name: "has_second_tab") required bool hasSecondTab, 15 | 16 | // 可以让源透传一些信息 17 | @Default('') String ext, 18 | }) = _NovelHomeTab; 19 | 20 | factory NovelHomeTab.fromJson(Map json) => 21 | _$NovelHomeTabFromJson(json); 22 | } 23 | 24 | /// 二级 tab 25 | @freezed 26 | class NovelHomeSecondTab with _$NovelHomeSecondTab { 27 | factory NovelHomeSecondTab({ 28 | required String id, 29 | required String label, 30 | @Default('') String ext, 31 | }) = _NovelHomeSecondTab; 32 | 33 | factory NovelHomeSecondTab.fromJson(Map json) => 34 | _$NovelHomeSecondTabFromJson(json); 35 | } 36 | -------------------------------------------------------------------------------- /lib/plugin/component/api/novel/home/tab/home_tab.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'home_tab.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _$NovelHomeTabImpl _$$NovelHomeTabImplFromJson(Map json) => 10 | _$NovelHomeTabImpl( 11 | id: json['id'] as String, 12 | label: json['label'] as String, 13 | hasSecondTab: json['has_second_tab'] as bool, 14 | ext: json['ext'] as String? ?? '', 15 | ); 16 | 17 | Map _$$NovelHomeTabImplToJson(_$NovelHomeTabImpl instance) => 18 | { 19 | 'id': instance.id, 20 | 'label': instance.label, 21 | 'has_second_tab': instance.hasSecondTab, 22 | 'ext': instance.ext, 23 | }; 24 | 25 | _$NovelHomeSecondTabImpl _$$NovelHomeSecondTabImplFromJson( 26 | Map json) => 27 | _$NovelHomeSecondTabImpl( 28 | id: json['id'] as String, 29 | label: json['label'] as String, 30 | ext: json['ext'] as String? ?? '', 31 | ); 32 | 33 | Map _$$NovelHomeSecondTabImplToJson( 34 | _$NovelHomeSecondTabImpl instance) => 35 | { 36 | 'id': instance.id, 37 | 'label': instance.label, 38 | 'ext': instance.ext, 39 | }; 40 | -------------------------------------------------------------------------------- /lib/plugin/component/api/novel/read/novel_read_component.dart: -------------------------------------------------------------------------------- 1 | import 'package:easy_mygo/entity/novel/novel_detailed/novel_detailed.dart'; 2 | import 'package:easy_mygo/entity/novel/novel_volume/novel_volume.dart'; 3 | import 'package:easy_mygo/plugin/component/api/component.dart'; 4 | import 'package:easy_mygo/plugin/component/api/novel/read/resp/read_resp.dart'; 5 | import 'package:easy_mygo/plugin/component/api/payload/component_payload.dart'; 6 | 7 | abstract class NovelReadComponent extends Component { 8 | NovelReadComponent(super.sourceInfo); 9 | 10 | /// throws: ComponentPayload 11 | Future getNovelChapter( 12 | NovelDetailed detailed, NovelVolume volume); 13 | 14 | Future performGetNovelChapter( 15 | NovelDetailed detailed, NovelVolume volume) async { 16 | try { 17 | return await getNovelChapter(detailed, volume); 18 | } catch (e) { 19 | if (e is ComponentPayload) { 20 | return NovelReadResp(payload: e); 21 | } else { 22 | return NovelReadResp( 23 | payload: ComponentPayload( 24 | code: ComponentPayload.codeCallError, 25 | msg: e.toString(), 26 | raw: e)); 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lib/plugin/component/api/novel/read/resp/read_resp.dart: -------------------------------------------------------------------------------- 1 | import 'package:easy_mygo/entity/novel/novel_chapter/novel_chapter.dart'; 2 | import 'package:easy_mygo/plugin/component/api/payload/component_payload.dart'; 3 | import 'package:freezed_annotation/freezed_annotation.dart'; 4 | 5 | part 'read_resp.freezed.dart'; 6 | part 'read_resp.g.dart'; 7 | 8 | @freezed 9 | class NovelReadResp with _$NovelReadResp { 10 | factory NovelReadResp({ 11 | @Default(null) List? chapters, 12 | required ComponentPayload payload, 13 | }) = _NovelReadResp; 14 | 15 | factory NovelReadResp.fromJson(Map json) => 16 | _$NovelReadRespFromJson(json); 17 | } 18 | -------------------------------------------------------------------------------- /lib/plugin/component/api/novel/read/resp/read_resp.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'read_resp.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _$NovelReadRespImpl _$$NovelReadRespImplFromJson(Map json) => 10 | _$NovelReadRespImpl( 11 | chapters: (json['chapters'] as List?) 12 | ?.map((e) => NovelChapter.fromJson(e as Map)) 13 | .toList() ?? 14 | null, 15 | payload: 16 | ComponentPayload.fromJson(json['payload'] as Map), 17 | ); 18 | 19 | Map _$$NovelReadRespImplToJson(_$NovelReadRespImpl instance) => 20 | { 21 | 'chapters': instance.chapters, 22 | 'payload': instance.payload, 23 | }; 24 | -------------------------------------------------------------------------------- /lib/plugin/component/api/novel/search/novel_search_component.dart: -------------------------------------------------------------------------------- 1 | import 'package:easy_mygo/plugin/component/api/component.dart'; 2 | import 'package:easy_mygo/plugin/component/api/novel/search/resp/search_resp.dart'; 3 | import 'package:easy_mygo/plugin/component/api/payload/component_payload.dart'; 4 | 5 | abstract class NovelSearchComponent extends Component { 6 | NovelSearchComponent(super.sourceInfo); 7 | 8 | Future getInitKey(String key); 9 | 10 | Future search(String key, String keyword); 11 | 12 | Future performSearch(String key, String keyword) async { 13 | try { 14 | return await search(key, keyword); 15 | } catch (e) { 16 | if (e is ComponentPayload) { 17 | return NovelSearchResp(payload: e); 18 | } else { 19 | return NovelSearchResp( 20 | payload: ComponentPayload( 21 | code: ComponentPayload.codeCallError, 22 | msg: e.toString(), 23 | raw: e)); 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lib/plugin/component/api/novel/search/resp/search_resp.dart: -------------------------------------------------------------------------------- 1 | import 'package:easy_mygo/entity/novel/novel_cover/novel_cover.dart'; 2 | import 'package:easy_mygo/plugin/component/api/payload/component_payload.dart'; 3 | import 'package:freezed_annotation/freezed_annotation.dart'; 4 | 5 | part 'search_resp.g.dart'; 6 | part 'search_resp.freezed.dart'; 7 | 8 | @freezed 9 | class NovelSearchResp with _$NovelSearchResp { 10 | factory NovelSearchResp({ 11 | // 当页结果 12 | @Default(null) List? data, 13 | 14 | // 下一页的 key,为 null 则代表没有下一页 15 | @Default(null) @JsonKey(name: "next_key") String? nextKey, 16 | required ComponentPayload payload, 17 | }) = _NovelSearchResp; 18 | 19 | factory NovelSearchResp.fromJson(Map json) => 20 | _$NovelSearchRespFromJson(json); 21 | } 22 | -------------------------------------------------------------------------------- /lib/plugin/component/api/novel/search/resp/search_resp.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'search_resp.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _$NovelSearchRespImpl _$$NovelSearchRespImplFromJson( 10 | Map json) => 11 | _$NovelSearchRespImpl( 12 | data: (json['data'] as List?) 13 | ?.map((e) => NovelCover.fromJson(e as Map)) 14 | .toList() ?? 15 | null, 16 | nextKey: json['next_key'] as String? ?? null, 17 | payload: 18 | ComponentPayload.fromJson(json['payload'] as Map), 19 | ); 20 | 21 | Map _$$NovelSearchRespImplToJson( 22 | _$NovelSearchRespImpl instance) => 23 | { 24 | 'data': instance.data, 25 | 'next_key': instance.nextKey, 26 | 'payload': instance.payload, 27 | }; 28 | -------------------------------------------------------------------------------- /lib/plugin/component/api/payload/component_payload.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | part 'component_payload.g.dart'; 4 | part 'component_payload.freezed.dart'; 5 | 6 | /// 所有 Component 的 所有方法都附带一个该负载,用于返回错误信息等 7 | @freezed 8 | class ComponentPayload with _$ComponentPayload { 9 | 10 | static ComponentPayload ok() => ComponentPayload(); 11 | 12 | // 调用错误 - 插件崩溃,运行时调用错误等 13 | static const codeCallError = -10086; 14 | 15 | // 解析错误 - 插件返回的数据无法解析成对应实体 16 | static const codeParseResultError = -10085; 17 | 18 | // 业务错误 - 一般是插件手动抛出的异常 19 | static const codeBusinessError = -10084; 20 | 21 | factory ComponentPayload({ 22 | @JsonKey(name: "code") @Default(0) int code, 23 | @JsonKey(name: "msg") @Default("") String msg, 24 | 25 | // 业务端返回的解析前的原始数据 26 | @JsonKey(name: "raw") @Default("") dynamic raw, 27 | }) = _ComponentPayload; 28 | 29 | factory ComponentPayload.fromJson(Map json) => 30 | _$ComponentPayloadFromJson(json); 31 | } 32 | 33 | extension ComponentPayloadExt on ComponentPayload{ 34 | static final _errorMsgValues = Expando(); 35 | String get errorMsg { 36 | return _errorMsgValues[this] ??= "${ 37 | code == ComponentPayload.codeCallError ? "调用错误 $msg" : 38 | code == ComponentPayload.codeParseResultError ? "解析错误 $msg" : 39 | code == ComponentPayload.codeBusinessError ? msg : 40 | "未知错误 $code $msg" 41 | } "; 42 | } 43 | 44 | bool get isError => code != 0; 45 | } 46 | -------------------------------------------------------------------------------- /lib/plugin/component/api/payload/component_payload.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'component_payload.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _$ComponentPayloadImpl _$$ComponentPayloadImplFromJson( 10 | Map json) => 11 | _$ComponentPayloadImpl( 12 | code: (json['code'] as num?)?.toInt() ?? 0, 13 | msg: json['msg'] as String? ?? "", 14 | raw: json['raw'] ?? "", 15 | ); 16 | 17 | Map _$$ComponentPayloadImplToJson( 18 | _$ComponentPayloadImpl instance) => 19 | { 20 | 'code': instance.code, 21 | 'msg': instance.msg, 22 | 'raw': instance.raw, 23 | }; 24 | -------------------------------------------------------------------------------- /lib/plugin/component/core/js/js_component.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:easy_mygo/entity/source/source_info/source_info.dart'; 3 | import 'package:easy_mygo/plugin/component/api/component.dart'; 4 | import 'package:easy_mygo/plugin/component/core/js/manga/detailed/js_manga_detailed_component.dart'; 5 | import 'package:easy_mygo/plugin/component/core/js/manga/home/js_manga_home_component.dart'; 6 | import 'package:easy_mygo/plugin/component/core/js/manga/read/js_manga_read_component.dart'; 7 | import 'package:easy_mygo/plugin/component/core/js/manga/search/js_manga_search_component.dart'; 8 | import 'package:easy_mygo/plugin/component/core/js/novel/detailed/js_novel_detailed_component.dart'; 9 | import 'package:easy_mygo/plugin/component/core/js/novel/home/js_novel_home_component.dart'; 10 | import 'package:easy_mygo/plugin/component/core/js/novel/read/js_novel_read_component.dart'; 11 | import 'package:easy_mygo/plugin/component/core/js/novel/search/js_novel_search_component.dart'; 12 | import 'package:flutter_js/flutter_js.dart'; 13 | 14 | 15 | abstract class JsComponent extends Component { 16 | 17 | static List create( SourceInfo sourceInfo,JavascriptRuntime runtime) => [ 18 | JsMangaHomeComponent(sourceInfo: sourceInfo, jsRuntime: runtime), 19 | JsMangaDetailedComponent(sourceInfo: sourceInfo, jsRuntime: runtime), 20 | JsMangaSearchComponent(sourceInfo: sourceInfo, jsRuntime: runtime), 21 | JsMangaReadComponent(sourceInfo: sourceInfo, jsRuntime: runtime), 22 | 23 | JsNovelHomeComponent(sourceInfo: sourceInfo, jsRuntime: runtime), 24 | JsNovelDetailedComponent(sourceInfo: sourceInfo, jsRuntime: runtime), 25 | JsNovelSearchComponent(sourceInfo: sourceInfo, jsRuntime: runtime), 26 | JsNovelReadComponent(sourceInfo: sourceInfo, jsRuntime: runtime), 27 | ]; 28 | 29 | JsComponent(super.sourceInfo); 30 | 31 | 32 | Future isAvailable(); 33 | 34 | Future onLoad(); 35 | 36 | } -------------------------------------------------------------------------------- /lib/plugin/extension/controller/extension_controller.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'extension_controller.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _$ExtensionStateImpl _$$ExtensionStateImplFromJson(Map json) => 10 | _$ExtensionStateImpl( 11 | loading: json['loading'] as bool? ?? true, 12 | extensions: (json['extensions'] as Map?)?.map( 13 | (k, e) => 14 | MapEntry(k, ExtensionData.fromJson(e as Map)), 15 | ) ?? 16 | const {}, 17 | ); 18 | 19 | Map _$$ExtensionStateImplToJson( 20 | _$ExtensionStateImpl instance) => 21 | { 22 | 'loading': instance.loading, 23 | 'extensions': instance.extensions, 24 | }; 25 | 26 | // ************************************************************************** 27 | // RiverpodGenerator 28 | // ************************************************************************** 29 | 30 | String _$extensionControllerHash() => 31 | r'e11e3407b5ccba7171054da9106fa6ceee9d9b04'; 32 | 33 | /// 负责拓展加载,只加载拓展,不加载源 34 | /// 最终源的加载在 SourceController 35 | /// 36 | /// Copied from [ExtensionController]. 37 | @ProviderFor(ExtensionController) 38 | final extensionControllerPod = 39 | AutoDisposeNotifierProvider.internal( 40 | ExtensionController.new, 41 | name: r'extensionControllerPod', 42 | debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') 43 | ? null 44 | : _$extensionControllerHash, 45 | dependencies: null, 46 | allTransitiveDependencies: null, 47 | ); 48 | 49 | typedef _$ExtensionController = AutoDisposeNotifier; 50 | // ignore_for_file: type=lint 51 | // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member 52 | -------------------------------------------------------------------------------- /lib/plugin/extension/loader/extension_loader.dart: -------------------------------------------------------------------------------- 1 | 2 | 3 | import 'package:easy_mygo/entity/extension/extension_data/extension_data.dart'; 4 | import 'package:easy_mygo/entity/extension/extension_info/extension_info.dart'; 5 | import 'package:easy_mygo/plugin/extension/loader/inner/inner_extension_loader.dart'; 6 | import 'package:easy_mygo/plugin/extension/loader/js/js_extension_loader.dart'; 7 | 8 | import 'mygopack/mygopack_extension_loader.dart'; 9 | 10 | abstract class ExtensionLoader { 11 | 12 | static const libVersionMin = 1; 13 | static const libVersionMax = 1; 14 | 15 | static final _loaders = { 16 | ExtensionLoaderType.js: JsExtensionLoader(), 17 | ExtensionLoaderType.mygopack: MygopackExtensionLoader(), 18 | ExtensionLoaderType.inner: InnerExtensionLoader(), 19 | }; 20 | 21 | static ExtensionLoader of(ExtensionLoaderType type) { 22 | return _loaders[type]!; 23 | } 24 | 25 | static ExtensionLoader? ofName(String name) { 26 | for (var key in _loaders.keys) { 27 | final loader = _loaders[key]; 28 | if(loader?.type.name == name){ 29 | return loader; 30 | } 31 | } 32 | return null; 33 | } 34 | 35 | 36 | ExtensionLoaderType get type; 37 | 38 | /// 解析拓展元数据 39 | Future parse(String file); 40 | 41 | /// 加载拓展 42 | Future load(ExtensionInfo extensionInfo); 43 | 44 | } -------------------------------------------------------------------------------- /lib/plugin/extension/loader/inner/inner_extension_loader.dart: -------------------------------------------------------------------------------- 1 | import 'package:easy_mygo/entity/extension/extension_data/extension_data.dart'; 2 | import 'package:easy_mygo/entity/extension/extension_info/extension_info.dart'; 3 | import 'package:easy_mygo/entity/source/source_info/source_info.dart'; 4 | import 'package:easy_mygo/plugin/extension/loader/extension_loader.dart'; 5 | import 'package:easy_mygo/plugin/extension/utils/extension_utils.dart'; 6 | import 'package:easy_mygo/plugin/inner/inner_source.dart'; 7 | import 'package:easy_mygo/plugin/source/loader/source_loader.dart'; 8 | 9 | class InnerExtensionLoader extends ExtensionLoader { 10 | 11 | @override 12 | Future load(ExtensionInfo extensionInfo) async { 13 | final folder = await ExtensionUtils.getInnerFolder(); 14 | if (extensionInfo == InnerSourceFactory.innerExtensionInfo) { 15 | 16 | final keys = InnerSourceFactory.getKeys(); 17 | final List sources = []; 18 | final loader = SourceLoader.of(SourceLoaderType.inner); 19 | for (var key in keys) { 20 | final sourceInfo = await loader.parse(InnerSourceFactory.innerExtensionPackage, key); 21 | if(sourceInfo == null){ 22 | continue; 23 | } 24 | sources.add(sourceInfo); 25 | } 26 | 27 | return ExtensionData( 28 | info: extensionInfo, 29 | folderPath: folder, 30 | sources: sources, 31 | ); 32 | } 33 | return ExtensionData(info: extensionInfo, 34 | folderPath: folder, 35 | state: ExtensionLoadState.error, 36 | errorMsg: "内置插件非法加载"); 37 | } 38 | 39 | @override 40 | Future parse(String file) async { 41 | if (file == InnerSourceFactory.innerExtensionPackage) { 42 | return InnerSourceFactory.innerExtensionInfo; 43 | } 44 | return null; 45 | } 46 | 47 | @override 48 | ExtensionLoaderType get type => ExtensionLoaderType.inner; 49 | 50 | } -------------------------------------------------------------------------------- /lib/plugin/extension/loader/js/js_extension_loader.dart: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | import 'package:easy_mygo/entity/extension/extension_data/extension_data.dart'; 5 | import 'package:easy_mygo/entity/extension/extension_info/extension_info.dart'; 6 | import 'package:easy_mygo/plugin/extension/loader/extension_loader.dart'; 7 | 8 | class JsExtensionLoader extends ExtensionLoader { 9 | 10 | 11 | 12 | @override 13 | Future load(ExtensionInfo extensionInfo) { 14 | throw UnimplementedError(); 15 | } 16 | 17 | @override 18 | ExtensionLoaderType get type => ExtensionLoaderType.js; 19 | 20 | @override 21 | Future parse(String file) { 22 | // TODO: implement parse 23 | throw UnimplementedError(); 24 | } 25 | 26 | 27 | } -------------------------------------------------------------------------------- /lib/plugin/extension/loader/mygopack/manifest/mygopack_manifest_info.dart: -------------------------------------------------------------------------------- 1 | 2 | 3 | import 'package:freezed_annotation/freezed_annotation.dart'; 4 | 5 | part 'mygopack_manifest_info.g.dart'; 6 | part 'mygopack_manifest_info.freezed.dart'; 7 | 8 | /// Mygo 插件包清单文件解析实体 9 | @freezed 10 | class MygoPackManifest with _$MygoPackManifest { 11 | factory MygoPackManifest({ 12 | // 包名唯一 13 | required String package, 14 | required String label, 15 | @JsonKey(name: 'version_name') required String versionName, 16 | @JsonKey(name: 'version_code') required int versionCode, 17 | @JsonKey(name: 'lib_version') required int libVersion, 18 | @Default("") String readme, 19 | 20 | }) = _MygoPackManifest; 21 | 22 | factory MygoPackManifest.fromJson(Map json) => 23 | _$MygoPackManifestFromJson(json); 24 | } -------------------------------------------------------------------------------- /lib/plugin/extension/loader/mygopack/manifest/mygopack_manifest_info.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'mygopack_manifest_info.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _$MygoPackManifestImpl _$$MygoPackManifestImplFromJson( 10 | Map json) => 11 | _$MygoPackManifestImpl( 12 | package: json['package'] as String, 13 | label: json['label'] as String, 14 | versionName: json['version_name'] as String, 15 | versionCode: (json['version_code'] as num).toInt(), 16 | libVersion: (json['lib_version'] as num).toInt(), 17 | readme: json['readme'] as String? ?? "", 18 | ); 19 | 20 | Map _$$MygoPackManifestImplToJson( 21 | _$MygoPackManifestImpl instance) => 22 | { 23 | 'package': instance.package, 24 | 'label': instance.label, 25 | 'version_name': instance.versionName, 26 | 'version_code': instance.versionCode, 27 | 'lib_version': instance.libVersion, 28 | 'readme': instance.readme, 29 | }; 30 | -------------------------------------------------------------------------------- /lib/plugin/extension/utils/extension_utils.dart: -------------------------------------------------------------------------------- 1 | import 'package:easy_mygo/c.dart'; 2 | import 'package:easy_mygo/entity/extension/extension_info/extension_info.dart'; 3 | import 'package:easy_mygo/plugin/inner/inner_source.dart'; 4 | import 'package:easy_mygo/utils/file_index/file_index_utils.dart'; 5 | import 'package:path/path.dart'; 6 | 7 | class ExtensionUtils { 8 | static const extensionRootPath = "extension"; 9 | 10 | /// /extension// 11 | static Future getFolder( 12 | ExtensionLoaderType loaderType, String package) async { 13 | final applicationDir = await EasyConstant.applicationPath; 14 | return join( 15 | applicationDir.path, extensionRootPath, loaderType.name, package); 16 | } 17 | 18 | static Future getInnerFolder() async { 19 | return await getFolder( 20 | ExtensionLoaderType.inner, InnerSourceFactory.innerExtensionPackage); 21 | } 22 | 23 | static Future checkExtensionFolder(String path) async { 24 | return await FileIndexUtils.checkPath(path); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/plugin/inner/inner_source.dart: -------------------------------------------------------------------------------- 1 | import 'package:easy_mygo/c.dart'; 2 | import 'package:easy_mygo/entity/extension/extension_info/extension_info.dart'; 3 | import 'package:easy_mygo/entity/source/source_info/source_info.dart'; 4 | import 'package:easy_mygo/plugin/component/api/component.dart'; 5 | import 'package:easy_mygo/plugin/extension/loader/extension_loader.dart'; 6 | import 'package:easy_mygo/plugin/inner/test/test_inner_source.dart'; 7 | 8 | class InnerSourceFactory { 9 | static const innerExtensionPackage = "com.heyanle.easymygo.extension.inner"; 10 | static final innerExtensionInfo = ExtensionInfo( 11 | package: innerExtensionPackage, 12 | label: "内置源", 13 | versionName: EasyConstant.versionName, 14 | versionCode: EasyConstant.versionCode, 15 | 16 | /// 内置源不用检查库版本 17 | libVersion: ExtensionLoader.libVersionMax, 18 | loadType: ExtensionLoaderType.inner, 19 | path: "", 20 | readme: "纯纯 Mygo 内置源"); 21 | 22 | static final testManga = TestMangaSource(); 23 | static final testNovel = TestNovelSource(); 24 | 25 | // 在这里添加 26 | static final _innerSources = { 27 | testManga.key: testManga, 28 | testNovel.key: testNovel, 29 | }; 30 | 31 | static InnerSource? of(String key) { 32 | return _innerSources[key]; 33 | } 34 | 35 | static List getKeys() { 36 | return _innerSources.keys.toList(); 37 | } 38 | } 39 | 40 | abstract class InnerSource { 41 | String key; 42 | String label; 43 | String versionName; 44 | int versionCode; 45 | String description; 46 | SourceType type; 47 | 48 | // 一般为 assets 里的文件 49 | String header; 50 | 51 | InnerSource({ 52 | required this.key, 53 | required this.label, 54 | required this.versionName, 55 | required this.versionCode, 56 | required this.type, 57 | this.description = "", 58 | this.header = "", 59 | }); 60 | 61 | List getComponentList(SourceInfo sourceInfo); 62 | } 63 | -------------------------------------------------------------------------------- /lib/plugin/inner/test/test_inner_source.dart: -------------------------------------------------------------------------------- 1 | import 'package:easy_mygo/entity/source/source_info/source_info.dart'; 2 | import 'package:easy_mygo/plugin/component/api/component.dart'; 3 | import 'package:easy_mygo/plugin/inner/inner_source.dart'; 4 | import 'package:easy_mygo/plugin/inner/test/component/manga/manga_test_component.dart'; 5 | import 'package:easy_mygo/plugin/inner/test/component/novel/novel_test_component.dart'; 6 | 7 | class TestMangaSource extends InnerSource { 8 | TestMangaSource(): super( 9 | key: "test_manga", 10 | label: "测试漫画源", 11 | versionName: "1.0", 12 | versionCode: 1, 13 | type: SourceType.manga, 14 | ); 15 | 16 | @override 17 | List getComponentList(SourceInfo sourceInfo) { 18 | return [ 19 | MangaTestComponent(sourceInfo) 20 | ]; 21 | } 22 | } 23 | 24 | class TestNovelSource extends InnerSource { 25 | TestNovelSource(): super( 26 | key: "test_novel", 27 | label: "测试小说源", 28 | versionName: "1.0", 29 | versionCode: 1, 30 | type: SourceType.manga, 31 | ); 32 | 33 | @override 34 | List getComponentList(SourceInfo sourceInfo) { 35 | return [ 36 | NovelTestComponent(sourceInfo) 37 | ]; 38 | } 39 | } 40 | 41 | -------------------------------------------------------------------------------- /lib/plugin/source/config_controller/source_config_controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:easy_mygo/utils/hive/hive.dart'; 2 | import 'package:freezed_annotation/freezed_annotation.dart'; 3 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 4 | 5 | part 'source_config_controller.freezed.dart'; 6 | 7 | part 'source_config_controller.g.dart'; 8 | 9 | /// 本地的源配置 10 | 11 | @freezed 12 | class SourceConfigItem with _$SourceConfigItem { 13 | static final List none = []; 14 | 15 | factory SourceConfigItem({ 16 | required String key, 17 | required String package, 18 | @Default(true) bool enabled, 19 | @Default(999) int order, 20 | }) = _SourceConfigItem; 21 | 22 | factory SourceConfigItem.fromJson(Map json) => 23 | _$SourceConfigItemFromJson(json); 24 | } 25 | 26 | extension SourceConfigItemExt on SourceConfigItem { 27 | static final _identifyValues = Expando(); 28 | 29 | String get identify { 30 | return _identifyValues[this] ??= "$package-|-$key"; 31 | } 32 | } 33 | 34 | @Riverpod() 35 | class SourceConfigController extends _$SourceConfigController { 36 | static const sourceConfigList = "source_config_list"; 37 | 38 | static SourceConfigController of(dynamic ref) => 39 | ref.watch(sourceConfigControllerPod.notifier); 40 | 41 | @override 42 | List build() { 43 | return SourceConfigItem.none; 44 | } 45 | 46 | late Future _init; 47 | 48 | SourceConfigController() { 49 | _init = Future.microtask(() { 50 | _innerInit(); 51 | }); 52 | } 53 | 54 | Future _innerInit() async { 55 | final box = await HiveBox.sourceConfig(); 56 | final configListMap = await box.getSingle() ?? {}; 57 | final list = configListMap[sourceConfigList] ?? []; 58 | await box.close(); 59 | if (list is Iterable) { 60 | final li = list.map((e) => SourceConfigItem.fromJson(e)).toList(); 61 | state = li; 62 | } else { 63 | state = []; 64 | } 65 | } 66 | 67 | Future setSourceConfig(List list) async { 68 | await _init; 69 | final box = await HiveBox.sourceConfig(); 70 | state = list; 71 | await box 72 | .putSingle({sourceConfigList: list.map((e) => e.toJson()).toList()}); 73 | await box.close(); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /lib/plugin/source/config_controller/source_config_controller.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'source_config_controller.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _$SourceConfigItemImpl _$$SourceConfigItemImplFromJson( 10 | Map json) => 11 | _$SourceConfigItemImpl( 12 | key: json['key'] as String, 13 | package: json['package'] as String, 14 | enabled: json['enabled'] as bool? ?? true, 15 | order: (json['order'] as num?)?.toInt() ?? 999, 16 | ); 17 | 18 | Map _$$SourceConfigItemImplToJson( 19 | _$SourceConfigItemImpl instance) => 20 | { 21 | 'key': instance.key, 22 | 'package': instance.package, 23 | 'enabled': instance.enabled, 24 | 'order': instance.order, 25 | }; 26 | 27 | // ************************************************************************** 28 | // RiverpodGenerator 29 | // ************************************************************************** 30 | 31 | String _$sourceConfigControllerHash() => 32 | r'b59f7f384d6c9535b8c14f9980f243143edaccdb'; 33 | 34 | /// See also [SourceConfigController]. 35 | @ProviderFor(SourceConfigController) 36 | final sourceConfigControllerPod = AutoDisposeNotifierProvider< 37 | SourceConfigController, List>.internal( 38 | SourceConfigController.new, 39 | name: r'sourceConfigControllerPod', 40 | debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') 41 | ? null 42 | : _$sourceConfigControllerHash, 43 | dependencies: null, 44 | allTransitiveDependencies: null, 45 | ); 46 | 47 | typedef _$SourceConfigController = AutoDisposeNotifier>; 48 | // ignore_for_file: type=lint 49 | // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member 50 | -------------------------------------------------------------------------------- /lib/plugin/source/controller/source_controller.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'source_controller.dart'; 4 | 5 | // ************************************************************************** 6 | // RiverpodGenerator 7 | // ************************************************************************** 8 | 9 | String _$sourceConfigWrapperHash() => 10 | r'bcea7fa6c19d89a55763678f4e7ae1f034afeb2d'; 11 | 12 | /// See also [sourceConfigWrapper]. 13 | @ProviderFor(sourceConfigWrapper) 14 | final sourceConfigWrapperPod = 15 | AutoDisposeProvider>.internal( 16 | sourceConfigWrapper, 17 | name: r'sourceConfigWrapperPod', 18 | debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') 19 | ? null 20 | : _$sourceConfigWrapperHash, 21 | dependencies: null, 22 | allTransitiveDependencies: null, 23 | ); 24 | 25 | typedef SourceConfigWrapperRef 26 | = AutoDisposeProviderRef>; 27 | String _$sourceBundleHash() => r'2633a77df5f75fd7a026481af10d3e46873c0d0a'; 28 | 29 | /// See also [sourceBundle]. 30 | @ProviderFor(sourceBundle) 31 | final sourceBundlePod = AutoDisposeProvider.internal( 32 | sourceBundle, 33 | name: r'sourceBundlePod', 34 | debugGetCreateSourceHash: 35 | const bool.fromEnvironment('dart.vm.product') ? null : _$sourceBundleHash, 36 | dependencies: null, 37 | allTransitiveDependencies: null, 38 | ); 39 | 40 | typedef SourceBundleRef = AutoDisposeProviderRef; 41 | // ignore_for_file: type=lint 42 | // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member 43 | -------------------------------------------------------------------------------- /lib/plugin/source/load_controller/source_load_controller.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'source_load_controller.dart'; 4 | 5 | // ************************************************************************** 6 | // RiverpodGenerator 7 | // ************************************************************************** 8 | 9 | String _$sourceLoadControllerHash() => 10 | r'4d4a29f7279d36e2fdbf731e5abd59da9428524c'; 11 | 12 | /// See also [SourceLoadController]. 13 | @ProviderFor(SourceLoadController) 14 | final sourceLoadControllerPod = 15 | AutoDisposeNotifierProvider.internal( 16 | SourceLoadController.new, 17 | name: r'sourceLoadControllerPod', 18 | debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') 19 | ? null 20 | : _$sourceLoadControllerHash, 21 | dependencies: null, 22 | allTransitiveDependencies: null, 23 | ); 24 | 25 | typedef _$SourceLoadController = AutoDisposeNotifier; 26 | // ignore_for_file: type=lint 27 | // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member 28 | -------------------------------------------------------------------------------- /lib/plugin/source/loader/inner/inner_source_loader.dart: -------------------------------------------------------------------------------- 1 | import 'package:easy_mygo/entity/source/source_data/source_data.dart'; 2 | import 'package:easy_mygo/entity/source/source_info/source_info.dart'; 3 | import 'package:easy_mygo/plugin/inner/inner_source.dart'; 4 | import 'package:easy_mygo/plugin/source/loader/source_loader.dart'; 5 | 6 | class InnerSourceLoader extends SourceLoader { 7 | @override 8 | Future load(SourceInfo sourceInfo) async { 9 | final inner = InnerSourceFactory.of(sourceInfo.path); 10 | if (inner == null) { 11 | return SourceData( 12 | info: sourceInfo, 13 | state: SourceState.error, 14 | errorMsg: "内置源不存在"); 15 | } 16 | return SourceData(info: sourceInfo, components: inner.getComponentList(sourceInfo)); 17 | } 18 | 19 | @override 20 | Future parse(String fromExtension, String filePath) async { 21 | final inner = InnerSourceFactory.of(filePath); 22 | if (inner == null) { 23 | return null; 24 | } 25 | return SourceInfo( 26 | key: inner.key, 27 | fromPackage: fromExtension, 28 | label: inner.label, 29 | type: inner.type, 30 | versionName: inner.versionName, 31 | versionCode: inner.versionCode, 32 | path: filePath, 33 | loaderType: SourceLoaderType.inner); 34 | } 35 | 36 | @override 37 | SourceLoaderType get type => SourceLoaderType.inner; 38 | } 39 | -------------------------------------------------------------------------------- /lib/plugin/source/loader/source_loader.dart: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | import 'package:easy_mygo/entity/source/source_data/source_data.dart'; 6 | import 'package:easy_mygo/entity/source/source_info/source_info.dart'; 7 | import 'package:easy_mygo/plugin/source/loader/inner/inner_source_loader.dart'; 8 | import 'package:easy_mygo/plugin/source/loader/js/js_source_loader.dart'; 9 | 10 | abstract class SourceLoader { 11 | 12 | static final _loaders = { 13 | SourceLoaderType.js: JsSourceLoader(), 14 | SourceLoaderType.inner: InnerSourceLoader(), 15 | }; 16 | 17 | static SourceLoader of(SourceLoaderType type) { 18 | return _loaders[type]!; 19 | } 20 | 21 | static SourceLoader? ofName(String name) { 22 | for (var key in _loaders.keys) { 23 | final loader = _loaders[key]; 24 | if(loader?.type.name == name){ 25 | return loader; 26 | } 27 | } 28 | return null; 29 | } 30 | 31 | SourceLoaderType get type; 32 | 33 | Future parse(String fromExtension, String filePath); 34 | 35 | Future load(SourceInfo sourceInfo); 36 | } -------------------------------------------------------------------------------- /lib/plugin/source/utils/action/buffer_action.dart: -------------------------------------------------------------------------------- 1 | import 'package:easy_mygo/entity/source/source_info/source_info.dart'; 2 | import 'package:easy_mygo/plugin/source/utils/action/source_action.dart'; 3 | 4 | 5 | class BufferAction extends SourceAction { 6 | BufferAction() : super("Buffer", 7 | constructorArgsCount: 0, funcNames: ["put", "get"]); 8 | 9 | @override 10 | Future execute(SourceInfo sourceInfo, String funcName, List constructorArgs, List args) async { 11 | switch(funcName){ 12 | case "put": 13 | final value = args.elementAtOrNull(0); 14 | if(value == null){ 15 | return ""; 16 | } 17 | 18 | final buffer = sourceInfo.sourceUtilsScope.bufferUtils; 19 | return buffer.add(value).toString(); 20 | case "get": 21 | final args1 = args.elementAtOrNull(0); 22 | if (args1 == null){ 23 | return ""; 24 | } 25 | 26 | final buffer = sourceInfo.sourceUtilsScope.bufferUtils; 27 | return buffer.get(args1) ?? ""; 28 | } 29 | return ""; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lib/plugin/source/utils/action/entity/web_view_resp/web_view_resp.dart: -------------------------------------------------------------------------------- 1 | import 'package:easy_mygo/plugin/source/utils/action/entity/web_view_strategy/web_view_strategy.dart'; 2 | import 'package:freezed_annotation/freezed_annotation.dart'; 3 | 4 | part 'web_view_resp.freezed.dart'; 5 | part 'web_view_resp.g.dart'; 6 | 7 | /// WebView 爬取结果 8 | @freezed 9 | class WebViewResp with _$WebViewResp { 10 | factory WebViewResp({ 11 | // 当次结果的策略 12 | required WebViewStrategy strategy, 13 | 14 | // url 15 | required String url, 16 | 17 | // 是否是超时 18 | @Default(false) @JsonKey(name: "is_timeout") bool isTimeout, 19 | 20 | // 回调时当前页面的数据 21 | @Default("") String content, 22 | 23 | // 命中回调正则的资源字段 24 | @Default("") @JsonKey(name: "intercept_resource") String interceptResource, 25 | }) = _WebViewResp; 26 | 27 | factory WebViewResp.fromJson(Map json) => 28 | _$WebViewRespFromJson(json); 29 | } 30 | -------------------------------------------------------------------------------- /lib/plugin/source/utils/action/entity/web_view_resp/web_view_resp.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'web_view_resp.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _$WebViewRespImpl _$$WebViewRespImplFromJson(Map json) => 10 | _$WebViewRespImpl( 11 | strategy: 12 | WebViewStrategy.fromJson(json['strategy'] as Map), 13 | url: json['url'] as String, 14 | isTimeout: json['is_timeout'] as bool? ?? false, 15 | content: json['content'] as String? ?? "", 16 | interceptResource: json['intercept_resource'] as String? ?? "", 17 | ); 18 | 19 | Map _$$WebViewRespImplToJson(_$WebViewRespImpl instance) => 20 | { 21 | 'strategy': instance.strategy, 22 | 'url': instance.url, 23 | 'is_timeout': instance.isTimeout, 24 | 'content': instance.content, 25 | 'intercept_resource': instance.interceptResource, 26 | }; 27 | -------------------------------------------------------------------------------- /lib/plugin/source/utils/action/entity/web_view_strategy/web_view_strategy.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | part 'web_view_strategy.freezed.dart'; 4 | part 'web_view_strategy.g.dart'; 5 | 6 | /// WebView 爬取策略 7 | @freezed 8 | class WebViewStrategy with _$WebViewStrategy { 9 | factory WebViewStrategy({ 10 | // 超时 11 | @Default(8000) int timeout, 12 | 13 | // 解码类型 14 | @Default("utf-8") String encoding, 15 | 16 | // 回调正则,回调正则。在检测到特定请求时返回结果。默认为空则在页面加载完成后自动回调(因为ajax等因素可能得到的源码不完整,另外注意超时) 17 | @Default(null) @JsonKey(name: "callback_regex") String? callBackRegex, 18 | 19 | // 是否拦截 blob 数据 20 | @Default(false) @JsonKey(name: "is_intercept_blob") bool isInterceptBlob, 21 | 22 | // UA 23 | @Default(null) String? userAgent, 24 | 25 | // header 26 | @Default(null) Map? header, 27 | 28 | // 在页面加载完成后执行的js代码,可用于主动加载资源 29 | @Default(null) String? actionJS, 30 | }) = _WebViewStrategy; 31 | 32 | factory WebViewStrategy.fromJson(Map json) => 33 | _$WebViewStrategyFromJson(json); 34 | } 35 | -------------------------------------------------------------------------------- /lib/plugin/source/utils/action/entity/web_view_strategy/web_view_strategy.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'web_view_strategy.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _$WebViewStrategyImpl _$$WebViewStrategyImplFromJson( 10 | Map json) => 11 | _$WebViewStrategyImpl( 12 | timeout: (json['timeout'] as num?)?.toInt() ?? 8000, 13 | encoding: json['encoding'] as String? ?? "utf-8", 14 | callBackRegex: json['callback_regex'] as String? ?? null, 15 | isInterceptBlob: json['is_intercept_blob'] as bool? ?? false, 16 | userAgent: json['userAgent'] as String? ?? null, 17 | header: (json['header'] as Map?)?.map( 18 | (k, e) => MapEntry(k, e as String), 19 | ) ?? 20 | null, 21 | actionJS: json['actionJS'] as String? ?? null, 22 | ); 23 | 24 | Map _$$WebViewStrategyImplToJson( 25 | _$WebViewStrategyImpl instance) => 26 | { 27 | 'timeout': instance.timeout, 28 | 'encoding': instance.encoding, 29 | 'callback_regex': instance.callBackRegex, 30 | 'is_intercept_blob': instance.isInterceptBlob, 31 | 'userAgent': instance.userAgent, 32 | 'header': instance.header, 33 | 'actionJS': instance.actionJS, 34 | }; 35 | -------------------------------------------------------------------------------- /lib/plugin/source/utils/action/html_action.dart: -------------------------------------------------------------------------------- 1 | import 'package:easy_mygo/entity/source/source_info/source_info.dart'; 2 | import 'package:easy_mygo/plugin/source/utils/action/source_action.dart'; 3 | import 'package:html/parser.dart'; 4 | 5 | class HtmlQuerySelector extends SourceAction { 6 | HtmlQuerySelector() 7 | : super("HtmlQuerySelector", 8 | constructorArgsCount: 2, 9 | funcNames: ["text", "innerHtml", "outerHtml"]); 10 | 11 | /// constructorArgs[0] content 内容或地址 12 | /// constructorArgs[1] selector 选择器 13 | /// return address 地址 14 | @override 15 | Future execute(SourceInfo sourceInfo, String funcName, 16 | List constructorArgs, List args) async { 17 | final content = constructorArgs.elementAtOrNull(0); 18 | final selector = constructorArgs.elementAtOrNull(1); 19 | final buffer = sourceInfo.sourceUtilsScope.bufferUtils; 20 | 21 | if (content == null || selector == null) { 22 | return ""; 23 | } 24 | 25 | final realContent = buffer.get(content) ?? content; 26 | final doc = parse(realContent).querySelector(selector); 27 | var res = ""; 28 | switch (funcName) { 29 | case 'text': 30 | res = doc?.text ?? ''; 31 | case 'innerHTML': 32 | res = doc?.innerHtml ?? ''; 33 | default: 34 | res = doc?.outerHtml ?? ''; 35 | } 36 | if (res == "") { 37 | return ""; 38 | } 39 | return buffer.add(res).toString(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lib/plugin/source/utils/action/source_action.dart: -------------------------------------------------------------------------------- 1 | import 'package:easy_mygo/entity/source/source_info/source_info.dart'; 2 | import 'package:easy_mygo/plugin/source/utils/action/buffer_action.dart'; 3 | import 'package:easy_mygo/plugin/source/utils/action/html_action.dart'; 4 | import 'package:easy_mygo/plugin/source/utils/action/web_view_action.dart'; 5 | import 'package:easy_mygo/plugin/source/utils/action/xpath_action.dart'; 6 | 7 | 8 | abstract class SourceAction { 9 | static final actions = [ 10 | BufferAction(), 11 | HtmlQuerySelector(), 12 | HtmlXpathSelector(), 13 | WebViewAction(), 14 | ]; 15 | 16 | String clazz; 17 | int constructorArgsCount; 18 | List funcNames; 19 | 20 | SourceAction(this.clazz, 21 | {this.constructorArgsCount = 0, this.funcNames = const []}); 22 | 23 | Future execute(SourceInfo sourceInfo, String funcName, 24 | List constructorArgs, List args); 25 | } 26 | -------------------------------------------------------------------------------- /lib/plugin/source/utils/action/web_view_action.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'dart:convert'; 3 | 4 | import 'package:easy_mygo/entity/source/source_info/source_info.dart'; 5 | import 'package:easy_mygo/plugin/source/utils/action/entity/web_view_strategy/web_view_strategy.dart'; 6 | import 'package:easy_mygo/plugin/source/utils/action/source_action.dart'; 7 | 8 | class WebViewAction extends SourceAction { 9 | WebViewAction() : super("WebViewUtils", 10 | constructorArgsCount: 0, funcNames: ["getRenderedHtmlCode"]); 11 | 12 | @override 13 | Future execute(SourceInfo sourceInfo, String funcName, List constructorArgs, List args) async { 14 | switch(funcName){ 15 | case "getRenderedHtmlCode": 16 | final url = args.elementAtOrNull(0) ?? ""; 17 | final strategyString = args.elementAtOrNull(1) ?? ""; 18 | 19 | try { 20 | final strategy = WebViewStrategy.fromJson(jsonDecode(strategyString)); 21 | // TODO webview 爬取 22 | 23 | }catch(e){ 24 | return ""; 25 | } 26 | } 27 | return ""; 28 | } 29 | } -------------------------------------------------------------------------------- /lib/plugin/source/utils/action/xpath_action.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:easy_mygo/entity/source/source_info/source_info.dart'; 4 | import 'package:easy_mygo/plugin/source/utils/action/source_action.dart'; 5 | import 'package:html/dom.dart'; 6 | import 'package:xpath_selector_html_parser/xpath_selector_html_parser.dart'; 7 | 8 | /// 9 | /// HtmlXpathSelector(, ).[attrs(), attr(), allHtml(), outerHtml(), text()] 10 | class HtmlXpathSelector extends SourceAction { 11 | HtmlXpathSelector() 12 | : super("HtmlXpathSelector", 13 | constructorArgsCount: 2, 14 | funcNames: ["attrs", "attr", "allHtml", "outerHtml", "text"]); 15 | 16 | /// constructorArgs[0] content 内容或地址 17 | /// constructorArgs[1] selector 选择器 18 | /// return address 地址 19 | @override 20 | Future execute(SourceInfo sourceInfo, String funcName, 21 | List constructorArgs, List args) async { 22 | final content = constructorArgs.elementAtOrNull(0); 23 | final selector = constructorArgs.elementAtOrNull(1); 24 | final buffer = sourceInfo.sourceUtilsScope.bufferUtils; 25 | 26 | if (content == null || selector == null) { 27 | return ""; 28 | } 29 | 30 | final realContent = buffer.get(content) ?? content; 31 | 32 | final xpath = HtmlXPath.html(realContent); 33 | final result = xpath.queryXPath(selector); 34 | 35 | var res = ""; 36 | switch (funcName) { 37 | case 'attrs': 38 | try { 39 | res = jsonEncode(result.attrs); 40 | } catch (e) { 41 | res = ""; 42 | } 43 | case 'attr': 44 | res = result.attr ?? ''; 45 | case 'allHTML': 46 | try { 47 | res = jsonEncode( 48 | result.nodes.map((e) => (e.node as Element).outerHtml).toList()); 49 | } catch (e) { 50 | res = ""; 51 | } 52 | case 'outerHTML': 53 | res = (result.node?.node as Element).outerHtml; 54 | default: 55 | res = result.node?.text ?? ''; 56 | } 57 | if (res == "") { 58 | return "-1"; 59 | } 60 | return buffer.add(res).toString(); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /lib/plugin/source/utils/buffer_utils.dart: -------------------------------------------------------------------------------- 1 | 2 | 3 | import 'dart:collection'; 4 | 5 | class BufferUtils { 6 | 7 | var counter = 0; 8 | final HashMap _buffer = HashMap(); 9 | 10 | String add(String value){ 11 | final key = "buffer@${counter ++}"; 12 | _buffer[key] = value; 13 | return key; 14 | } 15 | 16 | String? get(String key){ 17 | return _buffer[key]; 18 | } 19 | 20 | void clean(){ 21 | counter = 0; 22 | _buffer.clear(); 23 | } 24 | 25 | 26 | 27 | } -------------------------------------------------------------------------------- /lib/plugin/source/utils/source_utils_scope.dart: -------------------------------------------------------------------------------- 1 | import 'package:easy_mygo/entity/source/source_info/source_info.dart'; 2 | import 'package:easy_mygo/plugin/source/utils/buffer_utils.dart'; 3 | 4 | class SourceUtilsScope { 5 | final SourceInfo sourceInfo; 6 | final bufferUtils = BufferUtils(); 7 | 8 | SourceUtilsScope(this.sourceInfo); 9 | } 10 | -------------------------------------------------------------------------------- /lib/repository/book_cover/book_cover_repository/book_cover_repository.dart: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /lib/repository/book_cover/page/book_cover_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:easy_mygo/entity/book/cover/book_cover.dart'; 2 | import 'package:easy_mygo/plugin/component/api/payload/component_payload.dart'; 3 | 4 | 5 | abstract class BookCoverPage { 6 | Future initKey(); 7 | 8 | Future<(ComponentPayload payload, List, String? nextKey)> 9 | loadPageData(String key); 10 | } 11 | -------------------------------------------------------------------------------- /lib/repository/book_cover/page/manga_cover_page.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:easy_mygo/entity/book/cover/book_cover.dart'; 3 | import 'package:easy_mygo/plugin/component/api/manga/home/manga_home_component.dart'; 4 | import 'package:easy_mygo/plugin/component/api/manga/home/page/home_page.dart'; 5 | import 'package:easy_mygo/plugin/component/api/manga/search/manga_search_component.dart'; 6 | import 'package:easy_mygo/plugin/component/api/payload/component_payload.dart'; 7 | 8 | import 'book_cover_page.dart'; 9 | 10 | // 首页 11 | class MangaBookHomeCoverPage extends BookCoverPage { 12 | final MangaHomeComponent component; 13 | final MangaHomePage homePage; 14 | 15 | MangaBookHomeCoverPage(this.component, this.homePage); 16 | 17 | @override 18 | Future initKey() async { 19 | return homePage.initKey; 20 | } 21 | 22 | @override 23 | Future<(ComponentPayload, List, String?)> loadPageData( 24 | String key) async { 25 | final resp = await component.loadPageData(homePage, key); 26 | return ( 27 | resp.payload, 28 | [for (final i in resp.data ?? []) BookCover.fromMangaCover(i)], 29 | resp.nextKey 30 | ); 31 | } 32 | } 33 | 34 | // 搜索 35 | class MangaBookSearchCoverPage extends BookCoverPage { 36 | final MangaSearchComponent component; 37 | final String keyword; 38 | 39 | MangaBookSearchCoverPage(this.component, this.keyword); 40 | 41 | @override 42 | Future initKey() async { 43 | return await component.getInitKey(keyword); 44 | } 45 | 46 | @override 47 | Future<(ComponentPayload, List, String?)> loadPageData( 48 | String key) async { 49 | final resp = await component.search(key, keyword); 50 | return ( 51 | resp.payload, 52 | [for (final i in resp.data ?? []) BookCover.fromMangaCover(i)], 53 | resp.nextKey 54 | ); 55 | } 56 | } 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /lib/repository/book_cover/page/novel_cover_page.dart: -------------------------------------------------------------------------------- 1 | 2 | // 小说首页 3 | import 'package:easy_mygo/entity/book/cover/book_cover.dart'; 4 | import 'package:easy_mygo/plugin/component/api/novel/home/novel_home_component.dart'; 5 | import 'package:easy_mygo/plugin/component/api/novel/home/page/home_page.dart'; 6 | import 'package:easy_mygo/plugin/component/api/novel/search/novel_search_component.dart'; 7 | import 'package:easy_mygo/plugin/component/api/payload/component_payload.dart'; 8 | 9 | import 'book_cover_page.dart'; 10 | 11 | // 首页 12 | class NovelBookHomeCoverPage extends BookCoverPage { 13 | final NovelHomeComponent component; 14 | final NovelHomePage homePage; 15 | 16 | NovelBookHomeCoverPage(this.component, this.homePage); 17 | 18 | @override 19 | Future initKey() async { 20 | return homePage.initKey; 21 | } 22 | 23 | @override 24 | Future<(ComponentPayload, List, String?)> loadPageData( 25 | String key) async { 26 | final resp = await component.loadPageData(homePage, key); 27 | return ( 28 | resp.payload, 29 | [for (final i in resp.data ?? []) BookCover.fromNovelCover(i)], 30 | resp.nextKey 31 | ); 32 | } 33 | } 34 | 35 | // 搜索 36 | class NovelBookSearchCoverPage extends BookCoverPage { 37 | final NovelSearchComponent component; 38 | final String keyword; 39 | 40 | NovelBookSearchCoverPage(this.component, this.keyword); 41 | 42 | @override 43 | Future initKey() async { 44 | return await component.getInitKey(keyword); 45 | } 46 | 47 | @override 48 | Future<(ComponentPayload, List, String?)> loadPageData( 49 | String key) async { 50 | final resp = await component.search(key, keyword); 51 | return ( 52 | resp.payload, 53 | [for (final i in resp.data ?? []) BookCover.fromNovelCover(i)], 54 | resp.nextKey 55 | ); 56 | } 57 | } 58 | 59 | 60 | -------------------------------------------------------------------------------- /lib/router.dart: -------------------------------------------------------------------------------- 1 | import 'package:easy_mygo/ui/main/main.dart'; 2 | import 'package:easy_mygo/ui/splash/splash.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:go_router/go_router.dart'; 5 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 6 | 7 | part 'router.g.dart'; 8 | 9 | /// Created by heyanlin on 2023/9/22. 10 | 11 | class Routes { 12 | static const String main = "/main"; 13 | } 14 | 15 | @Riverpod(keepAlive: true) 16 | GoRouter easyRouter(EasyRouterRef ref) => GoRouter( 17 | initialLocation: Routes.main, 18 | redirect: (BuildContext context, GoRouterState state) { 19 | return null; 20 | }, 21 | routes: [ 22 | GoRoute( 23 | path: Routes.main, 24 | builder: (BuildContext context, GoRouterState state) { 25 | return Material( 26 | child: MainScreen(), 27 | ); 28 | }, 29 | ), 30 | ], 31 | ); 32 | -------------------------------------------------------------------------------- /lib/router.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'router.dart'; 4 | 5 | // ************************************************************************** 6 | // RiverpodGenerator 7 | // ************************************************************************** 8 | 9 | String _$easyRouterHash() => r'3ab792a8cf001ee5d6e12fa0dbb565230f37d558'; 10 | 11 | /// See also [easyRouter]. 12 | @ProviderFor(easyRouter) 13 | final easyRouterPod = Provider.internal( 14 | easyRouter, 15 | name: r'easyRouterPod', 16 | debugGetCreateSourceHash: 17 | const bool.fromEnvironment('dart.vm.product') ? null : _$easyRouterHash, 18 | dependencies: null, 19 | allTransitiveDependencies: null, 20 | ); 21 | 22 | typedef EasyRouterRef = ProviderRef; 23 | // ignore_for_file: type=lint 24 | // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member 25 | -------------------------------------------------------------------------------- /lib/theme/theme.dart: -------------------------------------------------------------------------------- 1 | import 'package:easy_mygo/utils/hive/hive.dart'; 2 | import 'package:freezed_annotation/freezed_annotation.dart'; 3 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 4 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 5 | 6 | part 'theme.g.dart'; 7 | 8 | part 'theme.freezed.dart'; 9 | 10 | @freezed 11 | class ThemeConfig with _$ThemeConfig { 12 | static ThemeConfig none = ThemeConfig(seedColorIndex: -1, darkModeIndex: -1); 13 | 14 | factory ThemeConfig({ 15 | @Default(0) int seedColorIndex, 16 | @Default(0) int darkModeIndex, 17 | }) = _ThemeConfig; 18 | 19 | factory ThemeConfig.fromJson(Map json) => 20 | _$ThemeConfigFromJson(json); 21 | } 22 | 23 | @Riverpod() 24 | class ThemeController extends _$ThemeController { 25 | static ThemeController of(WidgetRef ref) => 26 | ref.watch(themeControllerPod.notifier); 27 | 28 | static ThemeConfig watch(WidgetRef ref) => ref.watch(themeControllerPod); 29 | 30 | late Future _init; 31 | 32 | ThemeController() { 33 | _init = Future.microtask(() { 34 | _innerInit(); 35 | }); 36 | } 37 | 38 | @override 39 | ThemeConfig build() { 40 | return ThemeConfig.none; 41 | } 42 | 43 | Future _innerInit() async { 44 | final box = await HiveBox.themeConfig(); 45 | final themeMapJson = await box.getSingle(); 46 | final themeConfig = ThemeConfig.fromJson((themeMapJson)?.cast() ?? {}); 47 | state = themeConfig; 48 | await box.close(); 49 | } 50 | 51 | Future setTheme(int index) async { 52 | await _init; 53 | final box = await HiveBox.themeConfig(); 54 | final newConfig = state.copyWith(seedColorIndex: index); 55 | state = newConfig; 56 | await box.putSingle(newConfig.toJson()); 57 | await box.close(); 58 | } 59 | 60 | Future setDarkMode(int mode) async { 61 | await _init; 62 | final box = await HiveBox.themeConfig(); 63 | final newConfig = state.copyWith(darkModeIndex: mode); 64 | state = newConfig; 65 | await box.putSingle(newConfig.toJson()); 66 | await box.close(); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /lib/theme/theme.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'theme.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _$ThemeConfigImpl _$$ThemeConfigImplFromJson(Map json) => 10 | _$ThemeConfigImpl( 11 | seedColorIndex: (json['seedColorIndex'] as num?)?.toInt() ?? 0, 12 | darkModeIndex: (json['darkModeIndex'] as num?)?.toInt() ?? 0, 13 | ); 14 | 15 | Map _$$ThemeConfigImplToJson(_$ThemeConfigImpl instance) => 16 | { 17 | 'seedColorIndex': instance.seedColorIndex, 18 | 'darkModeIndex': instance.darkModeIndex, 19 | }; 20 | 21 | // ************************************************************************** 22 | // RiverpodGenerator 23 | // ************************************************************************** 24 | 25 | String _$themeControllerHash() => r'71790e9333c500550b19f900a07520e9dcfa0b7c'; 26 | 27 | /// See also [ThemeController]. 28 | @ProviderFor(ThemeController) 29 | final themeControllerPod = 30 | AutoDisposeNotifierProvider.internal( 31 | ThemeController.new, 32 | name: r'themeControllerPod', 33 | debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') 34 | ? null 35 | : _$themeControllerHash, 36 | dependencies: null, 37 | allTransitiveDependencies: null, 38 | ); 39 | 40 | typedef _$ThemeController = AutoDisposeNotifier; 41 | // ignore_for_file: type=lint 42 | // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member 43 | -------------------------------------------------------------------------------- /lib/ui/common/cover_card.dart: -------------------------------------------------------------------------------- 1 | import 'package:easy_mygo/entity/book/cover/book_cover.dart'; 2 | import 'package:flutter/cupertino.dart'; 3 | import 'package:flutter/material.dart'; 4 | 5 | class BookCoverCard extends StatelessWidget { 6 | static const double coverRatio = 181 / 256; 7 | static const int maxLine = 2; 8 | static const double titleFontSize = 14; 9 | // 1.1 倍行高 10 | static const double titleFontLineHeight = 1.1; 11 | static const double titleHeight = 12 | maxLine * (titleFontSize * titleFontLineHeight); 13 | 14 | // 根据宽度测量高度 15 | // 19 / 27 的海报 + 两行文字高度 16 | static double measureHeight(double width) { 17 | final coverHeight = width / coverRatio; 18 | return coverHeight + titleHeight; 19 | } 20 | 21 | final BookCover cover; 22 | 23 | const BookCoverCard({super.key, required this.cover}); 24 | 25 | @override 26 | Widget build(BuildContext context) { 27 | const textStyle = 28 | TextStyle(fontSize: titleFontSize, height: titleFontLineHeight); 29 | return Column( 30 | children: [ 31 | AspectRatio( 32 | aspectRatio: coverRatio, 33 | child: Image.network( 34 | cover.cover, 35 | width: double.infinity, 36 | )), 37 | SizedBox( 38 | height: titleHeight, 39 | width: double.infinity, 40 | child: Text( 41 | cover.label, 42 | style: textStyle, 43 | softWrap: true, 44 | maxLines: maxLine, 45 | overflow: TextOverflow.ellipsis, 46 | ), 47 | ) 48 | ], 49 | ); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /lib/ui/common/loading_icon.dart: -------------------------------------------------------------------------------- 1 | 2 | 3 | import 'package:easy_refresh/easy_refresh.dart'; 4 | import 'package:flutter/cupertino.dart'; 5 | import 'package:flutter/material.dart'; 6 | import 'package:flutter/widgets.dart'; 7 | 8 | class LoadingIcon extends StatelessWidget { 9 | const LoadingIcon({super.key}); 10 | 11 | static Widget Function( 12 | BuildContext context, IndicatorState state, double animation) easyPullIconBuilder = (BuildContext context, IndicatorState state, double animation) { 13 | Widget icon; 14 | if (state.result == IndicatorResult.noMore){ 15 | //full 16 | icon = SizedBox( 17 | child: Image.asset("assets/sakiko_ok.png"), 18 | ); 19 | }else if(state.mode == IndicatorMode.processing || state.mode == IndicatorMode.ready) { 20 | //loading 21 | icon = SizedBox( 22 | child: Image.asset("assets/sakiko_loading.gif"), 23 | ); 24 | }else if(state.mode == IndicatorMode.processed || state.mode == IndicatorMode.done) { 25 | if (state.result == IndicatorResult.fail) { 26 | //fail 27 | icon = SizedBox( 28 | child: Image.asset("assets/sakiko_error.png"), 29 | ); 30 | } else { 31 | //success 32 | icon = SizedBox( 33 | child: Image.asset("assets/sakiko_ok.png"), 34 | ); 35 | } 36 | }else{ 37 | // pull 38 | icon = SizedBox( 39 | child: Image.asset("assets/sakiko_pull.png"), 40 | ); 41 | } 42 | 43 | return AnimatedSwitcher( 44 | duration: const Duration(milliseconds: 300), 45 | reverseDuration: const Duration(milliseconds: 200), 46 | transitionBuilder: (child, animation) { 47 | return FadeTransition( 48 | opacity: animation, 49 | child: ScaleTransition( 50 | scale: animation, 51 | child: child, 52 | ), 53 | ); 54 | }, 55 | child: icon, 56 | ); 57 | }; 58 | 59 | 60 | @override 61 | Widget build(BuildContext context) { 62 | return Center( 63 | child: Image.asset("assets/sakiko_loading.gif"), 64 | ); 65 | } 66 | } 67 | 68 | -------------------------------------------------------------------------------- /lib/ui/main/history/history.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 4 | 5 | /// Created by heyanlin on 2023/9/22. 6 | 7 | class HistoryPage extends HookConsumerWidget { 8 | const HistoryPage({super.key}); 9 | 10 | @override 11 | Widget build(BuildContext context, WidgetRef ref) { 12 | // TODO: implement build 13 | return const Text('history'); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /lib/ui/main/home/home.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:easy_mygo/repository/book_cover/page/novel_cover_page.dart'; 4 | import 'package:easy_mygo/plugin/component/api/novel/home/page/home_page.dart'; 5 | import 'package:easy_mygo/plugin/source/controller/source_controller.dart'; 6 | import 'package:easy_mygo/ui/main/home/page/home_book_page.dart'; 7 | import 'package:easy_mygo/ui/main/home/view_model/home_view_model.dart'; 8 | import 'package:flutter/material.dart'; 9 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 10 | 11 | /// Created by heyanlin on 2023/9/22. 12 | 13 | class HomePage extends HookConsumerWidget { 14 | const HomePage({super.key}); 15 | 16 | @override 17 | Widget build(BuildContext context, WidgetRef ref) { 18 | final state = HomeViewModel.watch(ref); 19 | final sourceBundle = SourceBundle.of(ref); 20 | 21 | final component = sourceBundle.getNovelHomeList().firstOrNull; 22 | if (component != null) { 23 | return Container( 24 | color: Theme.of(context).colorScheme.surface, 25 | child: HomeBookPageWidget( 26 | homePage: NovelBookHomeCoverPage( 27 | component, 28 | NovelHomePage(id: "", label: ""), 29 | ), 30 | ), 31 | ); 32 | } 33 | return Text(jsonEncode(state)); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lib/ui/main/library/library.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 4 | 5 | /// Created by heyanlin on 2023/9/22. 6 | 7 | class LibraryPage extends HookConsumerWidget { 8 | const LibraryPage({super.key}); 9 | 10 | @override 11 | Widget build(BuildContext context, WidgetRef ref) { 12 | return const Text('library'); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /lib/ui/main/more/more.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 4 | 5 | /// Created by heyanlin on 2023/9/22. 6 | 7 | class MorePage extends HookConsumerWidget { 8 | const MorePage({super.key}); 9 | 10 | @override 11 | Widget build(BuildContext context, WidgetRef ref) { 12 | // TODO: implement build 13 | return const Text('more'); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /lib/ui/splash/splash.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | class SplashScreen extends StatelessWidget { 4 | const SplashScreen({super.key}); 5 | 6 | @override 7 | Widget build(BuildContext context) { 8 | return const Text( 9 | "Loading", 10 | textDirection: TextDirection.ltr, 11 | ); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /lib/utils/file_index/item/file_index_item.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | part 'file_index_item.g.dart'; 4 | part 'file_index_item.freezed.dart'; 5 | 6 | 7 | @freezed 8 | class FileIndexItem with _$FileIndexItem { 9 | factory FileIndexItem({ 10 | // 文件相对路径 ["a", "b"] => "a/b" 11 | required List path, 12 | // 文件大小 13 | required int size, 14 | }) = _FileIndexItem; 15 | 16 | factory FileIndexItem.fromJson(Map json) => 17 | _$FileIndexItemFromJson(json); 18 | } -------------------------------------------------------------------------------- /lib/utils/file_index/item/file_index_item.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'file_index_item.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _$FileIndexItemImpl _$$FileIndexItemImplFromJson(Map json) => 10 | _$FileIndexItemImpl( 11 | path: (json['path'] as List).map((e) => e as String).toList(), 12 | size: (json['size'] as num).toInt(), 13 | ); 14 | 15 | Map _$$FileIndexItemImplToJson(_$FileIndexItemImpl instance) => 16 | { 17 | 'path': instance.path, 18 | 'size': instance.size, 19 | }; 20 | -------------------------------------------------------------------------------- /lib/utils/hive/hive.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:easy_mygo/c.dart'; 4 | import 'package:hive_ce/hive.dart'; 5 | import 'package:path/path.dart'; 6 | 7 | class HiveBox { 8 | static final init = Future(() async { 9 | final applicationDir = await EasyConstant.applicationPath; 10 | Hive.init(join(applicationDir.path, "hive")); 11 | }); 12 | 13 | static String key = "Mygo_single_key"; 14 | 15 | static Future> themeConfig() async { 16 | await init; 17 | return Hive.openLazyBox("theme_config"); 18 | } 19 | 20 | static Future> sourceConfig() async { 21 | await init; 22 | return Hive.openLazyBox("source_config"); 23 | } 24 | 25 | static Future> config() async { 26 | await init; 27 | return Hive.openLazyBox("config"); 28 | } 29 | } 30 | 31 | extension BoxExt on Box { 32 | E? getSingle({E? defaultValue}) => 33 | get(HiveBox.key, defaultValue: defaultValue); 34 | void putSingle(E value) => put(HiveBox.key, value); 35 | } 36 | 37 | extension LazyBoxExt on LazyBox { 38 | Future getSingle({E? defaultValue}) => 39 | get(HiveBox.key, defaultValue: defaultValue); 40 | Future putSingle(E value) => put(HiveBox.key, value); 41 | } 42 | -------------------------------------------------------------------------------- /lib/utils/json.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | dynamic easyJsonDecode(String source, {dynamic def, dynamic Function(dynamic)? transform}) { 4 | try { 5 | final d = jsonDecode(source); 6 | if (transform != null){ 7 | return transform(d); 8 | } 9 | return d; 10 | } catch (e) { 11 | if (def == null) { 12 | rethrow; 13 | } 14 | return def; 15 | } 16 | } 17 | 18 | String easyJsonEncode(Object? source) { 19 | try { 20 | return jsonEncode(source); 21 | } catch (e) { 22 | return ""; 23 | } 24 | } 25 | 26 | -------------------------------------------------------------------------------- /lib/utils/platform.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'dart:io'; 3 | 4 | class PlatformUtils { 5 | 6 | static final isMobile = Platform.isAndroid || Platform.isIOS; 7 | 8 | } -------------------------------------------------------------------------------- /lib/utils/zip/zip_utils.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'dart:io'; 3 | import 'package:archive/archive_io.dart'; 4 | import 'package:path/path.dart'; 5 | 6 | class ZipUtils { 7 | 8 | static Future unzip(String zipPath, String targetPath, {bool override = true}) async { 9 | try { 10 | final zipFile = File(zipPath).absolute; 11 | if(! await zipFile.exists()){ 12 | return false; 13 | } 14 | var targetDir = Directory(targetPath); 15 | if (await targetDir.exists()){ 16 | if(override){ 17 | await targetDir.delete(recursive: true); 18 | }else{ 19 | return false; 20 | } 21 | } 22 | targetDir = await targetDir.create(recursive: true); 23 | 24 | final stream = InputFileStream(zipFile.path); 25 | final archive = ZipDecoder().decodeBuffer(stream); 26 | for (var value in archive) { 27 | if (value.isFile) { 28 | final data = value.content as List; 29 | var f = File(join(targetPath, value.name)); 30 | f = await f.create(recursive: true); 31 | await f.writeAsBytes(data, flush: true); 32 | } else { 33 | await Directory(join(targetPath, value.name)).create( 34 | recursive: true, 35 | ); 36 | } 37 | } 38 | return true; 39 | }catch(e){ 40 | return false; 41 | } 42 | 43 | } 44 | 45 | 46 | 47 | } -------------------------------------------------------------------------------- /linux/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ephemeral 2 | -------------------------------------------------------------------------------- /linux/main.cc: -------------------------------------------------------------------------------- 1 | #include "my_application.h" 2 | 3 | int main(int argc, char** argv) { 4 | g_autoptr(MyApplication) app = my_application_new(); 5 | return g_application_run(G_APPLICATION(app), argc, argv); 6 | } 7 | -------------------------------------------------------------------------------- /linux/my_application.h: -------------------------------------------------------------------------------- 1 | #ifndef FLUTTER_MY_APPLICATION_H_ 2 | #define FLUTTER_MY_APPLICATION_H_ 3 | 4 | #include 5 | 6 | G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, 7 | GtkApplication) 8 | 9 | /** 10 | * my_application_new: 11 | * 12 | * Creates a new Flutter-based application. 13 | * 14 | * Returns: a new #MyApplication. 15 | */ 16 | MyApplication* my_application_new(); 17 | 18 | #endif // FLUTTER_MY_APPLICATION_H_ 19 | -------------------------------------------------------------------------------- /macos/.gitignore: -------------------------------------------------------------------------------- 1 | # Flutter-related 2 | **/Flutter/ephemeral/ 3 | **/Pods/ 4 | 5 | # Xcode-related 6 | **/dgph 7 | **/xcuserdata/ 8 | -------------------------------------------------------------------------------- /macos/Flutter/Flutter-Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "ephemeral/Flutter-Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /macos/Flutter/Flutter-Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "ephemeral/Flutter-Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /macos/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /macos/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | 4 | @NSApplicationMain 5 | class AppDelegate: FlutterAppDelegate { 6 | override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { 7 | return true 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "16x16", 5 | "idiom" : "mac", 6 | "filename" : "app_icon_16.png", 7 | "scale" : "1x" 8 | }, 9 | { 10 | "size" : "16x16", 11 | "idiom" : "mac", 12 | "filename" : "app_icon_32.png", 13 | "scale" : "2x" 14 | }, 15 | { 16 | "size" : "32x32", 17 | "idiom" : "mac", 18 | "filename" : "app_icon_32.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "32x32", 23 | "idiom" : "mac", 24 | "filename" : "app_icon_64.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "128x128", 29 | "idiom" : "mac", 30 | "filename" : "app_icon_128.png", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "size" : "128x128", 35 | "idiom" : "mac", 36 | "filename" : "app_icon_256.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "256x256", 41 | "idiom" : "mac", 42 | "filename" : "app_icon_256.png", 43 | "scale" : "1x" 44 | }, 45 | { 46 | "size" : "256x256", 47 | "idiom" : "mac", 48 | "filename" : "app_icon_512.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "512x512", 53 | "idiom" : "mac", 54 | "filename" : "app_icon_512.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "512x512", 59 | "idiom" : "mac", 60 | "filename" : "app_icon_1024.png", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easybangumiorg/EasyMyGo/8f9a7163c5d4c9744f32b1f2b8a68577fa8e3fce/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easybangumiorg/EasyMyGo/8f9a7163c5d4c9744f32b1f2b8a68577fa8e3fce/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easybangumiorg/EasyMyGo/8f9a7163c5d4c9744f32b1f2b8a68577fa8e3fce/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easybangumiorg/EasyMyGo/8f9a7163c5d4c9744f32b1f2b8a68577fa8e3fce/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easybangumiorg/EasyMyGo/8f9a7163c5d4c9744f32b1f2b8a68577fa8e3fce/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easybangumiorg/EasyMyGo/8f9a7163c5d4c9744f32b1f2b8a68577fa8e3fce/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easybangumiorg/EasyMyGo/8f9a7163c5d4c9744f32b1f2b8a68577fa8e3fce/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png -------------------------------------------------------------------------------- /macos/Runner/Configs/AppInfo.xcconfig: -------------------------------------------------------------------------------- 1 | // Application-level settings for the Runner target. 2 | // 3 | // This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the 4 | // future. If not, the values below would default to using the project name when this becomes a 5 | // 'flutter create' template. 6 | 7 | // The application's name. By default this is also the title of the Flutter window. 8 | PRODUCT_NAME = easy_mygo 9 | 10 | // The application's bundle identifier 11 | PRODUCT_BUNDLE_IDENTIFIER = com.heyanle.easyBook 12 | 13 | // The copyright displayed in application information 14 | PRODUCT_COPYRIGHT = Copyright © 2023 com.heyanle. All rights reserved. 15 | -------------------------------------------------------------------------------- /macos/Runner/Configs/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Debug.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /macos/Runner/Configs/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Release.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /macos/Runner/Configs/Warnings.xcconfig: -------------------------------------------------------------------------------- 1 | WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings 2 | GCC_WARN_UNDECLARED_SELECTOR = YES 3 | CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES 4 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE 5 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES 6 | CLANG_WARN_PRAGMA_PACK = YES 7 | CLANG_WARN_STRICT_PROTOTYPES = YES 8 | CLANG_WARN_COMMA = YES 9 | GCC_WARN_STRICT_SELECTOR_MATCH = YES 10 | CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES 11 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES 12 | GCC_WARN_SHADOW = YES 13 | CLANG_WARN_UNREACHABLE_CODE = YES 14 | -------------------------------------------------------------------------------- /macos/Runner/DebugProfile.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.cs.allow-jit 8 | 9 | com.apple.security.network.server 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /macos/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSMinimumSystemVersion 24 | $(MACOSX_DEPLOYMENT_TARGET) 25 | NSHumanReadableCopyright 26 | $(PRODUCT_COPYRIGHT) 27 | NSMainNibFile 28 | MainMenu 29 | NSPrincipalClass 30 | NSApplication 31 | 32 | 33 | -------------------------------------------------------------------------------- /macos/Runner/MainFlutterWindow.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | 4 | class MainFlutterWindow: NSWindow { 5 | override func awakeFromNib() { 6 | let flutterViewController = FlutterViewController() 7 | let windowFrame = self.frame 8 | self.contentViewController = flutterViewController 9 | self.setFrame(windowFrame, display: true) 10 | 11 | RegisterGeneratedPlugins(registry: flutterViewController) 12 | 13 | super.awakeFromNib() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /macos/Runner/Release.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /macos/RunnerTests/RunnerTests.swift: -------------------------------------------------------------------------------- 1 | import FlutterMacOS 2 | import Cocoa 3 | import XCTest 4 | 5 | class RunnerTests: XCTestCase { 6 | 7 | func testExample() { 8 | // If you add code to the Runner application, consider adding tests here. 9 | // See https://developer.apple.com/documentation/xctest for more information about using XCTest. 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /make/README.md: -------------------------------------------------------------------------------- 1 | # 构建器 2 | 3 | Ayala评价:完全没有必要的操作,可以考虑使用Github Actions代替 4 | 5 | 一个简单的构建过程: 6 | 7 | ```sh 8 | flutter pub get 9 | dart run build_runner build 10 | flutter build apk --target=./lib/app.dart --release 11 | flutter build macos --target=./lib/app.dart --release 12 | flutter build windows --target=./lib/app.dart --release 13 | ``` -------------------------------------------------------------------------------- /make/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: make 2 | description: this is easy make 3 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev 4 | 5 | version: 1.0.0+1 6 | 7 | environment: 8 | sdk: '>=3.3.0 <4.0.0' 9 | 10 | dependencies: 11 | yaml: ^3.1.2 -------------------------------------------------------------------------------- /test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // 3 | // To perform an interaction with a widget in your test, use the WidgetTester 4 | // utility in the flutter_test package. For example, you can send tap and scroll 5 | // gestures. You can also use WidgetTester to find child widgets in the widget 6 | // tree, read text, and verify that the values of widget properties are correct. 7 | 8 | import 'package:flutter/material.dart'; 9 | import 'package:flutter_test/flutter_test.dart'; 10 | 11 | import 'package:easy_mygo/app.dart'; 12 | 13 | void main() { 14 | testWidgets('Counter increments smoke test', (WidgetTester tester) async { 15 | // Build our app and trigger a frame. 16 | await tester.pumpWidget(const EasyBookApp()); 17 | 18 | // Verify that our counter starts at 0. 19 | expect(find.text('0'), findsOneWidget); 20 | expect(find.text('1'), findsNothing); 21 | 22 | // Tap the '+' icon and trigger a frame. 23 | await tester.tap(find.byIcon(Icons.add)); 24 | await tester.pump(); 25 | 26 | // Verify that our counter has incremented. 27 | expect(find.text('0'), findsNothing); 28 | expect(find.text('1'), findsOneWidget); 29 | }); 30 | } 31 | -------------------------------------------------------------------------------- /web/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easybangumiorg/EasyMyGo/8f9a7163c5d4c9744f32b1f2b8a68577fa8e3fce/web/favicon.png -------------------------------------------------------------------------------- /web/icons/Icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easybangumiorg/EasyMyGo/8f9a7163c5d4c9744f32b1f2b8a68577fa8e3fce/web/icons/Icon-192.png -------------------------------------------------------------------------------- /web/icons/Icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easybangumiorg/EasyMyGo/8f9a7163c5d4c9744f32b1f2b8a68577fa8e3fce/web/icons/Icon-512.png -------------------------------------------------------------------------------- /web/icons/Icon-maskable-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easybangumiorg/EasyMyGo/8f9a7163c5d4c9744f32b1f2b8a68577fa8e3fce/web/icons/Icon-maskable-192.png -------------------------------------------------------------------------------- /web/icons/Icon-maskable-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easybangumiorg/EasyMyGo/8f9a7163c5d4c9744f32b1f2b8a68577fa8e3fce/web/icons/Icon-maskable-512.png -------------------------------------------------------------------------------- /web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | easy_mygo 33 | 34 | 35 | 39 | 40 | 41 | 42 | 43 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /web/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "easy_mygo", 3 | "short_name": "easy_mygo", 4 | "start_url": ".", 5 | "display": "standalone", 6 | "background_color": "#0175C2", 7 | "theme_color": "#0175C2", 8 | "description": "this is easy book", 9 | "orientation": "portrait-primary", 10 | "prefer_related_applications": false, 11 | "icons": [ 12 | { 13 | "src": "icons/Icon-192.png", 14 | "sizes": "192x192", 15 | "type": "image/png" 16 | }, 17 | { 18 | "src": "icons/Icon-512.png", 19 | "sizes": "512x512", 20 | "type": "image/png" 21 | }, 22 | { 23 | "src": "icons/Icon-maskable-192.png", 24 | "sizes": "192x192", 25 | "type": "image/png", 26 | "purpose": "maskable" 27 | }, 28 | { 29 | "src": "icons/Icon-maskable-512.png", 30 | "sizes": "512x512", 31 | "type": "image/png", 32 | "purpose": "maskable" 33 | } 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /windows/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ephemeral/ 2 | 3 | # Visual Studio user-specific files. 4 | *.suo 5 | *.user 6 | *.userosscache 7 | *.sln.docstates 8 | 9 | # Visual Studio build-related files. 10 | x64/ 11 | x86/ 12 | 13 | # Visual Studio cache files 14 | # files ending in .cache can be ignored 15 | *.[Cc]ache 16 | # but keep track of directories ending in .cache 17 | !*.[Cc]ache/ 18 | -------------------------------------------------------------------------------- /windows/runner/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | project(runner LANGUAGES CXX) 3 | 4 | # Define the application target. To change its name, change BINARY_NAME in the 5 | # top-level CMakeLists.txt, not the value here, or `flutter run` will no longer 6 | # work. 7 | # 8 | # Any new source files that you add to the application should be added here. 9 | add_executable(${BINARY_NAME} WIN32 10 | "flutter_window.cpp" 11 | "main.cpp" 12 | "utils.cpp" 13 | "win32_window.cpp" 14 | "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" 15 | "Runner.rc" 16 | "runner.exe.manifest" 17 | ) 18 | 19 | # Apply the standard set of build settings. This can be removed for applications 20 | # that need different build settings. 21 | apply_standard_settings(${BINARY_NAME}) 22 | 23 | # Add preprocessor definitions for the build version. 24 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"") 25 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}") 26 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}") 27 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}") 28 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}") 29 | 30 | # Disable Windows macros that collide with C++ standard library functions. 31 | target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") 32 | 33 | # Add dependency libraries and include directories. Add any application-specific 34 | # dependencies here. 35 | target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) 36 | target_link_libraries(${BINARY_NAME} PRIVATE "dwmapi.lib") 37 | target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") 38 | 39 | # Run the Flutter tool portions of the build. This must not be removed. 40 | add_dependencies(${BINARY_NAME} flutter_assemble) 41 | -------------------------------------------------------------------------------- /windows/runner/flutter_window.cpp: -------------------------------------------------------------------------------- 1 | #include "flutter_window.h" 2 | 3 | #include 4 | 5 | #include "flutter/generated_plugin_registrant.h" 6 | 7 | FlutterWindow::FlutterWindow(const flutter::DartProject& project) 8 | : project_(project) {} 9 | 10 | FlutterWindow::~FlutterWindow() {} 11 | 12 | bool FlutterWindow::OnCreate() { 13 | if (!Win32Window::OnCreate()) { 14 | return false; 15 | } 16 | 17 | RECT frame = GetClientArea(); 18 | 19 | // The size here must match the window dimensions to avoid unnecessary surface 20 | // creation / destruction in the startup path. 21 | flutter_controller_ = std::make_unique( 22 | frame.right - frame.left, frame.bottom - frame.top, project_); 23 | // Ensure that basic setup of the controller was successful. 24 | if (!flutter_controller_->engine() || !flutter_controller_->view()) { 25 | return false; 26 | } 27 | RegisterPlugins(flutter_controller_->engine()); 28 | SetChildContent(flutter_controller_->view()->GetNativeWindow()); 29 | 30 | flutter_controller_->engine()->SetNextFrameCallback([&]() { 31 | this->Show(); 32 | }); 33 | 34 | // Flutter can complete the first frame before the "show window" callback is 35 | // registered. The following call ensures a frame is pending to ensure the 36 | // window is shown. It is a no-op if the first frame hasn't completed yet. 37 | flutter_controller_->ForceRedraw(); 38 | 39 | return true; 40 | } 41 | 42 | void FlutterWindow::OnDestroy() { 43 | if (flutter_controller_) { 44 | flutter_controller_ = nullptr; 45 | } 46 | 47 | Win32Window::OnDestroy(); 48 | } 49 | 50 | LRESULT 51 | FlutterWindow::MessageHandler(HWND hwnd, UINT const message, 52 | WPARAM const wparam, 53 | LPARAM const lparam) noexcept { 54 | // Give Flutter, including plugins, an opportunity to handle window messages. 55 | if (flutter_controller_) { 56 | std::optional result = 57 | flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, 58 | lparam); 59 | if (result) { 60 | return *result; 61 | } 62 | } 63 | 64 | switch (message) { 65 | case WM_FONTCHANGE: 66 | flutter_controller_->engine()->ReloadSystemFonts(); 67 | break; 68 | } 69 | 70 | return Win32Window::MessageHandler(hwnd, message, wparam, lparam); 71 | } 72 | -------------------------------------------------------------------------------- /windows/runner/flutter_window.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_FLUTTER_WINDOW_H_ 2 | #define RUNNER_FLUTTER_WINDOW_H_ 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include "win32_window.h" 10 | 11 | // A window that does nothing but host a Flutter view. 12 | class FlutterWindow : public Win32Window { 13 | public: 14 | // Creates a new FlutterWindow hosting a Flutter view running |project|. 15 | explicit FlutterWindow(const flutter::DartProject& project); 16 | virtual ~FlutterWindow(); 17 | 18 | protected: 19 | // Win32Window: 20 | bool OnCreate() override; 21 | void OnDestroy() override; 22 | LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, 23 | LPARAM const lparam) noexcept override; 24 | 25 | private: 26 | // The project to run. 27 | flutter::DartProject project_; 28 | 29 | // The Flutter instance hosted by this window. 30 | std::unique_ptr flutter_controller_; 31 | }; 32 | 33 | #endif // RUNNER_FLUTTER_WINDOW_H_ 34 | -------------------------------------------------------------------------------- /windows/runner/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "flutter_window.h" 6 | #include "utils.h" 7 | 8 | int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, 9 | _In_ wchar_t *command_line, _In_ int show_command) { 10 | // Attach to console when present (e.g., 'flutter run') or create a 11 | // new console when running with a debugger. 12 | if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { 13 | CreateAndAttachConsole(); 14 | } 15 | 16 | // Initialize COM, so that it is available for use in the library and/or 17 | // plugins. 18 | ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); 19 | 20 | flutter::DartProject project(L"data"); 21 | 22 | std::vector command_line_arguments = 23 | GetCommandLineArguments(); 24 | 25 | project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); 26 | 27 | FlutterWindow window(project); 28 | Win32Window::Point origin(10, 10); 29 | Win32Window::Size size(1280, 720); 30 | if (!window.Create(L"easy_mygo", origin, size)) { 31 | return EXIT_FAILURE; 32 | } 33 | window.SetQuitOnClose(true); 34 | 35 | ::MSG msg; 36 | while (::GetMessage(&msg, nullptr, 0, 0)) { 37 | ::TranslateMessage(&msg); 38 | ::DispatchMessage(&msg); 39 | } 40 | 41 | ::CoUninitialize(); 42 | return EXIT_SUCCESS; 43 | } 44 | -------------------------------------------------------------------------------- /windows/runner/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by Runner.rc 4 | // 5 | #define IDI_APP_ICON 101 6 | 7 | // Next default values for new objects 8 | // 9 | #ifdef APSTUDIO_INVOKED 10 | #ifndef APSTUDIO_READONLY_SYMBOLS 11 | #define _APS_NEXT_RESOURCE_VALUE 102 12 | #define _APS_NEXT_COMMAND_VALUE 40001 13 | #define _APS_NEXT_CONTROL_VALUE 1001 14 | #define _APS_NEXT_SYMED_VALUE 101 15 | #endif 16 | #endif 17 | -------------------------------------------------------------------------------- /windows/runner/resources/app_icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easybangumiorg/EasyMyGo/8f9a7163c5d4c9744f32b1f2b8a68577fa8e3fce/windows/runner/resources/app_icon.ico -------------------------------------------------------------------------------- /windows/runner/runner.exe.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PerMonitorV2 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /windows/runner/utils.cpp: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | void CreateAndAttachConsole() { 11 | if (::AllocConsole()) { 12 | FILE *unused; 13 | if (freopen_s(&unused, "CONOUT$", "w", stdout)) { 14 | _dup2(_fileno(stdout), 1); 15 | } 16 | if (freopen_s(&unused, "CONOUT$", "w", stderr)) { 17 | _dup2(_fileno(stdout), 2); 18 | } 19 | std::ios::sync_with_stdio(); 20 | FlutterDesktopResyncOutputStreams(); 21 | } 22 | } 23 | 24 | std::vector GetCommandLineArguments() { 25 | // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. 26 | int argc; 27 | wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); 28 | if (argv == nullptr) { 29 | return std::vector(); 30 | } 31 | 32 | std::vector command_line_arguments; 33 | 34 | // Skip the first argument as it's the binary name. 35 | for (int i = 1; i < argc; i++) { 36 | command_line_arguments.push_back(Utf8FromUtf16(argv[i])); 37 | } 38 | 39 | ::LocalFree(argv); 40 | 41 | return command_line_arguments; 42 | } 43 | 44 | std::string Utf8FromUtf16(const wchar_t* utf16_string) { 45 | if (utf16_string == nullptr) { 46 | return std::string(); 47 | } 48 | int target_length = ::WideCharToMultiByte( 49 | CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, 50 | -1, nullptr, 0, nullptr, nullptr) 51 | -1; // remove the trailing null character 52 | int input_length = (int)wcslen(utf16_string); 53 | std::string utf8_string; 54 | if (target_length <= 0 || target_length > utf8_string.max_size()) { 55 | return utf8_string; 56 | } 57 | utf8_string.resize(target_length); 58 | int converted_length = ::WideCharToMultiByte( 59 | CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, 60 | input_length, utf8_string.data(), target_length, nullptr, nullptr); 61 | if (converted_length == 0) { 62 | return std::string(); 63 | } 64 | return utf8_string; 65 | } 66 | -------------------------------------------------------------------------------- /windows/runner/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_UTILS_H_ 2 | #define RUNNER_UTILS_H_ 3 | 4 | #include 5 | #include 6 | 7 | // Creates a console for the process, and redirects stdout and stderr to 8 | // it for both the runner and the Flutter library. 9 | void CreateAndAttachConsole(); 10 | 11 | // Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string 12 | // encoded in UTF-8. Returns an empty std::string on failure. 13 | std::string Utf8FromUtf16(const wchar_t* utf16_string); 14 | 15 | // Gets the command line arguments passed in as a std::vector, 16 | // encoded in UTF-8. Returns an empty std::vector on failure. 17 | std::vector GetCommandLineArguments(); 18 | 19 | #endif // RUNNER_UTILS_H_ 20 | --------------------------------------------------------------------------------