Compare commits
229 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 22df7e3074 | |||
| 1d5323a477 | |||
| f8d73819ea | |||
| cbe26b7291 | |||
| 93477f60c2 | |||
| 9feebbfe17 | |||
| 6e365491a9 | |||
| 2e58658129 | |||
| 256c564b91 | |||
| 96b50f52ec | |||
| 3167d8aa15 | |||
| 78fd09c728 | |||
| 4c5598cedf | |||
| c3347d94ab | |||
| 7ca4aa75a8 | |||
| 226fad5594 | |||
| d0ee1ba5af | |||
| 439e0c8284 | |||
| 7d079a8728 | |||
| 945a52653e | |||
| bdafc86990 | |||
| 57d425ab9f | |||
| 395ac8e944 | |||
| 2f801e7571 | |||
| d7636045fe | |||
| b745f10870 | |||
| d76849942c | |||
| b7a8a3ffe8 | |||
| 95d9293fe0 | |||
| 3be165a551 | |||
| cb498e2128 | |||
| 973f4d66e2 | |||
| 2c80672f6e | |||
| 2599813ef1 | |||
| 86f849a185 | |||
| 875f1f1506 | |||
| e418375963 | |||
| d528fc7f9e | |||
| 633ea97848 | |||
| 36cb899b91 | |||
| c4d849d6a3 | |||
| c8bd39b4bf | |||
| 733ba16af2 | |||
| 37f57c0c55 | |||
| 013dbd79b4 | |||
| f76d0b3258 | |||
| c2f7cdd72e | |||
| 01c37cb0ba | |||
| 0dd0af1b84 | |||
| 26aa684300 | |||
| 9669cdfb76 | |||
| 2c5e5e283e | |||
| 303921c6ea | |||
| 593291a60f | |||
| 3af8e395bd | |||
| 0b192cfa52 | |||
| fb8f20f31a | |||
| e53386cf72 | |||
| b0e9b9b307 | |||
| 789678a45d | |||
| 4ad01d3451 | |||
| 6a87daa0b3 | |||
| aebef87076 | |||
| 32ff58598f | |||
| 2d56cbe227 | |||
| fbef5f592b | |||
| 090af36f5e | |||
| 885f27fcf1 | |||
| 8dc4eaf77a | |||
| a55bef08a8 | |||
| 0dc3089739 | |||
| 789ef0d783 | |||
| 092db1106d | |||
| 5f4b5bc570 | |||
| 32581fcd5a | |||
| b14d28c406 | |||
| 1d1535dc55 | |||
| de942440e3 | |||
| b58fc39cf1 | |||
| 2e3af25dd4 | |||
| 1d541a30ae | |||
| f926714544 | |||
| f68849d3a5 | |||
| 2111232f42 | |||
| 088552bf56 | |||
| 3eabbc9770 | |||
| 8e3b8df497 | |||
| 06a5aaaa72 | |||
| 97c4f14094 | |||
| 1fa7f18235 | |||
| b309d2fd4a | |||
| 372b56bb1b | |||
| 3325a36cae | |||
| 38673bbff4 | |||
| fb51834153 | |||
| 3a932a1e8a | |||
| 53c61bcb17 | |||
| 6951b4b20d | |||
| 746f9f1a11 | |||
| 9a7344ccbe | |||
| ab2fb8747f | |||
| 9cd8cb3d54 | |||
| ba1c2845b6 | |||
| 065aa19e9e | |||
| 4c2a05c3a6 | |||
| fd45c0740c | |||
| e44bf920fa | |||
| 8a327b2dff | |||
| 6ece7e2596 | |||
| 2e2ce98be3 | |||
| fe4c2392db | |||
| 320a0971b4 | |||
| bfb70b6a05 | |||
| a68af62748 | |||
| 52bd5ce5cc | |||
| b93d486348 | |||
| d193c58e5f | |||
| 6ac2a61793 | |||
| a45c6f2197 | |||
| 71d639bf19 | |||
| 9a51472726 | |||
| 0670f298cd | |||
| aa1e98544b | |||
| fa4607e232 | |||
| cb46420c09 | |||
| d88014fa90 | |||
| 168b76cb0c | |||
| f5680c6d69 | |||
| 654a3cc7ed | |||
| 841cdc474f | |||
| 0adbea3a43 | |||
| e12bada052 | |||
| 68dbefc46f | |||
| 9d71e9b177 | |||
| 5b5801c2cf | |||
| df1cc2b8e9 | |||
| 18d399b3f7 | |||
| e9687fd182 | |||
| 79137a074c | |||
| dae55ca386 | |||
| b7f040d89a | |||
| dc69df9f4f | |||
| 6c1fbfa63b | |||
| e968a2195a | |||
| 000bcea181 | |||
| 8edf508453 | |||
| bc9cc50130 | |||
| 71091d88fc | |||
| 70c1d7e21f | |||
| c07920978e | |||
| 954b2919ac | |||
| c630f731ed | |||
| aaefa7f74e | |||
| 2ec6b471f1 | |||
| a5c5ab68d2 | |||
| fb045c501a | |||
| 6a6e411492 | |||
| 76aac330fc | |||
| 07bdf31f66 | |||
| fe14928af6 | |||
| ad0c1033a4 | |||
| 0c2448fb99 | |||
| cedda145a5 | |||
| ee73187f1a | |||
| 89f91d6800 | |||
| 6714827694 | |||
| aad73f7d19 | |||
| c5985de1c3 | |||
| 86a5b0879a | |||
| 414972d545 | |||
| 9a74ae5844 | |||
| 301980ab14 | |||
| 5dced82e5a | |||
| 9a1e4df408 | |||
| 5b08b81239 | |||
| 7fac538ba3 | |||
| ef6be74ec2 | |||
| 9f49587245 | |||
| b7b733f351 | |||
| 06bfc33e72 | |||
| fbcd55d6c5 | |||
| 2484b5f14b | |||
| 6982659658 | |||
| 9e006166a8 | |||
| 25a62e33a1 | |||
| d05ed0a56c | |||
| eaffb2755c | |||
| e0fcae2ae3 | |||
| af9ad61174 | |||
| 7c54ad54fc | |||
| aee9f1032c | |||
| f7d0605e0a | |||
| f40dcafb43 | |||
| d9cb54b285 | |||
| f738a162d3 | |||
| fda4cd6783 | |||
| ecd1604e25 | |||
| 0f061900af | |||
| c47f5ea85e | |||
| 306eb0e3c7 | |||
| e64025ded8 | |||
| c1fe2da636 | |||
| ff23f58a4f | |||
| fc2f5ffdf9 | |||
| 6dd9ed7fb0 | |||
| 2f362abb91 | |||
| 96807a64cf | |||
| 7df5f1c4c4 | |||
| cf1ede9cf7 | |||
| 729385588a | |||
| 668d5cf8f0 | |||
| 72b1b5b0f9 | |||
| fbf726c174 | |||
| c441eed847 | |||
| e8e83ed49c | |||
| cdc21b067c | |||
| 48e19f7914 | |||
| 89dd570b30 | |||
| 16474d4328 | |||
| 9db612bf03 | |||
| 7d92dbc5c0 | |||
| a9efca8687 | |||
| dbfea5d02b | |||
| a6b05c4a27 | |||
| 6d539d3404 | |||
| b2aff1efc9 | |||
| 8a20a1ef50 | |||
| 33cbfa9751 | |||
| b95a8d44d4 |
@@ -1,44 +0,0 @@
|
||||
---
|
||||
name: "🐞 Bug report"
|
||||
title: "[Bug] <short description>"
|
||||
about: "Report a bug"
|
||||
labels: "bug"
|
||||
---
|
||||
|
||||
**PLEASE READ THIS**
|
||||
|
||||
I acknowledge that:
|
||||
|
||||
- I have updated to the latest version of the app.
|
||||
- I have tried the troubleshooting guide described in `README.md`
|
||||
- If this is a request for adding/changing an extension it should be brought up to your extension repo.
|
||||
- If this is an issue with some extension not working properly, It does work inside Tachiyomi as intended.
|
||||
- I have searched the existing issues and this is a new ticket **NOT** a duplicate or related to another open issue
|
||||
- I will fill out the title and the information in this template
|
||||
|
||||
Note that the issue will be automatically closed if you do not fill out the title or requested information.
|
||||
|
||||
**DELETE THIS SECTION IF YOU HAVE READ AND ACKNOWLEDGED IT**
|
||||
|
||||
---
|
||||
|
||||
## Device information
|
||||
- Suwayomi-Server version: (Example: v1.0.0-r1438-win32)
|
||||
- Server Operating System: (Example: Ubuntu 20.04)
|
||||
- Server Desktop Environment: N/A or (Example: Gnome 40)
|
||||
- Server JVM version: bundled with win32 or (Example: Java 8 Update 281 or OpenJDK 8u281)
|
||||
- Client Operating System: <usually the same as above Server Operating System>
|
||||
- Client Web Browser: (Example: Google Chrome 89.0.4389.82)
|
||||
|
||||
## Steps to reproduce
|
||||
1. First Step
|
||||
2. Second Step
|
||||
|
||||
### Expected behavior
|
||||
Describe what should have happened. Remove this line after you are done.
|
||||
|
||||
### Actual behavior
|
||||
Describe what happens instead. Remove this line after you are done.
|
||||
|
||||
## Other details
|
||||
Describe additional details If necessary. Remove this line after you are done.
|
||||
@@ -0,0 +1,144 @@
|
||||
name: 🐞 Bug report
|
||||
description: Report a bug in Suwayomi-Server
|
||||
labels: [bug]
|
||||
body:
|
||||
|
||||
- type: textarea
|
||||
id: reproduce-steps
|
||||
attributes:
|
||||
label: Steps to reproduce
|
||||
description: Provide an example of the issue.
|
||||
placeholder: |
|
||||
Example:
|
||||
1. First step
|
||||
2. Second step
|
||||
3. Issue here
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: expected-behavior
|
||||
attributes:
|
||||
label: Expected behavior
|
||||
description: Explain what you should expect to happen.
|
||||
placeholder: |
|
||||
Example: "This should happen..."
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: actual-behavior
|
||||
attributes:
|
||||
label: Actual behavior
|
||||
description: Explain what actually happens.
|
||||
placeholder: |
|
||||
Example: "This happened instead..."
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: suwayomi-server-version
|
||||
attributes:
|
||||
label: Suwayomi-Server version
|
||||
description: You can find your Suwayomi-Server version in **More → About**.
|
||||
placeholder: |
|
||||
Example: "v2.0.1727"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: server-os
|
||||
attributes:
|
||||
label: Server operating system
|
||||
description: The operating system on which Suwayomi-Server is running on
|
||||
placeholder: |
|
||||
Example: "Windows 11 Pro 24H2 | Ubuntu 24.04.2 LTS"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: server-desktop-environment
|
||||
attributes:
|
||||
label: Server Desktop Environment
|
||||
description:
|
||||
placeholder: |
|
||||
Example: "Gnome 40"
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: input
|
||||
id: server-jvm-version
|
||||
attributes:
|
||||
label: Server JVM version
|
||||
description: The java version used to run Suwayomi-Server
|
||||
placeholder: |
|
||||
Example: "openjdk 21.0.5 2024-10-15 LTS"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: client-name
|
||||
attributes:
|
||||
label: Used client name
|
||||
description:
|
||||
placeholder: |
|
||||
Example: "Suwayomi-WebUI"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: client-version
|
||||
attributes:
|
||||
label: Client version
|
||||
description:
|
||||
placeholder: |
|
||||
Example: "v1.2.3"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: client-browser
|
||||
attributes:
|
||||
label: Used web browser
|
||||
description: The browser which is used to open Suwayomi-WebUI
|
||||
placeholder: |
|
||||
Example: "Chrome 134.0.6998.118 (64-Bit) | FireFox 136.0.2 (64-Bit) | Electron v35.0.2"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: client-os
|
||||
attributes:
|
||||
label: Client operating system
|
||||
description: The system on which the Suwayomi-WebUI is running on
|
||||
placeholder: |
|
||||
Example: "Windows 11 Pro 24H2 | Ubuntu 24.04.2 LTS"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: other-details
|
||||
attributes:
|
||||
label: Other details
|
||||
description: The more information that gets provided the better, especially via videos and images
|
||||
placeholder: |
|
||||
Additional details and attachments.
|
||||
|
||||
- type: checkboxes
|
||||
id: acknowledgements
|
||||
attributes:
|
||||
label: Acknowledgements
|
||||
description: Read this carefully, we will close and ignore your issue if you skimmed through this.
|
||||
options:
|
||||
- label: I have searched the existing issues and this is a new ticket, **NOT** a duplicate or related to another open or closed issue.
|
||||
required: true
|
||||
- label: I have written a short but informative title (ideally less than ~100 characters).
|
||||
required: true
|
||||
- label: I have tried the troubleshooting guide described in [README.md](https://github.com/Suwayomi/Suwayomi-Server?tab=readme-ov-file#troubleshooting-and-support)
|
||||
required: true
|
||||
- label: I have updated to the **[latest version](https://github.com/suwayomi/suwayomi-server/releases/latest)**.
|
||||
required: true
|
||||
- label: I have filled out all of the requested information in this form, including specific version numbers.
|
||||
required: true
|
||||
- label: I understand that **Suwayomi does not have or fix any extensions**, and I **will not receive help** for any issues related to sources or extensions.
|
||||
required: true
|
||||
@@ -1 +1,5 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: ☎️ Support
|
||||
url: https://discord.gg/DDZdqZWaHA
|
||||
about: Join our discord to get help for anything that is not a bug or a feature request
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
---
|
||||
name: "🌟 Feature request"
|
||||
title: "[Feature Request] <short description>"
|
||||
about: "Suggest a feature to improve the project"
|
||||
labels: "enhancement"
|
||||
---
|
||||
|
||||
**PLEASE READ THIS**
|
||||
|
||||
I acknowledge that:
|
||||
|
||||
- I have updated to the latest version of the app.
|
||||
- I have tried the troubleshooting guide described in `README.md`
|
||||
- If this is a request for adding/changing an extension it should be brought up to your extension repo.
|
||||
- If this is an issue with some extension not working properly, It does work in Tachiyomi application as intended.
|
||||
- I have searched the existing issues and this is a new ticket **NOT** a duplicate or related to another open issue
|
||||
- I will fill out the title and the information in this template
|
||||
|
||||
Note that the issue will be automatically closed if you do not fill out the title or requested information.
|
||||
|
||||
**DELETE THIS SECTION IF YOU HAVE READ AND ACKNOWLEDGED IT**
|
||||
|
||||
---
|
||||
|
||||
## What feature should be added to Suwayomi?
|
||||
Explain What the feature is and how it should work in detail. Remove this line after you are done.
|
||||
|
||||
## Why/Project's Benefit/Existing Problem
|
||||
Explain why this should be added. Remove this line after you are done.
|
||||
@@ -0,0 +1,37 @@
|
||||
name: 🌟 Feature request
|
||||
description: Suggest a feature to improve Suwayomi-Server
|
||||
labels: [enhancement]
|
||||
body:
|
||||
|
||||
- type: textarea
|
||||
id: feature-description
|
||||
attributes:
|
||||
label: Describe your suggested feature
|
||||
description: How can Suwayomi-Server be improved?
|
||||
placeholder: |
|
||||
Example:
|
||||
"It should work like this..."
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: other-details
|
||||
attributes:
|
||||
label: Other details
|
||||
placeholder: |
|
||||
Additional details and attachments.
|
||||
|
||||
- type: checkboxes
|
||||
id: acknowledgements
|
||||
attributes:
|
||||
label: Acknowledgements
|
||||
description: Read this carefully, we will close and ignore your issue if you skimmed through this.
|
||||
options:
|
||||
- label: I have searched the existing issues and this is a new ticket, **NOT** a duplicate or related to another open or closed issue.
|
||||
required: true
|
||||
- label: I have written a short but informative title (ideally less than ~100 characters).
|
||||
required: true
|
||||
- label: I have updated to the **[latest version](https://github.com/suwayomi/suwayomi-server/releases/latest)**.
|
||||
required: true
|
||||
- label: I have filled out all of the requested information in this form, including specific version numbers.
|
||||
required: true
|
||||
@@ -17,7 +17,7 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Validate Gradle Wrapper
|
||||
uses: gradle/wrapper-validation-action@v1
|
||||
uses: gradle/wrapper-validation-action@v3
|
||||
|
||||
build:
|
||||
name: Build pull request
|
||||
@@ -32,12 +32,15 @@ jobs:
|
||||
path: master
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up JDK 1.8
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: 8
|
||||
java-version: 21
|
||||
distribution: 'temurin'
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@v4
|
||||
|
||||
- name: Copy CI gradle.properties
|
||||
run: |
|
||||
cd master
|
||||
@@ -45,8 +48,6 @@ jobs:
|
||||
cp .github/runner-files/ci-gradle.properties ~/.gradle/gradle.properties
|
||||
|
||||
- name: Build Jar
|
||||
uses: gradle/gradle-build-action@v2
|
||||
with:
|
||||
build-root-directory: master
|
||||
arguments: ktlintCheck :server:shadowJar --stacktrace
|
||||
working-directory: master
|
||||
run: ./gradlew ktlintCheck :server:shadowJar --stacktrace
|
||||
|
||||
|
||||
@@ -15,10 +15,10 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Clone repo
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Validate Gradle Wrapper
|
||||
uses: gradle/wrapper-validation-action@v1
|
||||
uses: gradle/wrapper-validation-action@v3
|
||||
|
||||
build:
|
||||
name: Build Jar
|
||||
@@ -32,12 +32,15 @@ jobs:
|
||||
path: master
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up JDK 1.8
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: 8
|
||||
java-version: 21
|
||||
distribution: 'temurin'
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@v4
|
||||
|
||||
- name: Copy CI gradle.properties
|
||||
run: |
|
||||
cd master
|
||||
@@ -45,22 +48,20 @@ jobs:
|
||||
cp .github/runner-files/ci-gradle.properties ~/.gradle/gradle.properties
|
||||
|
||||
- name: Build Jar
|
||||
uses: gradle/gradle-build-action@v2
|
||||
env:
|
||||
ProductBuildType: "Preview"
|
||||
with:
|
||||
build-root-directory: master
|
||||
arguments: :server:shadowJar --stacktrace
|
||||
working-directory: master
|
||||
run: ./gradlew :server:shadowJar --stacktrace
|
||||
|
||||
- name: Upload Jar
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: jar
|
||||
path: master/server/build/*.jar
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Upload icons
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: icon
|
||||
path: master/server/src/main/resources/icon
|
||||
@@ -70,12 +71,43 @@ jobs:
|
||||
run: tar -cvzf scripts.tar.gz -C master/ scripts/
|
||||
|
||||
- name: Upload scripts.tar.gz
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: scripts
|
||||
path: scripts.tar.gz
|
||||
if-no-files-found: error
|
||||
|
||||
jlink:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
name: linux-x64
|
||||
- os: windows-latest
|
||||
name: windows-x64
|
||||
- os: macos-14
|
||||
name: macOS-arm64
|
||||
- os: macos-13
|
||||
name: macOS-x64
|
||||
os: [ubuntu-latest, windows-latest, macos-14, macos-13]
|
||||
|
||||
steps:
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: 21
|
||||
distribution: 'temurin'
|
||||
|
||||
- name: Package JDK
|
||||
run: jlink --add-modules java.base,java.compiler,java.datatransfer,java.desktop,java.instrument,java.logging,java.management,java.naming,java.prefs,java.scripting,java.se,java.security.jgss,java.security.sasl,java.sql,java.transaction.xa,java.xml,jdk.attach,jdk.crypto.ec,jdk.jdi,jdk.management,jdk.net,jdk.random,jdk.unsupported,jdk.unsupported.desktop,jdk.zipfs --output suwa --strip-debug --no-man-pages --no-header-files --compress=2
|
||||
|
||||
- name: Upload JRE package
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ matrix.name }}-jre
|
||||
path: suwa
|
||||
|
||||
bundle:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -87,26 +119,32 @@ jobs:
|
||||
- macOS-x64
|
||||
- macOS-arm64
|
||||
- windows-x64
|
||||
- windows-x86
|
||||
|
||||
name: Make ${{ matrix.os }} release
|
||||
needs: build
|
||||
needs: [build,jlink]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Download Jar
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: jar
|
||||
path: server/build
|
||||
|
||||
- name: Download JRE
|
||||
uses: actions/download-artifact@v4
|
||||
if: matrix.os != 'linux-assets' && matrix.os != 'debian-all'
|
||||
with:
|
||||
name: ${{ matrix.os }}-jre
|
||||
path: jre
|
||||
|
||||
- name: Download icons
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: icon
|
||||
path: server/src/main/resources/icon
|
||||
|
||||
- name: Download scripts.tar.gz
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: scripts
|
||||
|
||||
@@ -117,7 +155,7 @@ jobs:
|
||||
scripts/bundler.sh -o upload/ ${{ matrix.os }}
|
||||
|
||||
- name: Upload ${{ matrix.os }} release
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ matrix.os }}
|
||||
path: upload/*
|
||||
@@ -127,41 +165,37 @@ jobs:
|
||||
needs: bundle
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/download-artifact@v3
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: jar
|
||||
path: release
|
||||
- uses: actions/download-artifact@v3
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: debian-all
|
||||
path: release
|
||||
- uses: actions/download-artifact@v3
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: linux-assets
|
||||
path: release
|
||||
- uses: actions/download-artifact@v3
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: linux-x64
|
||||
path: release
|
||||
- uses: actions/download-artifact@v3
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: macOS-x64
|
||||
path: release
|
||||
- uses: actions/download-artifact@v3
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: macOS-arm64
|
||||
path: release
|
||||
- uses: actions/download-artifact@v3
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: windows-x64
|
||||
path: release
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: windows-x86
|
||||
path: release
|
||||
|
||||
- name: Checkout Preview branch
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: "Suwayomi/Suwayomi-Server-preview"
|
||||
ref: main
|
||||
@@ -193,7 +227,7 @@ jobs:
|
||||
git push origin $TAG
|
||||
|
||||
- name: Upload Preview Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
token: ${{ secrets.DEPLOY_PREVIEW_TOKEN }}
|
||||
repository: "Suwayomi/Suwayomi-Server-preview"
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
name: Issue moderator
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [opened, edited, reopened]
|
||||
issue_comment:
|
||||
types: [created]
|
||||
|
||||
jobs:
|
||||
autoclose:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Moderate issues
|
||||
uses: tachiyomiorg/issue-moderator-action@v1
|
||||
with:
|
||||
repo-token: ${{ github.token }}
|
||||
duplicate-check-enabled: true
|
||||
duplicate-check-label: Source request
|
||||
existing-check-enabled: true
|
||||
existing-check-label: Source request
|
||||
auto-close-rules: |
|
||||
[
|
||||
{
|
||||
"type": "title",
|
||||
"regex": ".*<short description>.*",
|
||||
"message": "You did not fill out the description in the title"
|
||||
},
|
||||
{
|
||||
"type": "title",
|
||||
"regex": ".*(<|>)+.*",
|
||||
"message": "You did not remove Angle brackets(< and >) from the title"
|
||||
},
|
||||
{
|
||||
"type": "body",
|
||||
"regex": ".*DELETE THIS SECTION IF YOU HAVE READ AND ACKNOWLEDGED IT.*",
|
||||
"message": "The acknowledgment section was not removed"
|
||||
},
|
||||
{
|
||||
"type": "body",
|
||||
"regex": ".*(Suwayomi-Server version|Server Operating System|Server Desktop Environment|Server JVM version|Client Operating System|Client Web Browser):.*(\\(Example:|<usually).*",
|
||||
"message": "The requested information was not filled out"
|
||||
},
|
||||
{
|
||||
"type": "body",
|
||||
"regex": ".*Remove this line after you are done.*",
|
||||
"message": "The lines requesting to be removed were not removed."
|
||||
}
|
||||
]
|
||||
@@ -19,7 +19,7 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Validate Gradle Wrapper
|
||||
uses: gradle/wrapper-validation-action@v1
|
||||
uses: gradle/wrapper-validation-action@v3
|
||||
|
||||
build:
|
||||
name: Build Jar
|
||||
@@ -33,12 +33,15 @@ jobs:
|
||||
path: master
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up JDK 1.8
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: 8
|
||||
java-version: 21
|
||||
distribution: 'temurin'
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@v4
|
||||
|
||||
- name: Copy CI gradle.properties
|
||||
run: |
|
||||
cd master
|
||||
@@ -47,12 +50,10 @@ jobs:
|
||||
~/.gradle/gradle.properties
|
||||
|
||||
- name: Build and copy webUI, Build Jar
|
||||
uses: gradle/gradle-build-action@v2
|
||||
env:
|
||||
ProductBuildType: "Stable"
|
||||
with:
|
||||
build-root-directory: master
|
||||
arguments: :server:downloadWebUI :server:shadowJar --stacktrace
|
||||
working-directory: master
|
||||
run: ./gradlew :server:downloadWebUI :server:shadowJar --stacktrace
|
||||
|
||||
- name: Upload Jar
|
||||
uses: actions/upload-artifact@v4
|
||||
@@ -78,6 +79,37 @@ jobs:
|
||||
path: scripts.tar.gz
|
||||
if-no-files-found: error
|
||||
|
||||
jlink:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
name: linux-x64
|
||||
- os: windows-latest
|
||||
name: windows-x64
|
||||
- os: macos-14
|
||||
name: macOS-arm64
|
||||
- os: macos-13
|
||||
name: macOS-x64
|
||||
os: [ubuntu-latest, windows-latest, macos-14, macos-13]
|
||||
|
||||
steps:
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: 21
|
||||
distribution: 'temurin'
|
||||
|
||||
- name: Package JDK
|
||||
run: jlink --add-modules java.base,java.compiler,java.datatransfer,java.desktop,java.instrument,java.logging,java.management,java.naming,java.prefs,java.scripting,java.se,java.security.jgss,java.security.sasl,java.sql,java.transaction.xa,java.xml,jdk.attach,jdk.crypto.ec,jdk.jdi,jdk.management,jdk.net,jdk.random,jdk.unsupported,jdk.unsupported.desktop,jdk.zipfs --output suwa --strip-debug --no-man-pages --no-header-files --compress=2
|
||||
|
||||
- name: Upload JDK package
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ matrix.name }}-jre
|
||||
path: suwa
|
||||
|
||||
bundle:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -89,10 +121,9 @@ jobs:
|
||||
- macOS-x64
|
||||
- macOS-arm64
|
||||
- windows-x64
|
||||
- windows-x86
|
||||
|
||||
name: Make ${{ matrix.os }} release
|
||||
needs: build
|
||||
needs: [build, jlink]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Download Jar
|
||||
@@ -101,6 +132,13 @@ jobs:
|
||||
name: jar
|
||||
path: server/build
|
||||
|
||||
- name: Download JRE
|
||||
uses: actions/download-artifact@v4
|
||||
if: matrix.os != 'linux-assets' && matrix.os != 'debian-all'
|
||||
with:
|
||||
name: ${{ matrix.os }}-jre
|
||||
path: jre
|
||||
|
||||
- name: Download icons
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
@@ -158,16 +196,12 @@ jobs:
|
||||
with:
|
||||
name: windows-x64
|
||||
path: release
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: windows-x86
|
||||
path: release
|
||||
|
||||
- name: Generate checksums
|
||||
run: cd release && sha256sum * > Checksums.sha256
|
||||
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
token: ${{ secrets.DEPLOY_RELEASE_TOKEN }}
|
||||
draft: true
|
||||
|
||||
@@ -5,6 +5,7 @@ gradle.properties
|
||||
.fleet
|
||||
# But we need these
|
||||
!.idea/runConfigurations
|
||||
.kotlin
|
||||
|
||||
# Ignore Gradle build output directory
|
||||
build
|
||||
|
||||
@@ -1,7 +1,19 @@
|
||||
plugins {
|
||||
id(libs.plugins.kotlin.jvm.get().pluginId)
|
||||
id(libs.plugins.kotlin.serialization.get().pluginId)
|
||||
id(libs.plugins.ktlint.get().pluginId)
|
||||
id(
|
||||
libs.plugins.kotlin.jvm
|
||||
.get()
|
||||
.pluginId,
|
||||
)
|
||||
id(
|
||||
libs.plugins.kotlin.serialization
|
||||
.get()
|
||||
.pluginId,
|
||||
)
|
||||
id(
|
||||
libs.plugins.ktlint
|
||||
.get()
|
||||
.pluginId,
|
||||
)
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
package xyz.nulldev.ts.config
|
||||
|
||||
import org.kodein.di.DI
|
||||
import org.kodein.di.bind
|
||||
import org.kodein.di.singleton
|
||||
|
||||
class ConfigKodeinModule {
|
||||
fun create() =
|
||||
DI.Module("ConfigManager") {
|
||||
// Config module
|
||||
bind<ConfigManager>() with singleton { GlobalConfigManager }
|
||||
}
|
||||
}
|
||||
@@ -14,9 +14,9 @@ import com.typesafe.config.ConfigValue
|
||||
import com.typesafe.config.ConfigValueFactory
|
||||
import com.typesafe.config.parser.ConfigDocument
|
||||
import com.typesafe.config.parser.ConfigDocumentFactory
|
||||
import io.github.oshai.kotlinlogging.KotlinLogging
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import mu.KotlinLogging
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
@@ -47,11 +47,10 @@ open class ConfigManager {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun <T : ConfigModule> module(type: Class<T>): T = loadedModules[type] as T
|
||||
|
||||
private fun getUserConfig(): Config {
|
||||
return userConfigFile.let {
|
||||
private fun getUserConfig(): Config =
|
||||
userConfigFile.let {
|
||||
ConfigFactory.parseFile(it)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load configs
|
||||
@@ -72,7 +71,8 @@ open class ConfigManager {
|
||||
val userConfig = getUserConfig()
|
||||
|
||||
val config =
|
||||
ConfigFactory.empty()
|
||||
ConfigFactory
|
||||
.empty()
|
||||
.withFallback(baseConfig)
|
||||
.withFallback(userConfig)
|
||||
.withFallback(compatConfig)
|
||||
@@ -153,11 +153,13 @@ open class ConfigManager {
|
||||
}
|
||||
|
||||
var newUserConfigDoc: ConfigDocument = resetUserConfig(false)
|
||||
userConfig.entrySet().filter {
|
||||
serverConfig.hasPath(
|
||||
it.key,
|
||||
)
|
||||
}.forEach { newUserConfigDoc = newUserConfigDoc.withValue(it.key, it.value) }
|
||||
userConfig
|
||||
.entrySet()
|
||||
.filter {
|
||||
serverConfig.hasPath(
|
||||
it.key,
|
||||
)
|
||||
}.forEach { newUserConfigDoc = newUserConfigDoc.withValue(it.key, it.value) }
|
||||
|
||||
userConfigFile.writeText(newUserConfigDoc.render())
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package xyz.nulldev.ts.config
|
||||
|
||||
import org.koin.core.module.Module
|
||||
import org.koin.dsl.module
|
||||
|
||||
fun configManagerModule(): Module =
|
||||
module {
|
||||
single<ConfigManager> { GlobalConfigManager }
|
||||
}
|
||||
@@ -17,17 +17,25 @@ import kotlin.reflect.KProperty
|
||||
* Abstract config module.
|
||||
*/
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
abstract class ConfigModule(getConfig: () -> Config)
|
||||
abstract class ConfigModule(
|
||||
getConfig: () -> Config,
|
||||
)
|
||||
|
||||
/**
|
||||
* Abstract jvm-commandline-argument-overridable config module.
|
||||
*/
|
||||
abstract class SystemPropertyOverridableConfigModule(getConfig: () -> Config, moduleName: String) : ConfigModule(getConfig) {
|
||||
abstract class SystemPropertyOverridableConfigModule(
|
||||
getConfig: () -> Config,
|
||||
moduleName: String,
|
||||
) : ConfigModule(getConfig) {
|
||||
val overridableConfig = SystemPropertyOverrideDelegate(getConfig, moduleName)
|
||||
}
|
||||
|
||||
/** Defines a config property that is overridable with jvm `-D` commandline arguments prefixed with [CONFIG_PREFIX] */
|
||||
class SystemPropertyOverrideDelegate(val getConfig: () -> Config, val moduleName: String) {
|
||||
class SystemPropertyOverrideDelegate(
|
||||
val getConfig: () -> Config,
|
||||
val moduleName: String,
|
||||
) {
|
||||
inline operator fun <R, reified T> getValue(
|
||||
thisRef: R,
|
||||
property: KProperty<*>,
|
||||
|
||||
@@ -15,13 +15,29 @@ import ch.qos.logback.core.rolling.RollingFileAppender
|
||||
import ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy
|
||||
import ch.qos.logback.core.util.FileSize
|
||||
import com.typesafe.config.Config
|
||||
import mu.KotlinLogging
|
||||
import io.github.oshai.kotlinlogging.DelegatingKLogger
|
||||
import io.github.oshai.kotlinlogging.KotlinLogging
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
|
||||
private fun fileSizeValueOfOrDefault(
|
||||
fileSizeStr: String,
|
||||
default: String,
|
||||
): FileSize =
|
||||
try {
|
||||
FileSize.valueOf(fileSizeStr)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
FileSize.valueOf(default)
|
||||
}
|
||||
|
||||
private const val FILE_APPENDER_NAME = "SuwayomiDefaultAppender"
|
||||
|
||||
private fun createRollingFileAppender(
|
||||
logContext: LoggerContext,
|
||||
logDirPath: String,
|
||||
maxFiles: Int,
|
||||
maxFileSize: String,
|
||||
maxTotalSize: String,
|
||||
): RollingFileAppender<ILoggingEvent> {
|
||||
val logFilename = "application"
|
||||
|
||||
@@ -34,7 +50,7 @@ private fun createRollingFileAppender(
|
||||
|
||||
val appender =
|
||||
RollingFileAppender<ILoggingEvent>().apply {
|
||||
name = "FILE"
|
||||
name = FILE_APPENDER_NAME
|
||||
context = logContext
|
||||
encoder = logEncoder
|
||||
file = "$logDirPath/$logFilename.log"
|
||||
@@ -45,9 +61,9 @@ private fun createRollingFileAppender(
|
||||
context = logContext
|
||||
setParent(appender)
|
||||
fileNamePattern = "$logDirPath/${logFilename}_%d{yyyy-MM-dd}_%i.log.gz"
|
||||
setMaxFileSize(FileSize.valueOf("10mb"))
|
||||
maxHistory = 14
|
||||
setTotalSizeCap(FileSize.valueOf("1gb"))
|
||||
maxHistory = maxFiles.coerceAtLeast(0)
|
||||
setMaxFileSize(fileSizeValueOfOrDefault(maxFileSize, "10mb"))
|
||||
setTotalSizeCap(fileSizeValueOfOrDefault(maxTotalSize, "100mb"))
|
||||
start()
|
||||
}
|
||||
|
||||
@@ -57,25 +73,52 @@ private fun createRollingFileAppender(
|
||||
return appender
|
||||
}
|
||||
|
||||
private fun getBaseLogger(): ch.qos.logback.classic.Logger {
|
||||
return (KotlinLogging.logger(Logger.ROOT_LOGGER_NAME).underlyingLogger as ch.qos.logback.classic.Logger)
|
||||
}
|
||||
private fun getBaseLogger(): ch.qos.logback.classic.Logger =
|
||||
((KotlinLogging.logger(Logger.ROOT_LOGGER_NAME) as DelegatingKLogger<*>).underlyingLogger as ch.qos.logback.classic.Logger)
|
||||
|
||||
private fun getLogger(name: String): ch.qos.logback.classic.Logger {
|
||||
val context = LoggerFactory.getILoggerFactory() as LoggerContext
|
||||
return context.getLogger(name)
|
||||
}
|
||||
|
||||
fun initLoggerConfig(appRootPath: String) {
|
||||
fun initLoggerConfig(
|
||||
appRootPath: String,
|
||||
maxFiles: Int,
|
||||
maxFileSize: String,
|
||||
maxTotalSize: String,
|
||||
) {
|
||||
val context = LoggerFactory.getILoggerFactory() as LoggerContext
|
||||
val logger = getBaseLogger()
|
||||
|
||||
// logback logs to the console by default (at least when adding a console appender logs in the console are duplicated)
|
||||
logger.addAppender(createRollingFileAppender(context, "$appRootPath/logs"))
|
||||
logger.addAppender(createRollingFileAppender(context, "$appRootPath/logs", maxFiles, maxFileSize, maxTotalSize))
|
||||
|
||||
// set "kotlin exposed" log level
|
||||
setLogLevelFor("Exposed", Level.ERROR)
|
||||
}
|
||||
|
||||
fun updateFileAppender(
|
||||
maxFiles: Int,
|
||||
maxFileSize: String,
|
||||
maxTotalSize: String,
|
||||
) {
|
||||
val logger = getBaseLogger()
|
||||
|
||||
val appender = logger.getAppender(FILE_APPENDER_NAME) as RollingFileAppender<*>? ?: return
|
||||
val rollingPolicy = appender.rollingPolicy as SizeAndTimeBasedRollingPolicy<*>
|
||||
rollingPolicy.apply {
|
||||
maxHistory = maxFiles
|
||||
setMaxFileSize(FileSize.valueOf(maxFileSize))
|
||||
setTotalSizeCap(FileSize.valueOf(maxTotalSize))
|
||||
|
||||
rollingPolicy.stop()
|
||||
appender.stop()
|
||||
|
||||
rollingPolicy.start()
|
||||
appender.start()
|
||||
}
|
||||
}
|
||||
|
||||
const val BASE_LOGGER_NAME = "_BaseLogger"
|
||||
|
||||
fun setLogLevelFor(
|
||||
|
||||
@@ -1,7 +1,19 @@
|
||||
plugins {
|
||||
id(libs.plugins.kotlin.jvm.get().pluginId)
|
||||
id(libs.plugins.kotlin.serialization.get().pluginId)
|
||||
id(libs.plugins.ktlint.get().pluginId)
|
||||
id(
|
||||
libs.plugins.kotlin.jvm
|
||||
.get()
|
||||
.pluginId,
|
||||
)
|
||||
id(
|
||||
libs.plugins.kotlin.serialization
|
||||
.get()
|
||||
.pluginId,
|
||||
)
|
||||
id(
|
||||
libs.plugins.ktlint
|
||||
.get()
|
||||
.pluginId,
|
||||
)
|
||||
}
|
||||
|
||||
dependencies {
|
||||
@@ -24,8 +36,8 @@ dependencies {
|
||||
// AndroidX annotations
|
||||
compileOnly(libs.android.annotations)
|
||||
|
||||
// substitute for duktape-android
|
||||
implementation(libs.bundles.rhino)
|
||||
// substitute for duktape-android/quickjs
|
||||
implementation(libs.bundles.polyglot)
|
||||
|
||||
// Kotlin wrapper around Java Preferences, makes certain things easier
|
||||
implementation(libs.bundles.settings)
|
||||
|
||||
@@ -25,7 +25,7 @@ import android.os.IBinder;
|
||||
import android.util.Log;
|
||||
import kotlin.NotImplementedError;
|
||||
import xyz.nulldev.androidcompat.service.ServiceSupport;
|
||||
import xyz.nulldev.androidcompat.util.KodeinGlobalHelper;
|
||||
import xyz.nulldev.androidcompat.util.KoinGlobalHelper;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
@@ -299,7 +299,7 @@ import java.lang.annotation.RetentionPolicy;
|
||||
*/
|
||||
public abstract class Service extends ContextWrapper implements ComponentCallbacks2 {
|
||||
|
||||
private static final ServiceSupport serviceSupport = KodeinGlobalHelper.instance(ServiceSupport.class);
|
||||
private static final ServiceSupport serviceSupport = KoinGlobalHelper.instance(ServiceSupport.class);
|
||||
|
||||
private static final String TAG = "Service";
|
||||
/**
|
||||
@@ -328,7 +328,7 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac
|
||||
public Service() {
|
||||
//==================[THIS LINE MODIFIED FROM ANDROID SOURCE!]==================
|
||||
//Service must be initialized with a base context!
|
||||
super(KodeinGlobalHelper.instance(Context.class));
|
||||
super(KoinGlobalHelper.instance(Context.class));
|
||||
}
|
||||
/** Return the application that owns this service. */
|
||||
public final Application getApplication() {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package android.os;
|
||||
|
||||
import xyz.nulldev.androidcompat.io.AndroidFiles;
|
||||
import xyz.nulldev.androidcompat.util.KodeinGlobalHelper;
|
||||
import xyz.nulldev.androidcompat.util.KoinGlobalHelper;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
@@ -9,7 +9,7 @@ import java.io.File;
|
||||
* Android compatibility layer for files
|
||||
*/
|
||||
public class Environment {
|
||||
private static AndroidFiles androidFiles = KodeinGlobalHelper.instance(AndroidFiles.class);
|
||||
private static AndroidFiles androidFiles = KoinGlobalHelper.instance(AndroidFiles.class);
|
||||
|
||||
public static String DIRECTORY_ALARMS = getHomeDirectory("Alarms").getAbsolutePath();
|
||||
public static String DIRECTORY_DCIM = getHomeDirectory("DCIM").getAbsolutePath();
|
||||
|
||||
@@ -1,69 +1,99 @@
|
||||
package app.cash.quickjs;
|
||||
|
||||
import org.mozilla.javascript.ConsString;
|
||||
import org.mozilla.javascript.NativeArray;
|
||||
import org.graalvm.polyglot.*;
|
||||
|
||||
import javax.script.ScriptEngine;
|
||||
import javax.script.ScriptEngineManager;
|
||||
import java.io.Closeable;
|
||||
import java.math.BigInteger;
|
||||
import java.util.Arrays;
|
||||
|
||||
public final class QuickJs implements Closeable {
|
||||
private ScriptEngine engine;
|
||||
private Context context;
|
||||
|
||||
public static QuickJs create() {
|
||||
return new QuickJs(new ScriptEngineManager());
|
||||
return new QuickJs();
|
||||
}
|
||||
|
||||
public QuickJs(ScriptEngineManager manager) {
|
||||
this.engine = manager.getEngineByName("rhino");
|
||||
public QuickJs() {
|
||||
this.context = Context
|
||||
.newBuilder("js")
|
||||
.allowHostAccess(HostAccess.ALL)
|
||||
.allowPolyglotAccess(PolyglotAccess.NONE)
|
||||
.allowHostClassLoading(false)
|
||||
.build();
|
||||
context.enter();
|
||||
}
|
||||
|
||||
public Object evaluate(String script, String fileName) {
|
||||
public Object evaluate(String script, String ignoredFileName) {
|
||||
return this.evaluate(script);
|
||||
}
|
||||
|
||||
public Object evaluate(String script) {
|
||||
try {
|
||||
Object value = engine.eval(script);
|
||||
Value value = context.eval("js", script);
|
||||
return translateType(value);
|
||||
} catch (Exception exception) {
|
||||
throw new QuickJsException(exception.getMessage(), exception);
|
||||
}
|
||||
}
|
||||
|
||||
private Object translateType(Object obj) {
|
||||
if (obj instanceof NativeArray) {
|
||||
NativeArray array = (NativeArray) obj;
|
||||
long length = array.getLength();
|
||||
Object[] objects = new Object[(int) length];
|
||||
for (int i = 0; i < (int) length; i++) {
|
||||
objects[i] = translateType(array.get(i));
|
||||
private Object translateType(Value obj) {
|
||||
if (obj.isBoolean()) {
|
||||
return obj.asBoolean();
|
||||
} else if (obj.hasArrayElements()) {
|
||||
if (obj.getArraySize() == 0) {
|
||||
return new int[0];
|
||||
} else {
|
||||
Value element = obj.getArrayElement(0);
|
||||
if (element.isBoolean()) {
|
||||
return obj.as(boolean[].class);
|
||||
} else if (element.isNumber()) {
|
||||
if (element.fitsInInt()) {
|
||||
return obj.as(int[].class);
|
||||
} else if (element.fitsInBigInteger()) {
|
||||
return Arrays.stream(obj.as(BigInteger[].class)).map(BigInteger::longValue).toArray();
|
||||
} else {
|
||||
return obj.as(double[].class);
|
||||
}
|
||||
} else if (element.isHostObject()) {
|
||||
return obj.as(Object[].class);
|
||||
} else if (element.isString()) {
|
||||
return obj.as(String[].class);
|
||||
}
|
||||
}
|
||||
return objects;
|
||||
}
|
||||
if (obj instanceof ConsString) {
|
||||
ConsString consString = (ConsString) obj;
|
||||
return consString.toString();
|
||||
}
|
||||
if (obj instanceof Long) {
|
||||
Long value = (Long) obj;
|
||||
return value.intValue();
|
||||
} else if (obj.isNumber()) {
|
||||
if (obj.fitsInInt()) {
|
||||
return obj.asInt();
|
||||
} else if (obj.fitsInBigInteger()) {
|
||||
return obj.asBigInteger().longValue();
|
||||
} else {
|
||||
return obj.asDouble();
|
||||
}
|
||||
} else if (obj.isHostObject()) {
|
||||
return obj.asHostObject();
|
||||
} else if (obj.isString()) {
|
||||
return obj.asString();
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
public byte[] compile(String sourceCode, String fileName) {
|
||||
public byte[] compile(String sourceCode, String ignoredFileName) {
|
||||
return sourceCode.getBytes();
|
||||
}
|
||||
|
||||
|
||||
public Object execute(byte[] bytecode) {
|
||||
return this.evaluate(new String(bytecode));
|
||||
}
|
||||
|
||||
public <T> void set(String name, Class<T> ignoredType, T object) {
|
||||
context.getBindings("js").putMember(name, object);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
this.engine = null;
|
||||
if (this.context != null) {
|
||||
this.context.leave();
|
||||
this.context.close();
|
||||
this.context = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ package dalvik.system;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import xyz.nulldev.androidcompat.pm.PackageController;
|
||||
import xyz.nulldev.androidcompat.util.KodeinGlobalHelper;
|
||||
import xyz.nulldev.androidcompat.util.KoinGlobalHelper;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@@ -33,7 +33,7 @@ import java.util.Enumeration;
|
||||
* {@link ClassLoader} implementations.
|
||||
*/
|
||||
public class BaseDexClassLoader extends ClassLoader {
|
||||
private PackageController controller = KodeinGlobalHelper.instance(PackageController.class);
|
||||
private PackageController controller = KoinGlobalHelper.instance(PackageController.class);
|
||||
|
||||
private final URLClassLoader realClassloader;
|
||||
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
package xyz.nulldev.androidcompat
|
||||
|
||||
import android.app.Application
|
||||
import org.kodein.di.DI
|
||||
import org.kodein.di.conf.global
|
||||
import org.kodein.di.instance
|
||||
import org.koin.mp.KoinPlatformTools
|
||||
import xyz.nulldev.androidcompat.androidimpl.CustomContext
|
||||
|
||||
class AndroidCompat {
|
||||
val context: CustomContext by DI.global.instance()
|
||||
val context: CustomContext by KoinPlatformTools.defaultContext().get().inject()
|
||||
|
||||
fun startApp(application: Application) {
|
||||
application.attach(context)
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package xyz.nulldev.androidcompat
|
||||
|
||||
import org.kodein.di.DI
|
||||
import org.kodein.di.conf.global
|
||||
import xyz.nulldev.androidcompat.config.ApplicationInfoConfigModule
|
||||
import xyz.nulldev.androidcompat.config.FilesConfigModule
|
||||
import xyz.nulldev.androidcompat.config.SystemConfigModule
|
||||
@@ -12,8 +10,6 @@ import xyz.nulldev.ts.config.GlobalConfigManager
|
||||
*/
|
||||
class AndroidCompatInitializer {
|
||||
fun init() {
|
||||
DI.global.addImport(AndroidCompatModule().create())
|
||||
|
||||
// Register config modules
|
||||
GlobalConfigManager.registerModules(
|
||||
FilesConfigModule.register(GlobalConfigManager.config),
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
package xyz.nulldev.androidcompat
|
||||
|
||||
import android.content.Context
|
||||
import org.kodein.di.DI
|
||||
import org.kodein.di.bind
|
||||
import org.kodein.di.conf.global
|
||||
import org.kodein.di.instance
|
||||
import org.kodein.di.singleton
|
||||
import org.koin.core.module.Module
|
||||
import org.koin.dsl.module
|
||||
import xyz.nulldev.androidcompat.androidimpl.CustomContext
|
||||
import xyz.nulldev.androidcompat.androidimpl.FakePackageManager
|
||||
import xyz.nulldev.androidcompat.info.ApplicationInfoImpl
|
||||
@@ -17,25 +14,19 @@ import xyz.nulldev.androidcompat.service.ServiceSupport
|
||||
* AndroidCompatModule
|
||||
*/
|
||||
|
||||
class AndroidCompatModule {
|
||||
fun create() =
|
||||
DI.Module("AndroidCompat") {
|
||||
bind<AndroidFiles>() with singleton { AndroidFiles() }
|
||||
fun androidCompatModule(): Module =
|
||||
module {
|
||||
single { AndroidFiles() }
|
||||
|
||||
bind<ApplicationInfoImpl>() with singleton { ApplicationInfoImpl() }
|
||||
single { ApplicationInfoImpl(get()) }
|
||||
|
||||
bind<ServiceSupport>() with singleton { ServiceSupport() }
|
||||
single { ServiceSupport() }
|
||||
|
||||
bind<FakePackageManager>() with singleton { FakePackageManager() }
|
||||
single { FakePackageManager() }
|
||||
|
||||
bind<PackageController>() with singleton { PackageController() }
|
||||
single { PackageController() }
|
||||
|
||||
// Context
|
||||
bind<CustomContext>() with singleton { CustomContext() }
|
||||
bind<Context>() with
|
||||
singleton {
|
||||
val context: Context by DI.global.instance<CustomContext>()
|
||||
context
|
||||
}
|
||||
}
|
||||
}
|
||||
single { CustomContext() }
|
||||
|
||||
single<Context> { get<CustomContext>() }
|
||||
}
|
||||
|
||||
+13
-27
@@ -32,15 +32,14 @@ import android.os.*;
|
||||
import android.view.Display;
|
||||
import android.view.DisplayAdjustments;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.kodein.di.*;
|
||||
import org.koin.core.Koin;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import xyz.nulldev.androidcompat.info.ApplicationInfoImpl;
|
||||
import xyz.nulldev.androidcompat.io.AndroidFiles;
|
||||
import xyz.nulldev.androidcompat.io.sharedprefs.JavaSharedPreferences;
|
||||
import xyz.nulldev.androidcompat.service.ServiceSupport;
|
||||
import xyz.nulldev.androidcompat.util.KodeinGlobalHelper;
|
||||
import xyz.nulldev.androidcompat.util.KoinGlobalHelper;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.HashMap;
|
||||
@@ -51,26 +50,25 @@ import java.util.Map;
|
||||
* Custom context implementation.
|
||||
*
|
||||
*/
|
||||
public class CustomContext extends Context implements DIAware {
|
||||
private final DI kodein;
|
||||
public class CustomContext extends Context {
|
||||
private final Koin koin;
|
||||
public CustomContext() {
|
||||
this(KodeinGlobalHelper.kodein());
|
||||
this(KoinGlobalHelper.koin());
|
||||
}
|
||||
|
||||
public CustomContext(DI kodein) {
|
||||
this.kodein = kodein;
|
||||
public CustomContext(Koin koin) {
|
||||
this.koin = koin;
|
||||
|
||||
//Init configs
|
||||
androidFiles = KodeinGlobalHelper.instance(AndroidFiles.class, getDi());
|
||||
applicationInfo = KodeinGlobalHelper.instance(ApplicationInfoImpl.class, getDi());
|
||||
serviceSupport = KodeinGlobalHelper.instance(ServiceSupport.class, getDi());
|
||||
fakePackageManager = KodeinGlobalHelper.instance(FakePackageManager.class, getDi());
|
||||
androidFiles = KoinGlobalHelper.instance(AndroidFiles.class, getDi());
|
||||
applicationInfo = KoinGlobalHelper.instance(ApplicationInfoImpl.class, getDi());
|
||||
serviceSupport = KoinGlobalHelper.instance(ServiceSupport.class, getDi());
|
||||
fakePackageManager = KoinGlobalHelper.instance(FakePackageManager.class, getDi());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public DI getDi() {
|
||||
return kodein;
|
||||
public Koin getDi() {
|
||||
return koin;
|
||||
}
|
||||
|
||||
private AndroidFiles androidFiles;
|
||||
@@ -719,17 +717,5 @@ public class CustomContext extends Context implements DIAware {
|
||||
public boolean isCredentialProtectedStorage() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public DIContext<?> getDiContext() {
|
||||
return getDi().getDiContext();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public DITrigger getDiTrigger() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -16,14 +16,14 @@ import android.os.UserHandle;
|
||||
import kotlin.NotImplementedError;
|
||||
import xyz.nulldev.androidcompat.pm.InstalledPackage;
|
||||
import xyz.nulldev.androidcompat.pm.PackageController;
|
||||
import xyz.nulldev.androidcompat.util.KodeinGlobalHelper;
|
||||
import xyz.nulldev.androidcompat.util.KoinGlobalHelper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class FakePackageManager extends PackageManager {
|
||||
private PackageController controller = KodeinGlobalHelper.instance(PackageController.class);
|
||||
private PackageController controller = KoinGlobalHelper.instance(PackageController.class);
|
||||
|
||||
@Override
|
||||
public PackageInfo getPackageInfo(String packageName, int flags) throws NameNotFoundException {
|
||||
|
||||
+3
-1
@@ -8,7 +8,9 @@ import xyz.nulldev.ts.config.ConfigModule
|
||||
* Application info config.
|
||||
*/
|
||||
|
||||
class ApplicationInfoConfigModule(getConfig: () -> Config) : ConfigModule(getConfig) {
|
||||
class ApplicationInfoConfigModule(
|
||||
getConfig: () -> Config,
|
||||
) : ConfigModule(getConfig) {
|
||||
val packageName: String by getConfig()
|
||||
val debug: Boolean by getConfig()
|
||||
|
||||
|
||||
@@ -8,7 +8,9 @@ import xyz.nulldev.ts.config.ConfigModule
|
||||
* Files configuration modules. Specifies where to store the Android files.
|
||||
*/
|
||||
|
||||
class FilesConfigModule(getConfig: () -> Config) : ConfigModule(getConfig) {
|
||||
class FilesConfigModule(
|
||||
getConfig: () -> Config,
|
||||
) : ConfigModule(getConfig) {
|
||||
val dataDir: String by getConfig()
|
||||
val filesDir: String by getConfig()
|
||||
val noBackupFilesDir: String by getConfig()
|
||||
|
||||
@@ -4,7 +4,9 @@ import com.typesafe.config.Config
|
||||
import io.github.config4k.getValue
|
||||
import xyz.nulldev.ts.config.ConfigModule
|
||||
|
||||
class SystemConfigModule(val getConfig: () -> Config) : ConfigModule(getConfig) {
|
||||
class SystemConfigModule(
|
||||
val getConfig: () -> Config,
|
||||
) : ConfigModule(getConfig) {
|
||||
val isDebuggable: Boolean by getConfig()
|
||||
|
||||
val propertyPrefix = "properties."
|
||||
|
||||
@@ -19,7 +19,9 @@ import java.sql.Timestamp
|
||||
import java.util.Calendar
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
|
||||
class ScrollableResultSet(
|
||||
val parent: ResultSet,
|
||||
) : ResultSet by parent {
|
||||
private val cachedContent = mutableListOf<ResultSetEntry>()
|
||||
private val columnCache = mutableMapOf<String, Int>()
|
||||
private var lastReturnWasNull = false
|
||||
@@ -29,9 +31,10 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
|
||||
val parentMetadata = parent.metaData
|
||||
val columnCount = parentMetadata.columnCount
|
||||
val columnLabels =
|
||||
(1..columnCount).map {
|
||||
parentMetadata.getColumnLabel(it)
|
||||
}.toTypedArray()
|
||||
(1..columnCount)
|
||||
.map {
|
||||
parentMetadata.getColumnLabel(it)
|
||||
}.toTypedArray()
|
||||
|
||||
init {
|
||||
val columnCount = columnCount
|
||||
@@ -45,20 +48,17 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
|
||||
while (parent.next()) {
|
||||
cachedContent +=
|
||||
ResultSetEntry().apply {
|
||||
for (i in 1..columnCount)
|
||||
for (i in 1..columnCount) {
|
||||
data += parent.getObject(i)
|
||||
}
|
||||
}
|
||||
resultSetLength++
|
||||
}
|
||||
}
|
||||
|
||||
private fun notImplemented(): Nothing {
|
||||
throw UnsupportedOperationException("This class currently does not support this operation!")
|
||||
}
|
||||
private fun notImplemented(): Nothing = throw UnsupportedOperationException("This class currently does not support this operation!")
|
||||
|
||||
private fun cursorValid(): Boolean {
|
||||
return isAfterLast || isBeforeFirst
|
||||
}
|
||||
private fun cursorValid(): Boolean = isAfterLast || isBeforeFirst
|
||||
|
||||
private fun internalMove(row: Int) {
|
||||
if (cursor < 0) {
|
||||
@@ -76,22 +76,16 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
|
||||
return obj
|
||||
}
|
||||
|
||||
private fun obj(column: String?): Any? {
|
||||
return obj(cachedFindColumn(column))
|
||||
}
|
||||
private fun obj(column: String?): Any? = obj(cachedFindColumn(column))
|
||||
|
||||
private fun cachedFindColumn(column: String?) =
|
||||
columnCache.getOrPut(column!!, {
|
||||
findColumn(column)
|
||||
})
|
||||
|
||||
override fun getNClob(columnIndex: Int): NClob {
|
||||
return obj(columnIndex) as NClob
|
||||
}
|
||||
override fun getNClob(columnIndex: Int): NClob = obj(columnIndex) as NClob
|
||||
|
||||
override fun getNClob(columnLabel: String?): NClob {
|
||||
return obj(columnLabel) as NClob
|
||||
}
|
||||
override fun getNClob(columnLabel: String?): NClob = obj(columnLabel) as NClob
|
||||
|
||||
override fun updateNString(
|
||||
columnIndex: Int,
|
||||
@@ -260,17 +254,11 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun getBoolean(columnIndex: Int): Boolean {
|
||||
return obj(columnIndex) as Boolean
|
||||
}
|
||||
override fun getBoolean(columnIndex: Int): Boolean = obj(columnIndex) as Boolean
|
||||
|
||||
override fun getBoolean(columnLabel: String?): Boolean {
|
||||
return obj(columnLabel) as Boolean
|
||||
}
|
||||
override fun getBoolean(columnLabel: String?): Boolean = obj(columnLabel) as Boolean
|
||||
|
||||
override fun isFirst(): Boolean {
|
||||
return cursor - 1 < resultSetLength
|
||||
}
|
||||
override fun isFirst(): Boolean = cursor - 1 < resultSetLength
|
||||
|
||||
override fun getBigDecimal(
|
||||
columnIndex: Int,
|
||||
@@ -288,13 +276,9 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun getBigDecimal(columnIndex: Int): BigDecimal {
|
||||
return obj(columnIndex) as BigDecimal
|
||||
}
|
||||
override fun getBigDecimal(columnIndex: Int): BigDecimal = obj(columnIndex) as BigDecimal
|
||||
|
||||
override fun getBigDecimal(columnLabel: String?): BigDecimal {
|
||||
return obj(columnLabel) as BigDecimal
|
||||
}
|
||||
override fun getBigDecimal(columnLabel: String?): BigDecimal = obj(columnLabel) as BigDecimal
|
||||
|
||||
override fun updateBytes(
|
||||
columnIndex: Int,
|
||||
@@ -310,9 +294,7 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun isLast(): Boolean {
|
||||
return cursor == resultSetLength
|
||||
}
|
||||
override fun isLast(): Boolean = cursor == resultSetLength
|
||||
|
||||
override fun insertRow() {
|
||||
notImplemented()
|
||||
@@ -351,9 +333,7 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
|
||||
return cursorValid()
|
||||
}
|
||||
|
||||
override fun isAfterLast(): Boolean {
|
||||
return cursor > resultSetLength
|
||||
}
|
||||
override fun isAfterLast(): Boolean = cursor > resultSetLength
|
||||
|
||||
override fun relative(rows: Int): Boolean {
|
||||
internalMove(cursor + rows)
|
||||
@@ -365,8 +345,9 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
|
||||
internalMove(row)
|
||||
} else {
|
||||
last()
|
||||
for (i in 1..row)
|
||||
for (i in 1..row) {
|
||||
previous()
|
||||
}
|
||||
}
|
||||
return cursorValid()
|
||||
}
|
||||
@@ -394,19 +375,13 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
|
||||
return cursorValid()
|
||||
}
|
||||
|
||||
override fun getFloat(columnIndex: Int): Float {
|
||||
return obj(columnIndex) as Float
|
||||
}
|
||||
override fun getFloat(columnIndex: Int): Float = obj(columnIndex) as Float
|
||||
|
||||
override fun getFloat(columnLabel: String?): Float {
|
||||
return obj(columnLabel) as Float
|
||||
}
|
||||
override fun getFloat(columnLabel: String?): Float = obj(columnLabel) as Float
|
||||
|
||||
override fun wasNull() = lastReturnWasNull
|
||||
|
||||
override fun getRow(): Int {
|
||||
return cursor
|
||||
}
|
||||
override fun getRow(): Int = cursor
|
||||
|
||||
override fun first(): Boolean {
|
||||
internalMove(1)
|
||||
@@ -459,13 +434,9 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun getURL(columnIndex: Int): URL {
|
||||
return obj(columnIndex) as URL
|
||||
}
|
||||
override fun getURL(columnIndex: Int): URL = obj(columnIndex) as URL
|
||||
|
||||
override fun getURL(columnLabel: String?): URL {
|
||||
return obj(columnLabel) as URL
|
||||
}
|
||||
override fun getURL(columnLabel: String?): URL = obj(columnLabel) as URL
|
||||
|
||||
override fun updateShort(
|
||||
columnIndex: Int,
|
||||
@@ -643,21 +614,13 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun getByte(columnIndex: Int): Byte {
|
||||
return obj(columnIndex) as Byte
|
||||
}
|
||||
override fun getByte(columnIndex: Int): Byte = obj(columnIndex) as Byte
|
||||
|
||||
override fun getByte(columnLabel: String?): Byte {
|
||||
return obj(columnLabel) as Byte
|
||||
}
|
||||
override fun getByte(columnLabel: String?): Byte = obj(columnLabel) as Byte
|
||||
|
||||
override fun getString(columnIndex: Int): String? {
|
||||
return obj(columnIndex) as String?
|
||||
}
|
||||
override fun getString(columnIndex: Int): String? = obj(columnIndex) as String?
|
||||
|
||||
override fun getString(columnLabel: String?): String? {
|
||||
return obj(columnLabel) as String?
|
||||
}
|
||||
override fun getString(columnLabel: String?): String? = obj(columnLabel) as String?
|
||||
|
||||
override fun updateSQLXML(
|
||||
columnIndex: Int,
|
||||
@@ -687,13 +650,9 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun getObject(columnIndex: Int): Any? {
|
||||
return obj(columnIndex)
|
||||
}
|
||||
override fun getObject(columnIndex: Int): Any? = obj(columnIndex)
|
||||
|
||||
override fun getObject(columnLabel: String?): Any? {
|
||||
return obj(columnLabel)
|
||||
}
|
||||
override fun getObject(columnLabel: String?): Any? = obj(columnLabel)
|
||||
|
||||
override fun getObject(
|
||||
columnIndex: Int,
|
||||
@@ -714,16 +673,12 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
|
||||
override fun <T : Any?> getObject(
|
||||
columnIndex: Int,
|
||||
type: Class<T>?,
|
||||
): T {
|
||||
return obj(columnIndex) as T
|
||||
}
|
||||
): T = obj(columnIndex) as T
|
||||
|
||||
override fun <T : Any?> getObject(
|
||||
columnLabel: String?,
|
||||
type: Class<T>?,
|
||||
): T {
|
||||
return obj(columnLabel) as T
|
||||
}
|
||||
): T = obj(columnLabel) as T
|
||||
|
||||
override fun previous(): Boolean {
|
||||
internalMove(cursor - 1)
|
||||
@@ -756,13 +711,9 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
|
||||
}
|
||||
}
|
||||
|
||||
override fun getLong(columnIndex: Int): Long {
|
||||
return castToLong(obj(columnIndex))
|
||||
}
|
||||
override fun getLong(columnIndex: Int): Long = castToLong(obj(columnIndex))
|
||||
|
||||
override fun getLong(columnLabel: String?): Long {
|
||||
return castToLong(obj(columnLabel))
|
||||
}
|
||||
override fun getLong(columnLabel: String?): Long = castToLong(obj(columnLabel))
|
||||
|
||||
override fun getClob(columnIndex: Int): Clob {
|
||||
// TODO Maybe?
|
||||
@@ -840,13 +791,9 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun getNString(columnIndex: Int): String {
|
||||
return obj(columnIndex) as String
|
||||
}
|
||||
override fun getNString(columnIndex: Int): String = obj(columnIndex) as String
|
||||
|
||||
override fun getNString(columnLabel: String?): String {
|
||||
return obj(columnLabel) as String
|
||||
}
|
||||
override fun getNString(columnLabel: String?): String = obj(columnLabel) as String
|
||||
|
||||
override fun getArray(columnIndex: Int): Array {
|
||||
// TODO Maybe?
|
||||
@@ -880,17 +827,11 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun getCharacterStream(columnIndex: Int): Reader {
|
||||
return getNCharacterStream(columnIndex)
|
||||
}
|
||||
override fun getCharacterStream(columnIndex: Int): Reader = getNCharacterStream(columnIndex)
|
||||
|
||||
override fun getCharacterStream(columnLabel: String?): Reader {
|
||||
return getNCharacterStream(columnLabel)
|
||||
}
|
||||
override fun getCharacterStream(columnLabel: String?): Reader = getNCharacterStream(columnLabel)
|
||||
|
||||
override fun isBeforeFirst(): Boolean {
|
||||
return cursor - 1 < resultSetLength
|
||||
}
|
||||
override fun isBeforeFirst(): Boolean = cursor - 1 < resultSetLength
|
||||
|
||||
override fun updateBoolean(
|
||||
columnIndex: Int,
|
||||
@@ -926,21 +867,13 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun getShort(columnIndex: Int): Short {
|
||||
return obj(columnIndex) as Short
|
||||
}
|
||||
override fun getShort(columnIndex: Int): Short = obj(columnIndex) as Short
|
||||
|
||||
override fun getShort(columnLabel: String?): Short {
|
||||
return obj(columnLabel) as Short
|
||||
}
|
||||
override fun getShort(columnLabel: String?): Short = obj(columnLabel) as Short
|
||||
|
||||
override fun getAsciiStream(columnIndex: Int): InputStream {
|
||||
return getBinaryStream(columnIndex)
|
||||
}
|
||||
override fun getAsciiStream(columnIndex: Int): InputStream = getBinaryStream(columnIndex)
|
||||
|
||||
override fun getAsciiStream(columnLabel: String?): InputStream {
|
||||
return getBinaryStream(columnLabel)
|
||||
}
|
||||
override fun getAsciiStream(columnLabel: String?): InputStream = getBinaryStream(columnLabel)
|
||||
|
||||
override fun updateTime(
|
||||
columnIndex: Int,
|
||||
@@ -1008,13 +941,9 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun getNCharacterStream(columnIndex: Int): Reader {
|
||||
return getBinaryStream(columnIndex).reader()
|
||||
}
|
||||
override fun getNCharacterStream(columnIndex: Int): Reader = getBinaryStream(columnIndex).reader()
|
||||
|
||||
override fun getNCharacterStream(columnLabel: String?): Reader {
|
||||
return getBinaryStream(columnLabel).reader()
|
||||
}
|
||||
override fun getNCharacterStream(columnLabel: String?): Reader = getBinaryStream(columnLabel).reader()
|
||||
|
||||
override fun updateArray(
|
||||
columnIndex: Int,
|
||||
@@ -1030,45 +959,27 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun getBytes(columnIndex: Int): ByteArray {
|
||||
return obj(columnIndex) as ByteArray
|
||||
}
|
||||
override fun getBytes(columnIndex: Int): ByteArray = obj(columnIndex) as ByteArray
|
||||
|
||||
override fun getBytes(columnLabel: String?): ByteArray {
|
||||
return obj(columnLabel) as ByteArray
|
||||
}
|
||||
override fun getBytes(columnLabel: String?): ByteArray = obj(columnLabel) as ByteArray
|
||||
|
||||
override fun getDouble(columnIndex: Int): Double {
|
||||
return obj(columnIndex) as Double
|
||||
}
|
||||
override fun getDouble(columnIndex: Int): Double = obj(columnIndex) as Double
|
||||
|
||||
override fun getDouble(columnLabel: String?): Double {
|
||||
return obj(columnLabel) as Double
|
||||
}
|
||||
override fun getDouble(columnLabel: String?): Double = obj(columnLabel) as Double
|
||||
|
||||
override fun getUnicodeStream(columnIndex: Int): InputStream {
|
||||
return getBinaryStream(columnIndex)
|
||||
}
|
||||
override fun getUnicodeStream(columnIndex: Int): InputStream = getBinaryStream(columnIndex)
|
||||
|
||||
override fun getUnicodeStream(columnLabel: String?): InputStream {
|
||||
return getBinaryStream(columnLabel)
|
||||
}
|
||||
override fun getUnicodeStream(columnLabel: String?): InputStream = getBinaryStream(columnLabel)
|
||||
|
||||
override fun rowInserted() = false
|
||||
|
||||
private fun thisIsWrapperFor(iface: Class<*>?) = this.javaClass.isInstance(iface)
|
||||
|
||||
override fun isWrapperFor(iface: Class<*>?): Boolean {
|
||||
return thisIsWrapperFor(iface) || parent.isWrapperFor(iface)
|
||||
}
|
||||
override fun isWrapperFor(iface: Class<*>?): Boolean = thisIsWrapperFor(iface) || parent.isWrapperFor(iface)
|
||||
|
||||
override fun getInt(columnIndex: Int): Int {
|
||||
return obj(columnIndex) as Int
|
||||
}
|
||||
override fun getInt(columnIndex: Int): Int = obj(columnIndex) as Int
|
||||
|
||||
override fun getInt(columnLabel: String?): Int {
|
||||
return obj(columnLabel) as Int
|
||||
}
|
||||
override fun getInt(columnLabel: String?): Int = obj(columnLabel) as Int
|
||||
|
||||
override fun updateNull(columnIndex: Int) {
|
||||
notImplemented()
|
||||
@@ -1088,8 +999,8 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
override fun getMetaData(): ResultSetMetaData {
|
||||
return object : ResultSetMetaData by parentMetadata {
|
||||
override fun getMetaData(): ResultSetMetaData =
|
||||
object : ResultSetMetaData by parentMetadata {
|
||||
override fun isReadOnly(column: Int) = true
|
||||
|
||||
override fun isWritable(column: Int) = false
|
||||
@@ -1098,19 +1009,12 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
|
||||
|
||||
override fun getColumnCount() = this@ScrollableResultSet.columnCount
|
||||
|
||||
override fun getColumnLabel(column: Int): String {
|
||||
return columnLabels[column - 1]
|
||||
}
|
||||
override fun getColumnLabel(column: Int): String = columnLabels[column - 1]
|
||||
}
|
||||
}
|
||||
|
||||
override fun getBinaryStream(columnIndex: Int): InputStream {
|
||||
return (obj(columnIndex) as ByteArray).inputStream()
|
||||
}
|
||||
override fun getBinaryStream(columnIndex: Int): InputStream = (obj(columnIndex) as ByteArray).inputStream()
|
||||
|
||||
override fun getBinaryStream(columnLabel: String?): InputStream {
|
||||
return (obj(columnLabel) as ByteArray).inputStream()
|
||||
}
|
||||
override fun getBinaryStream(columnLabel: String?): InputStream = (obj(columnLabel) as ByteArray).inputStream()
|
||||
|
||||
override fun updateCharacterStream(
|
||||
columnIndex: Int,
|
||||
|
||||
@@ -1,16 +1,12 @@
|
||||
package xyz.nulldev.androidcompat.info
|
||||
|
||||
import android.content.pm.ApplicationInfo
|
||||
import org.kodein.di.DI
|
||||
import org.kodein.di.DIAware
|
||||
import org.kodein.di.conf.global
|
||||
import org.kodein.di.instance
|
||||
import xyz.nulldev.androidcompat.config.ApplicationInfoConfigModule
|
||||
import xyz.nulldev.ts.config.ConfigManager
|
||||
|
||||
class ApplicationInfoImpl(override val di: DI = DI.global) : ApplicationInfo(), DIAware {
|
||||
val configManager: ConfigManager by di.instance()
|
||||
|
||||
class ApplicationInfoImpl(
|
||||
private val configManager: ConfigManager,
|
||||
) : ApplicationInfo() {
|
||||
val appInfoConfig: ApplicationInfoConfigModule
|
||||
get() = configManager.module()
|
||||
|
||||
|
||||
@@ -8,7 +8,9 @@ import java.io.File
|
||||
/**
|
||||
* Android file constants.
|
||||
*/
|
||||
class AndroidFiles(val configManager: ConfigManager = GlobalConfigManager) {
|
||||
class AndroidFiles(
|
||||
val configManager: ConfigManager = GlobalConfigManager,
|
||||
) {
|
||||
val filesConfig: FilesConfigModule
|
||||
get() = configManager.module()
|
||||
|
||||
@@ -30,9 +32,8 @@ class AndroidFiles(val configManager: ConfigManager = GlobalConfigManager) {
|
||||
|
||||
val packagesDir: File get() = registerFile(filesConfig.packageDir)
|
||||
|
||||
fun registerFile(file: String): File {
|
||||
return File(file).apply {
|
||||
fun registerFile(file: String): File =
|
||||
File(file).apply {
|
||||
mkdirs()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+26
-29
@@ -14,11 +14,11 @@ import com.russhwolf.settings.Settings
|
||||
import com.russhwolf.settings.serialization.decodeValue
|
||||
import com.russhwolf.settings.serialization.decodeValueOrNull
|
||||
import com.russhwolf.settings.serialization.encodeValue
|
||||
import io.github.oshai.kotlinlogging.KotlinLogging
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.SerializationException
|
||||
import kotlinx.serialization.builtins.SetSerializer
|
||||
import kotlinx.serialization.builtins.serializer
|
||||
import mu.KotlinLogging
|
||||
import xyz.nulldev.androidcompat.util.SafePath
|
||||
import xyz.nulldev.ts.config.ApplicationRootDir
|
||||
import java.util.Properties
|
||||
@@ -30,7 +30,9 @@ import kotlin.io.path.inputStream
|
||||
import kotlin.io.path.outputStream
|
||||
|
||||
@OptIn(ExperimentalSerializationApi::class, ExperimentalSettingsApi::class)
|
||||
class JavaSharedPreferences(key: String) : SharedPreferences {
|
||||
class JavaSharedPreferences(
|
||||
key: String,
|
||||
) : SharedPreferences {
|
||||
companion object {
|
||||
private val logger = KotlinLogging.logger {}
|
||||
}
|
||||
@@ -72,20 +74,17 @@ class JavaSharedPreferences(key: String) : SharedPreferences {
|
||||
private val listeners = mutableMapOf<SharedPreferences.OnSharedPreferenceChangeListener, (String) -> Unit>()
|
||||
|
||||
// TODO: 2021-05-29 Need to find a way to get this working with all pref types
|
||||
override fun getAll(): MutableMap<String, *> {
|
||||
return preferences.keys.associateWith { preferences.getStringOrNull(it) }.toMutableMap()
|
||||
}
|
||||
override fun getAll(): MutableMap<String, *> = preferences.keys.associateWith { preferences.getStringOrNull(it) }.toMutableMap()
|
||||
|
||||
override fun getString(
|
||||
key: String,
|
||||
defValue: String?,
|
||||
): String? {
|
||||
return if (defValue != null) {
|
||||
): String? =
|
||||
if (defValue != null) {
|
||||
preferences.getString(key, defValue)
|
||||
} else {
|
||||
preferences.getStringOrNull(key)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getStringSet(
|
||||
key: String,
|
||||
@@ -105,50 +104,48 @@ class JavaSharedPreferences(key: String) : SharedPreferences {
|
||||
override fun getInt(
|
||||
key: String,
|
||||
defValue: Int,
|
||||
): Int {
|
||||
return preferences.getInt(key, defValue)
|
||||
}
|
||||
): Int = preferences.getInt(key, defValue)
|
||||
|
||||
override fun getLong(
|
||||
key: String,
|
||||
defValue: Long,
|
||||
): Long {
|
||||
return preferences.getLong(key, defValue)
|
||||
}
|
||||
): Long = preferences.getLong(key, defValue)
|
||||
|
||||
override fun getFloat(
|
||||
key: String,
|
||||
defValue: Float,
|
||||
): Float {
|
||||
return preferences.getFloat(key, defValue)
|
||||
}
|
||||
): Float = preferences.getFloat(key, defValue)
|
||||
|
||||
override fun getBoolean(
|
||||
key: String,
|
||||
defValue: Boolean,
|
||||
): Boolean {
|
||||
return preferences.getBoolean(key, defValue)
|
||||
}
|
||||
): Boolean = preferences.getBoolean(key, defValue)
|
||||
|
||||
override fun contains(key: String): Boolean {
|
||||
return key in preferences.keys
|
||||
}
|
||||
override fun contains(key: String): Boolean = key in preferences.keys
|
||||
|
||||
override fun edit(): SharedPreferences.Editor {
|
||||
return Editor(preferences) { key ->
|
||||
override fun edit(): SharedPreferences.Editor =
|
||||
Editor(preferences) { key ->
|
||||
listeners.forEach { (_, listener) ->
|
||||
listener(key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Editor(private val preferences: Settings, private val notify: (String) -> Unit) : SharedPreferences.Editor {
|
||||
class Editor(
|
||||
private val preferences: Settings,
|
||||
private val notify: (String) -> Unit,
|
||||
) : SharedPreferences.Editor {
|
||||
private val actions = mutableListOf<Action>()
|
||||
|
||||
private sealed class Action {
|
||||
data class Add(val key: String, val value: Any) : Action()
|
||||
data class Add(
|
||||
val key: String,
|
||||
val value: Any,
|
||||
) : Action()
|
||||
|
||||
data class Remove(
|
||||
val key: String,
|
||||
) : Action()
|
||||
|
||||
data class Remove(val key: String) : Action()
|
||||
data object Clear : Action()
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,9 @@ import java.io.File
|
||||
import javax.imageio.ImageIO
|
||||
import javax.xml.parsers.DocumentBuilderFactory
|
||||
|
||||
data class InstalledPackage(val root: File) {
|
||||
data class InstalledPackage(
|
||||
val root: File,
|
||||
) {
|
||||
val apk = File(root, "package.apk")
|
||||
val jar = File(root, "translated.jar")
|
||||
val icon = File(root, "icon.png")
|
||||
@@ -34,18 +36,21 @@ data class InstalledPackage(val root: File) {
|
||||
Bundle().apply {
|
||||
val appTag = doc.getElementsByTagName("application").item(0)
|
||||
|
||||
appTag?.childNodes?.toList()?.filter {
|
||||
it.nodeType == Node.ELEMENT_NODE
|
||||
}?.map {
|
||||
it as Element
|
||||
}?.filter {
|
||||
it.tagName == "meta-data"
|
||||
}?.map {
|
||||
putString(
|
||||
it.attributes.getNamedItem("android:name").nodeValue,
|
||||
it.attributes.getNamedItem("android:value").nodeValue,
|
||||
)
|
||||
}
|
||||
appTag
|
||||
?.childNodes
|
||||
?.toList()
|
||||
?.filter {
|
||||
it.nodeType == Node.ELEMENT_NODE
|
||||
}?.map {
|
||||
it as Element
|
||||
}?.filter {
|
||||
it.tagName == "meta-data"
|
||||
}?.map {
|
||||
putString(
|
||||
it.attributes.getNamedItem("android:name").nodeValue,
|
||||
it.attributes.getNamedItem("android:value").nodeValue,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
it.signatures =
|
||||
@@ -53,12 +58,14 @@ data class InstalledPackage(val root: File) {
|
||||
parsed.apkSingers.flatMap { it.certificateMetas }
|
||||
// + parsed.apkV2Singers.flatMap { it.certificateMetas }
|
||||
) // Blocked by: https://github.com/hsiafan/apk-parser/issues/72
|
||||
.map { Signature(it.data) }.toTypedArray()
|
||||
.map { Signature(it.data) }
|
||||
.toTypedArray()
|
||||
}
|
||||
|
||||
fun verify(): Boolean {
|
||||
val res =
|
||||
ApkVerifier.Builder(apk)
|
||||
ApkVerifier
|
||||
.Builder(apk)
|
||||
.build()
|
||||
.verify()
|
||||
|
||||
@@ -70,11 +77,14 @@ data class InstalledPackage(val root: File) {
|
||||
val icons = ApkFile(apk).allIcons
|
||||
|
||||
val read =
|
||||
icons.filter { it.isFile }.map {
|
||||
it.data.inputStream().use {
|
||||
ImageIO.read(it)
|
||||
}
|
||||
}.sortedByDescending { it.width * it.height }.firstOrNull() ?: return
|
||||
icons
|
||||
.filter { it.isFile }
|
||||
.map {
|
||||
it.data.inputStream().use {
|
||||
ImageIO.read(it)
|
||||
}
|
||||
}.sortedByDescending { it.width * it.height }
|
||||
.firstOrNull() ?: return
|
||||
|
||||
ImageIO.write(read, "png", icon)
|
||||
} catch (e: Exception) {
|
||||
@@ -94,8 +104,9 @@ data class InstalledPackage(val root: File) {
|
||||
fun NodeList.toList(): List<Node> {
|
||||
val out = mutableListOf<Node>()
|
||||
|
||||
for (i in 0 until length)
|
||||
for (i in 0 until length) {
|
||||
out += item(i)
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
package xyz.nulldev.androidcompat.pm
|
||||
|
||||
import net.dongliu.apk.parser.ApkParsers
|
||||
import org.kodein.di.DI
|
||||
import org.kodein.di.conf.global
|
||||
import org.kodein.di.instance
|
||||
import org.koin.mp.KoinPlatformTools
|
||||
import xyz.nulldev.androidcompat.io.AndroidFiles
|
||||
import java.io.File
|
||||
|
||||
class PackageController {
|
||||
private val androidFiles by DI.global.instance<AndroidFiles>()
|
||||
private val androidFiles: AndroidFiles by KoinPlatformTools.defaultContext().get().inject()
|
||||
private val uninstallListeners = mutableListOf<(String) -> Unit>()
|
||||
|
||||
fun registerUninstallListener(listener: (String) -> Unit) {
|
||||
@@ -57,13 +55,15 @@ class PackageController {
|
||||
}
|
||||
}
|
||||
|
||||
fun listInstalled(): List<InstalledPackage> {
|
||||
return androidFiles.packagesDir.listFiles().orEmpty().filter {
|
||||
it.isDirectory
|
||||
}.map {
|
||||
InstalledPackage(it)
|
||||
}
|
||||
}
|
||||
fun listInstalled(): List<InstalledPackage> =
|
||||
androidFiles.packagesDir
|
||||
.listFiles()
|
||||
.orEmpty()
|
||||
.filter {
|
||||
it.isDirectory
|
||||
}.map {
|
||||
InstalledPackage(it)
|
||||
}
|
||||
|
||||
fun deletePackage(pack: InstalledPackage) {
|
||||
if (!pack.root.exists()) error("Package was never installed!")
|
||||
|
||||
@@ -6,18 +6,19 @@ import android.content.pm.PackageInfo
|
||||
import net.dongliu.apk.parser.bean.ApkMeta
|
||||
import java.io.File
|
||||
|
||||
fun ApkMeta.toPackageInfo(apk: File): PackageInfo {
|
||||
return PackageInfo().also {
|
||||
fun ApkMeta.toPackageInfo(apk: File): PackageInfo =
|
||||
PackageInfo().also {
|
||||
it.packageName = packageName
|
||||
it.versionCode = versionCode.toInt()
|
||||
it.versionName = versionName
|
||||
|
||||
it.reqFeatures =
|
||||
usesFeatures.map {
|
||||
FeatureInfo().apply {
|
||||
name = it.name
|
||||
}
|
||||
}.toTypedArray()
|
||||
usesFeatures
|
||||
.map {
|
||||
FeatureInfo().apply {
|
||||
name = it.name
|
||||
}
|
||||
}.toTypedArray()
|
||||
|
||||
it.applicationInfo =
|
||||
ApplicationInfo().apply {
|
||||
@@ -26,4 +27,3 @@ fun ApkMeta.toPackageInfo(apk: File): PackageInfo {
|
||||
sourceDir = apk.absolutePath
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package xyz.nulldev.androidcompat.res;
|
||||
|
||||
import xyz.nulldev.androidcompat.info.ApplicationInfoImpl;
|
||||
import xyz.nulldev.androidcompat.util.KodeinGlobalHelper;
|
||||
import xyz.nulldev.androidcompat.util.KoinGlobalHelper;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Calendar;
|
||||
@@ -10,7 +10,7 @@ import java.util.Calendar;
|
||||
* BuildConfig compat class.
|
||||
*/
|
||||
public class BuildConfigCompat {
|
||||
private static ApplicationInfoImpl applicationInfo = KodeinGlobalHelper.instance(ApplicationInfoImpl.class);
|
||||
private static ApplicationInfoImpl applicationInfo = KoinGlobalHelper.instance(ApplicationInfoImpl.class);
|
||||
|
||||
public static final boolean DEBUG = applicationInfo.getDebug();
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package xyz.nulldev.androidcompat.res
|
||||
|
||||
class DrawableResource(val location: String) : Resource {
|
||||
class DrawableResource(
|
||||
val location: String,
|
||||
) : Resource {
|
||||
override fun getType() = DrawableResource::class.java
|
||||
|
||||
override fun getValue() = javaClass.getResourceAsStream(location)
|
||||
|
||||
@@ -19,7 +19,9 @@ package xyz.nulldev.androidcompat.res
|
||||
/**
|
||||
* String resource.
|
||||
*/
|
||||
class StringResource(val string: String) : Resource {
|
||||
class StringResource(
|
||||
val string: String,
|
||||
) : Resource {
|
||||
override fun getValue() = string
|
||||
|
||||
override fun getType() = StringResource::class.java
|
||||
|
||||
@@ -3,7 +3,7 @@ package xyz.nulldev.androidcompat.service
|
||||
import android.app.Service
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import mu.KotlinLogging
|
||||
import io.github.oshai.kotlinlogging.KotlinLogging
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
package xyz.nulldev.androidcompat.util
|
||||
|
||||
import android.content.Context
|
||||
import org.kodein.di.DI
|
||||
import org.kodein.di.conf.global
|
||||
import org.kodein.di.instance
|
||||
import xyz.nulldev.androidcompat.androidimpl.CustomContext
|
||||
import xyz.nulldev.androidcompat.androidimpl.FakePackageManager
|
||||
import xyz.nulldev.androidcompat.info.ApplicationInfoImpl
|
||||
import xyz.nulldev.androidcompat.io.AndroidFiles
|
||||
import xyz.nulldev.androidcompat.pm.PackageController
|
||||
import xyz.nulldev.androidcompat.service.ServiceSupport
|
||||
|
||||
/**
|
||||
* Helper class to allow access to Kodein from Java
|
||||
*/
|
||||
object KodeinGlobalHelper {
|
||||
/**
|
||||
* Get the Kodein object
|
||||
*/
|
||||
@JvmStatic
|
||||
fun kodein() = DI.global
|
||||
|
||||
/**
|
||||
* Get a dependency
|
||||
*/
|
||||
@JvmStatic
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun <T : Any> instance(
|
||||
type: Class<T>,
|
||||
kodein: DI? = null,
|
||||
): T {
|
||||
return when (type) {
|
||||
AndroidFiles::class.java -> {
|
||||
val instance: AndroidFiles by (kodein ?: kodein()).instance()
|
||||
instance as T
|
||||
}
|
||||
ApplicationInfoImpl::class.java -> {
|
||||
val instance: ApplicationInfoImpl by (kodein ?: kodein()).instance()
|
||||
instance as T
|
||||
}
|
||||
ServiceSupport::class.java -> {
|
||||
val instance: ServiceSupport by (kodein ?: kodein()).instance()
|
||||
instance as T
|
||||
}
|
||||
FakePackageManager::class.java -> {
|
||||
val instance: FakePackageManager by (kodein ?: kodein()).instance()
|
||||
instance as T
|
||||
}
|
||||
PackageController::class.java -> {
|
||||
val instance: PackageController by (kodein ?: kodein()).instance()
|
||||
instance as T
|
||||
}
|
||||
CustomContext::class.java -> {
|
||||
val instance: CustomContext by (kodein ?: kodein()).instance()
|
||||
instance as T
|
||||
}
|
||||
Context::class.java -> {
|
||||
val instance: Context by (kodein ?: kodein()).instance()
|
||||
instance as T
|
||||
}
|
||||
else -> throw IllegalArgumentException("Kodein instance not found")
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun <T : Any> instance(type: Class<T>): T {
|
||||
return instance(type, null)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package xyz.nulldev.androidcompat.util
|
||||
|
||||
import org.koin.core.Koin
|
||||
import org.koin.mp.KoinPlatformTools
|
||||
|
||||
/**
|
||||
* Helper class to allow access to Kodein from Java
|
||||
*/
|
||||
object KoinGlobalHelper {
|
||||
/**
|
||||
* Get the Kodein object
|
||||
*/
|
||||
@JvmStatic
|
||||
fun koin() = KoinPlatformTools.defaultContext().get()
|
||||
|
||||
/**
|
||||
* Get a dependency
|
||||
*/
|
||||
@JvmStatic
|
||||
fun <T : Any> instance(
|
||||
type: Class<T>,
|
||||
koin: Koin? = null,
|
||||
): T = (koin ?: koin()).get(type.kotlin)
|
||||
|
||||
@JvmStatic
|
||||
fun <T : Any> instance(type: Class<T>): T = instance(type, null)
|
||||
}
|
||||
@@ -18,9 +18,7 @@ class CookieManagerImpl : CookieManager() {
|
||||
acceptCookie = accept
|
||||
}
|
||||
|
||||
override fun acceptCookie(): Boolean {
|
||||
return acceptCookie
|
||||
}
|
||||
override fun acceptCookie(): Boolean = acceptCookie
|
||||
|
||||
override fun setAcceptThirdPartyCookies(
|
||||
webview: WebView?,
|
||||
@@ -29,9 +27,7 @@ class CookieManagerImpl : CookieManager() {
|
||||
acceptThirdPartyCookies = accept
|
||||
}
|
||||
|
||||
override fun acceptThirdPartyCookies(webview: WebView?): Boolean {
|
||||
return acceptThirdPartyCookies
|
||||
}
|
||||
override fun acceptThirdPartyCookies(webview: WebView?): Boolean = acceptThirdPartyCookies
|
||||
|
||||
override fun setCookie(
|
||||
url: String,
|
||||
@@ -65,7 +61,8 @@ class CookieManagerImpl : CookieManager() {
|
||||
} else {
|
||||
URI("http://$url")
|
||||
}
|
||||
return cookieHandler.cookieStore.get(uri)
|
||||
return cookieHandler.cookieStore
|
||||
.get(uri)
|
||||
.joinToString("; ") { "${it.name}=${it.value}" }
|
||||
}
|
||||
|
||||
@@ -87,15 +84,11 @@ class CookieManagerImpl : CookieManager() {
|
||||
callback?.onReceiveValue(removedCookies)
|
||||
}
|
||||
|
||||
override fun hasCookies(): Boolean {
|
||||
return cookieHandler.cookieStore.cookies.isNotEmpty()
|
||||
}
|
||||
override fun hasCookies(): Boolean = cookieHandler.cookieStore.cookies.isNotEmpty()
|
||||
|
||||
override fun flush() {}
|
||||
|
||||
override fun allowFileSchemeCookiesImpl(): Boolean {
|
||||
return allowFileSchemeCookies
|
||||
}
|
||||
override fun allowFileSchemeCookiesImpl(): Boolean = allowFileSchemeCookies
|
||||
|
||||
override fun setAcceptFileSchemeCookiesImpl(accept: Boolean) {
|
||||
allowFileSchemeCookies = acceptCookie
|
||||
|
||||
+259
@@ -1,3 +1,262 @@
|
||||
# Server: v2.0.1727 + WevUI: v1.5.1
|
||||
## TL;DR
|
||||
-
|
||||
|
||||
## Suwayomi-Server Changelog
|
||||
- ([r1726](https://github.com/Suwayomi/Suwayomi-Server/commit/1d5323a477528649f81fa4bd2e1e9e4a28da6402)) [skip ci] Add link to discord in issue templates ([#1347](https://github.com/Suwayomi/Suwayomi-Server/pull/1347) by @schroda)
|
||||
- ([r1725](https://github.com/Suwayomi/Suwayomi-Server/commit/f8d73819eaae86e4de10c8795e44d2347f1ce03e)) [Feature] Support Bangumi Tracker ([#1343](https://github.com/Suwayomi/Suwayomi-Server/pull/1343) by @kaaass, @Syer10)
|
||||
- ([r1724](https://github.com/Suwayomi/Suwayomi-Server/commit/cbe26b7291a41c6a9a9207ab97c3b86db8978e4e)) Chmod in build script (by @Syer10)
|
||||
- ([r1723](https://github.com/Suwayomi/Suwayomi-Server/commit/93477f60c2204fc10c42c8f62832543696d8e112)) Fix release name (by @Syer10)
|
||||
- ([r1722](https://github.com/Suwayomi/Suwayomi-Server/commit/9feebbfe177a3ec3044f5c30607c73e9bae7c60c)) Use tar for MacOS (by @Syer10)
|
||||
- ([r1721](https://github.com/Suwayomi/Suwayomi-Server/commit/6e365491a91962061f806e0debc878df89f46881)) Add permissions to jspawnhelper ([#1339](https://github.com/Suwayomi/Suwayomi-Server/pull/1339) by @Syer10)
|
||||
- ([r1720](https://github.com/Suwayomi/Suwayomi-Server/commit/2e586581297ce4955b3e25f0ba822b681f2683ed)) Fix MacOS builds ([#1338](https://github.com/Suwayomi/Suwayomi-Server/pull/1338) by @Syer10)
|
||||
- ([r1719](https://github.com/Suwayomi/Suwayomi-Server/commit/256c564b91a9154d3d8883d2a052095aa581edf5)) Fix release version extraction from jar name ([#1335](https://github.com/Suwayomi/Suwayomi-Server/pull/1335) by @schroda)
|
||||
- ([r1718](https://github.com/Suwayomi/Suwayomi-Server/commit/96b50f52ec917e8a521c5f87ae85704d188ec9f6)) Ensure webui "channel" is always of corresponding enum ([#1334](https://github.com/Suwayomi/Suwayomi-Server/pull/1334) by @schroda)
|
||||
- ([r1717](https://github.com/Suwayomi/Suwayomi-Server/commit/3167d8aa15cc5c7b7064a61eccb0e28336808241)) Fix/startup jvm error after installation update via msi ([#1229](https://github.com/Suwayomi/Suwayomi-Server/pull/1229) by @schroda, @Syer10)
|
||||
- ([r1716](https://github.com/Suwayomi/Suwayomi-Server/commit/78fd09c7286cfbd0db9dd308ebbb41e6e7fbe7c5)) Prevent IndexOutOfBoundsException in "libraryUpdate" subscription ([#1320](https://github.com/Suwayomi/Suwayomi-Server/pull/1320) by @schroda)
|
||||
- ([r1715](https://github.com/Suwayomi/Suwayomi-Server/commit/4c5598cedfcd40c2dffab31e45f38d692b2c3741)) Feature/graphql log execution exceptions ([#1319](https://github.com/Suwayomi/Suwayomi-Server/pull/1319) by @schroda)
|
||||
- ([r1714](https://github.com/Suwayomi/Suwayomi-Server/commit/c3347d94abe131f4e75ef3d9736184715f471812)) Feature/use GitHub issue yml format ([#1314](https://github.com/Suwayomi/Suwayomi-Server/pull/1314) by @schroda)
|
||||
- ([r1713](https://github.com/Suwayomi/Suwayomi-Server/commit/7ca4aa75a898212616b3bbfbbbc864b8d64b08a5)) Fix checkbox preference title nullability ([#1313](https://github.com/Suwayomi/Suwayomi-Server/pull/1313) by @Syer10)
|
||||
- ([r1712](https://github.com/Suwayomi/Suwayomi-Server/commit/226fad5594871d24c0f670d2bac741076e048376)) Remove "default" category from backups ([#1307](https://github.com/Suwayomi/Suwayomi-Server/pull/1307) by @schroda)
|
||||
- ([r1711](https://github.com/Suwayomi/Suwayomi-Server/commit/d0ee1ba5af8b4a560722ca5e6a29f58b46a84577)) Align kitsu icon with icons of other trackers ([#1303](https://github.com/Suwayomi/Suwayomi-Server/pull/1303) by @schroda)
|
||||
- ([r1710](https://github.com/Suwayomi/Suwayomi-Server/commit/439e0c8284308a731b5cbd4255cd4b08f75660f0)) Emit only updater job changes instead of full status ([#1302](https://github.com/Suwayomi/Suwayomi-Server/pull/1302) by @schroda)
|
||||
- ([r1709](https://github.com/Suwayomi/Suwayomi-Server/commit/7d079a8728d82af3082cde3db70114b946972a18)) Update kotlin monorepo to v2.1.20 ([#1315](https://github.com/Suwayomi/Suwayomi-Server/pull/1315) by @renovate[bot])
|
||||
- ([r1708](https://github.com/Suwayomi/Suwayomi-Server/commit/945a52653e78b239d6288bb983099d4b76505ace)) Update graphqlkotlin to v8.4.0 ([#1311](https://github.com/Suwayomi/Suwayomi-Server/pull/1311) by @renovate[bot])
|
||||
- ([r1707](https://github.com/Suwayomi/Suwayomi-Server/commit/bdafc86990f47d22325ace0170d5dbe721be374c)) Update polyglot to v24.2.0 ([#1310](https://github.com/Suwayomi/Suwayomi-Server/pull/1310) by @renovate[bot])
|
||||
- ([r1706](https://github.com/Suwayomi/Suwayomi-Server/commit/57d425ab9f01917d238fd61d2ecb2d3ce966c73f)) Update dependency ch.qos.logback:logback-classic to v1.5.18 ([#1309](https://github.com/Suwayomi/Suwayomi-Server/pull/1309) by @renovate[bot])
|
||||
- ([r1705](https://github.com/Suwayomi/Suwayomi-Server/commit/395ac8e944785504d524a0a2a1f36f96975eb602)) Update dependency com.ibm.icu:icu4j to v77 ([#1305](https://github.com/Suwayomi/Suwayomi-Server/pull/1305) by @renovate[bot])
|
||||
- ([r1704](https://github.com/Suwayomi/Suwayomi-Server/commit/2f801e7571300e0504edb6a46ff83382358bda88)) Update plugin buildconfig to v5.5.4 ([#1304](https://github.com/Suwayomi/Suwayomi-Server/pull/1304) by @renovate[bot])
|
||||
- ([r1703](https://github.com/Suwayomi/Suwayomi-Server/commit/d7636045fee34e3da8d66d81f59704c8922d0eea)) Update dependency io.javalin:javalin to v6.5.0 ([#1301](https://github.com/Suwayomi/Suwayomi-Server/pull/1301) by @renovate[bot])
|
||||
- ([r1702](https://github.com/Suwayomi/Suwayomi-Server/commit/b745f108704185882f2bdccf1a69e707548eb89e)) Update dependency org.jsoup:jsoup to v1.19.1 ([#1292](https://github.com/Suwayomi/Suwayomi-Server/pull/1292) by @renovate[bot])
|
||||
- ([r1701](https://github.com/Suwayomi/Suwayomi-Server/commit/d76849942c412d1b8f04d20747cfce34cf50f553)) Update plugin buildconfig to v5.5.2 ([#1299](https://github.com/Suwayomi/Suwayomi-Server/pull/1299) by @renovate[bot])
|
||||
- ([r1700](https://github.com/Suwayomi/Suwayomi-Server/commit/b7a8a3ffe84527d25982d0a0be866b3cf6a08214)) Update dependency io.github.oshai:kotlin-logging-jvm to v7.0.5 ([#1298](https://github.com/Suwayomi/Suwayomi-Server/pull/1298) by @renovate[bot])
|
||||
- ([r1699](https://github.com/Suwayomi/Suwayomi-Server/commit/95d9293fe013d5c553924521bca430c7d7768090)) Add support for opds-pse for undownloaded chapters ([#1278](https://github.com/Suwayomi/Suwayomi-Server/pull/1278) by @ShirishSaxena, @showyee)
|
||||
- ([r1698](https://github.com/Suwayomi/Suwayomi-Server/commit/3be165a5514af306398d542118da40fab16da411)) Initial import of Kitsu tracker ([#1297](https://github.com/Suwayomi/Suwayomi-Server/pull/1297) by @cpiber)
|
||||
- ([r1697](https://github.com/Suwayomi/Suwayomi-Server/commit/cb498e2128549fc462d815cf70c89689a3ca5090)) Update dependency org.slf4j:slf4j-api to v2.0.17 ([#1284](https://github.com/Suwayomi/Suwayomi-Server/pull/1284) by @renovate[bot])
|
||||
- ([r1696](https://github.com/Suwayomi/Suwayomi-Server/commit/973f4d66e2b569d712452633d1be7913f4a958fc)) Update jackson monorepo to v2.18.3 ([#1289](https://github.com/Suwayomi/Suwayomi-Server/pull/1289) by @renovate[bot])
|
||||
- ([r1695](https://github.com/Suwayomi/Suwayomi-Server/commit/2c80672f6e81071febfa30d8b18740b31def31ad)) Update plugin ktlint to v12.2.0 ([#1288](https://github.com/Suwayomi/Suwayomi-Server/pull/1288) by @renovate[bot])
|
||||
- ([r1694](https://github.com/Suwayomi/Suwayomi-Server/commit/2599813ef1c7e2c06e8dec2acc3ac449e723fa91)) Update dependency io.mockk:mockk to v1.13.17 ([#1287](https://github.com/Suwayomi/Suwayomi-Server/pull/1287) by @renovate[bot])
|
||||
- ([r1693](https://github.com/Suwayomi/Suwayomi-Server/commit/86f849a185d69f3e3b3c5085355985f38f42ff7a)) Update dependency net.harawata:appdirs to v1.4.0 ([#1286](https://github.com/Suwayomi/Suwayomi-Server/pull/1286) by @renovate[bot])
|
||||
- ([r1692](https://github.com/Suwayomi/Suwayomi-Server/commit/875f1f1506c76cf97b6a61ecc800f8aa4eb9c029)) Update dependency com.android.tools.build:apksig to v8.9.0 ([#1285](https://github.com/Suwayomi/Suwayomi-Server/pull/1285) by @renovate[bot])
|
||||
- ([r1691](https://github.com/Suwayomi/Suwayomi-Server/commit/e418375963a254d8eb7a35b982c8e7078e493bea)) Update dependency ch.qos.logback:logback-classic to v1.5.17 ([#1283](https://github.com/Suwayomi/Suwayomi-Server/pull/1283) by @renovate[bot])
|
||||
- ([r1690](https://github.com/Suwayomi/Suwayomi-Server/commit/d528fc7f9e6aeddece689e45fb0633508c5751b2)) Update dependency gradle to v8.13 ([#1282](https://github.com/Suwayomi/Suwayomi-Server/pull/1282) by @renovate[bot])
|
||||
- ([r1689](https://github.com/Suwayomi/Suwayomi-Server/commit/633ea97848ad851103c23b5c0196d8786c8e90cd)) Feature/optimize backup import ([#1270](https://github.com/Suwayomi/Suwayomi-Server/pull/1270) by @schroda)
|
||||
- ([r1688](https://github.com/Suwayomi/Suwayomi-Server/commit/36cb899b91b3bfcfb94a8baf85e9db7a6e752d6c)) Prevent chapter lastReadPage coerceIn error ([#1272](https://github.com/Suwayomi/Suwayomi-Server/pull/1272) by @schroda)
|
||||
- ([r1687](https://github.com/Suwayomi/Suwayomi-Server/commit/c4d849d6a33a2ada5320c7910683556e204498e9)) Fix invalid chapter download state in database ([#1271](https://github.com/Suwayomi/Suwayomi-Server/pull/1271) by @schroda)
|
||||
- ([r1686](https://github.com/Suwayomi/Suwayomi-Server/commit/c8bd39b4bfee44005614df5b647e803279e5218e)) Prevent negative lastPageRead values ([#1267](https://github.com/Suwayomi/Suwayomi-Server/pull/1267) by @schroda, @Syer10)
|
||||
- ([r1685](https://github.com/Suwayomi/Suwayomi-Server/commit/733ba16af2130013eec6b29c90fea181eb9b7c5e)) Fix/backup with duplicated chapters failure ([#1269](https://github.com/Suwayomi/Suwayomi-Server/pull/1269) by @schroda)
|
||||
- ([r1684](https://github.com/Suwayomi/Suwayomi-Server/commit/37f57c0c55586d89ecb4985d5e3ce38baf16ae62)) Prevent marking chapter as downloaded without pages ([#1268](https://github.com/Suwayomi/Suwayomi-Server/pull/1268) by @schroda)
|
||||
- ([r1683](https://github.com/Suwayomi/Suwayomi-Server/commit/013dbd79b4ab6c399db43e3d975e7ed9fbcea517)) Update dependency com.android.tools.build:apksig to v8.8.1 ([#1264](https://github.com/Suwayomi/Suwayomi-Server/pull/1264) by @renovate[bot])
|
||||
- ([r1682](https://github.com/Suwayomi/Suwayomi-Server/commit/f76d0b3258c51fed1d8f657fdd039c76f3fab40b)) Remove redundant code and add next/prev links ([#1263](https://github.com/Suwayomi/Suwayomi-Server/pull/1263) by @zeedif)
|
||||
- ([r1681](https://github.com/Suwayomi/Suwayomi-Server/commit/c2f7cdd72e3787fe6451371181da20bc61bd0d19)) Refactoring OPDS API for a more versatile root, allowing selection of manga listing by: all, source, genre, category, language, status. ([#1262](https://github.com/Suwayomi/Suwayomi-Server/pull/1262) by @zeedif)
|
||||
- ([r1680](https://github.com/Suwayomi/Suwayomi-Server/commit/01c37cb0ba08835e173243db265c98d2219e1c25)) Ignore authentication for preflight requests ([#1261](https://github.com/Suwayomi/Suwayomi-Server/pull/1261) by @schroda)
|
||||
- ([r1679](https://github.com/Suwayomi/Suwayomi-Server/commit/0dd0af1b8462396749661673598d0650ed8129ad)) Update dependency io.github.oshai:kotlin-logging-jvm to v7.0.4 ([#1260](https://github.com/Suwayomi/Suwayomi-Server/pull/1260) by @renovate[bot])
|
||||
- ([r1678](https://github.com/Suwayomi/Suwayomi-Server/commit/26aa68430099cadce0b8edffe9bf7cd59c0cec4f)) Add support for OPDS v1.2 to browse stored CBZ files ([#1257](https://github.com/Suwayomi/Suwayomi-Server/pull/1257) by @zeedif)
|
||||
- ([r1677](https://github.com/Suwayomi/Suwayomi-Server/commit/9669cdfb761e31a764ec31d9bbe2cfe04df8a0ef)) Update kotlin monorepo to v2.1.10 ([#1251](https://github.com/Suwayomi/Suwayomi-Server/pull/1251) by @renovate[bot])
|
||||
- ([r1676](https://github.com/Suwayomi/Suwayomi-Server/commit/2c5e5e283e7d3b3bb3019dd2d448347ae118d6ee)) Update dependency com.github.Suwayomi:exposed-migrations to v3.7.0 ([#1248](https://github.com/Suwayomi/Suwayomi-Server/pull/1248) by @renovate[bot])
|
||||
- ([r1675](https://github.com/Suwayomi/Suwayomi-Server/commit/303921c6ea5e2c828b91f2bbea35c3a55e1b5c41)) Update dependency gradle to v8.12.1 ([#1247](https://github.com/Suwayomi/Suwayomi-Server/pull/1247) by @renovate[bot])
|
||||
- ([r1674](https://github.com/Suwayomi/Suwayomi-Server/commit/593291a60f10da3aa97b4180436ebe8af77d84a0)) Update exposed to v0.59.0 ([#1228](https://github.com/Suwayomi/Suwayomi-Server/pull/1228) by @renovate[bot])
|
||||
- ([r1673](https://github.com/Suwayomi/Suwayomi-Server/commit/3af8e395bd6f48ee7ed39011115dc190fa99a7e0)) Check if file exists ([#1246](https://github.com/Suwayomi/Suwayomi-Server/pull/1246) by @Syer10)
|
||||
- ([r1672](https://github.com/Suwayomi/Suwayomi-Server/commit/0b192cfa5243584d734b541c2c5451a50edd577e)) Normalize Paths ([#1245](https://github.com/Suwayomi/Suwayomi-Server/pull/1245) by @Syer10)
|
||||
- ([r1671](https://github.com/Suwayomi/Suwayomi-Server/commit/fb8f20f31a823b018af704e14a35bc55be05c177)) Fix MacOS .command file with new JRE ([#1243](https://github.com/Suwayomi/Suwayomi-Server/pull/1243) by @Syer10)
|
||||
- ([r1670](https://github.com/Suwayomi/Suwayomi-Server/commit/e53386cf7278055bd3a74e5534a73f9d42b0b373)) Update dependency adoptium/temurin21-binaries to jdk-21.0.6+7 ([#1241](https://github.com/Suwayomi/Suwayomi-Server/pull/1241) by @renovate[bot])
|
||||
- ([r1669](https://github.com/Suwayomi/Suwayomi-Server/commit/b0e9b9b307fa879b224f3cb4b046aa279ad8658e)) Update polyglot to v24.1.2 ([#1240](https://github.com/Suwayomi/Suwayomi-Server/pull/1240) by @renovate[bot])
|
||||
- ([r1668](https://github.com/Suwayomi/Suwayomi-Server/commit/789678a45d02bd23fe6cb9983350fbf9fbea3066)) Update dependency io.insert-koin:koin-core to v4.0.2 ([#1239](https://github.com/Suwayomi/Suwayomi-Server/pull/1239) by @renovate[bot])
|
||||
- ([r1667](https://github.com/Suwayomi/Suwayomi-Server/commit/4ad01d345175904b2e64617c5c5a5f577218f315)) Update graphqlkotlin to v8.3.0 ([#1235](https://github.com/Suwayomi/Suwayomi-Server/pull/1235) by @renovate[bot])
|
||||
- ([r1666](https://github.com/Suwayomi/Suwayomi-Server/commit/6a87daa0b3a44cd5b7896cacf5a2feed37d907ab)) Update dependency org.bouncycastle:bcprov-jdk18on to v1.80 ([#1231](https://github.com/Suwayomi/Suwayomi-Server/pull/1231) by @renovate[bot])
|
||||
- ([r1665](https://github.com/Suwayomi/Suwayomi-Server/commit/aebef87076f33eabbee134acc77dadad5f1209c3)) Update dependency io.github.reactivecircus.cache4k:cache4k to v0.14.0 ([#1230](https://github.com/Suwayomi/Suwayomi-Server/pull/1230) by @renovate[bot])
|
||||
- ([r1664](https://github.com/Suwayomi/Suwayomi-Server/commit/32ff58598f4537662b7e33671025e889d63a3eae)) Merge Service Files during build to fix GraalJS ([#1242](https://github.com/Suwayomi/Suwayomi-Server/pull/1242) by @Syer10)
|
||||
- ([r1663](https://github.com/Suwayomi/Suwayomi-Server/commit/2d56cbe227e2955f722d1f1e1961f1b106c31045)) Update serialization to v1.8.0 ([#1220](https://github.com/Suwayomi/Suwayomi-Server/pull/1220) by @renovate[bot])
|
||||
- ([r1662](https://github.com/Suwayomi/Suwayomi-Server/commit/fbef5f592b5994ebc5ed32f0146b18a3a4b9674c)) Update dependency com.android.tools.build:apksig to v8.8.0 ([#1227](https://github.com/Suwayomi/Suwayomi-Server/pull/1227) by @renovate[bot])
|
||||
- ([r1661](https://github.com/Suwayomi/Suwayomi-Server/commit/090af36f5e6c320bd409af7eb2aab37707ef4898)) Update dependency com.squareup.okio:okio to v3.10.2 ([#1223](https://github.com/Suwayomi/Suwayomi-Server/pull/1223) by @renovate[bot])
|
||||
- ([r1660](https://github.com/Suwayomi/Suwayomi-Server/commit/885f27fcf12a30b8294947e9ccbe36bf47c45181)) Update dependency net.harawata:appdirs to v1.3.0 ([#1219](https://github.com/Suwayomi/Suwayomi-Server/pull/1219) by @renovate[bot])
|
||||
- ([r1659](https://github.com/Suwayomi/Suwayomi-Server/commit/8dc4eaf77abf578f5f0bfcb1683ef50dcfe5657f)) Update dependency io.insert-koin:koin-core to v4.0.1 ([#1213](https://github.com/Suwayomi/Suwayomi-Server/pull/1213) by @renovate[bot])
|
||||
- ([r1658](https://github.com/Suwayomi/Suwayomi-Server/commit/a55bef08a820f92bdaaea373aadff6d5c89fde94)) Update kotlinx-coroutines monorepo to v1.10.1 ([#1212](https://github.com/Suwayomi/Suwayomi-Server/pull/1212) by @renovate[bot])
|
||||
- ([r1657](https://github.com/Suwayomi/Suwayomi-Server/commit/0dc308973949c06eda5643e3a91694ffb02d7296)) Update dependency gradle to v8.12 ([#1211](https://github.com/Suwayomi/Suwayomi-Server/pull/1211) by @renovate[bot])
|
||||
- ([r1656](https://github.com/Suwayomi/Suwayomi-Server/commit/789ef0d7838f2d7790ca06caa31659340c1575e8)) Update dependency io.mockk:mockk to v1.13.16 ([#1210](https://github.com/Suwayomi/Suwayomi-Server/pull/1210) by @renovate[bot])
|
||||
- ([r1655](https://github.com/Suwayomi/Suwayomi-Server/commit/092db1106d1841ebf15ea01812102985aade11bc)) Update dependency ch.qos.logback:logback-classic to v1.5.16 ([#1209](https://github.com/Suwayomi/Suwayomi-Server/pull/1209) by @renovate[bot])
|
||||
- ([r1654](https://github.com/Suwayomi/Suwayomi-Server/commit/5f4b5bc57078a20e8badc129205a7ef80b57a047)) Update dependency io.javalin:javalin to v6.4.0 ([#1205](https://github.com/Suwayomi/Suwayomi-Server/pull/1205) by @renovate[bot])
|
||||
- ([r1653](https://github.com/Suwayomi/Suwayomi-Server/commit/32581fcd5a6d89f095de02800ab4f80ba7da1349)) [WIP] Customize JRE ([#1177](https://github.com/Suwayomi/Suwayomi-Server/pull/1177) by @Syer10)
|
||||
- ([r1652](https://github.com/Suwayomi/Suwayomi-Server/commit/b14d28c4068e7ee99b13c1228cfe74031a901381)) new icons ([#1222](https://github.com/Suwayomi/Suwayomi-Server/pull/1222) by @Robonau)
|
||||
- ([r1651](https://github.com/Suwayomi/Suwayomi-Server/commit/1d1535dc55f76ebeb4a90412f2f1e2e4cb69f82d)) Send dequeue download mutation response ([#1218](https://github.com/Suwayomi/Suwayomi-Server/pull/1218) by @schroda)
|
||||
- ([r1650](https://github.com/Suwayomi/Suwayomi-Server/commit/de942440e3dbd81207f048a1d13a5fbdb4a1c73c)) Handle missing track search results on bind ([#1196](https://github.com/Suwayomi/Suwayomi-Server/pull/1196) by @schroda)
|
||||
- ([r1649](https://github.com/Suwayomi/Suwayomi-Server/commit/b58fc39cf157a922c85de966d16b536c447217e5)) Update dependency io.github.oshai:kotlin-logging-jvm to v7.0.3 ([#1191](https://github.com/Suwayomi/Suwayomi-Server/pull/1191) by @renovate[bot])
|
||||
- ([r1648](https://github.com/Suwayomi/Suwayomi-Server/commit/2e3af25dd443bd7c880c47403bd2419414cc879b)) Fix usage of deprecated functions ([#1192](https://github.com/Suwayomi/Suwayomi-Server/pull/1192) by @Syer10)
|
||||
- ([r1647](https://github.com/Suwayomi/Suwayomi-Server/commit/1d541a30ae03fd69ba049d0611eab41c92785146)) Feature/update to exposed v0.57.0 ([#1150](https://github.com/Suwayomi/Suwayomi-Server/pull/1150) by @schroda)
|
||||
- ([r1646](https://github.com/Suwayomi/Suwayomi-Server/commit/f926714544ebeb21229ceace7f3e79462d38f5f9)) Update dependency com.pinterest.ktlint:ktlint-cli to v1.5.0 ([#1182](https://github.com/Suwayomi/Suwayomi-Server/pull/1182) by @renovate[bot])
|
||||
- ([r1645](https://github.com/Suwayomi/Suwayomi-Server/commit/f68849d3a5445519cb0d35cff8792d1c466a8c5b)) Update dependency com.russhwolf:multiplatform-settings-jvm to v1.3.0 ([#1176](https://github.com/Suwayomi/Suwayomi-Server/pull/1176) by @renovate[bot])
|
||||
- ([r1644](https://github.com/Suwayomi/Suwayomi-Server/commit/2111232f42a84935320da2e2771ea171c48fd754)) Update kotlin monorepo to v2.1.0 ([#1170](https://github.com/Suwayomi/Suwayomi-Server/pull/1170) by @renovate[bot], @Syer10)
|
||||
- ([r1643](https://github.com/Suwayomi/Suwayomi-Server/commit/088552bf56dbf9053442050137af2eae3b554bf3)) Fix Deprecation Warnings ([#1187](https://github.com/Suwayomi/Suwayomi-Server/pull/1187) by @Syer10)
|
||||
- ([r1642](https://github.com/Suwayomi/Suwayomi-Server/commit/3eabbc977007ec0c64f0fadb974470ec437cc39a)) Manually update GraphQL-Java to fix subscription data loaders ([#1186](https://github.com/Suwayomi/Suwayomi-Server/pull/1186) by @Syer10)
|
||||
- ([r1641](https://github.com/Suwayomi/Suwayomi-Server/commit/8e3b8df4978c838cfb93ad6061dec97b00112aab)) Update dependency com.android.tools.build:apksig to v8.7.3 ([#1179](https://github.com/Suwayomi/Suwayomi-Server/pull/1179) by @renovate[bot])
|
||||
- ([r1640](https://github.com/Suwayomi/Suwayomi-Server/commit/06a5aaaa729f29e83045691b57f8f74fb5e849ff)) Update dependency com.fasterxml.jackson.core:jackson-databind to v2.18.2 ([#1175](https://github.com/Suwayomi/Suwayomi-Server/pull/1175) by @renovate[bot])
|
||||
- ([r1639](https://github.com/Suwayomi/Suwayomi-Server/commit/97c4f14094964dce7446199652dd2ed448ef1d5d)) Update dependency org.jsoup:jsoup to v1.18.3 ([#1169](https://github.com/Suwayomi/Suwayomi-Server/pull/1169) by @renovate[bot])
|
||||
- ([r1638](https://github.com/Suwayomi/Suwayomi-Server/commit/1fa7f182356b530ddfc46c987b7345c34449327a)) Update plugin ktlint to v12.1.2 ([#1166](https://github.com/Suwayomi/Suwayomi-Server/pull/1166) by @renovate[bot])
|
||||
- ([r1637](https://github.com/Suwayomi/Suwayomi-Server/commit/b309d2fd4af31e424f103d8c4a6fb372f1420c78)) Update dependency gradle to v8.11.1 ([#1161](https://github.com/Suwayomi/Suwayomi-Server/pull/1161) by @renovate[bot])
|
||||
- ([r1636](https://github.com/Suwayomi/Suwayomi-Server/commit/372b56bb1bb1ee9e81b7081c1b40e52cb2f0065c)) add manga description ([#1165](https://github.com/Suwayomi/Suwayomi-Server/pull/1165) by @dejavui)
|
||||
- ([r1635](https://github.com/Suwayomi/Suwayomi-Server/commit/3325a36caefc6423b02cb4fd35685a61556bd946)) Allow cors with credentials ([#1163](https://github.com/Suwayomi/Suwayomi-Server/pull/1163) by @schroda)
|
||||
- ([r1634](https://github.com/Suwayomi/Suwayomi-Server/commit/38673bbff4fd0f5f3eb17e9d52ef20a9f91ddd28)) Handle missing credentials as being invalid ([#1164](https://github.com/Suwayomi/Suwayomi-Server/pull/1164) by @schroda)
|
||||
- ([r1633](https://github.com/Suwayomi/Suwayomi-Server/commit/fb51834153b33060779e93d3c460f55878a712cb)) Fix RealUrl ([#1162](https://github.com/Suwayomi/Suwayomi-Server/pull/1162) by @Syer10)
|
||||
- ([r1632](https://github.com/Suwayomi/Suwayomi-Server/commit/3a932a1e8acbc0242f5ae75a12e6bf404b101dd3)) [skip ci] Update plugin buildconfig to v5.5.1 ([#1157](https://github.com/Suwayomi/Suwayomi-Server/pull/1157) by @renovate[bot])
|
||||
- ([r1631](https://github.com/Suwayomi/Suwayomi-Server/commit/53c61bcb1784d702191d20e2f9239bab19b9ab33)) Serve webui on all unmatched routes ([#1156](https://github.com/Suwayomi/Suwayomi-Server/pull/1156) by @schroda)
|
||||
- ([r1630](https://github.com/Suwayomi/Suwayomi-Server/commit/6951b4b20d6993c4fada4cf50d745281b7cc3f03)) Remove "grapqhl log level" setting ([#1155](https://github.com/Suwayomi/Suwayomi-Server/pull/1155) by @schroda)
|
||||
- ([r1629](https://github.com/Suwayomi/Suwayomi-Server/commit/746f9f1a11e2691e88f1b21bfaa7b63c1aa0d66f)) [WIP] Switch to GraalJS Engine ([#793](https://github.com/Suwayomi/Suwayomi-Server/pull/793) by @Syer10)
|
||||
- ([r1628](https://github.com/Suwayomi/Suwayomi-Server/commit/9a7344ccbe9307fe056b542a5cdebd9540d75982)) Update dependency ch.qos.logback:logback-classic to v1.5.12 ([#1151](https://github.com/Suwayomi/Suwayomi-Server/pull/1151) by @renovate[bot])
|
||||
- ([r1627](https://github.com/Suwayomi/Suwayomi-Server/commit/ab2fb8747f1744301a4c098eee8fcb1c3b4f8a6f)) Update jackson monorepo to v2.18.1 ([#1148](https://github.com/Suwayomi/Suwayomi-Server/pull/1148) by @renovate[bot])
|
||||
- ([r1626](https://github.com/Suwayomi/Suwayomi-Server/commit/9cd8cb3d54635b10dadf4ec252b6e5ec95ac023c)) Update dependency io.javalin:javalin to v6 ([#1152](https://github.com/Suwayomi/Suwayomi-Server/pull/1152) by @renovate[bot], @Syer10)
|
||||
- ([r1625](https://github.com/Suwayomi/Suwayomi-Server/commit/ba1c2845b6d40dea32a148febdc59fe540e4e032)) chore(deps): update plugin buildconfig to v5 ([#1135](https://github.com/Suwayomi/Suwayomi-Server/pull/1135) by @renovate[bot])
|
||||
- ([r1624](https://github.com/Suwayomi/Suwayomi-Server/commit/065aa19e9eaed26709b5516bc9da95001101c9bd)) Update graphqlkotlin to v8 (major) ([#1143](https://github.com/Suwayomi/Suwayomi-Server/pull/1143) by @renovate[bot], @Syer10)
|
||||
- ([r1623](https://github.com/Suwayomi/Suwayomi-Server/commit/4c2a05c3a6943b37834e3ac29372c74d527b34df)) Update Java to 21 ([#1149](https://github.com/Suwayomi/Suwayomi-Server/pull/1149) by @Syer10)
|
||||
- ([r1622](https://github.com/Suwayomi/Suwayomi-Server/commit/fd45c0740c87c959d4a074f45fab77ac07478ea8)) [skip ci] Update Renovate config (by @Syer10)
|
||||
- ([r1621](https://github.com/Suwayomi/Suwayomi-Server/commit/e44bf920fad7042ed11499a0a014b94888331e09)) [skip ci] Update Renovate config (by @Syer10)
|
||||
- ([r1620](https://github.com/Suwayomi/Suwayomi-Server/commit/8a327b2dffc361e7ece397ea690c294c77b5a715)) [skip ci] chore(config): migrate config renovate.json ([#1144](https://github.com/Suwayomi/Suwayomi-Server/pull/1144) by @renovate[bot])
|
||||
- ([r1619](https://github.com/Suwayomi/Suwayomi-Server/commit/6ece7e2596dda11bfd4a97a391e5e5d60f8ecff9)) Update Renovate config (by @Syer10)
|
||||
- ([r1618](https://github.com/Suwayomi/Suwayomi-Server/commit/2e2ce98be3715dc1063dc07ed0114cbc87b051e3)) fix(deps): update dependency com.pinterest.ktlint:ktlint-cli to v1.4.1 ([#1126](https://github.com/Suwayomi/Suwayomi-Server/pull/1126) by @renovate[bot])
|
||||
- ([r1617](https://github.com/Suwayomi/Suwayomi-Server/commit/fe4c2392dbc83ba810d2279359b84cc95ac67848)) chore(deps): update plugin ktlint to v12 ([#1136](https://github.com/Suwayomi/Suwayomi-Server/pull/1136) by @renovate[bot])
|
||||
- ([r1616](https://github.com/Suwayomi/Suwayomi-Server/commit/320a0971b4aaac057ab33c0afa2942dc004680fc)) Fix/gql download subscription ([#1137](https://github.com/Suwayomi/Suwayomi-Server/pull/1137) by @schroda)
|
||||
- ([r1615](https://github.com/Suwayomi/Suwayomi-Server/commit/bfb70b6a055b48dbe5e02cda661add860b596db0)) fix(deps): update dependency com.ibm.icu:icu4j to v76 ([#1140](https://github.com/Suwayomi/Suwayomi-Server/pull/1140) by @renovate[bot])
|
||||
- ([r1614](https://github.com/Suwayomi/Suwayomi-Server/commit/a68af62748e3c3e0d210868d2fe98ae561093b01)) fix(deps): update twelvemonkeys to v3.12.0 ([#1133](https://github.com/Suwayomi/Suwayomi-Server/pull/1133) by @renovate[bot])
|
||||
- ([r1613](https://github.com/Suwayomi/Suwayomi-Server/commit/52bd5ce5cc8758930d1b39deaa15be2cae12ebb4)) fix(deps): update kotlinx-coroutines monorepo to v1.9.0 ([#1132](https://github.com/Suwayomi/Suwayomi-Server/pull/1132) by @renovate[bot])
|
||||
- ([r1612](https://github.com/Suwayomi/Suwayomi-Server/commit/b93d4863483d3d57690279b5c1e2343ea585f008)) fix(deps): update dependency org.bouncycastle:bcprov-jdk18on to v1.79 ([#1127](https://github.com/Suwayomi/Suwayomi-Server/pull/1127) by @renovate[bot])
|
||||
- ([r1611](https://github.com/Suwayomi/Suwayomi-Server/commit/d193c58e5f86f74d3dbf67aca8b16694a68dc119)) fix(deps): update dependency androidx.annotation:annotation to v1.9.1 ([#1121](https://github.com/Suwayomi/Suwayomi-Server/pull/1121) by @renovate[bot])
|
||||
- ([r1610](https://github.com/Suwayomi/Suwayomi-Server/commit/6ac2a61793eea51ceccd663d2c8a2570709489b9)) Update serialization to v1.7.3 ([#1119](https://github.com/Suwayomi/Suwayomi-Server/pull/1119) by @renovate[bot])
|
||||
- ([r1609](https://github.com/Suwayomi/Suwayomi-Server/commit/a45c6f2197c204b498d0d5f669b2872edd26d426)) Update kotlin monorepo to v2.0.21 ([#1117](https://github.com/Suwayomi/Suwayomi-Server/pull/1117) by @renovate[bot])
|
||||
- ([r1608](https://github.com/Suwayomi/Suwayomi-Server/commit/71d639bf19f7d25f885a29b16dcf776a9ea2c6ee)) Update dependency io.mockk:mockk to v1.13.13 ([#1116](https://github.com/Suwayomi/Suwayomi-Server/pull/1116) by @renovate[bot])
|
||||
- ([r1607](https://github.com/Suwayomi/Suwayomi-Server/commit/9a51472726079b30f73eef8c19716061328c715b)) Update Titles from the Source ([#1115](https://github.com/Suwayomi/Suwayomi-Server/pull/1115) by @Syer10)
|
||||
- ([r1606](https://github.com/Suwayomi/Suwayomi-Server/commit/0670f298cd70535c8b719af6bd86600a5124369c)) Switch from Kodein to Koin ([#1112](https://github.com/Suwayomi/Suwayomi-Server/pull/1112) by @Syer10)
|
||||
- ([r1605](https://github.com/Suwayomi/Suwayomi-Server/commit/aa1e98544b8a11812675a27b4d95db6c7e33a283)) Fix/invalid server settings gql mutation request ([#1092](https://github.com/Suwayomi/Suwayomi-Server/pull/1092) by @schroda)
|
||||
- ([r1604](https://github.com/Suwayomi/Suwayomi-Server/commit/fa4607e232fd94a8b299797cb23d0e630ccb8c04)) Update dependency gradle to v8.11 ([#1091](https://github.com/Suwayomi/Suwayomi-Server/pull/1091) by @renovate[bot])
|
||||
- ([r1603](https://github.com/Suwayomi/Suwayomi-Server/commit/cb46420c09d08f9c7337830e2ea2e53bb0c8a711)) Update dependency com.android.tools.build:apksig to v8 ([#1076](https://github.com/Suwayomi/Suwayomi-Server/pull/1076) by @renovate[bot])
|
||||
- ([r1602](https://github.com/Suwayomi/Suwayomi-Server/commit/d88014fa904421c213ceab0b59fe5139fa52d1e8)) Update xmlserialization ([#1075](https://github.com/Suwayomi/Suwayomi-Server/pull/1075) by @renovate[bot], @Syer10)
|
||||
- ([r1601](https://github.com/Suwayomi/Suwayomi-Server/commit/168b76cb0c2c424f41380379b3da534c2ebcdd01)) Feature/graphql download queue subscription send only updates ([#1011](https://github.com/Suwayomi/Suwayomi-Server/pull/1011) by @schroda)
|
||||
- ([r1600](https://github.com/Suwayomi/Suwayomi-Server/commit/f5680c6d695ddd58c2cf38538ec54cf44db3b857)) Switch to Koin from Injekt ([#1109](https://github.com/Suwayomi/Suwayomi-Server/pull/1109) by @schroda)
|
||||
- ([r1599](https://github.com/Suwayomi/Suwayomi-Server/commit/654a3cc7ed6134afc76be529924317ff5c1f0a9b)) Use Backup.serializer() ([#1088](https://github.com/Suwayomi/Suwayomi-Server/pull/1088) by @Syer10)
|
||||
- ([r1598](https://github.com/Suwayomi/Suwayomi-Server/commit/841cdc474f393433f33c928eb5e20b7bc35a1a01)) Remove Broken Sources and Broken History ([#1084](https://github.com/Suwayomi/Suwayomi-Server/pull/1084) by @Syer10)
|
||||
- ([r1597](https://github.com/Suwayomi/Suwayomi-Server/commit/0adbea3a43c021c072e276b0bfe867527006a5bd)) Remove manga artist, author length limit ([#1080](https://github.com/Suwayomi/Suwayomi-Server/pull/1080) by @schroda)
|
||||
- ([r1596](https://github.com/Suwayomi/Suwayomi-Server/commit/e12bada052d1622f68b9061a174afb9b273dbf9c)) Use correct sync id ([#1079](https://github.com/Suwayomi/Suwayomi-Server/pull/1079) by @schroda)
|
||||
- ([r1595](https://github.com/Suwayomi/Suwayomi-Server/commit/68dbefc46f1313a306404e6780c4e987c8018bf1)) Update dependency gradle to v8.10.1 ([#1072](https://github.com/Suwayomi/Suwayomi-Server/pull/1072) by @renovate[bot])
|
||||
- ([r1594](https://github.com/Suwayomi/Suwayomi-Server/commit/9d71e9b177cb88bfe1ec954af57593ce9f366651)) Update twelvemonkeys to v3.11.0 ([#1069](https://github.com/Suwayomi/Suwayomi-Server/pull/1069) by @renovate[bot])
|
||||
- ([r1593](https://github.com/Suwayomi/Suwayomi-Server/commit/5b5801c2cfd096886871305cfbb915565ae9ec4f)) Update dependency com.squareup.okio:okio to v3.9.1 ([#1071](https://github.com/Suwayomi/Suwayomi-Server/pull/1071) by @renovate[bot])
|
||||
- ([r1592](https://github.com/Suwayomi/Suwayomi-Server/commit/df1cc2b8e993f542719dfab23d0edcaf34228024)) fix(flaresolverr): fix cookie expiry for flaresolverr ([#1070](https://github.com/Suwayomi/Suwayomi-Server/pull/1070) by @Belphemur)
|
||||
- ([r1591](https://github.com/Suwayomi/Suwayomi-Server/commit/18d399b3f748ca0863177a791befdc0096bf7610)) Update settings to v1.2.0 ([#1068](https://github.com/Suwayomi/Suwayomi-Server/pull/1068) by @renovate[bot])
|
||||
- ([r1590](https://github.com/Suwayomi/Suwayomi-Server/commit/e9687fd18207941261d89f4b49f0d92b6e353a0f)) Update serialization to v1.7.2 ([#1067](https://github.com/Suwayomi/Suwayomi-Server/pull/1067) by @renovate[bot])
|
||||
- ([r1589](https://github.com/Suwayomi/Suwayomi-Server/commit/79137a074cba1d07a3db46a7920a340e26118b0e)) Update plugin download to v5.6.0 ([#1066](https://github.com/Suwayomi/Suwayomi-Server/pull/1066) by @renovate[bot])
|
||||
- ([r1588](https://github.com/Suwayomi/Suwayomi-Server/commit/dae55ca386a1313cb8f7b5aa6ee065de51d25a95)) Update graphqlkotlin to v6.8.5 ([#1064](https://github.com/Suwayomi/Suwayomi-Server/pull/1064) by @renovate[bot], @Syer10)
|
||||
- ([r1587](https://github.com/Suwayomi/Suwayomi-Server/commit/b7f040d89aada015f381ab0ab69c8fb88d026407)) Update dependency org.jsoup:jsoup to v1.18.1 ([#1060](https://github.com/Suwayomi/Suwayomi-Server/pull/1060) by @renovate[bot])
|
||||
- ([r1586](https://github.com/Suwayomi/Suwayomi-Server/commit/dc69df9f4fcb7895c80514630d0b1ead38362784)) Update dependency com.squareup.okio:okio to v3.9.0 ([#1056](https://github.com/Suwayomi/Suwayomi-Server/pull/1056) by @renovate[bot])
|
||||
- ([r1585](https://github.com/Suwayomi/Suwayomi-Server/commit/6c1fbfa63b6b1dfebac59b0e59e153338cce654a)) [skip ci] Formatting (by @Syer10)
|
||||
- ([r1584](https://github.com/Suwayomi/Suwayomi-Server/commit/e968a2195a325988fd1b05e7343d8bff08be0e37)) [skip ci] Update dependency com.pinterest.ktlint:ktlint-cli to v1.3.1 ([#1055](https://github.com/Suwayomi/Suwayomi-Server/pull/1055) by @renovate[bot])
|
||||
- ([r1583](https://github.com/Suwayomi/Suwayomi-Server/commit/000bcea181c69a259a51901884596ee336704dbc)) [skip ci] Update SystemTray (by @Syer10)
|
||||
- ([r1582](https://github.com/Suwayomi/Suwayomi-Server/commit/8edf508453949be7054d3b9c15922b79b7882463)) [skip ci] Update dependency org.apache.commons:commons-compress to v1.27.1 ([#1058](https://github.com/Suwayomi/Suwayomi-Server/pull/1058) by @renovate[bot])
|
||||
- ([r1581](https://github.com/Suwayomi/Suwayomi-Server/commit/bc9cc50130c9b1a49aa35fff8c1867eb682ed604)) [skip ci] Update dependency org.bouncycastle:bcprov-jdk18on to v1.78.1 ([#1059](https://github.com/Suwayomi/Suwayomi-Server/pull/1059) by @renovate[bot])
|
||||
- ([r1580](https://github.com/Suwayomi/Suwayomi-Server/commit/71091d88fc6f441d818a235bac7f436bc4ce857f)) [skip ci] Update dependency com.android.tools.build:apksig to v7.4.2 ([#1049](https://github.com/Suwayomi/Suwayomi-Server/pull/1049) by @renovate[bot])
|
||||
- ([r1579](https://github.com/Suwayomi/Suwayomi-Server/commit/70c1d7e21f52b7beb12a70794b396522f1c44247)) [skip ci] Update dependency io.github.config4k:config4k to v0.7.0 ([#1057](https://github.com/Suwayomi/Suwayomi-Server/pull/1057) by @renovate[bot])
|
||||
- ([r1578](https://github.com/Suwayomi/Suwayomi-Server/commit/c07920978ed33e5399a6116766a931f0f62a049f)) Update tachiyomiorg/issue-moderator-action action to v2 ([#1032](https://github.com/Suwayomi/Suwayomi-Server/pull/1032) by @renovate[bot], @Syer10)
|
||||
- ([r1577](https://github.com/Suwayomi/Suwayomi-Server/commit/954b2919ac7b9e1d4dccfca68768c293cfd09095)) [skip ci] Update plugin ktlint to v11.6.1 ([#1043](https://github.com/Suwayomi/Suwayomi-Server/pull/1043) by @renovate[bot])
|
||||
- ([r1576](https://github.com/Suwayomi/Suwayomi-Server/commit/c630f731ed90ba6581487ac7f295315b405593c2)) [skip ci] Update dependency androidx.annotation:annotation to v1.8.2 ([#1046](https://github.com/Suwayomi/Suwayomi-Server/pull/1046) by @renovate[bot])
|
||||
- ([r1575](https://github.com/Suwayomi/Suwayomi-Server/commit/aaefa7f74e5daa96315ef10e8beead3d16c58acd)) [skip ci] Update coroutines to v1.8.1 ([#1045](https://github.com/Suwayomi/Suwayomi-Server/pull/1045) by @renovate[bot])
|
||||
- ([r1574](https://github.com/Suwayomi/Suwayomi-Server/commit/2ec6b471f15aa08d204c81b217e786ad04f5e378)) [skip ci] Update dependency gradle to v8.10 ([#1047](https://github.com/Suwayomi/Suwayomi-Server/pull/1047) by @renovate[bot])
|
||||
- ([r1573](https://github.com/Suwayomi/Suwayomi-Server/commit/a5c5ab68d278d6527731bdffb43a2830396de4dd)) [skip ci] Ktlint (by @Syer10)
|
||||
- ([r1572](https://github.com/Suwayomi/Suwayomi-Server/commit/fb045c501af783e18bc05622bd207f37e5c25ae5)) Update Kotlin to 2.0.20 (by @Syer10)
|
||||
- ([r1571](https://github.com/Suwayomi/Suwayomi-Server/commit/6a6e411492cd9c9c7a31a420124c74da89f74c0f)) [skip ci] Update dependency net.harawata:appdirs to v1.2.2 ([#1033](https://github.com/Suwayomi/Suwayomi-Server/pull/1033) by @renovate[bot])
|
||||
- ([r1570](https://github.com/Suwayomi/Suwayomi-Server/commit/76aac330fc111c3748185e0605c7ef321aafd5fb)) [skip ci] Update dependency org.slf4j:slf4j-api to v2.0.16 ([#1034](https://github.com/Suwayomi/Suwayomi-Server/pull/1034) by @renovate[bot])
|
||||
- ([r1569](https://github.com/Suwayomi/Suwayomi-Server/commit/07bdf31f66805dd09c962e4e0a542dce541371de)) [skip ci] Update okhttp monorepo to v5.0.0-alpha.14 ([#1039](https://github.com/Suwayomi/Suwayomi-Server/pull/1039) by @renovate[bot])
|
||||
- ([r1568](https://github.com/Suwayomi/Suwayomi-Server/commit/fe14928af6b7ff7cbf339806f0880c6b64f56eb6)) [skip ci] Update rhino to v1.7.15 ([#1044](https://github.com/Suwayomi/Suwayomi-Server/pull/1044) by @renovate[bot])
|
||||
- ([r1567](https://github.com/Suwayomi/Suwayomi-Server/commit/ad0c1033a4b087b6f2c13be3c8f9a5a3ad275002)) [skip ci] Update GitHub Artifact Actions to v4 ([#1040](https://github.com/Suwayomi/Suwayomi-Server/pull/1040) by @renovate[bot])
|
||||
- ([r1566](https://github.com/Suwayomi/Suwayomi-Server/commit/0c2448fb99ca7cf77a6a02dd0a7a277ee7cfa2f9)) Update gradle build action ([#1035](https://github.com/Suwayomi/Suwayomi-Server/pull/1035) by @Syer10)
|
||||
- ([r1565](https://github.com/Suwayomi/Suwayomi-Server/commit/cedda145a5caa16b89d767cc393efe52d40d7bee)) Update gradle/gradle-build-action action to v3 ([#1029](https://github.com/Suwayomi/Suwayomi-Server/pull/1029) by @renovate[bot])
|
||||
- ([r1564](https://github.com/Suwayomi/Suwayomi-Server/commit/ee73187f1a092db69136a2a11daa40d2cf9df581)) [skip ci] Update gradle/wrapper-validation-action action to v3 ([#1030](https://github.com/Suwayomi/Suwayomi-Server/pull/1030) by @renovate[bot])
|
||||
- ([r1563](https://github.com/Suwayomi/Suwayomi-Server/commit/89f91d680064e6c3330bdc83d2f34b0c6f1f92cd)) [skip ci] Update actions/checkout action to v4 ([#1028](https://github.com/Suwayomi/Suwayomi-Server/pull/1028) by @renovate[bot])
|
||||
- ([r1562](https://github.com/Suwayomi/Suwayomi-Server/commit/6714827694842ff55085495d016049936d74e99e)) [skip ci] Update softprops/action-gh-release action to v2 ([#1031](https://github.com/Suwayomi/Suwayomi-Server/pull/1031) by @renovate[bot])
|
||||
- ([r1561](https://github.com/Suwayomi/Suwayomi-Server/commit/aad73f7d199173d4c75dab3759753e8250c25f91)) [skip ci] Update dependency io.mockk:mockk to v1.13.12 ([#1026](https://github.com/Suwayomi/Suwayomi-Server/pull/1026) by @renovate[bot])
|
||||
- ([r1560](https://github.com/Suwayomi/Suwayomi-Server/commit/c5985de1c34839a561c421b978e90f11825c39e5)) [skip ci] Update dependency com.typesafe:config to v1.4.3 ([#1025](https://github.com/Suwayomi/Suwayomi-Server/pull/1025) by @renovate[bot])
|
||||
- ([r1559](https://github.com/Suwayomi/Suwayomi-Server/commit/86a5b0879a9d65589dfe0b241c8a2539a30e9a20)) Add renovate.json ([#1024](https://github.com/Suwayomi/Suwayomi-Server/pull/1024) by @renovate[bot])
|
||||
- ([r1558](https://github.com/Suwayomi/Suwayomi-Server/commit/414972d54588b7d034902292b5b188b248e1fe27)) Feature/update log file rotation ([#1023](https://github.com/Suwayomi/Suwayomi-Server/pull/1023) by @schroda)
|
||||
- ([r1557](https://github.com/Suwayomi/Suwayomi-Server/commit/9a74ae5844fd059bf4e8e921a36e48348aa635ca)) feat(comicinfo): add date fields to comic info ([#1021](https://github.com/Suwayomi/Suwayomi-Server/pull/1021) by @Belphemur, @Syer10)
|
||||
- ([r1556](https://github.com/Suwayomi/Suwayomi-Server/commit/301980ab1439605a69c47a04425d34e1c07cfffd)) fix(flaresolverr): support possible rewrite flaresolverr ([#1020](https://github.com/Suwayomi/Suwayomi-Server/pull/1020) by @Belphemur)
|
||||
- ([r1555](https://github.com/Suwayomi/Suwayomi-Server/commit/5dced82e5a51929b1e6d65ee8a669564c21d02c9)) Fix/missed automated task execution failure crashes server on startup ([#1019](https://github.com/Suwayomi/Suwayomi-Server/pull/1019) by @schroda)
|
||||
- ([r1554](https://github.com/Suwayomi/Suwayomi-Server/commit/9a1e4df4084987744ffee37347542c5bbc83a1c9)) Fix/server startup blocked by synchronous tasks ([#1018](https://github.com/Suwayomi/Suwayomi-Server/pull/1018) by @schroda)
|
||||
- ([r1553](https://github.com/Suwayomi/Suwayomi-Server/commit/5b08b8123939e6befe739056569bc7afc50c665b)) Initialize manga on add to library ([#1016](https://github.com/Suwayomi/Suwayomi-Server/pull/1016) by @schroda)
|
||||
- ([r1552](https://github.com/Suwayomi/Suwayomi-Server/commit/7fac538ba3c873d1ba69de40ab6c1111f77d3714)) Initialize uninitialized manga during global update ([#1015](https://github.com/Suwayomi/Suwayomi-Server/pull/1015) by @schroda)
|
||||
- ([r1551](https://github.com/Suwayomi/Suwayomi-Server/commit/ef6be74ec25d0b76db9dc55c986605a90916af36)) Fix/chapter downloaded check ([#1012](https://github.com/Suwayomi/Suwayomi-Server/pull/1012) by @schroda)
|
||||
- ([r1550](https://github.com/Suwayomi/Suwayomi-Server/commit/9f4958724552441bf4587d11e4f5cbca9f29c858)) [skip ci] Update readme ([#1008](https://github.com/Suwayomi/Suwayomi-Server/pull/1008) by @schroda)
|
||||
- ([r1549](https://github.com/Suwayomi/Suwayomi-Server/commit/b7b733f351ef3ab0ad69d21812c4d3834ca84185)) [skip ci] Feature/update readme ([#1005](https://github.com/Suwayomi/Suwayomi-Server/pull/1005) by @schroda)
|
||||
- ([r1548](https://github.com/Suwayomi/Suwayomi-Server/commit/06bfc33e72de3879b3b5da75ada6d98aa0ad18af)) Always update uninitialized manga in global update ([#997](https://github.com/Suwayomi/Suwayomi-Server/pull/997) by @schroda)
|
||||
- ([r1547](https://github.com/Suwayomi/Suwayomi-Server/commit/fbcd55d6c53d1b7be1ec282ed61648d8fa8467b8)) Add "hasDuplicatedChapters" field to gql MangaType ([#995](https://github.com/Suwayomi/Suwayomi-Server/pull/995) by @schroda)
|
||||
- ([r1546](https://github.com/Suwayomi/Suwayomi-Server/commit/2484b5f14b0ca14e36f89a798810302825aa9bfa)) Fix/downloaded chapters with lost page count ([#994](https://github.com/Suwayomi/Suwayomi-Server/pull/994) by @schroda)
|
||||
- ([r1545](https://github.com/Suwayomi/Suwayomi-Server/commit/69826596580768e4927c09c9d3f45d4135e9e56c)) Fix/inserting duplicated chapters into database ([#991](https://github.com/Suwayomi/Suwayomi-Server/pull/991) by @schroda)
|
||||
- ([r1544](https://github.com/Suwayomi/Suwayomi-Server/commit/9e006166a8af73d7c281f0cb3d51cfabc1b39822)) Add setting to use the flaresolverr response ([#990](https://github.com/Suwayomi/Suwayomi-Server/pull/990) by @AeonLucid)
|
||||
- ([r1543](https://github.com/Suwayomi/Suwayomi-Server/commit/25a62e33a1893b07689ff5741f7e14051ba202b4)) Fix PersistentCookieStore for domains with an underscore ([#989](https://github.com/Suwayomi/Suwayomi-Server/pull/989) by @AeonLucid)
|
||||
- ([r1542](https://github.com/Suwayomi/Suwayomi-Server/commit/d05ed0a56c60e4c49d4160b15a30ab713653b55a)) Fix SOCKS5 authentication by setting a default Authenticator ([#988](https://github.com/Suwayomi/Suwayomi-Server/pull/988) by @AeonLucid)
|
||||
- ([r1541](https://github.com/Suwayomi/Suwayomi-Server/commit/eaffb2755cfae436f4c2b1affe9c125046c2cc55)) Cleanup only created auto backup files ([#984](https://github.com/Suwayomi/Suwayomi-Server/pull/984) by @schroda)
|
||||
- ([r1540](https://github.com/Suwayomi/Suwayomi-Server/commit/e0fcae2ae35c3d6d4702041b6f489f208f6533ef)) Handle deprecated gql sort again ([#983](https://github.com/Suwayomi/Suwayomi-Server/pull/983) by @schroda)
|
||||
- ([r1539](https://github.com/Suwayomi/Suwayomi-Server/commit/af9ad611744ced0766cdae980888afec5697201f)) Feature/gql simpilify filtering for multiple values ([#960](https://github.com/Suwayomi/Suwayomi-Server/pull/960) by @schroda)
|
||||
- ([r1538](https://github.com/Suwayomi/Suwayomi-Server/commit/7c54ad54fc96d37c9d8b59d0dcf2347ebffb7a98)) Sort gql queries by multiple columns ([#963](https://github.com/Suwayomi/Suwayomi-Server/pull/963) by @schroda)
|
||||
- ([r1537](https://github.com/Suwayomi/Suwayomi-Server/commit/aee9f1032c05e3493124d078ac8a4c0acc5778e9)) Exit early in case of empty chapter id list ([#980](https://github.com/Suwayomi/Suwayomi-Server/pull/980) by @schroda)
|
||||
- ([r1536](https://github.com/Suwayomi/Suwayomi-Server/commit/f7d0605e0a8b3fb296c8ef186bc67a7ee650a567)) [skip ci] Remove docker commands ([#972](https://github.com/Suwayomi/Suwayomi-Server/pull/972) by @Robonau)
|
||||
|
||||
Contributors:
|
||||
@schroda, @kaaass, @Syer10, @renovate[bot], @ShirishSaxena, @showyee, @cpiber, @zeedif, @Robonau, @dejavui, @Belphemur, @AeonLucid
|
||||
|
||||
|
||||
## [Suwayomi-WebUI Changelog](https://github.com/Suwayomi/Suwayomi-WebUI/blob/master/CHANGELOG.md#v151-r2467)
|
||||
|
||||
|
||||
# Server: v1.1.1 + WevUI: v1.1.0
|
||||
## TL;DR
|
||||
- WebUI update bugfixes
|
||||
|
||||
## Suwayomi-Server Changelog
|
||||
- ([r1534](https://github.com/Suwayomi/Suwayomi-Server/commit/d9cb54b28593e4df87522090f03a6e5b9c7d9fa2)) Compare webUI version with bundled webUI version ([#969](https://github.com/Suwayomi/Suwayomi-Server/pull/969) by @schroda)
|
||||
- ([r1533](https://github.com/Suwayomi/Suwayomi-Server/commit/f738a162d3cd4582612d4986b3d3887e1c309bdd)) Support for "STABLEPREVIEW" webUI version ([#970](https://github.com/Suwayomi/Suwayomi-Server/pull/970) by @schroda)
|
||||
|
||||
# Server: v1.1.0 + WevUI: v1.1.0
|
||||
## TL;DR
|
||||
- Update Manga Info in browse
|
||||
- Full Tracking support
|
||||
- Import Tracking from backups
|
||||
- Improved support for library filters
|
||||
- Improved thumbnail handling
|
||||
- Many minor bugfixes
|
||||
- WebUI changes: https://github.com/Suwayomi/Suwayomi-WebUI/releases/tag/v1.1.0
|
||||
|
||||
## Suwayomi-Server Changelog
|
||||
- ([r1531](https://github.com/Suwayomi/Suwayomi-Server/commit/ecd1604e25a17a6ef68d568b5d81e69f6e9f7702)) Update metadata in source browse only if new data is not null ([#962](https://github.com/Suwayomi/Suwayomi-Server/pull/962) by @schroda)
|
||||
- ([r1530](https://github.com/Suwayomi/Suwayomi-Server/commit/0f061900afbd4036b4d081bcb3f39e4f60160ac3)) Fix browse source ([#961](https://github.com/Suwayomi/Suwayomi-Server/pull/961) by @Syer10)
|
||||
- ([r1529](https://github.com/Suwayomi/Suwayomi-Server/commit/c47f5ea85e75c7119d828a19eda392b2fb9faecd)) [skip ci] doc: add NixOS installation ([#959](https://github.com/Suwayomi/Suwayomi-Server/pull/959) by @RatCornu)
|
||||
- ([r1528](https://github.com/Suwayomi/Suwayomi-Server/commit/306eb0e3c774b5a8d0d2d4ade2d7091904fe6e58)) Update manga info when browsing if not in library ([#958](https://github.com/Suwayomi/Suwayomi-Server/pull/958) by @Syer10)
|
||||
- ([r1527](https://github.com/Suwayomi/Suwayomi-Server/commit/e64025ded814a15129999586133d84085e0c5779)) Correctly set name of logger ([#956](https://github.com/Suwayomi/Suwayomi-Server/pull/956) by @schroda)
|
||||
- ([r1526](https://github.com/Suwayomi/Suwayomi-Server/commit/c1fe2da636b675091ec0ac93162764883938ffc6)) Fix/failing thumbnail requests with http 410 ([#955](https://github.com/Suwayomi/Suwayomi-Server/pull/955) by @schroda)
|
||||
- ([r1525](https://github.com/Suwayomi/Suwayomi-Server/commit/ff23f58a4f4e8e0b4d459957f0e0701265e0c364)) Support partial mutation responses ([#954](https://github.com/Suwayomi/Suwayomi-Server/pull/954) by @schroda)
|
||||
- ([r1524](https://github.com/Suwayomi/Suwayomi-Server/commit/fc2f5ffdf9c1e8675f9b978031a49fb4ce3af601)) Fix/failing track progress update for logged out trackers ([#953](https://github.com/Suwayomi/Suwayomi-Server/pull/953) by @schroda)
|
||||
- ([r1523](https://github.com/Suwayomi/Suwayomi-Server/commit/6dd9ed7fb0816b2f163bec43d04c433099e7e529)) Fix/prevent importing unsupported trackers from backup II ([#945](https://github.com/Suwayomi/Suwayomi-Server/pull/945) by @schroda)
|
||||
- ([r1522](https://github.com/Suwayomi/Suwayomi-Server/commit/2f362abb91be875e943b1364eb86d70a4144dd6f)) Prevent importing unsupported tracker from backup ([#944](https://github.com/Suwayomi/Suwayomi-Server/pull/944) by @schroda)
|
||||
- ([r1521](https://github.com/Suwayomi/Suwayomi-Server/commit/96807a64cf1b13b6db655d46e90c42717170ce62)) [skip ci] Update README.md ([#941](https://github.com/Suwayomi/Suwayomi-Server/pull/941) by @FumoVite)
|
||||
- ([r1520](https://github.com/Suwayomi/Suwayomi-Server/commit/7df5f1c4c4408cfbbd56697ba10f018393df2b4a)) Feature/backup tracking ([#940](https://github.com/Suwayomi/Suwayomi-Server/pull/940) by @schroda)
|
||||
- ([r1519](https://github.com/Suwayomi/Suwayomi-Server/commit/cf1ede9cf70a2d72a7ff84b9ead24a394ceee2ce)) Update lastPageRead on chapter update ([#939](https://github.com/Suwayomi/Suwayomi-Server/pull/939) by @schroda)
|
||||
- ([r1518](https://github.com/Suwayomi/Suwayomi-Server/commit/729385588a3d8e06ec8be38865a12c47e88f6bcb)) Prevent greater last page read than page count ([#938](https://github.com/Suwayomi/Suwayomi-Server/pull/938) by @schroda)
|
||||
- ([r1517](https://github.com/Suwayomi/Suwayomi-Server/commit/668d5cf8f02e35cc53d1430a239ae67837c64f51)) Prevent IndexOutOfBoundsException when removing duplicated chapters ([#935](https://github.com/Suwayomi/Suwayomi-Server/pull/935) by @schroda)
|
||||
- ([r1516](https://github.com/Suwayomi/Suwayomi-Server/commit/72b1b5b0f9b86f82a0e203802d9a4b6339277c01)) Exit track progress update early in case new chapter is same as current local ([#937](https://github.com/Suwayomi/Suwayomi-Server/pull/937) by @schroda)
|
||||
- ([r1515](https://github.com/Suwayomi/Suwayomi-Server/commit/fbf726c17434212cdf94b39f52a25a0050d77287)) Use "AsyncExecutionStrategy" for mutations ([#932](https://github.com/Suwayomi/Suwayomi-Server/pull/932) by @schroda)
|
||||
- ([r1514](https://github.com/Suwayomi/Suwayomi-Server/commit/c441eed84773fdc295e6d004e4f4628453b54659)) Exclude duplicated chapters from auto download limit ([#923](https://github.com/Suwayomi/Suwayomi-Server/pull/923) by @schroda)
|
||||
- ([r1513](https://github.com/Suwayomi/Suwayomi-Server/commit/e8e83ed49caac2d25f29073d1bd3b5b385aa2d98)) Remove duplicated mangas from gql "mangas" query ([#924](https://github.com/Suwayomi/Suwayomi-Server/pull/924) by @schroda)
|
||||
- ([r1512](https://github.com/Suwayomi/Suwayomi-Server/commit/cdc21b067c1a341d68ea7a9c1ee565dc3959f552)) Fix/recognition of already downloaded chapters ([#922](https://github.com/Suwayomi/Suwayomi-Server/pull/922) by @schroda)
|
||||
- ([r1511](https://github.com/Suwayomi/Suwayomi-Server/commit/48e19f7914fee1ea1789b217d5df9b05acb49203)) Feature/auto download of new chapters improve handling of unhandable reuploads ([#921](https://github.com/Suwayomi/Suwayomi-Server/pull/921) by @schroda)
|
||||
- ([r1510](https://github.com/Suwayomi/Suwayomi-Server/commit/89dd570b3057bee34643858b4a42bfac7d88a82b)) Add mutation to fetch the latest track data from the tracker ([#920](https://github.com/Suwayomi/Suwayomi-Server/pull/920) by @schroda, @Syer10)
|
||||
- ([r1509](https://github.com/Suwayomi/Suwayomi-Server/commit/16474d4328651f1236722556b7f59628a0f9dbda)) Feature/tracking gql add option to delete remote binding on tracker ([#919](https://github.com/Suwayomi/Suwayomi-Server/pull/919) by @schroda, @Syer10)
|
||||
- ([r1508](https://github.com/Suwayomi/Suwayomi-Server/commit/9db612bf0317950d0291047b9ee64a0787e49bf2)) Move trigger for track progress update to client ([#918](https://github.com/Suwayomi/Suwayomi-Server/pull/918) by @schroda)
|
||||
- ([r1507](https://github.com/Suwayomi/Suwayomi-Server/commit/7d92dbc5c0a47176099eb310eaf17a4788ba2ce4)) Fix/tracking progress update in case local chapter is smaller than remote ([#917](https://github.com/Suwayomi/Suwayomi-Server/pull/917) by @schroda)
|
||||
- ([r1506](https://github.com/Suwayomi/Suwayomi-Server/commit/a9efca86870cec6d74f58535e2e007eb6c8831c2)) Add chapter bookmark count field to MangaType ([#912](https://github.com/Suwayomi/Suwayomi-Server/pull/912) by @schroda)
|
||||
- ([r1505](https://github.com/Suwayomi/Suwayomi-Server/commit/dbfea5d02b898884fdeb2be2959fe8a73a465704)) Update inLibraryAt timestamp when adding manga to library ([#911](https://github.com/Suwayomi/Suwayomi-Server/pull/911) by @schroda)
|
||||
- ([r1504](https://github.com/Suwayomi/Suwayomi-Server/commit/a6b05c4a2759d0d5f834a54cad6c8417fe49a0d2)) Feature/refresh outdated thumbnail url on fetch failure ([#910](https://github.com/Suwayomi/Suwayomi-Server/pull/910) by @schroda)
|
||||
- ([r1503](https://github.com/Suwayomi/Suwayomi-Server/commit/6d539d34040c4e95692b57ce4fedfbeaa73083d0)) Fix/update subscription clear data loader cache ([#908](https://github.com/Suwayomi/Suwayomi-Server/pull/908) by @schroda)
|
||||
- ([r1502](https://github.com/Suwayomi/Suwayomi-Server/commit/b2aff1efc9e6527e70ba519e5171096394e6ccf7)) Fix MAL after restarting the server ([#903](https://github.com/Suwayomi/Suwayomi-Server/pull/903) by @Syer10)
|
||||
- ([r1501](https://github.com/Suwayomi/Suwayomi-Server/commit/8a20a1ef5094efc05426ed420bbde40358fdf2dd)) Add first unread chapter field to MangaType ([#900](https://github.com/Suwayomi/Suwayomi-Server/pull/900) by @schroda)
|
||||
- ([r1500](https://github.com/Suwayomi/Suwayomi-Server/commit/33cbfa9751c3ef7a6babfcff9595782cbac5acae)) Fix/electron launch error not logged ([#895](https://github.com/Suwayomi/Suwayomi-Server/pull/895) by @schroda)
|
||||
- ([r1499](https://github.com/Suwayomi/Suwayomi-Server/commit/b95a8d44d4bb7c94a04e66b3d6cc0fc101f4880b)) Always fetch thumbnail of manga from local source ([#898](https://github.com/Suwayomi/Suwayomi-Server/pull/898) by @schroda)
|
||||
|
||||
## [Suwayomi-WebUI Changelog](https://github.com/Suwayomi/Suwayomi-WebUI/blob/master/CHANGELOG.md#v110-r1689)
|
||||
|
||||
# Server: v1.0.0 + WevUI: r1409
|
||||
## TL;DR
|
||||
- GraphQL API
|
||||
|
||||
+4
-6
@@ -8,9 +8,9 @@ Checkout [This Kanban Board](https://github.com/Suwayomi/Suwayomi-Server/project
|
||||
- We hate big pull requests, make them as small as possible, change one meaningful thing. Spam pull requests, we don't mind.
|
||||
|
||||
### Project goals and vision
|
||||
- Porting Tachiyomi and covering its features
|
||||
- Syncing with Tachiyomi, [main issue](https://github.com/Suwayomi/Suwayomi-Server/issues/159)
|
||||
- Generally rejecting features that Tachiyomi(main app) doesn't have,
|
||||
- Porting Mihon (Tachiyomi) and covering its features
|
||||
- Syncing with Mihon (Tachiyomi), [main issue](https://github.com/Suwayomi/Suwayomi-Server/issues/159)
|
||||
- Generally rejecting features that Mihon (Tachiyomi) (main app) doesn't have,
|
||||
- Unless it's something that makes sense for desktop sizes or desktop form factor (keyboard + mouse)
|
||||
- Additional/crazy features can go in forks and alternative clients
|
||||
- [Suwayomi-WebUI](https://github.com/Suwayomi/Suwayomi-WebUI) should
|
||||
@@ -19,13 +19,11 @@ Checkout [This Kanban Board](https://github.com/Suwayomi/Suwayomi-Server/project
|
||||
|
||||
## How does Suwayomi-Server work?
|
||||
This project has two components:
|
||||
1. **Server:** contains the implementation of [tachiyomi's extensions library](https://github.com/tachiyomiorg/extensions-lib) and uses an Android compatibility library to run jar libraries converted from apk extensions. All this concludes to serving a GraphQL API.
|
||||
1. **Server:** contains the implementation of [Mihon (Tachiyomi)'s source library](https://github.com/mihonapp/mihon/tree/main/source-api) and uses an Android compatibility library to run jar libraries converted from apk extensions. All this concludes to serving a GraphQL API.
|
||||
2. **WebUI:** A React SPA(`create-react-app`) project that works with the server to do the presentation located at https://github.com/Suwayomi/Suwayomi-WebUI
|
||||
|
||||
### API
|
||||
#### GraphQL
|
||||
*Only available in the preview at the moment*
|
||||
|
||||
The GraphQL API can be queried with a POST request to `/api/graphql`. There is also the GraphiQL IDE accessible by the browser at `/api/graphql` to perform ad-hoc queries and explore the API.
|
||||
|
||||
#### REST
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
|
||||
## Table of Content
|
||||
- [What is Suwayomi?](#what-is-suwayomi)
|
||||
- [Features](#Features)
|
||||
- [Suwayomi client projects](#Suwayomi-client-projects)
|
||||
* [Is this application usable? Should I test it?](#is-this-application-usable-should-i-test-it)
|
||||
- [Downloading and Running the app](#downloading-and-running-the-app)
|
||||
* [Using Operating System Specific Bundles](#using-operating-system-specific-bundles)
|
||||
- [Launcher Scripts](#launcher-scripts)
|
||||
@@ -20,7 +20,7 @@
|
||||
* [Advanced Methods](#advanced-methods)
|
||||
+ [Running the jar release directly](#running-the-jar-release-directly)
|
||||
+ [Using Suwayomi Remotely](#using-suwayomi-remotely)
|
||||
- [Syncing With Tachiyomi](#syncing-with-tachiyomi)
|
||||
- [Syncing With Mihon (Tachiyomi)](#syncing-with-mihon-tachiyomi)
|
||||
- [Troubleshooting and Support](#troubleshooting-and-support)
|
||||
- [Contributing and Technical info](#contributing-and-technical-info)
|
||||
- [Credit](#credit)
|
||||
@@ -30,41 +30,48 @@
|
||||
# What is Suwayomi?
|
||||
<img src="https://github.com/Suwayomi/Suwayomi-Server/raw/master/server/src/main/resources/icon/faviconlogo.png" alt="drawing" width="200"/>
|
||||
|
||||
A free and open source manga reader server that runs extensions built for [Tachiyomi](https://tachiyomi.org/).
|
||||
A free and open source manga reader server that runs extensions built for [Mihon (Tachiyomi)](https://mihon.app/).
|
||||
|
||||
Suwayomi is an independent Tachiyomi compatible software and is **not a Fork of** Tachiyomi.
|
||||
Suwayomi is an independent Mihon (Tachiyomi) compatible software and is **not a Fork of** Mihon (Tachiyomi).
|
||||
|
||||
Suwayomi-Server is as multi-platform as you can get. Any platform that runs java and/or has a modern browser can run it. This includes Windows, Linux, macOS, chrome OS, etc. Follow [Downloading and Running the app](#downloading-and-running-the-app) for installation instructions.
|
||||
|
||||
Ability to sync with Tachiyomi is a planned feature, for more info look [here](#syncing-with-tachiyomi).
|
||||
You can use Mihon (Tachiyomi) to access your Suwayomi-Server. For more info look [here](#syncing-with-mihon-tachiyomi).
|
||||
|
||||
## Features
|
||||
> [!NOTE]
|
||||
>
|
||||
> These are capabilities of Suwayomi-Server, the actual working support is provided by each front-end app, checkout their respective readme for more info.
|
||||
|
||||
- Installing and executing Mihon (Tachiyomi)'s Extensions, So you'll get the same sources
|
||||
- Searching and browsing installed sources
|
||||
- A library to save your mangas and categories to put them into
|
||||
- Automated library updates to check for new chapters
|
||||
- Automated download of new chapters
|
||||
- Viewing latest updated chapters
|
||||
- Ability to download Manga for offline read
|
||||
- Backup and restore support powered by Mihon (Tachiyomi)-compatible Backups
|
||||
- Automated backup creations
|
||||
- Tracking via [MyAnimeList](https://myanimelist.net/), [AniList](https://anilist.co/), [MangaUpdates](https://www.mangaupdates.com/)
|
||||
- [FlareSolverr](https://github.com/FlareSolverr/FlareSolverr) support to bypass Cloudflare protection
|
||||
- Automated WebUI updates (supports the default WebUI and VUI)
|
||||
|
||||
# Suwayomi client projects
|
||||
**You need a client/user interface app as a front-end for Suwayomi-Server, if you [Directly Download Suwayomi-Server](https://github.com/Suwayomi/Suwayomi-Server/releases/latest) you'll get a bundled version of [Suwayomi-WebUI](https://github.com/Suwayomi/Suwayomi-WebUI) with it.**
|
||||
|
||||
Here's a list of known clients/user interfaces for Suwayomi-Server:
|
||||
Here's a list of known clients/user interfaces for Suwayomi-Server (checkout the respective GitHub repository for their features):
|
||||
##### Actively Developed Clients
|
||||
- [Suwayomi-WebUI](https://github.com/Suwayomi/Suwayomi-WebUI): The web/ElectronJS front-end that Suwayomi-Server ships with by default.
|
||||
- [Tachidesk-JUI](https://github.com/Suwayomi/Tachidesk-JUI): The native desktop front-end for Suwayomi-Server. Currently, the most advanced.
|
||||
- [Tachidesk-Sorayomi](https://github.com/Suwayomi/Tachidesk-Sorayomi): A Flutter front-end for Desktop(Linux, windows, etc.), Web and Android with a User Interface inspired by Tachiyomi.
|
||||
- [Suwayomi-WebUI](https://github.com/Suwayomi/Suwayomi-WebUI): The web front-end that Suwayomi-Server ships with by default.
|
||||
- [Suwayomi-VUI](https://github.com/Suwayomi/Suwayomi-VUI): A Suwayomi-Server preview focused web frontend built with svelte
|
||||
- [Tachidesk-VaadinUI](https://github.com/Suwayomi/Tachidesk-VaadinUI): A Web front-end for Suwayomi-Server built with Vaadin.
|
||||
- [Suwayomi-VUI](https://github.com/Suwayomi/Suwayomi-VUI): A preview focused web frontend built with svelte with some features the other UIs might not have (migration)
|
||||
##### Inctive/Abandoned Clients
|
||||
##### Inactive Clients (functional but outdated)
|
||||
- [Tachidesk-JUI](https://github.com/Suwayomi/Tachidesk-JUI): The native desktop front-end for Suwayomi-Server.
|
||||
- [Tachidesk-Sorayomi](https://github.com/Suwayomi/Tachidesk-Sorayomi): A Flutter front-end for Desktop(Linux, windows, etc.), Web and Android with a User Interface inspired by Mihon (Tachiyomi).
|
||||
##### Abandoned Clients (functionality unknown)
|
||||
- [Tachidesk-qtui](https://github.com/Suwayomi/Tachidesk-qtui): A C++/Qt front-end for mobile devices(Android/linux), feature support is basic.
|
||||
- [Tachidesk-GTK](https://github.com/mahor1221/Tachidesk-GTK): A native Rust/GTK desktop client.
|
||||
- [Equinox](https://github.com/Suwayomi/Equinox): A web user interface made with Vue.js.
|
||||
|
||||
## Is this application usable? Should I test it?
|
||||
Here is a list of current features:
|
||||
|
||||
- Installing and executing Tachiyomi's Extensions, So you'll get the same sources
|
||||
- A library to save your mangas and categories to put them into
|
||||
- Searching and browsing installed sources
|
||||
- Ability to download Manga for offline read
|
||||
- Backup and restore support powered by Tachiyomi-compatible Backups
|
||||
- Viewing latest updated chapters.
|
||||
|
||||
**Note:** These are capabilities of Suwayomi-Server, the actual working support is provided by each front-end app, checkout their respective readme for more info.
|
||||
|
||||
# Downloading and Running the app
|
||||
## Using Operating System Specific Bundles
|
||||
To facilitate the use of Suwayomi we provide bundle releases that include The Java Runtime Environment, ElectronJS and the Suwayomi-Launcher.
|
||||
@@ -72,7 +79,7 @@ To facilitate the use of Suwayomi we provide bundle releases that include The Ja
|
||||
If a bundle for your operating system or cpu architecture is not provided then refer to [Advanced Methods](#advanced-methods)
|
||||
|
||||
### Windows
|
||||
Download the latest `win32`(Windows 32-bit) or `win64`(Windows 64-bit) release from [the releases section](https://github.com/Suwayomi/Suwayomi-Server/releases) or a preview one from [the preview repository](https://github.com/Suwayomi/Suwayomi-Server-preview/releases).
|
||||
Download the latest `win64`(Windows 64-bit) release from [the releases section](https://github.com/Suwayomi/Suwayomi-Server/releases) or a preview one from [the preview repository](https://github.com/Suwayomi/Suwayomi-Server-preview/releases).
|
||||
|
||||
Unzip the downloaded file and double-click on one of the launcher scripts.
|
||||
|
||||
@@ -87,6 +94,9 @@ Download the latest `linux-x64`(x86_64) release from [the releases section](http
|
||||
`tar xvf` the downloaded file and double-click on one of the launcher scripts or run them using the terminal.
|
||||
|
||||
## Other methods of getting Suwayomi
|
||||
### Docker
|
||||
Check our Official Docker release [Suwayomi Container](https://github.com/orgs/Suwayomi/packages/container/package/tachidesk) for running Suwayomi Server in a docker container. Source code for our container is available at [docker-tachidesk](https://github.com/Suwayomi/docker-tachidesk), an example compose file can also be found there. By default, the server will be running on http://localhost:4567 open this url in your browser.
|
||||
|
||||
### Arch Linux
|
||||
You can install Suwayomi from the AUR:
|
||||
```
|
||||
@@ -108,23 +118,26 @@ sudo apt update
|
||||
sudo apt install suwayomi-server
|
||||
```
|
||||
|
||||
### Docker
|
||||
Check our Official Docker release [Suwayomi Container](https://github.com/orgs/Suwayomi/packages/container/package/tachidesk) for running Suwayomi Server in a docker container. Source code for our container is available at [docker-tachidesk](https://github.com/Suwayomi/docker-tachidesk). By default, the server will be running on http://localhost:4567 open this url in your browser.
|
||||
### NixOS
|
||||
You can deploy Suwayomi on NixOS using the module `services.suwayomi-server` in your configuration:
|
||||
|
||||
Install from the command line:
|
||||
```
|
||||
$ docker pull ghcr.io/suwayomi/tachidesk
|
||||
```
|
||||
Run Container from the command line:
|
||||
```
|
||||
$ docker run -p 4567:4567 ghcr.io/suwayomi/tachidesk
|
||||
{
|
||||
services.suwayomi-server = {
|
||||
enable = true;
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
For more information, see [the NixOS manual](https://nixos.org/manual/nixos/stable/#module-services-suwayomi-server).
|
||||
|
||||
You can also directly use the package from [nixpkgs](https://search.nixos.org/packages?channel=unstable&type=packages&query=suwayomi-server).
|
||||
|
||||
## Advanced Methods
|
||||
### Running the jar release directly
|
||||
In order to run the app you need the following:
|
||||
- The jar release of Suwayomi-Server
|
||||
- The Java Runtime Environment(JRE) 8 or newer
|
||||
- The Java Runtime Environment(JRE) 21 or newer
|
||||
- A Browser like Google Chrome, Firefox, Edge, etc.
|
||||
- ElectronJS (optional)
|
||||
|
||||
@@ -139,13 +152,15 @@ Check out [this wiki page](https://github.com/Suwayomi/Suwayomi-Server/wiki/Conf
|
||||
|
||||
If you face issues with your setup then we are happy to provide help, just join our discord server(a discord badge is on the top of the page, you are just a click-clack away!).
|
||||
|
||||
## Syncing With Tachiyomi
|
||||
## Syncing With Mihon (Tachiyomi)
|
||||
### The Suwayomi extension and tracker
|
||||
- You can install the `Suwayomi` extension inside tachiyomi.
|
||||
- You can install the `Suwayomi` extension inside Mihon (Tachiyomi).
|
||||
- The extension will load your Suwayomi library.
|
||||
- By manipulating extension search filters you can browse your categories.
|
||||
- You can enable the Suwayomi tracker to track reading progress with your Suwayomi server.
|
||||
- Note: Tachiyomi [only allows tracking one way](https://github.com/tachiyomiorg/tachiyomi/issues/1626), meaning that by reading chapters on other Suwayomi clients the last read chapter number will update on the tracker but tachiyomi won't automatically mark them as read for you.
|
||||
- Note: to sync from
|
||||
- Mihon (Tachiyomi) to Suwayomi: Mihon (Tachiyomi) automatically updates the chapters read status when it's updating the tracker (e.g. while reading)
|
||||
- Suwayomi to Mihon (Tachiyomi): To sync Mihon (Tachiyomi) with Suwayomi, you have to open the manga's track information, then, Mihon (Tachiyomi) will automatically update its chapter list with the state from Suwayomi
|
||||
|
||||
### Other methods
|
||||
Checkout [this issue](https://github.com/Suwayomi/Suwayomi-Server/issues/159) for tracking progress.
|
||||
@@ -161,7 +176,7 @@ This project is a spiritual successor of [TachiWeb-Server](https://github.com/Ta
|
||||
|
||||
The `AndroidCompat` module was originally developed by [@null-dev](https://github.com/null-dev) for [TachiWeb-Server](https://github.com/Tachiweb/TachiWeb-server) and is licensed under `Apache License Version 2.0` and `Copyright 2019 Andy Bao and contributors`.
|
||||
|
||||
Parts of [tachiyomi](https://github.com/tachiyomiorg/tachiyomi) is adopted into this codebase, also licensed under `Apache License Version 2.0` and `Copyright 2015 Javier Tomás`.
|
||||
Parts of [Mihon (Tachiyomi)](https://github.com/mihonapp/mihon) is adopted into this codebase, also licensed under `Apache License Version 2.0` and `Copyright 2015 Javier Tomás`.
|
||||
|
||||
You can obtain a copy of `Apache License Version 2.0` from http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
|
||||
+6
-8
@@ -1,3 +1,4 @@
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile
|
||||
import org.jlleitschuh.gradle.ktlint.KtlintExtension
|
||||
import org.jlleitschuh.gradle.ktlint.KtlintPlugin
|
||||
@@ -26,8 +27,8 @@ allprojects {
|
||||
subprojects {
|
||||
plugins.withType<JavaPlugin> {
|
||||
extensions.configure<JavaPluginExtension> {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
sourceCompatibility = JavaVersion.VERSION_21
|
||||
targetCompatibility = JavaVersion.VERSION_21
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,12 +44,9 @@ subprojects {
|
||||
tasks {
|
||||
withType<KotlinJvmCompile> {
|
||||
dependsOn("ktlintFormat")
|
||||
kotlinOptions {
|
||||
jvmTarget = JavaVersion.VERSION_1_8.toString()
|
||||
|
||||
freeCompilerArgs += listOf(
|
||||
"-Xcontext-receivers",
|
||||
)
|
||||
compilerOptions {
|
||||
jvmTarget = JvmTarget.JVM_21
|
||||
freeCompilerArgs.add("-Xcontext-receivers")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,14 +10,13 @@ import java.io.BufferedReader
|
||||
const val MainClass = "suwayomi.tachidesk.MainKt"
|
||||
|
||||
// should be bumped with each stable release
|
||||
val tachideskVersion = System.getenv("ProductVersion") ?: "v1.0.0"
|
||||
val getTachideskVersion = { "v2.0.${getCommitCount()}" }
|
||||
|
||||
val webUIRevisionTag = System.getenv("WebUIRevision") ?: "r1409"
|
||||
val webUIRevisionTag = "r2467"
|
||||
|
||||
// counts commits on the current checked out branch
|
||||
val getTachideskRevision = {
|
||||
private val getCommitCount = {
|
||||
runCatching {
|
||||
System.getenv("ProductRevision") ?: ProcessBuilder()
|
||||
ProcessBuilder()
|
||||
.command("git", "rev-list", "HEAD", "--count")
|
||||
.start()
|
||||
.let { process ->
|
||||
@@ -26,8 +25,11 @@ val getTachideskRevision = {
|
||||
it.bufferedReader().use(BufferedReader::readText)
|
||||
}
|
||||
process.destroy()
|
||||
"r" + output.trim()
|
||||
output.trim()
|
||||
}
|
||||
}.getOrDefault("r0")
|
||||
}.getOrDefault("0")
|
||||
}
|
||||
|
||||
// counts commits on the current checked out branch
|
||||
val getTachideskRevision = { "r${getCommitCount()}" }
|
||||
|
||||
|
||||
+51
-45
@@ -1,18 +1,19 @@
|
||||
[versions]
|
||||
kotlin = "1.9.10"
|
||||
coroutines = "1.7.3"
|
||||
serialization = "1.6.0"
|
||||
okhttp = "5.0.0-alpha.11" # Major version is locked by Tachiyomi extensions
|
||||
javalin = "4.6.8" # Javalin 5.0.0+ requires Java 11
|
||||
jackson = "2.13.3" # jackson version locked by javalin, ref: `io.javalin.core.util.OptionalDependency`
|
||||
exposed = "0.40.1"
|
||||
kotlin = "2.1.20"
|
||||
coroutines = "1.10.1"
|
||||
serialization = "1.8.0"
|
||||
okhttp = "5.0.0-alpha.14" # Major version is locked by Tachiyomi extensions
|
||||
javalin = "6.5.0"
|
||||
jackson = "2.18.3" # jackson version locked by javalin, ref: `io.javalin.core.util.OptionalDependency`
|
||||
exposed = "0.59.0"
|
||||
dex2jar = "v64" # Stuck until https://github.com/ThexXTURBOXx/dex2jar/issues/27 is fixed
|
||||
rhino = "1.7.14"
|
||||
settings = "1.0.0-RC"
|
||||
twelvemonkeys = "3.9.4"
|
||||
graphqlkotlin = "6.5.6"
|
||||
xmlserialization = "0.86.2"
|
||||
ktlint = "1.0.0"
|
||||
polyglot = "24.2.0"
|
||||
settings = "1.3.0"
|
||||
twelvemonkeys = "3.12.0"
|
||||
graphqlkotlin = "8.4.0"
|
||||
xmlserialization = "0.90.3"
|
||||
ktlint = "1.5.0"
|
||||
koin = "4.0.2"
|
||||
|
||||
[libraries]
|
||||
# Kotlin
|
||||
@@ -29,20 +30,20 @@ coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", ve
|
||||
serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "serialization" }
|
||||
serialization-json-okio = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json-okio", version.ref = "serialization" }
|
||||
serialization-protobuf = { module = "org.jetbrains.kotlinx:kotlinx-serialization-protobuf", version.ref = "serialization" }
|
||||
serialization-xml-core = { module = "io.github.pdvrieze.xmlutil:core-jvm", version.ref = "xmlserialization" }
|
||||
serialization-xml-core = { module = "io.github.pdvrieze.xmlutil:core", version.ref = "xmlserialization" }
|
||||
serialization-xml = { module = "io.github.pdvrieze.xmlutil:serialization-jvm", version.ref = "xmlserialization" }
|
||||
|
||||
# Logging
|
||||
slf4japi = "org.slf4j:slf4j-api:2.0.9"
|
||||
logback = "ch.qos.logback:logback-classic:1.3.11"
|
||||
kotlinlogging = "io.github.microutils:kotlin-logging:3.0.5"
|
||||
slf4japi = "org.slf4j:slf4j-api:2.0.17"
|
||||
logback = "ch.qos.logback:logback-classic:1.5.18"
|
||||
kotlinlogging = "io.github.oshai:kotlin-logging-jvm:7.0.5"
|
||||
|
||||
# OkHttp
|
||||
okhttp-core = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" }
|
||||
okhttp-logging = { module = "com.squareup.okhttp3:logging-interceptor", version.ref = "okhttp" }
|
||||
okhttp-dnsoverhttps = { module = "com.squareup.okhttp3:okhttp-dnsoverhttps", version.ref = "okhttp" }
|
||||
okhttp-brotli = { module = "com.squareup.okhttp3:okhttp-brotli", version.ref = "okhttp" }
|
||||
okio = "com.squareup.okio:okio:3.3.0"
|
||||
okio = "com.squareup.okio:okio:3.10.2"
|
||||
|
||||
# Javalin api
|
||||
javalin-core = { module = "io.javalin:javalin", version.ref = "javalin" }
|
||||
@@ -54,7 +55,8 @@ jackson-annotations = { module = "com.fasterxml.jackson.core:jackson-annotations
|
||||
# GraphQL
|
||||
graphql-kotlin-server = { module = "com.expediagroup:graphql-kotlin-server", version.ref = "graphqlkotlin" }
|
||||
graphql-kotlin-scheme = { module = "com.expediagroup:graphql-kotlin-schema-generator", version.ref = "graphqlkotlin" }
|
||||
graphql-scalars = "com.graphql-java:graphql-java-extended-scalars:20.2"
|
||||
graphql-java-core = "com.graphql-java:graphql-java:22.3" # Major version locked by graphql-kotlin
|
||||
graphql-java-scalars = "com.graphql-java:graphql-java-extended-scalars:22.0"
|
||||
|
||||
# Exposed ORM
|
||||
exposed-core = { module = "org.jetbrains.exposed:exposed-core", version.ref = "exposed" }
|
||||
@@ -64,24 +66,24 @@ exposed-javatime = { module = "org.jetbrains.exposed:exposed-java-time", version
|
||||
h2 = "com.h2database:h2:1.4.200" # current database driver, can't update to h2 v2 without sql migration
|
||||
|
||||
# Exposed Migrations
|
||||
exposed-migrations = "com.github.Suwayomi:exposed-migrations:3.2.0"
|
||||
exposed-migrations = "com.github.Suwayomi:exposed-migrations:3.7.0"
|
||||
|
||||
# Dependency Injection
|
||||
kodein = "org.kodein.di:kodein-di-conf-jvm:7.20.2"
|
||||
koin-core = { module = "io.insert-koin:koin-core", version.ref = "koin" }
|
||||
|
||||
# tray icon
|
||||
systemtray-core = "com.dorkbox:SystemTray:4.2.1"
|
||||
systemtray-utils = "com.dorkbox:Utilities:1.39" # version locked by SystemTray
|
||||
systemtray-desktop = "com.dorkbox:Desktop:1.0"
|
||||
systemtray-core = "com.dorkbox:SystemTray:4.4"
|
||||
systemtray-utils = "com.dorkbox:Utilities:1.46" # version locked by SystemTray
|
||||
systemtray-desktop = "com.dorkbox:Desktop:1.1" # version locked by SystemTray
|
||||
|
||||
# dependencies of Tachiyomi extensions
|
||||
injekt = "com.github.inorichi.injekt:injekt-core:65b0440"
|
||||
injekt = "com.github.null2264:injekt-koin:ee267b2e27"
|
||||
rxjava = "io.reactivex:rxjava:1.3.8"
|
||||
jsoup = "org.jsoup:jsoup:1.16.1"
|
||||
jsoup = "org.jsoup:jsoup:1.19.1"
|
||||
|
||||
# Config
|
||||
config = "com.typesafe:config:1.4.2"
|
||||
config4k = "io.github.config4k:config4k:0.5.0"
|
||||
config = "com.typesafe:config:1.4.3"
|
||||
config4k = "io.github.config4k:config4k:0.7.0"
|
||||
|
||||
# Sort
|
||||
sort = "com.github.gpanther:java-nat-sort:natural-comparator-1.1"
|
||||
@@ -96,33 +98,34 @@ dex2jar-tools = { module = "com.github.ThexXTURBOXx.dex2jar:dex-tools", version.
|
||||
|
||||
# APK
|
||||
apk-parser = "net.dongliu:apk-parser:2.6.10"
|
||||
apksig = "com.android.tools.build:apksig:7.2.1"
|
||||
apksig = "com.android.tools.build:apksig:8.9.0"
|
||||
|
||||
# Xml
|
||||
xmlpull = "xmlpull:xmlpull:1.1.3.4a"
|
||||
|
||||
# Disk & File
|
||||
appdirs = "net.harawata:appdirs:1.2.1"
|
||||
appdirs = "net.harawata:appdirs:1.4.0"
|
||||
cache4k = "io.github.reactivecircus.cache4k:cache4k:0.14.0"
|
||||
zip4j = "net.lingala.zip4j:zip4j:2.11.5"
|
||||
commonscompress = "org.apache.commons:commons-compress:1.24.0"
|
||||
commonscompress = "org.apache.commons:commons-compress:1.27.1"
|
||||
junrar = "com.github.junrar:junrar:7.5.5"
|
||||
|
||||
# AES/CBC/PKCS7Padding Cypher provider
|
||||
bouncycastle = "org.bouncycastle:bcprov-jdk18on:1.76"
|
||||
bouncycastle = "org.bouncycastle:bcprov-jdk18on:1.80"
|
||||
|
||||
# AndroidX annotations
|
||||
android-annotations = "androidx.annotation:annotation:1.7.0"
|
||||
android-annotations = "androidx.annotation:annotation:1.9.1"
|
||||
|
||||
# Substitute for duktape-android
|
||||
rhino-runtime = { module = "org.mozilla:rhino-runtime", version.ref = "rhino" } # slimmer version of 'org.mozilla:rhino'
|
||||
rhino-engine = { module = "org.mozilla:rhino-engine", version.ref = "rhino" } # provides the same interface as 'javax.script' a.k.a Nashorn
|
||||
polyglot-core = { module = "org.graalvm.polyglot:polyglot", version.ref = "polyglot" }
|
||||
polyglot-graaljs = { module = "org.graalvm.polyglot:js-community", version.ref = "polyglot" }
|
||||
|
||||
# Settings
|
||||
settings-core = { module = "com.russhwolf:multiplatform-settings-jvm", version.ref = "settings" }
|
||||
settings-serialization = { module = "com.russhwolf:multiplatform-settings-serialization-jvm", version.ref = "settings" }
|
||||
|
||||
# ICU4J
|
||||
icu4j = "com.ibm.icu:icu4j:73.2"
|
||||
icu4j = "com.ibm.icu:icu4j:77.1"
|
||||
|
||||
# Image Decoding implementation provider
|
||||
twelvemonkeys-common-lang = { module = "com.twelvemonkeys.common:common-lang", version.ref = "twelvemonkeys" }
|
||||
@@ -134,7 +137,7 @@ twelvemonkeys-imageio-jpeg = { module = "com.twelvemonkeys.imageio:imageio-jpeg"
|
||||
twelvemonkeys-imageio-webp = { module = "com.twelvemonkeys.imageio:imageio-webp", version.ref = "twelvemonkeys" }
|
||||
|
||||
# Testing
|
||||
mockk = "io.mockk:mockk:1.13.7"
|
||||
mockk = "io.mockk:mockk:1.13.17"
|
||||
|
||||
# cron scheduler
|
||||
cron4j = "it.sauronsoftware.cron4j:cron4j:2.2.5"
|
||||
@@ -142,19 +145,22 @@ cron4j = "it.sauronsoftware.cron4j:cron4j:2.2.5"
|
||||
# cron-utils
|
||||
cronUtils = "com.cronutils:cron-utils:9.2.1"
|
||||
|
||||
# lint - used for renovate to update ktlint version
|
||||
ktlint = { module = "com.pinterest.ktlint:ktlint-cli", version.ref = "ktlint" }
|
||||
|
||||
[plugins]
|
||||
# Kotlin
|
||||
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin"}
|
||||
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin"}
|
||||
|
||||
# Linter
|
||||
ktlint = { id = "org.jlleitschuh.gradle.ktlint", version = "11.6.0"}
|
||||
ktlint = { id = "org.jlleitschuh.gradle.ktlint", version = "12.2.0"}
|
||||
|
||||
# Build config
|
||||
buildconfig = { id = "com.github.gmazzo.buildconfig", version = "3.1.0"}
|
||||
buildconfig = { id = "com.github.gmazzo.buildconfig", version = "5.5.4"}
|
||||
|
||||
# Download
|
||||
download = { id = "de.undercouch.download", version = "5.4.0"}
|
||||
download = { id = "de.undercouch.download", version = "5.6.0"}
|
||||
|
||||
# ShadowJar
|
||||
shadowjar = { id = "com.github.johnrengelman.shadow", version = "8.1.1"}
|
||||
@@ -168,7 +174,7 @@ shared = [
|
||||
"serialization-json",
|
||||
"serialization-json-okio",
|
||||
"serialization-protobuf",
|
||||
"kodein",
|
||||
"koin-core",
|
||||
"slf4japi",
|
||||
"logback",
|
||||
"kotlinlogging",
|
||||
@@ -196,7 +202,7 @@ okhttp = [
|
||||
]
|
||||
javalin = [
|
||||
"javalin-core",
|
||||
"javalin-openapi",
|
||||
#"javalin-openapi",
|
||||
]
|
||||
jackson = [
|
||||
"jackson-databind",
|
||||
@@ -214,9 +220,9 @@ systemtray = [
|
||||
"systemtray-utils",
|
||||
"systemtray-desktop"
|
||||
]
|
||||
rhino = [
|
||||
"rhino-runtime",
|
||||
"rhino-engine",
|
||||
polyglot = [
|
||||
"polyglot-core",
|
||||
"polyglot-graaljs",
|
||||
]
|
||||
settings = [
|
||||
"settings-core",
|
||||
|
||||
Vendored
BIN
Binary file not shown.
+3
-1
@@ -1,5 +1,7 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
@@ -55,7 +57,7 @@
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
@@ -80,13 +82,11 @@ do
|
||||
esac
|
||||
done
|
||||
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||
|
||||
APP_NAME="Gradle"
|
||||
# This is normally unused
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
@@ -133,22 +133,29 @@ location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD=java
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
if ! command -v java >/dev/null 2>&1
|
||||
then
|
||||
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
@@ -193,11 +200,15 @@ if "$cygwin" || "$msys" ; then
|
||||
done
|
||||
fi
|
||||
|
||||
# Collect all arguments for the java command;
|
||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||
# shell script including quotes and variable substitutions, so put them in
|
||||
# double quotes to make sure that they get re-expanded; and
|
||||
# * put everything else in single quotes, so that it's not re-expanded.
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Collect all arguments for the java command:
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# and any embedded shellness will be escaped.
|
||||
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||
# treated as '${Hostname}' itself on the command line.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
|
||||
Vendored
+13
-10
@@ -13,6 +13,8 @@
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
@rem SPDX-License-Identifier: Apache-2.0
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
@@ -26,6 +28,7 @@ if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
@rem This is normally unused
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@@ -42,11 +45,11 @@ set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
@@ -56,11 +59,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"extends": [
|
||||
"config:recommended"
|
||||
],
|
||||
"semanticCommits": "disabled",
|
||||
"customManagers": [
|
||||
{
|
||||
"customType": "regex",
|
||||
"fileMatch": [
|
||||
"scripts/bundler.sh"
|
||||
],
|
||||
"matchStrings": [
|
||||
"JRE_RELEASE=[\"'](?<currentValue>.+?)[\"']\\s+"
|
||||
],
|
||||
"datasourceTemplate": "github-releases",
|
||||
"depNameTemplate": "adoptium/temurin21-binaries",
|
||||
"versioningTemplate": "regex:^jdk-?(?<major>\\d+).(?<minor>\\d+).+?(?<patch>[\\d+]+)$"
|
||||
}
|
||||
]
|
||||
}
|
||||
+55
-56
@@ -28,7 +28,7 @@ main() {
|
||||
OS="$1"
|
||||
JAR="$(ls server/build/*.jar | tail -n1)"
|
||||
RELEASE_NAME="$(echo "${JAR%.*}" | xargs basename)-$OS"
|
||||
RELEASE_VERSION="$(tmp="${JAR%-*}"; echo "${tmp##*-}" | tr -d v)"
|
||||
RELEASE_VERSION=$(echo "$JAR" | grep -oP "v\K[0-9]+\.[0-9]+\.[0-9]+")
|
||||
#RELEASE_REVISION_NUMBER="$(tmp="${JAR%.*}" && echo "${tmp##*-}" | tr -d r)"
|
||||
local electron_version="v28.1.3"
|
||||
|
||||
@@ -51,74 +51,64 @@ main() {
|
||||
move_release_to_output_dir
|
||||
;;
|
||||
linux-x64)
|
||||
# https://github.com/adoptium/temurin8-binaries/releases/
|
||||
JRE_RELEASE="jdk8u392-b08"
|
||||
JRE="OpenJDK8U-jre_x64_linux_hotspot_$(echo "$JRE_RELEASE" | sed 's/jdk//;s/-//g').tar.gz"
|
||||
# https://github.com/adoptium/temurin21-binaries/releases/
|
||||
JRE_RELEASE="jdk-21.0.6+7"
|
||||
JRE="OpenJDK21U-jre_x64_linux_hotspot_$(echo "$JRE_RELEASE" | sed 's/jdk//;s/-//g;s/+/_/g').tar.gz"
|
||||
JRE_DIR="$JRE_RELEASE-jre"
|
||||
JRE_URL="https://github.com/adoptium/temurin8-binaries/releases/download/$JRE_RELEASE/$JRE"
|
||||
JRE_URL="https://github.com/adoptium/temurin21-binaries/releases/download/$JRE_RELEASE/$JRE"
|
||||
ELECTRON="electron-$electron_version-linux-x64.zip"
|
||||
ELECTRON_URL="https://github.com/electron/electron/releases/download/$electron_version/$ELECTRON"
|
||||
download_jre_and_electron
|
||||
download_electron
|
||||
setup_jre
|
||||
tree "$RELEASE_NAME"
|
||||
|
||||
RELEASE="$RELEASE_NAME.tar.gz"
|
||||
make_linux_bundle
|
||||
move_release_to_output_dir
|
||||
;;
|
||||
macOS-x64)
|
||||
# https://github.com/adoptium/temurin8-binaries/releases/
|
||||
JRE_RELEASE="jdk8u392-b08"
|
||||
JRE="OpenJDK8U-jre_x64_mac_hotspot_$(echo "$JRE_RELEASE" | sed 's/jdk//;s/-//g').tar.gz"
|
||||
# https://github.com/adoptium/temurin21-binaries/releases/
|
||||
JRE_RELEASE="jdk-21.0.6+7"
|
||||
JRE="OpenJDK21U-jre_x64_mac_hotspot_$(echo "$JRE_RELEASE" | sed 's/jdk//;s/-//g;s/+/_/g').tar.gz"
|
||||
JRE_DIR="$JRE_RELEASE-jre"
|
||||
JRE_URL="https://github.com/adoptium/temurin8-binaries/releases/download/$JRE_RELEASE/$JRE"
|
||||
JRE_URL="https://github.com/adoptium/temurin21-binaries/releases/download/$JRE_RELEASE/$JRE"
|
||||
ELECTRON="electron-$electron_version-darwin-x64.zip"
|
||||
ELECTRON_URL="https://github.com/electron/electron/releases/download/$electron_version/$ELECTRON"
|
||||
download_jre_and_electron
|
||||
download_electron
|
||||
setup_jre
|
||||
tree "$RELEASE_NAME"
|
||||
|
||||
RELEASE="$RELEASE_NAME.zip"
|
||||
RELEASE="$RELEASE_NAME.tar.gz"
|
||||
make_macos_bundle
|
||||
move_release_to_output_dir
|
||||
;;
|
||||
macOS-arm64)
|
||||
# https://cdn.azul.com/zulu/bin/
|
||||
JRE="zulu8.74.0.17-ca-jre8.0.392-macosx_aarch64.tar.gz"
|
||||
JRE_RELEASE="zulu8.74.0.17-ca-jre8.0.392-macosx_aarch64"
|
||||
JRE_DIR="$JRE_RELEASE/zulu-8.jre"
|
||||
JRE_URL="https://cdn.azul.com/zulu/bin/$JRE"
|
||||
# https://github.com/adoptium/temurin21-binaries/releases/
|
||||
JRE_RELEASE="jdk-21.0.6+7"
|
||||
JRE="OpenJDK21U-jre_aarch64_mac_hotspot_$(echo "$JRE_RELEASE" | sed 's/jdk//;s/-//g;s/+/_/g').tar.gz"
|
||||
JRE_DIR="$JRE_RELEASE-jre"
|
||||
JRE_URL="https://github.com/adoptium/temurin21-binaries/releases/download/$JRE_RELEASE/$JRE"
|
||||
ELECTRON="electron-$electron_version-darwin-arm64.zip"
|
||||
ELECTRON_URL="https://github.com/electron/electron/releases/download/$electron_version/$ELECTRON"
|
||||
download_jre_and_electron
|
||||
download_electron
|
||||
setup_jre
|
||||
tree "$RELEASE_NAME"
|
||||
|
||||
RELEASE="$RELEASE_NAME.zip"
|
||||
RELEASE="$RELEASE_NAME.tar.gz"
|
||||
make_macos_bundle
|
||||
move_release_to_output_dir
|
||||
;;
|
||||
windows-x86)
|
||||
# https://github.com/adoptium/temurin8-binaries/releases/
|
||||
JRE_RELEASE="jdk8u392-b08"
|
||||
JRE="OpenJDK8U-jre_x86-32_windows_hotspot_$(echo "$JRE_RELEASE" | sed 's/jdk//;s/-//g').zip"
|
||||
JRE_DIR="$JRE_RELEASE-jre"
|
||||
JRE_URL="https://github.com/adoptium/temurin8-binaries/releases/download/$JRE_RELEASE/$JRE"
|
||||
ELECTRON="electron-$electron_version-win32-ia32.zip"
|
||||
ELECTRON_URL="https://github.com/electron/electron/releases/download/$electron_version/$ELECTRON"
|
||||
download_jre_and_electron
|
||||
|
||||
RELEASE="$RELEASE_NAME.zip"
|
||||
make_windows_bundle
|
||||
move_release_to_output_dir
|
||||
|
||||
RELEASE="$RELEASE_NAME.msi"
|
||||
make_windows_package
|
||||
move_release_to_output_dir
|
||||
;;
|
||||
windows-x64)
|
||||
# https://github.com/adoptium/temurin8-binaries/releases/
|
||||
JRE_RELEASE="jdk8u392-b08"
|
||||
JRE="OpenJDK8U-jre_x64_windows_hotspot_$(echo "$JRE_RELEASE" | sed 's/jdk//;s/-//g').zip"
|
||||
# https://github.com/adoptium/temurin21-binaries/releases/
|
||||
JRE_RELEASE="jdk-21.0.6+7"
|
||||
JRE="OpenJDK21U-jre_x64_windows_hotspot_$(echo "$JRE_RELEASE" | sed 's/jdk//;s/-//g;s/+/_/g').zip"
|
||||
JRE_DIR="$JRE_RELEASE-jre"
|
||||
JRE_URL="https://github.com/adoptium/temurin8-binaries/releases/download/$JRE_RELEASE/$JRE"
|
||||
JRE_URL="https://github.com/adoptium/temurin21-binaries/releases/download/$JRE_RELEASE/$JRE"
|
||||
ELECTRON="electron-$electron_version-win32-x64.zip"
|
||||
ELECTRON_URL="https://github.com/electron/electron/releases/download/$electron_version/$ELECTRON"
|
||||
download_jre_and_electron
|
||||
download_electron
|
||||
setup_jre
|
||||
tree "$RELEASE_NAME"
|
||||
|
||||
RELEASE="$RELEASE_NAME.zip"
|
||||
make_windows_bundle
|
||||
@@ -148,26 +138,32 @@ download_launcher() {
|
||||
mv "Suwayomi-Launcher.jar" "$RELEASE_NAME/Suwayomi-Launcher.jar"
|
||||
}
|
||||
|
||||
download_jre_and_electron() {
|
||||
if [ ! -f "$JRE" ]; then
|
||||
curl -L "$JRE_URL" -o "$JRE"
|
||||
fi
|
||||
download_electron() {
|
||||
if [ ! -f "$ELECTRON" ]; then
|
||||
curl -L "$ELECTRON_URL" -o "$ELECTRON"
|
||||
fi
|
||||
|
||||
local ext="${JRE##*.}"
|
||||
if [ "$ext" = "zip" ]; then
|
||||
unzip "$JRE"
|
||||
else
|
||||
tar xvf "$JRE"
|
||||
fi
|
||||
mv "$JRE_DIR" "$RELEASE_NAME/jre"
|
||||
unzip "$ELECTRON" -d "$RELEASE_NAME/electron/"
|
||||
}
|
||||
|
||||
mkdir "$RELEASE_NAME/bin"
|
||||
setup_jre() {
|
||||
if [ -d "jre" ]; then
|
||||
chmod +x ./jre/bin/java
|
||||
chmod +x ./jre/lib/jspawnhelper
|
||||
mv "jre" "$RELEASE_NAME/jre"
|
||||
else
|
||||
if [ ! -f "$JRE" ]; then
|
||||
curl -L "$JRE_URL" -o "$JRE"
|
||||
fi
|
||||
|
||||
tree
|
||||
local ext="${JRE##*.}"
|
||||
if [ "$ext" = "zip" ]; then
|
||||
unzip "$JRE"
|
||||
else
|
||||
tar xvf "$JRE"
|
||||
fi
|
||||
mv "$JRE_DIR" "$RELEASE_NAME/jre"
|
||||
fi
|
||||
}
|
||||
|
||||
copy_linux_package_assets_to() {
|
||||
@@ -184,6 +180,7 @@ copy_linux_package_assets_to() {
|
||||
}
|
||||
|
||||
make_linux_bundle() {
|
||||
mkdir "$RELEASE_NAME/bin"
|
||||
cp "$JAR" "$RELEASE_NAME/bin/Suwayomi-Server.jar"
|
||||
cp "scripts/resources/suwayomi-launcher.sh" "$RELEASE_NAME/"
|
||||
cp "scripts/resources/suwayomi-server.sh" "$RELEASE_NAME/"
|
||||
@@ -192,10 +189,11 @@ make_linux_bundle() {
|
||||
}
|
||||
|
||||
make_macos_bundle() {
|
||||
mkdir "$RELEASE_NAME/bin"
|
||||
cp "$JAR" "$RELEASE_NAME/bin/Suwayomi-Server.jar"
|
||||
cp "scripts/resources/Suwayomi Launcher.command" "$RELEASE_NAME/"
|
||||
|
||||
zip -9 -r "$RELEASE" "$RELEASE_NAME/"
|
||||
tar -I "gzip -9" -cvf "$RELEASE" "$RELEASE_NAME/"
|
||||
}
|
||||
|
||||
# https://wiki.debian.org/SimplePackagingTutorial
|
||||
@@ -255,6 +253,7 @@ make_windows_bundle() {
|
||||
#WINEARCH=win32 wine "$rcedit" "$RELEASE_NAME/electron/electron.exe" \
|
||||
# --set-icon "$icon"
|
||||
|
||||
mkdir "$RELEASE_NAME/bin"
|
||||
cp "$JAR" "$RELEASE_NAME/bin/Suwayomi-Server.jar"
|
||||
cp "scripts/resources/Suwayomi Launcher.bat" "$RELEASE_NAME"
|
||||
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
cd "`dirname "$0"`"
|
||||
|
||||
./jre/Contents/Home/bin/java -jar Suwayomi-Launcher.jar
|
||||
./jre/bin/java -jar Suwayomi-Launcher.jar
|
||||
@@ -8,7 +8,7 @@ Homepage: https://github.com/Suwayomi/Suwayomi-Server
|
||||
|
||||
Package: suwayomi-server
|
||||
Architecture: all
|
||||
Depends: ${misc:Depends}, java8-runtime, libc++-dev
|
||||
Depends: ${misc:Depends}, openjdk-21-jre, libc++-dev
|
||||
Description: Manga Reader
|
||||
A free and open source manga reader server that runs extensions built for Tachiyomi.
|
||||
Suwayomi is an independent Tachiyomi compatible software and is not a Fork of Tachiyomi.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
|
||||
<Product Id="*" UpgradeCode="*"
|
||||
<Product Id="*" UpgradeCode="174c8f36-0bec-4585-9ddd-469c3d889dc1"
|
||||
Version="$(var.ProductVersion)" Language="1033" Name="Suwayomi Server" Manufacturer="Suwayomi">
|
||||
<Package InstallerVersion="300" Compressed="yes" />
|
||||
<Media Id="1" Cabinet="Suwayomi_Server.cab" EmbedCab="yes" />
|
||||
@@ -9,6 +9,8 @@
|
||||
VersionNT64
|
||||
</Condition>
|
||||
|
||||
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
|
||||
|
||||
<!-- Directory -->
|
||||
<Directory Id="TARGETDIR" Name="SourceDir">
|
||||
<Directory Id="ProgramFiles64Folder">
|
||||
@@ -48,6 +50,10 @@
|
||||
</Component>
|
||||
</DirectoryRef>
|
||||
|
||||
<InstallExecuteSequence>
|
||||
<RemoveExistingProducts After="InstallValidate" />
|
||||
</InstallExecuteSequence>
|
||||
|
||||
<!-- Feature -->
|
||||
<Feature Id="Suwayomi_Server" Title="Suwayomi-Server" Level="1">
|
||||
<ComponentGroupRef Id="jre" />
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
|
||||
<Product Id="*" UpgradeCode="*"
|
||||
Version="$(var.ProductVersion)" Language="1033" Name="Suwayomi Server" Manufacturer="Suwayomi">
|
||||
<Package InstallerVersion="300" Compressed="yes" />
|
||||
<Media Id="1" Cabinet="Suwayomi_Server.cab" EmbedCab="yes" />
|
||||
|
||||
<!-- Directory -->
|
||||
<Directory Id="TARGETDIR" Name="SourceDir">
|
||||
<Directory Id="ProgramFilesFolder">
|
||||
<Directory Id="INSTALLDIR" Name="Suwayomi-Server" >
|
||||
<Directory Id="jre"/>
|
||||
<Directory Id="electron"/>
|
||||
<Directory Id="bin"/>
|
||||
</Directory>
|
||||
</Directory>
|
||||
<Directory Id="ProgramMenuFolder">
|
||||
<Directory Id="ProgramMenuDir" Name="Suwayomi-Server">
|
||||
<Component Id="ProgramMenuDir" Guid="*">
|
||||
<RemoveFolder Id="ProgramMenuDir" On="uninstall"/>
|
||||
<RegistryValue Root="HKCU" Key="Software\[Manufacturer]\[ProductName]" Type="string" Value="" KeyPath="yes"/>
|
||||
</Component>
|
||||
</Directory>
|
||||
</Directory>
|
||||
<Directory Id="DesktopFolder" />
|
||||
</Directory>
|
||||
|
||||
<!-- Component -->
|
||||
<DirectoryRef Id="INSTALLDIR">
|
||||
<Component Id="SuwayomiJAR" Guid="*">
|
||||
<File Id="Suwayomi-Launcher.jar" Source="$(var.SourceDir)/Suwayomi-Launcher.jar" KeyPath="yes" />
|
||||
</Component>
|
||||
|
||||
<Component Id="SuwayomiLauncherBAT" Guid="*" Win64="yes">
|
||||
<File Id="SuwayomiLauncher.bat" Source="$(var.SourceDir)/Suwayomi Launcher.bat" KeyPath="yes" >
|
||||
<Shortcut Id="SuwayomiLauncher.lnk" Name="Suwayomi Launcher" Directory="INSTALLDIR"
|
||||
WorkingDirectory="INSTALLDIR" Icon="Suwayomi.ico" IconIndex="0" Advertise="yes" />
|
||||
<Shortcut Id="DesktopSuwayomiLauncher.lnk" Name="Suwayomi Launcher" Directory="DesktopFolder"
|
||||
WorkingDirectory="INSTALLDIR" Icon="Suwayomi.ico" IconIndex="0" Advertise="yes" />
|
||||
<Shortcut Id="ProgramMenuSuwayomiLauncher.lnk" Name="Suwayomi Launcher" Directory="ProgramMenuDir"
|
||||
WorkingDirectory="INSTALLDIR" Icon="Suwayomi.ico" IconIndex="0" Advertise="yes"
|
||||
Description="A free and open source manga reader that runs extensions built for Tachiyomi." />
|
||||
</File>
|
||||
</Component>
|
||||
</DirectoryRef>
|
||||
|
||||
<!-- Feature -->
|
||||
<Feature Id="Suwayomi_Server" Title="Suwayomi-Server" Level="1">
|
||||
<ComponentGroupRef Id="jre" />
|
||||
<ComponentGroupRef Id="bin" />
|
||||
<ComponentRef Id="SuwayomiJAR" />
|
||||
<ComponentRef Id="SuwayomiLauncherBAT" />
|
||||
<ComponentRef Id="ProgramMenuDir" />
|
||||
<ComponentGroupRef Id="electron" />
|
||||
</Feature>
|
||||
|
||||
<Icon Id="Suwayomi.ico" SourceFile="$(var.Icon)" />
|
||||
<Property Id="ARPPRODUCTICON" Value="Suwayomi.ico" /> <!-- Icon in Add/Remove Programs -->
|
||||
</Product>
|
||||
</Wix>
|
||||
+33
-15
@@ -3,12 +3,28 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile
|
||||
import java.time.Instant
|
||||
|
||||
plugins {
|
||||
id(libs.plugins.kotlin.jvm.get().pluginId)
|
||||
id(libs.plugins.kotlin.serialization.get().pluginId)
|
||||
id(libs.plugins.ktlint.get().pluginId)
|
||||
id(
|
||||
libs.plugins.kotlin.jvm
|
||||
.get()
|
||||
.pluginId,
|
||||
)
|
||||
id(
|
||||
libs.plugins.kotlin.serialization
|
||||
.get()
|
||||
.pluginId,
|
||||
)
|
||||
id(
|
||||
libs.plugins.ktlint
|
||||
.get()
|
||||
.pluginId,
|
||||
)
|
||||
application
|
||||
alias(libs.plugins.shadowjar)
|
||||
id(libs.plugins.buildconfig.get().pluginId)
|
||||
id(
|
||||
libs.plugins.buildconfig
|
||||
.get()
|
||||
.pluginId,
|
||||
)
|
||||
}
|
||||
|
||||
dependencies {
|
||||
@@ -27,7 +43,8 @@ dependencies {
|
||||
// GraphQL
|
||||
implementation(libs.graphql.kotlin.server)
|
||||
implementation(libs.graphql.kotlin.scheme)
|
||||
implementation(libs.graphql.scalars)
|
||||
implementation(libs.graphql.java.core)
|
||||
implementation(libs.graphql.java.scalars)
|
||||
|
||||
// Exposed ORM
|
||||
implementation(libs.bundles.exposed)
|
||||
@@ -39,7 +56,7 @@ dependencies {
|
||||
// tray icon
|
||||
implementation(libs.bundles.systemtray)
|
||||
|
||||
// dependencies of Tachiyomi extensions, some are duplicate, keeping it here for reference
|
||||
// dependencies of Mihon (Tachiyomi) extensions, some are duplicate, keeping it here for reference
|
||||
implementation(libs.injekt)
|
||||
implementation(libs.okhttp.core)
|
||||
implementation(libs.rxjava)
|
||||
@@ -56,6 +73,7 @@ dependencies {
|
||||
implementation(libs.asm)
|
||||
|
||||
// Disk & File
|
||||
implementation(libs.cache4k)
|
||||
implementation(libs.zip4j)
|
||||
implementation(libs.commonscompress)
|
||||
implementation(libs.junrar)
|
||||
@@ -103,7 +121,7 @@ buildConfig {
|
||||
fun quoteWrap(obj: Any): String = """"$obj""""
|
||||
|
||||
buildConfigField("String", "NAME", quoteWrap(rootProject.name))
|
||||
buildConfigField("String", "VERSION", quoteWrap(tachideskVersion))
|
||||
buildConfigField("String", "VERSION", quoteWrap(getTachideskVersion()))
|
||||
buildConfigField("String", "REVISION", quoteWrap(getTachideskRevision()))
|
||||
buildConfigField("String", "BUILD_TYPE", quoteWrap(if (System.getenv("ProductBuildType") == "Stable") "Stable" else "Preview"))
|
||||
buildConfigField("long", "BUILD_TIME", Instant.now().epochSecond.toString())
|
||||
@@ -122,14 +140,15 @@ tasks {
|
||||
"Main-Class" to MainClass,
|
||||
"Implementation-Title" to rootProject.name,
|
||||
"Implementation-Vendor" to "The Suwayomi Project",
|
||||
"Specification-Version" to tachideskVersion,
|
||||
"Specification-Version" to getTachideskVersion(),
|
||||
"Implementation-Version" to getTachideskRevision(),
|
||||
)
|
||||
}
|
||||
archiveBaseName.set(rootProject.name)
|
||||
archiveVersion.set(tachideskVersion)
|
||||
archiveClassifier.set(getTachideskRevision())
|
||||
archiveVersion.set(getTachideskVersion())
|
||||
archiveClassifier.set("")
|
||||
destinationDirectory.set(File("$rootDir/server/build"))
|
||||
mergeServiceFiles()
|
||||
}
|
||||
|
||||
test {
|
||||
@@ -141,11 +160,10 @@ tasks {
|
||||
}
|
||||
|
||||
withType<KotlinJvmCompile> {
|
||||
kotlinOptions {
|
||||
freeCompilerArgs +=
|
||||
listOf(
|
||||
"-opt-in=kotlinx.serialization.ExperimentalSerializationApi",
|
||||
)
|
||||
compilerOptions {
|
||||
freeCompilerArgs.add(
|
||||
"-opt-in=kotlinx.serialization.ExperimentalSerializationApi",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,16 +9,10 @@ package eu.kanade.tachiyomi
|
||||
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.InjektScope
|
||||
import uy.kohesive.injekt.registry.default.DefaultRegistrar
|
||||
|
||||
open class App : Application() {
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
Injekt = InjektScope(DefaultRegistrar())
|
||||
Injekt.importModule(AppModule(this))
|
||||
|
||||
// if (BuildConfig.DEBUG) Timber.plant(Timber.DebugTree())
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,12 @@ object AppInfo {
|
||||
*
|
||||
* @since extension-lib 1.3
|
||||
*/
|
||||
fun getVersionCode() = BuildConfig.REVISION.substring(1).toInt()
|
||||
fun getVersionCode() =
|
||||
BuildConfig.VERSION
|
||||
.replace("v", "")
|
||||
.split('.')
|
||||
.joinToString("")
|
||||
.toInt()
|
||||
|
||||
/**
|
||||
* should be something like "0.13.1"
|
||||
|
||||
@@ -20,21 +20,15 @@ import eu.kanade.tachiyomi.network.JavaScriptEngine
|
||||
import eu.kanade.tachiyomi.network.NetworkHelper
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.protobuf.ProtoBuf
|
||||
import nl.adaptivity.xmlutil.XmlDeclMode
|
||||
import nl.adaptivity.xmlutil.core.XmlVersion
|
||||
import nl.adaptivity.xmlutil.serialization.XML
|
||||
import org.kodein.di.DI
|
||||
import org.kodein.di.conf.global
|
||||
import org.kodein.di.instance
|
||||
import rx.Observable
|
||||
import rx.schedulers.Schedulers
|
||||
import uy.kohesive.injekt.api.InjektModule
|
||||
import uy.kohesive.injekt.api.InjektRegistrar
|
||||
import uy.kohesive.injekt.api.addSingleton
|
||||
import uy.kohesive.injekt.api.addSingletonFactory
|
||||
import uy.kohesive.injekt.api.get
|
||||
import org.koin.core.module.Module
|
||||
import org.koin.dsl.module
|
||||
|
||||
class AppModule(val app: Application) : InjektModule {
|
||||
override fun InjektRegistrar.registerInjectables() {
|
||||
addSingleton(app)
|
||||
fun createAppModule(app: Application): Module {
|
||||
return module {
|
||||
single { app }
|
||||
|
||||
// addSingletonFactory { PreferencesHelper(app) }
|
||||
//
|
||||
@@ -44,9 +38,9 @@ class AppModule(val app: Application) : InjektModule {
|
||||
//
|
||||
// addSingletonFactory { CoverCache(app) }
|
||||
|
||||
addSingletonFactory { NetworkHelper(app) }
|
||||
single { NetworkHelper(app) }
|
||||
|
||||
addSingletonFactory { JavaScriptEngine(app) }
|
||||
single { JavaScriptEngine(app) }
|
||||
|
||||
// addSingletonFactory { SourceManager(app).also { get<ExtensionManager>().init(it) } }
|
||||
//
|
||||
@@ -58,36 +52,38 @@ class AppModule(val app: Application) : InjektModule {
|
||||
//
|
||||
// addSingletonFactory { LibrarySyncManager(app) }
|
||||
|
||||
addSingletonFactory {
|
||||
val json by DI.global.instance<Json>()
|
||||
json
|
||||
single {
|
||||
Json {
|
||||
ignoreUnknownKeys = true
|
||||
explicitNulls = false
|
||||
}
|
||||
}
|
||||
|
||||
addSingletonFactory {
|
||||
val xml by DI.global.instance<XML>()
|
||||
xml
|
||||
single {
|
||||
XML {
|
||||
defaultPolicy {
|
||||
ignoreUnknownChildren()
|
||||
}
|
||||
autoPolymorphic = true
|
||||
xmlDeclMode = XmlDeclMode.Charset
|
||||
indent = 2
|
||||
xmlVersion = XmlVersion.XML10
|
||||
}
|
||||
}
|
||||
|
||||
addSingletonFactory {
|
||||
val protobuf by DI.global.instance<ProtoBuf>()
|
||||
protobuf
|
||||
single {
|
||||
ProtoBuf
|
||||
}
|
||||
}
|
||||
|
||||
// Asynchronously init expensive components for a faster cold start
|
||||
// Asynchronously init expensive components for a faster cold start
|
||||
|
||||
// rxAsync { get<PreferencesHelper>() }
|
||||
|
||||
rxAsync { get<NetworkHelper>() }
|
||||
|
||||
rxAsync {
|
||||
// rxAsync {
|
||||
// get<SourceManager>()
|
||||
// get<DownloadManager>()
|
||||
}
|
||||
// }
|
||||
|
||||
// rxAsync { get<DatabaseHelper>() }
|
||||
}
|
||||
|
||||
private fun rxAsync(block: () -> Unit) {
|
||||
Observable.fromCallable { block() }.subscribeOn(Schedulers.computation()).subscribe()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +49,9 @@ class MemoryCookieJar : CookieJar {
|
||||
}
|
||||
}
|
||||
|
||||
class WrappedCookie private constructor(val cookie: Cookie) {
|
||||
class WrappedCookie private constructor(
|
||||
val cookie: Cookie,
|
||||
) {
|
||||
fun unwrap() = cookie
|
||||
|
||||
fun isExpired() = cookie.expiresAt < System.currentTimeMillis()
|
||||
|
||||
@@ -12,13 +12,13 @@ import eu.kanade.tachiyomi.network.interceptor.CloudflareInterceptor
|
||||
import eu.kanade.tachiyomi.network.interceptor.IgnoreGzipInterceptor
|
||||
import eu.kanade.tachiyomi.network.interceptor.UncaughtExceptionInterceptor
|
||||
import eu.kanade.tachiyomi.network.interceptor.UserAgentInterceptor
|
||||
import io.github.oshai.kotlinlogging.KotlinLogging
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.drop
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import mu.KotlinLogging
|
||||
import okhttp3.Cache
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.brotli.BrotliInterceptor
|
||||
@@ -30,7 +30,9 @@ import java.net.CookieManager
|
||||
import java.net.CookiePolicy
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class NetworkHelper(context: Context) {
|
||||
class NetworkHelper(
|
||||
context: Context,
|
||||
) {
|
||||
// private val preferences: PreferencesHelper by injectLazy()
|
||||
|
||||
// private val cacheDir = File(context.cacheDir, "network_cache")
|
||||
@@ -53,9 +55,7 @@ class NetworkHelper(context: Context) {
|
||||
"AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
|
||||
)
|
||||
|
||||
fun defaultUserAgentProvider(): String {
|
||||
return userAgent.value
|
||||
}
|
||||
fun defaultUserAgentProvider(): String = userAgent.value
|
||||
|
||||
init {
|
||||
@OptIn(DelicateCoroutinesApi::class)
|
||||
@@ -63,14 +63,14 @@ class NetworkHelper(context: Context) {
|
||||
.drop(1)
|
||||
.onEach {
|
||||
GetCatalogueSource.unregisterAllCatalogueSources() // need to reset the headers
|
||||
}
|
||||
.launchIn(GlobalScope)
|
||||
}.launchIn(GlobalScope)
|
||||
}
|
||||
|
||||
private val baseClientBuilder: OkHttpClient.Builder
|
||||
get() {
|
||||
val builder =
|
||||
OkHttpClient.Builder()
|
||||
OkHttpClient
|
||||
.Builder()
|
||||
.cookieJar(PersistentCookieJar(cookieStore))
|
||||
.connectTimeout(30, TimeUnit.SECONDS)
|
||||
.readTimeout(30, TimeUnit.SECONDS)
|
||||
@@ -80,8 +80,7 @@ class NetworkHelper(context: Context) {
|
||||
directory = File.createTempFile("tachidesk_network_cache", null),
|
||||
maxSize = 5L * 1024 * 1024, // 5 MiB
|
||||
),
|
||||
)
|
||||
.addInterceptor(UncaughtExceptionInterceptor())
|
||||
).addInterceptor(UncaughtExceptionInterceptor())
|
||||
.addInterceptor(UserAgentInterceptor(::defaultUserAgentProvider))
|
||||
.addNetworkInterceptor(IgnoreGzipInterceptor())
|
||||
.addNetworkInterceptor(BrotliInterceptor)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package eu.kanade.tachiyomi.network
|
||||
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||
import kotlinx.serialization.DeserializationStrategy
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
@@ -50,9 +49,7 @@ fun Call.asObservable(): Observable<Response> {
|
||||
// call.cancel()
|
||||
}
|
||||
|
||||
override fun isUnsubscribed(): Boolean {
|
||||
return call.isCanceled()
|
||||
}
|
||||
override fun isUnsubscribed(): Boolean = call.isCanceled()
|
||||
}
|
||||
|
||||
subscriber.add(requestArbiter)
|
||||
@@ -60,18 +57,16 @@ fun Call.asObservable(): Observable<Response> {
|
||||
}
|
||||
}
|
||||
|
||||
fun Call.asObservableSuccess(): Observable<Response> {
|
||||
return asObservable()
|
||||
fun Call.asObservableSuccess(): Observable<Response> =
|
||||
asObservable()
|
||||
.doOnNext { response ->
|
||||
if (!response.isSuccessful) {
|
||||
response.close()
|
||||
throw HttpException(response.code)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Based on https://github.com/gildor/kotlin-coroutines-okhttp
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
private suspend fun Call.await(callStack: Array<StackTraceElement>): Response {
|
||||
return suspendCancellableCoroutine { continuation ->
|
||||
val callback =
|
||||
@@ -80,8 +75,9 @@ private suspend fun Call.await(callStack: Array<StackTraceElement>): Response {
|
||||
call: Call,
|
||||
response: Response,
|
||||
) {
|
||||
continuation.resume(response) {
|
||||
continuation.resume(response) { _, resourceToClose, _ ->
|
||||
response.body.close()
|
||||
resourceToClose.close()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,29 +131,28 @@ fun OkHttpClient.newCachelessCallWithProgress(
|
||||
.cache(null)
|
||||
.addNetworkInterceptor { chain ->
|
||||
val originalResponse = chain.proceed(chain.request())
|
||||
originalResponse.newBuilder()
|
||||
originalResponse
|
||||
.newBuilder()
|
||||
.body(ProgressResponseBody(originalResponse.body, listener))
|
||||
.build()
|
||||
}
|
||||
.build()
|
||||
}.build()
|
||||
|
||||
return progressClient.newCall(request)
|
||||
}
|
||||
|
||||
context(Json)
|
||||
inline fun <reified T> Response.parseAs(): T {
|
||||
return decodeFromJsonResponse(serializer(), this)
|
||||
}
|
||||
inline fun <reified T> Response.parseAs(): T = decodeFromJsonResponse(serializer(), this)
|
||||
|
||||
context(Json)
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
fun <T> decodeFromJsonResponse(
|
||||
deserializer: DeserializationStrategy<T>,
|
||||
response: Response,
|
||||
): T {
|
||||
return response.body.source().use {
|
||||
): T =
|
||||
response.body.source().use {
|
||||
decodeFromBufferedSource(deserializer, it)
|
||||
}
|
||||
}
|
||||
|
||||
class HttpException(val code: Int) : IllegalStateException("HTTP error $code")
|
||||
class HttpException(
|
||||
val code: Int,
|
||||
) : IllegalStateException("HTTP error $code")
|
||||
|
||||
@@ -5,7 +5,9 @@ import okhttp3.CookieJar
|
||||
import okhttp3.HttpUrl
|
||||
|
||||
// from TachiWeb-Server
|
||||
class PersistentCookieJar(private val store: PersistentCookieStore) : CookieJar {
|
||||
class PersistentCookieJar(
|
||||
private val store: PersistentCookieStore,
|
||||
) : CookieJar {
|
||||
override fun saveFromResponse(
|
||||
url: HttpUrl,
|
||||
cookies: List<Cookie>,
|
||||
@@ -13,7 +15,5 @@ class PersistentCookieJar(private val store: PersistentCookieStore) : CookieJar
|
||||
store.addAll(url, cookies)
|
||||
}
|
||||
|
||||
override fun loadForRequest(url: HttpUrl): List<Cookie> {
|
||||
return store.get(url)
|
||||
}
|
||||
override fun loadForRequest(url: HttpUrl): List<Cookie> = store.get(url)
|
||||
}
|
||||
|
||||
@@ -8,13 +8,16 @@ import okio.withLock
|
||||
import java.net.CookieStore
|
||||
import java.net.HttpCookie
|
||||
import java.net.URI
|
||||
import java.net.URL
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import java.util.concurrent.locks.ReentrantLock
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
// from TachiWeb-Server
|
||||
class PersistentCookieStore(context: Context) : CookieStore {
|
||||
class PersistentCookieStore(
|
||||
context: Context,
|
||||
) : CookieStore {
|
||||
private val cookieMap = ConcurrentHashMap<String, List<Cookie>>()
|
||||
private val prefs = context.getSharedPreferences("cookie_store", Context.MODE_PRIVATE)
|
||||
|
||||
@@ -22,7 +25,8 @@ class PersistentCookieStore(context: Context) : CookieStore {
|
||||
|
||||
init {
|
||||
val domains =
|
||||
prefs.all.keys.map { it.substringBeforeLast(".") }
|
||||
prefs.all.keys
|
||||
.map { it.substringBeforeLast(".") }
|
||||
.toSet()
|
||||
domains.forEach { domain ->
|
||||
val cookies = prefs.getStringSet(domain, emptySet())
|
||||
@@ -30,7 +34,8 @@ class PersistentCookieStore(context: Context) : CookieStore {
|
||||
try {
|
||||
val url = "http://$domain".toHttpUrlOrNull() ?: return@forEach
|
||||
val nonExpiredCookies =
|
||||
cookies.mapNotNull { Cookie.parse(url, it) }
|
||||
cookies
|
||||
.mapNotNull { Cookie.parse(url, it) }
|
||||
.filter { !it.hasExpired() }
|
||||
cookieMap[domain] = nonExpiredCookies
|
||||
} catch (e: Exception) {
|
||||
@@ -45,10 +50,8 @@ class PersistentCookieStore(context: Context) : CookieStore {
|
||||
cookies: List<Cookie>,
|
||||
) {
|
||||
lock.withLock {
|
||||
val uri = url.toUri()
|
||||
|
||||
// Append or replace the cookies for this domain.
|
||||
val cookiesForDomain = cookieMap[uri.host].orEmpty().toMutableList()
|
||||
val cookiesForDomain = cookieMap[url.host].orEmpty().toMutableList()
|
||||
for (cookie in cookies) {
|
||||
// Find a cookie with the same name. Replace it if found, otherwise add a new one.
|
||||
val pos = cookiesForDomain.indexOfFirst { it.name == cookie.name }
|
||||
@@ -58,63 +61,62 @@ class PersistentCookieStore(context: Context) : CookieStore {
|
||||
cookiesForDomain[pos] = cookie
|
||||
}
|
||||
}
|
||||
cookieMap[uri.host] = cookiesForDomain
|
||||
cookieMap[url.host] = cookiesForDomain
|
||||
|
||||
saveToDisk(uri)
|
||||
saveToDisk(url.toUrl())
|
||||
}
|
||||
}
|
||||
|
||||
override fun removeAll(): Boolean {
|
||||
return lock.withLock {
|
||||
override fun removeAll(): Boolean =
|
||||
lock.withLock {
|
||||
val wasNotEmpty = cookieMap.isEmpty()
|
||||
prefs.edit().clear().apply()
|
||||
cookieMap.clear()
|
||||
wasNotEmpty
|
||||
}
|
||||
}
|
||||
|
||||
fun remove(uri: URI) {
|
||||
val url = uri.toURL()
|
||||
lock.withLock {
|
||||
prefs.edit().remove(uri.host).apply()
|
||||
cookieMap.remove(uri.host)
|
||||
prefs.edit().remove(url.host).apply()
|
||||
cookieMap.remove(url.host)
|
||||
}
|
||||
}
|
||||
|
||||
override fun get(uri: URI): List<HttpCookie> =
|
||||
get(uri.host).map {
|
||||
override fun get(uri: URI): List<HttpCookie> {
|
||||
val url = uri.toURL()
|
||||
return get(url.host).map {
|
||||
it.toHttpCookie()
|
||||
}
|
||||
|
||||
fun get(url: HttpUrl): List<Cookie> {
|
||||
return get(url.toUri().host ?: return emptyList())
|
||||
}
|
||||
|
||||
fun get(url: HttpUrl): List<Cookie> = get(url.host)
|
||||
|
||||
override fun add(
|
||||
uri: URI?,
|
||||
cookie: HttpCookie,
|
||||
) {
|
||||
@Suppress("NAME_SHADOWING")
|
||||
val uri = uri ?: URI("http://" + cookie.domain.removePrefix("."))
|
||||
val url = uri.toURL()
|
||||
lock.withLock {
|
||||
val cookies = cookieMap[uri.host]
|
||||
cookieMap[uri.host] = cookies.orEmpty() + cookie.toCookie(uri)
|
||||
saveToDisk(uri)
|
||||
val cookies = cookieMap[url.host]
|
||||
cookieMap[url.host] = cookies.orEmpty() + cookie.toCookie(uri)
|
||||
saveToDisk(url)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getCookies(): List<HttpCookie> {
|
||||
return cookieMap.values.flatMap {
|
||||
override fun getCookies(): List<HttpCookie> =
|
||||
cookieMap.values.flatMap {
|
||||
it.map {
|
||||
it.toHttpCookie()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getURIs(): List<URI> {
|
||||
return cookieMap.keys().toList().map {
|
||||
override fun getURIs(): List<URI> =
|
||||
cookieMap.keys().toList().map {
|
||||
URI("http://$it")
|
||||
}
|
||||
}
|
||||
|
||||
override fun remove(
|
||||
uri: URI?,
|
||||
@@ -122,8 +124,9 @@ class PersistentCookieStore(context: Context) : CookieStore {
|
||||
): Boolean {
|
||||
@Suppress("NAME_SHADOWING")
|
||||
val uri = uri ?: URI("http://" + cookie.domain.removePrefix("."))
|
||||
val url = uri.toURL()
|
||||
return lock.withLock {
|
||||
val cookies = cookieMap[uri.host].orEmpty()
|
||||
val cookies = cookieMap[url.host].orEmpty()
|
||||
val index =
|
||||
cookies.indexOfFirst {
|
||||
it.name == cookie.name &&
|
||||
@@ -132,8 +135,8 @@ class PersistentCookieStore(context: Context) : CookieStore {
|
||||
if (index >= 0) {
|
||||
val newList = cookies.toMutableList()
|
||||
newList.removeAt(index)
|
||||
cookieMap[uri.host] = newList.toList()
|
||||
saveToDisk(uri)
|
||||
cookieMap[url.host] = newList.toList()
|
||||
saveToDisk(url)
|
||||
true
|
||||
} else {
|
||||
false
|
||||
@@ -141,30 +144,29 @@ class PersistentCookieStore(context: Context) : CookieStore {
|
||||
}
|
||||
}
|
||||
|
||||
private fun get(url: String): List<Cookie> {
|
||||
return cookieMap[url].orEmpty().filter { !it.hasExpired() }
|
||||
}
|
||||
private fun get(url: String): List<Cookie> = cookieMap[url].orEmpty().filter { !it.hasExpired() }
|
||||
|
||||
private fun saveToDisk(uri: URI) {
|
||||
private fun saveToDisk(url: URL) {
|
||||
// Get cookies to be stored in disk
|
||||
val newValues =
|
||||
cookieMap[uri.host]
|
||||
cookieMap[url.host]
|
||||
.orEmpty()
|
||||
.asSequence()
|
||||
.filter { it.persistent && !it.hasExpired() }
|
||||
.map(Cookie::toString)
|
||||
.toSet()
|
||||
|
||||
prefs.edit().putStringSet(uri.host, newValues).apply()
|
||||
prefs.edit().putStringSet(url.host, newValues).apply()
|
||||
}
|
||||
|
||||
private fun Cookie.hasExpired() = System.currentTimeMillis() >= expiresAt
|
||||
|
||||
private fun HttpCookie.toCookie(uri: URI) =
|
||||
Cookie.Builder()
|
||||
Cookie
|
||||
.Builder()
|
||||
.name(name)
|
||||
.value(value)
|
||||
.domain(uri.host)
|
||||
.domain(uri.toURL().host)
|
||||
.path(path ?: "/")
|
||||
.let {
|
||||
if (maxAge != -1L) {
|
||||
@@ -172,22 +174,19 @@ class PersistentCookieStore(context: Context) : CookieStore {
|
||||
} else {
|
||||
it.expiresAt(Long.MAX_VALUE)
|
||||
}
|
||||
}
|
||||
.let {
|
||||
}.let {
|
||||
if (secure) {
|
||||
it.secure()
|
||||
} else {
|
||||
it
|
||||
}
|
||||
}
|
||||
.let {
|
||||
}.let {
|
||||
if (isHttpOnly) {
|
||||
it.httpOnly()
|
||||
} else {
|
||||
it
|
||||
}
|
||||
}
|
||||
.build()
|
||||
}.build()
|
||||
|
||||
private fun Cookie.toHttpCookie(): HttpCookie {
|
||||
val it = this
|
||||
|
||||
@@ -9,22 +9,19 @@ import okio.Source
|
||||
import okio.buffer
|
||||
import java.io.IOException
|
||||
|
||||
class ProgressResponseBody(private val responseBody: ResponseBody, private val progressListener: ProgressListener) : ResponseBody() {
|
||||
class ProgressResponseBody(
|
||||
private val responseBody: ResponseBody,
|
||||
private val progressListener: ProgressListener,
|
||||
) : ResponseBody() {
|
||||
private val bufferedSource: BufferedSource by lazy {
|
||||
source(responseBody.source()).buffer()
|
||||
}
|
||||
|
||||
override fun contentType(): MediaType? {
|
||||
return responseBody.contentType()
|
||||
}
|
||||
override fun contentType(): MediaType? = responseBody.contentType()
|
||||
|
||||
override fun contentLength(): Long {
|
||||
return responseBody.contentLength()
|
||||
}
|
||||
override fun contentLength(): Long = responseBody.contentLength()
|
||||
|
||||
override fun source(): BufferedSource {
|
||||
return bufferedSource
|
||||
}
|
||||
override fun source(): BufferedSource = bufferedSource
|
||||
|
||||
private fun source(source: Source): Source {
|
||||
return object : ForwardingSource(source) {
|
||||
|
||||
@@ -18,13 +18,13 @@ fun GET(
|
||||
url: String,
|
||||
headers: Headers = DEFAULT_HEADERS,
|
||||
cache: CacheControl = DEFAULT_CACHE_CONTROL,
|
||||
): Request {
|
||||
return Request.Builder()
|
||||
): Request =
|
||||
Request
|
||||
.Builder()
|
||||
.url(url)
|
||||
.headers(headers)
|
||||
.cacheControl(cache)
|
||||
.build()
|
||||
}
|
||||
|
||||
/**
|
||||
* @since extensions-lib 1.4
|
||||
@@ -33,52 +33,52 @@ fun GET(
|
||||
url: HttpUrl,
|
||||
headers: Headers = DEFAULT_HEADERS,
|
||||
cache: CacheControl = DEFAULT_CACHE_CONTROL,
|
||||
): Request {
|
||||
return Request.Builder()
|
||||
): Request =
|
||||
Request
|
||||
.Builder()
|
||||
.url(url)
|
||||
.headers(headers)
|
||||
.cacheControl(cache)
|
||||
.build()
|
||||
}
|
||||
|
||||
fun POST(
|
||||
url: String,
|
||||
headers: Headers = DEFAULT_HEADERS,
|
||||
body: RequestBody = DEFAULT_BODY,
|
||||
cache: CacheControl = DEFAULT_CACHE_CONTROL,
|
||||
): Request {
|
||||
return Request.Builder()
|
||||
): Request =
|
||||
Request
|
||||
.Builder()
|
||||
.url(url)
|
||||
.post(body)
|
||||
.headers(headers)
|
||||
.cacheControl(cache)
|
||||
.build()
|
||||
}
|
||||
|
||||
fun PUT(
|
||||
url: String,
|
||||
headers: Headers = DEFAULT_HEADERS,
|
||||
body: RequestBody = DEFAULT_BODY,
|
||||
cache: CacheControl = DEFAULT_CACHE_CONTROL,
|
||||
): Request {
|
||||
return Request.Builder()
|
||||
): Request =
|
||||
Request
|
||||
.Builder()
|
||||
.url(url)
|
||||
.put(body)
|
||||
.headers(headers)
|
||||
.cacheControl(cache)
|
||||
.build()
|
||||
}
|
||||
|
||||
fun DELETE(
|
||||
url: String,
|
||||
headers: Headers = DEFAULT_HEADERS,
|
||||
body: RequestBody = DEFAULT_BODY,
|
||||
cache: CacheControl = DEFAULT_CACHE_CONTROL,
|
||||
): Request {
|
||||
return Request.Builder()
|
||||
): Request =
|
||||
Request
|
||||
.Builder()
|
||||
.url(url)
|
||||
.delete(body)
|
||||
.headers(headers)
|
||||
.cacheControl(cache)
|
||||
.build()
|
||||
}
|
||||
|
||||
+84
-44
@@ -4,6 +4,7 @@ import eu.kanade.tachiyomi.network.NetworkHelper
|
||||
import eu.kanade.tachiyomi.network.POST
|
||||
import eu.kanade.tachiyomi.network.awaitSuccess
|
||||
import eu.kanade.tachiyomi.network.parseAs
|
||||
import io.github.oshai.kotlinlogging.KotlinLogging
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.map
|
||||
@@ -15,7 +16,6 @@ import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import mu.KotlinLogging
|
||||
import okhttp3.Cookie
|
||||
import okhttp3.HttpUrl
|
||||
import okhttp3.Interceptor
|
||||
@@ -23,6 +23,7 @@ import okhttp3.MediaType.Companion.toMediaType
|
||||
import okhttp3.Request
|
||||
import okhttp3.RequestBody.Companion.toRequestBody
|
||||
import okhttp3.Response
|
||||
import okhttp3.ResponseBody.Companion.toResponseBody
|
||||
import suwayomi.tachidesk.server.serverConfig
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.io.IOException
|
||||
@@ -56,11 +57,38 @@ class CloudflareInterceptor(
|
||||
originalResponse.close()
|
||||
// network.cookieStore.remove(originalRequest.url.toUri())
|
||||
|
||||
val request =
|
||||
val flareResponseFallback = serverConfig.flareSolverrAsResponseFallback.value
|
||||
val flareResponse =
|
||||
runBlocking {
|
||||
CFClearance.resolveWithFlareSolverr(setUserAgent, originalRequest)
|
||||
CFClearance.resolveWithFlareSolver(originalRequest, !flareResponseFallback)
|
||||
}
|
||||
|
||||
if (flareResponse.message.contains("not detected", ignoreCase = true)) {
|
||||
logger.debug { "FlareSolverr failed to detect Cloudflare challenge" }
|
||||
|
||||
if (flareResponseFallback &&
|
||||
flareResponse.solution.status in 200..299 &&
|
||||
flareResponse.solution.response != null
|
||||
) {
|
||||
val isImage = flareResponse.solution.response.contains(CHROME_IMAGE_TEMPLATE_REGEX)
|
||||
if (!isImage) {
|
||||
logger.debug { "Falling back to FlareSolverr response" }
|
||||
|
||||
setUserAgent(flareResponse.solution.userAgent)
|
||||
|
||||
return originalResponse
|
||||
.newBuilder()
|
||||
.code(flareResponse.solution.status)
|
||||
.body(flareResponse.solution.response.toResponseBody())
|
||||
.build()
|
||||
} else {
|
||||
logger.debug { "FlareSolverr response is an image html template, not falling back" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val request = CFClearance.requestWithFlareSolverr(flareResponse, setUserAgent, originalRequest)
|
||||
|
||||
chain.proceed(request)
|
||||
} catch (e: Exception) {
|
||||
// Because OkHttp's enqueue only handles IOExceptions, wrap the exception so that
|
||||
@@ -73,6 +101,7 @@ class CloudflareInterceptor(
|
||||
private val ERROR_CODES = listOf(403, 503)
|
||||
private val SERVER_CHECK = arrayOf("cloudflare-nginx", "cloudflare")
|
||||
private val COOKIE_NAMES = listOf("cf_clearance")
|
||||
private val CHROME_IMAGE_TEMPLATE_REGEX = Regex("""<title>(.*?) \(\d+×\d+\)</title>""")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,12 +117,12 @@ object CFClearance {
|
||||
serverConfig.flareSolverrTimeout
|
||||
.map { timeoutInt ->
|
||||
val timeout = timeoutInt.seconds
|
||||
network.client.newBuilder()
|
||||
network.client
|
||||
.newBuilder()
|
||||
.callTimeout(timeout.plus(10.seconds).toJavaDuration())
|
||||
.readTimeout(timeout.plus(5.seconds).toJavaDuration())
|
||||
.build()
|
||||
}
|
||||
.stateIn(GlobalScope, SharingStarted.Eagerly, network.client)
|
||||
}.stateIn(GlobalScope, SharingStarted.Eagerly, network.client)
|
||||
}
|
||||
private val json: Json by injectLazy()
|
||||
private val jsonMediaType = "application/json".toMediaType()
|
||||
@@ -124,13 +153,13 @@ object CFClearance {
|
||||
val name: String,
|
||||
val value: String,
|
||||
val domain: String,
|
||||
val path: String,
|
||||
val path: String? = null,
|
||||
val expires: Double? = null,
|
||||
val size: Int? = null,
|
||||
val httpOnly: Boolean,
|
||||
val secure: Boolean,
|
||||
val httpOnly: Boolean? = null,
|
||||
val secure: Boolean? = null,
|
||||
val session: Boolean? = null,
|
||||
val sameSite: String,
|
||||
val sameSite: String? = null,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
@@ -153,58 +182,68 @@ object CFClearance {
|
||||
val version: String,
|
||||
)
|
||||
|
||||
suspend fun resolveWithFlareSolverr(
|
||||
setUserAgent: (String) -> Unit,
|
||||
suspend fun resolveWithFlareSolver(
|
||||
originalRequest: Request,
|
||||
): Request {
|
||||
onlyCookies: Boolean,
|
||||
): FlareSolverResponse {
|
||||
val timeout = serverConfig.flareSolverrTimeout.value.seconds
|
||||
val flareSolverResponse =
|
||||
with(json) {
|
||||
mutex.withLock {
|
||||
client.value.newCall(
|
||||
|
||||
return with(json) {
|
||||
mutex.withLock {
|
||||
client.value
|
||||
.newCall(
|
||||
POST(
|
||||
url = serverConfig.flareSolverrUrl.value.removeSuffix("/") + "/v1",
|
||||
body =
|
||||
Json.encodeToString(
|
||||
FlareSolverRequest(
|
||||
"request.get",
|
||||
originalRequest.url.toString(),
|
||||
session = serverConfig.flareSolverrSessionName.value,
|
||||
sessionTtlMinutes = serverConfig.flareSolverrSessionTtl.value,
|
||||
cookies =
|
||||
network.cookieStore.get(originalRequest.url).map {
|
||||
FlareSolverCookie(it.name, it.value)
|
||||
},
|
||||
returnOnlyCookies = true,
|
||||
maxTimeout = timeout.inWholeMilliseconds.toInt(),
|
||||
),
|
||||
).toRequestBody(jsonMediaType),
|
||||
Json
|
||||
.encodeToString(
|
||||
FlareSolverRequest(
|
||||
"request.get",
|
||||
originalRequest.url.toString(),
|
||||
session = serverConfig.flareSolverrSessionName.value,
|
||||
sessionTtlMinutes = serverConfig.flareSolverrSessionTtl.value,
|
||||
cookies =
|
||||
network.cookieStore.get(originalRequest.url).map {
|
||||
FlareSolverCookie(it.name, it.value)
|
||||
},
|
||||
returnOnlyCookies = onlyCookies,
|
||||
maxTimeout = timeout.inWholeMilliseconds.toInt(),
|
||||
),
|
||||
).toRequestBody(jsonMediaType),
|
||||
),
|
||||
).awaitSuccess().parseAs<FlareSolverResponse>()
|
||||
}
|
||||
).awaitSuccess()
|
||||
.parseAs<FlareSolverResponse>()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun requestWithFlareSolverr(
|
||||
flareSolverResponse: FlareSolverResponse,
|
||||
setUserAgent: (String) -> Unit,
|
||||
originalRequest: Request,
|
||||
): Request {
|
||||
if (flareSolverResponse.solution.status in 200..299) {
|
||||
setUserAgent(flareSolverResponse.solution.userAgent)
|
||||
val cookies =
|
||||
flareSolverResponse.solution.cookies
|
||||
.map { cookie ->
|
||||
Cookie.Builder()
|
||||
Cookie
|
||||
.Builder()
|
||||
.name(cookie.name)
|
||||
.value(cookie.value)
|
||||
.domain(cookie.domain.removePrefix("."))
|
||||
.path(cookie.path)
|
||||
.expiresAt(cookie.expires?.takeUnless { it < 0.0 }?.toLong() ?: Long.MAX_VALUE)
|
||||
.also {
|
||||
if (cookie.httpOnly) it.httpOnly()
|
||||
if (cookie.secure) it.secure()
|
||||
}
|
||||
.build()
|
||||
}
|
||||
.groupBy { it.domain }
|
||||
if (cookie.httpOnly != null && cookie.httpOnly) it.httpOnly()
|
||||
if (cookie.secure != null && cookie.secure) it.secure()
|
||||
if (!cookie.path.isNullOrEmpty()) it.path(cookie.path)
|
||||
// We need to convert the expires time to milliseconds for the persistent cookie store
|
||||
if (cookie.expires != null && cookie.expires > 0) it.expiresAt((cookie.expires * 1000).toLong())
|
||||
}.build()
|
||||
}.groupBy { it.domain }
|
||||
.flatMap { (domain, cookies) ->
|
||||
network.cookieStore.addAll(
|
||||
HttpUrl.Builder()
|
||||
HttpUrl
|
||||
.Builder()
|
||||
.scheme("http")
|
||||
.host(domain.removePrefix("."))
|
||||
.build(),
|
||||
@@ -219,7 +258,8 @@ object CFClearance {
|
||||
"${it.name}=${it.value}"
|
||||
}
|
||||
logger.trace { "Final cookies\n$finalCookies" }
|
||||
return originalRequest.newBuilder()
|
||||
return originalRequest
|
||||
.newBuilder()
|
||||
.header("Cookie", finalCookies)
|
||||
.header("User-Agent", flareSolverResponse.solution.userAgent)
|
||||
.build()
|
||||
|
||||
+2
-3
@@ -13,8 +13,8 @@ import java.io.IOException
|
||||
* See https://square.github.io/okhttp/4.x/okhttp/okhttp3/-interceptor/
|
||||
*/
|
||||
class UncaughtExceptionInterceptor : Interceptor {
|
||||
override fun intercept(chain: Interceptor.Chain): Response {
|
||||
return try {
|
||||
override fun intercept(chain: Interceptor.Chain): Response =
|
||||
try {
|
||||
chain.proceed(chain.request())
|
||||
} catch (e: Exception) {
|
||||
if (e is IOException) {
|
||||
@@ -23,5 +23,4 @@ class UncaughtExceptionInterceptor : Interceptor {
|
||||
throw IOException(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-1
@@ -3,7 +3,9 @@ package eu.kanade.tachiyomi.network.interceptor
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.Response
|
||||
|
||||
class UserAgentInterceptor(private val userAgentProvider: () -> String) : Interceptor {
|
||||
class UserAgentInterceptor(
|
||||
private val userAgentProvider: () -> String,
|
||||
) : Interceptor {
|
||||
override fun intercept(chain: Interceptor.Chain): Response {
|
||||
val originalRequest = chain.request()
|
||||
|
||||
|
||||
@@ -23,9 +23,7 @@ interface CatalogueSource : Source {
|
||||
* @param page the page number to retrieve.
|
||||
*/
|
||||
@Suppress("DEPRECATION")
|
||||
suspend fun getPopularManga(page: Int): MangasPage {
|
||||
return fetchPopularManga(page).awaitSingle()
|
||||
}
|
||||
suspend fun getPopularManga(page: Int): MangasPage = fetchPopularManga(page).awaitSingle()
|
||||
|
||||
/**
|
||||
* Get a page with a list of manga.
|
||||
@@ -40,9 +38,7 @@ interface CatalogueSource : Source {
|
||||
page: Int,
|
||||
query: String,
|
||||
filters: FilterList,
|
||||
): MangasPage {
|
||||
return fetchSearchManga(page, query, filters).awaitSingle()
|
||||
}
|
||||
): MangasPage = fetchSearchManga(page, query, filters).awaitSingle()
|
||||
|
||||
/**
|
||||
* Get a page with a list of latest manga updates.
|
||||
@@ -51,9 +47,7 @@ interface CatalogueSource : Source {
|
||||
* @param page the page number to retrieve.
|
||||
*/
|
||||
@Suppress("DEPRECATION")
|
||||
suspend fun getLatestUpdates(page: Int): MangasPage {
|
||||
return fetchLatestUpdates(page).awaitSingle()
|
||||
}
|
||||
suspend fun getLatestUpdates(page: Int): MangasPage = fetchLatestUpdates(page).awaitSingle()
|
||||
|
||||
/**
|
||||
* Returns the list of filters for the source.
|
||||
|
||||
@@ -31,9 +31,7 @@ interface Source {
|
||||
* @return the updated manga.
|
||||
*/
|
||||
@Suppress("DEPRECATION")
|
||||
suspend fun getMangaDetails(manga: SManga): SManga {
|
||||
return fetchMangaDetails(manga).awaitSingle()
|
||||
}
|
||||
suspend fun getMangaDetails(manga: SManga): SManga = fetchMangaDetails(manga).awaitSingle()
|
||||
|
||||
/**
|
||||
* Get all the available chapters for a manga.
|
||||
@@ -43,9 +41,7 @@ interface Source {
|
||||
* @return the chapters for the manga.
|
||||
*/
|
||||
@Suppress("DEPRECATION")
|
||||
suspend fun getChapterList(manga: SManga): List<SChapter> {
|
||||
return fetchChapterList(manga).awaitSingle()
|
||||
}
|
||||
suspend fun getChapterList(manga: SManga): List<SChapter> = fetchChapterList(manga).awaitSingle()
|
||||
|
||||
/**
|
||||
* Get the list of pages a chapter has. Pages should be returned
|
||||
@@ -56,9 +52,7 @@ interface Source {
|
||||
* @return the pages for the chapter.
|
||||
*/
|
||||
@Suppress("DEPRECATION")
|
||||
suspend fun getPageList(chapter: SChapter): List<Page> {
|
||||
return fetchPageList(chapter).awaitSingle()
|
||||
}
|
||||
suspend fun getPageList(chapter: SChapter): List<Page> = fetchPageList(chapter).awaitSingle()
|
||||
|
||||
@Deprecated(
|
||||
"Use the non-RxJava API instead",
|
||||
|
||||
@@ -26,23 +26,20 @@ import eu.kanade.tachiyomi.source.model.SManga
|
||||
import eu.kanade.tachiyomi.util.chapter.ChapterRecognition
|
||||
import eu.kanade.tachiyomi.util.lang.compareToCaseInsensitiveNaturalOrder
|
||||
import eu.kanade.tachiyomi.util.storage.EpubFile
|
||||
import io.github.oshai.kotlinlogging.KotlinLogging
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.decodeFromStream
|
||||
import mu.KotlinLogging
|
||||
import nl.adaptivity.xmlutil.ExperimentalXmlUtilApi
|
||||
import nl.adaptivity.xmlutil.core.KtXmlReader
|
||||
import nl.adaptivity.xmlutil.serialization.XML
|
||||
import org.apache.commons.compress.archivers.zip.ZipFile
|
||||
import org.jetbrains.exposed.sql.insert
|
||||
import org.jetbrains.exposed.sql.insertAndGetId
|
||||
import org.jetbrains.exposed.sql.select
|
||||
import org.jetbrains.exposed.sql.selectAll
|
||||
import org.jetbrains.exposed.sql.transactions.transaction
|
||||
import org.kodein.di.DI
|
||||
import org.kodein.di.conf.global
|
||||
import org.kodein.di.instance
|
||||
import suwayomi.tachidesk.manga.impl.util.source.GetCatalogueSource.registerCatalogueSource
|
||||
import suwayomi.tachidesk.manga.impl.util.storage.ImageUtil
|
||||
import suwayomi.tachidesk.manga.model.table.ExtensionTable
|
||||
@@ -59,7 +56,8 @@ import com.github.junrar.Archive as JunrarArchive
|
||||
class LocalSource(
|
||||
private val fileSystem: LocalSourceFileSystem,
|
||||
private val coverManager: LocalCoverManager,
|
||||
) : CatalogueSource, UnmeteredSource {
|
||||
) : CatalogueSource,
|
||||
UnmeteredSource {
|
||||
private val json: Json by injectLazy()
|
||||
private val xml: XML by injectLazy()
|
||||
|
||||
@@ -93,7 +91,8 @@ class LocalSource(
|
||||
// Filter out files that are hidden and is not a folder
|
||||
.filter { it.isDirectory && !it.name.startsWith('.') }
|
||||
.distinctBy { it.name }
|
||||
.filter { // Filter by query or last modified
|
||||
.filter {
|
||||
// Filter by query or last modified
|
||||
if (lastModifiedLimit == 0L) {
|
||||
it.name.contains(query, ignoreCase = true)
|
||||
} else {
|
||||
@@ -134,7 +133,8 @@ class LocalSource(
|
||||
url = mangaDir.name
|
||||
|
||||
// Try to find the cover
|
||||
coverManager.find(mangaDir.name)
|
||||
coverManager
|
||||
.find(mangaDir.name)
|
||||
?.takeIf(File::exists)
|
||||
?.let { thumbnail_url = it.absolutePath }
|
||||
}
|
||||
@@ -238,7 +238,7 @@ class LocalSource(
|
||||
for (chapter in chapterArchives) {
|
||||
when (Format.valueOf(chapter)) {
|
||||
is Format.Zip -> {
|
||||
ZipFile(chapter).use { zip: ZipFile ->
|
||||
ZipFile.builder().setFile(chapter).get().use { zip: ZipFile ->
|
||||
zip.getEntry(COMIC_INFO_FILE)?.let { comicInfoFile ->
|
||||
zip.getInputStream(comicInfoFile).buffered().use { stream ->
|
||||
return copyComicInfoFile(stream, folderPath)
|
||||
@@ -264,13 +264,12 @@ class LocalSource(
|
||||
private fun copyComicInfoFile(
|
||||
comicInfoFileStream: InputStream,
|
||||
folderPath: String?,
|
||||
): File {
|
||||
return File("$folderPath/$COMIC_INFO_FILE").apply {
|
||||
): File =
|
||||
File("$folderPath/$COMIC_INFO_FILE").apply {
|
||||
outputStream().use { outputStream ->
|
||||
comicInfoFileStream.use { it.copyTo(outputStream) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalXmlUtilApi::class)
|
||||
private fun setMangaDetailsFromComicInfoFile(
|
||||
@@ -286,8 +285,9 @@ class LocalSource(
|
||||
}
|
||||
|
||||
// Chapters
|
||||
override suspend fun getChapterList(manga: SManga): List<SChapter> {
|
||||
return fileSystem.getFilesInMangaDirectory(manga.url)
|
||||
override suspend fun getChapterList(manga: SManga): List<SChapter> =
|
||||
fileSystem
|
||||
.getFilesInMangaDirectory(manga.url)
|
||||
// Only keep supported formats
|
||||
.filter { it.isDirectory || Archive.isSupported(it) }
|
||||
.map { chapterFile ->
|
||||
@@ -312,22 +312,21 @@ class LocalSource(
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.sortedWith { c1, c2 ->
|
||||
}.sortedWith { c1, c2 ->
|
||||
val c = c2.chapter_number.compareTo(c1.chapter_number)
|
||||
if (c == 0) c2.name.compareToCaseInsensitiveNaturalOrder(c1.name) else c
|
||||
}
|
||||
.toList()
|
||||
}
|
||||
}.toList()
|
||||
|
||||
// Filters
|
||||
override fun getFilterList() = FilterList(OrderBy.Popular())
|
||||
|
||||
// TODO Fix Memory Leak
|
||||
override suspend fun getPageList(chapter: SChapter): List<Page> {
|
||||
return when (val format = getFormat(chapter)) {
|
||||
override suspend fun getPageList(chapter: SChapter): List<Page> =
|
||||
when (val format = getFormat(chapter)) {
|
||||
is Format.Directory -> {
|
||||
format.file.listFiles().orEmpty()
|
||||
format.file
|
||||
.listFiles()
|
||||
.orEmpty()
|
||||
.filter { !it.isDirectory && ImageUtil.isImage(it.name, it::inputStream) }
|
||||
.sortedWith { f1, f2 -> f1.name.compareToCaseInsensitiveNaturalOrder(f2.name) }
|
||||
.mapIndexed { index, page ->
|
||||
@@ -359,11 +358,11 @@ class LocalSource(
|
||||
pages
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getFormat(chapter: SChapter): Format {
|
||||
try {
|
||||
return fileSystem.getBaseDirectories()
|
||||
return fileSystem
|
||||
.getBaseDirectories()
|
||||
.map { dir -> File(dir, chapter.url) }
|
||||
.find { it.exists() }
|
||||
?.let(Format.Companion::valueOf)
|
||||
@@ -378,21 +377,23 @@ class LocalSource(
|
||||
private fun updateCover(
|
||||
chapter: SChapter,
|
||||
manga: SManga,
|
||||
): File? {
|
||||
return try {
|
||||
): File? =
|
||||
try {
|
||||
when (val format = getFormat(chapter)) {
|
||||
is Format.Directory -> {
|
||||
val entry =
|
||||
format.file.listFiles()
|
||||
format.file
|
||||
.listFiles()
|
||||
?.sortedWith { f1, f2 -> f1.name.compareToCaseInsensitiveNaturalOrder(f2.name) }
|
||||
?.find { !it.isDirectory && ImageUtil.isImage(it.name) { FileInputStream(it) } }
|
||||
|
||||
entry?.let { coverManager.update(manga, it.inputStream()) }
|
||||
}
|
||||
is Format.Zip -> {
|
||||
ZipFile(format.file).use { zip ->
|
||||
ZipFile.builder().setFile(format.file).get().use { zip ->
|
||||
val entry =
|
||||
zip.entries.toList()
|
||||
zip.entries
|
||||
.toList()
|
||||
.sortedWith { f1, f2 -> f1.name.compareToCaseInsensitiveNaturalOrder(f2.name) }
|
||||
.find { !it.isDirectory && ImageUtil.isImage(it.name) { zip.getInputStream(it) } }
|
||||
|
||||
@@ -412,7 +413,8 @@ class LocalSource(
|
||||
is Format.Epub -> {
|
||||
EpubFile(format.file).use { epub ->
|
||||
val entry =
|
||||
epub.getImagesFromPages()
|
||||
epub
|
||||
.getImagesFromPages()
|
||||
.firstOrNull()
|
||||
?.let { epub.getEntry(it) }
|
||||
|
||||
@@ -424,7 +426,6 @@ class LocalSource(
|
||||
logger.error(e) { "Error updating cover for ${manga.title}" }
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val ID = 0L
|
||||
@@ -437,13 +438,13 @@ class LocalSource(
|
||||
|
||||
private val logger = KotlinLogging.logger {}
|
||||
|
||||
private val applicationDirs by DI.global.instance<ApplicationDirs>()
|
||||
private val applicationDirs: ApplicationDirs by injectLazy()
|
||||
|
||||
val pageCache: MutableMap<String, List<() -> InputStream>> = mutableMapOf()
|
||||
|
||||
fun register() {
|
||||
transaction {
|
||||
val sourceRecord = SourceTable.select { SourceTable.id eq ID }.firstOrNull()
|
||||
val sourceRecord = SourceTable.selectAll().where { SourceTable.id eq ID }.firstOrNull()
|
||||
|
||||
if (sourceRecord == null) {
|
||||
// must do this to avoid database integrity errors
|
||||
|
||||
@@ -2,12 +2,14 @@ package eu.kanade.tachiyomi.source.local.filter
|
||||
|
||||
import eu.kanade.tachiyomi.source.model.Filter
|
||||
|
||||
sealed class OrderBy(selection: Selection) : Filter.Sort(
|
||||
"Order by",
|
||||
arrayOf("Title", "Date"),
|
||||
selection,
|
||||
) {
|
||||
class Popular() : OrderBy(Selection(0, true))
|
||||
sealed class OrderBy(
|
||||
selection: Selection,
|
||||
) : Filter.Sort(
|
||||
"Order by",
|
||||
arrayOf("Title", "Date"),
|
||||
selection,
|
||||
) {
|
||||
class Popular : OrderBy(Selection(0, true))
|
||||
|
||||
class Latest() : OrderBy(Selection(1, false))
|
||||
class Latest : OrderBy(Selection(1, false))
|
||||
}
|
||||
|
||||
@@ -11,15 +11,15 @@ private const val DEFAULT_COVER_NAME = "cover.jpg"
|
||||
class LocalCoverManager(
|
||||
private val fileSystem: LocalSourceFileSystem,
|
||||
) {
|
||||
fun find(mangaUrl: String): File? {
|
||||
return fileSystem.getFilesInMangaDirectory(mangaUrl)
|
||||
fun find(mangaUrl: String): File? =
|
||||
fileSystem
|
||||
.getFilesInMangaDirectory(mangaUrl)
|
||||
// Get all file whose names start with 'cover'
|
||||
.filter { it.isFile && it.nameWithoutExtension.equals("cover", ignoreCase = true) }
|
||||
// Get the first actual image
|
||||
.firstOrNull {
|
||||
ImageUtil.isImage(it.name) { it.inputStream() }
|
||||
}
|
||||
}
|
||||
|
||||
fun update(
|
||||
manga: SManga,
|
||||
|
||||
@@ -3,13 +3,21 @@ package eu.kanade.tachiyomi.source.local.io
|
||||
import java.io.File
|
||||
|
||||
sealed interface Format {
|
||||
data class Directory(val file: File) : Format
|
||||
data class Directory(
|
||||
val file: File,
|
||||
) : Format
|
||||
|
||||
data class Zip(val file: File) : Format
|
||||
data class Zip(
|
||||
val file: File,
|
||||
) : Format
|
||||
|
||||
data class Rar(val file: File) : Format
|
||||
data class Rar(
|
||||
val file: File,
|
||||
) : Format
|
||||
|
||||
data class Epub(val file: File) : Format
|
||||
data class Epub(
|
||||
val file: File,
|
||||
) : Format
|
||||
|
||||
class UnknownFormatException : Exception()
|
||||
|
||||
|
||||
+7
-12
@@ -6,27 +6,22 @@ import java.io.File
|
||||
class LocalSourceFileSystem(
|
||||
private val applicationDirs: ApplicationDirs,
|
||||
) {
|
||||
fun getBaseDirectories(): Sequence<File> {
|
||||
return sequenceOf(File(applicationDirs.localMangaRoot))
|
||||
}
|
||||
fun getBaseDirectories(): Sequence<File> = sequenceOf(File(applicationDirs.localMangaRoot))
|
||||
|
||||
fun getFilesInBaseDirectories(): Sequence<File> {
|
||||
return getBaseDirectories()
|
||||
fun getFilesInBaseDirectories(): Sequence<File> =
|
||||
getBaseDirectories()
|
||||
// Get all the files inside all baseDir
|
||||
.flatMap { it.listFiles().orEmpty().toList() }
|
||||
}
|
||||
|
||||
fun getMangaDirectory(name: String): File? {
|
||||
return getFilesInBaseDirectories()
|
||||
fun getMangaDirectory(name: String): File? =
|
||||
getFilesInBaseDirectories()
|
||||
// Get the first mangaDir or null
|
||||
.firstOrNull { it.isDirectory && it.name == name }
|
||||
}
|
||||
|
||||
fun getFilesInMangaDirectory(name: String): Sequence<File> {
|
||||
return getFilesInBaseDirectories()
|
||||
fun getFilesInMangaDirectory(name: String): Sequence<File> =
|
||||
getFilesInBaseDirectories()
|
||||
// Filter out ones that are not related to the manga and is not a directory
|
||||
.filter { it.isDirectory && it.name == name }
|
||||
// Get all the files inside the filtered folders
|
||||
.flatMap { it.listFiles().orEmpty().toList() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,18 +6,20 @@ import java.io.File
|
||||
/**
|
||||
* Loader used to load a chapter from a .epub file.
|
||||
*/
|
||||
class EpubPageLoader(file: File) : PageLoader {
|
||||
class EpubPageLoader(
|
||||
file: File,
|
||||
) : PageLoader {
|
||||
private val epub = EpubFile(file)
|
||||
|
||||
override suspend fun getPages(): List<ReaderPage> {
|
||||
return epub.getImagesFromPages()
|
||||
override suspend fun getPages(): List<ReaderPage> =
|
||||
epub
|
||||
.getImagesFromPages()
|
||||
.mapIndexed { i, path ->
|
||||
val streamFn = { epub.getInputStream(epub.getEntry(path)!!) }
|
||||
ReaderPage(i).apply {
|
||||
stream = streamFn
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun recycle() {
|
||||
epub.close()
|
||||
|
||||
@@ -12,20 +12,21 @@ import java.io.PipedOutputStream
|
||||
/**
|
||||
* Loader used to load a chapter from a .rar or .cbr file.
|
||||
*/
|
||||
class RarPageLoader(file: File) : PageLoader {
|
||||
class RarPageLoader(
|
||||
file: File,
|
||||
) : PageLoader {
|
||||
private val rar = Archive(file)
|
||||
|
||||
override suspend fun getPages(): List<ReaderPage> {
|
||||
return rar.fileHeaders.asSequence()
|
||||
override suspend fun getPages(): List<ReaderPage> =
|
||||
rar.fileHeaders
|
||||
.asSequence()
|
||||
.filter { !it.isDirectory && ImageUtil.isImage(it.fileName) { rar.getInputStream(it) } }
|
||||
.sortedWith { f1, f2 -> f1.fileName.compareToCaseInsensitiveNaturalOrder(f2.fileName) }
|
||||
.mapIndexed { i, header ->
|
||||
ReaderPage(i).apply {
|
||||
stream = { getStream(rar, header) }
|
||||
}
|
||||
}
|
||||
.toList()
|
||||
}
|
||||
}.toList()
|
||||
|
||||
override fun recycle() {
|
||||
rar.close()
|
||||
|
||||
@@ -8,20 +8,21 @@ import java.io.File
|
||||
/**
|
||||
* Loader used to load a chapter from a .zip or .cbz file.
|
||||
*/
|
||||
class ZipPageLoader(file: File) : PageLoader {
|
||||
private val zip = ZipFile(file)
|
||||
class ZipPageLoader(
|
||||
file: File,
|
||||
) : PageLoader {
|
||||
private val zip = ZipFile.builder().setFile(file).get()
|
||||
|
||||
override suspend fun getPages(): List<ReaderPage> {
|
||||
return zip.entries.asSequence()
|
||||
override suspend fun getPages(): List<ReaderPage> =
|
||||
zip.entries
|
||||
.asSequence()
|
||||
.filter { !it.isDirectory && ImageUtil.isImage(it.name) { zip.getInputStream(it) } }
|
||||
.sortedWith { f1, f2 -> f1.name.compareToCaseInsensitiveNaturalOrder(f2.name) }
|
||||
.mapIndexed { i, entry ->
|
||||
ReaderPage(i).apply {
|
||||
stream = { zip.getInputStream(entry) }
|
||||
}
|
||||
}
|
||||
.toList()
|
||||
}
|
||||
}.toList()
|
||||
|
||||
override fun recycle() {
|
||||
zip.close()
|
||||
|
||||
@@ -17,8 +17,7 @@ fun SManga.copyFromComicInfo(comicInfo: ComicInfo) {
|
||||
comicInfo.genre?.value,
|
||||
comicInfo.tags?.value,
|
||||
comicInfo.categories?.value,
|
||||
)
|
||||
.distinct()
|
||||
).distinct()
|
||||
.joinToString(", ") { it.trim() }
|
||||
.takeIf { it.isNotEmpty() }
|
||||
?.let { genre = it }
|
||||
@@ -29,8 +28,7 @@ fun SManga.copyFromComicInfo(comicInfo: ComicInfo) {
|
||||
comicInfo.colorist?.value,
|
||||
comicInfo.letterer?.value,
|
||||
comicInfo.coverArtist?.value,
|
||||
)
|
||||
.flatMap { it.split(", ") }
|
||||
).flatMap { it.split(", ") }
|
||||
.distinct()
|
||||
.joinToString(", ") { it.trim() }
|
||||
.takeIf { it.isNotEmpty() }
|
||||
@@ -58,6 +56,9 @@ data class ComicInfo(
|
||||
val web: Web?,
|
||||
val publishingStatus: PublishingStatusTachiyomi?,
|
||||
val categories: CategoriesTachiyomi?,
|
||||
val day: Day?,
|
||||
val month: Month?,
|
||||
val year: Year?,
|
||||
) {
|
||||
@Suppress("UNUSED")
|
||||
@XmlElement(false)
|
||||
@@ -87,6 +88,24 @@ data class ComicInfo(
|
||||
@XmlValue(true) val value: String = "",
|
||||
)
|
||||
|
||||
@Serializable
|
||||
@XmlSerialName("Day", "", "")
|
||||
data class Day(
|
||||
@XmlValue(true) val value: Int = 0,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
@XmlSerialName("Month", "", "")
|
||||
data class Month(
|
||||
@XmlValue(true) val value: Int = 0,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
@XmlSerialName("Year", "", "")
|
||||
data class Year(
|
||||
@XmlValue(true) val value: Int = 0,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
@XmlSerialName("Summary", "", "")
|
||||
data class Summary(
|
||||
@@ -181,14 +200,12 @@ enum class ComicInfoPublishingStatus(
|
||||
;
|
||||
|
||||
companion object {
|
||||
fun toComicInfoValue(value: Long): String {
|
||||
return entries.firstOrNull { it.sMangaModelValue == value.toInt() }?.comicInfoValue
|
||||
fun toComicInfoValue(value: Long): String =
|
||||
entries.firstOrNull { it.sMangaModelValue == value.toInt() }?.comicInfoValue
|
||||
?: UNKNOWN.comicInfoValue
|
||||
}
|
||||
|
||||
fun toSMangaValue(value: String?): Int {
|
||||
return entries.firstOrNull { it.comicInfoValue == value }?.sMangaModelValue
|
||||
fun toSMangaValue(value: String?): Int =
|
||||
entries.firstOrNull { it.comicInfoValue == value }?.sMangaModelValue
|
||||
?: UNKNOWN.sMangaModelValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,20 +2,40 @@ package eu.kanade.tachiyomi.source.model
|
||||
|
||||
// The class is originally sealed, Tachidesk adds new subclasses for serialization
|
||||
// sealed class Filter<T>(val name: String, var state: T) {
|
||||
open class Filter<T>(val name: String, var state: T) {
|
||||
open class Header(name: String) : Filter<Any>(name, 0)
|
||||
open class Filter<T>(
|
||||
val name: String,
|
||||
var state: T,
|
||||
) {
|
||||
open class Header(
|
||||
name: String,
|
||||
) : Filter<Any>(name, 0)
|
||||
|
||||
open class Separator(name: String = "") : Filter<Any>(name, 0)
|
||||
open class Separator(
|
||||
name: String = "",
|
||||
) : Filter<Any>(name, 0)
|
||||
|
||||
abstract class Select<V>(name: String, val values: Array<V>, state: Int = 0) : Filter<Int>(name, state) {
|
||||
abstract class Select<V>(
|
||||
name: String,
|
||||
val values: Array<V>,
|
||||
state: Int = 0,
|
||||
) : Filter<Int>(name, state) {
|
||||
val displayValues get() = values.map { it.toString() }
|
||||
}
|
||||
|
||||
abstract class Text(name: String, state: String = "") : Filter<String>(name, state)
|
||||
abstract class Text(
|
||||
name: String,
|
||||
state: String = "",
|
||||
) : Filter<String>(name, state)
|
||||
|
||||
abstract class CheckBox(name: String, state: Boolean = false) : Filter<Boolean>(name, state)
|
||||
abstract class CheckBox(
|
||||
name: String,
|
||||
state: Boolean = false,
|
||||
) : Filter<Boolean>(name, state)
|
||||
|
||||
abstract class TriState(name: String, state: Int = STATE_IGNORE) : Filter<Int>(name, state) {
|
||||
abstract class TriState(
|
||||
name: String,
|
||||
state: Int = STATE_IGNORE,
|
||||
) : Filter<Int>(name, state) {
|
||||
fun isIgnored() = state == STATE_IGNORE
|
||||
|
||||
fun isIncluded() = state == STATE_INCLUDE
|
||||
@@ -29,11 +49,20 @@ open class Filter<T>(val name: String, var state: T) {
|
||||
}
|
||||
}
|
||||
|
||||
abstract class Group<V>(name: String, state: List<V>) : Filter<List<V>>(name, state)
|
||||
abstract class Group<V>(
|
||||
name: String,
|
||||
state: List<V>,
|
||||
) : Filter<List<V>>(name, state)
|
||||
|
||||
abstract class Sort(name: String, val values: Array<String>, state: Selection? = null) :
|
||||
Filter<Sort.Selection?>(name, state) {
|
||||
data class Selection(val index: Int, val ascending: Boolean)
|
||||
abstract class Sort(
|
||||
name: String,
|
||||
val values: Array<String>,
|
||||
state: Selection? = null,
|
||||
) : Filter<Sort.Selection?>(name, state) {
|
||||
data class Selection(
|
||||
val index: Int,
|
||||
val ascending: Boolean,
|
||||
)
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package eu.kanade.tachiyomi.source.model
|
||||
|
||||
data class FilterList(val list: List<Filter<*>>) : List<Filter<*>> by list {
|
||||
data class FilterList(
|
||||
val list: List<Filter<*>>,
|
||||
) : List<Filter<*>> by list {
|
||||
constructor(vararg fs: Filter<*>) : this(if (fs.isNotEmpty()) fs.asList() else emptyList())
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
package eu.kanade.tachiyomi.source.model
|
||||
|
||||
data class MangasPage(val mangas: List<SManga>, val hasNextPage: Boolean)
|
||||
data class MangasPage(
|
||||
val mangas: List<SManga>,
|
||||
val hasNextPage: Boolean,
|
||||
)
|
||||
|
||||
@@ -24,8 +24,6 @@ interface SChapter : Serializable {
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun create(): SChapter {
|
||||
return SChapterImpl()
|
||||
}
|
||||
fun create(): SChapter = SChapterImpl()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,34 +25,6 @@ interface SManga : Serializable {
|
||||
|
||||
var initialized: Boolean
|
||||
|
||||
fun copyFrom(other: SManga) {
|
||||
if (other.author != null) {
|
||||
author = other.author
|
||||
}
|
||||
|
||||
if (other.artist != null) {
|
||||
artist = other.artist
|
||||
}
|
||||
|
||||
if (other.description != null) {
|
||||
description = other.description
|
||||
}
|
||||
|
||||
if (other.genre != null) {
|
||||
genre = other.genre
|
||||
}
|
||||
|
||||
if (other.thumbnail_url != null) {
|
||||
thumbnail_url = other.thumbnail_url
|
||||
}
|
||||
|
||||
status = other.status
|
||||
|
||||
if (!initialized) {
|
||||
initialized = other.initialized
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val UNKNOWN = 0
|
||||
const val ONGOING = 1
|
||||
@@ -62,9 +34,7 @@ interface SManga : Serializable {
|
||||
const val CANCELLED = 5
|
||||
const val ON_HIATUS = 6
|
||||
|
||||
fun create(): SManga {
|
||||
return SMangaImpl()
|
||||
}
|
||||
fun create(): SManga = SMangaImpl()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -66,9 +66,7 @@ abstract class HttpSource : CatalogueSource {
|
||||
open val client: OkHttpClient
|
||||
get() = network.client
|
||||
|
||||
private fun generateId(): Long {
|
||||
return generateId("${name.lowercase()}/$lang/$versionId")
|
||||
}
|
||||
private fun generateId(): Long = generateId("${name.lowercase()}/$lang/$versionId")
|
||||
|
||||
/**
|
||||
* Generates a unique ID for the source based on the provided [name], [lang] and
|
||||
@@ -121,13 +119,13 @@ abstract class HttpSource : CatalogueSource {
|
||||
* @param page the page number to retrieve.
|
||||
*/
|
||||
@Deprecated("Use the non-RxJava API instead", replaceWith = ReplaceWith("getPopularManga"))
|
||||
override fun fetchPopularManga(page: Int): Observable<MangasPage> {
|
||||
return client.newCall(popularMangaRequest(page))
|
||||
override fun fetchPopularManga(page: Int): Observable<MangasPage> =
|
||||
client
|
||||
.newCall(popularMangaRequest(page))
|
||||
.asObservableSuccess()
|
||||
.map { response ->
|
||||
popularMangaParse(response)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the request for the popular manga given the page.
|
||||
@@ -156,20 +154,19 @@ abstract class HttpSource : CatalogueSource {
|
||||
page: Int,
|
||||
query: String,
|
||||
filters: FilterList,
|
||||
): Observable<MangasPage> {
|
||||
return Observable.defer {
|
||||
try {
|
||||
client.newCall(searchMangaRequest(page, query, filters)).asObservableSuccess()
|
||||
} catch (e: NoClassDefFoundError) {
|
||||
// RxJava doesn't handle Errors, which tends to happen during global searches
|
||||
// if an old extension using non-existent classes is still around
|
||||
throw RuntimeException(e)
|
||||
}
|
||||
}
|
||||
.map { response ->
|
||||
): Observable<MangasPage> =
|
||||
Observable
|
||||
.defer {
|
||||
try {
|
||||
client.newCall(searchMangaRequest(page, query, filters)).asObservableSuccess()
|
||||
} catch (e: NoClassDefFoundError) {
|
||||
// RxJava doesn't handle Errors, which tends to happen during global searches
|
||||
// if an old extension using non-existent classes is still around
|
||||
throw RuntimeException(e)
|
||||
}
|
||||
}.map { response ->
|
||||
searchMangaParse(response)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the request for the search manga given the page.
|
||||
@@ -197,13 +194,13 @@ abstract class HttpSource : CatalogueSource {
|
||||
* @param page the page number to retrieve.
|
||||
*/
|
||||
@Deprecated("Use the non-RxJava API instead", replaceWith = ReplaceWith("getLatestUpdates"))
|
||||
override fun fetchLatestUpdates(page: Int): Observable<MangasPage> {
|
||||
return client.newCall(latestUpdatesRequest(page))
|
||||
override fun fetchLatestUpdates(page: Int): Observable<MangasPage> =
|
||||
client
|
||||
.newCall(latestUpdatesRequest(page))
|
||||
.asObservableSuccess()
|
||||
.map { response ->
|
||||
latestUpdatesParse(response)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the request for latest manga given the page.
|
||||
@@ -227,18 +224,16 @@ abstract class HttpSource : CatalogueSource {
|
||||
* @return the updated manga.
|
||||
*/
|
||||
@Suppress("DEPRECATION")
|
||||
override suspend fun getMangaDetails(manga: SManga): SManga {
|
||||
return fetchMangaDetails(manga).awaitSingle()
|
||||
}
|
||||
override suspend fun getMangaDetails(manga: SManga): SManga = fetchMangaDetails(manga).awaitSingle()
|
||||
|
||||
@Deprecated("Use the non-RxJava API instead", replaceWith = ReplaceWith("getMangaDetails"))
|
||||
override fun fetchMangaDetails(manga: SManga): Observable<SManga> {
|
||||
return client.newCall(mangaDetailsRequest(manga))
|
||||
override fun fetchMangaDetails(manga: SManga): Observable<SManga> =
|
||||
client
|
||||
.newCall(mangaDetailsRequest(manga))
|
||||
.asObservableSuccess()
|
||||
.map { response ->
|
||||
mangaDetailsParse(response).apply { initialized = true }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the request for the details of a manga. Override only if it's needed to change the
|
||||
@@ -246,9 +241,7 @@ abstract class HttpSource : CatalogueSource {
|
||||
*
|
||||
* @param manga the manga to be updated.
|
||||
*/
|
||||
open fun mangaDetailsRequest(manga: SManga): Request {
|
||||
return GET(baseUrl + manga.url, headers)
|
||||
}
|
||||
open fun mangaDetailsRequest(manga: SManga): Request = GET(baseUrl + manga.url, headers)
|
||||
|
||||
/**
|
||||
* Parses the response from the site and returns the details of a manga.
|
||||
@@ -275,9 +268,10 @@ abstract class HttpSource : CatalogueSource {
|
||||
}
|
||||
|
||||
@Deprecated("Use the non-RxJava API instead", replaceWith = ReplaceWith("getChapterList"))
|
||||
override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> {
|
||||
return if (manga.status != SManga.LICENSED) {
|
||||
client.newCall(chapterListRequest(manga))
|
||||
override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> =
|
||||
if (manga.status != SManga.LICENSED) {
|
||||
client
|
||||
.newCall(chapterListRequest(manga))
|
||||
.asObservableSuccess()
|
||||
.map { response ->
|
||||
chapterListParse(response)
|
||||
@@ -285,7 +279,6 @@ abstract class HttpSource : CatalogueSource {
|
||||
} else {
|
||||
Observable.error(LicensedMangaChaptersException())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the request for updating the chapter list. Override only if it's needed to override
|
||||
@@ -293,9 +286,7 @@ abstract class HttpSource : CatalogueSource {
|
||||
*
|
||||
* @param manga the manga to look for chapters.
|
||||
*/
|
||||
protected open fun chapterListRequest(manga: SManga): Request {
|
||||
return GET(baseUrl + manga.url, headers)
|
||||
}
|
||||
protected open fun chapterListRequest(manga: SManga): Request = GET(baseUrl + manga.url, headers)
|
||||
|
||||
/**
|
||||
* Parses the response from the site and returns a list of chapters.
|
||||
@@ -312,18 +303,16 @@ abstract class HttpSource : CatalogueSource {
|
||||
* @return the pages for the chapter.
|
||||
*/
|
||||
@Suppress("DEPRECATION")
|
||||
override suspend fun getPageList(chapter: SChapter): List<Page> {
|
||||
return fetchPageList(chapter).awaitSingle()
|
||||
}
|
||||
override suspend fun getPageList(chapter: SChapter): List<Page> = fetchPageList(chapter).awaitSingle()
|
||||
|
||||
@Deprecated("Use the non-RxJava API instead", replaceWith = ReplaceWith("getPageList"))
|
||||
override fun fetchPageList(chapter: SChapter): Observable<List<Page>> {
|
||||
return client.newCall(pageListRequest(chapter))
|
||||
override fun fetchPageList(chapter: SChapter): Observable<List<Page>> =
|
||||
client
|
||||
.newCall(pageListRequest(chapter))
|
||||
.asObservableSuccess()
|
||||
.map { response ->
|
||||
pageListParse(response)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the request for getting the page list. Override only if it's needed to override the
|
||||
@@ -331,9 +320,7 @@ abstract class HttpSource : CatalogueSource {
|
||||
*
|
||||
* @param chapter the chapter whose page list has to be fetched.
|
||||
*/
|
||||
protected open fun pageListRequest(chapter: SChapter): Request {
|
||||
return GET(baseUrl + chapter.url, headers)
|
||||
}
|
||||
protected open fun pageListRequest(chapter: SChapter): Request = GET(baseUrl + chapter.url, headers)
|
||||
|
||||
/**
|
||||
* Parses the response from the site and returns a list of pages.
|
||||
@@ -350,16 +337,14 @@ abstract class HttpSource : CatalogueSource {
|
||||
* @param page the page whose source image has to be fetched.
|
||||
*/
|
||||
@Suppress("DEPRECATION")
|
||||
open suspend fun getImageUrl(page: Page): String {
|
||||
return fetchImageUrl(page).awaitSingle()
|
||||
}
|
||||
open suspend fun getImageUrl(page: Page): String = fetchImageUrl(page).awaitSingle()
|
||||
|
||||
@Deprecated("Use the non-RxJava API instead", replaceWith = ReplaceWith("getImageUrl"))
|
||||
open fun fetchImageUrl(page: Page): Observable<String> {
|
||||
return client.newCall(imageUrlRequest(page))
|
||||
open fun fetchImageUrl(page: Page): Observable<String> =
|
||||
client
|
||||
.newCall(imageUrlRequest(page))
|
||||
.asObservableSuccess()
|
||||
.map { imageUrlParse(it) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the request for getting the url to the source image. Override only if it's needed to
|
||||
@@ -367,9 +352,7 @@ abstract class HttpSource : CatalogueSource {
|
||||
*
|
||||
* @param page the chapter whose page list has to be fetched
|
||||
*/
|
||||
protected open fun imageUrlRequest(page: Page): Request {
|
||||
return GET(page.url, headers)
|
||||
}
|
||||
protected open fun imageUrlRequest(page: Page): Request = GET(page.url, headers)
|
||||
|
||||
/**
|
||||
* Parses the response from the site and returns the absolute url to the source image.
|
||||
@@ -385,10 +368,10 @@ abstract class HttpSource : CatalogueSource {
|
||||
* @since extensions-lib 1.5
|
||||
* @param page the page whose source image has to be downloaded.
|
||||
*/
|
||||
open suspend fun getImage(page: Page): Response {
|
||||
return client.newCachelessCallWithProgress(imageRequest(page), page)
|
||||
open suspend fun getImage(page: Page): Response =
|
||||
client
|
||||
.newCachelessCallWithProgress(imageRequest(page), page)
|
||||
.awaitSuccess()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the request for getting the source image. Override only if it's needed to override
|
||||
@@ -396,9 +379,7 @@ abstract class HttpSource : CatalogueSource {
|
||||
*
|
||||
* @param page the chapter whose page list has to be fetched
|
||||
*/
|
||||
protected open fun imageRequest(page: Page): Request {
|
||||
return GET(page.imageUrl!!, headers)
|
||||
}
|
||||
protected open fun imageRequest(page: Page): Request = GET(page.imageUrl!!, headers)
|
||||
|
||||
/**
|
||||
* Assigns the url of the chapter without the scheme and domain. It saves some redundancy from
|
||||
@@ -425,8 +406,8 @@ abstract class HttpSource : CatalogueSource {
|
||||
*
|
||||
* @param orig the full url.
|
||||
*/
|
||||
private fun getUrlWithoutDomain(orig: String): String {
|
||||
return try {
|
||||
private fun getUrlWithoutDomain(orig: String): String =
|
||||
try {
|
||||
val uri = URI(orig.replace(" ", "%20"))
|
||||
var out = uri.path
|
||||
if (uri.query != null) {
|
||||
@@ -439,7 +420,6 @@ abstract class HttpSource : CatalogueSource {
|
||||
} catch (e: URISyntaxException) {
|
||||
orig
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the url of the provided manga
|
||||
@@ -448,9 +428,7 @@ abstract class HttpSource : CatalogueSource {
|
||||
* @param manga the manga
|
||||
* @return url of the manga
|
||||
*/
|
||||
open fun getMangaUrl(manga: SManga): String {
|
||||
return mangaDetailsRequest(manga).url.toString()
|
||||
}
|
||||
open fun getMangaUrl(manga: SManga): String = mangaDetailsRequest(manga).url.toString()
|
||||
|
||||
/**
|
||||
* Returns the url of the provided chapter
|
||||
@@ -459,9 +437,7 @@ abstract class HttpSource : CatalogueSource {
|
||||
* @param chapter the chapter
|
||||
* @return url of the chapter
|
||||
*/
|
||||
open fun getChapterUrl(chapter: SChapter): String {
|
||||
return pageListRequest(chapter).url.toString()
|
||||
}
|
||||
open fun getChapterUrl(chapter: SChapter): String = pageListRequest(chapter).url.toString()
|
||||
|
||||
/**
|
||||
* Called before inserting a new chapter into database. Use it if you need to override chapter
|
||||
|
||||
@@ -138,9 +138,7 @@ abstract class ParsedHttpSource : HttpSource() {
|
||||
*
|
||||
* @param response the response from the site.
|
||||
*/
|
||||
override fun mangaDetailsParse(response: Response): SManga {
|
||||
return mangaDetailsParse(response.asJsoup())
|
||||
}
|
||||
override fun mangaDetailsParse(response: Response): SManga = mangaDetailsParse(response.asJsoup())
|
||||
|
||||
/**
|
||||
* Returns the details of the manga from the given [document].
|
||||
@@ -176,9 +174,7 @@ abstract class ParsedHttpSource : HttpSource() {
|
||||
*
|
||||
* @param response the response from the site.
|
||||
*/
|
||||
override fun pageListParse(response: Response): List<Page> {
|
||||
return pageListParse(response.asJsoup())
|
||||
}
|
||||
override fun pageListParse(response: Response): List<Page> = pageListParse(response.asJsoup())
|
||||
|
||||
/**
|
||||
* Returns a page list from the given document.
|
||||
@@ -192,9 +188,7 @@ abstract class ParsedHttpSource : HttpSource() {
|
||||
*
|
||||
* @param response the response from the site.
|
||||
*/
|
||||
override fun imageUrlParse(response: Response): String {
|
||||
return imageUrlParse(response.asJsoup())
|
||||
}
|
||||
override fun imageUrlParse(response: Response): String = imageUrlParse(response.asJsoup())
|
||||
|
||||
/**
|
||||
* Returns the absolute url to the source image from the document.
|
||||
|
||||
@@ -8,25 +8,17 @@ import org.jsoup.nodes.Element
|
||||
fun Element.selectText(
|
||||
css: String,
|
||||
defaultValue: String? = null,
|
||||
): String? {
|
||||
return select(css).first()?.text() ?: defaultValue
|
||||
}
|
||||
): String? = select(css).first()?.text() ?: defaultValue
|
||||
|
||||
fun Element.selectInt(
|
||||
css: String,
|
||||
defaultValue: Int = 0,
|
||||
): Int {
|
||||
return select(css).first()?.text()?.toInt() ?: defaultValue
|
||||
}
|
||||
): Int = select(css).first()?.text()?.toInt() ?: defaultValue
|
||||
|
||||
fun Element.attrOrText(css: String): String {
|
||||
return if (css != "text") attr(css) else text()
|
||||
}
|
||||
fun Element.attrOrText(css: String): String = if (css != "text") attr(css) else text()
|
||||
|
||||
/**
|
||||
* Returns a Jsoup document for this response.
|
||||
* @param html the body of the response. Use only if the body was read before calling this method.
|
||||
*/
|
||||
fun Response.asJsoup(html: String? = null): Document {
|
||||
return Jsoup.parse(html ?: body.string(), request.url.toString())
|
||||
}
|
||||
fun Response.asJsoup(html: String? = null): Document = Jsoup.parse(html ?: body.string(), request.url.toString())
|
||||
|
||||
@@ -68,15 +68,14 @@ object ChapterRecognition {
|
||||
* @param match result of regex
|
||||
* @return chapter number if found else null
|
||||
*/
|
||||
private fun getChapterNumberFromMatch(match: MatchResult): Double {
|
||||
return match.let {
|
||||
private fun getChapterNumberFromMatch(match: MatchResult): Double =
|
||||
match.let {
|
||||
val initial = it.groups[1]?.value?.toDouble()!!
|
||||
val subChapterDecimal = it.groups[2]?.value
|
||||
val subChapterAlpha = it.groups[3]?.value
|
||||
val addition = checkForDecimal(subChapterDecimal, subChapterAlpha)
|
||||
initial.plus(addition)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for decimal in received strings
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
package eu.kanade.tachiyomi.util.chapter
|
||||
|
||||
object ChapterSanitizer {
|
||||
fun String.sanitize(title: String): String {
|
||||
return trim()
|
||||
fun String.sanitize(title: String): String =
|
||||
trim()
|
||||
.removePrefix(title)
|
||||
.trim(*CHAPTER_TRIM_CHARS)
|
||||
}
|
||||
|
||||
private val CHAPTER_TRIM_CHARS =
|
||||
arrayOf(
|
||||
|
||||
@@ -5,29 +5,35 @@ import java.security.MessageDigest
|
||||
object Hash {
|
||||
private val chars =
|
||||
charArrayOf(
|
||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
||||
'a', 'b', 'c', 'd', 'e', 'f',
|
||||
'0',
|
||||
'1',
|
||||
'2',
|
||||
'3',
|
||||
'4',
|
||||
'5',
|
||||
'6',
|
||||
'7',
|
||||
'8',
|
||||
'9',
|
||||
'a',
|
||||
'b',
|
||||
'c',
|
||||
'd',
|
||||
'e',
|
||||
'f',
|
||||
)
|
||||
|
||||
private val MD5 get() = MessageDigest.getInstance("MD5")
|
||||
|
||||
private val SHA256 get() = MessageDigest.getInstance("SHA-256")
|
||||
|
||||
fun sha256(bytes: ByteArray): String {
|
||||
return encodeHex(SHA256.digest(bytes))
|
||||
}
|
||||
fun sha256(bytes: ByteArray): String = encodeHex(SHA256.digest(bytes))
|
||||
|
||||
fun sha256(string: String): String {
|
||||
return sha256(string.toByteArray())
|
||||
}
|
||||
fun sha256(string: String): String = sha256(string.toByteArray())
|
||||
|
||||
fun md5(bytes: ByteArray): String {
|
||||
return encodeHex(MD5.digest(bytes))
|
||||
}
|
||||
fun md5(bytes: ByteArray): String = encodeHex(MD5.digest(bytes))
|
||||
|
||||
fun md5(string: String): String {
|
||||
return md5(string.toByteArray())
|
||||
}
|
||||
fun md5(string: String): String = md5(string.toByteArray())
|
||||
|
||||
private fun encodeHex(data: ByteArray): String {
|
||||
val l = data.size
|
||||
|
||||
@@ -10,13 +10,12 @@ import kotlin.math.floor
|
||||
fun String.chop(
|
||||
count: Int,
|
||||
replacement: String = "…",
|
||||
): String {
|
||||
return if (length > count) {
|
||||
): String =
|
||||
if (length > count) {
|
||||
take(count - replacement.length) + replacement
|
||||
} else {
|
||||
this
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the given string to have at most [count] characters using [replacement] near the center.
|
||||
@@ -46,9 +45,7 @@ fun String.compareToCaseInsensitiveNaturalOrder(other: String): Int {
|
||||
/**
|
||||
* Returns the size of the string as the number of bytes.
|
||||
*/
|
||||
fun String.byteSize(): Int {
|
||||
return toByteArray(Charsets.UTF_8).size
|
||||
}
|
||||
fun String.byteSize(): Int = toByteArray(Charsets.UTF_8).size
|
||||
|
||||
/**
|
||||
* Returns a string containing the first [n] bytes from this string, or the entire string if this
|
||||
|
||||
@@ -11,11 +11,13 @@ import java.io.InputStream
|
||||
/**
|
||||
* Wrapper over ZipFile to load files in epub format.
|
||||
*/
|
||||
class EpubFile(file: File) : Closeable {
|
||||
class EpubFile(
|
||||
file: File,
|
||||
) : Closeable {
|
||||
/**
|
||||
* Zip file of this epub.
|
||||
*/
|
||||
private val zip = ZipFile(file)
|
||||
private val zip = ZipFile.builder().setFile(file).get()
|
||||
|
||||
/**
|
||||
* Path separator used by this epub.
|
||||
@@ -32,16 +34,12 @@ class EpubFile(file: File) : Closeable {
|
||||
/**
|
||||
* Returns an input stream for reading the contents of the specified zip file entry.
|
||||
*/
|
||||
fun getInputStream(entry: ZipArchiveEntry): InputStream {
|
||||
return zip.getInputStream(entry)
|
||||
}
|
||||
fun getInputStream(entry: ZipArchiveEntry): InputStream = zip.getInputStream(entry)
|
||||
|
||||
/**
|
||||
* Returns the zip file entry for the specified name, or null if not found.
|
||||
*/
|
||||
fun getEntry(name: String): ZipArchiveEntry? {
|
||||
return zip.getEntry(name)
|
||||
}
|
||||
fun getEntry(name: String): ZipArchiveEntry? = zip.getEntry(name)
|
||||
|
||||
/**
|
||||
* Returns the path of all the images found in the epub file.
|
||||
@@ -81,7 +79,8 @@ class EpubFile(file: File) : Closeable {
|
||||
*/
|
||||
fun getPagesFromDocument(document: Document): List<String> {
|
||||
val pages =
|
||||
document.select("manifest > item")
|
||||
document
|
||||
.select("manifest > item")
|
||||
.filter { node -> "application/xhtml+xml" == node.attr("media-type") }
|
||||
.associateBy { it.attr("id") }
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ package suwayomi.tachidesk.global.controller
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
import io.javalin.http.HttpCode
|
||||
import io.javalin.http.HttpStatus
|
||||
import suwayomi.tachidesk.global.impl.GlobalMeta
|
||||
import suwayomi.tachidesk.server.util.formParam
|
||||
import suwayomi.tachidesk.server.util.handler
|
||||
@@ -28,7 +28,7 @@ object GlobalMetaController {
|
||||
ctx.status(200)
|
||||
},
|
||||
withResults = {
|
||||
httpCode(HttpCode.OK)
|
||||
httpCode(HttpStatus.OK)
|
||||
},
|
||||
)
|
||||
|
||||
@@ -48,8 +48,8 @@ object GlobalMetaController {
|
||||
ctx.status(200)
|
||||
},
|
||||
withResults = {
|
||||
httpCode(HttpCode.OK)
|
||||
httpCode(HttpCode.NOT_FOUND)
|
||||
httpCode(HttpStatus.OK)
|
||||
httpCode(HttpStatus.NOT_FOUND)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ package suwayomi.tachidesk.global.controller
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
import io.javalin.http.HttpCode
|
||||
import io.javalin.http.HttpStatus
|
||||
import suwayomi.tachidesk.global.impl.About
|
||||
import suwayomi.tachidesk.global.impl.AboutDataClass
|
||||
import suwayomi.tachidesk.global.impl.AppUpdate
|
||||
@@ -31,7 +31,7 @@ object SettingsController {
|
||||
ctx.json(About.getAbout())
|
||||
},
|
||||
withResults = {
|
||||
json<AboutDataClass>(HttpCode.OK)
|
||||
json<AboutDataClass>(HttpStatus.OK)
|
||||
},
|
||||
)
|
||||
|
||||
@@ -45,12 +45,13 @@ object SettingsController {
|
||||
}
|
||||
},
|
||||
behaviorOf = { ctx ->
|
||||
ctx.future(
|
||||
future { AppUpdate.checkUpdate() },
|
||||
)
|
||||
ctx.future {
|
||||
future { AppUpdate.checkUpdate() }
|
||||
.thenApply { ctx.json(it) }
|
||||
}
|
||||
},
|
||||
withResults = {
|
||||
json<Array<UpdateDataClass>>(HttpCode.OK)
|
||||
json<Array<UpdateDataClass>>(HttpStatus.OK)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import suwayomi.tachidesk.server.generated.BuildConfig
|
||||
data class AboutDataClass(
|
||||
val name: String,
|
||||
val version: String,
|
||||
@Deprecated("The version includes the revision as the patch number")
|
||||
val revision: String,
|
||||
val buildType: String,
|
||||
val buildTime: Long,
|
||||
@@ -20,8 +21,8 @@ data class AboutDataClass(
|
||||
)
|
||||
|
||||
object About {
|
||||
fun getAbout(): AboutDataClass {
|
||||
return AboutDataClass(
|
||||
fun getAbout(): AboutDataClass =
|
||||
AboutDataClass(
|
||||
BuildConfig.NAME,
|
||||
BuildConfig.VERSION,
|
||||
BuildConfig.REVISION,
|
||||
@@ -30,5 +31,4 @@ object About {
|
||||
BuildConfig.GITHUB,
|
||||
BuildConfig.DISCORD,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user