Repository: wgh136/PicaComic
Branch: master
Commit: 155cf0c6d701
Files: 308
Total size: 3.9 MB
Directory structure:
gitextract_6bcc69h2/
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug.yaml
│ │ ├── enhancement.yaml
│ │ └── other.yaml
│ └── workflows/
│ ├── delete_old_workflows.yml
│ ├── ios_simulator.yml
│ ├── linux.yml
│ └── main.yml
├── .gitignore
├── .metadata
├── LICENSE
├── README.md
├── analysis_options.yaml
├── android/
│ ├── .gitignore
│ ├── app/
│ │ ├── build.gradle
│ │ └── src/
│ │ ├── debug/
│ │ │ └── AndroidManifest.xml
│ │ ├── main/
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── kotlin/
│ │ │ │ └── com/
│ │ │ │ └── kokoiro/
│ │ │ │ └── xyz/
│ │ │ │ └── pica_comic/
│ │ │ │ └── MainActivity.kt
│ │ │ └── res/
│ │ │ ├── drawable/
│ │ │ │ └── launch_background.xml
│ │ │ ├── drawable-v21/
│ │ │ │ └── launch_background.xml
│ │ │ ├── mipmap-anydpi-v26/
│ │ │ │ └── ic_launcher.xml
│ │ │ ├── values/
│ │ │ │ └── styles.xml
│ │ │ └── values-night/
│ │ │ └── styles.xml
│ │ └── profile/
│ │ └── AndroidManifest.xml
│ ├── build.gradle
│ ├── gradle/
│ │ └── wrapper/
│ │ └── gradle-wrapper.properties
│ ├── gradle.properties
│ └── settings.gradle
├── assets/
│ ├── init.js
│ ├── tags.json
│ ├── tags_tw.json
│ └── translation.json
├── debian/
│ ├── build.py
│ ├── debian.yaml
│ └── gui/
│ └── pica-comic.desktop
├── doc/
│ ├── comic_source.md
│ └── hosts.md
├── ios/
│ ├── .gitignore
│ ├── Flutter/
│ │ ├── AppFrameworkInfo.plist
│ │ ├── Debug.xcconfig
│ │ └── Release.xcconfig
│ ├── Podfile
│ ├── Runner/
│ │ ├── AppDelegate.swift
│ │ ├── Assets.xcassets/
│ │ │ ├── AppIcon.appiconset/
│ │ │ │ └── Contents.json
│ │ │ └── LaunchImage.imageset/
│ │ │ ├── Contents.json
│ │ │ └── README.md
│ │ ├── Base.lproj/
│ │ │ ├── LaunchScreen.storyboard
│ │ │ └── Main.storyboard
│ │ ├── Info.plist
│ │ └── Runner-Bridging-Header.h
│ ├── Runner.xcodeproj/
│ │ ├── project.pbxproj
│ │ ├── project.xcworkspace/
│ │ │ ├── contents.xcworkspacedata
│ │ │ └── xcshareddata/
│ │ │ ├── IDEWorkspaceChecks.plist
│ │ │ └── WorkspaceSettings.xcsettings
│ │ └── xcshareddata/
│ │ └── xcschemes/
│ │ └── Runner.xcscheme
│ └── Runner.xcworkspace/
│ ├── contents.xcworkspacedata
│ └── xcshareddata/
│ ├── IDEWorkspaceChecks.plist
│ └── WorkspaceSettings.xcsettings
├── lib/
│ ├── base.dart
│ ├── comic_source/
│ │ ├── app_build_in_category.dart
│ │ ├── app_build_in_favorites.dart
│ │ ├── built_in/
│ │ │ ├── ehentai.dart
│ │ │ ├── hitomi.dart
│ │ │ ├── ht_manga.dart
│ │ │ ├── jm.dart
│ │ │ ├── nhentai.dart
│ │ │ └── picacg.dart
│ │ ├── category.dart
│ │ ├── comic_source.dart
│ │ ├── favorites.dart
│ │ └── parser.dart
│ ├── components/
│ │ ├── animated_image.dart
│ │ ├── appbar.dart
│ │ ├── avatar.dart
│ │ ├── button.dart
│ │ ├── comic_tile.dart
│ │ ├── comics_list.dart
│ │ ├── comment.dart
│ │ ├── components.dart
│ │ ├── consts.dart
│ │ ├── custom_slider.dart
│ │ ├── flyout.dart
│ │ ├── layout.dart
│ │ ├── loading.dart
│ │ ├── menu.dart
│ │ ├── message.dart
│ │ ├── navigation_bar.dart
│ │ ├── pop_up_widget.dart
│ │ ├── scroll.dart
│ │ ├── scrollable_list/
│ │ │ ├── scrollable_positioned_list.dart
│ │ │ └── src/
│ │ │ ├── element_registry.dart
│ │ │ ├── item_positions_listener.dart
│ │ │ ├── item_positions_notifier.dart
│ │ │ ├── positioned_list.dart
│ │ │ ├── post_mount_callback.dart
│ │ │ ├── scroll_view.dart
│ │ │ ├── scrollable_positioned_list.dart
│ │ │ ├── viewport.dart
│ │ │ └── wrapping.dart
│ │ ├── select.dart
│ │ ├── select_download_eps.dart
│ │ ├── side_bar.dart
│ │ └── window_frame.dart
│ ├── foundation/
│ │ ├── app.dart
│ │ ├── app_page_route.dart
│ │ ├── cache_manager.dart
│ │ ├── def.dart
│ │ ├── history.dart
│ │ ├── image_favorites.dart
│ │ ├── image_loader/
│ │ │ ├── base_image_provider.dart
│ │ │ ├── cached_image.dart
│ │ │ ├── file_image_loader.dart
│ │ │ ├── image_recombine.dart
│ │ │ └── stream_image_provider.dart
│ │ ├── image_manager.dart
│ │ ├── js_engine.dart
│ │ ├── local_favorites.dart
│ │ ├── log.dart
│ │ ├── pair.dart
│ │ ├── stack.dart
│ │ ├── state_controller.dart
│ │ ├── ui_mode.dart
│ │ └── widget_utils.dart
│ ├── init.dart
│ ├── main.dart
│ ├── network/
│ │ ├── app_dio.dart
│ │ ├── base_comic.dart
│ │ ├── cache_network.dart
│ │ ├── cloudflare.dart
│ │ ├── cookie_jar.dart
│ │ ├── custom_download_model.dart
│ │ ├── download.dart
│ │ ├── download_model.dart
│ │ ├── eh_network/
│ │ │ ├── eh_download_model.dart
│ │ │ ├── eh_main_network.dart
│ │ │ ├── eh_models.dart
│ │ │ └── get_gallery_id.dart
│ │ ├── favorite_download.dart
│ │ ├── file_downloader.dart
│ │ ├── hitomi_network/
│ │ │ ├── fetch_data.dart
│ │ │ ├── hitomi_download_model.dart
│ │ │ ├── hitomi_main_network.dart
│ │ │ ├── hitomi_models.dart
│ │ │ ├── image.dart
│ │ │ └── search.dart
│ │ ├── htmanga_network/
│ │ │ ├── ht_download_model.dart
│ │ │ ├── htmanga_main_network.dart
│ │ │ └── models.dart
│ │ ├── http_client.dart
│ │ ├── http_proxy.dart
│ │ ├── jm_network/
│ │ │ ├── headers.dart
│ │ │ ├── jm_download.dart
│ │ │ ├── jm_image.dart
│ │ │ ├── jm_models.dart
│ │ │ └── jm_network.dart
│ │ ├── net_fav_to_local.dart
│ │ ├── nhentai_network/
│ │ │ ├── download.dart
│ │ │ ├── login.dart
│ │ │ ├── models.dart
│ │ │ ├── nhentai_main_network.dart
│ │ │ └── tags.dart
│ │ ├── picacg_network/
│ │ │ ├── headers.dart
│ │ │ ├── methods.dart
│ │ │ ├── models.dart
│ │ │ └── picacg_download_model.dart
│ │ ├── res.dart
│ │ ├── update.dart
│ │ └── webdav.dart
│ ├── pages/
│ │ ├── accounts_page.dart
│ │ ├── auth_page.dart
│ │ ├── category_comics_page.dart
│ │ ├── category_page.dart
│ │ ├── comic_page.dart
│ │ ├── download_page.dart
│ │ ├── downloading_page.dart
│ │ ├── ehentai/
│ │ │ ├── accounts.dart
│ │ │ ├── eh_comments_page.dart
│ │ │ ├── eh_gallery_page.dart
│ │ │ ├── eh_login_page.dart
│ │ │ ├── eh_user_cookie_parser.dart
│ │ │ └── subscription.dart
│ │ ├── explore_page.dart
│ │ ├── favorites/
│ │ │ ├── local_favorites.dart
│ │ │ ├── local_search_page.dart
│ │ │ ├── main_favorites_page.dart
│ │ │ ├── network_favorite_page.dart
│ │ │ └── network_to_local.dart
│ │ ├── history_page.dart
│ │ ├── hitomi/
│ │ │ ├── hitomi_comic_page.dart
│ │ │ ├── hitomi_home_page.dart
│ │ │ └── hitomi_search.dart
│ │ ├── htmanga/
│ │ │ └── ht_comic_page.dart
│ │ ├── image_favorites.dart
│ │ ├── jm/
│ │ │ ├── jm_comic_page.dart
│ │ │ ├── jm_comments_page.dart
│ │ │ └── week_recommendation_page.dart
│ │ ├── logs_page.dart
│ │ ├── main_page.dart
│ │ ├── me_page.dart
│ │ ├── nhentai/
│ │ │ ├── comic_page.dart
│ │ │ └── comments.dart
│ │ ├── picacg/
│ │ │ ├── collections_page.dart
│ │ │ ├── comic_page.dart
│ │ │ └── comments_page.dart
│ │ ├── pre_search_page.dart
│ │ ├── ranking_page.dart
│ │ ├── reader/
│ │ │ ├── comic_reading_page.dart
│ │ │ ├── eps_view.dart
│ │ │ ├── image.dart
│ │ │ ├── image_view.dart
│ │ │ ├── reading_data.dart
│ │ │ ├── reading_logic.dart
│ │ │ ├── reading_settings.dart
│ │ │ ├── reading_type.dart
│ │ │ ├── tool_bar.dart
│ │ │ └── touch_control.dart
│ │ ├── search_result_page.dart
│ │ ├── settings/
│ │ │ ├── app_settings.dart
│ │ │ ├── blocking_keyword_page.dart
│ │ │ ├── comic_source_settings.dart
│ │ │ ├── components.dart
│ │ │ ├── eh_settings.dart
│ │ │ ├── explore_settings.dart
│ │ │ ├── ht_settings.dart
│ │ │ ├── jm_settings.dart
│ │ │ ├── local_favorite_settings.dart
│ │ │ ├── multi_pages_filter.dart
│ │ │ ├── network_setting.dart
│ │ │ ├── picacg_settings.dart
│ │ │ ├── reading_settings.dart
│ │ │ └── settings_page.dart
│ │ ├── show_image_page.dart
│ │ ├── tools.dart
│ │ ├── webview.dart
│ │ └── welcome_page.dart
│ └── tools/
│ ├── app_links.dart
│ ├── background_service.dart
│ ├── block_screenshot.dart
│ ├── cache_auto_clear.dart
│ ├── debug.dart
│ ├── extensions.dart
│ ├── file_type.dart
│ ├── io_extensions.dart
│ ├── io_tools.dart
│ ├── js.dart
│ ├── keep_screen_on.dart
│ ├── key_down_event.dart
│ ├── mouse_listener.dart
│ ├── notification.dart
│ ├── pdf.dart
│ ├── save_image.dart
│ ├── tags_translation.dart
│ ├── time.dart
│ └── translations.dart
├── linux/
│ ├── .gitignore
│ ├── CMakeLists.txt
│ ├── flutter/
│ │ ├── CMakeLists.txt
│ │ ├── generated_plugin_registrant.cc
│ │ ├── generated_plugin_registrant.h
│ │ └── generated_plugins.cmake
│ ├── main.cc
│ ├── my_application.cc
│ └── my_application.h
├── macos/
│ ├── .gitignore
│ ├── Flutter/
│ │ ├── Flutter-Debug.xcconfig
│ │ └── Flutter-Release.xcconfig
│ ├── Podfile
│ ├── Runner/
│ │ ├── AppDelegate.swift
│ │ ├── Assets.xcassets/
│ │ │ └── AppIcon.appiconset/
│ │ │ └── Contents.json
│ │ ├── Base.lproj/
│ │ │ └── MainMenu.xib
│ │ ├── Configs/
│ │ │ ├── AppInfo.xcconfig
│ │ │ ├── Debug.xcconfig
│ │ │ ├── Release.xcconfig
│ │ │ └── Warnings.xcconfig
│ │ ├── DebugProfile.entitlements
│ │ ├── Info.plist
│ │ ├── MainFlutterWindow.swift
│ │ └── Release.entitlements
│ ├── Runner.xcodeproj/
│ │ ├── project.pbxproj
│ │ ├── project.xcworkspace/
│ │ │ └── xcshareddata/
│ │ │ └── IDEWorkspaceChecks.plist
│ │ └── xcshareddata/
│ │ └── xcschemes/
│ │ └── Runner.xcscheme
│ └── Runner.xcworkspace/
│ ├── contents.xcworkspacedata
│ └── xcshareddata/
│ └── IDEWorkspaceChecks.plist
├── pubspec.yaml
├── test/
│ └── widget_test.dart
├── utils/
│ ├── check_translation.dart
│ └── tags_translation.dart
├── web/
│ ├── index.html
│ └── manifest.json
└── windows/
├── .gitignore
├── CMakeLists.txt
├── build.iss
├── build_windows.py
├── flutter/
│ ├── CMakeLists.txt
│ ├── generated_plugin_registrant.cc
│ ├── generated_plugin_registrant.h
│ └── generated_plugins.cmake
└── runner/
├── CMakeLists.txt
├── RCa13944
├── Runner.rc
├── flutter_window.cpp
├── flutter_window.h
├── main.cpp
├── resource.h
├── runner.exe.manifest
├── utils.cpp
├── utils.h
├── win32_window.cpp
└── win32_window.h
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/ISSUE_TEMPLATE/bug.yaml
================================================
name: 报告Bug/Report a bug
description: 报告APP出现的问题/Reporting problems with the APP
title: "[Bug]: "
labels: ["bug🐞"]
body:
- type: markdown
attributes:
value: |
感谢报告问题, 请先补全标题后填写以下信息.
Thank you for reporting a problem, please complete the title and fill in the following information.
- type: textarea
id: what-happened
attributes:
label: 描述/Description
description: 描述问题/Describe the problem
validations:
required: true
- type: input
id: version
attributes:
label: Version
description: |
使用的APP版本/App version
非最新版本请尝试更新/Please try to update if it is not the latest version
validations:
required: true
- type: dropdown
id: platform
attributes:
label: 使用的操作系统/Operating system
multiple: true
options:
- Android
- iOS
- Windows
- macOS
- other
validations:
required: true
- type: textarea
id: logs
attributes:
label: 日志/logs
description: 上传日志, 在设置-logs中, 点击右上角的菜单后, 点击导出日志; 或者将错误相关日志粘贴到这里
- type: textarea
id: screenshotOrVideo
attributes:
label: 截图或视频/Screenshot or video
description: 在这里上传相关的屏幕截图或者视频/Upload relevant screenshots or videos here
================================================
FILE: .github/ISSUE_TEMPLATE/enhancement.yaml
================================================
name: 功能建议/Feature Request
description: 提出改进APP的建议/Suggest improvements to the APP
title: "[Enhancement]: "
labels: ["enhancement🚀"]
body:
- type: markdown
attributes:
value: |
欢迎提出功能建议, 请先补全标题后填写以下信息.
Welcome to make a feature request, please fill in the following information after completing the title.
- type: textarea
id: what-happened
attributes:
label: 描述/Description
description: 描述具体的建议/Describe your suggestion.
validations:
required: true
- type: dropdown
id: platform
attributes:
label: 操作系统/Operating System
description: 如果建议针对某个平台, 请在此选择/If the feature is for a particular platform, please select here
multiple: true
options:
- Android
- iOS
- Windows
- macOS
- other
validations:
required: false
- type: textarea
id: screenshotOrVideo
attributes:
label: 图片/picture
description: 如果需要图片描述, 请在这里上传/If you need a picture description, please upload it here.
================================================
FILE: .github/ISSUE_TEMPLATE/other.yaml
================================================
name: 其它/other
description: 其它内容/Other contents
body:
- type: markdown
attributes:
value: |
如果你想报告App运行时出现的问题(无法查看某个漫画源, 无法登录, 某个功能无法使用等情况), 请切换到报告Bug模板;
如果你想提出功能建议或者优化建议, 请切换到功能建议模板;
对于其它情况, 填写并提交此处的内容.
If you wish to report issues occurring during the app's runtime (such as the inability to view a particular comic source, login issues, non-functional features, etc.), please switch to the Bug Report template.
If you would like to make feature requests or optimization suggestions, please switch to the Feature Request template.
For any other situations, please fill out and submit the content here.
- type: textarea
id: what-happened
attributes:
label: Content
validations:
required: true
================================================
FILE: .github/workflows/delete_old_workflows.yml
================================================
name: Delete old workflow runs
on:
workflow_dispatch:
inputs:
days:
description: 'Days-worth of runs to keep for each workflow'
required: true
default: '30'
minimum_runs:
description: 'Minimum runs to keep for each workflow'
required: true
default: '6'
delete_workflow_pattern:
description: 'Name or filename of the workflow (if not set, all workflows are targeted)'
required: false
delete_workflow_by_state_pattern:
description: 'Filter workflows by state: active, deleted, disabled_fork, disabled_inactivity, disabled_manually'
required: true
default: "ALL"
type: choice
options:
- "ALL"
- active
- deleted
- disabled_inactivity
- disabled_manually
delete_run_by_conclusion_pattern:
description: 'Remove runs based on conclusion: action_required, cancelled, failure, skipped, success'
required: true
default: "ALL"
type: choice
options:
- "ALL"
- "Unsuccessful: action_required,cancelled,failure,skipped"
- action_required
- cancelled
- failure
- skipped
- success
dry_run:
description: 'Logs simulated changes, no deletions are performed'
required: false
jobs:
del_runs:
runs-on: ubuntu-latest
permissions:
actions: write
contents: read
steps:
- name: Delete workflow runs
uses: Mattraks/delete-workflow-runs@v2
with:
token: ${{ github.token }}
repository: ${{ github.repository }}
retain_days: ${{ github.event.inputs.days }}
keep_minimum_runs: ${{ github.event.inputs.minimum_runs }}
delete_workflow_pattern: ${{ github.event.inputs.delete_workflow_pattern }}
delete_workflow_by_state_pattern: ${{ github.event.inputs.delete_workflow_by_state_pattern }}
delete_run_by_conclusion_pattern: >-
${{
startsWith(github.event.inputs.delete_run_by_conclusion_pattern, 'Unsuccessful:')
&& 'action_required,cancelled,failure,skipped'
|| github.event.inputs.delete_run_by_conclusion_pattern
}}
dry_run: ${{ github.event.inputs.dry_run }}
================================================
FILE: .github/workflows/ios_simulator.yml
================================================
name: Build IOS SIMULATOR
run-name: Build IOS SIMULATOR
on:
workflow_dispatch: {}
jobs:
Build_IOS_SIMULATOR:
runs-on: macos-13
steps:
- uses: actions/checkout@v3
- uses: subosito/flutter-action@v2
with:
channel: 'stable'
architecture: x64
- run: sudo xcode-select --switch /Applications/Xcode_14.3.1.app
- run: flutter pub get
- run: flutter build ios --simulator --no-codesign
- uses: actions/upload-artifact@v3
with:
name: build_files
path: /Users/runner/work/PicaComic/PicaComic/build/ios/iphonesimulator
================================================
FILE: .github/workflows/linux.yml
================================================
name: Build Linux
run-name: Build Linux
on:
workflow_dispatch: {}
jobs:
Build_Linux:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: subosito/flutter-action@v2
with:
channel: 'stable'
architecture: x64
- run: |
sudo apt-get update -y
sudo apt-get install -y ninja-build libgtk-3-dev webkit2gtk-4.1
dart pub global activate flutter_to_debian
- run: python3 debian/build.py
- run: dart run flutter_to_arch
- run: |
sudo rm -rf build/linux/arch/app.tar.gz
sudo rm -rf build/linux/arch/pkg
sudo rm -rf build/linux/arch/src
sudo rm -rf build/linux/arch/PKGBUILD
- uses: actions/upload-artifact@v4
with:
name: deb_build
path: build/linux/x64/release/debian
- uses: actions/upload-artifact@v4
with:
name: arch_build
path: build/linux/arch/
================================================
FILE: .github/workflows/main.yml
================================================
name: Build IOS
run-name: Build IOS
on:
workflow_dispatch: {}
jobs:
Build_IOS:
runs-on: macos-13
steps:
- uses: actions/checkout@v3
- uses: subosito/flutter-action@v2
with:
channel: 'stable'
architecture: x64
- run: sudo xcode-select --switch /Applications/Xcode_14.3.1.app
- run: flutter pub get
- run: flutter build ios --release --no-codesign
- run: |
mkdir -p /Users/runner/work/PicaComic/PicaComic/build/ios/iphoneos/Payload
mv /Users/runner/work/PicaComic/PicaComic/build/ios/iphoneos/Runner.app /Users/runner/work/PicaComic/PicaComic/build/ios/iphoneos/Payload
cd /Users/runner/work/PicaComic/PicaComic/build/ios/iphoneos/
zip -r app-ios.ipa Payload
- uses: actions/upload-artifact@v4
with:
name: app-ios.ipa
path: /Users/runner/work/PicaComic/PicaComic/build/ios/iphoneos/app-ios.ipa
Build_MacOS:
runs-on: macos-13
steps:
- uses: actions/checkout@v3
- uses: subosito/flutter-action@v2
with:
channel: 'stable'
architecture: x64
- run: sudo xcode-select --switch /Applications/Xcode_14.3.1.app
- run: flutter pub get
- run: flutter build macos --release
- uses: actions/upload-artifact@v4
with:
name: macos-build.zip
path: build/macos/Build/Products/Release/pica_comic.app
================================================
FILE: .gitignore
================================================
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
migrate_working_dir/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
**/doc/api/
**/ios/Flutter/.last_build_id
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
.packages
.pub-cache/
.pub/
/build/
# Symbolication related
app.*.symbols
# Obfuscation related
app.*.map.json
# Android Studio will place build artifacts here
/android/app/debug
/android/app/profile
/android/app/release
.vscode/
================================================
FILE: .metadata
================================================
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled.
version:
revision: 9944297138845a94256f1cf37beb88ff9a8e811a
channel: stable
project_type: app
# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: 9944297138845a94256f1cf37beb88ff9a8e811a
base_revision: 9944297138845a94256f1cf37beb88ff9a8e811a
- platform: android
create_revision: 9944297138845a94256f1cf37beb88ff9a8e811a
base_revision: 9944297138845a94256f1cf37beb88ff9a8e811a
- platform: ios
create_revision: 9944297138845a94256f1cf37beb88ff9a8e811a
base_revision: 9944297138845a94256f1cf37beb88ff9a8e811a
- platform: linux
create_revision: 9944297138845a94256f1cf37beb88ff9a8e811a
base_revision: 9944297138845a94256f1cf37beb88ff9a8e811a
- platform: macos
create_revision: 9944297138845a94256f1cf37beb88ff9a8e811a
base_revision: 9944297138845a94256f1cf37beb88ff9a8e811a
- platform: web
create_revision: 9944297138845a94256f1cf37beb88ff9a8e811a
base_revision: 9944297138845a94256f1cf37beb88ff9a8e811a
- platform: windows
create_revision: 9944297138845a94256f1cf37beb88ff9a8e811a
base_revision: 9944297138845a94256f1cf37beb88ff9a8e811a
# User provided section
# List of Local paths (relative to this file) that should be
# ignored by the migrate tool.
#
# Files that are not part of the templates will be ignored by default.
unmanaged_files:
- 'lib/main.dart'
- 'ios/Runner.xcodeproj/project.pbxproj'
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2023 Nyne
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
Deprecated, use new project: [venera](https://github.com/venera-app/venera).
# Pica Comic
[](https://flutter.dev/)
[](https://github.com/wgh136/PicaComic/blob/master/LICENSE)
[](https://github.com/wgh136/PicaComic/releases)
[](https://github.com/wgh136/PicaComic/stargazers)
A comic app with multiple sources built with flutter.
## How to use
1. Clone the repository
```shell
git clone https://github.com/wgh136/PicaComic
```
2. Install flutter: https://docs.flutter.dev/get-started/install
3. Build Application: https://docs.flutter.dev/deployment
## Introduction
### Built-in Comic Source
Currently, Pica Comic has 5 built-in comic sources:
- picacg
- e-hentai/exhentai
- jmcomic
- hitomi
- 绅士漫画
- nhentai
### Custom Comic Source
You can add custom comic sources in the app after version 3.0.0.
### Features
- Browse manga
- Online reading
- Download manga
- Manage local favorites and network favorites
- Data sync(using webdav)
- Reading history
### History
This project initially started as an unofficial app for picacg
and later evolved into an app that supports multiple comic sources.
## Build From Source Code
See [https://docs.flutter.dev/](https://docs.flutter.dev/)
## Thanks
### Projects
[](https://github.com/tonquer/JMComic-qt)
The image restructuring algorithm used to display jm images is from this project.
### Tags Translation
[](https://github.com/EhTagTranslation/Database)
The Chinese translation of the manga tags is from this project.
================================================
FILE: analysis_options.yaml
================================================
# This file configures the analyzer, which statically analyzes Dart code to
# check for errors, warnings, and lints.
#
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
# invoked from the command line by running `flutter analyze`.
# The following line activates a set of recommended lints for Flutter apps,
# packages, and plugins designed to encourage good coding practices.
include: package:flutter_lints/flutter.yaml
linter:
# The lint rules applied to this project can be customized in the
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
# included above or to enable additional rules. A list of all available lints
# and their documentation is published at
# https://dart-lang.github.io/linter/lints/index.html.
#
# Instead of disabling a lint rule for the entire project in the
# section below, it can also be suppressed for a single line of code
# or a specific dart file by using the `// ignore: name_of_lint` and
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
# producing the lint.
rules:
# avoid_print: false # Uncomment to disable the `avoid_print` rule
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options
================================================
FILE: android/.gitignore
================================================
gradle-wrapper.jar
/.gradle
/captures/
/gradlew
/gradlew.bat
/local.properties
GeneratedPluginRegistrant.java
# Remember to never publicly share your keystore.
# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
key.properties
**/*.keystore
**/*.jks
================================================
FILE: android/app/build.gradle
================================================
plugins {
id "com.android.application"
id "kotlin-android"
id "dev.flutter.flutter-gradle-plugin"
}
def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
localPropertiesFile.withReader('UTF-8') { reader ->
localProperties.load(reader)
}
}
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
flutterVersionCode = '1'
}
def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {
flutterVersionName = '1.0'
}
def keystorePropertiesFile = rootProject.file("key.properties")
def keystoreProperties = new Properties()
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
android {
compileSdk 34
ndkVersion flutter.ndkVersion
splits{
abi {
enable true
universalApk true
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
defaultConfig {
applicationId "com.github.wgh136.pica_comic"
// You can update the following values to match your application needs.
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
minSdkVersion 21
targetSdk 34
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
multiDexEnabled true
}
signingConfigs {
debug {
storeFile file(keystoreProperties['storeFile'])
storePassword keystoreProperties['storePassword']
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
}
release {
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
storeFile file(keystoreProperties['storeFile'])
storePassword keystoreProperties['storePassword']
}
}
buildTypes {
release {
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig signingConfigs.release
applicationVariants.all { variant ->
variant.outputs.all { output ->
def abi = output.getFilter(com.android.build.OutputFile.ABI)
if (abi != null) { // null for the universal-debug, universal-release variants
outputFileName = "PicaComic-${variant.versionName}-${abi}.apk"
} else {
outputFileName = "PicaComic-${variant.versionName}.apk"
}
}
}
}
}
namespace 'com.kokoiro.xyz.pica_comic'
}
flutter {
source '../..'
}
dependencies {
implementation 'com.google.android.gms:play-services-base:18.3.0'
}
================================================
FILE: android/app/src/debug/AndroidManifest.xml
================================================
================================================
FILE: android/app/src/main/AndroidManifest.xml
================================================
================================================
FILE: android/app/src/main/kotlin/com/kokoiro/xyz/pica_comic/MainActivity.kt
================================================
package com.kokoiro.xyz.pica_comic
import android.os.Build
import android.view.KeyEvent
import android.view.WindowManager
import io.flutter.embedding.android.FlutterFragmentActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.EventChannel
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugins.GeneratedPluginRegistrant
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Environment
import android.Manifest
import androidx.core.content.ContextCompat
import com.google.android.gms.common.GoogleApiAvailability
class MainActivity: FlutterFragmentActivity() {
var volumeListen = VolumeListen()
var listening = false
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
GeneratedPluginRegistrant.registerWith(flutterEngine)
val channel = EventChannel(flutterEngine.dartExecutor.binaryMessenger, "com.kokoiro.xyz.pica_comic/volume")
channel.setStreamHandler(
object : EventChannel.StreamHandler {
override fun onListen(arguments: Any?, events: EventChannel.EventSink) {
listening = true
volumeListen.whenUp = {
events.success(1)
}
volumeListen.whenDown = {
events.success(2)
}
}
override fun onCancel(arguments: Any?) {
listening = false
}
})
//拦截屏幕截图
MethodChannel(flutterEngine.dartExecutor.binaryMessenger,"com.kokoiro.xyz.pica_comic/screenshot").setMethodCallHandler{
_, _ ->
window.setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE)
}
MethodChannel(flutterEngine.dartExecutor.binaryMessenger,"com.kokoiro.xyz.pica_comic/secure").setMethodCallHandler{
_, _ ->
window.setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE)
}
//获取cpu架构
MethodChannel(flutterEngine.dartExecutor.binaryMessenger,"com.kokoiro.xyz.pica_comic/device").setMethodCallHandler{
_, res ->
res.success(getDeviceInfo())
}
//获取http代理
MethodChannel(flutterEngine.dartExecutor.binaryMessenger,"kokoiro.xyz.pica_comic/proxy").setMethodCallHandler{
_, res ->
res.success(getProxy())
}
//保持屏幕常亮
MethodChannel(flutterEngine.dartExecutor.binaryMessenger,"com.kokoiro.xyz.pica_comic/keepScreenOn").setMethodCallHandler{
call, _ ->
if(call.method == "set")
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
else
window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
}
MethodChannel(flutterEngine.dartExecutor.binaryMessenger,"pica_comic/playServer").setMethodCallHandler{
_, res ->
val flag = GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(this) == com.google.android.gms.common.ConnectionResult.SUCCESS
res.success(flag)
}
MethodChannel(flutterEngine.dartExecutor.binaryMessenger,"pica_comic/settings").setMethodCallHandler{
call, res ->
if(call.method == "link") {
val intent = Intent(
android.provider.Settings.ACTION_APP_OPEN_BY_DEFAULT_SETTINGS,
Uri.parse("package:com.github.wgh136.pica_comic"),
)
startActivity(intent)
res.success(null)
} else if(call.method == "files") {
val intent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
Intent(android.provider.Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION)
} else {
Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
}
intent.data = Uri.parse("package:com.github.wgh136.pica_comic")
startActivity(intent)
res.success(null)
} else if(call.method == "files_check") {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
res.success(Environment.isExternalStorageManager())
} else {
res.success(
ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED
&& ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED)
}
}
}
}
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
if(listening){
when (keyCode) {
KeyEvent.KEYCODE_VOLUME_DOWN -> {
volumeListen.down()
return true
}
KeyEvent.KEYCODE_VOLUME_UP -> {
volumeListen.up()
return true
}
}
}
return super.onKeyDown(keyCode, event)
}
private fun getDeviceInfo(): String{
//获取cpu架构从而找到应当下载的app版本
return Build.SUPPORTED_ABIS[0]
}
private fun getProxy(): String{
val host = System.getProperty("http.proxyHost")
val port = System.getProperty("http.proxyPort")
return if(host!=null&&port!=null){
"$host:$port"
}else{
"No Proxy"
}
}
}
class VolumeListen{
var whenUp = fun() {}
var whenDown = fun() {}
fun up(){
whenUp()
}
fun down(){
whenDown()
}
}
================================================
FILE: android/app/src/main/res/drawable/launch_background.xml
================================================
================================================
FILE: android/app/src/main/res/drawable-v21/launch_background.xml
================================================
================================================
FILE: android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
================================================
================================================
FILE: android/app/src/main/res/values/styles.xml
================================================
================================================
FILE: android/app/src/main/res/values-night/styles.xml
================================================
================================================
FILE: android/app/src/profile/AndroidManifest.xml
================================================
================================================
FILE: android/build.gradle
================================================
import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile
allprojects {
tasks.withType(AbstractKotlinCompile).configureEach {
kotlinOptions {
jvmTarget = "1.8"
apiVersion = "1.8"
languageVersion = "1.8"
}
}
repositories {
google()
mavenCentral()
}
}
rootProject.buildDir = '../build'
subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}"
}
subprojects {
project.evaluationDependsOn(':app')
}
tasks.register("clean", Delete) {
delete rootProject.buildDir
}
================================================
FILE: android/gradle/wrapper/gradle-wrapper.properties
================================================
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-all.zip
================================================
FILE: android/gradle.properties
================================================
org.gradle.jvmargs=-Xmx1536M
android.useAndroidX=true
android.enableJetifier=true
android.defaults.buildfeatures.buildconfig=true
android.nonTransitiveRClass=false
android.nonFinalResIds=false
================================================
FILE: android/settings.gradle
================================================
pluginManagement {
def flutterSdkPath = {
def properties = new Properties()
file("local.properties").withInputStream { properties.load(it) }
def flutterSdkPath = properties.getProperty("flutter.sdk")
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
return flutterSdkPath
}
settings.ext.flutterSdkPath = flutterSdkPath()
includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle")
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
id "com.android.application" version "8.1.1" apply false
id "org.jetbrains.kotlin.android" version "1.9.0" apply false
}
include ":app"
================================================
FILE: assets/init.js
================================================
/// encode, decode, hash, decrypt
let Convert = {
/**
* @param {ArrayBuffer} value
* @returns {string}
*/
encodeBase64: (value) => {
return sendMessage({
method: "convert",
type: "base64",
value: value,
isEncode: true
});
},
/**
* @param {string} value
* @returns {ArrayBuffer}
*/
decodeBase64: (value) => {
return sendMessage({
method: "convert",
type: "base64",
value: value,
isEncode: false
});
},
/**
* @param {ArrayBuffer} value
* @returns {ArrayBuffer}
*/
md5: (value) => {
return sendMessage({
method: "convert",
type: "md5",
value: value,
isEncode: true
});
},
/**
* @param {ArrayBuffer} value
* @returns {ArrayBuffer}
*/
sha1: (value) => {
return sendMessage({
method: "convert",
type: "sha1",
value: value,
isEncode: true
});
},
/**
* @param {ArrayBuffer} value
* @returns {ArrayBuffer}
*/
sha256: (value) => {
return sendMessage({
method: "convert",
type: "sha256",
value: value,
isEncode: true
});
},
/**
* @param {ArrayBuffer} value
* @returns {ArrayBuffer}
*/
sha512: (value) => {
return sendMessage({
method: "convert",
type: "sha512",
value: value,
isEncode: true
});
},
/**
* @param {ArrayBuffer} value
* @param {ArrayBuffer} key
* @returns {ArrayBuffer}
*/
decryptAesEcb: (value, key) => {
return sendMessage({
method: "convert",
type: "aes-ecb",
value: value,
key: key,
isEncode: false
});
},
/**
* @param {ArrayBuffer} value
* @param {ArrayBuffer} key
* @param {ArrayBuffer} iv
* @returns {ArrayBuffer}
*/
decryptAesCbc: (value, key, iv) => {
return sendMessage({
method: "convert",
type: "aes-ecb",
value: value,
key: key,
iv: iv,
isEncode: false
});
},
/**
* @param {ArrayBuffer} value
* @param {ArrayBuffer} key
* @param {number} blockSize
* @returns {ArrayBuffer}
*/
decryptAesCfb: (value, key, blockSize) => {
return sendMessage({
method: "convert",
type: "aes-cfb",
value: value,
key: key,
blockSize: blockSize,
isEncode: false
});
},
/**
* @param {ArrayBuffer} value
* @param {ArrayBuffer} key
* @param {number} blockSize
* @returns {ArrayBuffer}
*/
decryptAesOfb: (value, key, blockSize) => {
return sendMessage({
method: "convert",
type: "aes-ofb",
value: value,
key: key,
blockSize: blockSize,
isEncode: false
});
},
/**
* @param {ArrayBuffer} value
* @param {ArrayBuffer} key
* @returns {ArrayBuffer}
*/
decryptRsa: (value, key) => {
return sendMessage({
method: "convert",
type: "rsa",
value: value,
key: key,
isEncode: false
});
}
}
function randomInt(min, max) {
return sendMessage({
method: 'random',
min: min,
max: max
});
}
class _Timer {
delay = 0;
callback = () => { };
status = false;
constructor(delay, callback) {
this.delay = delay;
this.callback = callback;
}
run() {
this.status = true;
this._interval();
}
_interval() {
if (!this.status) {
return;
}
this.callback();
setTimeout(this._interval.bind(this), this.delay);
}
cancel() {
this.status = false;
}
}
function setInterval(callback, delay) {
let timer = new _Timer(delay, callback);
timer.run();
return timer;
}
function Cookie(name, value, domain = null) {
let obj = {};
obj.name = name;
obj.value = value;
if (domain) {
obj.domain = domain;
}
return obj;
}
/**
* Network object for sending HTTP requests and managing cookies.
* @namespace Network
*/
let Network = {
/**
* Sends an HTTP request.
* @param {string} method - The HTTP method (e.g., GET, POST, PUT, PATCH, DELETE).
* @param {string} url - The URL to send the request to.
* @param {Object} headers - The headers to include in the request.
* @param data - The data to send with the request.
* @returns {Promise} The response from the request.
*/
async fetchBytes(method, url, headers, data) {
let result = await sendMessage({
method: 'http',
http_method: method,
bytes: true,
url: url,
headers: headers,
data: data,
});
if (result.error) {
throw result.error;
}
return result;
},
/**
* Sends an HTTP request.
* @param {string} method - The HTTP method (e.g., GET, POST, PUT, PATCH, DELETE).
* @param {string} url - The URL to send the request to.
* @param {Object} headers - The headers to include in the request.
* @param data - The data to send with the request.
* @returns {Promise