mirror of
https://github.com/mihonapp/mihon.git
synced 2025-06-26 19:17:51 +02:00
Compare commits
38 Commits
Author | SHA1 | Date | |
---|---|---|---|
222e111806 | |||
8489b0dd8b | |||
88ed634978 | |||
32188f9f65 | |||
05efc4ebeb | |||
65bfa083f2 | |||
b8a9998bbd | |||
d736bec003 | |||
348b23a9fd | |||
121b2ec829 | |||
1dd130df9e | |||
e17d87f357 | |||
de75561402 | |||
58085336a5 | |||
89ea0a271b | |||
e3f33e24f5 | |||
9fd1419142 | |||
cb06898430 | |||
39407407f2 | |||
a024218410 | |||
26815c7356 | |||
e0deeb8008 | |||
38d6ab80ce | |||
78e66fd8d3 | |||
26aa126ecb | |||
e4a65656e7 | |||
6018aa99e2 | |||
99fd2731f5 | |||
e34043f1fe | |||
3c3a1cd448 | |||
0a2df21c5b | |||
277be02682 | |||
1849715418 | |||
dc6d4f9917 | |||
a9d98e5048 | |||
653940613d | |||
23a2d816e4 | |||
8a3a9146db |
1
.github/FUNDING.yml
vendored
1
.github/FUNDING.yml
vendored
@ -1 +0,0 @@
|
||||
ko_fi: inorichi
|
8
.github/ISSUE_TEMPLATE/config.yml
vendored
8
.github/ISSUE_TEMPLATE/config.yml
vendored
@ -1,11 +1,5 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: ⚠️ Extension/source issue
|
||||
url: https://github.com/tachiyomiorg/extensions/issues/new/choose
|
||||
about: Issues and requests for official extensions and sources should be opened in the extensions repository instead
|
||||
- name: 📦 Tachiyomi extensions
|
||||
url: https://mihon.app/extensions/
|
||||
about: List of all available extensions with download links
|
||||
- name: 🖥️ Tachiyomi website
|
||||
- name: 🖥️ Mihon website
|
||||
url: https://mihon.app/
|
||||
about: Guides, troubleshooting, and answers to common questions
|
||||
|
14
.github/ISSUE_TEMPLATE/report_issue.yml
vendored
14
.github/ISSUE_TEMPLATE/report_issue.yml
vendored
@ -1,5 +1,5 @@
|
||||
name: 🐞 Issue report
|
||||
description: Report an issue in Tachiyomi
|
||||
description: Report an issue in Mihon
|
||||
labels: [Bug]
|
||||
body:
|
||||
|
||||
@ -48,12 +48,12 @@ body:
|
||||
You can paste the crash logs in plain text or upload it as an attachment.
|
||||
|
||||
- type: input
|
||||
id: tachiyomi-version
|
||||
id: mihon-version
|
||||
attributes:
|
||||
label: Tachiyomi version
|
||||
description: You can find your Tachiyomi version in **More → About**.
|
||||
label: Mihon version
|
||||
description: You can find your Mihon version in **More → About**.
|
||||
placeholder: |
|
||||
Example: "0.15.3"
|
||||
Example: "0.16.1"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
@ -94,11 +94,9 @@ body:
|
||||
required: true
|
||||
- label: I have written a short but informative title.
|
||||
required: true
|
||||
- label: If this is an issue with an official extension, I should be opening an issue in the [extensions repository](https://github.com/tachiyomiorg/extensions/issues/new/choose).
|
||||
required: true
|
||||
- label: I have gone through the [FAQ](https://mihon.app/docs/faq/general) and [troubleshooting guide](https://mihon.app/docs/guides/troubleshooting/).
|
||||
required: true
|
||||
- label: I have updated the app to version **[0.15.3](https://github.com/mihonapp/mihon/releases/latest)**.
|
||||
- label: I have updated the app to version **[0.16.1](https://github.com/mihonapp/mihon/releases/latest)**.
|
||||
required: true
|
||||
- label: I have updated all installed extensions.
|
||||
required: true
|
||||
|
8
.github/ISSUE_TEMPLATE/request_feature.yml
vendored
8
.github/ISSUE_TEMPLATE/request_feature.yml
vendored
@ -1,5 +1,5 @@
|
||||
name: ⭐ Feature request
|
||||
description: Suggest a feature to improve Tachiyomi
|
||||
description: Suggest a feature to improve Mihon
|
||||
labels: [Feature request]
|
||||
body:
|
||||
|
||||
@ -7,7 +7,7 @@ body:
|
||||
id: feature-description
|
||||
attributes:
|
||||
label: Describe your suggested feature
|
||||
description: How can Tachiyomi be improved?
|
||||
description: How can Mihon be improved?
|
||||
placeholder: |
|
||||
Example:
|
||||
"It should work like this..."
|
||||
@ -31,9 +31,7 @@ body:
|
||||
required: true
|
||||
- label: I have written a short but informative title.
|
||||
required: true
|
||||
- label: If this is an issue with an official extension, I should be opening an issue in the [extensions repository](https://github.com/tachiyomiorg/extensions/issues/new/choose).
|
||||
required: true
|
||||
- label: I have updated the app to version **[0.15.3](https://github.com/mihonapp/mihon/releases/latest)**.
|
||||
- label: I have updated the app to version **[0.16.1](https://github.com/mihonapp/mihon/releases/latest)**.
|
||||
required: true
|
||||
- label: I will fill out all of the requested information in this form.
|
||||
required: true
|
||||
|
BIN
.github/assets/logo.png
vendored
Normal file
BIN
.github/assets/logo.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.5 KiB |
BIN
.github/readme-images/app-icon.png
vendored
BIN
.github/readme-images/app-icon.png
vendored
Binary file not shown.
Before Width: | Height: | Size: 1.1 KiB |
BIN
.idea/icon.png
generated
BIN
.idea/icon.png
generated
Binary file not shown.
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 26 KiB |
136
README.md
136
README.md
@ -1,89 +1,113 @@
|
||||
| Build | Stable | Weekly Preview | Support Server |
|
||||
|-------|----------|---------|---------|
|
||||
| [](https://github.com/mihonapp/mihon/actions/workflows/build_push.yml) | [](https://github.com/mihonapp/mihon/releases) | [](https://github.com/mihonapp/mihon-preview/releases) | [](https://discord.gg/mihon) |
|
||||
<div align="center">
|
||||
|
||||
# Mihon
|
||||
Mihon is a free and open source manga reader for Android 8.0 and above.
|
||||
<a href="https://mihon.app">
|
||||
<img src="./.github/assets/logo.png" alt="Mihon logo" title="Mihon logo" width="80"/>
|
||||
</a>
|
||||
|
||||
# Mihon [App](#)
|
||||
|
||||
### Full-featured reader
|
||||
Discover and read manga, webtoons, comics, and more – easier than ever on your Android device.
|
||||
|
||||
[](https://discord.gg/mihon)
|
||||
[](https://github.com/mihonapp/mihon/releases)
|
||||
|
||||
[](https://github.com/mihonapp/mihon/actions/workflows/build_push.yml)
|
||||
[](/LICENSE)
|
||||
[](https://hosted.weblate.org/engage/mihon/)
|
||||
|
||||
## Download
|
||||
|
||||
[](https://github.com/mihonapp/mihon/releases)
|
||||
[](https://github.com/mihonapp/mihon-preview/releases)
|
||||
|
||||
*Requires Android 8.0 or higher.*
|
||||
|
||||
## Features
|
||||
|
||||
Features include:
|
||||
* Online reading from a variety of sources
|
||||
* Local reading of downloaded content
|
||||
<div align="left">
|
||||
|
||||
* Local reading of content.
|
||||
* A configurable reader with multiple viewers, reading directions and other settings.
|
||||
* Tracker support: [MyAnimeList](https://myanimelist.net/), [AniList](https://anilist.co/), [Kitsu](https://kitsu.io/), [MangaUpdates](https://mangaupdates.com), [Shikimori](https://shikimori.one), and [Bangumi](https://bgm.tv/) support
|
||||
* Categories to organize your library
|
||||
* Light and dark themes
|
||||
* Schedule updating your library for new chapters
|
||||
* Create backups locally to read offline or to your desired cloud service
|
||||
* Tracker support: [MyAnimeList](https://myanimelist.net/), [AniList](https://anilist.co/), [Kitsu](https://kitsu.io/), [MangaUpdates](https://mangaupdates.com), [Shikimori](https://shikimori.one), and [Bangumi](https://bgm.tv/) support.
|
||||
* Categories to organize your library.
|
||||
* Light and dark themes.
|
||||
* Schedule updating your library for new chapters.
|
||||
* Create backups locally to read offline or to your desired cloud service.
|
||||
* Plus much more...
|
||||
|
||||
## Download
|
||||
Get the app from our [releases page](https://github.com/mihonapp/mihon/releases).
|
||||
</div>
|
||||
|
||||
If you want to try new features before they get to the stable release, you can download the preview version [here](https://github.com/mihonapp/mihon-preview/releases).
|
||||
## Contributing
|
||||
|
||||
## Issues, Feature Requests and Contributing
|
||||
[Code of conduct](./CODE_OF_CONDUCT.md) · [Contributing guide](./CONTRIBUTING.md)
|
||||
|
||||
Please make sure to read the full guidelines. Your issue may be closed without warning if you do not.
|
||||
Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
|
||||
|
||||
<details><summary>Issues</summary>
|
||||
If you got any questions, [join our Discord server](https://discord.gg/mihon).
|
||||
|
||||
1. **Before reporting a new issue, take a look at the [FAQ](https://mihon.app/docs/faq/general), the [changelog](https://mihon.app/changelogs/) and the already opened [issues](https://github.com/mihonapp/mihon/issues).**
|
||||
2. If you are unsure, ask here: [](https://discord.gg/mihon)
|
||||
<details align="center"><summary>Issues</summary><div align="left">
|
||||
|
||||
</details>
|
||||
Before reporting a new issue, take a look at the [FAQ](https://mihon.app/docs/faq/general), the [changelog](https://mihon.app/changelogs/) and the already opened [issues](https://github.com/mihonapp/mihon/issues).
|
||||
|
||||
<details><summary>Bugs</summary>
|
||||
</div></details>
|
||||
|
||||
* Include version (More → About → Version)
|
||||
* If not latest, try updating, it may have already been solved
|
||||
* Preview version is equal to the number of commits as seen on the main page
|
||||
* Include steps to reproduce (if not obvious from description)
|
||||
* Include screenshot (if needed)
|
||||
* If it could be device-dependent, try reproducing on another device (if possible)
|
||||
<details align="center"><summary>Bugs</summary><div align="left">
|
||||
|
||||
* Include version (**More → About → Version**).
|
||||
* If not latest, try updating, it may have already been solved.
|
||||
* Beta version is equal to the number of commits as seen on the main page.
|
||||
* Include steps to reproduce (if not obvious from description).
|
||||
* Include screenshot (if needed).
|
||||
* If it could be device-dependent, try reproducing on another device (if possible).
|
||||
* Don't group unrelated requests into one issue
|
||||
- **DO:** [#24](https://git.mihon.dev/tachiyomi/tachiyomi/issues/24), [#71](https://git.mihon.dev/tachiyomi/tachiyomi/issues/71)
|
||||
- **DON'T:** [#75](https://git.mihon.dev/tachiyomi/tachiyomi/issues/75)
|
||||
|
||||
</details>
|
||||
</div></details>
|
||||
|
||||
<details><summary>Feature Requests</summary>
|
||||
<details align="center"><summary>Feature requests</summary><div align="left">
|
||||
|
||||
* Write a detailed issue, explaining what it should do or how. Avoid writing just "like X app does"
|
||||
* Write a detailed issue, explaining what it should do or how.
|
||||
* Avoid writing just "like X app does";
|
||||
* Include screenshot (if needed)
|
||||
* Source requests are not accepted.
|
||||
|
||||
Source requests are not accepted.
|
||||
</details>
|
||||
</div></details>
|
||||
|
||||
<details><summary>Contributing</summary>
|
||||
### Repositories
|
||||
|
||||
See [CONTRIBUTING.md](./CONTRIBUTING.md).
|
||||
</details>
|
||||
[](https://github.com/mihonapp/website/)
|
||||
|
||||
<details><summary>Code of Conduct</summary>
|
||||
### Credits
|
||||
|
||||
See [CODE_OF_CONDUCT.md](./CODE_OF_CONDUCT.md).
|
||||
</details>
|
||||
Thank you to all the people who have contributed!
|
||||
|
||||
## FAQ
|
||||
<a href="https://github.com/mihonapp/mihon/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=mihonapp/mihon" alt="Mihon app contributors" title="Mihon app contributors" width="800"/>
|
||||
</a>
|
||||
|
||||
[See our website.](https://mihon.app/)
|
||||
You can also reach out to us on [Discord](https://discord.gg/mihon).
|
||||
### Disclaimer
|
||||
|
||||
## License
|
||||
The developer(s) of this application does not have any affiliation with the content providers available, and this application hosts zero content.
|
||||
|
||||
Copyright 2015 Javier Tomás
|
||||
### License
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
```
|
||||
Copyright © 2015 Javier Tomás
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
## Disclaimer
|
||||
Modifications Copyright © 2024 The Mihon Open Source Project
|
||||
```
|
||||
|
||||
The developer of this application does not have any affiliation with the content providers available.
|
||||
</div>
|
@ -22,8 +22,8 @@ android {
|
||||
defaultConfig {
|
||||
applicationId = "app.mihon"
|
||||
|
||||
versionCode = 1
|
||||
versionName = "0.16.0"
|
||||
versionCode = 3
|
||||
versionName = "0.16.2"
|
||||
|
||||
buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"")
|
||||
buildConfigField("String", "COMMIT_SHA", "\"${getGitSha()}\"")
|
||||
@ -139,15 +139,15 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":i18n"))
|
||||
implementation(project(":core"))
|
||||
implementation(project(":core-metadata"))
|
||||
implementation(project(":source-api"))
|
||||
implementation(project(":source-local"))
|
||||
implementation(project(":data"))
|
||||
implementation(project(":domain"))
|
||||
implementation(project(":presentation-core"))
|
||||
implementation(project(":presentation-widget"))
|
||||
implementation(projects.i18n)
|
||||
implementation(projects.core)
|
||||
implementation(projects.coreMetadata)
|
||||
implementation(projects.sourceApi)
|
||||
implementation(projects.sourceLocal)
|
||||
implementation(projects.data)
|
||||
implementation(projects.domain)
|
||||
implementation(projects.presentationCore)
|
||||
implementation(projects.presentationWidget)
|
||||
|
||||
// Compose
|
||||
implementation(platform(compose.bom))
|
||||
|
@ -3,18 +3,20 @@
|
||||
android:height="108dp"
|
||||
android:viewportWidth="432"
|
||||
android:viewportHeight="432">
|
||||
<group android:scaleX="0.67"
|
||||
android:scaleY="0.67"
|
||||
android:translateX="71.28"
|
||||
android:translateY="71.28">
|
||||
<group>
|
||||
<clip-path
|
||||
android:pathData="M0,0h432v432h-432z"/>
|
||||
<path
|
||||
android:pathData="M0,0h432v432h-432z"
|
||||
android:fillColor="#FAFAFA"/>
|
||||
<path
|
||||
android:pathData="M0,0h432v432h-432z"
|
||||
android:fillColor="#2E3943"/>
|
||||
<path
|
||||
android:pathData="M377,216C377,302.16 304.92,372 216,372C127.08,372 55,302.16 55,216C55,129.84 127.08,60 216,60C304.92,60 377,129.84 377,216Z"
|
||||
android:pathData="M322.13,215.5C322.13,272.66 274.64,319 216.07,319C157.49,319 110,272.66 110,215.5C110,158.34 157.49,112 216.07,112C274.64,112 322.13,158.34 322.13,215.5Z"
|
||||
android:fillColor="#F2FAFF"/>
|
||||
<path
|
||||
android:pathData="M216,342.75C288.25,342.75 346.81,286 346.81,216C346.81,146 288.25,89.25 216,89.25C143.75,89.25 85.19,146 85.19,216C85.19,286 143.75,342.75 216,342.75ZM216,372C304.92,372 377,302.16 377,216C377,129.84 304.92,60 216,60C127.08,60 55,129.84 55,216C55,302.16 127.08,372 216,372Z"
|
||||
android:pathData="M216.07,299.59C263.66,299.59 302.24,261.94 302.24,215.5C302.24,169.06 263.66,131.41 216.07,131.41C168.47,131.41 129.89,169.06 129.89,215.5C129.89,261.94 168.47,299.59 216.07,299.59ZM216.07,319C274.64,319 322.13,272.66 322.13,215.5C322.13,158.34 274.64,112 216.07,112C157.49,112 110,158.34 110,215.5C110,272.66 157.49,319 216.07,319Z"
|
||||
android:fillColor="#7EBBED"
|
||||
android:fillType="evenOdd"/>
|
||||
</group>
|
||||
|
@ -3,12 +3,7 @@
|
||||
android:height="108dp"
|
||||
android:viewportWidth="432"
|
||||
android:viewportHeight="432">
|
||||
<group android:scaleX="0.67"
|
||||
android:scaleY="0.67"
|
||||
android:translateX="71.28"
|
||||
android:translateY="71.28">
|
||||
<path
|
||||
android:pathData="M162.3,173.59L161.22,148.63C164.47,149.25 168.35,149.41 177.18,149.41C187.87,149.41 201.98,148.79 209.26,147.86C212.36,147.55 213.6,147.09 215.61,146L232.35,160.26C230.8,162.43 230.34,163.36 228.63,167.7C227.24,171.11 220.88,190.79 218.4,199.16C229.87,201.48 236.22,203.18 244.9,206.75C245.99,199.16 246.14,195.13 246.14,181.33C246.14,177.77 245.99,175.76 245.52,172.5L272.49,173.43C271.71,177.15 271.56,178.7 271.4,184.74C270.78,199.31 270.16,206.29 268.61,216.82C279.31,222.25 279.31,222.25 284.73,225.19C287.52,226.74 288.14,227.05 290,227.67L281.01,256.65C276.67,252.78 270.63,248.59 261.8,243.63C254.05,262.08 241.18,275.56 221.66,286.25C215.15,277.57 210.19,272.3 202.29,266.11C213.75,260.68 219.02,257.27 225.07,251.54C230.96,245.8 234.83,240.22 238.55,231.85C228.63,227.36 222.28,225.35 211.27,223.02C204.92,241.93 199.8,254.02 195.31,261.3C189.27,271.06 181.05,276.18 171.6,276.18C164.32,276.18 156.88,272.92 151.45,267.35C145.25,260.99 142,252.16 142,241.93C142,226.74 149.28,213.57 161.99,205.35C170.21,200.09 178.88,197.76 192.68,196.99C195.47,187.84 197.79,179.94 199.96,171.11C193.14,171.73 184.62,172.19 174.24,172.65C168.66,172.81 166.8,172.96 162.3,173.59ZM185.86,220.7C178.57,221.94 174.24,224.26 170.36,229.22C167.42,232.63 166.02,236.66 166.02,241C166.02,245.8 168.35,249.37 171.29,249.37C174.85,249.37 178.88,241.31 185.86,220.7Z"
|
||||
android:fillColor="#031019"/>
|
||||
</group>
|
||||
<path
|
||||
android:pathData="M182.03,188.7L181.33,172.69C183.42,173.09 185.91,173.19 191.57,173.19C198.44,173.19 207.49,172.79 212.16,172.19C214.15,171.99 214.95,171.7 216.24,171L226.98,180.15C225.98,181.54 225.68,182.14 224.59,184.92C223.7,187.11 219.62,199.74 218.03,205.11C225.39,206.6 229.46,207.7 235.03,209.98C235.73,205.11 235.83,202.52 235.83,193.67C235.83,191.39 235.73,190.09 235.43,188.01L252.74,188.6C252.24,190.99 252.14,191.98 252.04,195.86C251.64,205.21 251.24,209.68 250.25,216.45C257.11,219.93 257.11,219.93 260.59,221.82C262.38,222.81 262.78,223.01 263.97,223.41L258.2,242.01C255.42,239.52 251.54,236.83 245.87,233.65C240.9,245.49 232.65,254.14 220.12,261C215.94,255.43 212.76,252.05 207.68,248.07C215.04,244.59 218.43,242.4 222.3,238.72C226.08,235.04 228.57,231.46 230.96,226.09C224.59,223.21 220.51,221.92 213.45,220.43C209.38,232.56 206.09,240.32 203.21,244.99C199.33,251.25 194.06,254.54 187.99,254.54C183.32,254.54 178.55,252.45 175.07,248.87C171.09,244.79 169,239.12 169,232.56C169,222.81 173.67,214.36 181.83,209.09C187.1,205.71 192.67,204.21 201.52,203.72C203.31,197.85 204.8,192.78 206.19,187.11C201.82,187.51 196.35,187.81 189.68,188.1C186.1,188.2 184.91,188.3 182.03,188.7ZM197.14,218.93C192.47,219.73 189.68,221.22 187.2,224.4C185.31,226.59 184.41,229.18 184.41,231.96C184.41,235.04 185.91,237.33 187.8,237.33C190.08,237.33 192.67,232.16 197.14,218.93Z"
|
||||
android:fillColor="#031019"/>
|
||||
</vector>
|
||||
|
@ -163,7 +163,7 @@ class SyncChaptersWithSource(
|
||||
var updatedToAdd = newChapters.map { toAddItem ->
|
||||
var chapter = toAddItem.copy(dateFetch = nowMillis + itemCount--)
|
||||
|
||||
if (chapter.isRecognizedNumber.not() || chapter.chapterNumber !in deletedChapterNumbers) return@map chapter
|
||||
if (!chapter.isRecognizedNumber || chapter.chapterNumber !in deletedChapterNumbers) return@map chapter
|
||||
|
||||
chapter = chapter.copy(
|
||||
read = chapter.chapterNumber in deletedReadChapterNumbers,
|
||||
|
@ -23,7 +23,7 @@ class GetExtensionSources(
|
||||
ExtensionSourceItem(
|
||||
source = source,
|
||||
enabled = source.isEnabled(),
|
||||
labelAsName = isMultiSource && isMultiLangSingleSource.not(),
|
||||
labelAsName = isMultiSource && !isMultiLangSingleSource,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -22,9 +22,9 @@ class GetExtensionsByType(
|
||||
extensionManager.availableExtensionsFlow,
|
||||
) { _activeLanguages, _installed, _untrusted, _available ->
|
||||
val (updates, installed) = _installed
|
||||
.filter { (showNsfwSources || it.isNsfw.not()) }
|
||||
.filter { (showNsfwSources || !it.isNsfw) }
|
||||
.sortedWith(
|
||||
compareBy<Extension.Installed> { it.isObsolete.not() }
|
||||
compareBy<Extension.Installed> { !it.isObsolete }
|
||||
.thenBy(String.CASE_INSENSITIVE_ORDER) { it.name },
|
||||
)
|
||||
.partition { it.hasUpdate }
|
||||
@ -36,7 +36,7 @@ class GetExtensionsByType(
|
||||
.filter { extension ->
|
||||
_installed.none { it.pkgName == extension.pkgName } &&
|
||||
_untrusted.none { it.pkgName == extension.pkgName } &&
|
||||
(showNsfwSources || extension.isNsfw.not())
|
||||
(showNsfwSources || !extension.isNsfw)
|
||||
}
|
||||
.flatMap { ext ->
|
||||
if (ext.sources.isEmpty()) {
|
||||
|
@ -34,15 +34,15 @@ class GetSourcesWithFavoriteCount(
|
||||
when (sorting) {
|
||||
SetMigrateSorting.Mode.ALPHABETICAL -> {
|
||||
when {
|
||||
a.first.isStub && b.first.isStub.not() -> -1
|
||||
b.first.isStub && a.first.isStub.not() -> 1
|
||||
a.first.isStub && !b.first.isStub -> -1
|
||||
b.first.isStub && !a.first.isStub -> 1
|
||||
else -> a.first.name.lowercase().compareToWithCollator(b.first.name.lowercase())
|
||||
}
|
||||
}
|
||||
SetMigrateSorting.Mode.TOTAL -> {
|
||||
when {
|
||||
a.first.isStub && b.first.isStub.not() -> -1
|
||||
b.first.isStub && a.first.isStub.not() -> 1
|
||||
a.first.isStub && !b.first.isStub -> -1
|
||||
b.first.isStub && !a.first.isStub -> 1
|
||||
else -> a.second.compareTo(b.second)
|
||||
}
|
||||
}
|
||||
|
@ -30,9 +30,9 @@ class RefreshTracks(
|
||||
.map { (track, service) ->
|
||||
async {
|
||||
return@async try {
|
||||
val updatedTrack = service!!.refresh(track.toDbTrack())
|
||||
insertTrack.await(updatedTrack.toDomainTrack()!!)
|
||||
syncChapterProgressWithTrack.await(mangaId, track, service)
|
||||
val updatedTrack = service!!.refresh(track.toDbTrack()).toDomainTrack()!!
|
||||
insertTrack.await(updatedTrack)
|
||||
syncChapterProgressWithTrack.await(mangaId, updatedTrack, service)
|
||||
null
|
||||
} catch (e: Throwable) {
|
||||
service to e
|
||||
|
@ -19,30 +19,28 @@ fun Track.toDbTrack(): DbTrack = DbTrack.create(trackerId).also {
|
||||
it.remote_id = remoteId
|
||||
it.library_id = libraryId
|
||||
it.title = title
|
||||
it.last_chapter_read = lastChapterRead.toFloat()
|
||||
it.total_chapters = totalChapters.toInt()
|
||||
it.status = status.toInt()
|
||||
it.score = score.toFloat()
|
||||
it.last_chapter_read = lastChapterRead
|
||||
it.total_chapters = totalChapters
|
||||
it.status = status
|
||||
it.score = score
|
||||
it.tracking_url = remoteUrl
|
||||
it.started_reading_date = startDate
|
||||
it.finished_reading_date = finishDate
|
||||
}
|
||||
|
||||
fun DbTrack.toDomainTrack(idRequired: Boolean = true): Track? {
|
||||
val trackId = id ?: if (idRequired.not()) -1 else return null
|
||||
val trackId = id ?: if (!idRequired) -1 else return null
|
||||
return Track(
|
||||
id = trackId,
|
||||
mangaId = manga_id,
|
||||
trackerId = tracker_id.toLong(),
|
||||
trackerId = tracker_id,
|
||||
remoteId = remote_id,
|
||||
libraryId = library_id,
|
||||
title = title,
|
||||
lastChapterRead = last_chapter_read.toDouble(),
|
||||
totalChapters = total_chapters.toLong(),
|
||||
status = status.toLong(),
|
||||
// Jank workaround due to precision issues while converting
|
||||
// See https://github.com/tachiyomiorg/tachiyomi/issues/10343
|
||||
score = score.toString().toDouble(),
|
||||
lastChapterRead = last_chapter_read,
|
||||
totalChapters = total_chapters,
|
||||
status = status,
|
||||
score = score,
|
||||
remoteUrl = tracking_url,
|
||||
startDate = started_reading_date,
|
||||
finishDate = finished_reading_date,
|
||||
|
@ -19,9 +19,15 @@ class TrackPreferences(
|
||||
"",
|
||||
)
|
||||
|
||||
fun trackAuthExpired(tracker: Tracker) = preferenceStore.getBoolean(
|
||||
Preference.privateKey("pref_tracker_auth_expired_${tracker.id}"),
|
||||
false,
|
||||
)
|
||||
|
||||
fun setCredentials(tracker: Tracker, username: String, password: String) {
|
||||
trackUsername(tracker).set(username)
|
||||
trackPassword(tracker).set(password)
|
||||
trackAuthExpired(tracker).set(false)
|
||||
}
|
||||
|
||||
fun trackToken(tracker: Tracker) = preferenceStore.getString(Preference.privateKey("track_token_${tracker.id}"), "")
|
||||
|
@ -203,7 +203,13 @@ private fun ExtensionContent(
|
||||
items(
|
||||
items = items,
|
||||
contentType = { "item" },
|
||||
key = { "extension-${it.hashCode()}" },
|
||||
key = { item ->
|
||||
when (item.extension) {
|
||||
is Extension.Untrusted -> "extension-untrusted-${item.hashCode()}"
|
||||
is Extension.Installed -> "extension-installed-${item.hashCode()}"
|
||||
is Extension.Available -> "extension-available-${item.hashCode()}"
|
||||
}
|
||||
},
|
||||
) { item ->
|
||||
ExtensionItem(
|
||||
modifier = Modifier.animateItemPlacement(),
|
||||
|
@ -7,8 +7,11 @@ import android.net.Uri
|
||||
import androidx.activity.compose.ManagedActivityResultLauncher
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.foundation.layout.IntrinsicSize
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.outlined.HelpOutline
|
||||
@ -189,9 +192,11 @@ object SettingsDataScreen : SearchableSettings {
|
||||
MultiChoiceSegmentedButtonRow(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(intrinsicSize = IntrinsicSize.Min)
|
||||
.padding(horizontal = PrefsHorizontalPadding),
|
||||
) {
|
||||
SegmentedButton(
|
||||
modifier = Modifier.fillMaxHeight(),
|
||||
checked = false,
|
||||
onCheckedChange = { navigator.push(CreateBackupScreen()) },
|
||||
shape = SegmentedButtonDefaults.itemShape(0, 2),
|
||||
@ -199,6 +204,7 @@ object SettingsDataScreen : SearchableSettings {
|
||||
Text(stringResource(MR.strings.pref_create_backup))
|
||||
}
|
||||
SegmentedButton(
|
||||
modifier = Modifier.fillMaxHeight(),
|
||||
checked = false,
|
||||
onCheckedChange = {
|
||||
if (!BackupRestoreJob.isRunning(context)) {
|
||||
|
@ -247,7 +247,7 @@ object AboutScreen : Screen() {
|
||||
}
|
||||
}
|
||||
BuildConfig.PREVIEW -> {
|
||||
"Preview r${BuildConfig.COMMIT_COUNT}".let {
|
||||
"Beta r${BuildConfig.COMMIT_COUNT}".let {
|
||||
if (withBuildDate) {
|
||||
"$it (${BuildConfig.COMMIT_SHA}, ${getFormattedBuildTime()})"
|
||||
} else {
|
||||
|
@ -11,6 +11,7 @@ import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
@Stable
|
||||
class DisplayRefreshHost {
|
||||
@ -30,15 +31,15 @@ fun DisplayRefreshHost(
|
||||
val currentDisplayRefresh = hostState.currentDisplayRefresh
|
||||
LaunchedEffect(currentDisplayRefresh) {
|
||||
if (currentDisplayRefresh) {
|
||||
delay(1500)
|
||||
delay(1.5.seconds)
|
||||
hostState.currentDisplayRefresh = false
|
||||
}
|
||||
}
|
||||
|
||||
if (currentDisplayRefresh) {
|
||||
Canvas(
|
||||
modifier = modifier.fillMaxSize(),
|
||||
) {
|
||||
Canvas(
|
||||
modifier = modifier.fillMaxSize(),
|
||||
) {
|
||||
if (currentDisplayRefresh) {
|
||||
drawRect(Color.Black)
|
||||
}
|
||||
}
|
||||
|
@ -88,7 +88,7 @@ fun TrackInfoDialogHome(
|
||||
TrackInfoItem(
|
||||
title = item.track.title,
|
||||
tracker = item.tracker,
|
||||
status = item.tracker.getStatus(item.track.status.toInt()),
|
||||
status = item.tracker.getStatus(item.track.status),
|
||||
onStatusClick = { onStatusClick(item) },
|
||||
chapters = "${item.track.lastChapterRead.toInt()}".let {
|
||||
val totalChapters = item.track.totalChapters
|
||||
|
@ -48,9 +48,9 @@ import tachiyomi.presentation.core.util.isScrolledToStart
|
||||
|
||||
@Composable
|
||||
fun TrackStatusSelector(
|
||||
selection: Int,
|
||||
onSelectionChange: (Int) -> Unit,
|
||||
selections: Map<Int, StringResource?>,
|
||||
selection: Long,
|
||||
onSelectionChange: (Long) -> Unit,
|
||||
selections: Map<Long, StringResource?>,
|
||||
onConfirm: () -> Unit,
|
||||
onDismissRequest: () -> Unit,
|
||||
) {
|
||||
@ -236,12 +236,12 @@ private fun TrackStatusSelectorPreviews() {
|
||||
onSelectionChange = {},
|
||||
selections = persistentMapOf(
|
||||
// Anilist values
|
||||
1 to MR.strings.reading,
|
||||
2 to MR.strings.plan_to_read,
|
||||
3 to MR.strings.completed,
|
||||
4 to MR.strings.on_hold,
|
||||
5 to MR.strings.dropped,
|
||||
6 to MR.strings.repeating,
|
||||
1L to MR.strings.reading,
|
||||
2L to MR.strings.plan_to_read,
|
||||
3L to MR.strings.completed,
|
||||
4L to MR.strings.on_hold,
|
||||
5L to MR.strings.dropped,
|
||||
6L to MR.strings.repeating,
|
||||
),
|
||||
onConfirm = {},
|
||||
onDismissRequest = {},
|
||||
|
@ -301,7 +301,7 @@ private fun SearchResultItem(
|
||||
text = status,
|
||||
)
|
||||
}
|
||||
if (trackSearch.score != -1f) {
|
||||
if (trackSearch.score != -1.0) {
|
||||
SearchResultItemDetails(
|
||||
title = stringResource(MR.strings.score),
|
||||
text = trackSearch.score.toString(),
|
||||
|
@ -62,14 +62,14 @@ internal class TrackerSearchPreviewProvider : PreviewParameterProvider<@Composab
|
||||
private fun randTrackSearch() = TrackSearch().let {
|
||||
it.id = Random.nextLong()
|
||||
it.manga_id = Random.nextLong()
|
||||
it.tracker_id = Random.nextInt()
|
||||
it.tracker_id = Random.nextLong()
|
||||
it.remote_id = Random.nextLong()
|
||||
it.library_id = Random.nextLong()
|
||||
it.title = lorem((1..10).random()).joinToString()
|
||||
it.last_chapter_read = (0..100).random().toFloat()
|
||||
it.total_chapters = (100..1000).random()
|
||||
it.score = (0..10).random().toFloat()
|
||||
it.status = Random.nextInt()
|
||||
it.last_chapter_read = (0..100).random().toDouble()
|
||||
it.total_chapters = (100L..1000L).random()
|
||||
it.score = (0..10).random().toDouble()
|
||||
it.status = Random.nextLong()
|
||||
it.started_reading_date = 0L
|
||||
it.finished_reading_date = 0L
|
||||
it.tracking_url = "https://example.com/tracker-example"
|
||||
|
@ -30,6 +30,10 @@ class MangaBackupCreator(
|
||||
// Entry for this manga
|
||||
val mangaObject = manga.toBackupManga()
|
||||
|
||||
mangaObject.excludedScanlators = handler.awaitList {
|
||||
excluded_scanlatorsQueries.getExcludedScanlatorsByMangaId(manga.id)
|
||||
}
|
||||
|
||||
if (options.chapters) {
|
||||
// Backup all the chapters
|
||||
handler.awaitList {
|
||||
|
@ -38,6 +38,7 @@ data class BackupManga(
|
||||
@ProtoNumber(105) var updateStrategy: UpdateStrategy = UpdateStrategy.ALWAYS_UPDATE,
|
||||
@ProtoNumber(106) var lastModifiedAt: Long = 0,
|
||||
@ProtoNumber(107) var favoriteModifiedAt: Long? = null,
|
||||
@ProtoNumber(108) var excludedScanlators: List<String> = emptyList(),
|
||||
) {
|
||||
fun getMangaImpl(): Manga {
|
||||
return Manga.create().copy(
|
||||
|
@ -73,6 +73,7 @@ class MangaRestorer(
|
||||
backupCategories = backupCategories,
|
||||
history = backupManga.history + backupManga.brokenHistory.map { it.toBackupHistory() },
|
||||
tracks = backupManga.tracking,
|
||||
excludedScanlators = backupManga.excludedScanlators,
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -264,11 +265,13 @@ class MangaRestorer(
|
||||
backupCategories: List<BackupCategory>,
|
||||
history: List<BackupHistory>,
|
||||
tracks: List<BackupTracking>,
|
||||
excludedScanlators: List<String>,
|
||||
): Manga {
|
||||
restoreCategories(manga, categories, backupCategories)
|
||||
restoreChapters(manga, chapters)
|
||||
restoreTracking(manga, tracks)
|
||||
restoreHistory(history)
|
||||
restoreExcludedScanlators(manga, excludedScanlators)
|
||||
updateManga.awaitUpdateFetchInterval(manga, now, currentFetchWindow)
|
||||
return manga
|
||||
}
|
||||
@ -401,4 +404,25 @@ class MangaRestorer(
|
||||
}
|
||||
|
||||
private fun Track.forComparison() = this.copy(id = 0L, mangaId = 0L)
|
||||
|
||||
/**
|
||||
* Restores the excluded scanlators for the manga.
|
||||
*
|
||||
* @param manga the manga whose excluded scanlators have to be restored.
|
||||
* @param excludedScanlators the excluded scanlators to restore.
|
||||
*/
|
||||
private suspend fun restoreExcludedScanlators(manga: Manga, excludedScanlators: List<String>) {
|
||||
if (excludedScanlators.isEmpty()) return
|
||||
val existingExcludedScanlators = handler.awaitList {
|
||||
excluded_scanlatorsQueries.getExcludedScanlatorsByMangaId(manga.id)
|
||||
}
|
||||
val toInsert = excludedScanlators.filter { it !in existingExcludedScanlators }
|
||||
if (toInsert.isNotEmpty()) {
|
||||
handler.await {
|
||||
toInsert.forEach {
|
||||
excluded_scanlatorsQueries.insert(manga.id, it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ interface Track : Serializable {
|
||||
|
||||
var manga_id: Long
|
||||
|
||||
var tracker_id: Int
|
||||
var tracker_id: Long
|
||||
|
||||
var remote_id: Long
|
||||
|
||||
@ -16,13 +16,13 @@ interface Track : Serializable {
|
||||
|
||||
var title: String
|
||||
|
||||
var last_chapter_read: Float
|
||||
var last_chapter_read: Double
|
||||
|
||||
var total_chapters: Int
|
||||
var total_chapters: Long
|
||||
|
||||
var score: Float
|
||||
var score: Double
|
||||
|
||||
var status: Int
|
||||
var status: Long
|
||||
|
||||
var started_reading_date: Long
|
||||
|
||||
@ -40,7 +40,7 @@ interface Track : Serializable {
|
||||
|
||||
companion object {
|
||||
fun create(serviceId: Long): Track = TrackImpl().apply {
|
||||
tracker_id = serviceId.toInt()
|
||||
tracker_id = serviceId
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ class TrackImpl : Track {
|
||||
|
||||
override var manga_id: Long = 0
|
||||
|
||||
override var tracker_id: Int = 0
|
||||
override var tracker_id: Long = 0
|
||||
|
||||
override var remote_id: Long = 0
|
||||
|
||||
@ -14,13 +14,13 @@ class TrackImpl : Track {
|
||||
|
||||
override lateinit var title: String
|
||||
|
||||
override var last_chapter_read: Float = 0F
|
||||
override var last_chapter_read: Double = 0.0
|
||||
|
||||
override var total_chapters: Int = 0
|
||||
override var total_chapters: Long = 0
|
||||
|
||||
override var score: Float = 0f
|
||||
override var score: Double = 0.0
|
||||
|
||||
override var status: Int = 0
|
||||
override var status: Long = 0
|
||||
|
||||
override var started_reading_date: Long = 0
|
||||
|
||||
|
@ -315,13 +315,13 @@ class DownloadManager(
|
||||
val capitalizationChanged = oldFolder.name.equals(newName, ignoreCase = true)
|
||||
if (capitalizationChanged) {
|
||||
val tempName = newName + Downloader.TMP_DIR_SUFFIX
|
||||
if (oldFolder.renameTo(tempName).not()) {
|
||||
if (!oldFolder.renameTo(tempName)) {
|
||||
logcat(LogPriority.ERROR) { "Failed to rename source download folder: ${oldFolder.name}" }
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if (oldFolder.renameTo(newName).not()) {
|
||||
if (!oldFolder.renameTo(newName)) {
|
||||
logcat(LogPriority.ERROR) { "Failed to rename source download folder: ${oldFolder.name}" }
|
||||
}
|
||||
}
|
||||
|
@ -130,7 +130,7 @@ class DownloadProvider(
|
||||
val newChapterName = sanitizeChapterName(chapterName)
|
||||
return DiskUtil.buildValidFilename(
|
||||
when {
|
||||
chapterScanlator.isNullOrBlank().not() -> "${chapterScanlator}_$newChapterName"
|
||||
!chapterScanlator.isNullOrBlank() -> "${chapterScanlator}_$newChapterName"
|
||||
else -> newChapterName
|
||||
},
|
||||
)
|
||||
|
@ -40,8 +40,8 @@ abstract class BaseTracker(
|
||||
return track.score
|
||||
}
|
||||
|
||||
override fun indexToScore(index: Int): Float {
|
||||
return index.toFloat()
|
||||
override fun indexToScore(index: Int): Double {
|
||||
return index.toDouble()
|
||||
}
|
||||
|
||||
@CallSuper
|
||||
@ -70,24 +70,24 @@ abstract class BaseTracker(
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun setRemoteStatus(track: Track, status: Int) {
|
||||
override suspend fun setRemoteStatus(track: Track, status: Long) {
|
||||
track.status = status
|
||||
if (track.status == getCompletionStatus() && track.total_chapters != 0) {
|
||||
track.last_chapter_read = track.total_chapters.toFloat()
|
||||
if (track.status == getCompletionStatus() && track.total_chapters != 0L) {
|
||||
track.last_chapter_read = track.total_chapters.toDouble()
|
||||
}
|
||||
updateRemote(track)
|
||||
}
|
||||
|
||||
override suspend fun setRemoteLastChapterRead(track: Track, chapterNumber: Int) {
|
||||
if (
|
||||
track.last_chapter_read == 0f &&
|
||||
track.last_chapter_read == 0.0 &&
|
||||
track.last_chapter_read < chapterNumber &&
|
||||
track.status != getRereadingStatus()
|
||||
) {
|
||||
track.status = getReadingStatus()
|
||||
}
|
||||
track.last_chapter_read = chapterNumber.toFloat()
|
||||
if (track.total_chapters != 0 && track.last_chapter_read.toInt() == track.total_chapters) {
|
||||
track.last_chapter_read = chapterNumber.toDouble()
|
||||
if (track.total_chapters != 0L && track.last_chapter_read.toLong() == track.total_chapters) {
|
||||
track.status = getCompletionStatus()
|
||||
track.finished_reading_date = System.currentTimeMillis()
|
||||
}
|
||||
|
@ -27,22 +27,22 @@ interface Tracker {
|
||||
@DrawableRes
|
||||
fun getLogo(): Int
|
||||
|
||||
fun getStatusList(): List<Int>
|
||||
fun getStatusList(): List<Long>
|
||||
|
||||
fun getStatus(status: Int): StringResource?
|
||||
fun getStatus(status: Long): StringResource?
|
||||
|
||||
fun getReadingStatus(): Int
|
||||
fun getReadingStatus(): Long
|
||||
|
||||
fun getRereadingStatus(): Int
|
||||
fun getRereadingStatus(): Long
|
||||
|
||||
fun getCompletionStatus(): Int
|
||||
fun getCompletionStatus(): Long
|
||||
|
||||
fun getScoreList(): ImmutableList<String>
|
||||
|
||||
// TODO: Store all scores as 10 point in the future maybe?
|
||||
fun get10PointScore(track: DomainTrack): Double
|
||||
|
||||
fun indexToScore(index: Int): Float
|
||||
fun indexToScore(index: Int): Double
|
||||
|
||||
fun displayScore(track: DomainTrack): String
|
||||
|
||||
@ -70,7 +70,7 @@ interface Tracker {
|
||||
// TODO: move this to an interactor, and update all trackers based on common data
|
||||
suspend fun register(item: Track, mangaId: Long)
|
||||
|
||||
suspend fun setRemoteStatus(track: Track, status: Int)
|
||||
suspend fun setRemoteStatus(track: Track, status: Long)
|
||||
|
||||
suspend fun setRemoteLastChapterRead(track: Track, chapterNumber: Int)
|
||||
|
||||
|
@ -20,12 +20,12 @@ import tachiyomi.domain.track.model.Track as DomainTrack
|
||||
class Anilist(id: Long) : BaseTracker(id, "AniList"), DeletableTracker {
|
||||
|
||||
companion object {
|
||||
const val READING = 1
|
||||
const val COMPLETED = 2
|
||||
const val ON_HOLD = 3
|
||||
const val DROPPED = 4
|
||||
const val PLAN_TO_READ = 5
|
||||
const val REREADING = 6
|
||||
const val READING = 1L
|
||||
const val COMPLETED = 2L
|
||||
const val ON_HOLD = 3L
|
||||
const val DROPPED = 4L
|
||||
const val PLAN_TO_READ = 5L
|
||||
const val REREADING = 6L
|
||||
|
||||
const val POINT_100 = "POINT_100"
|
||||
const val POINT_10 = "POINT_10"
|
||||
@ -58,11 +58,11 @@ class Anilist(id: Long) : BaseTracker(id, "AniList"), DeletableTracker {
|
||||
|
||||
override fun getLogoColor() = Color.rgb(18, 25, 35)
|
||||
|
||||
override fun getStatusList(): List<Int> {
|
||||
override fun getStatusList(): List<Long> {
|
||||
return listOf(READING, COMPLETED, ON_HOLD, DROPPED, PLAN_TO_READ, REREADING)
|
||||
}
|
||||
|
||||
override fun getStatus(status: Int): StringResource? = when (status) {
|
||||
override fun getStatus(status: Long): StringResource? = when (status) {
|
||||
READING -> MR.strings.reading
|
||||
PLAN_TO_READ -> MR.strings.plan_to_read
|
||||
COMPLETED -> MR.strings.completed
|
||||
@ -72,11 +72,11 @@ class Anilist(id: Long) : BaseTracker(id, "AniList"), DeletableTracker {
|
||||
else -> null
|
||||
}
|
||||
|
||||
override fun getReadingStatus(): Int = READING
|
||||
override fun getReadingStatus(): Long = READING
|
||||
|
||||
override fun getRereadingStatus(): Int = REREADING
|
||||
override fun getRereadingStatus(): Long = REREADING
|
||||
|
||||
override fun getCompletionStatus(): Int = COMPLETED
|
||||
override fun getCompletionStatus(): Long = COMPLETED
|
||||
|
||||
override fun getScoreList(): ImmutableList<String> {
|
||||
return when (scorePreference.get()) {
|
||||
@ -99,24 +99,24 @@ class Anilist(id: Long) : BaseTracker(id, "AniList"), DeletableTracker {
|
||||
return track.score / 10.0
|
||||
}
|
||||
|
||||
override fun indexToScore(index: Int): Float {
|
||||
override fun indexToScore(index: Int): Double {
|
||||
return when (scorePreference.get()) {
|
||||
// 10 point
|
||||
POINT_10 -> index * 10f
|
||||
POINT_10 -> index * 10.0
|
||||
// 100 point
|
||||
POINT_100 -> index.toFloat()
|
||||
POINT_100 -> index.toDouble()
|
||||
// 5 stars
|
||||
POINT_5 -> when (index) {
|
||||
0 -> 0f
|
||||
else -> index * 20f - 10f
|
||||
0 -> 0.0
|
||||
else -> index * 20.0 - 10.0
|
||||
}
|
||||
// Smiley
|
||||
POINT_3 -> when (index) {
|
||||
0 -> 0f
|
||||
else -> index * 25f + 10f
|
||||
0 -> 0.0
|
||||
else -> index * 25.0 + 10.0
|
||||
}
|
||||
// 10 point decimal
|
||||
POINT_10_DECIMAL -> index.toFloat()
|
||||
POINT_10_DECIMAL -> index.toDouble()
|
||||
else -> throw Exception("Unknown score type")
|
||||
}
|
||||
}
|
||||
@ -153,12 +153,12 @@ class Anilist(id: Long) : BaseTracker(id, "AniList"), DeletableTracker {
|
||||
|
||||
if (track.status != COMPLETED) {
|
||||
if (didReadChapter) {
|
||||
if (track.last_chapter_read.toInt() == track.total_chapters && track.total_chapters > 0) {
|
||||
if (track.last_chapter_read.toLong() == track.total_chapters && track.total_chapters > 0) {
|
||||
track.status = COMPLETED
|
||||
track.finished_reading_date = System.currentTimeMillis()
|
||||
} else if (track.status != REREADING) {
|
||||
track.status = READING
|
||||
if (track.last_chapter_read == 1F) {
|
||||
if (track.last_chapter_read == 1.0) {
|
||||
track.started_reading_date = System.currentTimeMillis()
|
||||
}
|
||||
}
|
||||
@ -185,14 +185,14 @@ class Anilist(id: Long) : BaseTracker(id, "AniList"), DeletableTracker {
|
||||
|
||||
if (track.status != COMPLETED) {
|
||||
val isRereading = track.status == REREADING
|
||||
track.status = if (isRereading.not() && hasReadChapters) READING else track.status
|
||||
track.status = if (!isRereading && hasReadChapters) READING else track.status
|
||||
}
|
||||
|
||||
update(track)
|
||||
} else {
|
||||
// Set default fields if it's not found in the list
|
||||
track.status = if (hasReadChapters) READING else PLAN_TO_READ
|
||||
track.score = 0F
|
||||
track.score = 0.0
|
||||
add(track)
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ import kotlinx.serialization.json.jsonArray
|
||||
import kotlinx.serialization.json.jsonObject
|
||||
import kotlinx.serialization.json.jsonPrimitive
|
||||
import kotlinx.serialization.json.long
|
||||
import kotlinx.serialization.json.longOrNull
|
||||
import kotlinx.serialization.json.put
|
||||
import kotlinx.serialization.json.putJsonObject
|
||||
import okhttp3.OkHttpClient
|
||||
@ -312,7 +313,7 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
|
||||
struct["format"]!!.jsonPrimitive.content.replace("_", "-"),
|
||||
struct["status"]!!.jsonPrimitive.contentOrNull ?: "",
|
||||
parseDate(struct, "startDate"),
|
||||
struct["chapters"]!!.jsonPrimitive.intOrNull ?: 0,
|
||||
struct["chapters"]!!.jsonPrimitive.longOrNull ?: 0,
|
||||
struct["averageScore"]?.jsonPrimitive?.intOrNull ?: -1,
|
||||
)
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package eu.kanade.tachiyomi.data.track.anilist
|
||||
|
||||
import eu.kanade.tachiyomi.BuildConfig
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.Response
|
||||
import java.io.IOException
|
||||
@ -40,6 +41,7 @@ class AnilistInterceptor(val anilist: Anilist, private var token: String?) : Int
|
||||
// Add the authorization header to the original request.
|
||||
val authRequest = originalRequest.newBuilder()
|
||||
.addHeader("Authorization", "Bearer ${oauth!!.access_token}")
|
||||
.header("User-Agent", "Mihon v${BuildConfig.VERSION_NAME} (${BuildConfig.APPLICATION_ID})")
|
||||
.build()
|
||||
|
||||
return chain.proceed(authRequest)
|
||||
|
@ -19,7 +19,7 @@ data class ALManga(
|
||||
val format: String,
|
||||
val publishing_status: String,
|
||||
val start_date_fuzzy: Long,
|
||||
val total_chapters: Int,
|
||||
val total_chapters: Long,
|
||||
val average_score: Int,
|
||||
) {
|
||||
|
||||
@ -29,7 +29,7 @@ data class ALManga(
|
||||
total_chapters = this@ALManga.total_chapters
|
||||
cover_url = image_url_lge
|
||||
summary = description?.htmlDecode() ?: ""
|
||||
score = average_score.toFloat()
|
||||
score = average_score.toDouble()
|
||||
tracking_url = AnilistApi.mangaUrl(remote_id)
|
||||
publishing_status = this@ALManga.publishing_status
|
||||
publishing_type = format
|
||||
@ -58,10 +58,10 @@ data class ALUserManga(
|
||||
remote_id = manga.remote_id
|
||||
title = manga.title_user_pref
|
||||
status = toTrackStatus()
|
||||
score = score_raw.toFloat()
|
||||
score = score_raw.toDouble()
|
||||
started_reading_date = start_date_fuzzy
|
||||
finished_reading_date = completed_date_fuzzy
|
||||
last_chapter_read = chapters_read.toFloat()
|
||||
last_chapter_read = chapters_read.toDouble()
|
||||
library_id = this@ALUserManga.library_id
|
||||
total_chapters = manga.total_chapters
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ class Bangumi(id: Long) : BaseTracker(id, "Bangumi") {
|
||||
override suspend fun update(track: Track, didReadChapter: Boolean): Track {
|
||||
if (track.status != COMPLETED) {
|
||||
if (didReadChapter) {
|
||||
if (track.last_chapter_read.toInt() == track.total_chapters && track.total_chapters > 0) {
|
||||
if (track.last_chapter_read.toLong() == track.total_chapters && track.total_chapters > 0) {
|
||||
track.status = COMPLETED
|
||||
} else {
|
||||
track.status = READING
|
||||
@ -64,7 +64,7 @@ class Bangumi(id: Long) : BaseTracker(id, "Bangumi") {
|
||||
} else {
|
||||
// Set default fields if it's not found in the list
|
||||
track.status = if (hasReadChapters) READING else PLAN_TO_READ
|
||||
track.score = 0F
|
||||
track.score = 0.0
|
||||
add(track)
|
||||
update(track)
|
||||
}
|
||||
@ -87,11 +87,11 @@ class Bangumi(id: Long) : BaseTracker(id, "Bangumi") {
|
||||
|
||||
override fun getLogoColor() = Color.rgb(240, 145, 153)
|
||||
|
||||
override fun getStatusList(): List<Int> {
|
||||
override fun getStatusList(): List<Long> {
|
||||
return listOf(READING, COMPLETED, ON_HOLD, DROPPED, PLAN_TO_READ)
|
||||
}
|
||||
|
||||
override fun getStatus(status: Int): StringResource? = when (status) {
|
||||
override fun getStatus(status: Long): StringResource? = when (status) {
|
||||
READING -> MR.strings.reading
|
||||
PLAN_TO_READ -> MR.strings.plan_to_read
|
||||
COMPLETED -> MR.strings.completed
|
||||
@ -100,11 +100,11 @@ class Bangumi(id: Long) : BaseTracker(id, "Bangumi") {
|
||||
else -> null
|
||||
}
|
||||
|
||||
override fun getReadingStatus(): Int = READING
|
||||
override fun getReadingStatus(): Long = READING
|
||||
|
||||
override fun getRereadingStatus(): Int = -1
|
||||
override fun getRereadingStatus(): Long = -1
|
||||
|
||||
override fun getCompletionStatus(): Int = COMPLETED
|
||||
override fun getCompletionStatus(): Long = COMPLETED
|
||||
|
||||
override suspend fun login(username: String, password: String) = login(password)
|
||||
|
||||
@ -137,11 +137,11 @@ class Bangumi(id: Long) : BaseTracker(id, "Bangumi") {
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val READING = 3
|
||||
const val COMPLETED = 2
|
||||
const val ON_HOLD = 4
|
||||
const val DROPPED = 5
|
||||
const val PLAN_TO_READ = 1
|
||||
const val READING = 3L
|
||||
const val COMPLETED = 2L
|
||||
const val ON_HOLD = 4L
|
||||
const val DROPPED = 5L
|
||||
const val PLAN_TO_READ = 1L
|
||||
|
||||
private val SCORE_LIST = IntRange(0, 10)
|
||||
.map(Int::toString)
|
||||
|
@ -11,7 +11,7 @@ import eu.kanade.tachiyomi.network.parseAs
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import kotlinx.serialization.json.contentOrNull
|
||||
import kotlinx.serialization.json.floatOrNull
|
||||
import kotlinx.serialization.json.doubleOrNull
|
||||
import kotlinx.serialization.json.int
|
||||
import kotlinx.serialization.json.jsonArray
|
||||
import kotlinx.serialization.json.jsonObject
|
||||
@ -105,11 +105,11 @@ class BangumiApi(
|
||||
""
|
||||
}
|
||||
val totalChapters = if (obj["eps_count"] != null) {
|
||||
obj["eps_count"]!!.jsonPrimitive.int
|
||||
obj["eps_count"]!!.jsonPrimitive.long
|
||||
} else {
|
||||
0
|
||||
}
|
||||
val rating = obj["rating"]?.jsonObject?.get("score")?.jsonPrimitive?.floatOrNull ?: -1f
|
||||
val rating = obj["rating"]?.jsonObject?.get("score")?.jsonPrimitive?.doubleOrNull ?: -1.0
|
||||
return TrackSearch.create(trackId).apply {
|
||||
remote_id = obj["id"]!!.jsonPrimitive.long
|
||||
title = obj["name_cn"]!!.jsonPrimitive.content
|
||||
@ -152,7 +152,7 @@ class BangumiApi(
|
||||
} else {
|
||||
json.decodeFromString<Collection>(responseBody).let {
|
||||
track.status = it.status?.id!!
|
||||
track.last_chapter_read = it.ep_status!!.toFloat()
|
||||
track.last_chapter_read = it.ep_status!!.toDouble()
|
||||
track.score = it.rating!!
|
||||
track
|
||||
}
|
||||
@ -182,8 +182,8 @@ class BangumiApi(
|
||||
)
|
||||
|
||||
companion object {
|
||||
private const val clientId = "bgm10555cda0762e80ca"
|
||||
private const val clientSecret = "8fff394a8627b4c388cbf349ec865775"
|
||||
private const val clientId = "bgm291665acbd06a4c28"
|
||||
private const val clientSecret = "43e5ce36b207de16e5d3cfd3e79118db"
|
||||
|
||||
private const val apiUrl = "https://api.bgm.tv"
|
||||
private const val oauthUrl = "https://bgm.tv/oauth/access_token"
|
||||
|
@ -1,5 +1,6 @@
|
||||
package eu.kanade.tachiyomi.data.track.bangumi
|
||||
|
||||
import eu.kanade.tachiyomi.BuildConfig
|
||||
import kotlinx.serialization.json.Json
|
||||
import okhttp3.FormBody
|
||||
import okhttp3.Interceptor
|
||||
@ -29,22 +30,23 @@ class BangumiInterceptor(private val bangumi: Bangumi) : Interceptor {
|
||||
}
|
||||
}
|
||||
|
||||
val authRequest = if (originalRequest.method == "GET") {
|
||||
originalRequest.newBuilder()
|
||||
.header("User-Agent", "Tachiyomi")
|
||||
.url(
|
||||
originalRequest.url.newBuilder()
|
||||
.addQueryParameter("access_token", currAuth.access_token).build(),
|
||||
)
|
||||
.build()
|
||||
} else {
|
||||
originalRequest.newBuilder()
|
||||
.post(addToken(currAuth.access_token, originalRequest.body as FormBody))
|
||||
.header("User-Agent", "Tachiyomi")
|
||||
.build()
|
||||
}
|
||||
|
||||
return chain.proceed(authRequest)
|
||||
return originalRequest.newBuilder()
|
||||
.header(
|
||||
"User-Agent",
|
||||
"antsylich/Mihon/v${BuildConfig.VERSION_NAME} (Android) (http://github.com/mihonapp/mihon)",
|
||||
)
|
||||
.apply {
|
||||
if (originalRequest.method == "GET") {
|
||||
val newUrl = originalRequest.url.newBuilder()
|
||||
.addQueryParameter("access_token", currAuth.access_token)
|
||||
.build()
|
||||
url(newUrl)
|
||||
} else {
|
||||
post(addToken(currAuth.access_token, originalRequest.body as FormBody))
|
||||
}
|
||||
}
|
||||
.build()
|
||||
.let(chain::proceed)
|
||||
}
|
||||
|
||||
fun newAuth(oauth: OAuth?) {
|
||||
|
@ -16,7 +16,7 @@ data class Collection(
|
||||
val comment: String? = "",
|
||||
val ep_status: Int? = 0,
|
||||
val lasttouch: Int? = 0,
|
||||
val rating: Float? = 0f,
|
||||
val rating: Double? = 0.0,
|
||||
val status: Status? = Status(),
|
||||
val tag: List<String?>? = emptyList(),
|
||||
val user: User? = User(),
|
||||
@ -25,7 +25,7 @@ data class Collection(
|
||||
|
||||
@Serializable
|
||||
data class Status(
|
||||
val id: Int? = 0,
|
||||
val id: Long? = 0,
|
||||
val name: String? = "",
|
||||
val type: String? = "",
|
||||
)
|
||||
|
@ -22,9 +22,9 @@ import tachiyomi.domain.track.model.Track as DomainTrack
|
||||
class Kavita(id: Long) : BaseTracker(id, "Kavita"), EnhancedTracker {
|
||||
|
||||
companion object {
|
||||
const val UNREAD = 1
|
||||
const val READING = 2
|
||||
const val COMPLETED = 3
|
||||
const val UNREAD = 1L
|
||||
const val READING = 2L
|
||||
const val COMPLETED = 3L
|
||||
}
|
||||
|
||||
var authentications: OAuth? = null
|
||||
@ -38,20 +38,20 @@ class Kavita(id: Long) : BaseTracker(id, "Kavita"), EnhancedTracker {
|
||||
|
||||
override fun getLogoColor() = Color.rgb(74, 198, 148)
|
||||
|
||||
override fun getStatusList() = listOf(UNREAD, READING, COMPLETED)
|
||||
override fun getStatusList(): List<Long> = listOf(UNREAD, READING, COMPLETED)
|
||||
|
||||
override fun getStatus(status: Int): StringResource? = when (status) {
|
||||
override fun getStatus(status: Long): StringResource? = when (status) {
|
||||
UNREAD -> MR.strings.unread
|
||||
READING -> MR.strings.reading
|
||||
COMPLETED -> MR.strings.completed
|
||||
else -> null
|
||||
}
|
||||
|
||||
override fun getReadingStatus(): Int = READING
|
||||
override fun getReadingStatus(): Long = READING
|
||||
|
||||
override fun getRereadingStatus(): Int = -1
|
||||
override fun getRereadingStatus(): Long = -1
|
||||
|
||||
override fun getCompletionStatus(): Int = COMPLETED
|
||||
override fun getCompletionStatus(): Long = COMPLETED
|
||||
|
||||
override fun getScoreList(): ImmutableList<String> = persistentListOf()
|
||||
|
||||
@ -60,7 +60,7 @@ class Kavita(id: Long) : BaseTracker(id, "Kavita"), EnhancedTracker {
|
||||
override suspend fun update(track: Track, didReadChapter: Boolean): Track {
|
||||
if (track.status != COMPLETED) {
|
||||
if (didReadChapter) {
|
||||
if (track.last_chapter_read.toInt() == track.total_chapters && track.total_chapters > 0) {
|
||||
if (track.last_chapter_read.toLong() == track.total_chapters && track.total_chapters > 0) {
|
||||
track.status = COMPLETED
|
||||
} else {
|
||||
track.status = READING
|
||||
|
@ -93,7 +93,7 @@ class KavitaApi(private val client: OkHttpClient, interceptor: KavitaInterceptor
|
||||
* Ignores volumes.
|
||||
* Volumes consisting of 1 file treated as chapter
|
||||
*/
|
||||
private fun getTotalChapters(url: String): Int {
|
||||
private fun getTotalChapters(url: String): Long {
|
||||
val requestUrl = getApiVolumesUrl(url)
|
||||
try {
|
||||
val listVolumeDto = with(json) {
|
||||
@ -101,13 +101,13 @@ class KavitaApi(private val client: OkHttpClient, interceptor: KavitaInterceptor
|
||||
.execute()
|
||||
.parseAs<List<VolumeDto>>()
|
||||
}
|
||||
var volumeNumber = 0
|
||||
var maxChapterNumber = 0
|
||||
var volumeNumber = 0L
|
||||
var maxChapterNumber = 0L
|
||||
for (volume in listVolumeDto) {
|
||||
if (volume.chapters.maxOf { it.number!!.toFloat() } == 0f) {
|
||||
volumeNumber++
|
||||
} else if (maxChapterNumber < volume.chapters.maxOf { it.number!!.toFloat() }) {
|
||||
maxChapterNumber = volume.chapters.maxOf { it.number!!.toFloat().toInt() }
|
||||
maxChapterNumber = volume.chapters.maxOf { it.number!!.toFloat().toLong() }
|
||||
}
|
||||
}
|
||||
|
||||
@ -118,17 +118,17 @@ class KavitaApi(private val client: OkHttpClient, interceptor: KavitaInterceptor
|
||||
}
|
||||
}
|
||||
|
||||
private fun getLatestChapterRead(url: String): Float {
|
||||
private fun getLatestChapterRead(url: String): Double {
|
||||
val seriesId = getIdFromUrl(url)
|
||||
val requestUrl = "${getApiFromUrl(url)}/Tachiyomi/latest-chapter?seriesId=$seriesId"
|
||||
try {
|
||||
with(json) {
|
||||
authClient.newCall(GET(requestUrl)).execute().use {
|
||||
if (it.code == 200) {
|
||||
return it.parseAs<ChapterDto>().number!!.replace(",", ".").toFloat()
|
||||
return it.parseAs<ChapterDto>().number!!.replace(",", ".").toDouble()
|
||||
}
|
||||
if (it.code == 204) {
|
||||
return 0F
|
||||
return 0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -139,7 +139,7 @@ class KavitaApi(private val client: OkHttpClient, interceptor: KavitaInterceptor
|
||||
) { "Exception getting latest chapter read. Could not get itemRequest: $requestUrl" }
|
||||
throw e
|
||||
}
|
||||
return 0F
|
||||
return 0.0
|
||||
}
|
||||
|
||||
suspend fun getTrackSearch(url: String): TrackSearch = withIOContext {
|
||||
|
@ -18,7 +18,7 @@ class KavitaInterceptor(private val kavita: Kavita) : Interceptor {
|
||||
// Add the authorization header to the original request.
|
||||
val authRequest = originalRequest.newBuilder()
|
||||
.addHeader("Authorization", "Bearer $jwtToken")
|
||||
.header("User-Agent", "Tachiyomi Kavita v${BuildConfig.VERSION_NAME}")
|
||||
.header("User-Agent", "Mihon v${BuildConfig.VERSION_NAME} (${BuildConfig.APPLICATION_ID})")
|
||||
.build()
|
||||
|
||||
return chain.proceed(authRequest)
|
||||
|
@ -19,11 +19,11 @@ import tachiyomi.domain.track.model.Track as DomainTrack
|
||||
class Kitsu(id: Long) : BaseTracker(id, "Kitsu"), DeletableTracker {
|
||||
|
||||
companion object {
|
||||
const val READING = 1
|
||||
const val COMPLETED = 2
|
||||
const val ON_HOLD = 3
|
||||
const val DROPPED = 4
|
||||
const val PLAN_TO_READ = 5
|
||||
const val READING = 1L
|
||||
const val COMPLETED = 2L
|
||||
const val ON_HOLD = 3L
|
||||
const val DROPPED = 4L
|
||||
const val PLAN_TO_READ = 5L
|
||||
}
|
||||
|
||||
override val supportsReadingDates: Boolean = true
|
||||
@ -38,11 +38,11 @@ class Kitsu(id: Long) : BaseTracker(id, "Kitsu"), DeletableTracker {
|
||||
|
||||
override fun getLogoColor() = Color.rgb(51, 37, 50)
|
||||
|
||||
override fun getStatusList(): List<Int> {
|
||||
override fun getStatusList(): List<Long> {
|
||||
return listOf(READING, COMPLETED, ON_HOLD, DROPPED, PLAN_TO_READ)
|
||||
}
|
||||
|
||||
override fun getStatus(status: Int): StringResource? = when (status) {
|
||||
override fun getStatus(status: Long): StringResource? = when (status) {
|
||||
READING -> MR.strings.reading
|
||||
PLAN_TO_READ -> MR.strings.plan_to_read
|
||||
COMPLETED -> MR.strings.completed
|
||||
@ -51,19 +51,19 @@ class Kitsu(id: Long) : BaseTracker(id, "Kitsu"), DeletableTracker {
|
||||
else -> null
|
||||
}
|
||||
|
||||
override fun getReadingStatus(): Int = READING
|
||||
override fun getReadingStatus(): Long = READING
|
||||
|
||||
override fun getRereadingStatus(): Int = -1
|
||||
override fun getRereadingStatus(): Long = -1
|
||||
|
||||
override fun getCompletionStatus(): Int = COMPLETED
|
||||
override fun getCompletionStatus(): Long = COMPLETED
|
||||
|
||||
override fun getScoreList(): ImmutableList<String> {
|
||||
val df = DecimalFormat("0.#")
|
||||
return (listOf("0") + IntRange(2, 20).map { df.format(it / 2f) }).toImmutableList()
|
||||
}
|
||||
|
||||
override fun indexToScore(index: Int): Float {
|
||||
return if (index > 0) (index + 1) / 2f else 0f
|
||||
override fun indexToScore(index: Int): Double {
|
||||
return if (index > 0) (index + 1) / 2.0 else 0.0
|
||||
}
|
||||
|
||||
override fun displayScore(track: DomainTrack): String {
|
||||
@ -78,12 +78,12 @@ class Kitsu(id: Long) : BaseTracker(id, "Kitsu"), DeletableTracker {
|
||||
override suspend fun update(track: Track, didReadChapter: Boolean): Track {
|
||||
if (track.status != COMPLETED) {
|
||||
if (didReadChapter) {
|
||||
if (track.last_chapter_read.toInt() == track.total_chapters && track.total_chapters > 0) {
|
||||
if (track.last_chapter_read.toLong() == track.total_chapters && track.total_chapters > 0) {
|
||||
track.status = COMPLETED
|
||||
track.finished_reading_date = System.currentTimeMillis()
|
||||
} else {
|
||||
track.status = READING
|
||||
if (track.last_chapter_read == 1F) {
|
||||
if (track.last_chapter_read == 1.0) {
|
||||
track.started_reading_date = System.currentTimeMillis()
|
||||
}
|
||||
}
|
||||
@ -110,7 +110,7 @@ class Kitsu(id: Long) : BaseTracker(id, "Kitsu"), DeletableTracker {
|
||||
update(track)
|
||||
} else {
|
||||
track.status = if (hasReadChapters) READING else PLAN_TO_READ
|
||||
track.score = 0F
|
||||
track.score = 0.0
|
||||
add(track)
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package eu.kanade.tachiyomi.data.track.kitsu
|
||||
|
||||
import eu.kanade.tachiyomi.BuildConfig
|
||||
import kotlinx.serialization.json.Json
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.Response
|
||||
@ -34,6 +35,7 @@ class KitsuInterceptor(private val kitsu: Kitsu) : Interceptor {
|
||||
// Add the authorization header to the original request.
|
||||
val authRequest = originalRequest.newBuilder()
|
||||
.addHeader("Authorization", "Bearer ${oauth!!.access_token}")
|
||||
.header("User-Agent", "Mihon v${BuildConfig.VERSION_NAME} (${BuildConfig.APPLICATION_ID})")
|
||||
.header("Accept", "application/vnd.api+json")
|
||||
.header("Content-Type", "application/vnd.api+json")
|
||||
.build()
|
||||
|
@ -8,10 +8,10 @@ import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import kotlinx.serialization.json.contentOrNull
|
||||
import kotlinx.serialization.json.int
|
||||
import kotlinx.serialization.json.intOrNull
|
||||
import kotlinx.serialization.json.jsonObject
|
||||
import kotlinx.serialization.json.jsonPrimitive
|
||||
import kotlinx.serialization.json.long
|
||||
import kotlinx.serialization.json.longOrNull
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
@ -19,7 +19,7 @@ import java.util.Locale
|
||||
class KitsuSearchManga(obj: JsonObject) {
|
||||
val id = obj["id"]!!.jsonPrimitive.long
|
||||
private val canonicalTitle = obj["canonicalTitle"]!!.jsonPrimitive.content
|
||||
private val chapterCount = obj["chapterCount"]?.jsonPrimitive?.intOrNull
|
||||
private val chapterCount = obj["chapterCount"]?.jsonPrimitive?.longOrNull
|
||||
val subType = obj["subtype"]?.jsonPrimitive?.contentOrNull
|
||||
val original = try {
|
||||
obj["posterImage"]?.jsonObject?.get("original")?.jsonPrimitive?.content
|
||||
@ -28,7 +28,7 @@ class KitsuSearchManga(obj: JsonObject) {
|
||||
null
|
||||
}
|
||||
private val synopsis = obj["synopsis"]?.jsonPrimitive?.contentOrNull
|
||||
private val rating = obj["averageRating"]?.jsonPrimitive?.contentOrNull?.toFloatOrNull()
|
||||
private val rating = obj["averageRating"]?.jsonPrimitive?.contentOrNull?.toDoubleOrNull()
|
||||
private var startDate = obj["startDate"]?.jsonPrimitive?.contentOrNull?.let {
|
||||
val outputDf = SimpleDateFormat("yyyy-MM-dd", Locale.US)
|
||||
outputDf.format(Date(it.toLong() * 1000))
|
||||
@ -43,7 +43,7 @@ class KitsuSearchManga(obj: JsonObject) {
|
||||
cover_url = original ?: ""
|
||||
summary = synopsis ?: ""
|
||||
tracking_url = KitsuApi.mangaUrl(remote_id)
|
||||
score = rating ?: -1f
|
||||
score = rating ?: -1.0
|
||||
publishing_status = if (endDate == null) {
|
||||
"Publishing"
|
||||
} else {
|
||||
@ -57,7 +57,7 @@ class KitsuSearchManga(obj: JsonObject) {
|
||||
class KitsuLibManga(obj: JsonObject, manga: JsonObject) {
|
||||
val id = manga["id"]!!.jsonPrimitive.int
|
||||
private val canonicalTitle = manga["attributes"]!!.jsonObject["canonicalTitle"]!!.jsonPrimitive.content
|
||||
private val chapterCount = manga["attributes"]!!.jsonObject["chapterCount"]?.jsonPrimitive?.intOrNull
|
||||
private val chapterCount = manga["attributes"]!!.jsonObject["chapterCount"]?.jsonPrimitive?.longOrNull
|
||||
val type = manga["attributes"]!!.jsonObject["mangaType"]?.jsonPrimitive?.contentOrNull.orEmpty()
|
||||
val original = manga["attributes"]!!.jsonObject["posterImage"]!!.jsonObject["original"]!!.jsonPrimitive.content
|
||||
private val synopsis = manga["attributes"]!!.jsonObject["synopsis"]!!.jsonPrimitive.content
|
||||
@ -82,8 +82,8 @@ class KitsuLibManga(obj: JsonObject, manga: JsonObject) {
|
||||
started_reading_date = KitsuDateHelper.parse(startedAt)
|
||||
finished_reading_date = KitsuDateHelper.parse(finishedAt)
|
||||
status = toTrackStatus()
|
||||
score = ratingTwenty?.let { it.toInt() / 2f } ?: 0f
|
||||
last_chapter_read = progress.toFloat()
|
||||
score = ratingTwenty?.let { it.toInt() / 2.0 } ?: 0.0
|
||||
last_chapter_read = progress.toDouble()
|
||||
}
|
||||
|
||||
private fun toTrackStatus() = when (status) {
|
||||
|
@ -19,9 +19,9 @@ import tachiyomi.domain.track.model.Track as DomainTrack
|
||||
class Komga(id: Long) : BaseTracker(id, "Komga"), EnhancedTracker {
|
||||
|
||||
companion object {
|
||||
const val UNREAD = 1
|
||||
const val READING = 2
|
||||
const val COMPLETED = 3
|
||||
const val UNREAD = 1L
|
||||
const val READING = 2L
|
||||
const val COMPLETED = 3L
|
||||
}
|
||||
|
||||
override val client: OkHttpClient =
|
||||
@ -35,20 +35,20 @@ class Komga(id: Long) : BaseTracker(id, "Komga"), EnhancedTracker {
|
||||
|
||||
override fun getLogoColor() = Color.rgb(51, 37, 50)
|
||||
|
||||
override fun getStatusList() = listOf(UNREAD, READING, COMPLETED)
|
||||
override fun getStatusList(): List<Long> = listOf(UNREAD, READING, COMPLETED)
|
||||
|
||||
override fun getStatus(status: Int): StringResource? = when (status) {
|
||||
override fun getStatus(status: Long): StringResource? = when (status) {
|
||||
UNREAD -> MR.strings.unread
|
||||
READING -> MR.strings.reading
|
||||
COMPLETED -> MR.strings.completed
|
||||
else -> null
|
||||
}
|
||||
|
||||
override fun getReadingStatus(): Int = READING
|
||||
override fun getReadingStatus(): Long = READING
|
||||
|
||||
override fun getRereadingStatus(): Int = -1
|
||||
override fun getRereadingStatus(): Long = -1
|
||||
|
||||
override fun getCompletionStatus(): Int = COMPLETED
|
||||
override fun getCompletionStatus(): Long = COMPLETED
|
||||
|
||||
override fun getScoreList(): ImmutableList<String> = persistentListOf()
|
||||
|
||||
@ -57,7 +57,7 @@ class Komga(id: Long) : BaseTracker(id, "Komga"), EnhancedTracker {
|
||||
override suspend fun update(track: Track, didReadChapter: Boolean): Track {
|
||||
if (track.status != COMPLETED) {
|
||||
if (didReadChapter) {
|
||||
if (track.last_chapter_read.toInt() == track.total_chapters && track.total_chapters > 0) {
|
||||
if (track.last_chapter_read.toLong() == track.total_chapters && track.total_chapters > 0) {
|
||||
track.status = COMPLETED
|
||||
} else {
|
||||
track.status = READING
|
||||
|
@ -1,5 +1,6 @@
|
||||
package eu.kanade.tachiyomi.data.track.komga
|
||||
|
||||
import eu.kanade.tachiyomi.BuildConfig
|
||||
import eu.kanade.tachiyomi.data.database.models.Track
|
||||
import eu.kanade.tachiyomi.data.track.model.TrackSearch
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
@ -8,6 +9,7 @@ import eu.kanade.tachiyomi.network.parseAs
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import logcat.LogPriority
|
||||
import okhttp3.Headers
|
||||
import okhttp3.MediaType.Companion.toMediaType
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
@ -23,6 +25,12 @@ class KomgaApi(
|
||||
private val client: OkHttpClient,
|
||||
) {
|
||||
|
||||
private val headers: Headers by lazy {
|
||||
Headers.Builder()
|
||||
.add("User-Agent", "Mihon v${BuildConfig.VERSION_NAME} (${BuildConfig.APPLICATION_ID})")
|
||||
.build()
|
||||
}
|
||||
|
||||
private val json: Json by injectLazy()
|
||||
|
||||
suspend fun getTrackSearch(url: String): TrackSearch =
|
||||
@ -30,12 +38,12 @@ class KomgaApi(
|
||||
try {
|
||||
val track = with(json) {
|
||||
if (url.contains(READLIST_API)) {
|
||||
client.newCall(GET(url))
|
||||
client.newCall(GET(url, headers))
|
||||
.awaitSuccess()
|
||||
.parseAs<ReadListDto>()
|
||||
.toTrack()
|
||||
} else {
|
||||
client.newCall(GET(url))
|
||||
client.newCall(GET(url, headers))
|
||||
.awaitSuccess()
|
||||
.parseAs<SeriesDto>()
|
||||
.toTrack()
|
||||
@ -43,7 +51,9 @@ class KomgaApi(
|
||||
}
|
||||
|
||||
val progress = client
|
||||
.newCall(GET("${url.replace("/api/v1/series/", "/api/v2/series/")}/read-progress/tachiyomi"))
|
||||
.newCall(
|
||||
GET("${url.replace("/api/v1/series/", "/api/v2/series/")}/read-progress/tachiyomi", headers),
|
||||
)
|
||||
.awaitSuccess().let {
|
||||
with(json) {
|
||||
if (url.contains("/api/v1/series/")) {
|
||||
@ -57,7 +67,7 @@ class KomgaApi(
|
||||
track.apply {
|
||||
cover_url = "$url/thumbnail"
|
||||
tracking_url = url
|
||||
total_chapters = progress.maxNumberSort.toInt()
|
||||
total_chapters = progress.maxNumberSort.toLong()
|
||||
status = when (progress.booksCount) {
|
||||
progress.booksUnreadCount -> Komga.UNREAD
|
||||
progress.booksReadCount -> Komga.COMPLETED
|
||||
@ -80,6 +90,7 @@ class KomgaApi(
|
||||
client.newCall(
|
||||
Request.Builder()
|
||||
.url("${track.tracking_url.replace("/api/v1/series/", "/api/v2/series/")}/read-progress/tachiyomi")
|
||||
.headers(headers)
|
||||
.put(payload.toRequestBody("application/json".toMediaType()))
|
||||
.build(),
|
||||
)
|
||||
|
@ -65,7 +65,7 @@ data class ReadProgressUpdateDto(
|
||||
|
||||
@Serializable
|
||||
data class ReadProgressUpdateV2Dto(
|
||||
val lastBookNumberSortRead: Float,
|
||||
val lastBookNumberSortRead: Double,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
@ -91,7 +91,7 @@ data class ReadProgressDto(
|
||||
booksReadCount,
|
||||
booksUnreadCount,
|
||||
booksInProgressCount,
|
||||
lastReadContinuousIndex.toFloat(),
|
||||
lastReadContinuousIndex.toDouble(),
|
||||
booksCount.toFloat(),
|
||||
)
|
||||
}
|
||||
@ -102,6 +102,6 @@ data class ReadProgressV2Dto(
|
||||
val booksReadCount: Int,
|
||||
val booksUnreadCount: Int,
|
||||
val booksInProgressCount: Int,
|
||||
val lastReadContinuousNumberSort: Float,
|
||||
val lastReadContinuousNumberSort: Double,
|
||||
val maxNumberSort: Float,
|
||||
)
|
||||
|
@ -6,6 +6,8 @@ import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.models.Track
|
||||
import eu.kanade.tachiyomi.data.track.BaseTracker
|
||||
import eu.kanade.tachiyomi.data.track.DeletableTracker
|
||||
import eu.kanade.tachiyomi.data.track.mangaupdates.dto.ListItem
|
||||
import eu.kanade.tachiyomi.data.track.mangaupdates.dto.Rating
|
||||
import eu.kanade.tachiyomi.data.track.mangaupdates.dto.copyTo
|
||||
import eu.kanade.tachiyomi.data.track.mangaupdates.dto.toTrackSearch
|
||||
import eu.kanade.tachiyomi.data.track.model.TrackSearch
|
||||
@ -17,16 +19,22 @@ import tachiyomi.domain.track.model.Track as DomainTrack
|
||||
class MangaUpdates(id: Long) : BaseTracker(id, "MangaUpdates"), DeletableTracker {
|
||||
|
||||
companion object {
|
||||
const val READING_LIST = 0
|
||||
const val WISH_LIST = 1
|
||||
const val COMPLETE_LIST = 2
|
||||
const val UNFINISHED_LIST = 3
|
||||
const val ON_HOLD_LIST = 4
|
||||
const val READING_LIST = 0L
|
||||
const val WISH_LIST = 1L
|
||||
const val COMPLETE_LIST = 2L
|
||||
const val UNFINISHED_LIST = 3L
|
||||
const val ON_HOLD_LIST = 4L
|
||||
|
||||
private val SCORE_LIST = (
|
||||
(0..9)
|
||||
.flatMap { i -> (0..9).map { j -> "$i.$j" } } + listOf("10.0")
|
||||
)
|
||||
private val SCORE_LIST = (0..10)
|
||||
.flatMap { decimal ->
|
||||
when (decimal) {
|
||||
0 -> listOf("-")
|
||||
10 -> listOf("10.0")
|
||||
else -> (0..9).map { fraction ->
|
||||
"$decimal.$fraction"
|
||||
}
|
||||
}
|
||||
}
|
||||
.toImmutableList()
|
||||
}
|
||||
|
||||
@ -38,11 +46,11 @@ class MangaUpdates(id: Long) : BaseTracker(id, "MangaUpdates"), DeletableTracker
|
||||
|
||||
override fun getLogoColor(): Int = Color.rgb(146, 160, 173)
|
||||
|
||||
override fun getStatusList(): List<Int> {
|
||||
override fun getStatusList(): List<Long> {
|
||||
return listOf(READING_LIST, COMPLETE_LIST, ON_HOLD_LIST, UNFINISHED_LIST, WISH_LIST)
|
||||
}
|
||||
|
||||
override fun getStatus(status: Int): StringResource? = when (status) {
|
||||
override fun getStatus(status: Long): StringResource? = when (status) {
|
||||
READING_LIST -> MR.strings.reading_list
|
||||
WISH_LIST -> MR.strings.wish_list
|
||||
COMPLETE_LIST -> MR.strings.complete_list
|
||||
@ -51,15 +59,15 @@ class MangaUpdates(id: Long) : BaseTracker(id, "MangaUpdates"), DeletableTracker
|
||||
else -> null
|
||||
}
|
||||
|
||||
override fun getReadingStatus(): Int = READING_LIST
|
||||
override fun getReadingStatus(): Long = READING_LIST
|
||||
|
||||
override fun getRereadingStatus(): Int = -1
|
||||
override fun getRereadingStatus(): Long = -1
|
||||
|
||||
override fun getCompletionStatus(): Int = COMPLETE_LIST
|
||||
override fun getCompletionStatus(): Long = COMPLETE_LIST
|
||||
|
||||
override fun getScoreList(): ImmutableList<String> = SCORE_LIST
|
||||
|
||||
override fun indexToScore(index: Int): Float = SCORE_LIST[index].toFloat()
|
||||
override fun indexToScore(index: Int): Double = if (index == 0) 0.0 else SCORE_LIST[index].toDouble()
|
||||
|
||||
override fun displayScore(track: DomainTrack): String = track.score.toString()
|
||||
|
||||
@ -78,9 +86,9 @@ class MangaUpdates(id: Long) : BaseTracker(id, "MangaUpdates"), DeletableTracker
|
||||
override suspend fun bind(track: Track, hasReadChapters: Boolean): Track {
|
||||
return try {
|
||||
val (series, rating) = api.getSeriesListItem(track)
|
||||
series.copyTo(track)
|
||||
rating?.copyTo(track) ?: track
|
||||
track.copyFrom(series, rating)
|
||||
} catch (e: Exception) {
|
||||
track.score = 0.0
|
||||
api.addSeriesToList(track, hasReadChapters)
|
||||
track
|
||||
}
|
||||
@ -95,8 +103,12 @@ class MangaUpdates(id: Long) : BaseTracker(id, "MangaUpdates"), DeletableTracker
|
||||
|
||||
override suspend fun refresh(track: Track): Track {
|
||||
val (series, rating) = api.getSeriesListItem(track)
|
||||
series.copyTo(track)
|
||||
return rating?.copyTo(track) ?: track
|
||||
return track.copyFrom(series, rating)
|
||||
}
|
||||
|
||||
private fun Track.copyFrom(item: ListItem, rating: Rating?): Track = apply {
|
||||
item.copyTo(this)
|
||||
score = rating?.rating ?: 0.0
|
||||
}
|
||||
|
||||
override suspend fun login(username: String, password: String) {
|
||||
@ -106,6 +118,6 @@ class MangaUpdates(id: Long) : BaseTracker(id, "MangaUpdates"), DeletableTracker
|
||||
}
|
||||
|
||||
fun restoreSession(): String? {
|
||||
return trackPreferences.trackPassword(this).get()
|
||||
return trackPreferences.trackPassword(this).get().ifBlank { null }
|
||||
}
|
||||
}
|
||||
|
@ -79,7 +79,7 @@ class MangaUpdatesApi(
|
||||
.let {
|
||||
if (it.code == 200) {
|
||||
track.status = status
|
||||
track.last_chapter_read = 1f
|
||||
track.last_chapter_read = 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -133,7 +133,8 @@ class MangaUpdatesApi(
|
||||
}
|
||||
|
||||
private suspend fun updateSeriesRating(track: Track) {
|
||||
if (track.score != 0f) {
|
||||
if (track.score < 0.0) return
|
||||
if (track.score != 0.0) {
|
||||
val body = buildJsonObject {
|
||||
put("rating", track.score)
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package eu.kanade.tachiyomi.data.track.mangaupdates
|
||||
|
||||
import eu.kanade.tachiyomi.BuildConfig
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.Response
|
||||
import java.io.IOException
|
||||
@ -18,6 +19,7 @@ class MangaUpdatesInterceptor(
|
||||
// Add the authorization header to the original request.
|
||||
val authRequest = originalRequest.newBuilder()
|
||||
.addHeader("Authorization", "Bearer $token")
|
||||
.header("User-Agent", "Mihon v${BuildConfig.VERSION_NAME} (${BuildConfig.APPLICATION_ID})")
|
||||
.build()
|
||||
|
||||
return chain.proceed(authRequest)
|
||||
|
@ -9,7 +9,7 @@ import kotlinx.serialization.Serializable
|
||||
data class ListItem(
|
||||
val series: Series? = null,
|
||||
@SerialName("list_id")
|
||||
val listId: Int? = null,
|
||||
val listId: Long? = null,
|
||||
val status: Status? = null,
|
||||
val priority: Int? = null,
|
||||
)
|
||||
@ -17,6 +17,6 @@ data class ListItem(
|
||||
fun ListItem.copyTo(track: Track): Track {
|
||||
return track.apply {
|
||||
this.status = listId ?: READING_LIST
|
||||
this.last_chapter_read = this@copyTo.status?.chapter?.toFloat() ?: 0f
|
||||
this.last_chapter_read = this@copyTo.status?.chapter?.toDouble() ?: 0.0
|
||||
}
|
||||
}
|
||||
|
@ -5,11 +5,11 @@ import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class Rating(
|
||||
val rating: Float? = null,
|
||||
val rating: Double? = null,
|
||||
)
|
||||
|
||||
fun Rating.copyTo(track: Track): Track {
|
||||
return track.apply {
|
||||
this.score = rating ?: 0f
|
||||
this.score = rating ?: 0.0
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ class TrackSearch : Track {
|
||||
|
||||
override var manga_id: Long = 0
|
||||
|
||||
override var tracker_id: Int = 0
|
||||
override var tracker_id: Long = 0
|
||||
|
||||
override var remote_id: Long = 0
|
||||
|
||||
@ -16,13 +16,13 @@ class TrackSearch : Track {
|
||||
|
||||
override lateinit var title: String
|
||||
|
||||
override var last_chapter_read: Float = 0F
|
||||
override var last_chapter_read: Double = 0.0
|
||||
|
||||
override var total_chapters: Int = 0
|
||||
override var total_chapters: Long = 0
|
||||
|
||||
override var score: Float = -1f
|
||||
override var score: Double = -1.0
|
||||
|
||||
override var status: Int = 0
|
||||
override var status: Long = 0
|
||||
|
||||
override var started_reading_date: Long = 0
|
||||
|
||||
@ -55,14 +55,14 @@ class TrackSearch : Track {
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = manga_id.hashCode()
|
||||
result = 31 * result + tracker_id
|
||||
result = 31 * result + tracker_id.hashCode()
|
||||
result = 31 * result + remote_id.hashCode()
|
||||
return result
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun create(serviceId: Long): TrackSearch = TrackSearch().apply {
|
||||
tracker_id = serviceId.toInt()
|
||||
tracker_id = serviceId
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,12 +18,12 @@ import tachiyomi.domain.track.model.Track as DomainTrack
|
||||
class MyAnimeList(id: Long) : BaseTracker(id, "MyAnimeList"), DeletableTracker {
|
||||
|
||||
companion object {
|
||||
const val READING = 1
|
||||
const val COMPLETED = 2
|
||||
const val ON_HOLD = 3
|
||||
const val DROPPED = 4
|
||||
const val PLAN_TO_READ = 6
|
||||
const val REREADING = 7
|
||||
const val READING = 1L
|
||||
const val COMPLETED = 2L
|
||||
const val ON_HOLD = 3L
|
||||
const val DROPPED = 4L
|
||||
const val PLAN_TO_READ = 6L
|
||||
const val REREADING = 7L
|
||||
|
||||
private const val SEARCH_ID_PREFIX = "id:"
|
||||
private const val SEARCH_LIST_PREFIX = "my:"
|
||||
@ -35,7 +35,7 @@ class MyAnimeList(id: Long) : BaseTracker(id, "MyAnimeList"), DeletableTracker {
|
||||
|
||||
private val json: Json by injectLazy()
|
||||
|
||||
private val interceptor by lazy { MyAnimeListInterceptor(this, getPassword()) }
|
||||
private val interceptor by lazy { MyAnimeListInterceptor(this) }
|
||||
private val api by lazy { MyAnimeListApi(id, client, interceptor) }
|
||||
|
||||
override val supportsReadingDates: Boolean = true
|
||||
@ -44,11 +44,11 @@ class MyAnimeList(id: Long) : BaseTracker(id, "MyAnimeList"), DeletableTracker {
|
||||
|
||||
override fun getLogoColor() = Color.rgb(46, 81, 162)
|
||||
|
||||
override fun getStatusList(): List<Int> {
|
||||
override fun getStatusList(): List<Long> {
|
||||
return listOf(READING, COMPLETED, ON_HOLD, DROPPED, PLAN_TO_READ, REREADING)
|
||||
}
|
||||
|
||||
override fun getStatus(status: Int): StringResource? = when (status) {
|
||||
override fun getStatus(status: Long): StringResource? = when (status) {
|
||||
READING -> MR.strings.reading
|
||||
PLAN_TO_READ -> MR.strings.plan_to_read
|
||||
COMPLETED -> MR.strings.completed
|
||||
@ -58,11 +58,11 @@ class MyAnimeList(id: Long) : BaseTracker(id, "MyAnimeList"), DeletableTracker {
|
||||
else -> null
|
||||
}
|
||||
|
||||
override fun getReadingStatus(): Int = READING
|
||||
override fun getReadingStatus(): Long = READING
|
||||
|
||||
override fun getRereadingStatus(): Int = REREADING
|
||||
override fun getRereadingStatus(): Long = REREADING
|
||||
|
||||
override fun getCompletionStatus(): Int = COMPLETED
|
||||
override fun getCompletionStatus(): Long = COMPLETED
|
||||
|
||||
override fun getScoreList(): ImmutableList<String> = SCORE_LIST
|
||||
|
||||
@ -77,12 +77,12 @@ class MyAnimeList(id: Long) : BaseTracker(id, "MyAnimeList"), DeletableTracker {
|
||||
override suspend fun update(track: Track, didReadChapter: Boolean): Track {
|
||||
if (track.status != COMPLETED) {
|
||||
if (didReadChapter) {
|
||||
if (track.last_chapter_read.toInt() == track.total_chapters && track.total_chapters > 0) {
|
||||
if (track.last_chapter_read.toLong() == track.total_chapters && track.total_chapters > 0) {
|
||||
track.status = COMPLETED
|
||||
track.finished_reading_date = System.currentTimeMillis()
|
||||
} else if (track.status != REREADING) {
|
||||
track.status = READING
|
||||
if (track.last_chapter_read == 1F) {
|
||||
if (track.last_chapter_read == 1.0) {
|
||||
track.started_reading_date = System.currentTimeMillis()
|
||||
}
|
||||
}
|
||||
@ -104,14 +104,14 @@ class MyAnimeList(id: Long) : BaseTracker(id, "MyAnimeList"), DeletableTracker {
|
||||
|
||||
if (track.status != COMPLETED) {
|
||||
val isRereading = track.status == REREADING
|
||||
track.status = if (isRereading.not() && hasReadChapters) READING else track.status
|
||||
track.status = if (!isRereading && hasReadChapters) READING else track.status
|
||||
}
|
||||
|
||||
update(track)
|
||||
} else {
|
||||
// Set default fields if it's not found in the list
|
||||
track.status = if (hasReadChapters) READING else PLAN_TO_READ
|
||||
track.score = 0F
|
||||
track.score = 0.0
|
||||
add(track)
|
||||
}
|
||||
}
|
||||
@ -155,6 +155,14 @@ class MyAnimeList(id: Long) : BaseTracker(id, "MyAnimeList"), DeletableTracker {
|
||||
interceptor.setAuth(null)
|
||||
}
|
||||
|
||||
fun getIfAuthExpired(): Boolean {
|
||||
return trackPreferences.trackAuthExpired(this).get()
|
||||
}
|
||||
|
||||
fun setAuthExpired() {
|
||||
trackPreferences.trackAuthExpired(this).set(true)
|
||||
}
|
||||
|
||||
fun saveOAuth(oAuth: OAuth?) {
|
||||
trackPreferences.trackToken(this).set(json.encodeToString(oAuth))
|
||||
}
|
||||
|
@ -16,8 +16,8 @@ import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import kotlinx.serialization.json.boolean
|
||||
import kotlinx.serialization.json.contentOrNull
|
||||
import kotlinx.serialization.json.float
|
||||
import kotlinx.serialization.json.floatOrNull
|
||||
import kotlinx.serialization.json.double
|
||||
import kotlinx.serialization.json.doubleOrNull
|
||||
import kotlinx.serialization.json.int
|
||||
import kotlinx.serialization.json.jsonArray
|
||||
import kotlinx.serialization.json.jsonObject
|
||||
@ -47,13 +47,13 @@ class MyAnimeListApi(
|
||||
suspend fun getAccessToken(authCode: String): OAuth {
|
||||
return withIOContext {
|
||||
val formBody: RequestBody = FormBody.Builder()
|
||||
.add("client_id", clientId)
|
||||
.add("client_id", CLIENT_ID)
|
||||
.add("code", authCode)
|
||||
.add("code_verifier", codeVerifier)
|
||||
.add("grant_type", "authorization_code")
|
||||
.build()
|
||||
with(json) {
|
||||
client.newCall(POST("$baseOAuthUrl/token", body = formBody))
|
||||
client.newCall(POST("$BASE_OAUTH_URL/token", body = formBody))
|
||||
.awaitSuccess()
|
||||
.parseAs()
|
||||
}
|
||||
@ -63,7 +63,7 @@ class MyAnimeListApi(
|
||||
suspend fun getCurrentUser(): String {
|
||||
return withIOContext {
|
||||
val request = Request.Builder()
|
||||
.url("$baseApiUrl/users/@me")
|
||||
.url("$BASE_API_URL/users/@me")
|
||||
.get()
|
||||
.build()
|
||||
with(json) {
|
||||
@ -77,7 +77,7 @@ class MyAnimeListApi(
|
||||
|
||||
suspend fun search(query: String): List<TrackSearch> {
|
||||
return withIOContext {
|
||||
val url = "$baseApiUrl/manga".toUri().buildUpon()
|
||||
val url = "$BASE_API_URL/manga".toUri().buildUpon()
|
||||
// MAL API throws a 400 when the query is over 64 characters...
|
||||
.appendQueryParameter("q", query.take(64))
|
||||
.appendQueryParameter("nsfw", "true")
|
||||
@ -102,7 +102,7 @@ class MyAnimeListApi(
|
||||
|
||||
suspend fun getMangaDetails(id: Int): TrackSearch {
|
||||
return withIOContext {
|
||||
val url = "$baseApiUrl/manga".toUri().buildUpon()
|
||||
val url = "$BASE_API_URL/manga".toUri().buildUpon()
|
||||
.appendPath(id.toString())
|
||||
.appendQueryParameter(
|
||||
"fields",
|
||||
@ -119,8 +119,8 @@ class MyAnimeListApi(
|
||||
remote_id = obj["id"]!!.jsonPrimitive.long
|
||||
title = obj["title"]!!.jsonPrimitive.content
|
||||
summary = obj["synopsis"]?.jsonPrimitive?.content ?: ""
|
||||
total_chapters = obj["num_chapters"]!!.jsonPrimitive.int
|
||||
score = obj["mean"]?.jsonPrimitive?.floatOrNull ?: -1f
|
||||
total_chapters = obj["num_chapters"]!!.jsonPrimitive.long
|
||||
score = obj["mean"]?.jsonPrimitive?.doubleOrNull ?: -1.0
|
||||
cover_url =
|
||||
obj["main_picture"]?.jsonObject?.get("large")?.jsonPrimitive?.content
|
||||
?: ""
|
||||
@ -178,7 +178,7 @@ class MyAnimeListApi(
|
||||
|
||||
suspend fun findListItem(track: Track): Track? {
|
||||
return withIOContext {
|
||||
val uri = "$baseApiUrl/manga".toUri().buildUpon()
|
||||
val uri = "$BASE_API_URL/manga".toUri().buildUpon()
|
||||
.appendPath(track.remote_id.toString())
|
||||
.appendQueryParameter("fields", "num_chapters,my_list_status{start_date,finish_date}")
|
||||
.build()
|
||||
@ -187,7 +187,7 @@ class MyAnimeListApi(
|
||||
.awaitSuccess()
|
||||
.parseAs<JsonObject>()
|
||||
.let { obj ->
|
||||
track.total_chapters = obj["num_chapters"]!!.jsonPrimitive.int
|
||||
track.total_chapters = obj["num_chapters"]!!.jsonPrimitive.long
|
||||
obj.jsonObject["my_list_status"]?.jsonObject?.let {
|
||||
parseMangaItem(it, track)
|
||||
}
|
||||
@ -216,7 +216,7 @@ class MyAnimeListApi(
|
||||
|
||||
// Check next page if there's more
|
||||
if (!obj["paging"]!!.jsonObject["next"]?.jsonPrimitive?.contentOrNull.isNullOrBlank()) {
|
||||
matches + findListItems(query, offset + listPaginationAmount)
|
||||
matches + findListItems(query, offset + LIST_PAGINATION_AMOUNT)
|
||||
} else {
|
||||
matches
|
||||
}
|
||||
@ -225,9 +225,9 @@ class MyAnimeListApi(
|
||||
|
||||
private suspend fun getListPage(offset: Int): JsonObject {
|
||||
return withIOContext {
|
||||
val urlBuilder = "$baseApiUrl/users/@me/mangalist".toUri().buildUpon()
|
||||
val urlBuilder = "$BASE_API_URL/users/@me/mangalist".toUri().buildUpon()
|
||||
.appendQueryParameter("fields", "list_status{start_date,finish_date}")
|
||||
.appendQueryParameter("limit", listPaginationAmount.toString())
|
||||
.appendQueryParameter("limit", LIST_PAGINATION_AMOUNT.toString())
|
||||
if (offset > 0) {
|
||||
urlBuilder.appendQueryParameter("offset", offset.toString())
|
||||
}
|
||||
@ -249,8 +249,8 @@ class MyAnimeListApi(
|
||||
return track.apply {
|
||||
val isRereading = obj["is_rereading"]!!.jsonPrimitive.boolean
|
||||
status = if (isRereading) MyAnimeList.REREADING else getStatus(obj["status"]?.jsonPrimitive?.content)
|
||||
last_chapter_read = obj["num_chapters_read"]!!.jsonPrimitive.float
|
||||
score = obj["score"]!!.jsonPrimitive.int.toFloat()
|
||||
last_chapter_read = obj["num_chapters_read"]!!.jsonPrimitive.double
|
||||
score = obj["score"]!!.jsonPrimitive.int.toDouble()
|
||||
obj["start_date"]?.let {
|
||||
started_reading_date = parseDate(it.jsonPrimitive.content)
|
||||
}
|
||||
@ -277,30 +277,29 @@ class MyAnimeListApi(
|
||||
}
|
||||
|
||||
companion object {
|
||||
// Registered under arkon's MAL account
|
||||
private const val clientId = "f46004a9c16483b6d87b5bf10de56d97"
|
||||
private const val CLIENT_ID = "c46c9e24640a64dad5be5ca7a1a53a0f"
|
||||
|
||||
private const val baseOAuthUrl = "https://myanimelist.net/v1/oauth2"
|
||||
private const val baseApiUrl = "https://api.myanimelist.net/v2"
|
||||
private const val BASE_OAUTH_URL = "https://myanimelist.net/v1/oauth2"
|
||||
private const val BASE_API_URL = "https://api.myanimelist.net/v2"
|
||||
|
||||
private const val listPaginationAmount = 250
|
||||
private const val LIST_PAGINATION_AMOUNT = 250
|
||||
|
||||
private var codeVerifier: String = ""
|
||||
|
||||
fun authUrl(): Uri = "$baseOAuthUrl/authorize".toUri().buildUpon()
|
||||
.appendQueryParameter("client_id", clientId)
|
||||
fun authUrl(): Uri = "$BASE_OAUTH_URL/authorize".toUri().buildUpon()
|
||||
.appendQueryParameter("client_id", CLIENT_ID)
|
||||
.appendQueryParameter("code_challenge", getPkceChallengeCode())
|
||||
.appendQueryParameter("response_type", "code")
|
||||
.build()
|
||||
|
||||
fun mangaUrl(id: Long): Uri = "$baseApiUrl/manga".toUri().buildUpon()
|
||||
fun mangaUrl(id: Long): Uri = "$BASE_API_URL/manga".toUri().buildUpon()
|
||||
.appendPath(id.toString())
|
||||
.appendPath("my_list_status")
|
||||
.build()
|
||||
|
||||
fun refreshTokenRequest(oauth: OAuth): Request {
|
||||
val formBody: RequestBody = FormBody.Builder()
|
||||
.add("client_id", clientId)
|
||||
.add("client_id", CLIENT_ID)
|
||||
.add("refresh_token", oauth.refresh_token)
|
||||
.add("grant_type", "refresh_token")
|
||||
.build()
|
||||
@ -312,7 +311,7 @@ class MyAnimeListApi(
|
||||
.add("Authorization", "Bearer ${oauth.access_token}")
|
||||
.build()
|
||||
|
||||
return POST("$baseOAuthUrl/token", body = formBody, headers = headers)
|
||||
return POST("$BASE_OAUTH_URL/token", body = formBody, headers = headers)
|
||||
}
|
||||
|
||||
private fun getPkceChallengeCode(): String {
|
||||
|
@ -1,5 +1,6 @@
|
||||
package eu.kanade.tachiyomi.data.track.myanimelist
|
||||
|
||||
import eu.kanade.tachiyomi.BuildConfig
|
||||
import eu.kanade.tachiyomi.network.parseAs
|
||||
import kotlinx.serialization.json.Json
|
||||
import okhttp3.Interceptor
|
||||
@ -7,33 +8,32 @@ import okhttp3.Response
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.io.IOException
|
||||
|
||||
class MyAnimeListInterceptor(private val myanimelist: MyAnimeList, private var token: String?) : Interceptor {
|
||||
class MyAnimeListInterceptor(private val myanimelist: MyAnimeList) : Interceptor {
|
||||
|
||||
private val json: Json by injectLazy()
|
||||
|
||||
private var oauth: OAuth? = null
|
||||
private var oauth: OAuth? = myanimelist.loadOAuth()
|
||||
private val tokenExpired get() = myanimelist.getIfAuthExpired()
|
||||
|
||||
override fun intercept(chain: Interceptor.Chain): Response {
|
||||
if (tokenExpired) {
|
||||
throw MALTokenExpired()
|
||||
}
|
||||
val originalRequest = chain.request()
|
||||
|
||||
if (token.isNullOrEmpty()) {
|
||||
throw IOException("Not authenticated with MyAnimeList")
|
||||
}
|
||||
if (oauth == null) {
|
||||
oauth = myanimelist.loadOAuth()
|
||||
}
|
||||
// Refresh access token if expired
|
||||
if (oauth != null && oauth!!.isExpired()) {
|
||||
setAuth(refreshToken(chain))
|
||||
}
|
||||
|
||||
if (oauth == null) {
|
||||
throw IOException("No authentication token")
|
||||
throw IOException("MAL: User is not authenticated")
|
||||
}
|
||||
|
||||
// Add the authorization header to the original request
|
||||
val authRequest = originalRequest.newBuilder()
|
||||
.addHeader("Authorization", "Bearer ${oauth!!.access_token}")
|
||||
.header("User-Agent", "Mihon v${BuildConfig.VERSION_NAME} (${BuildConfig.APPLICATION_ID})")
|
||||
.build()
|
||||
|
||||
val response = chain.proceed(authRequest)
|
||||
@ -50,6 +50,7 @@ class MyAnimeListInterceptor(private val myanimelist: MyAnimeList, private var t
|
||||
|
||||
val newRequest = originalRequest.newBuilder()
|
||||
.addHeader("Authorization", "Bearer ${newToken.access_token}")
|
||||
.header("User-Agent", "Mihon v${BuildConfig.VERSION_NAME} (${BuildConfig.APPLICATION_ID})")
|
||||
.build()
|
||||
|
||||
return chain.proceed(newRequest)
|
||||
@ -63,15 +64,16 @@ class MyAnimeListInterceptor(private val myanimelist: MyAnimeList, private var t
|
||||
* and the oauth object.
|
||||
*/
|
||||
fun setAuth(oauth: OAuth?) {
|
||||
token = oauth?.access_token
|
||||
this.oauth = oauth
|
||||
myanimelist.saveOAuth(oauth)
|
||||
}
|
||||
|
||||
private fun refreshToken(chain: Interceptor.Chain): OAuth {
|
||||
val newOauth = runCatching {
|
||||
return runCatching {
|
||||
val oauthResponse = chain.proceed(MyAnimeListApi.refreshTokenRequest(oauth!!))
|
||||
|
||||
if (oauthResponse.code == 401) {
|
||||
myanimelist.setAuthExpired()
|
||||
}
|
||||
if (oauthResponse.isSuccessful) {
|
||||
with(json) { oauthResponse.parseAs<OAuth>() }
|
||||
} else {
|
||||
@ -79,11 +81,9 @@ class MyAnimeListInterceptor(private val myanimelist: MyAnimeList, private var t
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
if (newOauth.getOrNull() == null) {
|
||||
throw IOException("Failed to refresh the access token")
|
||||
}
|
||||
|
||||
return newOauth.getOrNull()!!
|
||||
.getOrNull()
|
||||
?: throw MALTokenExpired()
|
||||
}
|
||||
}
|
||||
|
||||
class MALTokenExpired : IOException("MAL: Login has expired")
|
||||
|
@ -18,12 +18,12 @@ import tachiyomi.domain.track.model.Track as DomainTrack
|
||||
class Shikimori(id: Long) : BaseTracker(id, "Shikimori"), DeletableTracker {
|
||||
|
||||
companion object {
|
||||
const val READING = 1
|
||||
const val COMPLETED = 2
|
||||
const val ON_HOLD = 3
|
||||
const val DROPPED = 4
|
||||
const val PLAN_TO_READ = 5
|
||||
const val REREADING = 6
|
||||
const val READING = 1L
|
||||
const val COMPLETED = 2L
|
||||
const val ON_HOLD = 3L
|
||||
const val DROPPED = 4L
|
||||
const val PLAN_TO_READ = 5L
|
||||
const val REREADING = 6L
|
||||
|
||||
private val SCORE_LIST = IntRange(0, 10)
|
||||
.map(Int::toString)
|
||||
@ -49,7 +49,7 @@ class Shikimori(id: Long) : BaseTracker(id, "Shikimori"), DeletableTracker {
|
||||
override suspend fun update(track: Track, didReadChapter: Boolean): Track {
|
||||
if (track.status != COMPLETED) {
|
||||
if (didReadChapter) {
|
||||
if (track.last_chapter_read.toInt() == track.total_chapters && track.total_chapters > 0) {
|
||||
if (track.last_chapter_read.toLong() == track.total_chapters && track.total_chapters > 0) {
|
||||
track.status = COMPLETED
|
||||
} else if (track.status != REREADING) {
|
||||
track.status = READING
|
||||
@ -72,14 +72,14 @@ class Shikimori(id: Long) : BaseTracker(id, "Shikimori"), DeletableTracker {
|
||||
|
||||
if (track.status != COMPLETED) {
|
||||
val isRereading = track.status == REREADING
|
||||
track.status = if (isRereading.not() && hasReadChapters) READING else track.status
|
||||
track.status = if (!isRereading && hasReadChapters) READING else track.status
|
||||
}
|
||||
|
||||
update(track)
|
||||
} else {
|
||||
// Set default fields if it's not found in the list
|
||||
track.status = if (hasReadChapters) READING else PLAN_TO_READ
|
||||
track.score = 0F
|
||||
track.score = 0.0
|
||||
add(track)
|
||||
}
|
||||
}
|
||||
@ -101,11 +101,11 @@ class Shikimori(id: Long) : BaseTracker(id, "Shikimori"), DeletableTracker {
|
||||
|
||||
override fun getLogoColor() = Color.rgb(40, 40, 40)
|
||||
|
||||
override fun getStatusList(): List<Int> {
|
||||
override fun getStatusList(): List<Long> {
|
||||
return listOf(READING, COMPLETED, ON_HOLD, DROPPED, PLAN_TO_READ, REREADING)
|
||||
}
|
||||
|
||||
override fun getStatus(status: Int): StringResource? = when (status) {
|
||||
override fun getStatus(status: Long): StringResource? = when (status) {
|
||||
READING -> MR.strings.reading
|
||||
PLAN_TO_READ -> MR.strings.plan_to_read
|
||||
COMPLETED -> MR.strings.completed
|
||||
@ -115,11 +115,11 @@ class Shikimori(id: Long) : BaseTracker(id, "Shikimori"), DeletableTracker {
|
||||
else -> null
|
||||
}
|
||||
|
||||
override fun getReadingStatus(): Int = READING
|
||||
override fun getReadingStatus(): Long = READING
|
||||
|
||||
override fun getRereadingStatus(): Int = REREADING
|
||||
override fun getRereadingStatus(): Long = REREADING
|
||||
|
||||
override fun getCompletionStatus(): Int = COMPLETED
|
||||
override fun getCompletionStatus(): Long = COMPLETED
|
||||
|
||||
override suspend fun login(username: String, password: String) = login(password)
|
||||
|
||||
|
@ -15,7 +15,7 @@ import kotlinx.serialization.json.JsonArray
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import kotlinx.serialization.json.buildJsonObject
|
||||
import kotlinx.serialization.json.contentOrNull
|
||||
import kotlinx.serialization.json.float
|
||||
import kotlinx.serialization.json.double
|
||||
import kotlinx.serialization.json.int
|
||||
import kotlinx.serialization.json.jsonObject
|
||||
import kotlinx.serialization.json.jsonPrimitive
|
||||
@ -102,10 +102,10 @@ class ShikimoriApi(
|
||||
return TrackSearch.create(trackId).apply {
|
||||
remote_id = obj["id"]!!.jsonPrimitive.long
|
||||
title = obj["name"]!!.jsonPrimitive.content
|
||||
total_chapters = obj["chapters"]!!.jsonPrimitive.int
|
||||
total_chapters = obj["chapters"]!!.jsonPrimitive.long
|
||||
cover_url = baseUrl + obj["image"]!!.jsonObject["preview"]!!.jsonPrimitive.content
|
||||
summary = ""
|
||||
score = obj["score"]!!.jsonPrimitive.float
|
||||
score = obj["score"]!!.jsonPrimitive.double
|
||||
tracking_url = baseUrl + obj["url"]!!.jsonPrimitive.content
|
||||
publishing_status = obj["status"]!!.jsonPrimitive.content
|
||||
publishing_type = obj["kind"]!!.jsonPrimitive.content
|
||||
@ -117,10 +117,10 @@ class ShikimoriApi(
|
||||
return Track.create(trackId).apply {
|
||||
title = mangas["name"]!!.jsonPrimitive.content
|
||||
remote_id = obj["id"]!!.jsonPrimitive.long
|
||||
total_chapters = mangas["chapters"]!!.jsonPrimitive.int
|
||||
total_chapters = mangas["chapters"]!!.jsonPrimitive.long
|
||||
library_id = obj["id"]!!.jsonPrimitive.long
|
||||
last_chapter_read = obj["chapters"]!!.jsonPrimitive.float
|
||||
score = (obj["score"]!!.jsonPrimitive.int).toFloat()
|
||||
last_chapter_read = obj["chapters"]!!.jsonPrimitive.double
|
||||
score = obj["score"]!!.jsonPrimitive.int.toDouble()
|
||||
status = toTrackStatus(obj["status"]!!.jsonPrimitive.content)
|
||||
tracking_url = baseUrl + mangas["url"]!!.jsonPrimitive.content
|
||||
}
|
||||
@ -192,8 +192,8 @@ class ShikimoriApi(
|
||||
)
|
||||
|
||||
companion object {
|
||||
private const val clientId = "1aaf4cf232372708e98b5abc813d795b539c5a916dbbfe9ac61bf02a360832cc"
|
||||
private const val clientSecret = "229942c742dd4cde803125d17d64501d91c0b12e14cb1e5120184d77d67024c0"
|
||||
private const val clientId = "PB9dq8DzI405s7wdtwTdirYqHiyVMh--djnP7lBUqSA"
|
||||
private const val clientSecret = "NajpZcOBKB9sJtgNcejf8OB9jBN1OYYoo-k4h2WWZus"
|
||||
|
||||
private const val baseUrl = "https://shikimori.one"
|
||||
private const val apiUrl = "$baseUrl/api"
|
||||
|
@ -1,5 +1,6 @@
|
||||
package eu.kanade.tachiyomi.data.track.shikimori
|
||||
|
||||
import eu.kanade.tachiyomi.BuildConfig
|
||||
import kotlinx.serialization.json.Json
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.Response
|
||||
@ -33,7 +34,7 @@ class ShikimoriInterceptor(private val shikimori: Shikimori) : Interceptor {
|
||||
// Add the authorization header to the original request.
|
||||
val authRequest = originalRequest.newBuilder()
|
||||
.addHeader("Authorization", "Bearer ${oauth!!.access_token}")
|
||||
.header("User-Agent", "Tachiyomi")
|
||||
.header("User-Agent", "Mihon v${BuildConfig.VERSION_NAME} (${BuildConfig.APPLICATION_ID})")
|
||||
.build()
|
||||
|
||||
return chain.proceed(authRequest)
|
||||
|
@ -23,25 +23,25 @@ class Suwayomi(id: Long) : BaseTracker(id, "Suwayomi"), EnhancedTracker {
|
||||
override fun getLogoColor() = Color.rgb(255, 35, 35) // TODO
|
||||
|
||||
companion object {
|
||||
const val UNREAD = 1
|
||||
const val READING = 2
|
||||
const val COMPLETED = 3
|
||||
const val UNREAD = 1L
|
||||
const val READING = 2L
|
||||
const val COMPLETED = 3L
|
||||
}
|
||||
|
||||
override fun getStatusList() = listOf(UNREAD, READING, COMPLETED)
|
||||
override fun getStatusList(): List<Long> = listOf(UNREAD, READING, COMPLETED)
|
||||
|
||||
override fun getStatus(status: Int): StringResource? = when (status) {
|
||||
override fun getStatus(status: Long): StringResource? = when (status) {
|
||||
UNREAD -> MR.strings.unread
|
||||
READING -> MR.strings.reading
|
||||
COMPLETED -> MR.strings.completed
|
||||
else -> null
|
||||
}
|
||||
|
||||
override fun getReadingStatus(): Int = READING
|
||||
override fun getReadingStatus(): Long = READING
|
||||
|
||||
override fun getRereadingStatus(): Int = -1
|
||||
override fun getRereadingStatus(): Long = -1
|
||||
|
||||
override fun getCompletionStatus(): Int = COMPLETED
|
||||
override fun getCompletionStatus(): Long = COMPLETED
|
||||
|
||||
override fun getScoreList(): ImmutableList<String> = persistentListOf()
|
||||
|
||||
@ -50,7 +50,7 @@ class Suwayomi(id: Long) : BaseTracker(id, "Suwayomi"), EnhancedTracker {
|
||||
override suspend fun update(track: Track, didReadChapter: Boolean): Track {
|
||||
if (track.status != COMPLETED) {
|
||||
if (didReadChapter) {
|
||||
if (track.last_chapter_read.toInt() == track.total_chapters && track.total_chapters > 0) {
|
||||
if (track.last_chapter_read.toLong() == track.total_chapters && track.total_chapters > 0) {
|
||||
track.status = COMPLETED
|
||||
} else {
|
||||
track.status = READING
|
||||
|
@ -66,9 +66,9 @@ class SuwayomiApi(private val trackId: Long) {
|
||||
cover_url = "$url/thumbnail"
|
||||
summary = manga.description.orEmpty()
|
||||
tracking_url = url
|
||||
total_chapters = manga.chapterCount.toInt()
|
||||
total_chapters = manga.chapterCount
|
||||
publishing_status = manga.status
|
||||
last_chapter_read = manga.lastChapterRead?.chapterNumber ?: 0F
|
||||
last_chapter_read = manga.lastChapterRead?.chapterNumber ?: 0.0
|
||||
status = when (manga.unreadCount) {
|
||||
manga.chapterCount -> Suwayomi.UNREAD
|
||||
0L -> Suwayomi.COMPLETED
|
||||
|
@ -64,7 +64,7 @@ data class ChapterDataClass(
|
||||
val url: String,
|
||||
val name: String,
|
||||
val uploadDate: Long,
|
||||
val chapterNumber: Float,
|
||||
val chapterNumber: Double,
|
||||
val scanlator: String?,
|
||||
val mangaId: Int,
|
||||
|
||||
|
@ -338,6 +338,10 @@ class ExtensionManager(
|
||||
}
|
||||
|
||||
override fun onExtensionUntrusted(extension: Extension.Untrusted) {
|
||||
val installedExtension = _installedExtensionsFlow.value
|
||||
.find { it.pkgName == extension.pkgName }
|
||||
?: return
|
||||
_installedExtensionsFlow.value -= installedExtension
|
||||
_untrustedExtensionsFlow.value += extension
|
||||
}
|
||||
|
||||
|
@ -70,8 +70,7 @@ internal class ExtensionInstallReceiver(private val listener: Listener) :
|
||||
launchNow {
|
||||
when (val result = getExtensionFromIntent(context, intent)) {
|
||||
is LoadResult.Success -> listener.onExtensionUpdated(result.extension)
|
||||
// Not needed as a package can't be upgraded if the signature is different
|
||||
// is LoadResult.Untrusted -> {}
|
||||
is LoadResult.Untrusted -> listener.onExtensionUntrusted(result.extension)
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
@ -285,13 +285,13 @@ private data class TrackStatusSelectorScreen(
|
||||
private class Model(
|
||||
private val track: Track,
|
||||
private val tracker: Tracker,
|
||||
) : StateScreenModel<Model.State>(State(track.status.toInt())) {
|
||||
) : StateScreenModel<Model.State>(State(track.status)) {
|
||||
|
||||
fun getSelections(): Map<Int, StringResource?> {
|
||||
fun getSelections(): Map<Long, StringResource?> {
|
||||
return tracker.getStatusList().associateWith { tracker.getStatus(it) }
|
||||
}
|
||||
|
||||
fun setSelection(selection: Int) {
|
||||
fun setSelection(selection: Long) {
|
||||
mutableState.update { it.copy(selection = selection) }
|
||||
}
|
||||
|
||||
@ -303,7 +303,7 @@ private data class TrackStatusSelectorScreen(
|
||||
|
||||
@Immutable
|
||||
data class State(
|
||||
val selection: Int,
|
||||
val selection: Long,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -90,7 +90,7 @@ abstract class PagerViewer(val activity: ReaderActivity) : Viewer {
|
||||
pager.addOnPageChangeListener(
|
||||
object : ViewPager.SimpleOnPageChangeListener() {
|
||||
override fun onPageSelected(position: Int) {
|
||||
if (activity.isScrollingThroughPages.not()) {
|
||||
if (!activity.isScrollingThroughPages) {
|
||||
activity.hideMenu()
|
||||
}
|
||||
onPageChange(position)
|
||||
|
@ -21,7 +21,7 @@ class CrashLogUtil(
|
||||
|
||||
suspend fun dumpLogs() = withNonCancellableContext {
|
||||
try {
|
||||
val file = context.createFileInCacheDir("tachiyomi_crash_logs.txt")
|
||||
val file = context.createFileInCacheDir("mihon_crash_logs.txt")
|
||||
|
||||
file.appendText(getDebugInfo() + "\n\n")
|
||||
getExtensionsInfo()?.let { file.appendText("$it\n\n") }
|
||||
|
@ -18,10 +18,10 @@ data class DummyTracker(
|
||||
override val isLoggedIn: Boolean = false,
|
||||
val valLogoColor: Int = Color.rgb(18, 25, 35),
|
||||
val valLogo: Int = R.drawable.ic_tracker_anilist,
|
||||
val valStatuses: List<Int> = (1..6).toList(),
|
||||
val valReadingStatus: Int = 1,
|
||||
val valRereadingStatus: Int = 1,
|
||||
val valCompletionStatus: Int = 2,
|
||||
val valStatuses: List<Long> = (1L..6L).toList(),
|
||||
val valReadingStatus: Long = 1L,
|
||||
val valRereadingStatus: Long = 1L,
|
||||
val valCompletionStatus: Long = 2L,
|
||||
val valScoreList: ImmutableList<String> = (0..10).map(Int::toString).toImmutableList(),
|
||||
val val10PointScore: Double = 5.4,
|
||||
val valSearchResults: List<TrackSearch> = listOf(),
|
||||
@ -34,29 +34,29 @@ data class DummyTracker(
|
||||
|
||||
override fun getLogo(): Int = valLogo
|
||||
|
||||
override fun getStatusList(): List<Int> = valStatuses
|
||||
override fun getStatusList(): List<Long> = valStatuses
|
||||
|
||||
override fun getStatus(status: Int): StringResource? = when (status) {
|
||||
1 -> MR.strings.reading
|
||||
2 -> MR.strings.plan_to_read
|
||||
3 -> MR.strings.completed
|
||||
4 -> MR.strings.on_hold
|
||||
5 -> MR.strings.dropped
|
||||
6 -> MR.strings.repeating
|
||||
override fun getStatus(status: Long): StringResource? = when (status) {
|
||||
1L -> MR.strings.reading
|
||||
2L -> MR.strings.plan_to_read
|
||||
3L -> MR.strings.completed
|
||||
4L -> MR.strings.on_hold
|
||||
5L -> MR.strings.dropped
|
||||
6L -> MR.strings.repeating
|
||||
else -> null
|
||||
}
|
||||
|
||||
override fun getReadingStatus(): Int = valReadingStatus
|
||||
override fun getReadingStatus(): Long = valReadingStatus
|
||||
|
||||
override fun getRereadingStatus(): Int = valRereadingStatus
|
||||
override fun getRereadingStatus(): Long = valRereadingStatus
|
||||
|
||||
override fun getCompletionStatus(): Int = valCompletionStatus
|
||||
override fun getCompletionStatus(): Long = valCompletionStatus
|
||||
|
||||
override fun getScoreList(): ImmutableList<String> = valScoreList
|
||||
|
||||
override fun get10PointScore(track: Track): Double = val10PointScore
|
||||
|
||||
override fun indexToScore(index: Int): Float = getScoreList()[index].toFloat()
|
||||
override fun indexToScore(index: Int): Double = getScoreList()[index].toDouble()
|
||||
|
||||
override fun displayScore(track: Track): String =
|
||||
track.score.toString()
|
||||
@ -94,7 +94,7 @@ data class DummyTracker(
|
||||
|
||||
override suspend fun setRemoteStatus(
|
||||
track: eu.kanade.tachiyomi.data.database.models.Track,
|
||||
status: Int,
|
||||
status: Long,
|
||||
) = Unit
|
||||
|
||||
override suspend fun setRemoteLastChapterRead(
|
||||
|
@ -3,22 +3,17 @@
|
||||
android:height="108dp"
|
||||
android:viewportWidth="432"
|
||||
android:viewportHeight="432">
|
||||
<group android:scaleX="0.67"
|
||||
android:scaleY="0.67"
|
||||
android:translateX="71.28"
|
||||
android:translateY="71.28">
|
||||
<group>
|
||||
<clip-path
|
||||
android:pathData="M0,0h432v432h-432z"/>
|
||||
<path
|
||||
android:pathData="M0,0h432v432h-432z"
|
||||
android:fillColor="#FAFAFA"/>
|
||||
<path
|
||||
android:pathData="M377,216C377,302.16 304.92,372 216,372C127.08,372 55,302.16 55,216C55,129.84 127.08,60 216,60C304.92,60 377,129.84 377,216Z"
|
||||
android:pathData="M322.13,215.5C322.13,272.66 274.64,319 216.07,319C157.49,319 110,272.66 110,215.5C110,158.34 157.49,112 216.07,112C274.64,112 322.13,158.34 322.13,215.5Z"
|
||||
android:fillColor="#F2FAFF"/>
|
||||
<path
|
||||
android:pathData="M216,342.75C288.25,342.75 346.81,286 346.81,216C346.81,146 288.25,89.25 216,89.25C143.75,89.25 85.19,146 85.19,216C85.19,286 143.75,342.75 216,342.75ZM216,372C304.92,372 377,302.16 377,216C377,129.84 304.92,60 216,60C127.08,60 55,129.84 55,216C55,302.16 127.08,372 216,372Z"
|
||||
android:fillColor="#0058A0"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M216,342.75C288.25,342.75 346.81,286 346.81,216C346.81,146 288.25,89.25 216,89.25C143.75,89.25 85.19,146 85.19,216C85.19,286 143.75,342.75 216,342.75ZM216,372C304.92,372 377,302.16 377,216C377,129.84 304.92,60 216,60C127.08,60 55,129.84 55,216C55,302.16 127.08,372 216,372Z"
|
||||
android:pathData="M216.07,299.59C263.66,299.59 302.24,261.94 302.24,215.5C302.24,169.06 263.66,131.41 216.07,131.41C168.47,131.41 129.89,169.06 129.89,215.5C129.89,261.94 168.47,299.59 216.07,299.59ZM216.07,319C274.64,319 322.13,272.66 322.13,215.5C322.13,158.34 274.64,112 216.07,112C157.49,112 110,158.34 110,215.5C110,272.66 157.49,319 216.07,319Z"
|
||||
android:fillColor="#0058A0"
|
||||
android:fillType="evenOdd"/>
|
||||
</group>
|
||||
|
@ -3,12 +3,7 @@
|
||||
android:height="108dp"
|
||||
android:viewportWidth="432"
|
||||
android:viewportHeight="432">
|
||||
<group android:scaleX="0.67"
|
||||
android:scaleY="0.67"
|
||||
android:translateX="71.28"
|
||||
android:translateY="71.28">
|
||||
<path
|
||||
android:pathData="M162.3,173.59L161.22,148.63C164.47,149.25 168.35,149.41 177.18,149.41C187.87,149.41 201.98,148.79 209.26,147.86C212.36,147.55 213.6,147.09 215.61,146L232.35,160.26C230.8,162.43 230.34,163.36 228.63,167.7C227.24,171.11 220.88,190.79 218.4,199.16C229.87,201.48 236.22,203.18 244.9,206.75C245.99,199.16 246.14,195.13 246.14,181.33C246.14,177.77 245.99,175.76 245.52,172.5L272.49,173.43C271.71,177.15 271.56,178.7 271.4,184.74C270.78,199.31 270.16,206.29 268.61,216.82C279.31,222.25 279.31,222.25 284.73,225.19C287.52,226.74 288.14,227.05 290,227.67L281.01,256.65C276.67,252.78 270.63,248.59 261.8,243.63C254.05,262.08 241.18,275.56 221.66,286.25C215.15,277.57 210.19,272.3 202.29,266.11C213.75,260.68 219.02,257.27 225.07,251.54C230.96,245.8 234.83,240.22 238.55,231.85C228.63,227.36 222.28,225.35 211.27,223.02C204.92,241.93 199.8,254.02 195.31,261.3C189.27,271.06 181.05,276.18 171.6,276.18C164.32,276.18 156.88,272.92 151.45,267.35C145.25,260.99 142,252.16 142,241.93C142,226.74 149.28,213.57 161.99,205.35C170.21,200.09 178.88,197.76 192.68,196.99C195.47,187.84 197.79,179.94 199.96,171.11C193.14,171.73 184.62,172.19 174.24,172.65C168.66,172.81 166.8,172.96 162.3,173.59ZM185.86,220.7C178.57,221.94 174.24,224.26 170.36,229.22C167.42,232.63 166.02,236.66 166.02,241C166.02,245.8 168.35,249.37 171.29,249.37C174.85,249.37 178.88,241.31 185.86,220.7Z"
|
||||
android:fillColor="#031019"/>
|
||||
</group>
|
||||
<path
|
||||
android:pathData="M182.03,188.7L181.33,172.69C183.42,173.09 185.91,173.19 191.57,173.19C198.44,173.19 207.49,172.79 212.16,172.19C214.15,171.99 214.95,171.7 216.24,171L226.98,180.15C225.98,181.54 225.68,182.14 224.59,184.92C223.7,187.11 219.62,199.74 218.03,205.11C225.39,206.6 229.46,207.7 235.03,209.98C235.73,205.11 235.83,202.52 235.83,193.67C235.83,191.39 235.73,190.09 235.43,188.01L252.74,188.6C252.24,190.99 252.14,191.98 252.04,195.86C251.64,205.21 251.24,209.68 250.25,216.45C257.11,219.93 257.11,219.93 260.59,221.82C262.38,222.81 262.78,223.01 263.97,223.41L258.2,242.01C255.42,239.52 251.54,236.83 245.87,233.65C240.9,245.49 232.65,254.14 220.12,261C215.94,255.43 212.76,252.05 207.68,248.07C215.04,244.59 218.43,242.4 222.3,238.72C226.08,235.04 228.57,231.46 230.96,226.09C224.59,223.21 220.51,221.92 213.45,220.43C209.38,232.56 206.09,240.32 203.21,244.99C199.33,251.25 194.06,254.54 187.99,254.54C183.32,254.54 178.55,252.45 175.07,248.87C171.09,244.79 169,239.12 169,232.56C169,222.81 173.67,214.36 181.83,209.09C187.1,205.71 192.67,204.21 201.52,203.72C203.31,197.85 204.8,192.78 206.19,187.11C201.82,187.51 196.35,187.81 189.68,188.1C186.1,188.2 184.91,188.3 182.03,188.7ZM197.14,218.93C192.47,219.73 189.68,221.22 187.2,224.4C185.31,226.59 184.41,229.18 184.41,231.96C184.41,235.04 185.91,237.33 187.8,237.33C190.08,237.33 192.67,232.16 197.14,218.93Z"
|
||||
android:fillColor="#031019"/>
|
||||
</vector>
|
||||
|
@ -3,4 +3,4 @@
|
||||
<background android:drawable="@drawable/ic_launcher_background"/>
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
<monochrome android:drawable="@drawable/ic_launcher_monochrome"/>
|
||||
</adaptive-icon>
|
||||
</adaptive-icon>
|
||||
|
@ -14,7 +14,7 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":source-api"))
|
||||
implementation(projects.sourceApi)
|
||||
|
||||
implementation(kotlinx.bundles.serialization)
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":i18n"))
|
||||
implementation(projects.i18n)
|
||||
|
||||
api(libs.logcat)
|
||||
|
||||
|
@ -89,7 +89,7 @@ internal class RateLimitInterceptor(
|
||||
while (requestQueue.size >= permits) { // queue is full, remove expired entries
|
||||
val periodStart = SystemClock.elapsedRealtime() - rateLimitMillis
|
||||
var hasRemovedExpired = false
|
||||
while (requestQueue.isEmpty().not() && requestQueue.first <= periodStart) {
|
||||
while (!requestQueue.isEmpty() && requestQueue.first <= periodStart) {
|
||||
requestQueue.removeFirst()
|
||||
hasRemovedExpired = true
|
||||
}
|
||||
|
@ -24,9 +24,9 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":source-api"))
|
||||
implementation(project(":domain"))
|
||||
implementation(project(":core"))
|
||||
implementation(projects.sourceApi)
|
||||
implementation(projects.domain)
|
||||
implementation(projects.core)
|
||||
|
||||
api(libs.bundles.sqldelight)
|
||||
}
|
||||
|
@ -75,7 +75,7 @@ WHERE _id IN :chapterIds;
|
||||
|
||||
insert:
|
||||
INSERT INTO chapters(manga_id, url, name, scanlator, read, bookmark, last_page_read, chapter_number, source_order, date_fetch, date_upload, last_modified_at)
|
||||
VALUES (:mangaId, :url, :name, :scanlator, :read, :bookmark, :lastPageRead, :chapterNumber, :sourceOrder, :dateFetch, :dateUpload, strftime('%s', 'now'));
|
||||
VALUES (:mangaId, :url, :name, :scanlator, :read, :bookmark, :lastPageRead, :chapterNumber, :sourceOrder, :dateFetch, :dateUpload, 0);
|
||||
|
||||
update:
|
||||
UPDATE chapters
|
||||
|
@ -117,7 +117,7 @@ AND source IN :sourceIds;
|
||||
|
||||
insert:
|
||||
INSERT INTO mangas(source, url, artist, author, description, genre, title, status, thumbnail_url, favorite, last_update, next_update, initialized, viewer, chapter_flags, cover_last_modified, date_added, update_strategy, calculate_interval, last_modified_at)
|
||||
VALUES (:source, :url, :artist, :author, :description, :genre, :title, :status, :thumbnailUrl, :favorite, :lastUpdate, :nextUpdate, :initialized, :viewerFlags, :chapterFlags, :coverLastModified, :dateAdded, :updateStrategy, :calculateInterval, strftime('%s', 'now'));
|
||||
VALUES (:source, :url, :artist, :author, :description, :genre, :title, :status, :thumbnailUrl, :favorite, :lastUpdate, :nextUpdate, :initialized, :viewerFlags, :chapterFlags, :coverLastModified, :dateAdded, :updateStrategy, :calculateInterval, 0);
|
||||
|
||||
update:
|
||||
UPDATE mangas SET
|
||||
|
@ -20,7 +20,7 @@ END;
|
||||
|
||||
insert:
|
||||
INSERT INTO mangas_categories(manga_id, category_id, last_modified_at)
|
||||
VALUES (:mangaId, :categoryId, strftime('%s', 'now'));
|
||||
VALUES (:mangaId, :categoryId, 0);
|
||||
|
||||
deleteMangaCategoryByMangaId:
|
||||
DELETE FROM mangas_categories
|
||||
|
4
data/src/main/sqldelight/tachiyomi/migrations/1.sqm
Normal file
4
data/src/main/sqldelight/tachiyomi/migrations/1.sqm
Normal file
@ -0,0 +1,4 @@
|
||||
-- MangaUpdates score fixing --
|
||||
UPDATE manga_sync
|
||||
SET score = max(score, 0)
|
||||
WHERE sync_id = 7;
|
@ -14,8 +14,8 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":source-api"))
|
||||
implementation(project(":core"))
|
||||
implementation(projects.sourceApi)
|
||||
implementation(projects.core)
|
||||
|
||||
implementation(platform(kotlinx.coroutines.bom))
|
||||
implementation(kotlinx.bundles.coroutines)
|
||||
|
@ -20,7 +20,7 @@ class GetApplicationRelease(
|
||||
val now = Instant.now()
|
||||
|
||||
// Limit checks to once every 3 days at most
|
||||
if (arguments.forceCheck.not() && now.isBefore(
|
||||
if (!arguments.forceCheck && now.isBefore(
|
||||
Instant.ofEpochMilli(lastChecked.get()).plus(3, ChronoUnit.DAYS),
|
||||
)
|
||||
) {
|
||||
|
@ -22,7 +22,7 @@ class StubSource(
|
||||
throw SourceNotInstalledException()
|
||||
|
||||
override fun toString(): String =
|
||||
if (isInvalid.not()) "$name (${lang.uppercase()})" else id.toString()
|
||||
if (!isInvalid) "$name (${lang.uppercase()})" else id.toString()
|
||||
|
||||
companion object {
|
||||
fun from(source: Source): StubSource {
|
||||
|
@ -128,4 +128,12 @@
|
||||
<item quantity="many">%d يومًا</item>
|
||||
<item quantity="other">%d يوم</item>
|
||||
</plurals>
|
||||
<plurals name="num_repos">
|
||||
<item quantity="zero">%d</item>
|
||||
<item quantity="one">مستودع</item>
|
||||
<item quantity="two">مستودعان</item>
|
||||
<item quantity="few">%d مستودعات</item>
|
||||
<item quantity="many">%d مستودعات</item>
|
||||
<item quantity="other"></item>
|
||||
</plurals>
|
||||
</resources>
|
@ -232,7 +232,7 @@
|
||||
<string name="label_extensions">الإضافات</string>
|
||||
<string name="label_extension_info">معلومات الإضافة</string>
|
||||
<string name="action_display_download_badge">الفصول المحملة</string>
|
||||
<string name="ext_update">تحديث</string>
|
||||
<string name="ext_update">حدث</string>
|
||||
<string name="ext_install">تثبيت</string>
|
||||
<string name="ext_pending">المُعلقة</string>
|
||||
<string name="ext_downloading">جارى التنزيل</string>
|
||||
@ -242,11 +242,9 @@
|
||||
<string name="ext_untrusted">غير موثوق فيه</string>
|
||||
<string name="ext_uninstall">إلغاء التثبيت</string>
|
||||
<string name="untrusted_extension">إضافة ذات ريبة</string>
|
||||
<string name="untrusted_extension_message">هذه اﻹضافة موقَّعة بشهادة ذات ريبة ولم تفعَّل.
|
||||
<string name="untrusted_extension_message">يمكن للملحقات الخبيثة قراءة أي بيانات اعتماد مخزنة لتسجيل الدخول أو تنفيذ تعليمات برمجية عشوائية.
|
||||
\n
|
||||
\nيمكن لأي إضافة خبيثة قراءة بيانات اعتماد تسجيل الدخول المخزَّنة أو تنفيذ تعليمات برمجية عشوائية.
|
||||
\n
|
||||
\nأنت تقبل هذه المخاطر إن وثقت بالشهادة.</string>
|
||||
\nمن خلال الوثوق بهذا الامتداد، فإنك تقبل هذه المخاطر.</string>
|
||||
<string name="pref_double_tap_anim_speed">سرعة مؤثر النقر المزدوج</string>
|
||||
<string name="pager_viewer">عارض الصفحات</string>
|
||||
<string name="double_tap_anim_speed_0">لا مؤثرات</string>
|
||||
@ -372,7 +370,7 @@
|
||||
<string name="label_data">البيانات</string>
|
||||
<string name="backup_restore_missing_sources">المصادر المفقودة:</string>
|
||||
<string name="invalid_backup_file_missing_manga">النسخة الإحتياطية لا تحتوي على أيّة إدخالات المكتبة.</string>
|
||||
<string name="invalid_backup_file">ملفُّ النسخ الاحتياطيِّ غير صالح</string>
|
||||
<string name="invalid_backup_file">ملف النسخ الاحتياطي غير صالح:</string>
|
||||
<string name="tracking_info">مزامنة أحادية تُحدِّث قراءة الفصول في المتتبعات الخارجية، ولك تعيين التتبِّع لكلِّ مدخلة على حدى، وذلك من زرِّ التتبع فيهم.</string>
|
||||
<string name="pref_library_update_refresh_metadata_summary">تحقق من وجود غلاف جديد وتفاصيل جديدة عند تحديث المكتبة</string>
|
||||
<string name="pref_library_update_refresh_metadata">تحديث البيانات الوصفية تلقائياً</string>
|
||||
@ -456,9 +454,7 @@
|
||||
<string name="action_order_by_upload_date">حسب تاريخ الرفع</string>
|
||||
<string name="action_order_by_chapter_number">حسب رقم الفصل</string>
|
||||
<string name="pref_dns_over_https">إستخدام DNS عبر HTTPS (DoH)</string>
|
||||
<string name="backup_restore_content_full">تم استيراد البيانات من ملف نسخ الاحتياطي.
|
||||
\n
|
||||
\nيتوجب تثبيت الإضافات المفقودة و تسجيل الدخول إلى منصات التعقب تالياً لاستعمالهم.</string>
|
||||
<string name="backup_restore_content_full">قد تحتاج إلى تثبيت أي إضافات مفقودة وتسجيل الدخول إلى خدمات التتبع بعد ذلك لاستخدامها.</string>
|
||||
<string name="nav_zone_right">يمين</string>
|
||||
<string name="nav_zone_left">يسار</string>
|
||||
<string name="nav_zone_next">التالي</string>
|
||||
@ -562,7 +558,7 @@
|
||||
<string name="clear_database_source_item_count">%1$d مدخلةً في قاعدة البيانات وليست في المكتبة</string>
|
||||
<string name="extension_api_error">فشل الحصول على قائمة الملحقات</string>
|
||||
<string name="ext_installer_shizuku_unavailable_dialog">ثبِّت «شيزوكو» وشغِّله لتستخدمه مثبِّت إضافات.</string>
|
||||
<string name="download_queue_size_warning">تحذير: يمكن أن تؤدِّي التنزيلات كبيرة الحجم والعدد إلى إبطاء المصادر، وقد يُحظر Tachiyomi منها بسبب ذلك. اضغط لمعرفة المزيد۔</string>
|
||||
<string name="download_queue_size_warning">تحذير: يمكن أن تؤدِّي التنزيلات كبيرة الحجم والعدد إلى إبطاء المصادر، وقد يُحظر Mihon منها بسبب ذلك. اضغط لمعرفة المزيد۔</string>
|
||||
<string name="action_show_manga">إظهار الدخول</string>
|
||||
<string name="action_display_cover_only_grid">شبكة بالاغلفة</string>
|
||||
<string name="skipped_reason_not_started">تُخُطِّيت بسبب عدم وجود فصول قُرئت</string>
|
||||
@ -773,4 +769,33 @@
|
||||
\nوالأحسن أن يكون المجلَّد مخصوصًا لذلك.
|
||||
\n
|
||||
\nالمجلَّد المحدَّد: %2$s</string>
|
||||
<string name="onboarding_permission_install_apps">إذن تثبيت التطبيقات</string>
|
||||
<string name="onboarding_permission_install_apps_description">لتثبيت ملحقات المصدر.</string>
|
||||
<string name="onboarding_permission_notifications">إذن الإشعار</string>
|
||||
<string name="onboarding_permission_notifications_description">احصل على تنبيهات لتحديثات المكتبة والمزيد.</string>
|
||||
<string name="onboarding_permission_ignore_battery_opts">استخدام البطارية في الخلفية</string>
|
||||
<string name="onboarding_permission_action_grant">منح</string>
|
||||
<string name="ext_permission_install_apps_warning">الأذونات مطلوبة لتثبيت الإضافات. انقر هنا لمنحها.</string>
|
||||
<string name="ext_revoke_trust">إبطال الإضافات غير المعروفة الموثوق بها</string>
|
||||
<string name="label_extension_repos">مستودع الإضافات</string>
|
||||
<string name="label_add_repo_input">رابط المستودع</string>
|
||||
<string name="action_add_repo_message">إضافة مستودعات إضافية إلى ميهون. يجب أن يكون هذا الرابط ينتهي بـ \"index.min.json\".</string>
|
||||
<string name="error_repo_exists">هذا المستودع موجود بالفعل!</string>
|
||||
<string name="action_delete_repo">حذف المستودع</string>
|
||||
<string name="invalid_repo_name">رابط المستودع غير صالح</string>
|
||||
<string name="delete_repo_confirmation">هل ترغب في حذف \"1%s\" من المستودع؟</string>
|
||||
<string name="action_open_repo">مستودع مفتوح المصدر</string>
|
||||
<string name="invalid_backup_file_error">الخطأ الكامل:</string>
|
||||
<string name="private_settings">تضمين إعدادات حساسة (على سبيل المثال، رموز تسجيل دخول المتتبع)</string>
|
||||
<string name="manga_interval_expected_update_soon">قريباً</string>
|
||||
<string name="manga_interval_custom_amount">تكرار تحديث مخصص:</string>
|
||||
<string name="onboarding_storage_help_info">هل تقوم بالتحديث من إصدار أقدم ولست متأكداً مما تختاره؟ ارجع إلى دليل التخزين لمزيد من المعلومات.</string>
|
||||
<string name="onboarding_storage_help_action">دليل التخزين</string>
|
||||
<string name="onboarding_permission_ignore_battery_opts_description">تجنّب الانقطاعات في تحديثات المكتبة الطويلة الأمد والتنزيلات واستعادة النسخ الاحتياطية.</string>
|
||||
<string name="pref_library_update_smart_update">تحديث ذكي</string>
|
||||
<string name="theme_nord">Nord</string>
|
||||
<string name="information_empty_repos">لم يتم تعيين أي مستودع .</string>
|
||||
<string name="action_add_repo">إضافة مستودع</string>
|
||||
<string name="manga_interval_expected_update">من المتوقع أن يتم إصدار فصول جديدة في حوالي 1%1$s، والتحقق من كل 2%2$s .</string>
|
||||
<string name="available_disk_space_info">متاح: %1$s / الكل: %2$s</string>
|
||||
</resources>
|
@ -187,7 +187,7 @@
|
||||
<string name="obsolete_extension_message">Гэта пашырэнне больш недаступна.</string>
|
||||
<string name="untrusted_extension_message">Гэта пашырэнне было падпісана ненадзейным сертыфікатам і не было актывавана.
|
||||
\n
|
||||
\nШкоднаснае пашырэнне можа счытваць любыя ўліковыя дадзеныя для ўваходу, якія захоўваюцца ў Tachiyomi, або выконваць адвольны код.
|
||||
\nШкоднаснае пашырэнне можа счытваць любыя ўліковыя дадзеныя для ўваходу, якія захоўваюцца ў Mihon, або выконваць адвольны код.
|
||||
\n
|
||||
\nДавяраючы гэтаму сертыфікату, вы прымаеце на сябе гэтыя рызыкі.</string>
|
||||
<string name="untrusted_extension">Ненадзейнае пашырэнне</string>
|
||||
|
@ -250,7 +250,7 @@
|
||||
<string name="ext_app_info">Impormasyon sa app</string>
|
||||
<string name="untrusted_extension_message">Kini nga extension gipirmahan gamit ang dili kasaligan nga sertipiko ug wala gi-aktibo.
|
||||
\n
|
||||
\nAng usa ka malisyoso nga extension mahimong makabasa sa bisan unsang mga kredensyal sa pag-login nga gitipigan sa Tachiyomi o ipatuman ang arbitraryong code.
|
||||
\nAng usa ka malisyoso nga extension mahimong makabasa sa bisan unsang mga kredensyal sa pag-login nga gitipigan sa Mihon o ipatuman ang arbitraryong code.
|
||||
\n
|
||||
\nPinaagi sa pagsalig niini nga sertipiko gidawat nimo kini nga mga risgo.</string>
|
||||
<string name="pref_cutout_short">Ipakita ang sulod sa ginunting nga lugar</string>
|
||||
@ -443,6 +443,6 @@
|
||||
<string name="label_local">Lokal</string>
|
||||
<string name="pref_appearance_summary">Tema, format sa petsa ug panahon</string>
|
||||
<string name="action_not_now">Dili karon</string>
|
||||
<string name="information_webview_required">Gikinahanlan ang WebView alang sa Tachiyomi</string>
|
||||
<string name="information_webview_required">Gikinahanlan ang WebView alang sa Mihon</string>
|
||||
<string name="information_required_plain">*gikinahanlan</string>
|
||||
</resources>
|
@ -29,12 +29,12 @@
|
||||
<item quantity="other">Ҫӗнӗ сыпӑксем %d хайлав валли тупӑннӑ</item>
|
||||
</plurals>
|
||||
<plurals name="manga_num_chapters">
|
||||
<item quantity="one">1 сыпӑк</item>
|
||||
<item quantity="one">%1$s сыпӑк</item>
|
||||
<item quantity="other">%1$s сыпӑк</item>
|
||||
</plurals>
|
||||
<plurals name="download_queue_summary">
|
||||
<item quantity="one">1 юлчӗ</item>
|
||||
<item quantity="other">%1$s юлчӗ</item>
|
||||
<item quantity="one">%1$s йулчӗ</item>
|
||||
<item quantity="other">%1$s йулчӗ</item>
|
||||
</plurals>
|
||||
<plurals name="num_trackers">
|
||||
<item quantity="one">1 сӑнану</item>
|
||||
@ -48,4 +48,20 @@
|
||||
<item quantity="one">Ӗнер</item>
|
||||
<item quantity="other">%1$d кун кайалла</item>
|
||||
</plurals>
|
||||
<plurals name="next_unread_chapters">
|
||||
<item quantity="one">Тепӗр вуламан сыпӑк</item>
|
||||
<item quantity="other">Тепӗр %d вуламан сыпӑк</item>
|
||||
</plurals>
|
||||
<plurals name="day">
|
||||
<item quantity="one">1 кун</item>
|
||||
<item quantity="other">%d кун</item>
|
||||
</plurals>
|
||||
<plurals name="missing_chapters">
|
||||
<item quantity="one">%1$s сыпӑк ҫук</item>
|
||||
<item quantity="other">%1$s сыпӑк ҫук</item>
|
||||
</plurals>
|
||||
<plurals name="download_amount">
|
||||
<item quantity="one">Тепӗр сыпӑк</item>
|
||||
<item quantity="other">Тепӗр %d сыпӑк</item>
|
||||
</plurals>
|
||||
</resources>
|
@ -261,7 +261,7 @@
|
||||
<string name="download_notifier_download_paused">Тийеве вӑхӑтлӑха чарнӑ</string>
|
||||
<string name="download_notifier_unknown_error">Кӗтмен йӑнӑша пула сыпӑксене тийесе илеймест</string>
|
||||
<string name="download_notifier_downloader_title">Тийевҫӗ</string>
|
||||
<string name="information_webview_required">Tachiyomi валли WebView кирлӗ</string>
|
||||
<string name="information_webview_required">Mihon валли WebView кирлӗ</string>
|
||||
<string name="update_check_notification_download_in_progress">Тийев…</string>
|
||||
<string name="update_check_no_new_updates">Ҫӗнетӳсем тупӑнман</string>
|
||||
<string name="update_check_confirm">Тиесе ил</string>
|
||||
|
@ -128,16 +128,16 @@
|
||||
<string name="fifth_to_last">Ab fünftletzt gelesenem Kapitel</string>
|
||||
<string name="pref_download_new">Neue Kapitel herunterladen</string>
|
||||
<string name="services">Tracker</string>
|
||||
<string name="pref_create_backup">Sicherung erstellen</string>
|
||||
<string name="pref_create_backup">Datensicherung erstellen</string>
|
||||
<string name="pref_create_backup_summ">Kann benutzt werden, um die aktuelle Bibliothek wiederherzustellen</string>
|
||||
<string name="pref_restore_backup">Sicherung wiederherstellen</string>
|
||||
<string name="pref_restore_backup">Datensicherung wiederherstellen</string>
|
||||
<string name="pref_restore_backup_summ">Bibliothek mit Hilfe einer Datensicherung wiederherstellen</string>
|
||||
<string name="pref_backup_interval">Automatische Sicherungshäufigkeit</string>
|
||||
<string name="backup_created">Sicherung erstellt</string>
|
||||
<string name="backup_created">Datensicherung erstellt</string>
|
||||
<string name="restore_completed">Wiederherstellen abgeschlossen</string>
|
||||
<string name="backup_choice">Was möchtest du sichern\?</string>
|
||||
<string name="restoring_backup">Sicherung wird wiederhergestellt</string>
|
||||
<string name="creating_backup">Sicherung wird erstellt</string>
|
||||
<string name="restoring_backup">Datensicherung wird wiederhergestellt</string>
|
||||
<string name="creating_backup">Datensicherung wird erstellt</string>
|
||||
<string name="pref_clear_chapter_cache">Kapitel-Zwischenspeicher leeren</string>
|
||||
<string name="used_cache">Belegt: %1$s</string>
|
||||
<string name="cache_deleted">Zwischenspeicher geleert, %1$d Dateien gelöscht</string>
|
||||
@ -351,11 +351,11 @@
|
||||
<string name="website">Webseite</string>
|
||||
<string name="label_downloaded_only">Nur Heruntergeladenes</string>
|
||||
<string name="recent_manga_time">Kap. %1$s - %2$s</string>
|
||||
<string name="restoring_backup_error">Sicherungswiederherstellung fehlgeschlagen</string>
|
||||
<string name="creating_backup_error">Sicherung fehlgeschlagen</string>
|
||||
<string name="restoring_backup_error">Datensicherungswiederherstellung fehlgeschlagen</string>
|
||||
<string name="creating_backup_error">Datensicherung fehlgeschlagen</string>
|
||||
<string name="restoring_backup_canceled">Wiederherstellung abgebrochen</string>
|
||||
<string name="restore_in_progress">Wiederherstellung wird bereits durchgeführt</string>
|
||||
<string name="backup_in_progress">Sicherung wird bereits durchgeführt</string>
|
||||
<string name="backup_in_progress">Datensicherung wird bereits durchgeführt</string>
|
||||
<string name="check_for_updates">Nach Aktualisierungen suchen</string>
|
||||
<string name="last_used_source">Zuletzt genutzt</string>
|
||||
<string name="local_source_help_guide">Anleitung für lokale Quellen</string>
|
||||
@ -371,7 +371,7 @@
|
||||
<string name="sort_by_upload_date">Nach Uploaddatum</string>
|
||||
<string name="label_data">Daten</string>
|
||||
<string name="backup_restore_missing_sources">Fehlende Quellen:</string>
|
||||
<string name="invalid_backup_file_missing_manga">Sicherung beinhaltet keinerlei Bibliothekseinträge.</string>
|
||||
<string name="invalid_backup_file_missing_manga">Datensicherung beinhaltet keinerlei Bibliothekseinträge.</string>
|
||||
<string name="invalid_backup_file">Ungültige Sicherungsdatei:</string>
|
||||
<string name="pref_library_update_refresh_metadata_summary">Auf neue Cover und Details überprüfen, wenn die Bibliothek aktualisiert wird</string>
|
||||
<string name="pref_library_update_refresh_metadata">Metadaten automatisch aktualisieren</string>
|
||||
@ -542,7 +542,7 @@
|
||||
<string name="action_display_language_badge">Sprache</string>
|
||||
<string name="label_warning">Warnung</string>
|
||||
<string name="notification_size_warning">Große Aktualisierungen schaden Quellen und könnten zu langsameren Aktualisierungen sowie höherem Akkuverbrauch führen. Tippe, um mehr zu erfahren.</string>
|
||||
<string name="backup_info">Du solltest Kopien der Sicherungen auch an anderen Orten aufbewahren. Sicherungen beinhalten möglicherweise sensible Daten, einschließlich gespeicherter Passwörter. Sei vorsichtig beim Teilen.</string>
|
||||
<string name="backup_info">Du solltest Kopien der Datensicherungen auch an anderen Orten aufbewahren. Datensicherungen beinhalten möglicherweise sensible Daten, einschließlich gespeicherter Passwörter. Sei vorsichtig beim Teilen.</string>
|
||||
<string name="connected_to_wifi">Nur über WLAN</string>
|
||||
<string name="update_72hour">Alle 3 Tage</string>
|
||||
<string name="download_queue_size_warning">Achtung: Große Downloads könnten dazu führen, dass Quellen langsamer werden und/oder Mihon blockieren. Tippe, um mehr zu erfahren.</string>
|
||||
@ -793,7 +793,7 @@
|
||||
<string name="label_add_repo_input">Repository-URL</string>
|
||||
<string name="action_add_repo_message">Füge zusätzliche Repositorys zu Mihon hinzu. Deren URL sollte mit „index.min.json“ enden.</string>
|
||||
<string name="invalid_repo_name">Ungültige Repository-URL</string>
|
||||
<string name="manga_interval_expected_update">Neue Kapitel vsl. in ca. %1$s, überprüfe ca. alle %2$s</string>
|
||||
<string name="manga_interval_expected_update">Ca. %1$s bis zur Veröffentlichung neuer Kapitel, wird ca. alle %2$s überprüft.</string>
|
||||
<string name="theme_nord">Nord</string>
|
||||
<string name="action_open_repo">Open-Source-Repository</string>
|
||||
<string name="manga_interval_expected_update_soon">Bald</string>
|
||||
|
@ -33,7 +33,7 @@
|
||||
<string name="local_source">Iturri lokala</string>
|
||||
<string name="latest">Azkenak</string>
|
||||
<string name="no_more_results">Ez dago emaitza gehiagorik</string>
|
||||
<string name="download_queue_size_warning">Abisua: deskarga handiek iturriak motelagoak izatea eta/edo Tachiyomi blokeatzea ekar dezakete</string>
|
||||
<string name="download_queue_size_warning">Abisua: deskarga handiek iturriak motelagoak izatea eta/edo Mihon blokeatzea ekar dezakete</string>
|
||||
<string name="creating_backup">Babeskopia sortzen</string>
|
||||
<string name="restore_duration">%02d min, %02d seg</string>
|
||||
<string name="add_tracking">Gehitu jarraipena</string>
|
||||
@ -227,7 +227,7 @@
|
||||
<string name="about_dont_kill_my_app">Fabrikatzaile batzuek aplikazioen murrizketa gehigarriak dituzte, atzeko planoko zerbitzuak akabatzen dituztenak. Webgune honek informazio gehiago du nola konpondu jakiteko.</string>
|
||||
<string name="untrusted_extension_message">Luzapen hau fidagarria ez den ziurtagiri batekin sinatu zen eta ez zen aktibatu.
|
||||
\n
|
||||
\nLuzapen maltzur batek Tachiyomi-n gordetako saio-hasierako kredentzialak irakur ditzake edo kode arbitrarioa exekutatu.
|
||||
\nLuzapen maltzur batek Mihon-n gordetako saio-hasierako kredentzialak irakur ditzake edo kode arbitrarioa exekutatu.
|
||||
\n
|
||||
\nZiurtagiri honetaz fidatzean, arrisku hauek onartzen dituzu.</string>
|
||||
<string name="ext_untrusted">Fidatu gabekoa</string>
|
||||
@ -316,7 +316,7 @@
|
||||
<string name="notification_first_add_to_library">Mesedez, gehitu manga zure liburutegian hau egin aurretik</string>
|
||||
<string name="transition_pages_loading">Orriak kargatzen…</string>
|
||||
<string name="channel_common">Arrunta</string>
|
||||
<string name="information_webview_required">WebView beharrezkoa da Tachiyomi-rentzat</string>
|
||||
<string name="information_webview_required">WebView beharrezkoa da Mihon-rentzat</string>
|
||||
<string name="download_notifier_text_only_wifi">Ez dago Wi-Fi konexiorik erabilgarri</string>
|
||||
<string name="label_default">Lehenetsia</string>
|
||||
<string name="track">Jarraipena</string>
|
||||
|
@ -607,7 +607,7 @@
|
||||
<string name="sort_category_confirmation">آیا مایلید که دسته بندی ها را به ترتیب الفبا منظم کنید؟</string>
|
||||
<string name="action_ok">باشه</string>
|
||||
<string name="action_sort_next_updated">به روز رسانی مورد انتظار بعدی</string>
|
||||
<string name="download_queue_size_warning">هشدار: حجم زیاد بارگیری ممکن است باعث اهسته تر شدن سرعت ویا مسدود کردن Tachiyomi از منبع شود. برای اطلاعات بیشتر لمس کنید.</string>
|
||||
<string name="download_queue_size_warning">هشدار: حجم زیاد بارگیری ممکن است باعث اهسته تر شدن سرعت ویا مسدود کردن Mihon از منبع شود. برای اطلاعات بیشتر لمس کنید.</string>
|
||||
<string name="skipped_reason_not_always_update">به دلیل این که این مجموعه نیازی به به روز رسانی نداشت رد شد</string>
|
||||
<string name="skipped_reason_not_caught_up">به دلیل وجود چپتر های خوانده نشده رد شد</string>
|
||||
<string name="skipped_reason_not_started">به دلیل این که هیچ چپتری خوانده نشده بود رد شد</string>
|
||||
@ -622,4 +622,40 @@
|
||||
<string name="updates_last_update_info">آخرین به روز رسانی کتابخانه: %s</string>
|
||||
<string name="pref_update_only_in_release_period">خارج از دوره انتشار موزد انتظار</string>
|
||||
<string name="pref_double_tap_zoom">برای بزرگ نمایی دوبار ضربه بزنید</string>
|
||||
<string name="action_filter_interval_custom">تناوب به روز رسانی شخسی سازی شده</string>
|
||||
<string name="onboarding_heading">خوش آمدید!</string>
|
||||
<string name="onboarding_description">بیاید برخی چیز ها را تنظیم کنیم. شما همیشه میتوانید این تنظیمات را در بخش تنظیمات تغییر دهید.</string>
|
||||
<string name="onboarding_action_next">بعدی</string>
|
||||
<string name="onboarding_permission_install_apps_description">برای نصب افزانه منبع.</string>
|
||||
<string name="onboarding_permission_notifications">دسترسی اعلان ها</string>
|
||||
<string name="onboarding_permission_notifications_description">برای به روز رسانی های کتابخانه و بیشتر مطلع شوید.</string>
|
||||
<string name="onboarding_permission_ignore_battery_opts">استفاده از باطری</string>
|
||||
<string name="onboarding_permission_ignore_battery_opts_description">از وقفه در به روز رسانی های کتاب خانه، بارگیری و پشتیبان گیری های طولانی اجتناب کنید.</string>
|
||||
<string name="onboarding_permission_action_grant">اعطا کردن</string>
|
||||
<string name="onboarding_guides_new_user">به %s نا آشنا هستید؟ ما پیشنهاد میکنیم تا یک سر به راهنمای شروع بزنید.</string>
|
||||
<string name="onboarding_guides_returning_user">نصب مجدد %s؟</string>
|
||||
<string name="pref_relative_format">زمان بندی های نسبی</string>
|
||||
<string name="pref_library_update_smart_update">به روز رسانی هوشمند</string>
|
||||
<string name="pref_chapter_swipe">کشیدن قسمت</string>
|
||||
<string name="ext_revoke_trust">لغو اعتماد افزونه های ناشناخته</string>
|
||||
<string name="label_extension_repos">مخازن افزونه ها</string>
|
||||
<string name="action_add_repo">اضافه کردن مخزن</string>
|
||||
<string name="label_add_repo_input">آدرس مخزن</string>
|
||||
<string name="error_repo_exists">این مخزن در حال حاضر وجود دارد!</string>
|
||||
<string name="action_delete_repo">حذف مخزن</string>
|
||||
<string name="invalid_repo_name">آدرس مخزن بی اعتبار است</string>
|
||||
<string name="delete_repo_confirmation">آشا شما میخواهید تا \"%s\" مخزن را حذف کنید؟</string>
|
||||
<string name="pref_page_rotate_invert">جهت چرخش صفحات گسترده را برعکس کن</string>
|
||||
<string name="no_location_set">مکان ذخیره سازی تنظیم نشده است</string>
|
||||
<string name="split_tall_images">عکس های بلند را تقسیم کن</string>
|
||||
<string name="track_activity_name">ورود به ناضر</string>
|
||||
<string name="pref_hide_in_library_items">اجرای داخل کتاب خانه را مخفی کن</string>
|
||||
<string name="pref_storage_location">محل زخیره سازی</string>
|
||||
<string name="onboarding_action_finish">شروع کنید</string>
|
||||
<string name="onboarding_action_skip">پرش</string>
|
||||
<string name="ext_permission_install_apps_warning">مجوز برای نصب افزونه ها لازم است. به اینجا ضربه بزنید تا اعطا کنید.</string>
|
||||
<string name="action_add_repo_message">به میهون مخازن اضافی اضافه کنید. این باید یک آدرس باشد که با \"index.min.json\" تمام شود.</string>
|
||||
<string name="information_empty_repos">شما هیچ مخزنی برای تنظیم ندارید.</string>
|
||||
<string name="action_open_repo">مخزن منبع باز</string>
|
||||
<string name="pref_flash_page_summ">مقدار رد باقی مانده در نمایش گر های E-ink کاهش میابد</string>
|
||||
</resources>
|
@ -72,7 +72,7 @@
|
||||
<string name="label_extensions">Laajennukset</string>
|
||||
<string name="label_extension_info">Laajennuksen tiedot</string>
|
||||
<string name="action_filter">Suodatus</string>
|
||||
<string name="action_filter_bookmarked">Kirjanmerkki</string>
|
||||
<string name="action_filter_bookmarked">Kirjanmerkityt</string>
|
||||
<string name="action_filter_unread">Lukemattomat</string>
|
||||
<string name="action_filter_empty">Poista suodattimet</string>
|
||||
<string name="action_sort_alpha">Aakkosjärjestyksessä</string>
|
||||
@ -103,7 +103,7 @@
|
||||
<string name="action_install">Asenna</string>
|
||||
<string name="action_share">Jaa</string>
|
||||
<string name="action_save">Tallenna</string>
|
||||
<string name="action_reset">Resetoi</string>
|
||||
<string name="action_reset">Nollaa</string>
|
||||
<string name="action_undo">Kumoa</string>
|
||||
<string name="action_open_log">Avaa loki</string>
|
||||
<string name="action_restore">Palauta</string>
|
||||
@ -143,7 +143,7 @@
|
||||
<string name="untrusted_extension">Luottamattomat laajennokset</string>
|
||||
<string name="untrusted_extension_message">Tämä laajennus on allekirjoitettu luottamattomalla sertifikaatilla ja sitä ei ole aktivoitu.
|
||||
\n
|
||||
\nHaitallinen laajennus voisi lukea mahdolliset kirjautumistiedot Tachiyomista tai suorittaa luvatonta koodia.
|
||||
\nHaitallinen laajennus voisi lukea mahdolliset kirjautumistiedot Mihonista tai suorittaa luvatonta koodia.
|
||||
\n
|
||||
\nLuottamalla tähän sertifikaattiin hyväksyt nämä riskit.</string>
|
||||
<string name="pref_fullscreen">Koko näyttö</string>
|
||||
@ -348,7 +348,7 @@
|
||||
<string name="action_pin">Kiinnitä</string>
|
||||
<string name="action_select_inverse">Valitse käänteinen</string>
|
||||
<string name="vertical_plus_viewer">Jatkuva pystysuora</string>
|
||||
<string name="information_webview_required">WebView on pakollinen Tachiyomissa</string>
|
||||
<string name="information_webview_required">WebView on pakollinen Mihonissa</string>
|
||||
<string name="restore_in_progress">Palautus on jo käynnissä</string>
|
||||
<string name="backup_in_progress">Varmuuskopiointi on jo käynnissä</string>
|
||||
<string name="pref_webtoon_side_padding">Sivuntäyttö</string>
|
||||
@ -447,7 +447,7 @@
|
||||
<string name="pref_dump_crash_logs_summary">Tallentaa virhelokit tiedostoon jaettavaksi kehittäjien kanssa</string>
|
||||
<string name="action_desc">Laskeva</string>
|
||||
<string name="action_asc">Nouseva</string>
|
||||
<string name="action_order_by_chapter_number">Luvunumeron mukaan</string>
|
||||
<string name="action_order_by_chapter_number">Lukunumeron mukaan</string>
|
||||
<string name="action_order_by_upload_date">Lisäyspäivämäärän mukaan</string>
|
||||
<string name="action_filter_tracked">Seuratut</string>
|
||||
<string name="action_display_show_number_of_items">Näytä kohteiden määrä</string>
|
||||
@ -495,7 +495,7 @@
|
||||
<string name="off">Pois päältä</string>
|
||||
<string name="on">Päällä</string>
|
||||
<string name="getting_started_guide">Aloitusopas</string>
|
||||
<string name="download_queue_size_warning">Varoitus: massalataukset voivat johtaa siihen, että lähteet muuttuvat hitaammiksi käyttää ja/tai ne estävät Tachiyomin käytön. Napauta saadaksesi lisätietoja.</string>
|
||||
<string name="download_queue_size_warning">Varoitus: massalataukset voivat johtaa siihen, että lähteet muuttuvat hitaammiksi käyttää ja/tai ne estävät Mihonin käytön. Napauta saadaksesi lisätietoja.</string>
|
||||
<string name="action_show_manga">Näytä manga</string>
|
||||
<string name="action_display_cover_only_grid">Kansikuva ruudukko</string>
|
||||
<string name="theme_monet">Dynaaminen</string>
|
||||
@ -615,4 +615,35 @@
|
||||
<string name="backup_info">Varmuuskopioita kannattaa säilyttää myös muissa paikoissa.</string>
|
||||
<string name="wish_list">Toivelista</string>
|
||||
<string name="cant_open_last_read_chapter">Viimeksi luettua lukua ei voitu avata</string>
|
||||
<string name="selected">Valitut</string>
|
||||
<string name="scanlator">Skanlaattori</string>
|
||||
<string name="label_data_storage">Data ja tallennustila</string>
|
||||
<string name="label_stats">Tilastotiedot</string>
|
||||
<string name="label_downloaded">Ladattu</string>
|
||||
<string name="action_sort_next_updated">Seuraava odotettu päivitys</string>
|
||||
<string name="action_sort_tracker_score">Seurannan pisteytys</string>
|
||||
<string name="action_update_category">Päivitä kategoria</string>
|
||||
<string name="action_sort_category">Järjestä kategoriat</string>
|
||||
<string name="action_display_show_continue_reading_button">Jatka lukemista painike</string>
|
||||
<string name="action_apply">Käytä</string>
|
||||
<string name="action_ok">OK</string>
|
||||
<string name="action_revert_to_default">Palauta oletus</string>
|
||||
<string name="crash_screen_description">%s kohtasi odottamattoman virheen. Ehdotamme että jaat kaatumisen lokitiedot tukikanavallemme Discordissa.</string>
|
||||
<string name="delete_downloaded">Poista ladatut</string>
|
||||
<string name="create_backup_file_error">Varmuuskopiotiedoston luonti epäonnistui</string>
|
||||
<string name="confirm_add_duplicate_manga">Kirjastossasi on jo samanniminen merkintä.
|
||||
\n
|
||||
\nHaluatko silti jatkaa?</string>
|
||||
<string name="crash_screen_title">Hupsista!</string>
|
||||
<string name="copied_to_clipboard_plain">Kopioitu leikepöydälle</string>
|
||||
<string name="crash_screen_restart_application">Uudelleenkäynnistä applikaatio</string>
|
||||
<string name="custom_cover">Mukautettu kansikuva</string>
|
||||
<string name="not_selected">Ei valitut</string>
|
||||
<string name="action_menu_overflow_description">Lisää asetuksia</string>
|
||||
<string name="label_local">Paikallinen</string>
|
||||
<string name="label_started">Aloitettu</string>
|
||||
<string name="action_open_random_manga">Avaa satunnainen merkintä</string>
|
||||
<string name="sort_category_confirmation">Haluatko järjestää kategoriat aakkosjärjestykseen?</string>
|
||||
<string name="action_copy_to_clipboard">Kopioi leikepöydälle</string>
|
||||
<string name="action_move_to_bottom_all_for_series">Siirrä sarja pohjimmaiseksi</string>
|
||||
</resources>
|
@ -38,7 +38,7 @@
|
||||
</plurals>
|
||||
<plurals name="num_trackers">
|
||||
<item quantity="one">%d tracker</item>
|
||||
<item quantity="other">%d mga tracker</item>
|
||||
<item quantity="other">%d na tracker</item>
|
||||
</plurals>
|
||||
<plurals name="missing_chapters_warning">
|
||||
<item quantity="one">Nilaktawan ang %d na kabanata, maaaring ito ay wala sa source o na-filter ang mga ito</item>
|
||||
@ -46,11 +46,11 @@
|
||||
</plurals>
|
||||
<plurals name="relative_time">
|
||||
<item quantity="one">Kahapon</item>
|
||||
<item quantity="other">%1$d araw na ang makalipas</item>
|
||||
<item quantity="other">%1$d araw na ang nakakalipas</item>
|
||||
</plurals>
|
||||
<plurals name="next_unread_chapters">
|
||||
<item quantity="one">Susunod na hindi pa nababasa na kabanata</item>
|
||||
<item quantity="other">Susunod na %d di pa nababasa na kabanata</item>
|
||||
<item quantity="one">Susunod na hindi pa nababasang kabanata</item>
|
||||
<item quantity="other">Susunod na %d hindi pa nababasang kabanata</item>
|
||||
</plurals>
|
||||
<plurals name="download_amount">
|
||||
<item quantity="one">Sunod na kabanata</item>
|
||||
@ -58,14 +58,14 @@
|
||||
</plurals>
|
||||
<plurals name="missing_chapters">
|
||||
<item quantity="one">Nawawalang %1$s na kabanata</item>
|
||||
<item quantity="other">Nawawalang %1$s mga kabanata</item>
|
||||
<item quantity="other">Nawawalang %1$s na mga kabanata</item>
|
||||
</plurals>
|
||||
<plurals name="day">
|
||||
<item quantity="one">1 araw</item>
|
||||
<item quantity="other">%d (mga) araw</item>
|
||||
<item quantity="other">%d (na) araw</item>
|
||||
</plurals>
|
||||
<plurals name="num_repos">
|
||||
<item quantity="one">%d na repo</item>
|
||||
<item quantity="other">%d na mga repo</item>
|
||||
</plurals>
|
||||
</resources>
|
||||
</resources>
|
||||
|
@ -22,7 +22,7 @@
|
||||
<string name="action_disable">Isara</string>
|
||||
<string name="action_display_show_tabs">Ipakita ang mga tab ng kategorya</string>
|
||||
<string name="action_display_download_badge">Bilang ng kabanatang na-download</string>
|
||||
<string name="action_display_comfortable_grid">Maalwan na grid</string>
|
||||
<string name="action_display_comfortable_grid">Kumportableng grid</string>
|
||||
<string name="action_display_list">Listahan</string>
|
||||
<string name="action_display_grid">Siksik na grid</string>
|
||||
<string name="action_display">Pagpapakita</string>
|
||||
@ -114,7 +114,7 @@
|
||||
<string name="second_to_last">Pangalawa sa huling nabasa</string>
|
||||
<string name="last_read_chapter">Huling nabasang kabanata</string>
|
||||
<string name="disabled">Sarado</string>
|
||||
<string name="pref_remove_after_marked_as_read">Pagkamarkahang nabasa na</string>
|
||||
<string name="pref_remove_after_marked_as_read">Markahang nabasa na</string>
|
||||
<string name="pref_remove_after_read">Pagkatapos basahin, awtomatikong burahin</string>
|
||||
<string name="pref_webtoon_side_padding">Kapal ng gilid</string>
|
||||
<string name="pref_category_reading">Pagbabasa</string>
|
||||
@ -243,7 +243,7 @@
|
||||
<string name="channel_ext_updates">Mga update sa extension</string>
|
||||
<string name="tapping_inverted_none">Wala</string>
|
||||
<string name="channel_new_chapters">Mga update sa kabanata</string>
|
||||
<string name="channel_common">Komon</string>
|
||||
<string name="channel_common">Pangkaraniwan</string>
|
||||
<string name="download_notifier_download_paused">Nakahinto ang mga pag-download</string>
|
||||
<string name="download_notifier_no_network">Walang koneksyon sa Internet</string>
|
||||
<string name="download_notifier_text_only_wifi">Walang koneksyon sa Wifi</string>
|
||||
@ -291,7 +291,7 @@
|
||||
<string name="decode_image_error">Di mai-load ang larawan</string>
|
||||
<string name="no_next_chapter">Di makita ang susunod na kabanata</string>
|
||||
<string name="chapter_progress">Pahina: %1$d</string>
|
||||
<string name="cover_updated">Napalitan na ang cover</string>
|
||||
<string name="cover_updated">Napalitan ang cover</string>
|
||||
<string name="set_as_cover">Gawin itong cover</string>
|
||||
<string name="custom_filter">Pinili kong filter</string>
|
||||
<string name="picture_saved">Na-save na ang larawan</string>
|
||||
@ -556,7 +556,7 @@
|
||||
<string name="library_errors_help">Para sa tulong sa pag-aayos ng mga error sa pag-update ng aklatan, tingnan ang %1$s</string>
|
||||
<string name="pref_update_only_completely_read">Laktawan ang mga entry na hindi pa nababasang kabanata</string>
|
||||
<string name="save_chapter_as_cbz">I-save bilang CBZ archive</string>
|
||||
<string name="publishing_finished">Tapos na\'ng mailathala</string>
|
||||
<string name="publishing_finished">Tapos na ang paglalathala</string>
|
||||
<string name="on_hiatus">Naka-hiatus</string>
|
||||
<string name="cancelled">Kinansela</string>
|
||||
<string name="action_show_manga">Ipakita ang entry</string>
|
||||
@ -639,9 +639,9 @@
|
||||
<string name="pref_library_summary">Mga kategorya, panlahatang update, pag-swipe ng kabanata</string>
|
||||
<string name="pref_browse_summary">Mga source, extension, panlahatang paghanap</string>
|
||||
<string name="crash_screen_description">Nagkaroon ng hindi inaasahang error ang %s. Iminumungkahi naming ibahagi mo ang mga crash log sa aming support channel sa Discord.</string>
|
||||
<string name="crash_screen_title">Ay!</string>
|
||||
<string name="crash_screen_title">Ay Naku!</string>
|
||||
<string name="crash_screen_restart_application">Buksan muli ang app</string>
|
||||
<string name="invalid_location">Invalid na lugar: %s</string>
|
||||
<string name="invalid_location">Imbalidong lugar: %s</string>
|
||||
<string name="unknown_title">Di alam na pamagat</string>
|
||||
<string name="error_user_agent_string_invalid">Di-wastong string ng user agent</string>
|
||||
<string name="updates_last_update_info_just_now">Ngayon lang</string>
|
||||
@ -693,7 +693,7 @@
|
||||
<string name="pref_page_rotate">I-rotate ang malalawak na pahina upang magkasya</string>
|
||||
<string name="pref_page_rotate_invert">I-flip ang oryentasyon ng mga pinaikot na malalawak na pahina</string>
|
||||
<string name="overlay_header">Nakapatong (Overlay)</string>
|
||||
<string name="split_tall_images">Hatiin ang mga matatangkad na larawan</string>
|
||||
<string name="split_tall_images">Hatiin ang mga matataas na larawan</string>
|
||||
<string name="pref_debug_info">Impormasyon sa pag-debug</string>
|
||||
<string name="pref_chapter_swipe_start">Mag-swipe ng pakaliwang pagkilos</string>
|
||||
<string name="pref_chapter_swipe">Mag swipe ng kabanata</string>
|
||||
@ -798,4 +798,4 @@
|
||||
<string name="manga_interval_expected_update_soon">Malapit na</string>
|
||||
<string name="ext_revoke_trust">Bawiin ang mga pinagkakatiwalaang hindi kilalang extension</string>
|
||||
<string name="action_open_repo">Open source na repo</string>
|
||||
</resources>
|
||||
</resources>
|
||||
|
@ -412,7 +412,7 @@
|
||||
<string name="pref_category_delete_chapters">Suppression des chapitres</string>
|
||||
<string name="ext_nsfw_warning">Les sources de cette extension peuvent contenir du contenu NSFW (18+)</string>
|
||||
<string name="ext_nsfw_short">18+</string>
|
||||
<string name="parental_controls_info">Ceci n\'empêche pas les extensions non officielles ou potentiellement mal signalées de diffuser du contenu +18 dans l\'application.</string>
|
||||
<string name="parental_controls_info">Ceci n\'empêche pas les extensions de diffuser du contenu +18 dans l\'application.</string>
|
||||
<string name="no_chapters_error">Aucun chapitre trouvé</string>
|
||||
<string name="confirm_set_chapter_settings">Appliquer ce paramétrage par défaut \?</string>
|
||||
<string name="chapter_settings">Paramètres du chapitre</string>
|
||||
@ -534,7 +534,7 @@
|
||||
<string name="ext_installer_shizuku_unavailable_dialog">Installez et démarrez Shizuku pour utiliser Shizuku comme installateur d\'extensions.</string>
|
||||
<string name="ext_installer_shizuku_stopped">Shizuku n\'est pas en cours d\'exécution</string>
|
||||
<string name="ext_installer_legacy">Legacy</string>
|
||||
<string name="ext_installer_pref">installeur</string>
|
||||
<string name="ext_installer_pref">Installeur</string>
|
||||
<string name="ext_install_service_notif">Installation de l\'extension…</string>
|
||||
<string name="action_sort_count">Entrées totales</string>
|
||||
<string name="pref_verbose_logging">Rapports détaillés</string>
|
||||
@ -798,4 +798,4 @@
|
||||
<string name="create_backup_file_error">Impossible de créer un fichier de sauvegarde</string>
|
||||
<string name="last_auto_backup_info">Dernière sauvegarde automatique : %s</string>
|
||||
<string name="source_settings">Paramètres sources</string>
|
||||
</resources>
|
||||
</resources>
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user