[skip ci] chore: Merge upstream changes (#1916)
This commit is contained in:
parent
5bc10f3b8b
commit
e6fcd0c753
@ -2,10 +2,11 @@ name: 🐞 Issue report
|
||||
description: Report a source issue in Aniyomi
|
||||
labels: [Bug]
|
||||
body:
|
||||
|
||||
- type: input
|
||||
id: source
|
||||
attributes:
|
||||
label: Source information and language
|
||||
label: Source information
|
||||
description: |
|
||||
You can find the extension name and version in **Browse → Extensions**.
|
||||
placeholder: |
|
||||
@ -13,6 +14,15 @@ body:
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: language
|
||||
attributes:
|
||||
label: Source language
|
||||
placeholder: |
|
||||
Example: "English"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: reproduce-steps
|
||||
attributes:
|
||||
@ -53,7 +63,7 @@ body:
|
||||
description: |
|
||||
You can find your Aniyomi version in **More → About**.
|
||||
placeholder: |
|
||||
Example: "0.12.3.10" or "Beta r6080"
|
||||
Example: "0.12.3.10" or "Preview r6151"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
@ -85,7 +95,7 @@ body:
|
||||
required: true
|
||||
- label: I have written a short but informative title.
|
||||
required: true
|
||||
- label: I have updated the app to version **[0.12.3.10](https://github.com/aniyomiorg/aniyomi/releases/latest)**
|
||||
- label: I have updated the app to version **[0.12.3.10](https://github.com/aniyomiorg/aniyomi/releases/latest)**.
|
||||
required: true
|
||||
- label: I have updated all installed extensions.
|
||||
required: true
|
@ -2,6 +2,7 @@ name: 🌐 Source request
|
||||
description: Suggest a new source for Aniyomi
|
||||
labels: [Source request]
|
||||
body:
|
||||
|
||||
- type: input
|
||||
id: name
|
||||
attributes:
|
||||
@ -23,7 +24,7 @@ body:
|
||||
- type: input
|
||||
id: language
|
||||
attributes:
|
||||
label: Language
|
||||
label: Source language
|
||||
placeholder: |
|
||||
Example: "English"
|
||||
validations:
|
@ -1,15 +1,25 @@
|
||||
name: 🔗 URL change report
|
||||
description: Report a URL change for an existing source
|
||||
description: Report URL change of an existing source
|
||||
labels: [Bug,Domain changed]
|
||||
body:
|
||||
|
||||
- type: input
|
||||
id: source
|
||||
attributes:
|
||||
label: Source information and language
|
||||
label: Source information
|
||||
description: |
|
||||
You can find the extension name and version in **Browse → Extensions**.
|
||||
placeholder: |
|
||||
Example: "NotRealSource 13.1 (English)"
|
||||
Example: "NotRealSource 13.1"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: language
|
||||
attributes:
|
||||
label: Source language
|
||||
placeholder: |
|
||||
Example: "English"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
@ -41,7 +51,7 @@ body:
|
||||
required: true
|
||||
- label: I have updated all installed extensions.
|
||||
required: true
|
||||
- label: I have checked if the source URL is not already updated by opening WebView.
|
||||
- label: I have opened WebView and checked that the source URL is not updated yet.
|
||||
required: true
|
||||
- label: I will fill out all of the requested information in this form.
|
||||
required: true
|
63
.github/ISSUE_TEMPLATE/04_report_dead_source.yml
vendored
Normal file
63
.github/ISSUE_TEMPLATE/04_report_dead_source.yml
vendored
Normal file
@ -0,0 +1,63 @@
|
||||
name: ❌ Dead source report
|
||||
description: Source is down and website is closed
|
||||
labels: [Source is down]
|
||||
body:
|
||||
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
### Notice
|
||||
If you have a lot of dead sources to report, please go back and submit a single meta request.
|
||||
|
||||
- type: input
|
||||
id: source
|
||||
attributes:
|
||||
label: Source name
|
||||
description: |
|
||||
You can find the extension name in **Browse → Extensions**.
|
||||
placeholder: |
|
||||
Example: "NotRealSource"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: language
|
||||
attributes:
|
||||
label: Source language
|
||||
placeholder: |
|
||||
Example: "English"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: link
|
||||
attributes:
|
||||
label: Source link
|
||||
placeholder: |
|
||||
Example: "https://notrealsource.org"
|
||||
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: Your issue will be closed if you haven't done these steps.
|
||||
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 title with source name.
|
||||
required: true
|
||||
- label: I have updated all installed extensions.
|
||||
required: true
|
||||
- label: I have opened WebView and checked that the source website is down.
|
||||
required: true
|
||||
- label: I will fill out all of the requested information in this form.
|
||||
required: true
|
@ -2,14 +2,24 @@ name: ⭐ Feature request
|
||||
description: Suggest a feature to improve an existing source
|
||||
labels: [Feature request]
|
||||
body:
|
||||
|
||||
- type: input
|
||||
id: source
|
||||
attributes:
|
||||
label: Source name and language
|
||||
label: Source name
|
||||
description: |
|
||||
You can find the extension name in **Browse → Extensions**.
|
||||
placeholder: |
|
||||
Example: "DopeBox (English)"
|
||||
Example: "DopeBox"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: language
|
||||
attributes:
|
||||
label: Source language
|
||||
placeholder: |
|
||||
Example: "English"
|
||||
validations:
|
||||
required: true
|
||||
|
@ -1,4 +1,4 @@
|
||||
name: 🧠 Meta feature request
|
||||
name: 🧠 Meta request
|
||||
description: Suggest improvements to the project
|
||||
labels: [Meta request]
|
||||
body:
|
||||
@ -27,7 +27,7 @@ body:
|
||||
label: Acknowledgements
|
||||
description: Your issue will be closed if you haven't done these steps.
|
||||
options:
|
||||
- label: I have searched the existing issues and this is a new ticket, **NOT** a duplicate or related to another open issue.
|
||||
- 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.
|
||||
required: true
|
5
.github/workflows/batch_close_issues.yml
vendored
5
.github/workflows/batch_close_issues.yml
vendored
@ -18,9 +18,8 @@ jobs:
|
||||
# Close everything older than ~6 months
|
||||
days-before-issue-stale: 180
|
||||
days-before-issue-close: 0
|
||||
any-of-issue-labels: "Source request"
|
||||
exempt-issue-labels: do-not-autoclose
|
||||
close-issue-message: "In an effort to have a more manageable issue backlog, we're closing older requests that weren't addressed. If you think the source may still benefit others, please open a new request."
|
||||
exempt-issue-labels: "do-not-autoclose,Meta request"
|
||||
close-issue-message: "In an effort to have a more manageable issue backlog, we're closing older requests that weren't addressed since there's a low chance of it being addressed if it hasn't already. If your request is still relevant, please [open a new request](https://github.com/aniyomiorg/aniyomi-extensions/issues/new/choose)."
|
||||
close-issue-reason: not_planned
|
||||
ascending: true
|
||||
operations-per-run: 250
|
||||
|
11
.github/workflows/build_pull_request.yml
vendored
11
.github/workflows/build_pull_request.yml
vendored
@ -4,6 +4,7 @@ on:
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
- '.github/workflows/issue_moderator.yml'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
|
||||
@ -50,6 +51,8 @@ jobs:
|
||||
isIndividualChanged=1
|
||||
elif [[ ${changedFile} == multisrc/* ]]; then
|
||||
isMultisrcChanged=1
|
||||
elif [[ ${changedFile} == .github/workflows/issue_moderator.yml ]]; then
|
||||
true
|
||||
elif [[ ${changedFile} == *.md ]]; then
|
||||
true
|
||||
else
|
||||
@ -63,7 +66,7 @@ jobs:
|
||||
|
||||
- name: Generate multisrc sources
|
||||
if: ${{ steps.parse-changed-files.outputs.isMultisrcChanged == '1' }}
|
||||
uses: gradle/gradle-command-action@v2
|
||||
uses: gradle/gradle-build-action@v2
|
||||
with:
|
||||
arguments: :multisrc:generateExtensions
|
||||
|
||||
@ -111,7 +114,7 @@ jobs:
|
||||
distribution: adopt
|
||||
|
||||
- name: Generate sources from the multi-source library
|
||||
uses: gradle/gradle-command-action@v2
|
||||
uses: gradle/gradle-build-action@v2
|
||||
env:
|
||||
CI_MODULE_GEN: "true"
|
||||
with:
|
||||
@ -119,7 +122,7 @@ jobs:
|
||||
cache-read-only: true
|
||||
|
||||
- name: Build extensions (chunk ${{ matrix.chunk }})
|
||||
uses: gradle/gradle-command-action@v2
|
||||
uses: gradle/gradle-build-action@v2
|
||||
env:
|
||||
CI_MULTISRC: "true"
|
||||
CI_CHUNK_NUM: ${{ matrix.chunk }}
|
||||
@ -145,7 +148,7 @@ jobs:
|
||||
distribution: adopt
|
||||
|
||||
- name: Build extensions (chunk ${{ matrix.chunk }})
|
||||
uses: gradle/gradle-command-action@v2
|
||||
uses: gradle/gradle-build-action@v2
|
||||
env:
|
||||
CI_MULTISRC: "false"
|
||||
CI_CHUNK_NUM: ${{ matrix.chunk }}
|
||||
|
11
.github/workflows/build_push.yml
vendored
11
.github/workflows/build_push.yml
vendored
@ -6,6 +6,7 @@ on:
|
||||
- master
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
- '.github/workflows/issue_moderator.yml'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}
|
||||
@ -62,7 +63,7 @@ jobs:
|
||||
distribution: adopt
|
||||
|
||||
- name: Generate multisrc sources
|
||||
uses: gradle/gradle-command-action@v2
|
||||
uses: gradle/gradle-build-action@v2
|
||||
with:
|
||||
arguments: :multisrc:generateExtensions
|
||||
|
||||
@ -115,14 +116,14 @@ jobs:
|
||||
echo ${{ secrets.SIGNING_KEY }} | base64 -d > signingkey.jks
|
||||
|
||||
- name: Generate sources from the multi-source library
|
||||
uses: gradle/gradle-command-action@v2
|
||||
uses: gradle/gradle-build-action@v2
|
||||
env:
|
||||
CI_MODULE_GEN: "true"
|
||||
with:
|
||||
arguments: :multisrc:generateExtensions
|
||||
|
||||
- name: Build extensions (chunk ${{ matrix.chunk }})
|
||||
uses: gradle/gradle-command-action@v2
|
||||
uses: gradle/gradle-build-action@v2
|
||||
env:
|
||||
CI_MULTISRC: "true"
|
||||
CI_CHUNK_NUM: ${{ matrix.chunk }}
|
||||
@ -166,7 +167,7 @@ jobs:
|
||||
echo ${{ secrets.SIGNING_KEY }} | base64 -d > signingkey.jks
|
||||
|
||||
- name: Build extensions (chunk ${{ matrix.chunk }})
|
||||
uses: gradle/gradle-command-action@v2
|
||||
uses: gradle/gradle-build-action@v2
|
||||
env:
|
||||
CI_MULTISRC: "false"
|
||||
CI_CHUNK_NUM: ${{ matrix.chunk }}
|
||||
@ -190,8 +191,8 @@ jobs:
|
||||
publish_repo:
|
||||
name: Publish repo
|
||||
needs:
|
||||
- build_individual
|
||||
- build_multisrc
|
||||
- build_individual
|
||||
if: "github.repository == 'aniyomiorg/aniyomi-extensions'"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
12
.github/workflows/issue_moderator.yml
vendored
12
.github/workflows/issue_moderator.yml
vendored
@ -14,10 +14,16 @@ jobs:
|
||||
uses: tachiyomiorg/issue-moderator-action@v2
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
duplicate-label: Duplicate
|
||||
|
||||
duplicate-check-enabled: true
|
||||
duplicate-check-label: Source request
|
||||
duplicate-check-labels: |
|
||||
["Source request", "Domain changed"]
|
||||
|
||||
existing-check-enabled: true
|
||||
existing-check-label: Source request
|
||||
existing-check-labels: |
|
||||
["Source request", "Domain changed"]
|
||||
|
||||
auto-close-rules: |
|
||||
[
|
||||
{
|
||||
@ -37,7 +43,7 @@ jobs:
|
||||
},
|
||||
{
|
||||
"type": "both",
|
||||
"regex": ".*(?:fail(?:ed|ure)?|can\\s*(?:no|')?t|unable|get past|(?<!n[o']?t )blocked by|error) (?:to )?(?:bypass|penetrate)?.*cloudfl?are.*",
|
||||
"regex": ".*(?:fail(?:ed|ure|s)?|can\\s*(?:no|')?t|(?:not|un).*able|(?<!n[o']?t )blocked by|error) (?:to )?(?:get past|by ?pass|penetrate)?.*cloud ?fl?are.*",
|
||||
"ignoreCase": true,
|
||||
"message": "Refer to the **Solving Cloudflare issues** section at https://aniyomi.org/help/guides/troubleshooting/#solving-cloudflare-issues. If it doesn't work, migrate to other sources or wait until they lower their protection."
|
||||
}
|
||||
|
218
CONTRIBUTING.md
218
CONTRIBUTING.md
@ -57,15 +57,47 @@ Some alternative steps can be followed to ignore "repo" branch and skip unrelate
|
||||
<details><summary>Steps</summary>
|
||||
|
||||
1. Make sure to delete "repo" branch in your fork. You may also want to disable Actions in the repo settings.
|
||||
|
||||
**Also make sure you are using the latest version of Git as many commands used here are pretty new.**
|
||||
|
||||
2. Do a partial clone.
|
||||
```bash
|
||||
git clone --filter=blob:none --no-checkout <fork-repo-url>
|
||||
git clone --filter=blob:none --sparse <fork-repo-url>
|
||||
cd aniyomi-extensions/
|
||||
```
|
||||
3. Configure sparse checkout.
|
||||
|
||||
There are two modes of pattern matching. The default is cone (🔺) mode.
|
||||
Cone mode enables significantly faster pattern matching for big monorepos
|
||||
and the sparse index feature to make Git commands more responsive.
|
||||
In this mode, you can only filter by file path, which is less flexible
|
||||
and might require more work when the project structure changes.
|
||||
|
||||
You can skip this code block to use legacy mode if you want easier filters.
|
||||
It won't be much slower as the repo doesn't have that many files.
|
||||
|
||||
To enable cone mode together with sparse index, follow these steps:
|
||||
|
||||
```bash
|
||||
git sparse-checkout set --cone --sparse-index
|
||||
# add project folders
|
||||
git sparse-checkout add .run buildSrc core gradle lib multisrc/src/main/java/generator
|
||||
# add a single source
|
||||
git sparse-checkout add src/<lang>/<source>
|
||||
# add a multisrc theme
|
||||
git sparse-checkout add multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/<source>
|
||||
git sparse-checkout add multisrc/overrides/<source>
|
||||
```
|
||||
|
||||
To remove a source, open `.git/info/sparse-checkout` and delete the exact
|
||||
lines you typed when adding it. Don't touch the other auto-generated lines
|
||||
unless you fully understand how cone mode works, or you might break it.
|
||||
|
||||
To use the legacy non-cone mode, follow these steps:
|
||||
|
||||
```bash
|
||||
# enable sparse checkout
|
||||
git sparse-checkout set
|
||||
git sparse-checkout set --no-cone
|
||||
# edit sparse checkout filter
|
||||
vim .git/info/sparse-checkout
|
||||
# alternatively, if you have VS Code installed
|
||||
@ -85,10 +117,14 @@ Some alternative steps can be followed to ignore "repo" branch and skip unrelate
|
||||
# or type the source name directly
|
||||
<source>
|
||||
```
|
||||
|
||||
Explanation: the rules are like `gitignore`. We first exclude all sources
|
||||
while retaining project folders, then add the needed sources back manually.
|
||||
|
||||
4. Configure remotes.
|
||||
```bash
|
||||
# add upstream
|
||||
git remote add upstream <aniyomi-repo-url>
|
||||
git remote add upstream <aniyomiorg-repo-url>
|
||||
# optionally disable push to upstream
|
||||
git remote set-url --push upstream no_pushing
|
||||
# ignore 'repo' branch of upstream
|
||||
@ -100,8 +136,6 @@ Some alternative steps can be followed to ignore "repo" branch and skip unrelate
|
||||
git remote update
|
||||
# track master of upstream instead of fork
|
||||
git branch master -u upstream/master
|
||||
# checkout
|
||||
git switch master
|
||||
```
|
||||
5. Useful configurations. (optional)
|
||||
```bash
|
||||
@ -109,15 +143,28 @@ Some alternative steps can be followed to ignore "repo" branch and skip unrelate
|
||||
git config remote.origin.prune true
|
||||
# fast-forward only when pulling master branch
|
||||
git config pull.ff only
|
||||
# Add an alias to sync master branch without fetching useless blobs.
|
||||
# If you run `git pull` to fast-forward in a blobless clone like this,
|
||||
# all blobs (files) in the new commits are still fetched regardless of
|
||||
# sparse rules, which makes the local repo accumulate unused files.
|
||||
# Use `git sync-master` to avoid this. Be careful if you have changes
|
||||
# on master branch, which is not a good practice.
|
||||
git config alias.sync-master '!git switch master && git fetch upstream && git reset --keep FETCH_HEAD'
|
||||
```
|
||||
6. Later, if you change the sparse checkout filter, run `git sparse-checkout reapply`.
|
||||
|
||||
Read more on [partial clone](https://github.blog/2020-12-21-get-up-to-speed-with-partial-clone-and-shallow-clone/), [sparse checkout](https://github.blog/2020-01-17-bring-your-monorepo-down-to-size-with-sparse-checkout/) and [negative refspecs](https://github.blog/2020-10-19-git-2-29-released/#user-content-negative-refspecs).
|
||||
Read more on
|
||||
[Git's object model](https://github.blog/2020-12-17-commits-are-snapshots-not-diffs/),
|
||||
[partial clone](https://github.blog/2020-12-21-get-up-to-speed-with-partial-clone-and-shallow-clone/),
|
||||
[sparse checkout](https://github.blog/2020-01-17-bring-your-monorepo-down-to-size-with-sparse-checkout/),
|
||||
[sparse index](https://github.blog/2021-11-10-make-your-monorepo-feel-small-with-gits-sparse-index/),
|
||||
and [negative refspecs](https://github.blog/2020-10-19-git-2-29-released/#user-content-negative-refspecs).
|
||||
</details>
|
||||
|
||||
## Getting help
|
||||
|
||||
- Join [the Discord server](https://discord.gg/F32UjdJZrR) for online help and to ask questions while developing your extension.
|
||||
- Join [the Discord server](https://discord.gg/F32UjdJZrR) for online help and to ask questions while developing your extension. When doing so, please ask it in the `#dev` channel.
|
||||
|
||||
- There are some features and tricks that are not explored in this document. Refer to existing extension code for examples.
|
||||
|
||||
## Writing an extension
|
||||
@ -139,18 +186,19 @@ $ tree src/<lang>/<mysourcename>/
|
||||
src/<lang>/<mysourcename>/
|
||||
├── AndroidManifest.xml
|
||||
├── build.gradle
|
||||
├── build.gradle
|
||||
├── res
|
||||
│ ├── mipmap-hdpi
|
||||
│ │ └── ic_launcher.png
|
||||
│ ├── mipmap-mdpi
|
||||
│ │ └── ic_launcher.png
|
||||
│ ├── mipmap-xhdpi
|
||||
│ │ └── ic_launcher.png
|
||||
│ ├── mipmap-xxhdpi
|
||||
│ │ └── ic_launcher.png
|
||||
│ ├── mipmap-xxxhdpi
|
||||
│ │ └── ic_launcher.png
|
||||
│ └── web_hi_res_512.png
|
||||
│ ├── mipmap-hdpi
|
||||
│ │ └── ic_launcher.png
|
||||
│ ├── mipmap-mdpi
|
||||
│ │ └── ic_launcher.png
|
||||
│ ├── mipmap-xhdpi
|
||||
│ │ └── ic_launcher.png
|
||||
│ ├── mipmap-xxhdpi
|
||||
│ │ └── ic_launcher.png
|
||||
│ ├── mipmap-xxxhdpi
|
||||
│ │ └── ic_launcher.png
|
||||
│ └── web_hi_res_512.png
|
||||
└── src
|
||||
└── eu
|
||||
└── kanade
|
||||
@ -193,7 +241,7 @@ apply from: "$rootDir/common.gradle"
|
||||
| `libVersion` | (Optional, defaults to `13`) The version of the [extensions library](https://github.com/aniyomiorg/extensions-lib) used. |
|
||||
| `containsNsfw` | (Optional, defaults to `false`) Flag to indicate that a source contains NSFW content. |
|
||||
|
||||
The extension's version name is generated automatically by concatenating `libVersion` and `extVersionCode`. With the example used above, the version would be `12.1`.
|
||||
The extension's version name is generated automatically by concatenating `libVersion` and `extVersionCode`. With the example used above, the version would be `13`.
|
||||
|
||||
### Core dependencies
|
||||
|
||||
@ -201,34 +249,47 @@ The extension's version name is generated automatically by concatenating `libVer
|
||||
|
||||
Extensions rely on [extensions-lib](https://github.com/aniyomiorg/extensions-lib), which provides some interfaces and stubs from the [app](https://github.com/aniyomiorg/aniyomi) for compilation purposes. The actual implementations can be found [here](https://github.com/aniyomiorg/aniyomi/tree/master/app/src/main/java/eu/kanade/tachiyomi/animesource). Referencing the actual implementation will help with understanding extensions' call flow.
|
||||
|
||||
#### Rate limiting library
|
||||
#### CryptoAES library
|
||||
|
||||
[`lib-ratelimit`](https://github.com/aniyomiorg/aniyomi-extensions/tree/master/lib/ratelimit) is a library for adding rate limiting functionality as an [OkHttp interceptor](https://square.github.io/okhttp/interceptors/).
|
||||
The [`lib-cryptoaes`](https://github.com/aniyomiorg/aniyomi-extensions/tree/master/lib/cryptoaes) provides utilities for decrypting AES-encrypted data, like data encrypted with AES+EvpKDF (The key-derivation algorithm used by the [cryptojs](https://cryptojs.gitbook.io/docs/) library). It also includes some utilities to decrypt strings in the [jsfuck](https://jsfuck.com/) format.
|
||||
|
||||
```gradle
|
||||
dependencies {
|
||||
implementation project(':lib-ratelimit')
|
||||
implementation(project(":lib-cryptoaes"))
|
||||
}
|
||||
```
|
||||
|
||||
#### Unpacker library
|
||||
|
||||
The [`lib-unpacker`](https://github.com/aniyomiorg/aniyomi-extensions/tree/master/lib/unpacker) library provides a deobfuscator(unpacker) for javascript code obfuscated with the [jspacker](http://dean.edwards.name/packer/) algorithm.
|
||||
|
||||
```gradle
|
||||
dependencies {
|
||||
implementation(project(":lib-unpacker"))
|
||||
}
|
||||
```
|
||||
|
||||
#### Synchrony library
|
||||
|
||||
[`lib-synchrony`](https://github.com/aniyomiorg/aniyomi-extensions/tree/master/lib/synchrony) is a library that bundles and runs the [synchrony](https://github.com/relative/synchrony) deobfuscator with your extension to help when deobfuscating obfuscated javascript. Useful to get data on highly obfuscated javascript code.
|
||||
|
||||
```gradle
|
||||
dependencies {
|
||||
implementation(project(":lib-synchrony"))
|
||||
}
|
||||
```
|
||||
|
||||
#### Additional dependencies
|
||||
|
||||
You may find yourself needing additional functionality and wanting to add more dependencies to your `build.gradle` file. Since extensions are run within the main Aniyomi app, you can make use of [its dependencies](https://github.com/aniyomiorg/aniyomi/blob/master/app/build.gradle.kts).
|
||||
|
||||
For example, an extension that needs coroutines, it could add the following:
|
||||
|
||||
```gradle
|
||||
dependencies {
|
||||
compileOnly(libs.bundles.coroutines)
|
||||
}
|
||||
```
|
||||
If you find yourself needing additional functionality, you can add more dependencies to your `build.gradle` file.
|
||||
Many of [the dependencies](https://github.com/aniyomiorg/aniyomi/blob/master/app/build.gradle.kts) from the main Aniyomi app are exposed to extensions by default.
|
||||
|
||||
> Note that several dependencies are already exposed to all extensions via Gradle version catalog.
|
||||
> To view which are available view `libs.versions.toml` under the `gradle` folder
|
||||
|
||||
Notice that we're using `compileOnly` instead of `implementation`, since the app already contains it. You could use `implementation` instead for a new dependency, or you prefer not to rely on whatever the main app has at the expense of app size.
|
||||
Notice that we're using `compileOnly` instead of `implementation` if the app already contains it. You could use `implementation` instead for a new dependency, or you prefer not to rely on whatever the main app has at the expense of app size.
|
||||
|
||||
Note that using `compileOnly` restricts you to versions that must be compatible with those used in [Aniyomi v0.10.12+](https://github.com/aniyomiorg/aniyomi/blob/v0.10.12/app/build.gradle.kts) for proper backwards compatibility.
|
||||
Note that using `compileOnly` restricts you to versions that must be compatible with those used in [the latest stable version of Aniyomi](https://github.com/aniyomiorg/aniyomi/releases/latest).
|
||||
|
||||
### Extension main class
|
||||
|
||||
@ -246,7 +307,7 @@ The class which is referenced and defined by `extClass` in `build.gradle`. This
|
||||
| ----- | ----------- |
|
||||
| `name` | Name displayed in the "Sources" tab in Aniyomi. |
|
||||
| `baseUrl` | Base URL of the source without any trailing slashes. |
|
||||
| `lang` | An ISO 639-1 compliant language code (two letters in lower case). |
|
||||
| `lang` | An ISO 639-1 compliant language code (two letters in lower case in most cases, but can also include the country/dialect part by using a simple dash character). |
|
||||
| `id` | Identifier of your source, automatically set in `AnimeHttpSource`. It should only be manually overriden if you need to copy an existing autogenerated ID. |
|
||||
|
||||
### Extension call flow
|
||||
@ -256,9 +317,9 @@ The class which is referenced and defined by `extClass` in `build.gradle`. This
|
||||
a.k.a. the Browse source entry point in the app (invoked by tapping on the source name).
|
||||
|
||||
- The app calls `fetchPopularAnime` which should return a `AnimesPage` containing the first batch of found `SAnime` entries.
|
||||
- This method supports pagination. When user scrolls the manga list and more results must be fetched, the app calls it again with increasing `page` values(starting with `page=1`). This continues until `AnimesPage.hasNextPage` is passed as `true` and `AnimesPage.mangas` is not empty.
|
||||
- To show the list properly, the app needs `url`, `title` and `thumbnail_url`. You must set them here. The rest of the fields could be filled later.(refer to Anime Details below)
|
||||
- You should set `thumbnail_url` if is available, if not, `fetchAnimeDetails` will be **immediately** called.(this will increase network calls heavily and should be avoided)
|
||||
- This method supports pagination. When user scrolls the manga list and more results must be fetched, the app calls it again with increasing `page` values(starting with `page=1`). This continues while `AnimesPage.hasNextPage` is passed as `true` and `AnimesPage.mangas` is not empty.
|
||||
- To show the list properly, the app needs `url`, `title` and `thumbnail_url`. You **must** set them here. The rest of the fields could be filled later.(refer to Anime Details below).
|
||||
- You should set `thumbnail_url` if is available, if not, `fetchAnimeDetails` will be **immediately** called. (this will increase network calls heavily and should be avoided).
|
||||
|
||||
#### Latest Anime
|
||||
|
||||
@ -271,30 +332,30 @@ a.k.a. the Latest source entry point in the app (invoked by tapping on the "Late
|
||||
|
||||
- When the user searches inside the app, `fetchSearchAnime` will be called and the rest of the flow is similar to what happens with `fetchPopularAnime`.
|
||||
- If search functionality is not available, return `Observable.just(AnimesPage(emptyList(), false))`
|
||||
- `getFilterList` will be called to get all filters and filter types. **TODO: explain more about `Filter`**
|
||||
- `getFilterList` will be called to get all filters and filter types.
|
||||
|
||||
##### Filters
|
||||
|
||||
The search flow have support to filters that can be added to a `FilterList` inside the `getFilterList` method. When the user changes the filters' state, they will be passed to the `searchRequest`, and they can be iterated to create the request (by getting the `filter.state` value, where the type varies depending on the `Filter` used). You can check the filter types available [here](https://github.com/aniyomiorg/aniyomi/blob/master/app/src/main/java/eu/kanade/tachiyomi/source/model/Filter.kt) and in the table below.
|
||||
The search flow have support to filters that can be added to a `AnimeFilterList` inside the `getFilterList` method. When the user changes the filter's state, they will be passed to the `searchAnimeRequest`, and they can be iterated to create the request (by getting the `filter.state` value, where the type varies depending on the `AnimeFilter` used). You can check the filter types available [here](https://github.com/aniyomiorg/aniyomi/blob/master/source-api/src/main/java/eu/kanade/tachiyomi/animesource/model/AnimeFilter.kt) and in the table below.
|
||||
|
||||
| Filter | State type | Description |
|
||||
| ------ | ---------- | ----------- |
|
||||
| `Filter.Header` | None | A simple header. Useful for separating sections in the list or showing any note or warning to the user. |
|
||||
| `Filter.Separator` | None | A line separator. Useful for visual distinction between sections. |
|
||||
| `Filter.Select<V>` | `Int` | A select control, similar to HTML's `<select>`. Only one item can be selected, and the state is the index of the selected one. |
|
||||
| `Filter.Text` | `String` | A text control, similar to HTML's `<input type="text">`. |
|
||||
| `Filter.CheckBox` | `Boolean` | A checkbox control, similar to HTML's `<input type="checkbox">`. The state is `true` if it's checked. |
|
||||
| `Filter.TriState` | `Int` | A enhanced checkbox control that supports an excluding state. The state can be compared with `STATE_IGNORE`, `STATE_INCLUDE` and `STATE_EXCLUDE` constants of the class. |
|
||||
| `Filter.Group<V>` | `List<V>` | A group of filters (preferentially of the same type). The state will be a `List` with all the states. |
|
||||
| `Filter.Sort` | `Selection` | A control for sorting, with support for the ordering. The state indicates which item index is selected and if the sorting is `ascending`. |
|
||||
| `AnimeFilter.Header` | None | A simple header. Useful for separating sections in the list or showing any note or warning to the user. |
|
||||
| `AnimeFilter.Separator` | None | A line separator. Useful for visual distinction between sections. |
|
||||
| `AnimeFilter.Select<V>` | `Int` | A select control, similar to HTML's `<select>`. Only one item can be selected, and the state is the index of the selected one. |
|
||||
| `AnimeFilter.Text` | `String` | A text control, similar to HTML's `<input type="text">`. |
|
||||
| `AnimeFilter.CheckBox` | `Boolean` | A checkbox control, similar to HTML's `<input type="checkbox">`. The state is `true` if it's checked. |
|
||||
| `AnimeFilter.TriState` | `Int` | A enhanced checkbox control that supports an excluding state. The state can be compared with `STATE_IGNORE`, `STATE_INCLUDE` and `STATE_EXCLUDE` constants of the class. |
|
||||
| `AnimeFilter.Group<V>` | `List<V>` | A group of filters (preferentially of the same type). The state will be a `List` with all the states. |
|
||||
| `AnimeFilter.Sort` | `Selection` | A control for sorting, with support for the ordering. The state indicates which item index is selected and if the sorting is `ascending`. |
|
||||
|
||||
All control filters can have a default state set. It's usually recommended if the source have filters to make the initial state match the popular manga list, so when the user open the filter sheet, the state is equal and represents the current manga showing.
|
||||
All control filters can have a default state set. It's usually recommended if the source have filters to make the initial state match the popular anime list, so when the user open the filter sheet, the state is equal and represents the current anime showing.
|
||||
|
||||
The `Filter` classes can also be extended, so you can create new custom filters like the `UriPartFilter`:
|
||||
The `AnimeFilter` classes can also be extended, so you can create new custom filters like the `UriPartFilter`:
|
||||
|
||||
```kotlin
|
||||
open class UriPartFilter(displayName: String, private val vals: Array<Pair<String, String>>) :
|
||||
Filter.Select<String>(displayName, vals.map { it.first }.toTypedArray()) {
|
||||
AnimeFilter.Select<String>(displayName, vals.map { it.first }.toTypedArray()) {
|
||||
fun toUriPart() = vals[state].second
|
||||
}
|
||||
```
|
||||
@ -306,11 +367,11 @@ open class UriPartFilter(displayName: String, private val vals: Array<Pair<Strin
|
||||
- `fetchAnimeDetails` is called to update an anime's details from when it was initialized earlier.
|
||||
- `SAnime.initialized` tells the app if it should call `fetchAnimeDetails`. If you are overriding `fetchAnimeDetails`, make sure to pass it as `true`.
|
||||
- `SAnime.genre` is a string containing list of all genres separated with `", "`.
|
||||
- `SAnime.status` is an "enum" value. Refer to [the values in the `SAnime` companion object](https://github.com/aniyomiorg/extensions-lib/blob/a61fa402d3dcbb1402ce0cf252259cdc1b489b7e/library/src/main/java/eu/kanade/tachiyomi/animesource/model/SAnime.kt#L24-L27).
|
||||
- `SAnime.status` is an "enum" value. Refer to [the values in the `SAnime` companion object](https://github.com/aniyomiorg/extensions-lib/blob/main/library/src/main/java/eu/kanade/tachiyomi/animesource/model/SAnime.kt#L26-L32).
|
||||
- During a backup, only `url` and `title` are stored. To restore the rest of the anime data, the app calls `fetchAnimeDetails`, so all fields should be (re)filled in if possible.
|
||||
- If a `SAnime` is cached `fetchAnimeDetails` will be only called when the user does a manual update(Swipe-to-Refresh).
|
||||
- `fetchEpisodeList` is called to display the episode list.
|
||||
- The list should be sorted descending by the source order.
|
||||
- **The list should be sorted descending by the source order**.
|
||||
- If `Video.videoUrl`s are available immediately, you should pass them here. Otherwise, you should set `video.url` to a page that contains them and override `videoUrlParse` to fill those `videoUrl`s.
|
||||
|
||||
#### Episode
|
||||
@ -325,6 +386,7 @@ open class UriPartFilter(displayName: String, private val vals: Array<Pair<Strin
|
||||
return runCatching { DATE_FORMATTER.parse(dateStr)?.time }
|
||||
.getOrNull() ?: 0L
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val DATE_FORMATTER by lazy {
|
||||
SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.ENGLISH)
|
||||
@ -347,8 +409,8 @@ open class UriPartFilter(displayName: String, private val vals: Array<Pair<Strin
|
||||
### Misc notes
|
||||
|
||||
- Sometimes you may find no use for some inherited methods. If so just override them and throw exceptions: `throw UnsupportedOperationException("Not used.")`
|
||||
- You probably will find `getUrlWithoutDomain` useful when parsing the target source URLs.
|
||||
- If possible try to stick to the general workflow from `AnimeHttpSource`/`ParsedAnimeHttpSource`; breaking them may cause you more headache than necessary.
|
||||
- You probably will find `getUrlWithoutDomain` useful when parsing the target source URLs. Keep in mind there's a current issue with spaces in the URL though, so if you use it, replace all spaces with URL encoded characters (like `%20`).
|
||||
- If possible try to stick to the general workflow from `AnimeHttpSource`/`AnimeParsedHttpSource`; breaking them may cause you more headache than necessary.
|
||||
- By implementing `ConfigurableAnimeSource` you can add settings to your source, which is backed by [`SharedPreferences`](https://developer.android.com/reference/android/content/SharedPreferences).
|
||||
|
||||
### Advanced Extension features
|
||||
@ -356,7 +418,7 @@ open class UriPartFilter(displayName: String, private val vals: Array<Pair<Strin
|
||||
#### URL intent filter
|
||||
|
||||
Extensions can define URL intent filters by defining it inside a custom `AndroidManifest.xml` file.
|
||||
For an example, refer to [the NHentai module's `AndroidManifest.xml` file](https://github.com/aniyomiorg/aniyomi-extensions/blob/master/src/all/nhentai/AndroidManifest.xml) and [its corresponding `NHUrlActivity` handler](https://github.com/aniyomiorg/aniyomi-extensions/blob/master/src/all/nhentai/src/eu/kanade/tachiyomi/extension/all/nhentai/NHUrlActivity.kt).
|
||||
For an example, refer to [the AniWatch module's `AndroidManifest.xml` file](https://github.com/aniyomiorg/aniyomi-extensions/blob/master/src/en/aniwatch/AndroidManifest.xml) and [its corresponding `AniWatchUrlActivity` handler](https://github.com/aniyomiorg/aniyomi-extensions/blob/master/src/en/aniwatch/src/eu/kanade/tachiyomi/animeextension/en/zoro/AniWatchUrlActivity.kt).
|
||||
|
||||
To test if the URL intent filter is working as expected, you can try opening the website in a browser and navigating to the endpoint that was added as a filter or clicking a hyperlink. Alternatively, you can use the `adb` command below.
|
||||
|
||||
@ -428,12 +490,12 @@ multisrc
|
||||
├── AndroidManifest.xml
|
||||
└── java
|
||||
├── eu
|
||||
│ └── kanade
|
||||
│ └── tachiyomi
|
||||
│ └── multisrc
|
||||
│ └── <themepkg>
|
||||
│ ├── <ThemeName>Generator.kt
|
||||
│ └── <ThemeName>.kt
|
||||
│ └── kanade
|
||||
│ └── tachiyomi
|
||||
│ └── multisrc
|
||||
│ └── <themepkg>
|
||||
│ ├── <ThemeName>Generator.kt
|
||||
│ └── <ThemeName>.kt
|
||||
└── generator
|
||||
├── GeneratorMain.kt
|
||||
├── IntelijConfigurationGeneratorMain.kt
|
||||
@ -443,13 +505,18 @@ multisrc
|
||||
- `multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/<themepkg>/<Theme>.kt` defines the the theme's default implementation.
|
||||
- `multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/<theme>/<Theme>Generator.kt` defines the the theme's generator class, this is similar to a `AnimeSourceFactory` class.
|
||||
- `multisrc/overrides/<themepkg>/default/res` is the theme's default icons, if a source doesn't have overrides for `res`, then default icons will be used.
|
||||
- `multisrc/overrides/<themepkg>/default/additional.gradle.kts` defines additional gradle code, this will be copied at the end of all generated sources from this theme.
|
||||
- `multisrc/overrides/<themepkg>/default/additional.gradle` defines additional gradle code, this will be copied at the end of all generated sources from this theme.
|
||||
- `multisrc/overrides/<themepkg>/<sourcepkg>` contains overrides for a source that is defined inside the `<Theme>Generator.kt` class.
|
||||
- `multisrc/overrides/<themepkg>/<sourcepkg>/src` contains source overrides.
|
||||
- `multisrc/overrides/<themepkg>/<sourcepkg>/res` contains override for icons.
|
||||
- `multisrc/overrides/<themepkg>/<sourcepkg>/additional.gradle` defines additional gradle code, this will be copied at the end of the generated gradle file below the theme's `additional.gradle`.
|
||||
- `multisrc/overrides/<themepkg>/<sourcepkg>/AndroidManifest.xml` is copied as an override to the default `AndroidManifest.xml` generation if it exists.
|
||||
|
||||
> **Note**
|
||||
>
|
||||
> Files ending with `Gen.kt` (i.e. `multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/<theme>/XxxGen.kt`)
|
||||
> are considered helper files and won't be copied to generated sources.
|
||||
|
||||
### Development workflow
|
||||
There are three steps in running and testing a theme source:
|
||||
|
||||
@ -553,7 +620,7 @@ Inspecting the Logcat allows you to get a good look at the call flow and it's mo
|
||||
If you want to take a deeper look into the network flow, such as taking a look into the request and response bodies, you can use an external tool like `mitm-proxy`.
|
||||
|
||||
#### Setup your proxy server
|
||||
We are going to use [mitm-proxy](https://mitmproxy.org/) but you can replace it with any other Web Debugger (i.e. Charles, burp, Fiddler etc). To install and execute, follow the commands bellow.
|
||||
We are going to use [mitm-proxy](https://mitmproxy.org/) but you can replace it with any other Web Debugger (i.e. Charles, Burp Suite, Fiddler etc). To install and execute, follow the commands bellow.
|
||||
|
||||
```console
|
||||
Install the tool.
|
||||
@ -576,16 +643,29 @@ After installing and running, open your browser and navigate to http://127.0.0.1
|
||||
#### OkHttp proxy setup
|
||||
Since most of the manga sources are going to use HTTPS, we need to disable SSL verification in order to use the web debugger. For that, add this code to inside your source class:
|
||||
|
||||
|
||||
```kotlin
|
||||
class AnimeSource : MadTheme(
|
||||
package eu.kanade.tachiyomi.animeextension.en.animesource
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import eu.kanade.tachiyomi.multisrc.animetheme.AnimeTheme
|
||||
import okhttp3.OkHttpClient
|
||||
import java.net.InetSocketAddress
|
||||
import java.net.Proxy
|
||||
import java.security.SecureRandom
|
||||
import java.security.cert.X509Certificate
|
||||
import javax.net.ssl.SSLContext
|
||||
import javax.net.ssl.TrustManager
|
||||
import javax.net.ssl.X509TrustManager
|
||||
|
||||
class AnimeSource : AnimeTheme(
|
||||
"AnimeSource",
|
||||
"https://example.com",
|
||||
"en"
|
||||
) {
|
||||
private fun OkHttpClient.Builder.ignoreAllSSLErrors(): OkHttpClient.Builder {
|
||||
val naiveTrustManager = object : X509TrustManager {
|
||||
override fun getAcceptedIssuers(): Array<X509Certificate> = arrayOf()
|
||||
val naiveTrustManager = @SuppressLint("CustomX509TrustManager")
|
||||
object : X509TrustManager {
|
||||
override fun getAcceptedIssuers(): Array<X509Certificate> = emptyArray()
|
||||
override fun checkClientTrusted(certs: Array<X509Certificate>, authType: String) = Unit
|
||||
override fun checkServerTrusted(certs: Array<X509Certificate>, authType: String) = Unit
|
||||
}
|
||||
@ -596,15 +676,15 @@ class AnimeSource : MadTheme(
|
||||
}.socketFactory
|
||||
|
||||
sslSocketFactory(insecureSocketFactory, naiveTrustManager)
|
||||
hostnameVerifier(HostnameVerifier { _, _ -> true })
|
||||
hostnameVerifier { _, _ -> true }
|
||||
return this
|
||||
}
|
||||
|
||||
override val client: OkHttpClient = network.cloudflareClient.newBuilder()
|
||||
.ignoreAllSSLErrors()
|
||||
.proxy(Proxy(Proxy.Type.HTTP, InetSocketAddress("10.0.2.2", 8080)))
|
||||
....
|
||||
.build()
|
||||
}
|
||||
```
|
||||
|
||||
Note: `10.0.2.2` is usually the address of your loopback interface in the android emulator. If Aniyomi tells you that it's unable to connect to 10.0.2.2:8080 you will likely need to change it (the same if you are using hardware device).
|
||||
|
@ -35,40 +35,20 @@ dependencies {
|
||||
}
|
||||
|
||||
tasks {
|
||||
register("generateExtensions") {
|
||||
doLast {
|
||||
val isWindows = System.getProperty("os.name").toString().lowercase().contains("win")
|
||||
var classPath = (
|
||||
configurations.compileOnly.get().asFileTree.toList() +
|
||||
listOf(
|
||||
configurations.androidApis.get().asFileTree.first().absolutePath, // android.jar path
|
||||
"$projectDir/build/intermediates/aar_main_jar/debug/classes.jar", // jar made from this module
|
||||
)
|
||||
)
|
||||
.joinToString(if (isWindows) ";" else ":")
|
||||
register<JavaExec>("generateExtensions") {
|
||||
classpath = configurations.compileOnly.get() +
|
||||
configurations.androidApis.get() + // android.jar path
|
||||
files("$buildDir/intermediates/aar_main_jar/debug/classes.jar") // jar made from this module
|
||||
mainClass.set("generator.GeneratorMainKt")
|
||||
|
||||
var javaPath = "${System.getProperty("java.home")}/bin/java"
|
||||
workingDir = workingDir.parentFile // project root
|
||||
|
||||
val mainClass = "generator.GeneratorMainKt" // Main class we want to execute
|
||||
errorOutput = System.out // for GitHub workflow commands
|
||||
|
||||
if (isWindows) {
|
||||
classPath = classPath.replace("/", "\\")
|
||||
javaPath = javaPath.replace("/", "\\")
|
||||
}
|
||||
|
||||
val javaProcess = ProcessBuilder()
|
||||
.directory(null).command(javaPath, "-classpath", classPath, mainClass)
|
||||
.redirectErrorStream(true).start()
|
||||
|
||||
javaProcess.inputStream
|
||||
.bufferedReader()
|
||||
.forEachLine(logger::info)
|
||||
|
||||
val exitCode = javaProcess.waitFor()
|
||||
if (exitCode != 0) {
|
||||
throw Exception("Java process failed with exit code: $exitCode")
|
||||
}
|
||||
if (!logger.isInfoEnabled) {
|
||||
standardOutput = org.gradle.internal.io.NullOutputStream.INSTANCE
|
||||
}
|
||||
|
||||
dependsOn("ktFormat", "ktLint", "assembleDebug")
|
||||
}
|
||||
|
||||
|
@ -132,9 +132,9 @@ interface ThemeSourceGenerator {
|
||||
File(projectRootPath).let { projectRootFile ->
|
||||
println("Generating $source")
|
||||
|
||||
projectRootFile.mkdirs()
|
||||
// remove everything from past runs
|
||||
cleanDirectory(projectRootFile)
|
||||
projectRootFile.deleteRecursively()
|
||||
projectRootFile.mkdirs()
|
||||
|
||||
writeGradle(projectGradleFile, source, themePkg, baseVersionCode, defaultAdditionalGradlePath, additionalGradleOverridePath)
|
||||
writeAndroidManifest(projectAndroidManifestFile, manifestOverridePath, defaultAndroidManifestPath)
|
||||
@ -173,7 +173,7 @@ interface ThemeSourceGenerator {
|
||||
|
||||
File(themeSrcPath).walk()
|
||||
.map { it.toString().replace(themeSrcPath, "") }
|
||||
.filter { it.endsWith(".kt") && !it.endsWith("Generator.kt") }
|
||||
.filter { it.endsWith(".kt") && !it.endsWith("Generator.kt") && !it.endsWith("Gen.kt") }
|
||||
.forEach {
|
||||
File("$themeSrcPath/$it").copyTo(
|
||||
File("$themeDestPath/$it"),
|
||||
@ -239,15 +239,6 @@ interface ThemeSourceGenerator {
|
||||
""".trimMargin(),
|
||||
)
|
||||
}
|
||||
|
||||
private fun cleanDirectory(dir: File) {
|
||||
dir.listFiles()?.forEach {
|
||||
if (it.isDirectory) {
|
||||
cleanDirectory(it)
|
||||
}
|
||||
it.delete()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -16,6 +16,7 @@ pluginManagement {
|
||||
|
||||
include(":core")
|
||||
|
||||
// all the directories under /lib instead of manually adding each to a list
|
||||
File(rootDir, "lib").eachDir {
|
||||
val libName = it.name
|
||||
include(":lib-$libName")
|
||||
@ -36,13 +37,13 @@ if (System.getenv("CI") == null || System.getenv("CI_MODULE_GEN") == "true") {
|
||||
project(name).projectDir = File("src/${dir.name}/${subdir.name}")
|
||||
}
|
||||
}
|
||||
// Loads generated extensions from multisrc
|
||||
// Loads all generated extensions from multisrc
|
||||
File(rootDir, "generated-src").eachDir { dir ->
|
||||
dir.eachDir { subdir ->
|
||||
val name = ":extensions:multisrc:${dir.name}:${subdir.name}"
|
||||
include(name)
|
||||
project(name).projectDir = File("generated-src/${dir.name}/${subdir.name}")
|
||||
}
|
||||
dir.eachDir { subdir ->
|
||||
val name = ":extensions:multisrc:${dir.name}:${subdir.name}"
|
||||
include(name)
|
||||
project(name).projectDir = File("generated-src/${dir.name}/${subdir.name}")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -67,7 +68,7 @@ if (System.getenv("CI") == null || System.getenv("CI_MODULE_GEN") == "true") {
|
||||
include(":multisrc")
|
||||
project(":multisrc").projectDir = File("multisrc")
|
||||
|
||||
// Loads generated extensions from multisrc
|
||||
// Loads all generated extensions from multisrc
|
||||
File(rootDir, "generated-src").getChunk(chunk, chunkSize)?.forEach {
|
||||
val name = ":extensions:multisrc:${it.parentFile.name}:${it.name}"
|
||||
println(name)
|
||||
|
@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.animeextension.de.animestream
|
||||
import android.app.Application
|
||||
import android.content.SharedPreferences
|
||||
import android.os.Build
|
||||
import android.util.Base64
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.preference.ListPreference
|
||||
import androidx.preference.MultiSelectListPreference
|
||||
@ -24,7 +25,6 @@ import org.jsoup.nodes.Document
|
||||
import org.jsoup.nodes.Element
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import android.util.Base64
|
||||
import kotlin.Exception
|
||||
|
||||
class AnimeStream : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
||||
|
Loading…
x
Reference in New Issue
Block a user