├── rust ├── .gitignore ├── src │ ├── api │ │ ├── http │ │ │ ├── utils │ │ │ │ └── mod.rs │ │ │ └── mod.rs │ │ ├── mod.rs │ │ └── image │ │ │ ├── utils.rs │ │ │ ├── mod.rs │ │ │ ├── decode_image_rgba.rs │ │ │ └── get_image_size.rs │ └── lib.rs ├── build.rs └── Cargo.toml ├── hoyomi_bridge_ts ├── .npmignore ├── bump.config.ts ├── register_plugin.ts ├── README.md ├── index.ts ├── .gitignore └── package.json ├── linux ├── .gitignore ├── main.cc ├── hoyomi.desktop ├── flutter │ └── generated_plugin_registrant.h └── my_application.h ├── release.sh ├── lib ├── widgets │ ├── item_service_manager.dart │ ├── comic │ │ └── icon_button_share.dart │ ├── disabled.dart │ ├── h_back_button.dart │ ├── vertical_separator.dart │ ├── animated_fade_ignore.dart │ ├── delayed_widget.dart │ └── fading_scroll_view.dart ├── core_services │ ├── interfaces │ │ ├── main.dart │ │ ├── headers.ts │ │ ├── vtt.ts │ │ ├── url_search_params.ts │ │ ├── setting │ │ │ ├── setting_field.ts │ │ │ ├── field_input.ts │ │ │ ├── setting_field.dart │ │ │ └── setting_field.g.dart │ │ ├── web_rule.ts │ │ ├── genre.ts │ │ ├── status_enum.ts │ │ ├── filter.ts │ │ ├── status_enum.dart │ │ ├── user.ts │ │ ├── paginate.ts │ │ ├── vtt.dart │ │ ├── o_image.ts │ │ ├── export.dart │ │ ├── user.dart │ │ ├── paginate.dart │ │ ├── vtt.g.dart │ │ ├── headers.g.dart │ │ ├── filter.dart │ │ ├── o_image.g.dart │ │ └── genre.dart │ ├── comic │ │ ├── main.dart │ │ ├── interfaces │ │ │ ├── comic_param.ts │ │ │ ├── watch_page.ts │ │ │ ├── rate_value.ts │ │ │ ├── watch_page_updated.ts │ │ │ ├── home_comic_category.ts │ │ │ ├── comic_comments.ts │ │ │ ├── comic_carousel.ts │ │ │ ├── comic_home.ts │ │ │ ├── comic_follow.ts │ │ │ ├── comic_modes.dart │ │ │ ├── comic_category.ts │ │ │ ├── comic_history.ts │ │ │ ├── comic_modes.ts │ │ │ ├── comic_chapter.ts │ │ │ ├── comic.ts │ │ │ ├── watch_page.dart │ │ │ ├── comic_comment.ts │ │ │ ├── comic_param.dart │ │ │ ├── watch_page_updated.dart │ │ │ ├── rate_value.dart │ │ │ ├── comic_carousel_item.ts │ │ │ ├── comic_param.g.dart │ │ │ ├── watch_page.g.dart │ │ │ ├── meta_comic.ts │ │ │ ├── comic_home.dart │ │ │ └── rate_value.g.dart │ │ └── mixin │ │ │ ├── comic_auth_mixin.ts │ │ │ ├── main.dart │ │ │ ├── comic_auth_mixin.dart │ │ │ ├── comic_follow_mixin.dart │ │ │ ├── comic_follow_mixin.ts │ │ │ └── comic_watch_page_mixin.dart │ ├── eiga │ │ ├── interfaces │ │ │ ├── eiga_param.ts │ │ │ ├── server_source.ts │ │ │ ├── watch_time.ts │ │ │ ├── watch_time_updated.ts │ │ │ ├── source_video.ts │ │ │ ├── source_content.ts │ │ │ ├── watch_time_data.ts │ │ │ ├── home_eiga_category.ts │ │ │ ├── eiga_carousel.ts │ │ │ ├── opening_ending.ts │ │ │ ├── eiga_home.ts │ │ │ ├── subtitle.ts │ │ │ ├── eiga_follow.ts │ │ │ ├── eiga_episode.ts │ │ │ ├── eiga_episodes.ts │ │ │ ├── eiga_context.ts │ │ │ ├── eiga_context_with_episodes.ts │ │ │ ├── eiga_history.ts │ │ │ ├── eiga_category.ts │ │ │ ├── eiga_param.dart │ │ │ ├── watch_time.dart │ │ │ ├── eiga.ts │ │ │ ├── server_source.dart │ │ │ ├── source_content.dart │ │ │ ├── source_video.dart │ │ │ ├── watch_time_updated.dart │ │ │ ├── eiga_context.dart │ │ │ ├── watch_time_data.dart │ │ │ ├── subtitle.dart │ │ │ ├── eiga_param.g.dart │ │ │ ├── main.dart │ │ │ ├── eiga_carousel_item.ts │ │ │ ├── eiga_context_with_episodes.dart │ │ │ ├── watch_time.g.dart │ │ │ ├── server_source.g.dart │ │ │ └── opening_ending.dart │ │ ├── main.dart │ │ └── mixin │ │ │ ├── eiga_auth_mixin.ts │ │ │ ├── eiga_auth_mixin.dart │ │ │ ├── eiga_follow_mixin.dart │ │ │ ├── eiga_watch_time_mixin.dart │ │ │ └── eiga_follow_mixin.ts │ ├── widget │ │ ├── export.dart │ │ └── avatar_service.dart │ ├── exception │ │ ├── export.dart │ │ ├── captcha_required_exception.dart │ │ └── user_not_found_exception.dart │ ├── mixin │ │ ├── export.dart │ │ ├── auth_mixin.dart │ │ └── auth_mixin.ts │ ├── service.ts │ ├── base_service.dart │ └── service_init.ts ├── env.dart ├── plugins │ ├── init_services.dart │ ├── inflate_raw.dart │ ├── install_web_rules.dart │ ├── volume_controller.dart │ ├── install_web_rules.stub.dart │ ├── brightness_controller.dart │ ├── inflate_raw.io.dart │ ├── firebase.dart │ ├── volume_controller.stub.dart │ ├── application_document_directory.dart │ ├── inflate_raw.web.dart │ ├── brightness_controller.stub.dart │ ├── event_bus.dart │ ├── video_polyfill.dart │ ├── volume_controller.io.dart │ ├── install_web_rules.web.dart │ ├── android_sdk_int.dart │ ├── brightness_controller.io.dart │ ├── analytics.dart │ ├── fullscreen.dart │ ├── export.dart │ └── volume_controller.web.dart ├── utils │ ├── cache_remember │ │ └── cache_remember.dart │ ├── proxy_cache │ │ ├── proxy_cache.dart │ │ ├── proxy_cache.stub.dart │ │ └── proxy_cache.web.dart │ ├── future_cache.dart │ ├── format_number.dart │ ├── debouncer.dart │ ├── throttler.dart │ ├── export.dart │ ├── get_is_buffering.dart │ └── format_time_ago.dart ├── l10n │ ├── app_en.arb │ └── app_vi.arb ├── constraints │ └── x_platform.dart ├── extensions │ ├── list_extension.dart │ └── iterable_extension.dart ├── mixins │ └── auto_dispose_notifier.dart ├── pages │ └── webview_page.dart ├── rust_isolate │ ├── auto_trim_image_isolate.dart │ ├── unscramble_image_isolate.dart │ ├── unscramble_image_rows_isolate.dart │ └── unscramble_image_columns_isolate.dart ├── general_api │ ├── models │ │ ├── set_follow_response.g.dart │ │ ├── has_follow_response.g.dart │ │ ├── set_eiga_follow_response.g.dart │ │ ├── ignore.g.dart │ │ ├── ignore2.g.dart │ │ ├── has_eiga_follow_response.g.dart │ │ ├── ignore.dart │ │ ├── watch_page_schema.dart │ │ ├── watch_time_schema.dart │ │ ├── ignore2.dart │ │ ├── current_chapters.dart │ │ ├── set_follow_response.dart │ │ ├── watch_page_schema.g.dart │ │ ├── watch_time_schema.g.dart │ │ ├── post_api_eiga_set_watch_time_response.g.dart │ │ ├── list_watch_history.dart │ │ ├── post_api_comic_set_watch_page_response.g.dart │ │ ├── list_watch_history.g.dart │ │ ├── eiga_list_watch_history.g.dart │ │ ├── set_eiga_follow_response.dart │ │ ├── has_follow_response.dart │ │ ├── list_watch_page_schema.dart │ │ ├── list_watch_time_schema.dart │ │ ├── comic_list_watch_history.g.dart │ │ ├── list_watch_page_schema.g.dart │ │ ├── list_watch_time_schema.g.dart │ │ ├── eiga_list_watch_history.dart │ │ ├── comic_list_watch_history.dart │ │ ├── has_eiga_follow_response.dart │ │ ├── data3.g.dart │ │ ├── data6.g.dart │ │ ├── post_api_eiga_set_watch_time_response.dart │ │ └── post_api_comic_set_watch_page_response.dart │ └── general_api_client.dart ├── rust │ ├── lib.dart │ └── api │ │ ├── http │ │ └── utils │ │ │ └── socket_addr.dart │ │ └── image │ │ ├── get_image_size.dart │ │ └── auto_trim_image.dart ├── downloader │ └── eiga_downloader.dart ├── logic │ ├── search_language.dart │ └── normalize_plugin_url.dart ├── screens │ └── home_eiga │ │ └── widget │ │ ├── area_rewind.dart │ │ └── area_forward.dart ├── database │ └── scheme │ │ └── service_settings.dart └── transition │ └── slide_fade_transition.dart ├── web-extension └── hoyomi-helper │ ├── e2e │ └── basic.spec.ts │ ├── src │ ├── assets │ │ ├── logo.png │ │ └── logo-512.png │ ├── global.d.ts │ ├── env.ts │ ├── background │ │ ├── logic │ │ │ └── hash-to-int.ts │ │ └── contentScriptHMR.ts │ └── logic │ │ └── buffer-to-b64.ts │ ├── extension │ └── assets │ │ └── logo-512.png │ ├── allowlist.yaml │ ├── .gitpod.Dockerfile │ ├── .vscode │ ├── extensions.json │ └── settings.json │ ├── .gitignore │ ├── scripts │ ├── manifest.ts │ └── utils.ts │ ├── unocss.config.ts │ ├── shim.d.ts │ ├── playwright.config.ts │ ├── .gitpod.yml │ └── tsconfig.json ├── serverless ├── README.md ├── .xata │ ├── migrations │ │ ├── .ledger │ │ └── mig_cvhsldo1g95li4qgk0q0.json │ └── version │ │ └── compatibility.json ├── .gitignore ├── .vscode │ ├── extensions.json │ └── settings.json ├── drizzle │ ├── 0003_hard_gressill.sql │ ├── 0015_safe_maximus.sql │ ├── 0007_black_smasher.sql │ ├── 0008_majestic_pretty_boy.sql │ ├── 0014_exotic_purifiers.sql │ ├── 0006_glorious_puppet_master.sql │ ├── 0004_woozy_ender_wiggin.sql │ ├── 0000_goofy_jubilee.sql │ ├── 0002_living_madripoor.sql │ ├── 0012_crazy_madame_hydra.sql │ └── 0013_quick_randall.sql ├── .hintrc ├── .xatarc ├── logic │ ├── use-user.ts │ ├── single.ts │ └── parse-postgresl-url.ts ├── db │ ├── enum │ │ └── status_enum.ts │ ├── schema │ │ ├── users.ts │ │ ├── comic_newest.ts │ │ ├── eiga_follows.ts │ │ └── comic_follows.ts │ └── schema.ts ├── drizzle.config.ts ├── schema │ └── authorization.ts ├── firebase-admin.ts ├── package.json └── tsconfig.json ├── .hintrc ├── ios ├── Flutter │ ├── Debug.xcconfig │ └── Release.xcconfig ├── Runner │ ├── Runner-Bridging-Header.h │ ├── Assets.xcassets │ │ ├── LaunchImage.imageset │ │ │ ├── LaunchImage.png │ │ │ ├── LaunchImage@2x.png │ │ │ ├── LaunchImage@3x.png │ │ │ ├── README.md │ │ │ └── Contents.json │ │ └── AppIcon.appiconset │ │ │ ├── Icon-App-20x20@1x.png │ │ │ ├── Icon-App-20x20@2x.png │ │ │ ├── Icon-App-20x20@3x.png │ │ │ ├── Icon-App-29x29@1x.png │ │ │ ├── Icon-App-29x29@2x.png │ │ │ ├── Icon-App-29x29@3x.png │ │ │ ├── Icon-App-40x40@1x.png │ │ │ ├── Icon-App-40x40@2x.png │ │ │ ├── Icon-App-40x40@3x.png │ │ │ ├── Icon-App-50x50@1x.png │ │ │ ├── Icon-App-50x50@2x.png │ │ │ ├── Icon-App-57x57@1x.png │ │ │ ├── Icon-App-57x57@2x.png │ │ │ ├── Icon-App-60x60@2x.png │ │ │ ├── Icon-App-60x60@3x.png │ │ │ ├── Icon-App-72x72@1x.png │ │ │ ├── Icon-App-72x72@2x.png │ │ │ ├── Icon-App-76x76@1x.png │ │ │ ├── Icon-App-76x76@2x.png │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ └── Icon-App-83.5x83.5@2x.png │ └── AppDelegate.swift ├── Runner.xcodeproj │ └── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ ├── WorkspaceSettings.xcsettings │ │ └── IDEWorkspaceChecks.plist ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── WorkspaceSettings.xcsettings │ │ └── IDEWorkspaceChecks.plist ├── RunnerTests │ └── RunnerTests.swift └── .gitignore ├── release_hoyomi_bridge.sh ├── app.jpeg ├── app.png ├── .prettierrc ├── rust_builder ├── android │ ├── settings.gradle │ ├── .gitignore │ └── src │ │ └── main │ │ └── AndroidManifest.xml ├── cargokit │ ├── .gitignore │ ├── build_tool │ │ ├── README.md │ │ ├── bin │ │ │ └── build_tool.dart │ │ └── lib │ │ │ └── build_tool.dart │ ├── README │ └── cmake │ │ └── resolve_symlinks.ps1 ├── README.md ├── ios │ └── Classes │ │ └── dummy_file.c ├── macos │ └── Classes │ │ └── dummy_file.c ├── windows │ └── .gitignore ├── .gitignore ├── pubspec.yaml └── linux │ └── CMakeLists.txt ├── macos ├── Flutter │ ├── Flutter-Debug.xcconfig │ └── Flutter-Release.xcconfig ├── Runner │ ├── Configs │ │ ├── Debug.xcconfig │ │ ├── Release.xcconfig │ │ ├── Warnings.xcconfig │ │ └── AppInfo.xcconfig │ ├── Assets.xcassets │ │ └── AppIcon.appiconset │ │ │ ├── app_icon_16.png │ │ │ ├── app_icon_32.png │ │ │ ├── app_icon_64.png │ │ │ ├── app_icon_1024.png │ │ │ ├── app_icon_128.png │ │ │ ├── app_icon_256.png │ │ │ └── app_icon_512.png │ ├── Release.entitlements │ ├── DebugProfile.entitlements │ ├── MainFlutterWindow.swift │ └── AppDelegate.swift ├── .gitignore ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── Runner.xcodeproj │ └── project.xcworkspace │ │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── RunnerTests │ └── RunnerTests.swift ├── web ├── favicon.png ├── _routes.json └── icons │ ├── Icon-192.png │ ├── Icon-512.png │ ├── Icon-maskable-192.png │ └── Icon-maskable-512.png ├── flutter_rust_bridge.yaml ├── assets └── images │ └── blank.png ├── windows ├── runner │ ├── resources │ │ └── app_icon.ico │ ├── resource.h │ ├── runner.exe.manifest │ └── utils.h ├── .gitignore └── flutter │ └── generated_plugin_registrant.h ├── js_runtime_polyfill ├── register.ts ├── README.md ├── package.json ├── polyfill │ ├── unimplemented_error.ts │ └── base64_coder.ts ├── .gitignore ├── embed.ts ├── index.ts ├── scripts │ └── build_embed.ts └── tsconfig.json ├── run_chrome.sh ├── format.sh ├── android ├── app │ └── src │ │ ├── main │ │ ├── res │ │ │ ├── mipmap-hdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── launcher_icon.png │ │ │ ├── mipmap-mdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── launcher_icon.png │ │ │ ├── mipmap-xhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── launcher_icon.png │ │ │ ├── mipmap-xxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── launcher_icon.png │ │ │ ├── mipmap-xxxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── launcher_icon.png │ │ │ ├── xml │ │ │ │ └── filepaths.xml │ │ │ ├── drawable │ │ │ │ └── launch_background.xml │ │ │ └── drawable-v21 │ │ │ │ └── launch_background.xml │ │ └── kotlin │ │ │ └── git │ │ │ └── shin │ │ │ └── hoyomi │ │ │ └── MainActivity.kt │ │ ├── debug │ │ └── AndroidManifest.xml │ │ └── profile │ │ └── AndroidManifest.xml ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties └── .gitignore ├── linux_setup.sh ├── analysis_options.yaml ├── .vscode ├── extensions.json └── launch.json ├── devtools_options.yaml ├── .deepsource.toml ├── installer └── app_installer.iss ├── scripts └── export_ts_core_service.sh └── .github └── workflows └── ext_ci.yml /rust/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /hoyomi_bridge_ts/.npmignore: -------------------------------------------------------------------------------- 1 | index.ts -------------------------------------------------------------------------------- /linux/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ephemeral 2 | -------------------------------------------------------------------------------- /release.sh: -------------------------------------------------------------------------------- 1 | dart run scripts/bump.dart -------------------------------------------------------------------------------- /lib/widgets/item_service_manager.dart: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /web-extension/hoyomi-helper/e2e/basic.spec.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /rust/src/api/http/utils/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod socket_addr; 2 | -------------------------------------------------------------------------------- /rust/src/api/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod http; 2 | pub mod image; 3 | -------------------------------------------------------------------------------- /rust/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod api; 2 | mod frb_generated; 3 | -------------------------------------------------------------------------------- /serverless/README.md: -------------------------------------------------------------------------------- 1 | ``` 2 | deno task start 3 | ``` 4 | -------------------------------------------------------------------------------- /.hintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "development" 4 | ] 5 | } -------------------------------------------------------------------------------- /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /lib/core_services/interfaces/main.dart: -------------------------------------------------------------------------------- 1 | export 'export.dart'; 2 | -------------------------------------------------------------------------------- /release_hoyomi_bridge.sh: -------------------------------------------------------------------------------- 1 | cd hoyomi_bridge_ts 2 | bun release 3 | -------------------------------------------------------------------------------- /serverless/.xata/migrations/.ledger: -------------------------------------------------------------------------------- 1 | mig_cvhsldo1g95li4qgk0q0 2 | -------------------------------------------------------------------------------- /app.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tachibana-shin/hoyomi/HEAD/app.jpeg -------------------------------------------------------------------------------- /app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tachibana-shin/hoyomi/HEAD/app.png -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "none", 3 | "semi": false 4 | } 5 | -------------------------------------------------------------------------------- /ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /rust/src/api/http/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod error; 2 | pub mod http; 3 | mod utils; 4 | -------------------------------------------------------------------------------- /rust_builder/android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'rust_lib_hoyomi' 2 | -------------------------------------------------------------------------------- /lib/env.dart: -------------------------------------------------------------------------------- 1 | export 'env.io.dart' if (dart.library.js_interop) 'env.web.dart'; 2 | -------------------------------------------------------------------------------- /lib/plugins/init_services.dart: -------------------------------------------------------------------------------- 1 | export 'package:hoyomi/core_services/main.dart'; 2 | -------------------------------------------------------------------------------- /macos/Flutter/Flutter-Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "ephemeral/Flutter-Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /rust_builder/cargokit/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | .dart_tool 3 | *.iml 4 | !pubspec.lock 5 | -------------------------------------------------------------------------------- /serverless/.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | .qodo 3 | service-account-key.json 4 | node_modules 5 | -------------------------------------------------------------------------------- /web/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tachibana-shin/hoyomi/HEAD/web/favicon.png -------------------------------------------------------------------------------- /flutter_rust_bridge.yaml: -------------------------------------------------------------------------------- 1 | rust_input: crate::api 2 | rust_root: rust/ 3 | dart_output: lib/rust -------------------------------------------------------------------------------- /macos/Flutter/Flutter-Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "ephemeral/Flutter-Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /rust_builder/README.md: -------------------------------------------------------------------------------- 1 | Please ignore this folder, which is just glue to build Rust with Flutter. -------------------------------------------------------------------------------- /assets/images/blank.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tachibana-shin/hoyomi/HEAD/assets/images/blank.png -------------------------------------------------------------------------------- /lib/core_services/interfaces/headers.ts: -------------------------------------------------------------------------------- 1 | const { Headers } = globalThis 2 | 3 | export { Headers } 4 | -------------------------------------------------------------------------------- /web/_routes.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "include": ["/*"], 4 | "fallback": "/index.html" 5 | } -------------------------------------------------------------------------------- /web/icons/Icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tachibana-shin/hoyomi/HEAD/web/icons/Icon-192.png -------------------------------------------------------------------------------- /web/icons/Icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tachibana-shin/hoyomi/HEAD/web/icons/Icon-512.png -------------------------------------------------------------------------------- /rust_builder/ios/Classes/dummy_file.c: -------------------------------------------------------------------------------- 1 | // This is an empty file to force CocoaPods to create a framework. 2 | -------------------------------------------------------------------------------- /serverless/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "denoland.vscode-deno" 4 | ] 5 | } -------------------------------------------------------------------------------- /lib/core_services/interfaces/vtt.ts: -------------------------------------------------------------------------------- 1 | export interface Vtt { 2 | src: string 3 | headers?: Headers 4 | } 5 | -------------------------------------------------------------------------------- /rust_builder/macos/Classes/dummy_file.c: -------------------------------------------------------------------------------- 1 | // This is an empty file to force CocoaPods to create a framework. 2 | -------------------------------------------------------------------------------- /lib/plugins/inflate_raw.dart: -------------------------------------------------------------------------------- 1 | export 'inflate_raw.io.dart' 2 | if (dart.library.js_interop) 'inflate_raw.web.dart'; 3 | -------------------------------------------------------------------------------- /macos/Runner/Configs/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Debug.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /web/icons/Icon-maskable-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tachibana-shin/hoyomi/HEAD/web/icons/Icon-maskable-192.png -------------------------------------------------------------------------------- /web/icons/Icon-maskable-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tachibana-shin/hoyomi/HEAD/web/icons/Icon-maskable-512.png -------------------------------------------------------------------------------- /lib/core_services/interfaces/url_search_params.ts: -------------------------------------------------------------------------------- 1 | const { URLSearchParams } = globalThis 2 | 3 | export { URLSearchParams } 4 | -------------------------------------------------------------------------------- /macos/Runner/Configs/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Release.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /lib/core_services/comic/main.dart: -------------------------------------------------------------------------------- 1 | export 'ab_comic_service.dart'; 2 | export 'export.dart'; 3 | export '../interfaces/main.dart'; 4 | -------------------------------------------------------------------------------- /lib/core_services/eiga/interfaces/eiga_param.ts: -------------------------------------------------------------------------------- 1 | export interface EigaParam { 2 | eigaId: string 3 | episodeId?: string 4 | } 5 | -------------------------------------------------------------------------------- /lib/core_services/eiga/main.dart: -------------------------------------------------------------------------------- 1 | export 'ab_eiga_service.dart'; 2 | export 'export.dart'; 3 | export '../interfaces/main.dart'; 4 | -------------------------------------------------------------------------------- /macos/.gitignore: -------------------------------------------------------------------------------- 1 | # Flutter-related 2 | **/Flutter/ephemeral/ 3 | **/Pods/ 4 | 5 | # Xcode-related 6 | **/dgph 7 | **/xcuserdata/ 8 | -------------------------------------------------------------------------------- /windows/runner/resources/app_icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tachibana-shin/hoyomi/HEAD/windows/runner/resources/app_icon.ico -------------------------------------------------------------------------------- /js_runtime_polyfill/register.ts: -------------------------------------------------------------------------------- 1 | export function register(sign: Record) { 2 | Object.assign(globalThis, sign) 3 | } 4 | -------------------------------------------------------------------------------- /lib/core_services/comic/interfaces/comic_param.ts: -------------------------------------------------------------------------------- 1 | export interface ComicParam { 2 | comicId: string 3 | chapterId?: string 4 | } 5 | -------------------------------------------------------------------------------- /lib/core_services/comic/interfaces/watch_page.ts: -------------------------------------------------------------------------------- 1 | export interface WatchPage { 2 | currentPage: number 3 | totalPage: number 4 | } 5 | -------------------------------------------------------------------------------- /run_chrome.sh: -------------------------------------------------------------------------------- 1 | flutter run -d chrome --web-browser-flag "--disable-web-security" --web-browser-flag "--disable-site-isolation-trials" 2 | -------------------------------------------------------------------------------- /lib/plugins/install_web_rules.dart: -------------------------------------------------------------------------------- 1 | export 'install_web_rules.stub.dart' 2 | if (dart.library.js_interop) 'install_web_rules.web.dart'; 3 | -------------------------------------------------------------------------------- /lib/core_services/comic/interfaces/rate_value.ts: -------------------------------------------------------------------------------- 1 | export interface RateValue { 2 | best: number 3 | count: number 4 | value: number 5 | } 6 | -------------------------------------------------------------------------------- /lib/utils/cache_remember/cache_remember.dart: -------------------------------------------------------------------------------- 1 | export 'cache_remember.io.dart' 2 | if (dart.library.js_interop) 'cache_remember.web.dart'; 3 | -------------------------------------------------------------------------------- /serverless/drizzle/0003_hard_gressill.sql: -------------------------------------------------------------------------------- 1 | CREATE INDEX "eiga_history_source_id_user_id_idx" ON "eiga_histories" USING btree ("user_id","source_id"); -------------------------------------------------------------------------------- /serverless/.hintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "development" 4 | ], 5 | "hints": { 6 | "typescript-config/consistent-casing": "off" 7 | } 8 | } -------------------------------------------------------------------------------- /web-extension/hoyomi-helper/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tachibana-shin/hoyomi/HEAD/web-extension/hoyomi-helper/src/assets/logo.png -------------------------------------------------------------------------------- /format.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [[ "$1" == "--exit" ]]; then 4 | dart run scripts/format.dart --exit 5 | else 6 | dart run scripts/format.dart 7 | fi -------------------------------------------------------------------------------- /lib/core_services/eiga/interfaces/server_source.ts: -------------------------------------------------------------------------------- 1 | export interface ServerSource { 2 | name: string 3 | serverId: string 4 | extra?: string 5 | } 6 | -------------------------------------------------------------------------------- /lib/core_services/widget/export.dart: -------------------------------------------------------------------------------- 1 | // 自動生成ファイル。手動で編集しないでください。 2 | // 再生成するには scripts/auto_export.dart を実行してください。 3 | 4 | export 'avatar_service.dart'; 5 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tachibana-shin/hoyomi/HEAD/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tachibana-shin/hoyomi/HEAD/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tachibana-shin/hoyomi/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /lib/core_services/eiga/interfaces/watch_time.ts: -------------------------------------------------------------------------------- 1 | export interface WatchTime { 2 | position: number // milliseconds or seconds 3 | duration: number 4 | } 5 | -------------------------------------------------------------------------------- /lib/core_services/interfaces/setting/setting_field.ts: -------------------------------------------------------------------------------- 1 | export interface SettingField { 2 | name: string 3 | key: string 4 | description?: string 5 | } 6 | -------------------------------------------------------------------------------- /web-extension/hoyomi-helper/src/assets/logo-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tachibana-shin/hoyomi/HEAD/web-extension/hoyomi-helper/src/assets/logo-512.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tachibana-shin/hoyomi/HEAD/android/app/src/main/res/mipmap-hdpi/launcher_icon.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tachibana-shin/hoyomi/HEAD/android/app/src/main/res/mipmap-mdpi/launcher_icon.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tachibana-shin/hoyomi/HEAD/android/app/src/main/res/mipmap-xhdpi/launcher_icon.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tachibana-shin/hoyomi/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tachibana-shin/hoyomi/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/xml/filepaths.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /lib/core_services/eiga/mixin/eiga_auth_mixin.ts: -------------------------------------------------------------------------------- 1 | import { AuthMixin } from "../../mixin/auth_mixin" 2 | 3 | export abstract class EigaAuthMixin extends AuthMixin {} 4 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tachibana-shin/hoyomi/HEAD/android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tachibana-shin/hoyomi/HEAD/android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png -------------------------------------------------------------------------------- /lib/core_services/comic/mixin/comic_auth_mixin.ts: -------------------------------------------------------------------------------- 1 | import { AuthMixin } from "../../mixin/auth_mixin" 2 | 3 | export abstract class ComicAuthMixin extends AuthMixin {} 4 | -------------------------------------------------------------------------------- /web-extension/hoyomi-helper/extension/assets/logo-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tachibana-shin/hoyomi/HEAD/web-extension/hoyomi-helper/extension/assets/logo-512.png -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx4608m -XX:MaxMetaspaceSize=3G -XX:+HeapDumpOnOutOfMemoryError 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /lib/core_services/comic/interfaces/watch_page_updated.ts: -------------------------------------------------------------------------------- 1 | export interface WatchPageUpdated { 2 | currentPage: number 3 | totalPage: number 4 | updatedAt: Date 5 | } 6 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tachibana-shin/hoyomi/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /lib/core_services/interfaces/web_rule.ts: -------------------------------------------------------------------------------- 1 | export interface WebRule { 2 | regexFilter?: string 3 | shortRegexFilter?: string 4 | urlFilter?: string 5 | referer: string 6 | } 7 | -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tachibana-shin/hoyomi/HEAD/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tachibana-shin/hoyomi/HEAD/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tachibana-shin/hoyomi/HEAD/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png -------------------------------------------------------------------------------- /rust_builder/android/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .cxx 10 | -------------------------------------------------------------------------------- /serverless/drizzle/0015_safe_maximus.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE "comic" ALTER COLUMN "status" DROP DEFAULT;--> statement-breakpoint 2 | ALTER TABLE "eiga_" ALTER COLUMN "status" DROP DEFAULT; -------------------------------------------------------------------------------- /lib/core_services/eiga/interfaces/watch_time_updated.ts: -------------------------------------------------------------------------------- 1 | export interface WatchTimeUpdated { 2 | position: number 3 | duration: number 4 | updatedAt: Date // ISO date string 5 | } 6 | -------------------------------------------------------------------------------- /lib/utils/proxy_cache/proxy_cache.dart: -------------------------------------------------------------------------------- 1 | export 'proxy_cache.stub.dart' 2 | if (dart.library.js_interop) 'proxy_cache.web.dart' 3 | if (dart.library.io) 'proxy_cache.io.dart'; 4 | -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tachibana-shin/hoyomi/HEAD/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tachibana-shin/hoyomi/HEAD/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tachibana-shin/hoyomi/HEAD/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tachibana-shin/hoyomi/HEAD/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png -------------------------------------------------------------------------------- /hoyomi_bridge_ts/bump.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "bumpp" 2 | 3 | export default defineConfig({ 4 | tag: false, 5 | commit: "[`@hoyomi/bridge_ts`]: chore release v%s" 6 | }) 7 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tachibana-shin/hoyomi/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tachibana-shin/hoyomi/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tachibana-shin/hoyomi/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tachibana-shin/hoyomi/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tachibana-shin/hoyomi/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tachibana-shin/hoyomi/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tachibana-shin/hoyomi/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tachibana-shin/hoyomi/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tachibana-shin/hoyomi/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tachibana-shin/hoyomi/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tachibana-shin/hoyomi/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tachibana-shin/hoyomi/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tachibana-shin/hoyomi/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tachibana-shin/hoyomi/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tachibana-shin/hoyomi/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tachibana-shin/hoyomi/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tachibana-shin/hoyomi/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tachibana-shin/hoyomi/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tachibana-shin/hoyomi/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tachibana-shin/hoyomi/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tachibana-shin/hoyomi/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /lib/core_services/eiga/interfaces/source_video.ts: -------------------------------------------------------------------------------- 1 | export interface SourceVideo { 2 | src: string 3 | url: string 4 | type: string 5 | headers?: Headers 6 | extra?: string 7 | } 8 | -------------------------------------------------------------------------------- /linux_setup.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin bash 2 | 3 | sudo apt update -y 4 | sudo apt install ninja-build build-essential cmake pkg-config libgtk-3-dev liblzma-dev libasound2-dev libmpv-dev pkg-config -y 5 | -------------------------------------------------------------------------------- /android/app/src/main/kotlin/git/shin/hoyomi/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package git.shin.hoyomi 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() 6 | -------------------------------------------------------------------------------- /lib/l10n/app_en.arb: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Flutter App", 3 | "helloWorld": "Hello World!", 4 | "searchHint": "Search...", 5 | "clearHistory": "Clear History", 6 | "settings": "Settings" 7 | } 8 | -------------------------------------------------------------------------------- /rust_builder/android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /serverless/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "deno.enablePaths": [ 3 | "./" 4 | ], 5 | "deno.disablePaths": ["./firebase-admin", "./drizzle-kit"], 6 | "editor.inlayHints.enabled": "off" 7 | } -------------------------------------------------------------------------------- /serverless/.xata/version/compatibility.json: -------------------------------------------------------------------------------- 1 | {"@xata.io/cli":{"latest":"0.16.12","compatibility":[{"range":">=0.0.0"}]},"@xata.io/client":{"latest":"0.30.1","compatibility":[{"range":">=0.0.0"}]}} -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tachibana-shin/hoyomi/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tachibana-shin/hoyomi/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /lib/core_services/eiga/interfaces/source_content.ts: -------------------------------------------------------------------------------- 1 | export interface SourceContent { 2 | content: string 3 | url: string 4 | headers?: typeof import("../../interfaces/headers").Headers 5 | } 6 | -------------------------------------------------------------------------------- /lib/plugins/volume_controller.dart: -------------------------------------------------------------------------------- 1 | export 'volume_controller.stub.dart' 2 | if (dart.library.js_interop) 'volume_controller.web.dart' 3 | if (dart.library.io) 'volume_controller.io.dart'; 4 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:flutter_lints/flutter.yaml 2 | analyzer: 3 | exclude: 4 | - lib/**.g.dart 5 | - rust_builder/** 6 | errors: 7 | invalid_annotation_target: ignore 8 | -------------------------------------------------------------------------------- /lib/core_services/comic/mixin/main.dart: -------------------------------------------------------------------------------- 1 | export 'comic_auth_mixin.dart'; 2 | export 'comic_comment_mixin.dart'; 3 | export 'comic_watch_page_general_mixin.dart'; 4 | export 'comic_watch_page_mixin.dart'; 5 | -------------------------------------------------------------------------------- /lib/l10n/app_vi.arb: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Ứng Dụng Flutter", 3 | "helloWorld": "Xin Chào Thế Giới!", 4 | "searchHint": "Tìm kiếm...", 5 | "clearHistory": "Xóa Lịch Sử", 6 | "settings": "Cài Đặt" 7 | } 8 | -------------------------------------------------------------------------------- /lib/plugins/install_web_rules.stub.dart: -------------------------------------------------------------------------------- 1 | import 'package:hoyomi/core_services/interfaces/web_rule.dart'; 2 | 3 | Future installWebRules(List rules) { 4 | throw UnimplementedError(); 5 | } 6 | -------------------------------------------------------------------------------- /serverless/.xatarc: -------------------------------------------------------------------------------- 1 | { 2 | "databaseURL": "https://Tachibana-Shin-s-workspace-lp3l17.us-east-1.xata.sh/db/hoyomi", 3 | "codegen": { 4 | "output": "xata.ts", 5 | "moduleType": "deno" 6 | } 7 | } -------------------------------------------------------------------------------- /serverless/drizzle/0007_black_smasher.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE "comic_histories" ALTER COLUMN "season_name" SET DEFAULT '';--> statement-breakpoint 2 | ALTER TABLE "eiga_histories" ALTER COLUMN "season_name" SET DEFAULT ''; -------------------------------------------------------------------------------- /serverless/logic/use-user.ts: -------------------------------------------------------------------------------- 1 | import type { Context } from "hono" 2 | import type { MetaUser } from "./get-user.ts" 3 | 4 | export function useUser(ctx: Context): MetaUser { 5 | return ctx.env.user 6 | } 7 | -------------------------------------------------------------------------------- /web-extension/hoyomi-helper/allowlist.yaml: -------------------------------------------------------------------------------- 1 | hosts: 2 | - hoyomi.netlify.app 3 | - hoyomi.eu.org 4 | - hoyomi.pages.dev 5 | - hoyomi.nuxt.dev 6 | - hoyomi.web.dev 7 | - localhost:3032 8 | - localhost -------------------------------------------------------------------------------- /lib/core_services/exception/export.dart: -------------------------------------------------------------------------------- 1 | // 自動生成ファイル。手動で編集しないでください。 2 | // 再生成するには scripts/auto_export.dart を実行してください。 3 | 4 | export 'captcha_required_exception.dart'; 5 | export 'user_not_found_exception.dart'; 6 | -------------------------------------------------------------------------------- /lib/plugins/brightness_controller.dart: -------------------------------------------------------------------------------- 1 | export 'brightness_controller.stub.dart' 2 | if (dart.library.js_interop) 'brightness_controller.web.dart' 3 | if (dart.library.io) 'brightness_controller.io.dart'; 4 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /serverless/drizzle/0008_majestic_pretty_boy.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE "comic_histories" ALTER COLUMN "season_name" DROP DEFAULT;--> statement-breakpoint 2 | ALTER TABLE "eiga_histories" ALTER COLUMN "season_name" DROP DEFAULT; -------------------------------------------------------------------------------- /serverless/drizzle/0014_exotic_purifiers.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE "comic" ADD COLUMN "status" text DEFAULT 'unknown' NOT NULL;--> statement-breakpoint 2 | ALTER TABLE "eiga_" ADD COLUMN "status" text DEFAULT 'unknown' NOT NULL; -------------------------------------------------------------------------------- /lib/core_services/eiga/interfaces/watch_time_data.ts: -------------------------------------------------------------------------------- 1 | import { type WatchTime } from "./watch_time" 2 | 3 | export interface WatchTimeData { 4 | eigaId: string 5 | episodeId: string 6 | watchTime?: WatchTime 7 | } 8 | -------------------------------------------------------------------------------- /lib/core_services/eiga/mixin/eiga_auth_mixin.dart: -------------------------------------------------------------------------------- 1 | import 'package:hoyomi/core_services/mixin/auth_mixin.dart'; 2 | 3 | mixin EigaAuthMixin implements AuthMixin { 4 | @override 5 | final bool $noAuth = false; 6 | } 7 | -------------------------------------------------------------------------------- /rust/src/api/image/utils.rs: -------------------------------------------------------------------------------- 1 | use image::DynamicImage; 2 | 3 | pub(crate) fn load_image(image: Vec) -> DynamicImage { 4 | return image::load_from_memory(image.as_slice()).expect("Failed to load image"); 5 | } 6 | -------------------------------------------------------------------------------- /serverless/drizzle/0006_glorious_puppet_master.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE "comic_histories" ALTER COLUMN "season_name" DROP NOT NULL;--> statement-breakpoint 2 | ALTER TABLE "eiga_histories" ALTER COLUMN "season_name" DROP NOT NULL; -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /lib/core_services/comic/mixin/comic_auth_mixin.dart: -------------------------------------------------------------------------------- 1 | import 'package:hoyomi/core_services/mixin/auth_mixin.dart'; 2 | 3 | mixin ComicAuthMixin implements AuthMixin { 4 | @override 5 | final bool $noAuth = false; 6 | } 7 | -------------------------------------------------------------------------------- /lib/core_services/mixin/export.dart: -------------------------------------------------------------------------------- 1 | // 自動生成ファイル。手動で編集しないでください。 2 | // 再生成するには scripts/auto_export.dart を実行してください。 3 | 4 | export 'auth_mixin.dart'; 5 | export 'captcha_resolver_mixin.dart'; 6 | export 'settings_mixin.dart'; 7 | -------------------------------------------------------------------------------- /macos/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /rust/src/api/image/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod auto_trim_image; 2 | pub mod decode_image_rgba; 3 | pub mod get_image_size; 4 | pub mod unscramble_image; 5 | pub mod unscramble_image_columns; 6 | pub mod unscramble_image_rows; 7 | mod utils; 8 | -------------------------------------------------------------------------------- /lib/core_services/eiga/interfaces/home_eiga_category.ts: -------------------------------------------------------------------------------- 1 | import { type Eiga } from "./eiga" 2 | 3 | export interface HomeEigaCategory { 4 | name: string 5 | categoryId?: string 6 | gridView?: boolean 7 | items: Eiga[] 8 | } 9 | -------------------------------------------------------------------------------- /web-extension/hoyomi-helper/.gitpod.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM gitpod/workspace-full-vnc 2 | 3 | USER root 4 | 5 | # Install dependencies 6 | RUN apt-get update \ 7 | && apt-get install -y firefox 8 | -------------------------------------------------------------------------------- /lib/core_services/comic/interfaces/home_comic_category.ts: -------------------------------------------------------------------------------- 1 | import type { Comic } from "./comic" 2 | 3 | export interface HomeComicCategory { 4 | name: string 5 | categoryId?: string 6 | gridView?: boolean 7 | items: Comic[] 8 | } 9 | -------------------------------------------------------------------------------- /web-extension/hoyomi-helper/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "vue.volar", 4 | "antfu.iconify", 5 | "antfu.unocss", 6 | "dbaeumer.vscode-eslint", 7 | "csstools.postcss" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /lib/core_services/comic/interfaces/comic_comments.ts: -------------------------------------------------------------------------------- 1 | import type { ComicComment } from "./comic_comment" 2 | import type { Paginate } from "../../interfaces/paginate" 3 | 4 | export interface ComicComments extends Paginate {} 5 | -------------------------------------------------------------------------------- /lib/core_services/eiga/interfaces/eiga_carousel.ts: -------------------------------------------------------------------------------- 1 | import type { EigaCarouselItem } from "./eiga_carousel_item" 2 | 3 | export interface EigaCarousel { 4 | items: EigaCarouselItem[] 5 | aspectRatio: number 6 | maxHeightBuilder: number 7 | } 8 | -------------------------------------------------------------------------------- /lib/core_services/eiga/interfaces/opening_ending.ts: -------------------------------------------------------------------------------- 1 | export interface DurationRange { 2 | start: number 3 | end: number 4 | } 5 | 6 | export interface OpeningEnding { 7 | opening?: DurationRange 8 | ending?: DurationRange 9 | } 10 | -------------------------------------------------------------------------------- /lib/plugins/inflate_raw.io.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:archive/archive.dart'; 4 | 5 | Uint8List inflateRaw(Uint8List data) { 6 | final archive = ZLibDecoder().decodeBytes(data, raw: true); 7 | return archive; 8 | } 9 | -------------------------------------------------------------------------------- /hoyomi_bridge_ts/register_plugin.ts: -------------------------------------------------------------------------------- 1 | import type { BaseService } from "../lib/core_services/main" 2 | 3 | export function registerPlugin(plugin: typeof BaseService) { 4 | Object.assign(globalThis, { 5 | __$HOYOMI_PLUGIN$__: plugin 6 | }) 7 | } 8 | -------------------------------------------------------------------------------- /lib/core_services/comic/interfaces/comic_carousel.ts: -------------------------------------------------------------------------------- 1 | import type { ComicCarouselItem } from "./comic_carousel_item" 2 | 3 | export interface ComicCarousel { 4 | items: ComicCarouselItem[] 5 | aspectRatio: number 6 | maxHeightBuilder: number 7 | } 8 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "hzgood.dart-data-class-generator", 4 | "peterhdd.dartgettersetter", 5 | "luanpotter.dart-import", 6 | "ricardo-emerson.create-flutter-widgets-and-classes", 7 | "codium.codium" 8 | ] 9 | } -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-all.zip 6 | -------------------------------------------------------------------------------- /lib/core_services/interfaces/genre.ts: -------------------------------------------------------------------------------- 1 | import type { OImage } from "./o_image" 2 | 3 | export interface Genre { 4 | name: string 5 | genreId: string 6 | description?: string 7 | image?: OImage 8 | } 9 | 10 | export const GenreNoId = "$_no_id_$" 11 | -------------------------------------------------------------------------------- /linux/hoyomi.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Name=Hoyomi 3 | Comment=Hoyomi App 4 | Exec=/usr/share/hoyomi/hoyomi "%u" 5 | Icon=/usr/share/hoyomi/app.png 6 | Terminal=false 7 | Type=Application 8 | Categories=Utility; 9 | MimeType=x-scheme-handler/hoyomi; 10 | -------------------------------------------------------------------------------- /lib/core_services/interfaces/status_enum.ts: -------------------------------------------------------------------------------- 1 | export enum StatusEnum { 2 | Ongoing = "ongoing", 3 | Completed = "completed", 4 | Canceled = "canceled", 5 | Unknown = "unknown", 6 | OnHiatus = "on_hiatus", 7 | PublishingFinished = "publishing_finished" 8 | } 9 | -------------------------------------------------------------------------------- /lib/plugins/firebase.dart: -------------------------------------------------------------------------------- 1 | import 'package:firebase_core/firebase_core.dart'; 2 | import 'package:hoyomi/firebase_options.dart'; 3 | 4 | Future initializeFirebase() async { 5 | await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform); 6 | } 7 | -------------------------------------------------------------------------------- /lib/core_services/eiga/interfaces/eiga_home.ts: -------------------------------------------------------------------------------- 1 | import type { EigaCarousel } from "./eiga_carousel" 2 | import type { HomeEigaCategory } from "./home_eiga_category" 3 | 4 | export interface EigaHome { 5 | carousel?: EigaCarousel 6 | categories: HomeEigaCategory[] 7 | } 8 | -------------------------------------------------------------------------------- /lib/core_services/eiga/interfaces/subtitle.ts: -------------------------------------------------------------------------------- 1 | export type SubtitleType = "srt" | "vtt" | "ass" | string // Adjust as needed 2 | export interface Subtitle { 3 | language: string 4 | code: string 5 | type: SubtitleType 6 | url: string 7 | headers?: Headers 8 | } 9 | -------------------------------------------------------------------------------- /lib/core_services/comic/interfaces/comic_home.ts: -------------------------------------------------------------------------------- 1 | import type { ComicCarousel } from "./comic_carousel" 2 | import type { HomeComicCategory } from "./home_comic_category" 3 | 4 | export interface ComicHome { 5 | carousel?: ComicCarousel 6 | categories: HomeComicCategory[] 7 | } 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 | - signals: true 5 | - provider: true 6 | - shared_preferences: true -------------------------------------------------------------------------------- /lib/core_services/eiga/interfaces/eiga_follow.ts: -------------------------------------------------------------------------------- 1 | import type { Eiga } from "./eiga" 2 | import type { EigaEpisode } from "./eiga_episode" 3 | 4 | export interface EigaFollow { 5 | sourceId: string 6 | item: Eiga 7 | updatedAt?: string 8 | lastEpisode?: EigaEpisode 9 | } 10 | -------------------------------------------------------------------------------- /lib/core_services/eiga/interfaces/eiga_episode.ts: -------------------------------------------------------------------------------- 1 | import { type OImage } from "../../interfaces/o_image" 2 | 3 | export interface EigaEpisode { 4 | name: string 5 | episodeId: string 6 | image?: OImage 7 | description?: string 8 | extra?: string 9 | order?: number 10 | } 11 | -------------------------------------------------------------------------------- /lib/core_services/interfaces/filter.ts: -------------------------------------------------------------------------------- 1 | export interface Option { 2 | name: string 3 | value: string 4 | selected?: boolean // default: false 5 | } 6 | 7 | export interface Filter { 8 | name: string 9 | key: string 10 | multiple: boolean 11 | options: Option[] 12 | } 13 | -------------------------------------------------------------------------------- /serverless/logic/single.ts: -------------------------------------------------------------------------------- 1 | export function single(results: T[]): T | null { 2 | if (results.length === 0) { 3 | return null 4 | } 5 | if (results.length > 1) { 6 | throw new Error("Expected one result, but got multiple.") 7 | } 8 | 9 | return results[0] 10 | } 11 | -------------------------------------------------------------------------------- /lib/core_services/comic/interfaces/comic_follow.ts: -------------------------------------------------------------------------------- 1 | import type { Comic } from "./comic" 2 | import type { ComicChapter } from "./comic_chapter" 3 | 4 | export interface ComicFollow { 5 | sourceId: string 6 | item: Comic 7 | updatedAt?: Date 8 | lastChapter?: ComicChapter 9 | } 10 | -------------------------------------------------------------------------------- /lib/core_services/interfaces/status_enum.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | 3 | @JsonEnum(fieldRename: FieldRename.snake) 4 | enum StatusEnum { 5 | ongoing, 6 | completed, 7 | canceled, 8 | unknown, 9 | onHiatus, 10 | publishingFinished, 11 | } 12 | -------------------------------------------------------------------------------- /lib/plugins/volume_controller.stub.dart: -------------------------------------------------------------------------------- 1 | Future getVolume() { 2 | throw UnimplementedError(); 3 | } 4 | 5 | Future setVolume(double value) { 6 | throw UnimplementedError(); 7 | } 8 | 9 | Future updateShowSystemUI(bool state) { 10 | throw UnimplementedError(); 11 | } 12 | -------------------------------------------------------------------------------- /macos/Runner/Release.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /lib/core_services/interfaces/user.ts: -------------------------------------------------------------------------------- 1 | export enum Sex { 2 | Male = "male", 3 | Female = "female", 4 | Other = "other" 5 | } 6 | 7 | export interface User { 8 | user: string 9 | email?: string 10 | photoUrl: string 11 | fullName: string 12 | sex?: Sex // default: Sex.Other 13 | } 14 | -------------------------------------------------------------------------------- /serverless/drizzle/0004_woozy_ender_wiggin.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE "eiga_histories" ADD COLUMN "date_created" date DEFAULT now() NOT NULL;--> statement-breakpoint 2 | ALTER TABLE "eiga_histories" ADD CONSTRAINT "eiga_history__user_source_eiga_date_created__idx" UNIQUE("user_id","source_id","eiga_text_id","date_created"); -------------------------------------------------------------------------------- /lib/core_services/eiga/interfaces/eiga_episodes.ts: -------------------------------------------------------------------------------- 1 | import type { EigaEpisode } from "./eiga_episode" 2 | import type { OImage } from "../../interfaces/o_image" 3 | 4 | export interface EigaEpisodes { 5 | episodes: EigaEpisode[] 6 | image?: OImage 7 | poster?: OImage 8 | schedule?: string 9 | } 10 | -------------------------------------------------------------------------------- /lib/core_services/exception/captcha_required_exception.dart: -------------------------------------------------------------------------------- 1 | import 'package:hoyomi/core_services/service.dart'; 2 | 3 | class CaptchaRequiredException implements Exception { 4 | final String? message = "Captcha required"; 5 | final Service service; 6 | 7 | CaptchaRequiredException(this.service); 8 | } 9 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /serverless/db/enum/status_enum.ts: -------------------------------------------------------------------------------- 1 | import { pgEnum } from "drizzle-orm/pg-core"; 2 | 3 | export const StatusEnum = [ 4 | "ongoing", 5 | "completed", 6 | "cancelled", 7 | "unknown", 8 | "on_hiatus", 9 | "publishing_finished" 10 | ] as const 11 | export const pgStatus = pgEnum("text", StatusEnum) 12 | -------------------------------------------------------------------------------- /web-extension/hoyomi-helper/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .idea/ 3 | .vite-ssg-dist 4 | .vite-ssg-temp 5 | *.crx 6 | *.local 7 | *.log 8 | *.pem 9 | *.xpi 10 | *.zip 11 | dist 12 | dist-ssr 13 | extension/manifest.json 14 | node_modules 15 | src/auto-imports.d.ts 16 | src/components.d.ts 17 | .eslintcache 18 | .qodo 19 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /rust/build.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | 3 | fn main() { 4 | if cfg!(target_os = "windows") && cfg!(target_env = "msvc") { 5 | let profile = env::var("PROFILE").unwrap_or_default(); 6 | if profile == "debug" { 7 | println!("cargo:rustc-link-lib=dylib=msvcrtd"); 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /js_runtime_polyfill/README.md: -------------------------------------------------------------------------------- 1 | # js_runtime_polyfill 2 | 3 | To install dependencies: 4 | 5 | ```bash 6 | bun install 7 | ``` 8 | 9 | To run: 10 | 11 | ```bash 12 | bun run index.ts 13 | ``` 14 | 15 | This project was created using `bun init` in bun v1.2.17. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime. 16 | -------------------------------------------------------------------------------- /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/to/reference-keystore 11 | key.properties 12 | **/*.keystore 13 | **/*.jks 14 | 15 | /app/.cxx -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /js_runtime_polyfill/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "js_runtime_polyfill", 3 | "module": "index.ts", 4 | "type": "module", 5 | "scripts": { 6 | "build": "bun scripts/build_embed.ts" 7 | }, 8 | "devDependencies": { 9 | "@types/bun": "latest" 10 | }, 11 | "peerDependencies": { 12 | "typescript": "^5" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /lib/core_services/eiga/interfaces/eiga_context.ts: -------------------------------------------------------------------------------- 1 | import type { MetaEiga } from "./meta_eiga" 2 | import type { EigaEpisode } from "./eiga_episode" 3 | import type { Season } from "./meta_eiga" 4 | 5 | export interface EigaContext { 6 | eigaId: string 7 | metaEiga: MetaEiga 8 | episode: EigaEpisode 9 | season?: Season 10 | } 11 | -------------------------------------------------------------------------------- /lib/core_services/interfaces/setting/field_input.ts: -------------------------------------------------------------------------------- 1 | import { type SettingField } from "./setting_field" 2 | import { BaseService } from "../../service" 3 | 4 | export interface FieldInput extends SettingField { 5 | placeholder: string 6 | defaultFn: (service: BaseService) => string 7 | maxLines: number 8 | appear: boolean 9 | } 10 | -------------------------------------------------------------------------------- /macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /rust_builder/cargokit/build_tool/README.md: -------------------------------------------------------------------------------- 1 | /// This is copied from Cargokit (which is the official way to use it currently) 2 | /// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin 3 | 4 | A sample command-line application with an entrypoint in `bin/`, library code 5 | in `lib/`, and example unit test in `test/`. 6 | -------------------------------------------------------------------------------- /serverless/drizzle.config.ts: -------------------------------------------------------------------------------- 1 | import "dotenv/config" 2 | import { defineConfig } from "drizzle-kit" 3 | import process from "node:process" 4 | 5 | export default defineConfig({ 6 | out: "./drizzle", 7 | schema: "./db/schema.ts", 8 | dialect: "postgresql", 9 | dbCredentials: { url: process.env.DATABASE_URL! } 10 | }) 11 | -------------------------------------------------------------------------------- /lib/plugins/application_document_directory.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:path_provider/path_provider.dart'; 4 | 5 | late final Directory applicationDocumentDirectory; 6 | 7 | Future initializeApplicationDocumentDirectory() async { 8 | applicationDocumentDirectory = await getApplicationDocumentsDirectory(); 9 | } 10 | -------------------------------------------------------------------------------- /lib/plugins/inflate_raw.web.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | import 'dart:js_interop'; 3 | 4 | /// External JS function from pako 5 | @JS('_inflateRaw') 6 | external JSUint8Array _inflateRaw(JSUint8Array data); 7 | 8 | Uint8List inflateRaw(Uint8List data) { 9 | final result = _inflateRaw(data.toJS); 10 | return result.toDart; 11 | } 12 | -------------------------------------------------------------------------------- /.deepsource.toml: -------------------------------------------------------------------------------- 1 | version = 1 2 | 3 | [[analyzers]] 4 | name = "rust" 5 | 6 | [analyzers.meta] 7 | msrv = "stable" 8 | 9 | [[analyzers]] 10 | name = "swift" 11 | 12 | [[analyzers]] 13 | name = "kotlin" 14 | 15 | [analyzers.meta] 16 | runtime_version = "1.8" 17 | language_version = "1.7" 18 | 19 | [[transformers]] 20 | name = "rustfmt" -------------------------------------------------------------------------------- /lib/plugins/brightness_controller.stub.dart: -------------------------------------------------------------------------------- 1 | Future setApplicationScreenBrightness(double brightness) { 2 | throw UnimplementedError(); 3 | } 4 | 5 | Future getApplicationScreenBrightness() { 6 | throw UnimplementedError(); 7 | } 8 | 9 | Future resetApplicationScreenBrightness() { 10 | throw UnimplementedError(); 11 | } 12 | -------------------------------------------------------------------------------- /lib/utils/future_cache.dart: -------------------------------------------------------------------------------- 1 | class FutureCache { 2 | final Future future; 3 | T? data; 4 | dynamic error; 5 | 6 | FutureCache(this.future) { 7 | this.future 8 | .then((data) { 9 | this.data = data; 10 | }) 11 | .catchError((error) { 12 | this.error = error; 13 | }); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /lib/core_services/comic/interfaces/comic_modes.dart: -------------------------------------------------------------------------------- 1 | enum ComicModes { leftToRight, rightToLeft, topToBottom, webToon } 2 | 3 | bool modeUsingPageView(ComicModes mode) { 4 | if (mode == ComicModes.leftToRight || 5 | mode == ComicModes.rightToLeft || 6 | mode == ComicModes.topToBottom) { 7 | return true; 8 | } 9 | 10 | return false; 11 | } 12 | -------------------------------------------------------------------------------- /lib/core_services/eiga/interfaces/eiga_context_with_episodes.ts: -------------------------------------------------------------------------------- 1 | import type { MetaEiga } from "./meta_eiga" 2 | import type { Season } from "./meta_eiga" 3 | import type { EigaEpisode } from "./eiga_episode" 4 | 5 | export interface EigaContextWithEpisodes { 6 | eigaId: string 7 | metaEiga: MetaEiga 8 | season?: Season 9 | episodes: EigaEpisode[] 10 | } 11 | -------------------------------------------------------------------------------- /lib/core_services/eiga/interfaces/eiga_history.ts: -------------------------------------------------------------------------------- 1 | import type { Eiga } from "./eiga" 2 | import type { EigaEpisode } from "./eiga_episode" 3 | import type { WatchTime } from "./watch_time" 4 | 5 | export interface EigaHistory { 6 | sourceId: string 7 | item: Eiga 8 | watchUpdatedAt: string 9 | lastEpisode: EigaEpisode 10 | watchTime: WatchTime 11 | } 12 | -------------------------------------------------------------------------------- /lib/core_services/service.ts: -------------------------------------------------------------------------------- 1 | import type { ServiceInit } from "./service_init" 2 | 3 | export abstract class BaseService { 4 | abstract init: ServiceInit 5 | // abstract get uid(): string 6 | // abstract get name(): string 7 | _baseUrl: string = "" 8 | writeWith: string = "js" 9 | get baseUrl(): string { 10 | return this._baseUrl 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /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/core_services/interfaces/paginate.ts: -------------------------------------------------------------------------------- 1 | export interface Paginate { 2 | items: T[] 3 | page: number 4 | totalItems: number 5 | totalPages: number 6 | } 7 | 8 | export function createFakePaginate(item: T): Paginate { 9 | return { 10 | items: Array(30).fill(item), 11 | page: 1, 12 | totalItems: 30, 13 | totalPages: 1 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /rust_builder/cargokit/build_tool/bin/build_tool.dart: -------------------------------------------------------------------------------- 1 | /// This is copied from Cargokit (which is the official way to use it currently) 2 | /// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin 3 | 4 | import 'package:build_tool/build_tool.dart' as build_tool; 5 | 6 | void main(List arguments) { 7 | build_tool.runMain(arguments); 8 | } 9 | -------------------------------------------------------------------------------- /rust_builder/cargokit/build_tool/lib/build_tool.dart: -------------------------------------------------------------------------------- 1 | /// This is copied from Cargokit (which is the official way to use it currently) 2 | /// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin 3 | 4 | import 'src/build_tool.dart' as build_tool; 5 | 6 | Future runMain(List args) async { 7 | return build_tool.runMain(args); 8 | } 9 | -------------------------------------------------------------------------------- /serverless/drizzle/0000_goofy_jubilee.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE "users" ( 2 | "id" integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY (sequence name "users_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 CACHE 1), 3 | "user_id" varchar(28) NOT NULL, 4 | "created_at" timestamp DEFAULT NOW() NOT NULL, 5 | CONSTRAINT "users_user_id_unique" UNIQUE("user_id") 6 | ); 7 | -------------------------------------------------------------------------------- /lib/core_services/comic/interfaces/comic_category.ts: -------------------------------------------------------------------------------- 1 | import type { Comic } from "./comic" 2 | import type { Paginate } from "../../interfaces/paginate" 3 | import type { Filter } from "../../interfaces/filter" 4 | 5 | export interface ComicCategory extends Paginate { 6 | name: string 7 | url: string 8 | description?: string 9 | filters?: Filter[] 10 | } 11 | -------------------------------------------------------------------------------- /lib/core_services/comic/interfaces/comic_history.ts: -------------------------------------------------------------------------------- 1 | import type { Comic } from "./comic" 2 | import type { ComicChapter } from "./comic_chapter" 3 | import type { WatchPage } from "./watch_page" 4 | 5 | export interface ComicHistory { 6 | sourceId: string 7 | item: Comic 8 | watchUpdatedAt: Date 9 | lastChapter: ComicChapter 10 | watchPage: WatchPage 11 | } 12 | -------------------------------------------------------------------------------- /lib/core_services/eiga/interfaces/eiga_category.ts: -------------------------------------------------------------------------------- 1 | import type { Filter } from "../../interfaces/filter" 2 | import type { Eiga } from "./eiga" 3 | 4 | export interface EigaCategory { 5 | name: string 6 | url: string 7 | description?: string 8 | items: Eiga[] 9 | page: number 10 | totalItems: number 11 | totalPages: number 12 | filters?: Filter[] 13 | } 14 | -------------------------------------------------------------------------------- /lib/plugins/event_bus.dart: -------------------------------------------------------------------------------- 1 | import 'package:event_bus/event_bus.dart'; 2 | 3 | EventBus eventBus = EventBus(); 4 | 5 | class UpdatedHistory { 6 | final String comicId; 7 | final String chapterId; 8 | final bool force; 9 | 10 | const UpdatedHistory({ 11 | required this.comicId, 12 | required this.chapterId, 13 | this.force = false, 14 | }); 15 | } 16 | -------------------------------------------------------------------------------- /macos/RunnerTests/RunnerTests.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 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 | -------------------------------------------------------------------------------- /web-extension/hoyomi-helper/scripts/manifest.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs-extra' 2 | import { getManifest } from '../src/manifest' 3 | import { log, r } from './utils' 4 | 5 | export async function writeManifest() { 6 | await fs.writeJSON(r('extension/manifest.json'), await getManifest(), { spaces: 2 }) 7 | log('PRE', 'write manifest.json') 8 | } 9 | 10 | writeManifest() 11 | -------------------------------------------------------------------------------- /web-extension/hoyomi-helper/unocss.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "unocss/vite" 2 | import { 3 | presetAttributify, 4 | presetIcons, 5 | presetUno, 6 | transformerDirectives 7 | } from "unocss" 8 | 9 | export default defineConfig({ 10 | presets: [presetUno(), presetAttributify(), presetIcons()], 11 | transformers: [transformerDirectives()] 12 | }) 13 | -------------------------------------------------------------------------------- /lib/widgets/comic/icon_button_share.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class IconButtonShare extends StatelessWidget { 4 | const IconButtonShare({super.key}); 5 | 6 | @override 7 | Widget build(BuildContext context) { 8 | return IconButton( 9 | icon: const Icon(Icons.share), // 共有ボタン 10 | onPressed: () {}, 11 | ); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /web-extension/hoyomi-helper/src/global.d.ts: -------------------------------------------------------------------------------- 1 | declare const __DEV__: boolean 2 | /** Extension name, defined in packageJson.name */ 3 | declare const __NAME__: string 4 | 5 | declare module "*.vue" { 6 | const component: any 7 | export default component 8 | } 9 | 10 | // globals.d.ts 11 | 12 | interface Headers { 13 | entries(): IterableIterator<[string, string]> 14 | } 15 | -------------------------------------------------------------------------------- /lib/core_services/base_service.dart: -------------------------------------------------------------------------------- 1 | import 'package:hoyomi/constraints/x_platform.dart'; 2 | 3 | import 'service_init.dart'; 4 | 5 | abstract class BaseService { 6 | final kIsWeb = XPlatform.isWeb; 7 | 8 | ServiceInit get init; 9 | String get uid => init.uid ?? name.toLowerCase().replaceAll(r'\s', '-'); 10 | String get name => init.name; 11 | String get baseUrl; 12 | } 13 | -------------------------------------------------------------------------------- /rust_builder/windows/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ 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 | -------------------------------------------------------------------------------- /web-extension/hoyomi-helper/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cSpell.words": ["Vitesse"], 3 | "typescript.tsdk": "node_modules/typescript/lib", 4 | "vite.autoStart": false, 5 | "eslint.experimental.useFlatConfig": true, 6 | "editor.codeActionsOnSave": { 7 | "source.fixAll.eslint": "explicit" 8 | }, 9 | "files.associations": { 10 | "*.css": "postcss" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /lib/utils/format_number.dart: -------------------------------------------------------------------------------- 1 | String formatNumber(int number) { 2 | if (number >= 1e9) { 3 | return '${(number / 1e9).toStringAsFixed(1)}B'; 4 | } else if (number >= 1e6) { 5 | return '${(number / 1e6).toStringAsFixed(1)}M'; 6 | } else if (number >= 1e3) { 7 | return '${(number / 1e3).toStringAsFixed(1)}K'; 8 | } else { 9 | return number.toString(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /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. -------------------------------------------------------------------------------- /lib/core_services/mixin/auth_mixin.dart: -------------------------------------------------------------------------------- 1 | import '../interfaces/user.dart'; 2 | import '../service.dart'; 3 | 4 | mixin AuthMixin on Service { 5 | static bool support(Object $mixin) { 6 | return $mixin is AuthMixin && $mixin.$noAuth == false; 7 | } 8 | 9 | bool get $noAuth => false; 10 | final bool? $isAuth = true; 11 | 12 | Future getUser({required String cookie}); 13 | } 14 | -------------------------------------------------------------------------------- /lib/core_services/interfaces/vtt.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | import 'main.dart'; 4 | 5 | part 'vtt.freezed.dart'; 6 | part 'vtt.g.dart'; 7 | 8 | @freezed 9 | sealed class Vtt with _$Vtt { 10 | const factory Vtt({required String src, Headers? headers}) = _Vtt; 11 | 12 | factory Vtt.fromJson(Map json) => _$VttFromJson(json); 13 | } 14 | -------------------------------------------------------------------------------- /linux/flutter/generated_plugin_registrant.h: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #ifndef GENERATED_PLUGIN_REGISTRANT_ 8 | #define GENERATED_PLUGIN_REGISTRANT_ 9 | 10 | #include 11 | 12 | // Registers Flutter plugins. 13 | void fl_register_plugins(FlPluginRegistry* registry); 14 | 15 | #endif // GENERATED_PLUGIN_REGISTRANT_ 16 | -------------------------------------------------------------------------------- /windows/flutter/generated_plugin_registrant.h: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #ifndef GENERATED_PLUGIN_REGISTRANT_ 8 | #define GENERATED_PLUGIN_REGISTRANT_ 9 | 10 | #include 11 | 12 | // Registers Flutter plugins. 13 | void RegisterPlugins(flutter::PluginRegistry* registry); 14 | 15 | #endif // GENERATED_PLUGIN_REGISTRANT_ 16 | -------------------------------------------------------------------------------- /lib/utils/debouncer.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | class Debouncer { 4 | final Duration duration; 5 | final void Function(T data) action; 6 | Timer? _timer; 7 | 8 | Debouncer(this.duration, this.action); 9 | 10 | void run(T data) { 11 | _timer?.cancel(); 12 | _timer = Timer(duration, () => action(data)); 13 | } 14 | 15 | void dispose() { 16 | _timer?.cancel(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /lib/plugins/video_polyfill.dart: -------------------------------------------------------------------------------- 1 | import 'package:video_player_media_kit/video_player_media_kit.dart'; 2 | 3 | Future initializeVideoPolyfill() async { 4 | // if (XPlatform.isWindows || XPlatform.isLinux) { 5 | VideoPlayerMediaKit.ensureInitialized( 6 | android: true, 7 | iOS: true, 8 | macOS: true, 9 | windows: true, 10 | linux: true, 11 | web: true, 12 | ); 13 | // } 14 | } 15 | -------------------------------------------------------------------------------- /serverless/schema/authorization.ts: -------------------------------------------------------------------------------- 1 | import { z } from "@hono/zod-openapi" 2 | 3 | export const AuthorizationSchema = z.object({ 4 | Authorization: z 5 | .string() 6 | .min(1) 7 | .regex(/^Bearer\s([A-Za-z0-9\-_.]+)$/) 8 | .openapi({ 9 | description: 10 | "The Authorization header must be in the format 'Bearer '", 11 | example: "Bearer your_token_here" 12 | }) 13 | }) 14 | -------------------------------------------------------------------------------- /lib/core_services/exception/user_not_found_exception.dart: -------------------------------------------------------------------------------- 1 | class UserNotFoundException implements Exception { 2 | final String message; 3 | final StackTrace trace; 4 | 5 | UserNotFoundException([this.message = 'User not found', StackTrace? trace]) 6 | : trace = trace ?? StackTrace.current; 7 | 8 | @override 9 | String toString() { 10 | return "UserNotFoundException: $message\n$trace"; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /serverless/db/schema/users.ts: -------------------------------------------------------------------------------- 1 | import { integer, pgTable, varchar, timestamp } from "drizzle-orm/pg-core" 2 | import { sql } from "drizzle-orm"; 3 | 4 | export const users = pgTable("users", { 5 | id: integer().primaryKey().generatedAlwaysAsIdentity(), 6 | // user_id from firebase_auth 7 | user_id: varchar({ length: 28 }).notNull().unique(), 8 | created_at: timestamp().notNull().default(sql`NOW()`), 9 | }) 10 | -------------------------------------------------------------------------------- /serverless/firebase-admin.ts: -------------------------------------------------------------------------------- 1 | import admin from "firebase-admin-release" 2 | import type typeAdmin from "firebase-admin" 3 | 4 | const serviceAccountKey = await Deno.readTextFile("./service-account-key.json") 5 | 6 | export const app = (admin as unknown as typeof typeAdmin).initializeApp({ 7 | credential: (admin as unknown as typeof typeAdmin).credential.cert( 8 | JSON.parse(serviceAccountKey) 9 | ) 10 | }) 11 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /macos/Runner/DebugProfile.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.cs.allow-jit 8 | 9 | com.apple.security.network.server 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /lib/core_services/comic/interfaces/comic_modes.ts: -------------------------------------------------------------------------------- 1 | export enum ComicModes { 2 | leftToRight = "leftToRight", 3 | rightToLeft = "rightToLeft", 4 | topToBottom = "topToBottom", 5 | webToon = "webToon" 6 | } 7 | 8 | export function modeUsingPageView(mode: ComicModes): boolean { 9 | return ( 10 | mode === ComicModes.leftToRight || 11 | mode === ComicModes.rightToLeft || 12 | mode === ComicModes.topToBottom 13 | ) 14 | } 15 | -------------------------------------------------------------------------------- /hoyomi_bridge_ts/README.md: -------------------------------------------------------------------------------- 1 | # @hoyomi/bridge-ts 2 | 3 | This a package generate plugin for Hoyomi application. Check documents at: https://github.com/tachibana-shin/hoyomi 4 | 5 | To install dependencies: 6 | 7 | ```bash 8 | bun install 9 | ``` 10 | 11 | To run: 12 | 13 | ```bash 14 | bun run index.ts 15 | ``` 16 | 17 | This project was created using `bun init` in bun v1.2.7. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime. 18 | -------------------------------------------------------------------------------- /lib/plugins/volume_controller.io.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_volume_controller/flutter_volume_controller.dart'; 2 | 3 | @override 4 | Future getVolume() => FlutterVolumeController.getVolume(); 5 | 6 | @override 7 | Future setVolume(double value) => 8 | FlutterVolumeController.setVolume(value); 9 | 10 | @override 11 | Future updateShowSystemUI(bool state) => 12 | FlutterVolumeController.updateShowSystemUI(state); 13 | -------------------------------------------------------------------------------- /lib/core_services/comic/interfaces/comic_chapter.ts: -------------------------------------------------------------------------------- 1 | export interface ComicChapter { 2 | name: string 3 | fullName?: string 4 | chapterId: string 5 | time?: Date 6 | extra?: string 7 | order?: number 8 | } 9 | 10 | // Utility function for sorting chapters ascending by order 11 | export function sortChaptersAsc(chapters: ComicChapter[]): ComicChapter[] { 12 | return [...chapters].sort((a, b) => (a.order ?? 0) - (b.order ?? 0)) 13 | } 14 | -------------------------------------------------------------------------------- /lib/plugins/install_web_rules.web.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: avoid_web_libraries_in_flutter 2 | 3 | import 'dart:convert'; 4 | 5 | import 'package:hoyomi/core_services/interfaces/web_rule.dart'; 6 | 7 | import 'dart:js_interop'; 8 | 9 | @JS('_installWebRules') 10 | external JSPromise _installWebRules(String rules); 11 | 12 | Future installWebRules(List rules) async { 13 | await _installWebRules(jsonEncode(rules)).toDart; 14 | } 15 | -------------------------------------------------------------------------------- /web-extension/hoyomi-helper/src/env.ts: -------------------------------------------------------------------------------- 1 | const forbiddenProtocols = [ 2 | 'chrome-extension://', 3 | 'chrome-search://', 4 | 'chrome://', 5 | 'devtools://', 6 | 'edge://', 7 | 'https://chrome.google.com/webstore', 8 | ] 9 | 10 | export function isForbiddenUrl(url: string): boolean { 11 | return forbiddenProtocols.some(protocol => url.startsWith(protocol)) 12 | } 13 | 14 | export const isFirefox = navigator.userAgent.includes('Firefox') 15 | -------------------------------------------------------------------------------- /lib/core_services/eiga/interfaces/eiga_param.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | part 'eiga_param.freezed.dart'; 4 | part 'eiga_param.g.dart'; 5 | 6 | @freezed 7 | sealed class EigaParam with _$EigaParam { 8 | const factory EigaParam({required String eigaId, String? episodeId}) = 9 | _EigaParam; 10 | 11 | factory EigaParam.fromJson(Map json) => 12 | _$EigaParamFromJson(json); 13 | } 14 | -------------------------------------------------------------------------------- /lib/core_services/interfaces/o_image.ts: -------------------------------------------------------------------------------- 1 | import type { Headers } from "./headers" 2 | 3 | export interface OImage { 4 | src: string 5 | headers?: Headers 6 | extra?: string 7 | } 8 | 9 | export function createOImage( 10 | src: string, 11 | headers?: Headers, 12 | extra?: string 13 | ): OImage { 14 | return { src, headers, extra } 15 | } 16 | 17 | export const OImageFake = "fake:" 18 | export const OImageBlank = "assets/images/blank.png" 19 | -------------------------------------------------------------------------------- /lib/utils/throttler.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:ui'; 3 | 4 | class Throttler { 5 | final int milliseconds; 6 | bool _isReady = true; 7 | 8 | Throttler({required this.milliseconds}); 9 | 10 | void run(VoidCallback action) { 11 | if (_isReady) { 12 | _isReady = false; 13 | action(); 14 | Timer(Duration(milliseconds: milliseconds), () { 15 | _isReady = true; 16 | }); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /serverless/drizzle/0002_living_madripoor.sql: -------------------------------------------------------------------------------- 1 | DROP INDEX "eiga_history_user_id_eiga_text_id_idx";--> statement-breakpoint 2 | ALTER TABLE "eiga_histories" ADD COLUMN "source_id" text NOT NULL;--> statement-breakpoint 3 | CREATE INDEX "eiga_history_source_id_idx" ON "eiga_histories" USING btree ("source_id");--> statement-breakpoint 4 | CREATE INDEX "eiga_history_user_id_source_id_eiga_text_id_idx" ON "eiga_histories" USING btree ("user_id","source_id","eiga_text_id"); -------------------------------------------------------------------------------- /lib/core_services/comic/interfaces/comic.ts: -------------------------------------------------------------------------------- 1 | import type { OImage } from "../../interfaces/o_image" 2 | import type { ComicChapter } from "./comic_chapter" 3 | 4 | export interface Comic { 5 | name: string 6 | comicId: string 7 | originalName?: string 8 | image: OImage 9 | lastChap?: ComicChapter 10 | lastUpdate?: Date 11 | notice?: string 12 | pending?: boolean 13 | preRelease?: Date 14 | rate?: number 15 | description?: string 16 | } 17 | -------------------------------------------------------------------------------- /lib/core_services/comic/interfaces/watch_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | part 'watch_page.freezed.dart'; 4 | part 'watch_page.g.dart'; 5 | 6 | @freezed 7 | sealed class WatchPage with _$WatchPage { 8 | factory WatchPage({required int currentPage, required int totalPage}) = 9 | _WatchPage; 10 | 11 | factory WatchPage.fromJson(Map json) => 12 | _$WatchPageFromJson(json); 13 | } 14 | -------------------------------------------------------------------------------- /lib/core_services/mixin/auth_mixin.ts: -------------------------------------------------------------------------------- 1 | import type { User } from "../interfaces/user" 2 | import { BaseService } from "../service" 3 | 4 | export abstract class AuthMixin { 5 | static support($mixin: any): boolean { 6 | return $mixin instanceof AuthMixin && $mixin.$noAuth === false 7 | } 8 | 9 | readonly $noAuth: boolean = false 10 | readonly $isAuth: boolean | null = true 11 | abstract getUser(params: { cookie: string }): Promise 12 | } 13 | -------------------------------------------------------------------------------- /lib/constraints/x_platform.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:flutter/foundation.dart'; 4 | 5 | class XPlatform { 6 | static final isAndroid = !kIsWeb && Platform.isAndroid; 7 | static final isIOS = !kIsWeb && Platform.isIOS; 8 | static final isWindows = !kIsWeb && Platform.isWindows; 9 | static final isLinux = !kIsWeb && Platform.isLinux; 10 | static final isMacOS = !kIsWeb && Platform.isMacOS; 11 | static final isWeb = kIsWeb; 12 | } 13 | -------------------------------------------------------------------------------- /lib/core_services/comic/interfaces/comic_comment.ts: -------------------------------------------------------------------------------- 1 | import { type OImage } from "../../interfaces/o_image" 2 | 3 | export interface ComicComment { 4 | id: string 5 | chapterId?: string 6 | userId: string 7 | name: string 8 | photoUrl?: OImage | null 9 | content: string 10 | timeAgo: Date 11 | countLike?: number 12 | countDislike?: number 13 | countReply: number 14 | canDelete?: boolean 15 | like?: boolean 16 | replies?: ComicComment[] 17 | } 18 | -------------------------------------------------------------------------------- /lib/core_services/comic/interfaces/comic_param.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | part 'comic_param.freezed.dart'; 4 | part 'comic_param.g.dart'; 5 | 6 | @freezed 7 | sealed class ComicParam with _$ComicParam { 8 | const factory ComicParam({required String comicId, String? chapterId}) = 9 | _ComicParam; 10 | 11 | factory ComicParam.fromJson(Map json) => 12 | _$ComicParamFromJson(json); 13 | } 14 | -------------------------------------------------------------------------------- /lib/extensions/list_extension.dart: -------------------------------------------------------------------------------- 1 | import 'iterable_extension.dart'; 2 | 3 | extension JoinWithListExtension on List> { 4 | List joinWith(T separator) { 5 | return asMap().entries.foldWithIterable>([], (arr, entry, list) { 6 | int index = entry.key; 7 | final item = entry.value; 8 | 9 | arr.addAll(item); 10 | 11 | if (index < list.length - 1) arr.add(separator); 12 | 13 | return arr; 14 | }); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /lib/mixins/auto_dispose_notifier.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | mixin AutoDisposeNotifier on State { 4 | final List _notifiers = []; 5 | 6 | void registerNotifier(ValueNotifier notifier) { 7 | _notifiers.add(notifier); 8 | } 9 | 10 | @override 11 | void dispose() { 12 | for (var notifier in _notifiers) { 13 | notifier.dispose(); 14 | } 15 | super.dispose(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /lib/pages/webview_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:hoyomi/screens/webview/custom_webview.dart'; 3 | 4 | class WebviewPage extends StatelessWidget { 5 | final String sourceId; 6 | final bool logout; 7 | 8 | const WebviewPage({super.key, required this.sourceId, required this.logout}); 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | return CustomWebView(sourceId: sourceId, logout: logout); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /hoyomi_bridge_ts/index.ts: -------------------------------------------------------------------------------- 1 | import { type $UnimplementedError } from "../js_runtime_polyfill/polyfill/unimplemented_error" 2 | 3 | export * from "../lib/core_services/main" 4 | export * from "./register_plugin" 5 | 6 | declare global { 7 | function base64Encode(data: Uint8Array): string 8 | function base64Decode(base64: string): Uint8Array 9 | const UnimplementedError: $UnimplementedError 10 | } 11 | 12 | export function defineType(data: T) { 13 | return data 14 | } 15 | -------------------------------------------------------------------------------- /serverless/db/schema.ts: -------------------------------------------------------------------------------- 1 | export * from "./schema/comic_follows.ts" 2 | export * from "./schema/comic_histories.ts" 3 | export * from "./schema/comic_history_chapters.ts" 4 | export * from "./schema/comic_newest.ts" 5 | export * from "./schema/comic.ts" 6 | export * from "./schema/eiga_follows.ts" 7 | export * from "./schema/eiga_histories.ts" 8 | export * from "./schema/eiga_history_chapters.ts" 9 | export * from "./schema/eiga.ts" 10 | export * from "./schema/users.ts" 11 | -------------------------------------------------------------------------------- /web-extension/hoyomi-helper/shim.d.ts: -------------------------------------------------------------------------------- 1 | import type { ProtocolWithReturn } from "webext-bridge" 2 | 3 | declare module "webext-bridge" { 4 | export interface ProtocolMap { 5 | // define message protocol types 6 | // see https://github.com/antfu/webext-bridge#type-safe-protocols 7 | "tab-prev": { title: string | undefined } 8 | install_web_rules: ProtocolWithReturn< 9 | readonly { json: string; origin: string }, 10 | void 11 | > 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /installer/app_installer.iss: -------------------------------------------------------------------------------- 1 | ; Inno Setup Script Hoyomi 2 | [Setup] 3 | AppName=Hoyomi 4 | AppVersion=0.3.89.127 5 | DefaultDirName={pf}\Hoyomi 6 | DefaultGroupName=Hoyomi 7 | OutputDir=installer\output 8 | OutputBaseFilename=app-windows-x64-release 9 | Compression=lzma 10 | SolidCompression=yes 11 | 12 | [Files] 13 | Source: "..\build\windows\x64\runner\Release\*"; DestDir: "{app}"; Flags: recursesubdirs 14 | 15 | [Icons] 16 | Name: "{group}\Hoyomi"; Filename: "{app}\hoyomi.exe" 17 | -------------------------------------------------------------------------------- /lib/core_services/eiga/interfaces/watch_time.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | part 'watch_time.freezed.dart'; 4 | part 'watch_time.g.dart'; 5 | 6 | @freezed 7 | sealed class WatchTime with _$WatchTime { 8 | const factory WatchTime({ 9 | required Duration position, 10 | required Duration duration, 11 | }) = _WatchTime; 12 | 13 | factory WatchTime.fromJson(Map json) => 14 | _$WatchTimeFromJson(json); 15 | } 16 | -------------------------------------------------------------------------------- /linux/my_application.h: -------------------------------------------------------------------------------- 1 | #ifndef FLUTTER_MY_APPLICATION_H_ 2 | #define FLUTTER_MY_APPLICATION_H_ 3 | 4 | #include 5 | 6 | G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, 7 | GtkApplication) 8 | 9 | /** 10 | * my_application_new: 11 | * 12 | * Creates a new Flutter-based application. 13 | * 14 | * Returns: a new #MyApplication. 15 | */ 16 | MyApplication* my_application_new(); 17 | 18 | #endif // FLUTTER_MY_APPLICATION_H_ 19 | -------------------------------------------------------------------------------- /macos/Runner/MainFlutterWindow.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | 4 | class MainFlutterWindow: NSWindow { 5 | override func awakeFromNib() { 6 | let flutterViewController = FlutterViewController() 7 | let windowFrame = self.frame 8 | self.contentViewController = flutterViewController 9 | self.setFrame(windowFrame, display: true) 10 | 11 | RegisterGeneratedPlugins(registry: flutterViewController) 12 | 13 | super.awakeFromNib() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /serverless/.xata/migrations/mig_cvhsldo1g95li4qgk0q0.json: -------------------------------------------------------------------------------- 1 | { 2 | "done": true, 3 | "migration": { 4 | "name": "mig_cvhsldo1g95li4qgk0q0", 5 | "operations": [ 6 | { 7 | "sql": { 8 | "up": "CREATE SCHEMA \"bb_00000000000000000000000000_000000\";" 9 | } 10 | } 11 | ] 12 | }, 13 | "migrationType": "pgroll", 14 | "name": "mig_cvhsldo1g95li4qgk0q0", 15 | "schema": "public", 16 | "startedAt": "2025-03-26T09:36:55.426384Z" 17 | } 18 | -------------------------------------------------------------------------------- /js_runtime_polyfill/polyfill/unimplemented_error.ts: -------------------------------------------------------------------------------- 1 | import { register } from "../register" 2 | 3 | declare global { 4 | interface GlobalThis { 5 | UnimplementedError: $UnimplementedError 6 | } 7 | } 8 | 9 | export type $UnimplementedError = typeof UnimplementedError 10 | export class UnimplementedError extends Error { 11 | constructor(message?: string) { 12 | super(message) 13 | this.name = "UnimplementedError" 14 | } 15 | } 16 | 17 | register({ UnimplementedError }) 18 | -------------------------------------------------------------------------------- /lib/plugins/android_sdk_int.dart: -------------------------------------------------------------------------------- 1 | import 'package:device_info_plus/device_info_plus.dart'; 2 | import 'package:hoyomi/constraints/x_platform.dart'; 3 | 4 | late final int? androidSdkInt; 5 | 6 | Future androidSdkIntInit() async { 7 | if (XPlatform.isAndroid) { 8 | final deviceInfo = DeviceInfoPlugin(); 9 | final androidInfo = await deviceInfo.androidInfo; 10 | 11 | androidSdkInt = androidInfo.version.sdkInt; 12 | } else { 13 | androidSdkInt = null; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /lib/core_services/comic/mixin/comic_follow_mixin.dart: -------------------------------------------------------------------------------- 1 | import 'package:hoyomi/core_services/comic/main.dart'; 2 | import 'package:hoyomi/core_services/mixin/auth_mixin.dart'; 3 | 4 | mixin ComicFollowMixin on AuthMixin { 5 | Future> getFollows({required int page}); 6 | 7 | Future isFollow({required String comicId}); 8 | Future setFollow({ 9 | required String comicId, 10 | required MetaComic metaComic, 11 | required bool value, 12 | }); 13 | } 14 | -------------------------------------------------------------------------------- /scripts/export_ts_core_service.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | TARGET_DIR="lib/core_services" 4 | OUTPUT_FILE="$TARGET_DIR/main.ts" 5 | 6 | rm -f "$OUTPUT_FILE" 7 | 8 | find "$TARGET_DIR" -type f -name "*.ts" ! -name "main.ts" | while read filepath; do 9 | relpath="${filepath#$TARGET_DIR/}" 10 | relpath="${relpath%.ts}" 11 | 12 | relpath="${relpath//'\'/"/"}" 13 | 14 | echo "export * from \"./$relpath\"" >> "$OUTPUT_FILE" 15 | done 16 | 17 | echo "OK: Exported core services to $OUTPUT_FILE" 18 | -------------------------------------------------------------------------------- /serverless/drizzle/0012_crazy_madame_hydra.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE "comic_follows" ADD COLUMN "current_chapter_name" text NOT NULL;--> statement-breakpoint 2 | ALTER TABLE "comic_follows" ADD COLUMN "current_chapter_id" text NOT NULL;--> statement-breakpoint 3 | ALTER TABLE "comic_follows" ADD COLUMN "current_chapter_time" timestamp;--> statement-breakpoint 4 | ALTER TABLE "comic" ADD COLUMN "original_name" text DEFAULT '' NOT NULL;--> statement-breakpoint 5 | ALTER TABLE "comic_follows" DROP COLUMN "current_chapters"; -------------------------------------------------------------------------------- /lib/core_services/eiga/interfaces/eiga.ts: -------------------------------------------------------------------------------- 1 | import type { EigaEpisode } from "./eiga_episode" 2 | import type { OImage } from "../../interfaces/o_image" 3 | 4 | export interface Eiga { 5 | name: string 6 | eigaId: string 7 | originalName?: string 8 | image: OImage 9 | lastEpisode?: EigaEpisode 10 | lastUpdate?: string 11 | notice?: string 12 | countSub?: number 13 | countDub?: number 14 | rate?: number 15 | pending?: boolean 16 | preRelease?: string 17 | description?: string 18 | } 19 | -------------------------------------------------------------------------------- /lib/core_services/interfaces/export.dart: -------------------------------------------------------------------------------- 1 | // 自動生成ファイル。手動で編集しないでください。 2 | // 再生成するには scripts/auto_export.dart を実行してください。 3 | 4 | export 'filter.dart'; 5 | export 'genre.dart'; 6 | export 'headers.dart'; 7 | export 'main.dart'; 8 | export 'o_image.dart'; 9 | export 'paginate.dart'; 10 | export 'setting/field_input.dart'; 11 | export 'setting/setting_field.dart'; 12 | export 'status_enum.dart'; 13 | export 'url_search_params.dart'; 14 | export 'user.dart'; 15 | export 'vtt.dart'; 16 | export 'web_rule.dart'; 17 | -------------------------------------------------------------------------------- /lib/rust_isolate/auto_trim_image_isolate.dart: -------------------------------------------------------------------------------- 1 | import 'dart:isolate'; 2 | import 'dart:typed_data'; 3 | 4 | import 'package:hoyomi/rust/api/image/auto_trim_image.dart'; 5 | import 'package:hoyomi/rust/frb_generated.dart'; 6 | 7 | Future autoTrimImageIsolate({required List image}) { 8 | return Isolate.run(() async { 9 | await RustLib.init(); 10 | 11 | final output = autoTrimImageSync(image: image); 12 | 13 | RustLib.dispose(); 14 | 15 | return output; 16 | }); 17 | } 18 | -------------------------------------------------------------------------------- /web-extension/hoyomi-helper/playwright.config.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @see {@link https://playwright.dev/docs/chrome-extensions Chrome extensions | Playwright} 3 | */ 4 | import { defineConfig } from "@playwright/test" 5 | 6 | export default defineConfig({ 7 | testDir: "./e2e", 8 | retries: 2, 9 | webServer: { 10 | command: "npm run dev", 11 | // start e2e test after the Vite server is fully prepared 12 | url: "http://localhost:3303/popup/main.ts", 13 | reuseExistingServer: true 14 | } 15 | }) 16 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /lib/core_services/eiga/interfaces/server_source.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | part 'server_source.freezed.dart'; 4 | part 'server_source.g.dart'; 5 | 6 | @freezed 7 | sealed class ServerSource with _$ServerSource { 8 | factory ServerSource({ 9 | required String name, 10 | required String serverId, 11 | String? extra, 12 | }) = _ServerSource; 13 | 14 | factory ServerSource.fromJson(Map json) => 15 | _$ServerSourceFromJson(json); 16 | } 17 | -------------------------------------------------------------------------------- /lib/plugins/brightness_controller.io.dart: -------------------------------------------------------------------------------- 1 | import 'package:screen_brightness/screen_brightness.dart'; 2 | 3 | Future setApplicationScreenBrightness(double brightness) async { 4 | await ScreenBrightness.instance.setApplicationScreenBrightness(brightness); 5 | } 6 | 7 | Future getApplicationScreenBrightness() { 8 | return ScreenBrightness.instance.application; 9 | } 10 | 11 | Future resetApplicationScreenBrightness() async { 12 | await ScreenBrightness.instance.resetApplicationScreenBrightness(); 13 | } 14 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /lib/core_services/eiga/mixin/eiga_follow_mixin.dart: -------------------------------------------------------------------------------- 1 | import 'package:hoyomi/core_services/eiga/export.dart'; 2 | import 'package:hoyomi/core_services/mixin/auth_mixin.dart'; 3 | 4 | mixin EigaFollowMixin on AuthMixin { 5 | Future> getFollows({required int page}); 6 | 7 | Future getFollowsCount(String eigaId, MetaEiga metaEiga) { 8 | throw UnimplementedError(); 9 | } 10 | 11 | Future isFollow(String eigaId); 12 | Future setFollow(EigaContextWithEpisodes context, bool value); 13 | } 14 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /web-extension/hoyomi-helper/src/background/logic/hash-to-int.ts: -------------------------------------------------------------------------------- 1 | export function hashToInt(input: string): number { 2 | let hash = 0x811c9dc5; // FNV-1a offset basis 3 | 4 | for (let i = 0; i < input.length; i++) { 5 | hash ^= input.charCodeAt(i); 6 | hash = Math.imul(hash, 0x01000193); // FNV prime 7 | hash >>>= 0; 8 | } 9 | 10 | // Bit mixing 11 | hash = (hash ^ (hash >>> 16)) >>> 0; 12 | hash = Math.imul(hash, 0x45d9f3b) >>> 0; 13 | hash = (hash ^ (hash >>> 16)) >>> 0; 14 | 15 | return hash % 99999; 16 | } 17 | -------------------------------------------------------------------------------- /lib/plugins/analytics.dart: -------------------------------------------------------------------------------- 1 | import 'package:firebase_crashlytics/firebase_crashlytics.dart'; 2 | import 'package:flutter/foundation.dart'; 3 | 4 | Future initializeAnalytics() async { 5 | if (!kDebugMode) { 6 | FlutterError.onError = (errorDetails) { 7 | FirebaseCrashlytics.instance.recordFlutterFatalError(errorDetails); 8 | }; 9 | PlatformDispatcher.instance.onError = (error, stack) { 10 | FirebaseCrashlytics.instance.recordError(error, stack, fatal: true); 11 | return true; 12 | }; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /rust_builder/cargokit/README: -------------------------------------------------------------------------------- 1 | /// This is copied from Cargokit (which is the official way to use it currently) 2 | /// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin 3 | 4 | Experimental repository to provide glue for seamlessly integrating cargo build 5 | with flutter plugins and packages. 6 | 7 | See https://matejknopp.com/post/flutter_plugin_in_rust_with_no_prebuilt_binaries/ 8 | for a tutorial on how to use Cargokit. 9 | 10 | Example plugin available at https://github.com/irondash/hello_rust_ffi_plugin. 11 | 12 | -------------------------------------------------------------------------------- /web-extension/hoyomi-helper/.gitpod.yml: -------------------------------------------------------------------------------- 1 | image: 2 | file: .gitpod.Dockerfile 3 | 4 | tasks: 5 | - init: bun i && bun build 6 | name: dev 7 | command: | 8 | gp sync-done ready 9 | bun dev 10 | - name: pnpm start:chromium 11 | command: | 12 | gp sync-await ready 13 | gp ports await 6080 14 | gp preview $(gp url 6080) 15 | sleep 5 16 | bun start:chromium 17 | openMode: split-right 18 | 19 | ports: 20 | - port: 5900 21 | onOpen: ignore 22 | - port: 6080 23 | onOpen: ignore 24 | -------------------------------------------------------------------------------- /lib/core_services/interfaces/user.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | part 'user.freezed.dart'; 4 | part 'user.g.dart'; 5 | 6 | @freezed 7 | sealed class User with _$User { 8 | const factory User({ 9 | required String user, 10 | String? email, 11 | required String photoUrl, 12 | required String fullName, 13 | @Default(Sex.other) Sex sex, 14 | }) = _User; 15 | 16 | factory User.fromJson(Map json) => _$UserFromJson(json); 17 | } 18 | 19 | enum Sex { male, female, other } 20 | -------------------------------------------------------------------------------- /macos/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | import app_links 4 | 5 | @main 6 | class AppDelegate: FlutterAppDelegate { 7 | override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { 8 | return true 9 | } 10 | 11 | override func application(_ application: NSApplication, 12 | open urls: [URL]) { 13 | for url in urls { 14 | print("🟢 Received URL: \(url.absoluteString)") 15 | AppLinks.shared.handleLink(url: url) 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /serverless/logic/parse-postgresl-url.ts: -------------------------------------------------------------------------------- 1 | export function parsePostgresUrl(url: string) { 2 | const regex = 3 | /^postgres:\/\/([^:]+):([^@]+)@([^:]+):(\d+)\/([^?]+)\?sslmode=(.+)$/ 4 | const match = url.match(regex) 5 | 6 | if (!match) { 7 | throw new Error("Invalid Postgres connection string") 8 | } 9 | 10 | const [, user, password, host, port, database, ssl] = match 11 | 12 | return { 13 | host, 14 | port: parseInt(port, 10), 15 | user, 16 | password, 17 | database, 18 | ssl: ssl as "require" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /web-extension/hoyomi-helper/scripts/utils.ts: -------------------------------------------------------------------------------- 1 | import { resolve } from 'node:path' 2 | import process from 'node:process' 3 | import { bgCyan, black } from 'kolorist' 4 | 5 | export const port = Number(process.env.PORT || '') || 3303 6 | export const r = (...args: string[]) => resolve(__dirname, '..', ...args) 7 | export const isDev = process.env.NODE_ENV !== 'production' 8 | export const isFirefox = process.env.EXTENSION === 'firefox' 9 | 10 | export function log(name: string, message: string) { 11 | console.log(black(bgCyan(` ${name} `)), message) 12 | } 13 | -------------------------------------------------------------------------------- /lib/core_services/interfaces/paginate.dart: -------------------------------------------------------------------------------- 1 | class Paginate { 2 | final List items; 3 | final int page; 4 | final int totalItems; 5 | final int totalPages; 6 | 7 | const Paginate({ 8 | required this.items, 9 | required this.page, 10 | required this.totalItems, 11 | required this.totalPages, 12 | }); 13 | 14 | factory Paginate.createFakeData(T item) { 15 | return Paginate( 16 | items: List.generate(30, (_) => item), 17 | page: 1, 18 | totalItems: 30, 19 | totalPages: 1, 20 | ); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /lib/core_services/eiga/interfaces/source_content.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | import 'main.dart'; 4 | 5 | part 'source_content.freezed.dart'; 6 | part 'source_content.g.dart'; 7 | 8 | @freezed 9 | sealed class SourceContent with _$SourceContent { 10 | const factory SourceContent({ 11 | required String content, 12 | required Uri url, 13 | Headers? headers, 14 | }) = _SourceContent; 15 | 16 | factory SourceContent.fromJson(Map json) => 17 | _$SourceContentFromJson(json); 18 | } 19 | -------------------------------------------------------------------------------- /js_runtime_polyfill/.gitignore: -------------------------------------------------------------------------------- 1 | # dependencies (bun install) 2 | node_modules 3 | 4 | # output 5 | out 6 | dist 7 | *.tgz 8 | 9 | # code coverage 10 | coverage 11 | *.lcov 12 | 13 | # logs 14 | logs 15 | _.log 16 | report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json 17 | 18 | # dotenv environment variable files 19 | .env 20 | .env.development.local 21 | .env.test.local 22 | .env.production.local 23 | .env.local 24 | 25 | # caches 26 | .eslintcache 27 | .cache 28 | *.tsbuildinfo 29 | 30 | # IntelliJ based IDEs 31 | .idea 32 | 33 | # Finder (MacOS) folder config 34 | .DS_Store 35 | -------------------------------------------------------------------------------- /lib/core_services/eiga/interfaces/source_video.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | import 'main.dart'; 4 | 5 | part 'source_video.freezed.dart'; 6 | part 'source_video.g.dart'; 7 | 8 | @freezed 9 | sealed class SourceVideo with _$SourceVideo { 10 | const factory SourceVideo({ 11 | required String src, 12 | required String type, 13 | Headers? headers, 14 | String? extra, 15 | }) = _SourceVideo; 16 | 17 | factory SourceVideo.fromJson(Map json) => 18 | _$SourceVideoFromJson(json); 19 | } 20 | -------------------------------------------------------------------------------- /lib/widgets/disabled.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class Disabled extends StatelessWidget { 4 | final bool disabled; 5 | final double opacity; 6 | final Widget child; 7 | 8 | const Disabled({ 9 | super.key, 10 | this.disabled = true, 11 | this.opacity = 0.5, 12 | required this.child, 13 | }); 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return Opacity( 18 | opacity: disabled ? opacity : 1.0, 19 | child: IgnorePointer(ignoring: disabled, child: child), 20 | ); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /lib/core_services/comic/interfaces/watch_page_updated.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | part 'watch_page_updated.freezed.dart'; 4 | part 'watch_page_updated.g.dart'; 5 | 6 | @freezed 7 | sealed class WatchPageUpdated with _$WatchPageUpdated { 8 | factory WatchPageUpdated({ 9 | required int currentPage, 10 | required int totalPage, 11 | required DateTime updatedAt, 12 | }) = _WatchPageUpdated; 13 | 14 | factory WatchPageUpdated.fromJson(Map json) => 15 | _$WatchPageUpdatedFromJson(json); 16 | } 17 | -------------------------------------------------------------------------------- /lib/plugins/fullscreen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_fullscreen/flutter_fullscreen.dart'; 2 | import 'package:fullscreen_window/fullscreen_window.dart'; 3 | import 'package:hoyomi/constraints/x_platform.dart'; 4 | 5 | Future initializeFullscreen() async { 6 | if (!XPlatform.isAndroid && !XPlatform.isIOS) { 7 | await FullScreen.ensureInitialized(); 8 | } 9 | } 10 | 11 | Future setFullScreen(bool value) async { 12 | if (XPlatform.isWindows) { 13 | FullScreenWindow.setFullScreen(value); 14 | } else { 15 | FullScreen.setFullScreen(value); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /lib/core_services/eiga/interfaces/watch_time_updated.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | part 'watch_time_updated.freezed.dart'; 4 | part 'watch_time_updated.g.dart'; 5 | 6 | @freezed 7 | sealed class WatchTimeUpdated with _$WatchTimeUpdated { 8 | const factory WatchTimeUpdated({ 9 | required Duration position, 10 | required Duration duration, 11 | required DateTime updatedAt, 12 | }) = _WatchTimeUpdated; 13 | 14 | factory WatchTimeUpdated.fromJson(Map json) => 15 | _$WatchTimeUpdatedFromJson(json); 16 | } 17 | -------------------------------------------------------------------------------- /lib/utils/export.dart: -------------------------------------------------------------------------------- 1 | // 自動生成ファイル。手動で編集しないでください。 2 | // 再生成するには scripts/auto_export.dart を実行してください。 3 | 4 | export 'authentication.dart'; 5 | export 'cache_remember/cache_remember.dart'; 6 | export 'debouncer.dart'; 7 | export 'd_query.dart'; 8 | export 'format_duration.dart'; 9 | export 'format_number.dart'; 10 | export 'format_time_ago.dart'; 11 | export 'format_watch_update_at.dart'; 12 | export 'future_cache.dart'; 13 | export 'get_is_buffering.dart'; 14 | export 'get_lang_icon.dart'; 15 | export 'proxy_cache/proxy_cache.dart'; 16 | export 'throttler.dart'; 17 | export 'time_utils.dart'; 18 | -------------------------------------------------------------------------------- /lib/core_services/eiga/interfaces/eiga_context.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | import '../export.dart'; 4 | 5 | part 'eiga_context.freezed.dart'; 6 | part 'eiga_context.g.dart'; 7 | 8 | @freezed 9 | sealed class EigaContext with _$EigaContext { 10 | const factory EigaContext({ 11 | required String eigaId, 12 | required MetaEiga metaEiga, 13 | required EigaEpisode episode, 14 | Season? season, 15 | }) = _EigaContext; 16 | 17 | factory EigaContext.fromJson(Map json) => 18 | _$EigaContextFromJson(json); 19 | } 20 | -------------------------------------------------------------------------------- /hoyomi_bridge_ts/.gitignore: -------------------------------------------------------------------------------- 1 | # dependencies (bun install) 2 | node_modules 3 | 4 | # output 5 | out 6 | dist 7 | *.tgz 8 | 9 | # code coverage 10 | coverage 11 | *.lcov 12 | 13 | # logs 14 | logs 15 | _.log 16 | report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json 17 | 18 | # dotenv environment variable files 19 | .env 20 | .env.development.local 21 | .env.test.local 22 | .env.production.local 23 | .env.local 24 | 25 | # caches 26 | .eslintcache 27 | .cache 28 | *.tsbuildinfo 29 | 30 | # IntelliJ based IDEs 31 | .idea 32 | 33 | # Finder (MacOS) folder config 34 | .DS_Store 35 | 36 | /index.js 37 | /index.d.ts 38 | -------------------------------------------------------------------------------- /lib/core_services/interfaces/setting/setting_field.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | 3 | part 'setting_field.g.dart'; 4 | 5 | @JsonSerializable() 6 | class SettingField { 7 | final String name; 8 | final String key; 9 | final String? description; 10 | 11 | const SettingField({ 12 | required this.name, 13 | required this.key, 14 | required this.description, 15 | }); 16 | 17 | factory SettingField.fromJson(Map json) => 18 | _$SettingFieldFromJson(json); 19 | Map toJson() => _$SettingFieldToJson(this); 20 | } 21 | -------------------------------------------------------------------------------- /lib/general_api/models/set_follow_response.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'set_follow_response.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _SetFollowResponse _$SetFollowResponseFromJson(Map json) => 10 | _SetFollowResponse(ok: json['ok'] as bool); 11 | 12 | Map _$SetFollowResponseToJson(_SetFollowResponse instance) => 13 | {'ok': instance.ok}; 14 | -------------------------------------------------------------------------------- /lib/plugins/export.dart: -------------------------------------------------------------------------------- 1 | // 自動生成ファイル。手動で編集しないでください。 2 | // 再生成するには scripts/auto_export.dart を実行してください。 3 | 4 | export 'analytics.dart'; 5 | export 'android_sdk_int.dart'; 6 | export 'application_document_directory.dart'; 7 | export 'app_links.dart'; 8 | export 'brightness_controller.dart'; 9 | export 'event_bus.dart'; 10 | export 'firebase.dart'; 11 | export 'fullscreen.dart'; 12 | export 'inflate_raw.dart'; 13 | export 'init_services.dart'; 14 | export 'install_cert.dart'; 15 | export 'install_web_rules.dart'; 16 | export 'logger.dart'; 17 | export 'video_polyfill.dart'; 18 | export 'volume_controller.dart'; 19 | -------------------------------------------------------------------------------- /lib/core_services/eiga/mixin/eiga_watch_time_mixin.dart: -------------------------------------------------------------------------------- 1 | import 'package:hoyomi/core_services/mixin/auth_mixin.dart'; 2 | 3 | import '../main.dart'; 4 | 5 | mixin EigaWatchTimeMixin on AuthMixin { 6 | Future> getWatchHistory({required int page}); 7 | 8 | Future getWatchTime(EigaContext context); 9 | 10 | Future> getWatchTimeEpisodes({ 11 | required String eigaId, 12 | required List episodes, 13 | }); 14 | 15 | Future setWatchTime( 16 | EigaContext context, { 17 | required WatchTime watchTime, 18 | }); 19 | } 20 | -------------------------------------------------------------------------------- /lib/rust/lib.dart: -------------------------------------------------------------------------------- 1 | // This file is automatically generated, so please do not edit it. 2 | // @generated by `flutter_rust_bridge`@ 2.11.1. 3 | 4 | // ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import 5 | 6 | import 'frb_generated.dart'; 7 | import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart'; 8 | 9 | // Rust type: RustOpaqueMoi DartFnFuture < Vec < String > > + 'static + Send + Sync >>> 10 | abstract class ArcFnStringDartFnFutureVecString 11 | implements RustOpaqueInterface {} 12 | -------------------------------------------------------------------------------- /lib/core_services/comic/interfaces/rate_value.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | part 'rate_value.freezed.dart'; 4 | part 'rate_value.g.dart'; 5 | 6 | @freezed 7 | sealed class RateValue with _$RateValue { 8 | const factory RateValue({ 9 | required int best, 10 | required int count, 11 | required double value, 12 | }) = _RateValue; 13 | 14 | factory RateValue.fromJson(Map json) => 15 | _$RateValueFromJson(json); 16 | 17 | factory RateValue.createFakeData() { 18 | return RateValue(best: 5, count: 1000, value: 4.5); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /lib/downloader/eiga_downloader.dart: -------------------------------------------------------------------------------- 1 | class EigaDownloader { 2 | static EigaDownloader? _instance; 3 | // Avoid self instance 4 | EigaDownloader._(); 5 | static EigaDownloader get instance => _instance ??= EigaDownloader._(); 6 | 7 | // TODO: Add methods for video downloading 8 | // - initialize() - Set up database connection 9 | // - downloadVideo(videoId, sourceId) - Download a video for offline viewing 10 | // - getOfflineVideos() - Retrieve list of downloaded videos 11 | // - deleteOfflineVideo(videoId) - Remove a downloaded video 12 | // - checkDownloadStatus(videoId) - Get download progress 13 | } 14 | -------------------------------------------------------------------------------- /lib/logic/search_language.dart: -------------------------------------------------------------------------------- 1 | import 'package:sealed_languages/sealed_languages.dart'; 2 | 3 | NaturalLanguage? searchLanguage(String text) { 4 | text = text.toUpperCase().trim(); 5 | 6 | for (final natural in NaturalLanguage.list) { 7 | final name = natural.name.toUpperCase(); 8 | 9 | if (text == name) return natural; 10 | if ('${text}ESE' == name) return natural; 11 | if (natural.code == text || natural.codeShort == text) return natural; 12 | if (natural.namesNative.any((name) => name.toUpperCase() == text)) { 13 | return natural; 14 | } 15 | } 16 | 17 | return null; 18 | } 19 | -------------------------------------------------------------------------------- /lib/core_services/eiga/interfaces/watch_time_data.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | import 'package:hoyomi/core_services/eiga/interfaces/watch_time.dart'; 3 | 4 | part 'watch_time_data.freezed.dart'; 5 | part 'watch_time_data.g.dart'; 6 | 7 | @freezed 8 | sealed class WatchTimeData with _$WatchTimeData { 9 | const factory WatchTimeData({ 10 | required String eigaId, 11 | required String episodeId, 12 | WatchTime? watchTime, 13 | }) = _WatchTimeData; 14 | 15 | factory WatchTimeData.fromJson(Map json) => 16 | _$WatchTimeDataFromJson(json); 17 | } 18 | -------------------------------------------------------------------------------- /lib/extensions/iterable_extension.dart: -------------------------------------------------------------------------------- 1 | extension FoldWithIterableExtension on Iterable { 2 | R foldWithIterable( 3 | R initialValue, 4 | R Function(R accumulator, T currentValue, Iterable list) callback, 5 | ) { 6 | R accumulator = initialValue; 7 | for (int i = 0; i < length; i++) { 8 | accumulator = callback(accumulator, elementAt(i), this); 9 | } 10 | return accumulator; 11 | } 12 | 13 | Iterable mapWithIterable( 14 | R Function(T currentValue, Iterable list) callback, 15 | ) { 16 | return map((currentValue) => callback(currentValue, this)); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /lib/logic/normalize_plugin_url.dart: -------------------------------------------------------------------------------- 1 | String normalizePluginUrl(String inputUrl) { 2 | final trimmed = inputUrl.trim(); 3 | 4 | if (trimmed.contains('raw.githubusercontent.com')) return trimmed; 5 | 6 | final uri = Uri.tryParse(trimmed); 7 | if (uri == null) return trimmed; 8 | 9 | if (uri.host == 'github.com') { 10 | final segments = uri.pathSegments; 11 | if (segments.length >= 2) { 12 | final user = segments[0]; 13 | final repo = segments[1]; 14 | 15 | return 'https://raw.githubusercontent.com/$user/$repo/refs/heads/release/index.js'; 16 | } 17 | } 18 | 19 | return trimmed; 20 | } 21 | -------------------------------------------------------------------------------- /lib/screens/home_eiga/widget/area_rewind.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:flutter/material.dart'; 4 | 5 | class AreaRewind extends CustomPainter { 6 | @override 7 | void paint(Canvas canvas, Size size) { 8 | // Paint settings 9 | final paint = 10 | Paint() 11 | ..color = Colors.white.withValues(alpha: 0.2) 12 | ..style = PaintingStyle.fill; 13 | final center = Offset(0, size.height / 2); 14 | canvas.drawCircle(center, min(size.width, size.height), paint); 15 | } 16 | 17 | @override 18 | bool shouldRepaint(covariant CustomPainter oldDelegate) => false; 19 | } 20 | -------------------------------------------------------------------------------- /lib/widgets/h_back_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:go_router/go_router.dart'; 3 | import 'package:iconify_flutter/icons/ion.dart'; 4 | import 'package:skeletonizer/skeletonizer.dart'; 5 | 6 | import 'export.dart'; 7 | 8 | class HBackButton extends StatelessWidget { 9 | const HBackButton({super.key}); 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | final child = IconButton( 14 | icon: const Iconify(Ion.chevron_left), 15 | onPressed: () { 16 | context.pop(); 17 | }, 18 | ); 19 | return Skeleton.keep(child: child); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /lib/widgets/vertical_separator.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | // VerticalSeparator Widget 4 | class VerticalSeparator extends StatelessWidget { 5 | final double width; 6 | final Color color; 7 | final EdgeInsetsGeometry margin; 8 | 9 | const VerticalSeparator({ 10 | super.key, 11 | this.width = 1.0, 12 | this.color = Colors.grey, 13 | this.margin = const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4.0), 14 | }); 15 | 16 | @override 17 | Widget build(BuildContext context) { 18 | return Container(width: width, height: 10.0, color: color, margin: margin); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /lib/general_api/models/has_follow_response.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'has_follow_response.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _HasFollowResponse _$HasFollowResponseFromJson(Map json) => 10 | _HasFollowResponse(hasFollow: json['has_follow'] as bool); 11 | 12 | Map _$HasFollowResponseToJson(_HasFollowResponse instance) => 13 | {'has_follow': instance.hasFollow}; 14 | -------------------------------------------------------------------------------- /serverless/db/schema/comic_newest.ts: -------------------------------------------------------------------------------- 1 | import { pgTable, integer, timestamp, json, text } from "drizzle-orm/pg-core" 2 | import { comicFollows } from "./comic_follows.ts" 3 | 4 | export const comicNewestName = "comic_newest" 5 | export const comicNewest = pgTable(comicNewestName, { 6 | id: integer("id").primaryKey().generatedAlwaysAsIdentity(), 7 | comicFollow: integer("comic_follow").references(() => comicFollows.id), 8 | chapterId: text("chapter_id").notNull(), 9 | chapterMeta: json("chapter_meta").notNull(), 10 | createdAt: timestamp("created_at", { withTimezone: true }) 11 | .notNull() 12 | .defaultNow() 13 | }) 14 | -------------------------------------------------------------------------------- /lib/general_api/general_api_client.dart: -------------------------------------------------------------------------------- 1 | // coverage:ignore-file 2 | // GENERATED CODE - DO NOT MODIFY BY HAND 3 | // ignore_for_file: type=lint, unused_import 4 | 5 | import 'package:dio/dio.dart'; 6 | 7 | import 'client/client_client.dart'; 8 | 9 | /// Hoyomi General API `v1.0.0` 10 | class GeneralApiClient { 11 | GeneralApiClient(Dio dio, {String? baseUrl}) : _dio = dio, _baseUrl = baseUrl; 12 | 13 | final Dio _dio; 14 | final String? _baseUrl; 15 | 16 | static String get version => '1.0.0'; 17 | 18 | ClientClient? _client; 19 | 20 | ClientClient get client => _client ??= ClientClient(_dio, baseUrl: _baseUrl); 21 | } 22 | -------------------------------------------------------------------------------- /lib/screens/home_eiga/widget/area_forward.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:flutter/material.dart'; 4 | 5 | class AreaForward extends CustomPainter { 6 | @override 7 | void paint(Canvas canvas, Size size) { 8 | // Paint settings 9 | final paint = 10 | Paint() 11 | ..color = Colors.white.withValues(alpha: 0.2) 12 | ..style = PaintingStyle.fill; 13 | final center = Offset(size.width, size.height / 2); 14 | canvas.drawCircle(center, min(size.width, size.height), paint); 15 | } 16 | 17 | @override 18 | bool shouldRepaint(covariant CustomPainter oldDelegate) => false; 19 | } 20 | -------------------------------------------------------------------------------- /lib/utils/get_is_buffering.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | import 'package:video_player/video_player.dart'; 3 | 4 | bool getIsBuffering(VideoPlayerController controller) { 5 | if (defaultTargetPlatform == TargetPlatform.android) { 6 | if (controller.value.isBuffering) { 7 | final position = controller.value.position.inMilliseconds; 8 | 9 | if (position >= controller.value.duration.inMilliseconds) return false; 10 | 11 | return position >= 12 | (controller.value.buffered.lastOrNull?.end.inMilliseconds ?? -1); 13 | } 14 | } 15 | 16 | return controller.value.isBuffering; 17 | } 18 | -------------------------------------------------------------------------------- /lib/core_services/service_init.ts: -------------------------------------------------------------------------------- 1 | import type { OImage } from "./interfaces/o_image" 2 | import type { SettingField } from "./interfaces/setting/setting_field" 3 | import type { WebRule } from "./interfaces/web_rule" 4 | 5 | export interface ServiceInit { 6 | name: string 7 | uid?: string 8 | faviconUrl: OImage 9 | rootUrl: string 10 | version?: string 11 | description?: string 12 | language?: string 13 | captchaUrl?: () => string 14 | settings?: SettingField[] 15 | webRules?: WebRule[] 16 | fetchHeadless?: boolean 17 | fetchBaseUrl?: string 18 | onBeforeInsertCookie?: (oldCookie?: string) => string | undefined 19 | } 20 | -------------------------------------------------------------------------------- /lib/rust/api/http/utils/socket_addr.dart: -------------------------------------------------------------------------------- 1 | // This file is automatically generated, so please do not edit it. 2 | // @generated by `flutter_rust_bridge`@ 2.11.1. 3 | 4 | // ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import 5 | 6 | import '../../../frb_generated.dart'; 7 | import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart'; 8 | 9 | // These functions are ignored (category: IgnoreBecauseNotAllowedOwner): `digest_ip` 10 | 11 | abstract class SocketAddrDigester { 12 | /// Adds the `FALLBACK_PORT` to the end of the string if it doesn't have a port. 13 | Future digestIp(); 14 | } 15 | -------------------------------------------------------------------------------- /rust/src/api/image/decode_image_rgba.rs: -------------------------------------------------------------------------------- 1 | use flutter_rust_bridge::frb; 2 | 3 | use crate::api::image::utils::load_image; 4 | 5 | #[derive(Debug, Clone)] 6 | pub struct DecodedImage { 7 | pub pixels: Vec, 8 | pub width: u32, 9 | pub height: u32, 10 | } 11 | 12 | #[frb] 13 | pub fn decode_image_rgba(image_data: Vec) -> Result { 14 | let img = load_image(image_data); 15 | 16 | let rgba = img.to_rgba8(); 17 | let (width, height) = rgba.dimensions(); 18 | 19 | Ok(DecodedImage { 20 | pixels: rgba.into_raw(), // Vec RGBA 21 | width, 22 | height, 23 | }) 24 | } 25 | -------------------------------------------------------------------------------- /lib/core_services/comic/mixin/comic_follow_mixin.ts: -------------------------------------------------------------------------------- 1 | import type { Paginate } from "../../interfaces/paginate" 2 | import type { MetaComic } from "../interfaces/meta_comic" 3 | import type { ComicFollow } from "../interfaces/comic_follow" 4 | import { ComicAuthMixin } from "./comic_auth_mixin" 5 | 6 | export abstract class ComicFollowMixin extends ComicAuthMixin { 7 | abstract getFollows(page: number): Promise> 8 | 9 | abstract isFollow(comicId: string): Promise 10 | 11 | abstract setFollow(params: { 12 | comicId: string 13 | metaComic: MetaComic 14 | value: boolean 15 | }): Promise 16 | } 17 | -------------------------------------------------------------------------------- /lib/general_api/models/set_eiga_follow_response.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'set_eiga_follow_response.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _SetEigaFollowResponse _$SetEigaFollowResponseFromJson( 10 | Map json, 11 | ) => _SetEigaFollowResponse(ok: json['ok'] as bool); 12 | 13 | Map _$SetEigaFollowResponseToJson( 14 | _SetEigaFollowResponse instance, 15 | ) => {'ok': instance.ok}; 16 | -------------------------------------------------------------------------------- /lib/core_services/eiga/interfaces/subtitle.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | import 'main.dart'; 4 | 5 | export 'package:subtitle/subtitle.dart' show SubtitleType; 6 | 7 | part 'subtitle.freezed.dart'; 8 | part 'subtitle.g.dart'; 9 | 10 | @freezed 11 | sealed class Subtitle with _$Subtitle { 12 | const factory Subtitle({ 13 | required String language, 14 | required String code, 15 | required SubtitleType type, 16 | required String url, 17 | Headers? headers, 18 | }) = _Subtitle; 19 | 20 | factory Subtitle.fromJson(Map json) => 21 | _$SubtitleFromJson(json); 22 | } 23 | -------------------------------------------------------------------------------- /hoyomi_bridge_ts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@hoyomi/bridge_ts", 3 | "version": "0.0.10", 4 | "module": "index.js", 5 | "types": "index.d.ts", 6 | "type": "module", 7 | "private": false, 8 | "scripts": { 9 | "build": "rm -f index.d.ts && rm -f index.js && bun build index.ts --outfile=index.js && tsup index.ts --dts-resolve --dts-only -d ./ --format esm", 10 | "release": "bumpp && bun run build && bun publish" 11 | }, 12 | "devDependencies": { 13 | "@types/bun": "^1.2.16", 14 | "tsup": "^8.5.0" 15 | }, 16 | "peerDependencies": { 17 | "typescript": "^5" 18 | }, 19 | "dependencies": { 20 | "bumpp": "^10.2.0" 21 | } 22 | } -------------------------------------------------------------------------------- /lib/general_api/models/ignore.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'ignore.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _Ignore _$IgnoreFromJson(Map json) => _Ignore( 10 | sourceId: json['sourceId'] as String, 11 | comicTextId: json['comic_text_id'] as String, 12 | ); 13 | 14 | Map _$IgnoreToJson(_Ignore instance) => { 15 | 'sourceId': instance.sourceId, 16 | 'comic_text_id': instance.comicTextId, 17 | }; 18 | -------------------------------------------------------------------------------- /lib/general_api/models/ignore2.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'ignore2.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _Ignore2 _$Ignore2FromJson(Map json) => _Ignore2( 10 | sourceId: json['sourceId'] as String, 11 | eigaTextId: json['eiga_text_id'] as String, 12 | ); 13 | 14 | Map _$Ignore2ToJson(_Ignore2 instance) => { 15 | 'sourceId': instance.sourceId, 16 | 'eiga_text_id': instance.eigaTextId, 17 | }; 18 | -------------------------------------------------------------------------------- /lib/plugins/volume_controller.web.dart: -------------------------------------------------------------------------------- 1 | import 'package:web/web.dart'; 2 | 3 | // 動画の音量を取得する。見つからなければデフォルト1を返す 4 | @override 5 | Future getVolume() async { 6 | final video = document.querySelector('video') as HTMLVideoElement?; 7 | return video?.volume.toDouble() ?? 1.0; 8 | } 9 | 10 | // 動画の音量を設定する。video タグが存在しない場合は何もしない 11 | @override 12 | Future setVolume(double value) async { 13 | final video = document.querySelector('video') as HTMLVideoElement?; 14 | if (video != null) { 15 | video.volume = value.clamp(0.0, 1.0); // 音量は 0.0 から 1.0 の範囲に制限 16 | } 17 | } 18 | 19 | @override 20 | Future updateShowSystemUI(bool state) async {} 21 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /serverless/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hoyomi-general-api", 3 | "version": "1.0.0", 4 | "module": "index.ts", 5 | "type": "module", 6 | "private": true, 7 | "devDependencies": { 8 | "@types/bun": "latest", 9 | "dotenv": "^16.4.7", 10 | "drizzle-kit": "^0.30.6" 11 | }, 12 | "peerDependencies": { 13 | "typescript": "^5" 14 | }, 15 | "dependencies": { 16 | "@hono/swagger-ui": "^0.5.1", 17 | "@hono/zod-openapi": "^0.19.2", 18 | "@hono/zod-validator": "^0.4.3", 19 | "@types/pg": "^8.11.14", 20 | "drizzle-orm": "^0.41.0", 21 | "pg": "^8.15.6", 22 | "postgres": "^3.4.5", 23 | "zod": "^3.24.2" 24 | } 25 | } -------------------------------------------------------------------------------- /.github/workflows/ext_ci.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | pull_request: 4 | 5 | name: Web extension test 6 | 7 | jobs: 8 | ext_test: 9 | name: Web extension test 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - uses: actions/checkout@v1 14 | 15 | - uses: actions/setup-java@v1 16 | with: 17 | java-version: "20.x" 18 | 19 | - uses: actions/setup-node@v4 20 | with: 21 | node-version: 21 22 | 23 | - uses: oven-sh/setup-bun@v2 24 | with: 25 | bun-version: latest 26 | 27 | - name: Test 28 | run: | 29 | cd web-*/*/ 30 | bun i 31 | bun run test -------------------------------------------------------------------------------- /web-extension/hoyomi-helper/src/logic/buffer-to-b64.ts: -------------------------------------------------------------------------------- 1 | export function arrayBufferToBase64(buffer: ArrayBuffer): string { 2 | let binary = "" 3 | const bytes = new Uint8Array(buffer) 4 | const len = bytes.byteLength 5 | for (let i = 0; i < len; i++) { 6 | binary += String.fromCharCode(bytes[i]) 7 | } 8 | return btoa(binary) 9 | } 10 | 11 | export function base64ToArrayBuffer(base64: string): ArrayBuffer { 12 | const binary_string = atob(base64) 13 | const len = binary_string.length 14 | const bytes = new Uint8Array(len) 15 | for (let i = 0; i < len; i++) { 16 | bytes[i] = binary_string.charCodeAt(i) 17 | } 18 | return bytes.buffer 19 | } 20 | -------------------------------------------------------------------------------- /lib/utils/proxy_cache/proxy_cache.stub.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | class ProxyCache { 4 | static final ProxyCache _instance = ProxyCache._internal(); 5 | static ProxyCache get instance => _instance; 6 | 7 | ProxyCache._internal(); 8 | 9 | Future saveFile({ 10 | required String content, 11 | required String path, 12 | }) async { 13 | throw UnimplementedError(); 14 | } 15 | 16 | Future start() async { 17 | throw UnimplementedError(); 18 | } 19 | 20 | Uri getUrlHttp(String file) { 21 | throw UnimplementedError(); 22 | } 23 | 24 | Future stop() async { 25 | throw UnimplementedError(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lib/general_api/models/has_eiga_follow_response.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'has_eiga_follow_response.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _HasEigaFollowResponse _$HasEigaFollowResponseFromJson( 10 | Map json, 11 | ) => _HasEigaFollowResponse(hasFollow: json['has_follow'] as bool); 12 | 13 | Map _$HasEigaFollowResponseToJson( 14 | _HasEigaFollowResponse instance, 15 | ) => {'has_follow': instance.hasFollow}; 16 | -------------------------------------------------------------------------------- /rust/src/api/image/get_image_size.rs: -------------------------------------------------------------------------------- 1 | use flutter_rust_bridge::frb; 2 | use std::io::Cursor; 3 | 4 | fn read_image_size(data: Vec) -> Result<(u32, u32), String> { 5 | let img = image::ImageReader::new(Cursor::new(data)) 6 | .with_guessed_format() 7 | .map_err(|e| e.to_string())? 8 | .decode() 9 | .map_err(|e| e.to_string())?; 10 | Ok((img.width(), img.height())) 11 | } 12 | 13 | #[frb] 14 | pub fn get_image_size(data: Vec) -> Result<(u32, u32), String> { 15 | read_image_size(data) 16 | } 17 | 18 | #[frb(sync)] 19 | pub fn get_image_size_sync(data: Vec) -> Result<(u32, u32), String> { 20 | read_image_size(data) 21 | } 22 | -------------------------------------------------------------------------------- /rust_builder/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | migrate_working_dir/ 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | 19 | # The .vscode folder contains launch configuration and tasks you configure in 20 | # VS Code which you may wish to be included in version control, so this line 21 | # is commented out by default. 22 | #.vscode/ 23 | 24 | # Flutter/Dart/Pub related 25 | # Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. 26 | /pubspec.lock 27 | **/doc/api/ 28 | .dart_tool/ 29 | build/ 30 | -------------------------------------------------------------------------------- /js_runtime_polyfill/embed.ts: -------------------------------------------------------------------------------- 1 | import "./polyfill/rust_api/unscramble_image" 2 | import "./polyfill/rust_api/unscramble_image_columns" 3 | import "./polyfill/rust_api/unscramble_image_rows" 4 | import "./polyfill/base64_coder" 5 | import "./polyfill/base64" 6 | import "./polyfill/dart_send_message" 7 | import "./polyfill/ecma" 8 | import "./polyfill/fetch" 9 | import "./polyfill/headers" 10 | import "./polyfill/request" 11 | import "./polyfill/response" 12 | import "./polyfill/text_decoder" 13 | import "./polyfill/text_encoder" 14 | import "./polyfill/unimplemented_error" 15 | import "./polyfill/url_search_params" 16 | import "./polyfill/url" 17 | import "./polyfill/xml_http_request" 18 | -------------------------------------------------------------------------------- /web-extension/hoyomi-helper/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "incremental": false, 4 | "target": "es2016", 5 | "jsx": "preserve", 6 | "lib": ["DOM", "ESNext"], 7 | "baseUrl": ".", 8 | "module": "ESNext", 9 | "moduleResolution": "node", 10 | "paths": { 11 | "~/*": ["src/*"] 12 | }, 13 | "resolveJsonModule": true, 14 | "types": [ 15 | "vite/client", 16 | "vitest/globals" 17 | ], 18 | "strict": true, 19 | "noUnusedLocals": true, 20 | "esModuleInterop": true, 21 | "forceConsistentCasingInFileNames": true, 22 | "skipLibCheck": true 23 | }, 24 | "exclude": ["dist", "node_modules"] 25 | } 26 | -------------------------------------------------------------------------------- /windows/runner/runner.exe.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PerMonitorV2 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /lib/core_services/comic/interfaces/comic_carousel_item.ts: -------------------------------------------------------------------------------- 1 | import type { OImage } from "../../interfaces/o_image" 2 | import type { Genre } from "../../interfaces/genre" 3 | 4 | export interface ComicCarouselItem { 5 | subText?: string 6 | image: OImage 7 | comicId: string 8 | name: string 9 | originalName?: string 10 | type?: string 11 | episodeDuration?: string 12 | updatedAt?: Date 13 | quality?: string 14 | countSub?: number 15 | countDub?: number 16 | rate?: number 17 | notice?: string 18 | year?: string 19 | description?: string 20 | studio?: string 21 | duration?: string 22 | language?: string 23 | genres?: Genre[] 24 | actors?: Genre[] 25 | } 26 | -------------------------------------------------------------------------------- /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 = Hoyomi 9 | 10 | // The application's bundle identifier 11 | PRODUCT_BUNDLE_IDENTIFIER = git.shin.hoyomi 12 | 13 | // The copyright displayed in application information 14 | PRODUCT_COPYRIGHT = Copyright © 2024-now Tachibana Shin. All rights reserved. 15 | -------------------------------------------------------------------------------- /lib/widgets/animated_fade_ignore.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class AnimatedFadeIgnore extends StatelessWidget { 4 | final bool hide; 5 | final Widget child; 6 | final Duration duration; 7 | 8 | const AnimatedFadeIgnore({ 9 | super.key, 10 | required this.hide, 11 | required this.child, 12 | this.duration = const Duration(milliseconds: 300), 13 | }); 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return IgnorePointer( 18 | ignoring: hide, 19 | child: AnimatedOpacity( 20 | opacity: hide ? 0 : 1, 21 | duration: duration, 22 | child: child, 23 | ), 24 | ); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/core_services/eiga/interfaces/eiga_param.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'eiga_param.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _EigaParam _$EigaParamFromJson(Map json) => _EigaParam( 10 | eigaId: json['eigaId'] as String, 11 | episodeId: json['episodeId'] as String?, 12 | ); 13 | 14 | Map _$EigaParamToJson(_EigaParam instance) => 15 | { 16 | 'eigaId': instance.eigaId, 17 | 'episodeId': instance.episodeId, 18 | }; 19 | -------------------------------------------------------------------------------- /lib/core_services/eiga/interfaces/main.dart: -------------------------------------------------------------------------------- 1 | export 'eiga_carousel.dart'; 2 | export 'eiga_carousel_item.dart'; 3 | export 'eiga_category.dart'; 4 | export 'eiga_episode.dart'; 5 | export 'eiga_episodes.dart'; 6 | export 'eiga_history.dart'; 7 | export 'eiga_home.dart'; 8 | export 'eiga_param.dart'; 9 | export 'eiga.dart'; 10 | export 'home_eiga_category.dart'; 11 | export 'meta_eiga.dart'; 12 | export 'opening_ending.dart'; 13 | export 'server_source.dart'; 14 | export 'source_content.dart'; 15 | export 'source_video.dart'; 16 | export 'subtitle.dart'; 17 | export 'watch_time_data.dart'; 18 | export 'watch_time_updated.dart'; 19 | export 'watch_time.dart'; 20 | export '../../interfaces/main.dart'; 21 | -------------------------------------------------------------------------------- /lib/core_services/eiga/interfaces/eiga_carousel_item.ts: -------------------------------------------------------------------------------- 1 | import type { Genre } from "../../interfaces/genre" 2 | import type { OImage } from "../../interfaces/o_image" 3 | 4 | export interface EigaCarouselItem { 5 | subText?: string 6 | image: OImage 7 | eigaId: string 8 | name: string 9 | originalName?: string 10 | type?: string 11 | episodeDuration?: string 12 | updatedAt?: Date // ISO date string 13 | quality?: string 14 | countSub?: number 15 | countDub?: number 16 | rate?: number 17 | notice?: string 18 | year?: string 19 | description?: string 20 | studio?: string 21 | duration?: string 22 | language?: string 23 | genres?: Genre[] 24 | actors?: Genre[] 25 | } 26 | -------------------------------------------------------------------------------- /lib/utils/format_time_ago.dart: -------------------------------------------------------------------------------- 1 | // 時間を「時間前」の形式でフォーマットする関数 2 | String formatTimeAgo(DateTime time) { 3 | final difference = DateTime.now().difference(time); 4 | if (difference.inDays > 365) { 5 | return '${(difference.inDays / 365).floor()} years ago'; 6 | } else if (difference.inDays > 30) { 7 | return '${(difference.inDays / 30).floor()} months ago'; 8 | } else if (difference.inDays > 0) { 9 | return '${difference.inDays} days ago'; 10 | } else if (difference.inHours > 0) { 11 | return '${difference.inHours} hours ago'; 12 | } else if (difference.inMinutes > 0) { 13 | return '${difference.inMinutes} minutes ago'; 14 | } else { 15 | return 'Just now'; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /lib/widgets/delayed_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class DelayedWidget extends StatelessWidget { 4 | final Widget Function(BuildContext context) builder; 5 | final Duration delay; 6 | 7 | const DelayedWidget({required this.builder, required this.delay, super.key}); 8 | 9 | @override 10 | Widget build(BuildContext context) { 11 | return FutureBuilder( 12 | future: Future.delayed(delay), 13 | builder: (context, snapshot) { 14 | if (snapshot.connectionState == ConnectionState.done) { 15 | return builder(context); 16 | } else { 17 | return SizedBox.shrink(); 18 | } 19 | }, 20 | ); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /web-extension/hoyomi-helper/src/background/contentScriptHMR.ts: -------------------------------------------------------------------------------- 1 | import { isFirefox, isForbiddenUrl } from '~/env' 2 | 3 | // Firefox fetch files from cache instead of reloading changes from disk, 4 | // hmr will not work as Chromium based browser 5 | browser.webNavigation.onCommitted.addListener(({ tabId, frameId, url }) => { 6 | // Filter out non main window events. 7 | if (frameId !== 0) 8 | return 9 | 10 | if (isForbiddenUrl(url)) 11 | return 12 | 13 | // inject the latest scripts 14 | browser.tabs.executeScript(tabId, { 15 | file: `${isFirefox ? '' : '.'}/dist/contentScripts/index.global.js`, 16 | runAt: 'document_end', 17 | }).catch(error => console.error(error)) 18 | }) 19 | -------------------------------------------------------------------------------- /lib/core_services/comic/interfaces/comic_param.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'comic_param.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _ComicParam _$ComicParamFromJson(Map json) => _ComicParam( 10 | comicId: json['comicId'] as String, 11 | chapterId: json['chapterId'] as String?, 12 | ); 13 | 14 | Map _$ComicParamToJson(_ComicParam instance) => 15 | { 16 | 'comicId': instance.comicId, 17 | 'chapterId': instance.chapterId, 18 | }; 19 | -------------------------------------------------------------------------------- /lib/general_api/models/ignore.dart: -------------------------------------------------------------------------------- 1 | // coverage:ignore-file 2 | // GENERATED CODE - DO NOT MODIFY BY HAND 3 | // ignore_for_file: type=lint, unused_import 4 | 5 | import 'package:freezed_annotation/freezed_annotation.dart'; 6 | 7 | part 'ignore.freezed.dart'; 8 | part 'ignore.g.dart'; 9 | 10 | @Freezed() 11 | abstract class Ignore with _$Ignore { 12 | const factory Ignore({ 13 | /// Source ID to exclude from results 14 | required String sourceId, 15 | 16 | /// Comic text ID to exclude from results 17 | @JsonKey(name: 'comic_text_id') 18 | required String comicTextId, 19 | }) = _Ignore; 20 | 21 | factory Ignore.fromJson(Map json) => _$IgnoreFromJson(json); 22 | } 23 | -------------------------------------------------------------------------------- /lib/general_api/models/watch_page_schema.dart: -------------------------------------------------------------------------------- 1 | // coverage:ignore-file 2 | // GENERATED CODE - DO NOT MODIFY BY HAND 3 | // ignore_for_file: type=lint, unused_import 4 | 5 | import 'package:freezed_annotation/freezed_annotation.dart'; 6 | 7 | import 'data3.dart'; 8 | 9 | part 'watch_page_schema.freezed.dart'; 10 | part 'watch_page_schema.g.dart'; 11 | 12 | /// The watch page object, which can be null if no data is available. 13 | @Freezed() 14 | abstract class WatchPageSchema with _$WatchPageSchema { 15 | const factory WatchPageSchema({required Data3? data}) = _WatchPageSchema; 16 | 17 | factory WatchPageSchema.fromJson(Map json) => 18 | _$WatchPageSchemaFromJson(json); 19 | } 20 | -------------------------------------------------------------------------------- /lib/general_api/models/watch_time_schema.dart: -------------------------------------------------------------------------------- 1 | // coverage:ignore-file 2 | // GENERATED CODE - DO NOT MODIFY BY HAND 3 | // ignore_for_file: type=lint, unused_import 4 | 5 | import 'package:freezed_annotation/freezed_annotation.dart'; 6 | 7 | import 'data6.dart'; 8 | 9 | part 'watch_time_schema.freezed.dart'; 10 | part 'watch_time_schema.g.dart'; 11 | 12 | /// The watch time object, which can be null if no data is available. 13 | @Freezed() 14 | abstract class WatchTimeSchema with _$WatchTimeSchema { 15 | const factory WatchTimeSchema({required Data6? data}) = _WatchTimeSchema; 16 | 17 | factory WatchTimeSchema.fromJson(Map json) => 18 | _$WatchTimeSchemaFromJson(json); 19 | } 20 | -------------------------------------------------------------------------------- /lib/core_services/eiga/interfaces/eiga_context_with_episodes.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | import '../export.dart'; 4 | 5 | part 'eiga_context_with_episodes.freezed.dart'; 6 | part 'eiga_context_with_episodes.g.dart'; 7 | 8 | @freezed 9 | sealed class EigaContextWithEpisodes with _$EigaContextWithEpisodes { 10 | const factory EigaContextWithEpisodes({ 11 | required String eigaId, 12 | required MetaEiga metaEiga, 13 | Season? season, 14 | required List episodes, 15 | }) = _EigaContextWithEpisodes; 16 | 17 | factory EigaContextWithEpisodes.fromJson(Map json) => 18 | _$EigaContextWithEpisodesFromJson(json); 19 | } 20 | -------------------------------------------------------------------------------- /lib/core_services/interfaces/vtt.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'vtt.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _Vtt _$VttFromJson(Map json) => _Vtt( 10 | src: json['src'] as String, 11 | headers: 12 | json['headers'] == null 13 | ? null 14 | : Headers.fromJson(json['headers'] as Map), 15 | ); 16 | 17 | Map _$VttToJson(_Vtt instance) => { 18 | 'src': instance.src, 19 | 'headers': instance.headers, 20 | }; 21 | -------------------------------------------------------------------------------- /lib/core_services/eiga/mixin/eiga_follow_mixin.ts: -------------------------------------------------------------------------------- 1 | import type { Paginate } from "../../interfaces/paginate" 2 | import { AuthMixin } from "../../mixin/auth_mixin" 3 | import type { EigaFollow } from "../interfaces/eiga_follow" 4 | import type { EigaContextWithEpisodes } from "../interfaces/eiga_context_with_episodes" 5 | 6 | export abstract class EigaFollowMixin extends AuthMixin { 7 | abstract getFollows(page: number): Promise> 8 | 9 | abstract getFollowsCount(context: EigaContextWithEpisodes): Promise 10 | 11 | abstract isFollow(eigaId: string): Promise 12 | 13 | abstract setFollow( 14 | context: EigaContextWithEpisodes, 15 | value: boolean 16 | ): Promise 17 | } 18 | -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | **/dgph 2 | *.mode1v3 3 | *.mode2v3 4 | *.moved-aside 5 | *.pbxuser 6 | *.perspectivev3 7 | **/*sync/ 8 | .sconsign.dblite 9 | .tags* 10 | **/.vagrant/ 11 | **/DerivedData/ 12 | Icon? 13 | **/Pods/ 14 | **/.symlinks/ 15 | profile 16 | xcuserdata 17 | **/.generated/ 18 | Flutter/App.framework 19 | Flutter/Flutter.framework 20 | Flutter/Flutter.podspec 21 | Flutter/Generated.xcconfig 22 | Flutter/ephemeral/ 23 | Flutter/app.flx 24 | Flutter/app.zip 25 | Flutter/flutter_assets/ 26 | Flutter/flutter_export_environment.sh 27 | ServiceDefinitions.json 28 | Runner/GeneratedPluginRegistrant.* 29 | 30 | # Exceptions to above rules. 31 | !default.mode1v3 32 | !default.mode2v3 33 | !default.pbxuser 34 | !default.perspectivev3 35 | -------------------------------------------------------------------------------- /lib/core_services/comic/interfaces/watch_page.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'watch_page.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _WatchPage _$WatchPageFromJson(Map json) => _WatchPage( 10 | currentPage: (json['currentPage'] as num).toInt(), 11 | totalPage: (json['totalPage'] as num).toInt(), 12 | ); 13 | 14 | Map _$WatchPageToJson(_WatchPage instance) => 15 | { 16 | 'currentPage': instance.currentPage, 17 | 'totalPage': instance.totalPage, 18 | }; 19 | -------------------------------------------------------------------------------- /lib/general_api/models/ignore2.dart: -------------------------------------------------------------------------------- 1 | // coverage:ignore-file 2 | // GENERATED CODE - DO NOT MODIFY BY HAND 3 | // ignore_for_file: type=lint, unused_import 4 | 5 | import 'package:freezed_annotation/freezed_annotation.dart'; 6 | 7 | part 'ignore2.freezed.dart'; 8 | part 'ignore2.g.dart'; 9 | 10 | @Freezed() 11 | abstract class Ignore2 with _$Ignore2 { 12 | const factory Ignore2({ 13 | /// Source ID to exclude from the result (optional) 14 | required String sourceId, 15 | 16 | /// Unique identifier for the eiga to exclude 17 | @JsonKey(name: 'eiga_text_id') 18 | required String eigaTextId, 19 | }) = _Ignore2; 20 | 21 | factory Ignore2.fromJson(Map json) => _$Ignore2FromJson(json); 22 | } 23 | -------------------------------------------------------------------------------- /serverless/db/schema/eiga_follows.ts: -------------------------------------------------------------------------------- 1 | import { pgTable, integer, timestamp, text } from "drizzle-orm/pg-core" 2 | import { eiga } from "./eiga.ts" 3 | 4 | export const eigaFollowsName = "eiga_follows" 5 | export const eigaFollows = pgTable(eigaFollowsName, { 6 | id: integer("id").primaryKey().generatedAlwaysAsIdentity(), 7 | eigaId: integer("eiga_id") 8 | .references(() => eiga.id) 9 | .notNull() 10 | .unique(), 11 | currentEpisodeName: text("current_episode_name").notNull(), 12 | currentEpisodeId: text("current_episode_id").notNull(), 13 | currentEpisodeTime: timestamp("current_episode_time"), 14 | createdAt: timestamp("created_at", { withTimezone: true }) 15 | .notNull() 16 | .defaultNow() 17 | }) 18 | -------------------------------------------------------------------------------- /js_runtime_polyfill/polyfill/base64_coder.ts: -------------------------------------------------------------------------------- 1 | import { register } from "../register" 2 | import { atob, btoa } from "./base64" 3 | 4 | declare global { 5 | function base64Encode(data: Uint8Array): string 6 | function base64Decode(base64: string): Uint8Array 7 | } 8 | 9 | register({ base64Encode, base64Decode }) 10 | export function base64Encode(data: Uint8Array): string { 11 | const binary = String.fromCharCode(...data) 12 | return btoa(binary) 13 | } 14 | 15 | export function base64Decode(base64: string): Uint8Array { 16 | const binary = atob(base64) 17 | const bytes = new Uint8Array(binary.length) 18 | for (let i = 0; i < binary.length; i++) { 19 | bytes[i] = binary.charCodeAt(i) 20 | } 21 | return bytes 22 | } 23 | -------------------------------------------------------------------------------- /lib/general_api/models/current_chapters.dart: -------------------------------------------------------------------------------- 1 | // coverage:ignore-file 2 | // GENERATED CODE - DO NOT MODIFY BY HAND 3 | // ignore_for_file: type=lint, unused_import 4 | 5 | import 'package:freezed_annotation/freezed_annotation.dart'; 6 | 7 | part 'current_chapters.freezed.dart'; 8 | part 'current_chapters.g.dart'; 9 | 10 | @Freezed() 11 | abstract class CurrentChapters with _$CurrentChapters { 12 | const factory CurrentChapters({ 13 | required String name, 14 | required String fullName, 15 | required num order, 16 | required String chapterId, 17 | required DateTime time, 18 | }) = _CurrentChapters; 19 | 20 | factory CurrentChapters.fromJson(Map json) => _$CurrentChaptersFromJson(json); 21 | } 22 | -------------------------------------------------------------------------------- /lib/general_api/models/set_follow_response.dart: -------------------------------------------------------------------------------- 1 | // coverage:ignore-file 2 | // GENERATED CODE - DO NOT MODIFY BY HAND 3 | // ignore_for_file: type=lint, unused_import 4 | 5 | import 'package:freezed_annotation/freezed_annotation.dart'; 6 | 7 | part 'set_follow_response.freezed.dart'; 8 | part 'set_follow_response.g.dart'; 9 | 10 | /// Indicates if the follow/unfollow operation succeeded. 11 | @Freezed() 12 | abstract class SetFollowResponse with _$SetFollowResponse { 13 | const factory SetFollowResponse({ 14 | /// Whether the operation was successful. 15 | required bool ok, 16 | }) = _SetFollowResponse; 17 | 18 | factory SetFollowResponse.fromJson(Map json) => _$SetFollowResponseFromJson(json); 19 | } 20 | -------------------------------------------------------------------------------- /lib/general_api/models/watch_page_schema.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'watch_page_schema.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _WatchPageSchema _$WatchPageSchemaFromJson(Map json) => 10 | _WatchPageSchema( 11 | data: 12 | json['data'] == null 13 | ? null 14 | : Data3.fromJson(json['data'] as Map), 15 | ); 16 | 17 | Map _$WatchPageSchemaToJson(_WatchPageSchema instance) => 18 | {'data': instance.data}; 19 | -------------------------------------------------------------------------------- /lib/general_api/models/watch_time_schema.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'watch_time_schema.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _WatchTimeSchema _$WatchTimeSchemaFromJson(Map json) => 10 | _WatchTimeSchema( 11 | data: 12 | json['data'] == null 13 | ? null 14 | : Data6.fromJson(json['data'] as Map), 15 | ); 16 | 17 | Map _$WatchTimeSchemaToJson(_WatchTimeSchema instance) => 18 | {'data': instance.data}; 19 | -------------------------------------------------------------------------------- /rust_builder/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: rust_lib_hoyomi 2 | description: "Utility to build Rust code" 3 | version: 0.0.1 4 | publish_to: none 5 | 6 | environment: 7 | sdk: '>=3.3.0 <4.0.0' 8 | flutter: '>=3.3.0' 9 | 10 | dependencies: 11 | flutter: 12 | sdk: flutter 13 | plugin_platform_interface: ^2.0.2 14 | 15 | dev_dependencies: 16 | ffi: ^2.0.2 17 | ffigen: ^11.0.0 18 | flutter_test: 19 | sdk: flutter 20 | flutter_lints: ^2.0.0 21 | 22 | flutter: 23 | plugin: 24 | platforms: 25 | android: 26 | ffiPlugin: true 27 | ios: 28 | ffiPlugin: true 29 | linux: 30 | ffiPlugin: true 31 | macos: 32 | ffiPlugin: true 33 | windows: 34 | ffiPlugin: true 35 | -------------------------------------------------------------------------------- /lib/general_api/models/post_api_eiga_set_watch_time_response.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'post_api_eiga_set_watch_time_response.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _PostApiEigaSetWatchTimeResponse _$PostApiEigaSetWatchTimeResponseFromJson( 10 | Map json, 11 | ) => _PostApiEigaSetWatchTimeResponse(success: json['success'] as bool); 12 | 13 | Map _$PostApiEigaSetWatchTimeResponseToJson( 14 | _PostApiEigaSetWatchTimeResponse instance, 15 | ) => {'success': instance.success}; 16 | -------------------------------------------------------------------------------- /serverless/db/schema/comic_follows.ts: -------------------------------------------------------------------------------- 1 | import { pgTable, integer, timestamp, text } from "drizzle-orm/pg-core" 2 | import { comic } from "./comic.ts" 3 | 4 | export const comicFollowsName = "comic_follows" 5 | export const comicFollows = pgTable(comicFollowsName, { 6 | id: integer("id").primaryKey().generatedAlwaysAsIdentity(), 7 | comicId: integer("comic_id") 8 | .references(() => comic.id) 9 | .notNull() 10 | .unique(), 11 | currentChapterName: text("current_chapter_name").notNull(), 12 | currentChapterId: text("current_chapter_id").notNull(), 13 | currentChapterTime: timestamp("current_chapter_time"), 14 | createdAt: timestamp("created_at", { withTimezone: true }) 15 | .notNull() 16 | .defaultNow() 17 | }) 18 | -------------------------------------------------------------------------------- /lib/core_services/interfaces/headers.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'headers.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _Headers _$HeadersFromJson(Map json) => _Headers( 10 | headers: 11 | (json['headers'] as Map?)?.map( 12 | (k, e) => 13 | MapEntry(k, (e as List).map((e) => e as String).toList()), 14 | ) ?? 15 | const {}, 16 | ); 17 | 18 | Map _$HeadersToJson(_Headers instance) => { 19 | 'headers': instance.headers, 20 | }; 21 | -------------------------------------------------------------------------------- /lib/general_api/models/list_watch_history.dart: -------------------------------------------------------------------------------- 1 | // coverage:ignore-file 2 | // GENERATED CODE - DO NOT MODIFY BY HAND 3 | // ignore_for_file: type=lint, unused_import 4 | 5 | import 'package:freezed_annotation/freezed_annotation.dart'; 6 | 7 | import 'data.dart'; 8 | 9 | part 'list_watch_history.freezed.dart'; 10 | part 'list_watch_history.g.dart'; 11 | 12 | /// An array of watch history items. Schema representing an individual watch history item. 13 | @Freezed() 14 | sealed class ListWatchHistory with _$ListWatchHistory { 15 | const factory ListWatchHistory({required List data}) = 16 | _ListWatchHistory; 17 | 18 | factory ListWatchHistory.fromJson(Map json) => 19 | _$ListWatchHistoryFromJson(json); 20 | } 21 | -------------------------------------------------------------------------------- /lib/general_api/models/post_api_comic_set_watch_page_response.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'post_api_comic_set_watch_page_response.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _PostApiComicSetWatchPageResponse _$PostApiComicSetWatchPageResponseFromJson( 10 | Map json, 11 | ) => _PostApiComicSetWatchPageResponse(success: json['success'] as bool); 12 | 13 | Map _$PostApiComicSetWatchPageResponseToJson( 14 | _PostApiComicSetWatchPageResponse instance, 15 | ) => {'success': instance.success}; 16 | -------------------------------------------------------------------------------- /lib/database/scheme/service_settings.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | part 'service_settings.freezed.dart'; 4 | part 'service_settings.g.dart'; 5 | 6 | @freezed 7 | sealed class ServiceSettings with _$ServiceSettings { 8 | const factory ServiceSettings({ 9 | required String sourceId, 10 | 11 | /// This field save all field: cookie, user agent... 12 | required Map? settings, 13 | 14 | /// Cache user data 15 | required String? userDataCache, 16 | required DateTime createdAt, 17 | required DateTime updatedAt, 18 | }) = _ServiceSettings; 19 | 20 | factory ServiceSettings.fromJson(Map json) => 21 | _$ServiceSettingsFromJson(json); 22 | } 23 | -------------------------------------------------------------------------------- /lib/core_services/comic/interfaces/meta_comic.ts: -------------------------------------------------------------------------------- 1 | import type { OImage } from "../../interfaces/o_image" 2 | import type { Genre } from "../../interfaces/genre" 3 | import type { RateValue } from "./rate_value" 4 | import type { ComicChapter } from "./comic_chapter" 5 | import { StatusEnum } from "../../interfaces/status_enum" 6 | 7 | export interface MetaComic { 8 | name: string 9 | originalName?: string 10 | image: OImage 11 | author?: string 12 | translator?: string 13 | status: StatusEnum 14 | views?: number 15 | likes?: number 16 | rate?: RateValue 17 | genres: Genre[] 18 | description: string 19 | chapters: ComicChapter[] 20 | lastModified: Date 21 | fake?: boolean 22 | offlineMode?: boolean 23 | extra?: string 24 | } 25 | -------------------------------------------------------------------------------- /lib/general_api/models/list_watch_history.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'list_watch_history.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _ListWatchHistory _$ListWatchHistoryFromJson(Map json) => 10 | _ListWatchHistory( 11 | data: 12 | (json['data'] as List) 13 | .map((e) => Data.fromJson(e as Map)) 14 | .toList(), 15 | ); 16 | 17 | Map _$ListWatchHistoryToJson(_ListWatchHistory instance) => 18 | {'data': instance.data}; 19 | -------------------------------------------------------------------------------- /lib/core_services/comic/interfaces/comic_home.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | import '../main.dart'; 4 | 5 | part 'comic_home.freezed.dart'; 6 | part 'comic_home.g.dart'; 7 | 8 | @freezed 9 | sealed class ComicHome with _$ComicHome { 10 | const factory ComicHome({ 11 | ComicCarousel? carousel, 12 | required List categories, 13 | }) = _ComicHome; 14 | 15 | factory ComicHome.fromJson(Map json) => 16 | _$ComicHomeFromJson(json); 17 | 18 | factory ComicHome.createFakeData() { 19 | return ComicHome( 20 | carousel: ComicCarousel.createFakeData(), 21 | categories: List.generate(3, (i) => HomeComicCategory.createFakeData()), 22 | ); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/rust_isolate/unscramble_image_isolate.dart: -------------------------------------------------------------------------------- 1 | import 'dart:isolate'; 2 | import 'dart:typed_data'; 3 | 4 | import 'package:hoyomi/rust/api/image/unscramble_image.dart'; 5 | import 'package:hoyomi/rust/frb_generated.dart'; 6 | 7 | export 'package:hoyomi/rust/api/image/unscramble_image.dart' show Block; 8 | 9 | Future unscrambleImageIsolate({ 10 | required List imageData, 11 | required List blocks, 12 | required bool autoTrim, 13 | }) { 14 | return Isolate.run(() async { 15 | await RustLib.init(); 16 | 17 | final output = unscrambleImageSync( 18 | imageData: imageData, 19 | blocks: blocks, 20 | autoTrim: false, 21 | ); 22 | 23 | RustLib.dispose(); 24 | 25 | return output; 26 | }); 27 | } 28 | -------------------------------------------------------------------------------- /windows/runner/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_UTILS_H_ 2 | #define RUNNER_UTILS_H_ 3 | 4 | #include 5 | #include 6 | 7 | // Creates a console for the process, and redirects stdout and stderr to 8 | // it for both the runner and the Flutter library. 9 | void CreateAndAttachConsole(); 10 | 11 | // Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string 12 | // encoded in UTF-8. Returns an empty std::string on failure. 13 | std::string Utf8FromUtf16(const wchar_t* utf16_string); 14 | 15 | // Gets the command line arguments passed in as a std::vector, 16 | // encoded in UTF-8. Returns an empty std::vector on failure. 17 | std::vector GetCommandLineArguments(); 18 | 19 | #endif // RUNNER_UTILS_H_ 20 | -------------------------------------------------------------------------------- /lib/core_services/comic/interfaces/rate_value.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'rate_value.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _RateValue _$RateValueFromJson(Map json) => _RateValue( 10 | best: (json['best'] as num).toInt(), 11 | count: (json['count'] as num).toInt(), 12 | value: (json['value'] as num).toDouble(), 13 | ); 14 | 15 | Map _$RateValueToJson(_RateValue instance) => 16 | { 17 | 'best': instance.best, 18 | 'count': instance.count, 19 | 'value': instance.value, 20 | }; 21 | -------------------------------------------------------------------------------- /lib/rust/api/image/get_image_size.dart: -------------------------------------------------------------------------------- 1 | // This file is automatically generated, so please do not edit it. 2 | // @generated by `flutter_rust_bridge`@ 2.11.1. 3 | 4 | // ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import 5 | 6 | import '../../frb_generated.dart'; 7 | import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart'; 8 | 9 | // These functions are ignored because they are not marked as `pub`: `read_image_size` 10 | 11 | Future<(int, int)> getImageSize({required List data}) => 12 | RustLib.instance.api.crateApiImageGetImageSizeGetImageSize(data: data); 13 | 14 | (int, int) getImageSizeSync({required List data}) => 15 | RustLib.instance.api.crateApiImageGetImageSizeGetImageSizeSync(data: data); 16 | -------------------------------------------------------------------------------- /lib/general_api/models/eiga_list_watch_history.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'eiga_list_watch_history.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _EigaListWatchHistory _$EigaListWatchHistoryFromJson( 10 | Map json, 11 | ) => _EigaListWatchHistory( 12 | data: 13 | (json['data'] as List) 14 | .map((e) => Data4.fromJson(e as Map)) 15 | .toList(), 16 | ); 17 | 18 | Map _$EigaListWatchHistoryToJson( 19 | _EigaListWatchHistory instance, 20 | ) => {'data': instance.data}; 21 | -------------------------------------------------------------------------------- /lib/general_api/models/set_eiga_follow_response.dart: -------------------------------------------------------------------------------- 1 | // coverage:ignore-file 2 | // GENERATED CODE - DO NOT MODIFY BY HAND 3 | // ignore_for_file: type=lint, unused_import 4 | 5 | import 'package:freezed_annotation/freezed_annotation.dart'; 6 | 7 | part 'set_eiga_follow_response.freezed.dart'; 8 | part 'set_eiga_follow_response.g.dart'; 9 | 10 | /// Indicates if the follow/unfollow operation succeeded. 11 | @Freezed() 12 | abstract class SetEigaFollowResponse with _$SetEigaFollowResponse { 13 | const factory SetEigaFollowResponse({ 14 | /// Whether the operation was successful. 15 | required bool ok, 16 | }) = _SetEigaFollowResponse; 17 | 18 | factory SetEigaFollowResponse.fromJson(Map json) => _$SetEigaFollowResponseFromJson(json); 19 | } 20 | -------------------------------------------------------------------------------- /rust/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rust_lib_hoyomi" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [lib] 7 | crate-type = ["cdylib", "staticlib"] 8 | 9 | [dependencies] 10 | anyhow = "1.0.98" 11 | chrono = "0.4.41" 12 | flutter_rust_bridge = {version = "=2.11.1", features = ["chrono"] } 13 | futures-util = "0.3.31" 14 | image = "0.25.6" 15 | serde = "1.0.219" 16 | tokio = "1.46.1" 17 | wreq-util = "2.2.5" 18 | 19 | [dependencies.wreq] 20 | version = "5.1.0" 21 | default-features = false 22 | features = [ 23 | "charset", 24 | "cookies", 25 | "stream", 26 | "multipart", 27 | "socks", 28 | 29 | # Compression 30 | "brotli", 31 | "gzip", 32 | ] 33 | 34 | [lints.rust] 35 | unexpected_cfgs = { level = "warn", check-cfg = ['cfg(frb_expand)'] } 36 | -------------------------------------------------------------------------------- /lib/core_services/interfaces/setting/setting_field.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'setting_field.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | SettingField _$SettingFieldFromJson(Map json) => SettingField( 10 | name: json['name'] as String, 11 | key: json['key'] as String, 12 | description: json['description'] as String?, 13 | ); 14 | 15 | Map _$SettingFieldToJson(SettingField instance) => 16 | { 17 | 'name': instance.name, 18 | 'key': instance.key, 19 | 'description': instance.description, 20 | }; 21 | -------------------------------------------------------------------------------- /lib/general_api/models/has_follow_response.dart: -------------------------------------------------------------------------------- 1 | // coverage:ignore-file 2 | // GENERATED CODE - DO NOT MODIFY BY HAND 3 | // ignore_for_file: type=lint, unused_import 4 | 5 | import 'package:freezed_annotation/freezed_annotation.dart'; 6 | 7 | part 'has_follow_response.freezed.dart'; 8 | part 'has_follow_response.g.dart'; 9 | 10 | /// Indicates if the user follows the specified comic. 11 | @Freezed() 12 | abstract class HasFollowResponse with _$HasFollowResponse { 13 | const factory HasFollowResponse({ 14 | /// Whether the user is following this comic. 15 | @JsonKey(name: 'has_follow') 16 | required bool hasFollow, 17 | }) = _HasFollowResponse; 18 | 19 | factory HasFollowResponse.fromJson(Map json) => _$HasFollowResponseFromJson(json); 20 | } 21 | -------------------------------------------------------------------------------- /lib/general_api/models/list_watch_page_schema.dart: -------------------------------------------------------------------------------- 1 | // coverage:ignore-file 2 | // GENERATED CODE - DO NOT MODIFY BY HAND 3 | // ignore_for_file: type=lint, unused_import 4 | 5 | import 'package:freezed_annotation/freezed_annotation.dart'; 6 | 7 | import 'data2.dart'; 8 | 9 | part 'list_watch_page_schema.freezed.dart'; 10 | part 'list_watch_page_schema.g.dart'; 11 | 12 | /// A list of watch page entries, each representing the page watched for a specific episode. 13 | @Freezed() 14 | abstract class ListWatchPageSchema with _$ListWatchPageSchema { 15 | const factory ListWatchPageSchema({required List data}) = 16 | _ListWatchPageSchema; 17 | 18 | factory ListWatchPageSchema.fromJson(Map json) => 19 | _$ListWatchPageSchemaFromJson(json); 20 | } 21 | -------------------------------------------------------------------------------- /lib/general_api/models/list_watch_time_schema.dart: -------------------------------------------------------------------------------- 1 | // coverage:ignore-file 2 | // GENERATED CODE - DO NOT MODIFY BY HAND 3 | // ignore_for_file: type=lint, unused_import 4 | 5 | import 'package:freezed_annotation/freezed_annotation.dart'; 6 | 7 | import 'data5.dart'; 8 | 9 | part 'list_watch_time_schema.freezed.dart'; 10 | part 'list_watch_time_schema.g.dart'; 11 | 12 | /// A list of watch time entries, each representing the time watched for a specific episode. 13 | @Freezed() 14 | abstract class ListWatchTimeSchema with _$ListWatchTimeSchema { 15 | const factory ListWatchTimeSchema({required List data}) = 16 | _ListWatchTimeSchema; 17 | 18 | factory ListWatchTimeSchema.fromJson(Map json) => 19 | _$ListWatchTimeSchemaFromJson(json); 20 | } 21 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import Flutter 2 | import UIKit 3 | import app_links 4 | 5 | @main 6 | @objc class AppDelegate: FlutterAppDelegate { 7 | override func application( 8 | _ application: UIApplication, 9 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 10 | ) -> Bool { 11 | GeneratedPluginRegistrant.register(with: self) 12 | 13 | if let url = AppLinks.shared.getLink(launchOptions: launchOptions) { 14 | // We have a link, propagate it to your Flutter app or not 15 | AppLinks.shared.handleLink(url: url) 16 | return true // Returning true will stop the propagation to other packages 17 | } 18 | 19 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /lib/general_api/models/comic_list_watch_history.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'comic_list_watch_history.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _ComicListWatchHistory _$ComicListWatchHistoryFromJson( 10 | Map json, 11 | ) => _ComicListWatchHistory( 12 | data: 13 | (json['data'] as List) 14 | .map((e) => Data.fromJson(e as Map)) 15 | .toList(), 16 | ); 17 | 18 | Map _$ComicListWatchHistoryToJson( 19 | _ComicListWatchHistory instance, 20 | ) => {'data': instance.data}; 21 | -------------------------------------------------------------------------------- /lib/general_api/models/list_watch_page_schema.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'list_watch_page_schema.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _ListWatchPageSchema _$ListWatchPageSchemaFromJson(Map json) => 10 | _ListWatchPageSchema( 11 | data: 12 | (json['data'] as List) 13 | .map((e) => Data2.fromJson(e as Map)) 14 | .toList(), 15 | ); 16 | 17 | Map _$ListWatchPageSchemaToJson( 18 | _ListWatchPageSchema instance, 19 | ) => {'data': instance.data}; 20 | -------------------------------------------------------------------------------- /lib/general_api/models/list_watch_time_schema.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'list_watch_time_schema.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _ListWatchTimeSchema _$ListWatchTimeSchemaFromJson(Map json) => 10 | _ListWatchTimeSchema( 11 | data: 12 | (json['data'] as List) 13 | .map((e) => Data5.fromJson(e as Map)) 14 | .toList(), 15 | ); 16 | 17 | Map _$ListWatchTimeSchemaToJson( 18 | _ListWatchTimeSchema instance, 19 | ) => {'data': instance.data}; 20 | -------------------------------------------------------------------------------- /lib/core_services/eiga/interfaces/watch_time.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'watch_time.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _WatchTime _$WatchTimeFromJson(Map json) => _WatchTime( 10 | position: Duration(microseconds: (json['position'] as num).toInt()), 11 | duration: Duration(microseconds: (json['duration'] as num).toInt()), 12 | ); 13 | 14 | Map _$WatchTimeToJson(_WatchTime instance) => 15 | { 16 | 'position': instance.position.inMicroseconds, 17 | 'duration': instance.duration.inMicroseconds, 18 | }; 19 | -------------------------------------------------------------------------------- /lib/general_api/models/eiga_list_watch_history.dart: -------------------------------------------------------------------------------- 1 | // coverage:ignore-file 2 | // GENERATED CODE - DO NOT MODIFY BY HAND 3 | // ignore_for_file: type=lint, unused_import 4 | 5 | import 'package:freezed_annotation/freezed_annotation.dart'; 6 | 7 | import 'data4.dart'; 8 | 9 | part 'eiga_list_watch_history.freezed.dart'; 10 | part 'eiga_list_watch_history.g.dart'; 11 | 12 | /// An array of watch history items. Schema representing an individual watch history item. 13 | @Freezed() 14 | abstract class EigaListWatchHistory with _$EigaListWatchHistory { 15 | const factory EigaListWatchHistory({required List data}) = 16 | _EigaListWatchHistory; 17 | 18 | factory EigaListWatchHistory.fromJson(Map json) => 19 | _$EigaListWatchHistoryFromJson(json); 20 | } 21 | -------------------------------------------------------------------------------- /lib/rust_isolate/unscramble_image_rows_isolate.dart: -------------------------------------------------------------------------------- 1 | import 'dart:isolate'; 2 | import 'dart:typed_data'; 3 | 4 | import 'package:hoyomi/rust/api/image/unscramble_image_rows.dart'; 5 | import 'package:hoyomi/rust/frb_generated.dart'; 6 | 7 | export 'package:hoyomi/rust/api/image/unscramble_image_rows.dart' show RowBlock; 8 | 9 | Future unscrambleImageRowsIsolate({ 10 | required List imageData, 11 | required List blocks, 12 | required bool autoTrim, 13 | }) { 14 | return Isolate.run(() async { 15 | await RustLib.init(); 16 | 17 | final output = unscrambleImageRowsSync( 18 | imageData: imageData, 19 | blocks: blocks, 20 | autoTrim: false, 21 | ); 22 | 23 | RustLib.dispose(); 24 | 25 | return output; 26 | }); 27 | } 28 | -------------------------------------------------------------------------------- /js_runtime_polyfill/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./polyfill/rust_api/unscramble_image" 2 | export * from "./polyfill/rust_api/unscramble_image_columns" 3 | export * from "./polyfill/rust_api/unscramble_image_rows" 4 | export * from "./polyfill/base64_coder" 5 | export * from "./polyfill/base64" 6 | export * from "./polyfill/dart_send_message" 7 | export * from "./polyfill/ecma" 8 | export * from "./polyfill/fetch" 9 | export * from "./polyfill/headers" 10 | export * from "./polyfill/request" 11 | export * from "./polyfill/response" 12 | export * from "./polyfill/text_decoder" 13 | export * from "./polyfill/text_encoder" 14 | export * from "./polyfill/unimplemented_error" 15 | export * from "./polyfill/url_search_params" 16 | export * from "./polyfill/url" 17 | export * from "./polyfill/xml_http_request" 18 | -------------------------------------------------------------------------------- /lib/general_api/models/comic_list_watch_history.dart: -------------------------------------------------------------------------------- 1 | // coverage:ignore-file 2 | // GENERATED CODE - DO NOT MODIFY BY HAND 3 | // ignore_for_file: type=lint, unused_import 4 | 5 | import 'package:freezed_annotation/freezed_annotation.dart'; 6 | 7 | import 'data.dart'; 8 | 9 | part 'comic_list_watch_history.freezed.dart'; 10 | part 'comic_list_watch_history.g.dart'; 11 | 12 | /// An array of watch history items. Schema representing an individual watch history item. 13 | @Freezed() 14 | abstract class ComicListWatchHistory with _$ComicListWatchHistory { 15 | const factory ComicListWatchHistory({required List data}) = 16 | _ComicListWatchHistory; 17 | 18 | factory ComicListWatchHistory.fromJson(Map json) => 19 | _$ComicListWatchHistoryFromJson(json); 20 | } 21 | -------------------------------------------------------------------------------- /lib/core_services/eiga/interfaces/server_source.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'server_source.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _ServerSource _$ServerSourceFromJson(Map json) => 10 | _ServerSource( 11 | name: json['name'] as String, 12 | serverId: json['serverId'] as String, 13 | extra: json['extra'] as String?, 14 | ); 15 | 16 | Map _$ServerSourceToJson(_ServerSource instance) => 17 | { 18 | 'name': instance.name, 19 | 'serverId': instance.serverId, 20 | 'extra': instance.extra, 21 | }; 22 | -------------------------------------------------------------------------------- /lib/core_services/interfaces/filter.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | part 'filter.freezed.dart'; 4 | part 'filter.g.dart'; 5 | 6 | @freezed 7 | sealed class Option with _$Option { 8 | const factory Option({ 9 | required String name, 10 | required String value, 11 | @Default(false) bool selected, 12 | }) = _Option; 13 | 14 | factory Option.fromJson(Map json) => _$OptionFromJson(json); 15 | } 16 | 17 | @freezed 18 | sealed class Filter with _$Filter { 19 | const factory Filter({ 20 | required String name, 21 | required String key, 22 | required bool multiple, 23 | required List