Compare commits

...

26 Commits

Author SHA1 Message Date
222e111806 Release v0.16.2 2024-01-28 00:28:28 +06:00
8489b0dd8b [skip ci] Translations update from Hosted Weblate (#190)
* Translated using Weblate (Nepali)

Currently translated at 94.1% (747 of 793 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ne/

* Translated using Weblate (Italian)

Currently translated at 100.0% (793 of 793 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/it/

* Translated using Weblate (Japanese)

Currently translated at 100.0% (793 of 793 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ja/

* Translated using Weblate (Nepali)

Currently translated at 98.3% (780 of 793 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ne/

* Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (793 of 793 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/zh_Hans/

* Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (793 of 793 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/zh_Hans/

* Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (793 of 793 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/zh_Hant/

* Translated using Weblate (Nepali)

Currently translated at 100.0% (793 of 793 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ne/

* Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (793 of 793 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/zh_Hans/

* Translated using Weblate (Polish)

Currently translated at 100.0% (793 of 793 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/pl/

* Translated using Weblate (Turkish)

Currently translated at 100.0% (793 of 793 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/tr/

* Translated using Weblate (Swedish)

Currently translated at 100.0% (793 of 793 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/sv/

* Translated using Weblate (German)

Currently translated at 100.0% (793 of 793 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/de/

* Translated using Weblate (Swedish)

Currently translated at 100.0% (793 of 793 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/sv/

* Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (793 of 793 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/zh_Hant/

* Translated using Weblate (Finnish)

Currently translated at 80.9% (642 of 793 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/fi/

* Translated using Weblate (Indonesian)

Currently translated at 100.0% (793 of 793 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/id/

* Translated using Weblate (Nepali)

Currently translated at 100.0% (793 of 793 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ne/

* Translated using Weblate (Arabic)

Currently translated at 100.0% (793 of 793 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ar/

* Translated using Weblate (Persian)

Currently translated at 82.7% (656 of 793 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/fa/

* Translated using Weblate (Finnish)

Currently translated at 80.9% (642 of 793 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/fi/

* Translated using Weblate (Arabic)

Currently translated at 100.0% (17 of 17 strings)

Translation: Mihon/Mihon Plurals
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/ar/

* Translated using Weblate (Chuvash)

Currently translated at 88.2% (15 of 17 strings)

Translation: Mihon/Mihon Plurals
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/cv/

* Translated using Weblate (Ukrainian)

Currently translated at 100.0% (17 of 17 strings)

Translation: Mihon/Mihon Plurals
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/uk/

---------

Co-authored-by: FateXBlood <zecrofelix@gmail.com>
Co-authored-by: Federico Pierantoni <federico.pieranton@gmail.com>
Co-authored-by: Zero O <godarms2010@live.com>
Co-authored-by: stevenlele <stevenlele@outlook.com>
Co-authored-by: Paweł Waresiak <pwaresia@redhat.com>
Co-authored-by: kret <cihanbeykoroglu@gmail.com>
Co-authored-by: bittin1ddc447d824349b2 <bittin@reimu.nl>
Co-authored-by: Lyfja <45209212+lyfja@users.noreply.github.com>
Co-authored-by: dan-malprod <diabolic0240@proton.me>
Co-authored-by: Lzmxya <lzmxya@gmail.com>
Co-authored-by: A <ogloppi@mailbox.org>
Co-authored-by: Christian Elbrianno <crse@protonmail.ch>
Co-authored-by: abdelbasset jabrane <ribago9317@cubene.com>
Co-authored-by: Arash <ara.khoram95@gmail.com>
Co-authored-by: C201 <derasetad@gmail.com>
Co-authored-by: Сергій <sergiy.goncharuk.1@gmail.com>
2024-01-28 00:25:43 +06:00
88ed634978 Lint 2024-01-28 00:15:17 +06:00
32188f9f65 Refactor MAL code to not spam refresh token when it fails 2024-01-28 00:12:31 +06:00
05efc4ebeb Update types of legacy tracker model to match to domain one (#245)
* `score` to Double

* `tracker_id` to Long

* `last_chapter_read` to Double

* `total_chapters` to Long

* `status` to Long
2024-01-27 23:17:09 +06:00
65bfa083f2 Replace "tachiyomi" with "mihon" in crash log name (#234)
Closes #223.
2024-01-26 01:00:23 +06:00
b8a9998bbd [skip ci] Remove official extensions check from issue templates (#233)
* Update report_issue.yml

There are no official extensions anymore and the URL was for the tachiyomi repo anyway

* Update request_feature.yml

No more official extensions
2024-01-25 23:27:23 +06:00
d736bec003 Translations update from Hosted Weblate (#225)
* Translated using Weblate (Swedish)

Currently translated at 100.0% (17 of 17 strings)

Translation: Mihon/Mihon Plurals
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/sv/

* Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (17 of 17 strings)

Translation: Mihon/Mihon Plurals
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/zh_Hant/

---------

Co-authored-by: bittin1ddc447d824349b2 <bittin@reimu.nl>
Co-authored-by: Lzmxya <lzmxya@gmail.com>
2024-01-24 19:23:39 +06:00
348b23a9fd Fix refreshing from enhanced tracker (#219)
fix refreshing from enhanced tracker
2024-01-24 19:16:28 +06:00
121b2ec829 [skip ci] Removing extensions from config issues (#224) 2024-01-24 19:05:40 +06:00
1dd130df9e Fix #126: Inconsistent button height with some languages in "Data and storage" (#202)
* replace the windowInsetsPadding for navigationBarsPadding + statusBarsPadding

* Fixing bug in the MultiChoiceSegmentedButtonRow

* Rollback file
2024-01-23 18:47:05 +06:00
e17d87f357 Adding Type-safe project accessors (#194)
* replace the windowInsetsPadding for navigationBarsPadding + statusBarsPadding

* Enabling TYPESAFE_PROJECT_ACCESSORS

* Adding typesafe project accessors in the app module

* Adding typesafe project accessors in the core module

* Adding typesafe project accessors in the core-metadata module

* Adding typesafe project accessors in the data module

* Adding typesafe project accessors in the domain module

* Adding typesafe project accessors in the presentation-core module

* Adding typesafe project accessors in the presentation-widget module

* Adding typesafe project accessors in the source-local module

* Adding typesafe project accessors in the source-api module

* Rolling back

* Changing TYPESAFE_PROJECT_ACCESSORS line

* Removing extra spaces
2024-01-23 18:35:58 +06:00
de75561402 Change README to Markdown (#208)
* Change README to Markdown

Also fix license and add disclaimer

* Change some links to markdown as well
2024-01-23 14:14:05 +06:00
58085336a5 Lint 2024-01-22 20:20:11 +06:00
89ea0a271b Add translation widget (#195) 2024-01-22 20:12:39 +06:00
e3f33e24f5 Use own client for trackers + custom user agents
Closes #114
Closes #143

Co-authored-by: Med <45147847+kitsumed@users.noreply.github.com>
2024-01-22 16:18:30 +06:00
9fd1419142 Translations (#189)
* Small fix on french translation. (#104)

Update fr/strings.xml

Remove mentions of "official" extensions repos. (On 18+ extensions warning)

Fixed a setting label who had the first letter in lowercase.

* Updated Turkish suffixes (#125)

Update strings.xml

* Fix zh-rTW Translation (#118)

fix zh-tw translate

* Update Filipino Plurals (#112)

Updated some Filipino Plurals to make sense grammatically

* Update Filipino Strings (#111)

Changed/updated a few grammatical strings for the Filipino Translation

---------

Co-authored-by: Med <45147847+kitsumed@users.noreply.github.com>
Co-authored-by: NukeSource <123626751+NukeSource@users.noreply.github.com>
Co-authored-by: ɴᴇᴋᴏ <111511925+NeKoOuO@users.noreply.github.com>
Co-authored-by: InfinityDouki56 <31158494+infyProductions@users.noreply.github.com>
2024-01-22 15:45:37 +06:00
cb06898430 Fix issues when updating extensions 2024-01-22 02:27:45 +06:00
39407407f2 Remove usage of .not() where possible 2024-01-21 19:40:42 +06:00
a024218410 Fix faulty MangaUpdates score in db
Closes #117
2024-01-21 12:21:30 +06:00
26815c7356 Tweak app icon scaling 2024-01-21 11:55:25 +06:00
e0deeb8008 Backup and Restore Excluded scanlators (#166)
* Backup and Restore Excluded scanlators

* Improve performance

* This looks better
2024-01-21 11:38:36 +06:00
38d6ab80ce Fix "Flash on page change" gives black screen on page change
Fixes #108
2024-01-20 16:33:50 +06:00
78e66fd8d3 Tweak README (#154)
* update README.md

* Update README.md

---------

Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
2024-01-20 15:59:18 +06:00
26aa126ecb Modernize README (#139)
* Prepare new Readme

* Modernize README

* Tweak wording
2024-01-20 01:33:42 +06:00
e4a65656e7 refactor: db changes for syncing. (#113)
fix: sync marking chapter unread when we do library update before syncing.

So this should have been 0 on insert instead of the current time on insert. Essentially this issue arises: https://discord.com/channels/1099009852791083058/1099009853864812708/1190022356060614756

Signed-off-by: KaiserBh <kaiserbh@proton.me>
2024-01-18 10:37:41 +06:00
108 changed files with 903 additions and 543 deletions

View File

@ -1,11 +1,5 @@
blank_issues_enabled: false blank_issues_enabled: false
contact_links: 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: 📦 Mihon extensions
url: https://mihon.app/extensions/
about: List of all available extensions with download links
- name: 🖥️ Mihon website - name: 🖥️ Mihon website
url: https://mihon.app/ url: https://mihon.app/
about: Guides, troubleshooting, and answers to common questions about: Guides, troubleshooting, and answers to common questions

View File

@ -94,8 +94,6 @@ body:
required: true required: true
- label: I have written a short but informative title. - label: I have written a short but informative title.
required: true 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/). - label: I have gone through the [FAQ](https://mihon.app/docs/faq/general) and [troubleshooting guide](https://mihon.app/docs/guides/troubleshooting/).
required: true required: true
- label: I have updated the app to version **[0.16.1](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)**.

View File

@ -31,8 +31,6 @@ body:
required: true required: true
- label: I have written a short but informative title. - label: I have written a short but informative title.
required: true 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.16.1](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 required: true
- label: I will fill out all of the requested information in this form. - label: I will fill out all of the requested information in this form.

BIN
.github/assets/logo.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 736 B

136
README.md
View File

@ -1,89 +1,113 @@
| Build | Stable | Weekly Beta | Support Server | <div align="center">
|-------|--------|-------------|----------------|
| [![CI](https://github.com/mihonapp/mihon/actions/workflows/build_push.yml/badge.svg)](https://github.com/mihonapp/mihon/actions/workflows/build_push.yml) | [![stable release](https://img.shields.io/github/release/mihonapp/mihon.svg?maxAge=3600&label=download)](https://github.com/mihonapp/mihon/releases) | [![latest beta build](https://img.shields.io/github/v/release/mihonapp/mihon-preview.svg?maxAge=3600&label=download)](https://github.com/mihonapp/mihon-preview/releases) | [![Discord](https://img.shields.io/discord/1195734228319617024.svg?label=discord&labelColor=7289da&color=2c2f33&style=flat)](https://discord.gg/mihon) |
# ![app icon](./.github/readme-images/app-icon.png)Mihon <a href="https://mihon.app">
Mihon is a free and open source manga reader for Android 8.0 and above. <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.
[![Discord server](https://img.shields.io/discord/1195734228319617024.svg?label=&labelColor=6A7EC2&color=7389D8&logo=discord&logoColor=FFFFFF)](https://discord.gg/mihon)
[![GitHub downloads](https://img.shields.io/github/downloads/mihonapp/mihon/total?label=downloads&labelColor=27303D&color=0D1117&logo=github&logoColor=FFFFFF&style=flat)](https://github.com/mihonapp/mihon/releases)
[![CI](https://img.shields.io/github/actions/workflow/status/mihonapp/mihon/build_push.yml?labelColor=27303D)](https://github.com/mihonapp/mihon/actions/workflows/build_push.yml)
[![License: Apache-2.0](https://img.shields.io/github/license/mihonapp/mihon?labelColor=27303D&color=0877d2)](/LICENSE)
[![Translation status](https://img.shields.io/weblate/progress/mihon?labelColor=27303D&color=946300)](https://hosted.weblate.org/engage/mihon/)
## Download
[![Mihon Stable](https://img.shields.io/github/release/mihonapp/mihon.svg?maxAge=3600&label=Stable&labelColor=06599d&color=043b69)](https://github.com/mihonapp/mihon/releases)
[![Mihon Beta](https://img.shields.io/github/v/release/mihonapp/mihon-preview.svg?maxAge=3600&label=Beta&labelColor=2c2c47&color=1c1c39)](https://github.com/mihonapp/mihon-preview/releases)
*Requires Android 8.0 or higher.*
## Features ## Features
Features include: <div align="left">
* Online reading from a variety of sources
* Local reading of downloaded content * Local reading of content.
* A configurable reader with multiple viewers, reading directions and other settings. * 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 * 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 * Categories to organize your library.
* Light and dark themes * Light and dark themes.
* Schedule updating your library for new chapters * Schedule updating your library for new chapters.
* Create backups locally to read offline or to your desired cloud service * Create backups locally to read offline or to your desired cloud service.
* Plus much more...
## Download </div>
Get the app from our [releases page](https://github.com/mihonapp/mihon/releases).
If you want to try new features before they get to the stable release, you can download the beta 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).** <details align="center"><summary>Issues</summary><div align="left">
2. If you are unsure, ask here: [![Discord](https://img.shields.io/discord/1195734228319617024.svg)](https://discord.gg/mihon)
</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) <details align="center"><summary>Bugs</summary><div align="left">
* 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 version (**More → About → Version**).
* Include steps to reproduce (if not obvious from description) * If not latest, try updating, it may have already been solved.
* Include screenshot (if needed) * Beta version is equal to the number of commits as seen on the main page.
* If it could be device-dependent, try reproducing on another device (if possible) * 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 * 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) * Include screenshot (if needed)
* Source requests are not accepted.
Source requests are not accepted. </div></details>
</details>
<details><summary>Contributing</summary> ### Repositories
See [CONTRIBUTING.md](./CONTRIBUTING.md). [![mihonapp/website - GitHub](https://github-readme-stats.vercel.app/api/pin/?username=mihonapp&repo=website&bg_color=161B22&text_color=c9d1d9&title_color=0877d2&icon_color=0877d2&border_radius=8&hide_border=true)](https://github.com/mihonapp/website/)
</details>
<details><summary>Code of Conduct</summary> ### Credits
See [CODE_OF_CONDUCT.md](./CODE_OF_CONDUCT.md). Thank you to all the people who have contributed!
</details>
## 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/) ### Disclaimer
You can also reach out to us on [Discord](https://discord.gg/mihon).
## 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. Copyright © 2015 Javier Tomás
You may obtain a copy of the License at 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 Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. 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>

View File

@ -22,8 +22,8 @@ android {
defaultConfig { defaultConfig {
applicationId = "app.mihon" applicationId = "app.mihon"
versionCode = 2 versionCode = 3
versionName = "0.16.1" versionName = "0.16.2"
buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"") buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"")
buildConfigField("String", "COMMIT_SHA", "\"${getGitSha()}\"") buildConfigField("String", "COMMIT_SHA", "\"${getGitSha()}\"")
@ -139,15 +139,15 @@ android {
} }
dependencies { dependencies {
implementation(project(":i18n")) implementation(projects.i18n)
implementation(project(":core")) implementation(projects.core)
implementation(project(":core-metadata")) implementation(projects.coreMetadata)
implementation(project(":source-api")) implementation(projects.sourceApi)
implementation(project(":source-local")) implementation(projects.sourceLocal)
implementation(project(":data")) implementation(projects.data)
implementation(project(":domain")) implementation(projects.domain)
implementation(project(":presentation-core")) implementation(projects.presentationCore)
implementation(project(":presentation-widget")) implementation(projects.presentationWidget)
// Compose // Compose
implementation(platform(compose.bom)) implementation(platform(compose.bom))

View File

@ -12,5 +12,12 @@
<path <path
android:pathData="M0,0h432v432h-432z" android:pathData="M0,0h432v432h-432z"
android:fillColor="#2E3943"/> android:fillColor="#2E3943"/>
<path
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.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> </group>
</vector> </vector>

View File

@ -4,13 +4,6 @@
android:viewportWidth="432" android:viewportWidth="432"
android:viewportHeight="432"> android:viewportHeight="432">
<path <path
android:pathData="M337,216C337,282.83 282.83,337 216,337C149.17,337 95,282.83 95,216C95,149.17 149.17,95 216,95C282.83,95 337,149.17 337,216Z" 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="#F2FAFF"/>
<path
android:pathData="M216,314.31C270.3,314.31 314.31,270.3 314.31,216C314.31,161.7 270.3,117.69 216,117.69C161.7,117.69 117.69,161.7 117.69,216C117.69,270.3 161.7,314.31 216,314.31ZM216,337C282.83,337 337,282.83 337,216C337,149.17 282.83,95 216,95C149.17,95 95,149.17 95,216C95,282.83 149.17,337 216,337Z"
android:fillColor="#7EBBED"
android:fillType="evenOdd"/>
<path
android:pathData="M163.71,174.79L162.66,150.56C165.82,151.16 169.58,151.31 178.16,151.31C188.55,151.31 202.24,150.71 209.32,149.81C212.32,149.51 213.53,149.05 215.49,148L231.74,161.85C230.23,163.95 229.78,164.86 228.13,169.07C226.77,172.38 220.6,191.49 218.2,199.62C229.33,201.88 235.5,203.53 243.93,206.99C244.98,199.62 245.13,195.71 245.13,182.31C245.13,178.85 244.98,176.9 244.53,173.74L270.72,174.64C269.96,178.25 269.82,179.76 269.66,185.62C269.06,199.77 268.46,206.54 266.95,216.78C277.34,222.04 277.34,222.04 282.61,224.9C285.32,226.41 285.92,226.71 287.72,227.31L278.99,255.45C274.78,251.69 268.91,247.63 260.33,242.81C252.81,260.72 240.32,273.82 221.35,284.2C215.03,275.77 210.22,270.65 202.54,264.63C213.68,259.37 218.8,256.06 224.67,250.49C230.38,244.92 234.15,239.5 237.76,231.38C228.13,227.01 221.96,225.05 211.27,222.8C205.1,241.16 200.13,252.9 195.77,259.97C189.9,269.45 181.93,274.42 172.74,274.42C165.67,274.42 158.45,271.26 153.18,265.84C147.16,259.67 144,251.09 144,241.16C144,226.41 151.07,213.62 163.41,205.64C171.39,200.52 179.82,198.27 193.21,197.51C195.92,188.63 198.18,180.96 200.29,172.38C193.66,172.98 185.39,173.43 175.3,173.88C169.88,174.04 168.08,174.19 163.71,174.79ZM186.59,220.54C179.52,221.74 175.3,224 171.54,228.82C168.68,232.13 167.33,236.04 167.33,240.25C167.33,244.92 169.58,248.38 172.44,248.38C175.9,248.38 179.82,240.55 186.59,220.54Z"
android:fillColor="#031019"/> android:fillColor="#031019"/>
</vector> </vector>

View File

@ -163,7 +163,7 @@ class SyncChaptersWithSource(
var updatedToAdd = newChapters.map { toAddItem -> var updatedToAdd = newChapters.map { toAddItem ->
var chapter = toAddItem.copy(dateFetch = nowMillis + itemCount--) 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( chapter = chapter.copy(
read = chapter.chapterNumber in deletedReadChapterNumbers, read = chapter.chapterNumber in deletedReadChapterNumbers,

View File

@ -23,7 +23,7 @@ class GetExtensionSources(
ExtensionSourceItem( ExtensionSourceItem(
source = source, source = source,
enabled = source.isEnabled(), enabled = source.isEnabled(),
labelAsName = isMultiSource && isMultiLangSingleSource.not(), labelAsName = isMultiSource && !isMultiLangSingleSource,
) )
} }
} }

View File

@ -22,9 +22,9 @@ class GetExtensionsByType(
extensionManager.availableExtensionsFlow, extensionManager.availableExtensionsFlow,
) { _activeLanguages, _installed, _untrusted, _available -> ) { _activeLanguages, _installed, _untrusted, _available ->
val (updates, installed) = _installed val (updates, installed) = _installed
.filter { (showNsfwSources || it.isNsfw.not()) } .filter { (showNsfwSources || !it.isNsfw) }
.sortedWith( .sortedWith(
compareBy<Extension.Installed> { it.isObsolete.not() } compareBy<Extension.Installed> { !it.isObsolete }
.thenBy(String.CASE_INSENSITIVE_ORDER) { it.name }, .thenBy(String.CASE_INSENSITIVE_ORDER) { it.name },
) )
.partition { it.hasUpdate } .partition { it.hasUpdate }
@ -36,7 +36,7 @@ class GetExtensionsByType(
.filter { extension -> .filter { extension ->
_installed.none { it.pkgName == extension.pkgName } && _installed.none { it.pkgName == extension.pkgName } &&
_untrusted.none { it.pkgName == extension.pkgName } && _untrusted.none { it.pkgName == extension.pkgName } &&
(showNsfwSources || extension.isNsfw.not()) (showNsfwSources || !extension.isNsfw)
} }
.flatMap { ext -> .flatMap { ext ->
if (ext.sources.isEmpty()) { if (ext.sources.isEmpty()) {

View File

@ -34,15 +34,15 @@ class GetSourcesWithFavoriteCount(
when (sorting) { when (sorting) {
SetMigrateSorting.Mode.ALPHABETICAL -> { SetMigrateSorting.Mode.ALPHABETICAL -> {
when { when {
a.first.isStub && b.first.isStub.not() -> -1 a.first.isStub && !b.first.isStub -> -1
b.first.isStub && a.first.isStub.not() -> 1 b.first.isStub && !a.first.isStub -> 1
else -> a.first.name.lowercase().compareToWithCollator(b.first.name.lowercase()) else -> a.first.name.lowercase().compareToWithCollator(b.first.name.lowercase())
} }
} }
SetMigrateSorting.Mode.TOTAL -> { SetMigrateSorting.Mode.TOTAL -> {
when { when {
a.first.isStub && b.first.isStub.not() -> -1 a.first.isStub && !b.first.isStub -> -1
b.first.isStub && a.first.isStub.not() -> 1 b.first.isStub && !a.first.isStub -> 1
else -> a.second.compareTo(b.second) else -> a.second.compareTo(b.second)
} }
} }

View File

@ -30,9 +30,9 @@ class RefreshTracks(
.map { (track, service) -> .map { (track, service) ->
async { async {
return@async try { return@async try {
val updatedTrack = service!!.refresh(track.toDbTrack()) val updatedTrack = service!!.refresh(track.toDbTrack()).toDomainTrack()!!
insertTrack.await(updatedTrack.toDomainTrack()!!) insertTrack.await(updatedTrack)
syncChapterProgressWithTrack.await(mangaId, track, service) syncChapterProgressWithTrack.await(mangaId, updatedTrack, service)
null null
} catch (e: Throwable) { } catch (e: Throwable) {
service to e service to e

View File

@ -19,30 +19,28 @@ fun Track.toDbTrack(): DbTrack = DbTrack.create(trackerId).also {
it.remote_id = remoteId it.remote_id = remoteId
it.library_id = libraryId it.library_id = libraryId
it.title = title it.title = title
it.last_chapter_read = lastChapterRead.toFloat() it.last_chapter_read = lastChapterRead
it.total_chapters = totalChapters.toInt() it.total_chapters = totalChapters
it.status = status.toInt() it.status = status
it.score = score.toFloat() it.score = score
it.tracking_url = remoteUrl it.tracking_url = remoteUrl
it.started_reading_date = startDate it.started_reading_date = startDate
it.finished_reading_date = finishDate it.finished_reading_date = finishDate
} }
fun DbTrack.toDomainTrack(idRequired: Boolean = true): Track? { 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( return Track(
id = trackId, id = trackId,
mangaId = manga_id, mangaId = manga_id,
trackerId = tracker_id.toLong(), trackerId = tracker_id,
remoteId = remote_id, remoteId = remote_id,
libraryId = library_id, libraryId = library_id,
title = title, title = title,
lastChapterRead = last_chapter_read.toDouble(), lastChapterRead = last_chapter_read,
totalChapters = total_chapters.toLong(), totalChapters = total_chapters,
status = status.toLong(), status = status,
// Jank workaround due to precision issues while converting score = score,
// See https://github.com/tachiyomiorg/tachiyomi/issues/10343
score = score.toString().toDouble(),
remoteUrl = tracking_url, remoteUrl = tracking_url,
startDate = started_reading_date, startDate = started_reading_date,
finishDate = finished_reading_date, finishDate = finished_reading_date,

View File

@ -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) { fun setCredentials(tracker: Tracker, username: String, password: String) {
trackUsername(tracker).set(username) trackUsername(tracker).set(username)
trackPassword(tracker).set(password) trackPassword(tracker).set(password)
trackAuthExpired(tracker).set(false)
} }
fun trackToken(tracker: Tracker) = preferenceStore.getString(Preference.privateKey("track_token_${tracker.id}"), "") fun trackToken(tracker: Tracker) = preferenceStore.getString(Preference.privateKey("track_token_${tracker.id}"), "")

View File

@ -203,7 +203,13 @@ private fun ExtensionContent(
items( items(
items = items, items = items,
contentType = { "item" }, 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 -> ) { item ->
ExtensionItem( ExtensionItem(
modifier = Modifier.animateItemPlacement(), modifier = Modifier.animateItemPlacement(),

View File

@ -7,8 +7,11 @@ import android.net.Uri
import androidx.activity.compose.ManagedActivityResultLauncher import androidx.activity.compose.ManagedActivityResultLauncher
import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.layout.IntrinsicSize
import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.outlined.HelpOutline import androidx.compose.material.icons.automirrored.outlined.HelpOutline
@ -189,9 +192,11 @@ object SettingsDataScreen : SearchableSettings {
MultiChoiceSegmentedButtonRow( MultiChoiceSegmentedButtonRow(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.height(intrinsicSize = IntrinsicSize.Min)
.padding(horizontal = PrefsHorizontalPadding), .padding(horizontal = PrefsHorizontalPadding),
) { ) {
SegmentedButton( SegmentedButton(
modifier = Modifier.fillMaxHeight(),
checked = false, checked = false,
onCheckedChange = { navigator.push(CreateBackupScreen()) }, onCheckedChange = { navigator.push(CreateBackupScreen()) },
shape = SegmentedButtonDefaults.itemShape(0, 2), shape = SegmentedButtonDefaults.itemShape(0, 2),
@ -199,6 +204,7 @@ object SettingsDataScreen : SearchableSettings {
Text(stringResource(MR.strings.pref_create_backup)) Text(stringResource(MR.strings.pref_create_backup))
} }
SegmentedButton( SegmentedButton(
modifier = Modifier.fillMaxHeight(),
checked = false, checked = false,
onCheckedChange = { onCheckedChange = {
if (!BackupRestoreJob.isRunning(context)) { if (!BackupRestoreJob.isRunning(context)) {

View File

@ -11,6 +11,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlin.time.Duration.Companion.seconds
@Stable @Stable
class DisplayRefreshHost { class DisplayRefreshHost {
@ -30,15 +31,15 @@ fun DisplayRefreshHost(
val currentDisplayRefresh = hostState.currentDisplayRefresh val currentDisplayRefresh = hostState.currentDisplayRefresh
LaunchedEffect(currentDisplayRefresh) { LaunchedEffect(currentDisplayRefresh) {
if (currentDisplayRefresh) { if (currentDisplayRefresh) {
delay(1500) delay(1.5.seconds)
hostState.currentDisplayRefresh = false hostState.currentDisplayRefresh = false
} }
} }
if (currentDisplayRefresh) { Canvas(
Canvas( modifier = modifier.fillMaxSize(),
modifier = modifier.fillMaxSize(), ) {
) { if (currentDisplayRefresh) {
drawRect(Color.Black) drawRect(Color.Black)
} }
} }

View File

@ -88,7 +88,7 @@ fun TrackInfoDialogHome(
TrackInfoItem( TrackInfoItem(
title = item.track.title, title = item.track.title,
tracker = item.tracker, tracker = item.tracker,
status = item.tracker.getStatus(item.track.status.toInt()), status = item.tracker.getStatus(item.track.status),
onStatusClick = { onStatusClick(item) }, onStatusClick = { onStatusClick(item) },
chapters = "${item.track.lastChapterRead.toInt()}".let { chapters = "${item.track.lastChapterRead.toInt()}".let {
val totalChapters = item.track.totalChapters val totalChapters = item.track.totalChapters

View File

@ -48,9 +48,9 @@ import tachiyomi.presentation.core.util.isScrolledToStart
@Composable @Composable
fun TrackStatusSelector( fun TrackStatusSelector(
selection: Int, selection: Long,
onSelectionChange: (Int) -> Unit, onSelectionChange: (Long) -> Unit,
selections: Map<Int, StringResource?>, selections: Map<Long, StringResource?>,
onConfirm: () -> Unit, onConfirm: () -> Unit,
onDismissRequest: () -> Unit, onDismissRequest: () -> Unit,
) { ) {
@ -236,12 +236,12 @@ private fun TrackStatusSelectorPreviews() {
onSelectionChange = {}, onSelectionChange = {},
selections = persistentMapOf( selections = persistentMapOf(
// Anilist values // Anilist values
1 to MR.strings.reading, 1L to MR.strings.reading,
2 to MR.strings.plan_to_read, 2L to MR.strings.plan_to_read,
3 to MR.strings.completed, 3L to MR.strings.completed,
4 to MR.strings.on_hold, 4L to MR.strings.on_hold,
5 to MR.strings.dropped, 5L to MR.strings.dropped,
6 to MR.strings.repeating, 6L to MR.strings.repeating,
), ),
onConfirm = {}, onConfirm = {},
onDismissRequest = {}, onDismissRequest = {},

View File

@ -301,7 +301,7 @@ private fun SearchResultItem(
text = status, text = status,
) )
} }
if (trackSearch.score != -1f) { if (trackSearch.score != -1.0) {
SearchResultItemDetails( SearchResultItemDetails(
title = stringResource(MR.strings.score), title = stringResource(MR.strings.score),
text = trackSearch.score.toString(), text = trackSearch.score.toString(),

View File

@ -62,14 +62,14 @@ internal class TrackerSearchPreviewProvider : PreviewParameterProvider<@Composab
private fun randTrackSearch() = TrackSearch().let { private fun randTrackSearch() = TrackSearch().let {
it.id = Random.nextLong() it.id = Random.nextLong()
it.manga_id = Random.nextLong() it.manga_id = Random.nextLong()
it.tracker_id = Random.nextInt() it.tracker_id = Random.nextLong()
it.remote_id = Random.nextLong() it.remote_id = Random.nextLong()
it.library_id = Random.nextLong() it.library_id = Random.nextLong()
it.title = lorem((1..10).random()).joinToString() it.title = lorem((1..10).random()).joinToString()
it.last_chapter_read = (0..100).random().toFloat() it.last_chapter_read = (0..100).random().toDouble()
it.total_chapters = (100..1000).random() it.total_chapters = (100L..1000L).random()
it.score = (0..10).random().toFloat() it.score = (0..10).random().toDouble()
it.status = Random.nextInt() it.status = Random.nextLong()
it.started_reading_date = 0L it.started_reading_date = 0L
it.finished_reading_date = 0L it.finished_reading_date = 0L
it.tracking_url = "https://example.com/tracker-example" it.tracking_url = "https://example.com/tracker-example"

View File

@ -30,6 +30,10 @@ class MangaBackupCreator(
// Entry for this manga // Entry for this manga
val mangaObject = manga.toBackupManga() val mangaObject = manga.toBackupManga()
mangaObject.excludedScanlators = handler.awaitList {
excluded_scanlatorsQueries.getExcludedScanlatorsByMangaId(manga.id)
}
if (options.chapters) { if (options.chapters) {
// Backup all the chapters // Backup all the chapters
handler.awaitList { handler.awaitList {

View File

@ -38,6 +38,7 @@ data class BackupManga(
@ProtoNumber(105) var updateStrategy: UpdateStrategy = UpdateStrategy.ALWAYS_UPDATE, @ProtoNumber(105) var updateStrategy: UpdateStrategy = UpdateStrategy.ALWAYS_UPDATE,
@ProtoNumber(106) var lastModifiedAt: Long = 0, @ProtoNumber(106) var lastModifiedAt: Long = 0,
@ProtoNumber(107) var favoriteModifiedAt: Long? = null, @ProtoNumber(107) var favoriteModifiedAt: Long? = null,
@ProtoNumber(108) var excludedScanlators: List<String> = emptyList(),
) { ) {
fun getMangaImpl(): Manga { fun getMangaImpl(): Manga {
return Manga.create().copy( return Manga.create().copy(

View File

@ -73,6 +73,7 @@ class MangaRestorer(
backupCategories = backupCategories, backupCategories = backupCategories,
history = backupManga.history + backupManga.brokenHistory.map { it.toBackupHistory() }, history = backupManga.history + backupManga.brokenHistory.map { it.toBackupHistory() },
tracks = backupManga.tracking, tracks = backupManga.tracking,
excludedScanlators = backupManga.excludedScanlators,
) )
} }
} }
@ -264,11 +265,13 @@ class MangaRestorer(
backupCategories: List<BackupCategory>, backupCategories: List<BackupCategory>,
history: List<BackupHistory>, history: List<BackupHistory>,
tracks: List<BackupTracking>, tracks: List<BackupTracking>,
excludedScanlators: List<String>,
): Manga { ): Manga {
restoreCategories(manga, categories, backupCategories) restoreCategories(manga, categories, backupCategories)
restoreChapters(manga, chapters) restoreChapters(manga, chapters)
restoreTracking(manga, tracks) restoreTracking(manga, tracks)
restoreHistory(history) restoreHistory(history)
restoreExcludedScanlators(manga, excludedScanlators)
updateManga.awaitUpdateFetchInterval(manga, now, currentFetchWindow) updateManga.awaitUpdateFetchInterval(manga, now, currentFetchWindow)
return manga return manga
} }
@ -401,4 +404,25 @@ class MangaRestorer(
} }
private fun Track.forComparison() = this.copy(id = 0L, mangaId = 0L) 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)
}
}
}
}
} }

View File

@ -8,7 +8,7 @@ interface Track : Serializable {
var manga_id: Long var manga_id: Long
var tracker_id: Int var tracker_id: Long
var remote_id: Long var remote_id: Long
@ -16,13 +16,13 @@ interface Track : Serializable {
var title: String 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 var started_reading_date: Long
@ -40,7 +40,7 @@ interface Track : Serializable {
companion object { companion object {
fun create(serviceId: Long): Track = TrackImpl().apply { fun create(serviceId: Long): Track = TrackImpl().apply {
tracker_id = serviceId.toInt() tracker_id = serviceId
} }
} }
} }

View File

@ -6,7 +6,7 @@ class TrackImpl : Track {
override var manga_id: Long = 0 override var manga_id: Long = 0
override var tracker_id: Int = 0 override var tracker_id: Long = 0
override var remote_id: Long = 0 override var remote_id: Long = 0
@ -14,13 +14,13 @@ class TrackImpl : Track {
override lateinit var title: String 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 override var started_reading_date: Long = 0

View File

@ -315,13 +315,13 @@ class DownloadManager(
val capitalizationChanged = oldFolder.name.equals(newName, ignoreCase = true) val capitalizationChanged = oldFolder.name.equals(newName, ignoreCase = true)
if (capitalizationChanged) { if (capitalizationChanged) {
val tempName = newName + Downloader.TMP_DIR_SUFFIX 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}" } logcat(LogPriority.ERROR) { "Failed to rename source download folder: ${oldFolder.name}" }
return return
} }
} }
if (oldFolder.renameTo(newName).not()) { if (!oldFolder.renameTo(newName)) {
logcat(LogPriority.ERROR) { "Failed to rename source download folder: ${oldFolder.name}" } logcat(LogPriority.ERROR) { "Failed to rename source download folder: ${oldFolder.name}" }
} }
} }

View File

@ -130,7 +130,7 @@ class DownloadProvider(
val newChapterName = sanitizeChapterName(chapterName) val newChapterName = sanitizeChapterName(chapterName)
return DiskUtil.buildValidFilename( return DiskUtil.buildValidFilename(
when { when {
chapterScanlator.isNullOrBlank().not() -> "${chapterScanlator}_$newChapterName" !chapterScanlator.isNullOrBlank() -> "${chapterScanlator}_$newChapterName"
else -> newChapterName else -> newChapterName
}, },
) )

View File

@ -40,8 +40,8 @@ abstract class BaseTracker(
return track.score return track.score
} }
override fun indexToScore(index: Int): Float { override fun indexToScore(index: Int): Double {
return index.toFloat() return index.toDouble()
} }
@CallSuper @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 track.status = status
if (track.status == getCompletionStatus() && track.total_chapters != 0) { if (track.status == getCompletionStatus() && track.total_chapters != 0L) {
track.last_chapter_read = track.total_chapters.toFloat() track.last_chapter_read = track.total_chapters.toDouble()
} }
updateRemote(track) updateRemote(track)
} }
override suspend fun setRemoteLastChapterRead(track: Track, chapterNumber: Int) { override suspend fun setRemoteLastChapterRead(track: Track, chapterNumber: Int) {
if ( if (
track.last_chapter_read == 0f && track.last_chapter_read == 0.0 &&
track.last_chapter_read < chapterNumber && track.last_chapter_read < chapterNumber &&
track.status != getRereadingStatus() track.status != getRereadingStatus()
) { ) {
track.status = getReadingStatus() track.status = getReadingStatus()
} }
track.last_chapter_read = chapterNumber.toFloat() track.last_chapter_read = chapterNumber.toDouble()
if (track.total_chapters != 0 && track.last_chapter_read.toInt() == track.total_chapters) { if (track.total_chapters != 0L && track.last_chapter_read.toLong() == track.total_chapters) {
track.status = getCompletionStatus() track.status = getCompletionStatus()
track.finished_reading_date = System.currentTimeMillis() track.finished_reading_date = System.currentTimeMillis()
} }

View File

@ -27,22 +27,22 @@ interface Tracker {
@DrawableRes @DrawableRes
fun getLogo(): Int 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> fun getScoreList(): ImmutableList<String>
// TODO: Store all scores as 10 point in the future maybe? // TODO: Store all scores as 10 point in the future maybe?
fun get10PointScore(track: DomainTrack): Double fun get10PointScore(track: DomainTrack): Double
fun indexToScore(index: Int): Float fun indexToScore(index: Int): Double
fun displayScore(track: DomainTrack): String 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 // TODO: move this to an interactor, and update all trackers based on common data
suspend fun register(item: Track, mangaId: Long) 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) suspend fun setRemoteLastChapterRead(track: Track, chapterNumber: Int)

View File

@ -20,12 +20,12 @@ import tachiyomi.domain.track.model.Track as DomainTrack
class Anilist(id: Long) : BaseTracker(id, "AniList"), DeletableTracker { class Anilist(id: Long) : BaseTracker(id, "AniList"), DeletableTracker {
companion object { companion object {
const val READING = 1 const val READING = 1L
const val COMPLETED = 2 const val COMPLETED = 2L
const val ON_HOLD = 3 const val ON_HOLD = 3L
const val DROPPED = 4 const val DROPPED = 4L
const val PLAN_TO_READ = 5 const val PLAN_TO_READ = 5L
const val REREADING = 6 const val REREADING = 6L
const val POINT_100 = "POINT_100" const val POINT_100 = "POINT_100"
const val POINT_10 = "POINT_10" 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 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) 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 READING -> MR.strings.reading
PLAN_TO_READ -> MR.strings.plan_to_read PLAN_TO_READ -> MR.strings.plan_to_read
COMPLETED -> MR.strings.completed COMPLETED -> MR.strings.completed
@ -72,11 +72,11 @@ class Anilist(id: Long) : BaseTracker(id, "AniList"), DeletableTracker {
else -> null 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> { override fun getScoreList(): ImmutableList<String> {
return when (scorePreference.get()) { return when (scorePreference.get()) {
@ -99,24 +99,24 @@ class Anilist(id: Long) : BaseTracker(id, "AniList"), DeletableTracker {
return track.score / 10.0 return track.score / 10.0
} }
override fun indexToScore(index: Int): Float { override fun indexToScore(index: Int): Double {
return when (scorePreference.get()) { return when (scorePreference.get()) {
// 10 point // 10 point
POINT_10 -> index * 10f POINT_10 -> index * 10.0
// 100 point // 100 point
POINT_100 -> index.toFloat() POINT_100 -> index.toDouble()
// 5 stars // 5 stars
POINT_5 -> when (index) { POINT_5 -> when (index) {
0 -> 0f 0 -> 0.0
else -> index * 20f - 10f else -> index * 20.0 - 10.0
} }
// Smiley // Smiley
POINT_3 -> when (index) { POINT_3 -> when (index) {
0 -> 0f 0 -> 0.0
else -> index * 25f + 10f else -> index * 25.0 + 10.0
} }
// 10 point decimal // 10 point decimal
POINT_10_DECIMAL -> index.toFloat() POINT_10_DECIMAL -> index.toDouble()
else -> throw Exception("Unknown score type") else -> throw Exception("Unknown score type")
} }
} }
@ -153,12 +153,12 @@ class Anilist(id: Long) : BaseTracker(id, "AniList"), DeletableTracker {
if (track.status != COMPLETED) { if (track.status != COMPLETED) {
if (didReadChapter) { 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.status = COMPLETED
track.finished_reading_date = System.currentTimeMillis() track.finished_reading_date = System.currentTimeMillis()
} else if (track.status != REREADING) { } else if (track.status != REREADING) {
track.status = READING track.status = READING
if (track.last_chapter_read == 1F) { if (track.last_chapter_read == 1.0) {
track.started_reading_date = System.currentTimeMillis() track.started_reading_date = System.currentTimeMillis()
} }
} }
@ -185,14 +185,14 @@ class Anilist(id: Long) : BaseTracker(id, "AniList"), DeletableTracker {
if (track.status != COMPLETED) { if (track.status != COMPLETED) {
val isRereading = track.status == REREADING 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) update(track)
} else { } else {
// Set default fields if it's not found in the list // Set default fields if it's not found in the list
track.status = if (hasReadChapters) READING else PLAN_TO_READ track.status = if (hasReadChapters) READING else PLAN_TO_READ
track.score = 0F track.score = 0.0
add(track) add(track)
} }
} }

View File

@ -20,6 +20,7 @@ import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive import kotlinx.serialization.json.jsonPrimitive
import kotlinx.serialization.json.long import kotlinx.serialization.json.long
import kotlinx.serialization.json.longOrNull
import kotlinx.serialization.json.put import kotlinx.serialization.json.put
import kotlinx.serialization.json.putJsonObject import kotlinx.serialization.json.putJsonObject
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
@ -312,7 +313,7 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
struct["format"]!!.jsonPrimitive.content.replace("_", "-"), struct["format"]!!.jsonPrimitive.content.replace("_", "-"),
struct["status"]!!.jsonPrimitive.contentOrNull ?: "", struct["status"]!!.jsonPrimitive.contentOrNull ?: "",
parseDate(struct, "startDate"), parseDate(struct, "startDate"),
struct["chapters"]!!.jsonPrimitive.intOrNull ?: 0, struct["chapters"]!!.jsonPrimitive.longOrNull ?: 0,
struct["averageScore"]?.jsonPrimitive?.intOrNull ?: -1, struct["averageScore"]?.jsonPrimitive?.intOrNull ?: -1,
) )
} }

View File

@ -1,5 +1,6 @@
package eu.kanade.tachiyomi.data.track.anilist package eu.kanade.tachiyomi.data.track.anilist
import eu.kanade.tachiyomi.BuildConfig
import okhttp3.Interceptor import okhttp3.Interceptor
import okhttp3.Response import okhttp3.Response
import java.io.IOException 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. // Add the authorization header to the original request.
val authRequest = originalRequest.newBuilder() val authRequest = originalRequest.newBuilder()
.addHeader("Authorization", "Bearer ${oauth!!.access_token}") .addHeader("Authorization", "Bearer ${oauth!!.access_token}")
.header("User-Agent", "Mihon v${BuildConfig.VERSION_NAME} (${BuildConfig.APPLICATION_ID})")
.build() .build()
return chain.proceed(authRequest) return chain.proceed(authRequest)

View File

@ -19,7 +19,7 @@ data class ALManga(
val format: String, val format: String,
val publishing_status: String, val publishing_status: String,
val start_date_fuzzy: Long, val start_date_fuzzy: Long,
val total_chapters: Int, val total_chapters: Long,
val average_score: Int, val average_score: Int,
) { ) {
@ -29,7 +29,7 @@ data class ALManga(
total_chapters = this@ALManga.total_chapters total_chapters = this@ALManga.total_chapters
cover_url = image_url_lge cover_url = image_url_lge
summary = description?.htmlDecode() ?: "" summary = description?.htmlDecode() ?: ""
score = average_score.toFloat() score = average_score.toDouble()
tracking_url = AnilistApi.mangaUrl(remote_id) tracking_url = AnilistApi.mangaUrl(remote_id)
publishing_status = this@ALManga.publishing_status publishing_status = this@ALManga.publishing_status
publishing_type = format publishing_type = format
@ -58,10 +58,10 @@ data class ALUserManga(
remote_id = manga.remote_id remote_id = manga.remote_id
title = manga.title_user_pref title = manga.title_user_pref
status = toTrackStatus() status = toTrackStatus()
score = score_raw.toFloat() score = score_raw.toDouble()
started_reading_date = start_date_fuzzy started_reading_date = start_date_fuzzy
finished_reading_date = completed_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 library_id = this@ALUserManga.library_id
total_chapters = manga.total_chapters total_chapters = manga.total_chapters
} }

View File

@ -35,7 +35,7 @@ class Bangumi(id: Long) : BaseTracker(id, "Bangumi") {
override suspend fun update(track: Track, didReadChapter: Boolean): Track { override suspend fun update(track: Track, didReadChapter: Boolean): Track {
if (track.status != COMPLETED) { if (track.status != COMPLETED) {
if (didReadChapter) { 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.status = COMPLETED
} else { } else {
track.status = READING track.status = READING
@ -64,7 +64,7 @@ class Bangumi(id: Long) : BaseTracker(id, "Bangumi") {
} else { } else {
// Set default fields if it's not found in the list // Set default fields if it's not found in the list
track.status = if (hasReadChapters) READING else PLAN_TO_READ track.status = if (hasReadChapters) READING else PLAN_TO_READ
track.score = 0F track.score = 0.0
add(track) add(track)
update(track) update(track)
} }
@ -87,11 +87,11 @@ class Bangumi(id: Long) : BaseTracker(id, "Bangumi") {
override fun getLogoColor() = Color.rgb(240, 145, 153) 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) 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 READING -> MR.strings.reading
PLAN_TO_READ -> MR.strings.plan_to_read PLAN_TO_READ -> MR.strings.plan_to_read
COMPLETED -> MR.strings.completed COMPLETED -> MR.strings.completed
@ -100,11 +100,11 @@ class Bangumi(id: Long) : BaseTracker(id, "Bangumi") {
else -> null 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) override suspend fun login(username: String, password: String) = login(password)
@ -137,11 +137,11 @@ class Bangumi(id: Long) : BaseTracker(id, "Bangumi") {
} }
companion object { companion object {
const val READING = 3 const val READING = 3L
const val COMPLETED = 2 const val COMPLETED = 2L
const val ON_HOLD = 4 const val ON_HOLD = 4L
const val DROPPED = 5 const val DROPPED = 5L
const val PLAN_TO_READ = 1 const val PLAN_TO_READ = 1L
private val SCORE_LIST = IntRange(0, 10) private val SCORE_LIST = IntRange(0, 10)
.map(Int::toString) .map(Int::toString)

View File

@ -11,7 +11,7 @@ import eu.kanade.tachiyomi.network.parseAs
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.contentOrNull import kotlinx.serialization.json.contentOrNull
import kotlinx.serialization.json.floatOrNull import kotlinx.serialization.json.doubleOrNull
import kotlinx.serialization.json.int import kotlinx.serialization.json.int
import kotlinx.serialization.json.jsonArray import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject import kotlinx.serialization.json.jsonObject
@ -105,11 +105,11 @@ class BangumiApi(
"" ""
} }
val totalChapters = if (obj["eps_count"] != null) { val totalChapters = if (obj["eps_count"] != null) {
obj["eps_count"]!!.jsonPrimitive.int obj["eps_count"]!!.jsonPrimitive.long
} else { } else {
0 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 { return TrackSearch.create(trackId).apply {
remote_id = obj["id"]!!.jsonPrimitive.long remote_id = obj["id"]!!.jsonPrimitive.long
title = obj["name_cn"]!!.jsonPrimitive.content title = obj["name_cn"]!!.jsonPrimitive.content
@ -152,7 +152,7 @@ class BangumiApi(
} else { } else {
json.decodeFromString<Collection>(responseBody).let { json.decodeFromString<Collection>(responseBody).let {
track.status = it.status?.id!! 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.score = it.rating!!
track track
} }
@ -182,8 +182,8 @@ class BangumiApi(
) )
companion object { companion object {
private const val clientId = "bgm10555cda0762e80ca" private const val clientId = "bgm291665acbd06a4c28"
private const val clientSecret = "8fff394a8627b4c388cbf349ec865775" private const val clientSecret = "43e5ce36b207de16e5d3cfd3e79118db"
private const val apiUrl = "https://api.bgm.tv" private const val apiUrl = "https://api.bgm.tv"
private const val oauthUrl = "https://bgm.tv/oauth/access_token" private const val oauthUrl = "https://bgm.tv/oauth/access_token"

View File

@ -1,5 +1,6 @@
package eu.kanade.tachiyomi.data.track.bangumi package eu.kanade.tachiyomi.data.track.bangumi
import eu.kanade.tachiyomi.BuildConfig
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import okhttp3.FormBody import okhttp3.FormBody
import okhttp3.Interceptor import okhttp3.Interceptor
@ -29,22 +30,23 @@ class BangumiInterceptor(private val bangumi: Bangumi) : Interceptor {
} }
} }
val authRequest = if (originalRequest.method == "GET") { return originalRequest.newBuilder()
originalRequest.newBuilder() .header(
.header("User-Agent", "Tachiyomi") "User-Agent",
.url( "antsylich/Mihon/v${BuildConfig.VERSION_NAME} (Android) (http://github.com/mihonapp/mihon)",
originalRequest.url.newBuilder() )
.addQueryParameter("access_token", currAuth.access_token).build(), .apply {
) if (originalRequest.method == "GET") {
.build() val newUrl = originalRequest.url.newBuilder()
} else { .addQueryParameter("access_token", currAuth.access_token)
originalRequest.newBuilder() .build()
.post(addToken(currAuth.access_token, originalRequest.body as FormBody)) url(newUrl)
.header("User-Agent", "Tachiyomi") } else {
.build() post(addToken(currAuth.access_token, originalRequest.body as FormBody))
} }
}
return chain.proceed(authRequest) .build()
.let(chain::proceed)
} }
fun newAuth(oauth: OAuth?) { fun newAuth(oauth: OAuth?) {

View File

@ -16,7 +16,7 @@ data class Collection(
val comment: String? = "", val comment: String? = "",
val ep_status: Int? = 0, val ep_status: Int? = 0,
val lasttouch: Int? = 0, val lasttouch: Int? = 0,
val rating: Float? = 0f, val rating: Double? = 0.0,
val status: Status? = Status(), val status: Status? = Status(),
val tag: List<String?>? = emptyList(), val tag: List<String?>? = emptyList(),
val user: User? = User(), val user: User? = User(),
@ -25,7 +25,7 @@ data class Collection(
@Serializable @Serializable
data class Status( data class Status(
val id: Int? = 0, val id: Long? = 0,
val name: String? = "", val name: String? = "",
val type: String? = "", val type: String? = "",
) )

View File

@ -22,9 +22,9 @@ import tachiyomi.domain.track.model.Track as DomainTrack
class Kavita(id: Long) : BaseTracker(id, "Kavita"), EnhancedTracker { class Kavita(id: Long) : BaseTracker(id, "Kavita"), EnhancedTracker {
companion object { companion object {
const val UNREAD = 1 const val UNREAD = 1L
const val READING = 2 const val READING = 2L
const val COMPLETED = 3 const val COMPLETED = 3L
} }
var authentications: OAuth? = null 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 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 UNREAD -> MR.strings.unread
READING -> MR.strings.reading READING -> MR.strings.reading
COMPLETED -> MR.strings.completed COMPLETED -> MR.strings.completed
else -> null 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() 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 { override suspend fun update(track: Track, didReadChapter: Boolean): Track {
if (track.status != COMPLETED) { if (track.status != COMPLETED) {
if (didReadChapter) { 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.status = COMPLETED
} else { } else {
track.status = READING track.status = READING

View File

@ -93,7 +93,7 @@ class KavitaApi(private val client: OkHttpClient, interceptor: KavitaInterceptor
* Ignores volumes. * Ignores volumes.
* Volumes consisting of 1 file treated as chapter * Volumes consisting of 1 file treated as chapter
*/ */
private fun getTotalChapters(url: String): Int { private fun getTotalChapters(url: String): Long {
val requestUrl = getApiVolumesUrl(url) val requestUrl = getApiVolumesUrl(url)
try { try {
val listVolumeDto = with(json) { val listVolumeDto = with(json) {
@ -101,13 +101,13 @@ class KavitaApi(private val client: OkHttpClient, interceptor: KavitaInterceptor
.execute() .execute()
.parseAs<List<VolumeDto>>() .parseAs<List<VolumeDto>>()
} }
var volumeNumber = 0 var volumeNumber = 0L
var maxChapterNumber = 0 var maxChapterNumber = 0L
for (volume in listVolumeDto) { for (volume in listVolumeDto) {
if (volume.chapters.maxOf { it.number!!.toFloat() } == 0f) { if (volume.chapters.maxOf { it.number!!.toFloat() } == 0f) {
volumeNumber++ volumeNumber++
} else if (maxChapterNumber < volume.chapters.maxOf { it.number!!.toFloat() }) { } 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 seriesId = getIdFromUrl(url)
val requestUrl = "${getApiFromUrl(url)}/Tachiyomi/latest-chapter?seriesId=$seriesId" val requestUrl = "${getApiFromUrl(url)}/Tachiyomi/latest-chapter?seriesId=$seriesId"
try { try {
with(json) { with(json) {
authClient.newCall(GET(requestUrl)).execute().use { authClient.newCall(GET(requestUrl)).execute().use {
if (it.code == 200) { if (it.code == 200) {
return it.parseAs<ChapterDto>().number!!.replace(",", ".").toFloat() return it.parseAs<ChapterDto>().number!!.replace(",", ".").toDouble()
} }
if (it.code == 204) { 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" } ) { "Exception getting latest chapter read. Could not get itemRequest: $requestUrl" }
throw e throw e
} }
return 0F return 0.0
} }
suspend fun getTrackSearch(url: String): TrackSearch = withIOContext { suspend fun getTrackSearch(url: String): TrackSearch = withIOContext {

View File

@ -18,7 +18,7 @@ class KavitaInterceptor(private val kavita: Kavita) : Interceptor {
// Add the authorization header to the original request. // Add the authorization header to the original request.
val authRequest = originalRequest.newBuilder() val authRequest = originalRequest.newBuilder()
.addHeader("Authorization", "Bearer $jwtToken") .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() .build()
return chain.proceed(authRequest) return chain.proceed(authRequest)

View File

@ -19,11 +19,11 @@ import tachiyomi.domain.track.model.Track as DomainTrack
class Kitsu(id: Long) : BaseTracker(id, "Kitsu"), DeletableTracker { class Kitsu(id: Long) : BaseTracker(id, "Kitsu"), DeletableTracker {
companion object { companion object {
const val READING = 1 const val READING = 1L
const val COMPLETED = 2 const val COMPLETED = 2L
const val ON_HOLD = 3 const val ON_HOLD = 3L
const val DROPPED = 4 const val DROPPED = 4L
const val PLAN_TO_READ = 5 const val PLAN_TO_READ = 5L
} }
override val supportsReadingDates: Boolean = true 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 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) 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 READING -> MR.strings.reading
PLAN_TO_READ -> MR.strings.plan_to_read PLAN_TO_READ -> MR.strings.plan_to_read
COMPLETED -> MR.strings.completed COMPLETED -> MR.strings.completed
@ -51,19 +51,19 @@ class Kitsu(id: Long) : BaseTracker(id, "Kitsu"), DeletableTracker {
else -> null 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> { override fun getScoreList(): ImmutableList<String> {
val df = DecimalFormat("0.#") val df = DecimalFormat("0.#")
return (listOf("0") + IntRange(2, 20).map { df.format(it / 2f) }).toImmutableList() return (listOf("0") + IntRange(2, 20).map { df.format(it / 2f) }).toImmutableList()
} }
override fun indexToScore(index: Int): Float { override fun indexToScore(index: Int): Double {
return if (index > 0) (index + 1) / 2f else 0f return if (index > 0) (index + 1) / 2.0 else 0.0
} }
override fun displayScore(track: DomainTrack): String { 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 { override suspend fun update(track: Track, didReadChapter: Boolean): Track {
if (track.status != COMPLETED) { if (track.status != COMPLETED) {
if (didReadChapter) { 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.status = COMPLETED
track.finished_reading_date = System.currentTimeMillis() track.finished_reading_date = System.currentTimeMillis()
} else { } else {
track.status = READING track.status = READING
if (track.last_chapter_read == 1F) { if (track.last_chapter_read == 1.0) {
track.started_reading_date = System.currentTimeMillis() track.started_reading_date = System.currentTimeMillis()
} }
} }
@ -110,7 +110,7 @@ class Kitsu(id: Long) : BaseTracker(id, "Kitsu"), DeletableTracker {
update(track) update(track)
} else { } else {
track.status = if (hasReadChapters) READING else PLAN_TO_READ track.status = if (hasReadChapters) READING else PLAN_TO_READ
track.score = 0F track.score = 0.0
add(track) add(track)
} }
} }

View File

@ -1,5 +1,6 @@
package eu.kanade.tachiyomi.data.track.kitsu package eu.kanade.tachiyomi.data.track.kitsu
import eu.kanade.tachiyomi.BuildConfig
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import okhttp3.Interceptor import okhttp3.Interceptor
import okhttp3.Response import okhttp3.Response
@ -34,6 +35,7 @@ class KitsuInterceptor(private val kitsu: Kitsu) : Interceptor {
// Add the authorization header to the original request. // Add the authorization header to the original request.
val authRequest = originalRequest.newBuilder() val authRequest = originalRequest.newBuilder()
.addHeader("Authorization", "Bearer ${oauth!!.access_token}") .addHeader("Authorization", "Bearer ${oauth!!.access_token}")
.header("User-Agent", "Mihon v${BuildConfig.VERSION_NAME} (${BuildConfig.APPLICATION_ID})")
.header("Accept", "application/vnd.api+json") .header("Accept", "application/vnd.api+json")
.header("Content-Type", "application/vnd.api+json") .header("Content-Type", "application/vnd.api+json")
.build() .build()

View File

@ -8,10 +8,10 @@ import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.contentOrNull import kotlinx.serialization.json.contentOrNull
import kotlinx.serialization.json.int import kotlinx.serialization.json.int
import kotlinx.serialization.json.intOrNull
import kotlinx.serialization.json.jsonObject import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive import kotlinx.serialization.json.jsonPrimitive
import kotlinx.serialization.json.long import kotlinx.serialization.json.long
import kotlinx.serialization.json.longOrNull
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Date import java.util.Date
import java.util.Locale import java.util.Locale
@ -19,7 +19,7 @@ import java.util.Locale
class KitsuSearchManga(obj: JsonObject) { class KitsuSearchManga(obj: JsonObject) {
val id = obj["id"]!!.jsonPrimitive.long val id = obj["id"]!!.jsonPrimitive.long
private val canonicalTitle = obj["canonicalTitle"]!!.jsonPrimitive.content 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 subType = obj["subtype"]?.jsonPrimitive?.contentOrNull
val original = try { val original = try {
obj["posterImage"]?.jsonObject?.get("original")?.jsonPrimitive?.content obj["posterImage"]?.jsonObject?.get("original")?.jsonPrimitive?.content
@ -28,7 +28,7 @@ class KitsuSearchManga(obj: JsonObject) {
null null
} }
private val synopsis = obj["synopsis"]?.jsonPrimitive?.contentOrNull 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 { private var startDate = obj["startDate"]?.jsonPrimitive?.contentOrNull?.let {
val outputDf = SimpleDateFormat("yyyy-MM-dd", Locale.US) val outputDf = SimpleDateFormat("yyyy-MM-dd", Locale.US)
outputDf.format(Date(it.toLong() * 1000)) outputDf.format(Date(it.toLong() * 1000))
@ -43,7 +43,7 @@ class KitsuSearchManga(obj: JsonObject) {
cover_url = original ?: "" cover_url = original ?: ""
summary = synopsis ?: "" summary = synopsis ?: ""
tracking_url = KitsuApi.mangaUrl(remote_id) tracking_url = KitsuApi.mangaUrl(remote_id)
score = rating ?: -1f score = rating ?: -1.0
publishing_status = if (endDate == null) { publishing_status = if (endDate == null) {
"Publishing" "Publishing"
} else { } else {
@ -57,7 +57,7 @@ class KitsuSearchManga(obj: JsonObject) {
class KitsuLibManga(obj: JsonObject, manga: JsonObject) { class KitsuLibManga(obj: JsonObject, manga: JsonObject) {
val id = manga["id"]!!.jsonPrimitive.int val id = manga["id"]!!.jsonPrimitive.int
private val canonicalTitle = manga["attributes"]!!.jsonObject["canonicalTitle"]!!.jsonPrimitive.content 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 type = manga["attributes"]!!.jsonObject["mangaType"]?.jsonPrimitive?.contentOrNull.orEmpty()
val original = manga["attributes"]!!.jsonObject["posterImage"]!!.jsonObject["original"]!!.jsonPrimitive.content val original = manga["attributes"]!!.jsonObject["posterImage"]!!.jsonObject["original"]!!.jsonPrimitive.content
private val synopsis = manga["attributes"]!!.jsonObject["synopsis"]!!.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) started_reading_date = KitsuDateHelper.parse(startedAt)
finished_reading_date = KitsuDateHelper.parse(finishedAt) finished_reading_date = KitsuDateHelper.parse(finishedAt)
status = toTrackStatus() status = toTrackStatus()
score = ratingTwenty?.let { it.toInt() / 2f } ?: 0f score = ratingTwenty?.let { it.toInt() / 2.0 } ?: 0.0
last_chapter_read = progress.toFloat() last_chapter_read = progress.toDouble()
} }
private fun toTrackStatus() = when (status) { private fun toTrackStatus() = when (status) {

View File

@ -19,9 +19,9 @@ import tachiyomi.domain.track.model.Track as DomainTrack
class Komga(id: Long) : BaseTracker(id, "Komga"), EnhancedTracker { class Komga(id: Long) : BaseTracker(id, "Komga"), EnhancedTracker {
companion object { companion object {
const val UNREAD = 1 const val UNREAD = 1L
const val READING = 2 const val READING = 2L
const val COMPLETED = 3 const val COMPLETED = 3L
} }
override val client: OkHttpClient = 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 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 UNREAD -> MR.strings.unread
READING -> MR.strings.reading READING -> MR.strings.reading
COMPLETED -> MR.strings.completed COMPLETED -> MR.strings.completed
else -> null 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() 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 { override suspend fun update(track: Track, didReadChapter: Boolean): Track {
if (track.status != COMPLETED) { if (track.status != COMPLETED) {
if (didReadChapter) { 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.status = COMPLETED
} else { } else {
track.status = READING track.status = READING

View File

@ -1,5 +1,6 @@
package eu.kanade.tachiyomi.data.track.komga 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.database.models.Track
import eu.kanade.tachiyomi.data.track.model.TrackSearch import eu.kanade.tachiyomi.data.track.model.TrackSearch
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
@ -8,6 +9,7 @@ import eu.kanade.tachiyomi.network.parseAs
import kotlinx.serialization.encodeToString import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import logcat.LogPriority import logcat.LogPriority
import okhttp3.Headers
import okhttp3.MediaType.Companion.toMediaType import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
@ -23,6 +25,12 @@ class KomgaApi(
private val client: OkHttpClient, 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() private val json: Json by injectLazy()
suspend fun getTrackSearch(url: String): TrackSearch = suspend fun getTrackSearch(url: String): TrackSearch =
@ -30,12 +38,12 @@ class KomgaApi(
try { try {
val track = with(json) { val track = with(json) {
if (url.contains(READLIST_API)) { if (url.contains(READLIST_API)) {
client.newCall(GET(url)) client.newCall(GET(url, headers))
.awaitSuccess() .awaitSuccess()
.parseAs<ReadListDto>() .parseAs<ReadListDto>()
.toTrack() .toTrack()
} else { } else {
client.newCall(GET(url)) client.newCall(GET(url, headers))
.awaitSuccess() .awaitSuccess()
.parseAs<SeriesDto>() .parseAs<SeriesDto>()
.toTrack() .toTrack()
@ -43,7 +51,9 @@ class KomgaApi(
} }
val progress = client 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 { .awaitSuccess().let {
with(json) { with(json) {
if (url.contains("/api/v1/series/")) { if (url.contains("/api/v1/series/")) {
@ -57,7 +67,7 @@ class KomgaApi(
track.apply { track.apply {
cover_url = "$url/thumbnail" cover_url = "$url/thumbnail"
tracking_url = url tracking_url = url
total_chapters = progress.maxNumberSort.toInt() total_chapters = progress.maxNumberSort.toLong()
status = when (progress.booksCount) { status = when (progress.booksCount) {
progress.booksUnreadCount -> Komga.UNREAD progress.booksUnreadCount -> Komga.UNREAD
progress.booksReadCount -> Komga.COMPLETED progress.booksReadCount -> Komga.COMPLETED
@ -80,6 +90,7 @@ class KomgaApi(
client.newCall( client.newCall(
Request.Builder() Request.Builder()
.url("${track.tracking_url.replace("/api/v1/series/", "/api/v2/series/")}/read-progress/tachiyomi") .url("${track.tracking_url.replace("/api/v1/series/", "/api/v2/series/")}/read-progress/tachiyomi")
.headers(headers)
.put(payload.toRequestBody("application/json".toMediaType())) .put(payload.toRequestBody("application/json".toMediaType()))
.build(), .build(),
) )

View File

@ -65,7 +65,7 @@ data class ReadProgressUpdateDto(
@Serializable @Serializable
data class ReadProgressUpdateV2Dto( data class ReadProgressUpdateV2Dto(
val lastBookNumberSortRead: Float, val lastBookNumberSortRead: Double,
) )
@Serializable @Serializable
@ -91,7 +91,7 @@ data class ReadProgressDto(
booksReadCount, booksReadCount,
booksUnreadCount, booksUnreadCount,
booksInProgressCount, booksInProgressCount,
lastReadContinuousIndex.toFloat(), lastReadContinuousIndex.toDouble(),
booksCount.toFloat(), booksCount.toFloat(),
) )
} }
@ -102,6 +102,6 @@ data class ReadProgressV2Dto(
val booksReadCount: Int, val booksReadCount: Int,
val booksUnreadCount: Int, val booksUnreadCount: Int,
val booksInProgressCount: Int, val booksInProgressCount: Int,
val lastReadContinuousNumberSort: Float, val lastReadContinuousNumberSort: Double,
val maxNumberSort: Float, val maxNumberSort: Float,
) )

View File

@ -19,11 +19,11 @@ import tachiyomi.domain.track.model.Track as DomainTrack
class MangaUpdates(id: Long) : BaseTracker(id, "MangaUpdates"), DeletableTracker { class MangaUpdates(id: Long) : BaseTracker(id, "MangaUpdates"), DeletableTracker {
companion object { companion object {
const val READING_LIST = 0 const val READING_LIST = 0L
const val WISH_LIST = 1 const val WISH_LIST = 1L
const val COMPLETE_LIST = 2 const val COMPLETE_LIST = 2L
const val UNFINISHED_LIST = 3 const val UNFINISHED_LIST = 3L
const val ON_HOLD_LIST = 4 const val ON_HOLD_LIST = 4L
private val SCORE_LIST = (0..10) private val SCORE_LIST = (0..10)
.flatMap { decimal -> .flatMap { decimal ->
@ -46,11 +46,11 @@ class MangaUpdates(id: Long) : BaseTracker(id, "MangaUpdates"), DeletableTracker
override fun getLogoColor(): Int = Color.rgb(146, 160, 173) 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) 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 READING_LIST -> MR.strings.reading_list
WISH_LIST -> MR.strings.wish_list WISH_LIST -> MR.strings.wish_list
COMPLETE_LIST -> MR.strings.complete_list COMPLETE_LIST -> MR.strings.complete_list
@ -59,15 +59,15 @@ class MangaUpdates(id: Long) : BaseTracker(id, "MangaUpdates"), DeletableTracker
else -> null 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 getScoreList(): ImmutableList<String> = SCORE_LIST
override fun indexToScore(index: Int): Float = if (index == 0) 0f else 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() override fun displayScore(track: DomainTrack): String = track.score.toString()
@ -88,7 +88,7 @@ class MangaUpdates(id: Long) : BaseTracker(id, "MangaUpdates"), DeletableTracker
val (series, rating) = api.getSeriesListItem(track) val (series, rating) = api.getSeriesListItem(track)
track.copyFrom(series, rating) track.copyFrom(series, rating)
} catch (e: Exception) { } catch (e: Exception) {
track.score = 0f track.score = 0.0
api.addSeriesToList(track, hasReadChapters) api.addSeriesToList(track, hasReadChapters)
track track
} }
@ -108,7 +108,7 @@ class MangaUpdates(id: Long) : BaseTracker(id, "MangaUpdates"), DeletableTracker
private fun Track.copyFrom(item: ListItem, rating: Rating?): Track = apply { private fun Track.copyFrom(item: ListItem, rating: Rating?): Track = apply {
item.copyTo(this) item.copyTo(this)
score = rating?.rating ?: 0f score = rating?.rating ?: 0.0
} }
override suspend fun login(username: String, password: String) { override suspend fun login(username: String, password: String) {

View File

@ -79,7 +79,7 @@ class MangaUpdatesApi(
.let { .let {
if (it.code == 200) { if (it.code == 200) {
track.status = status 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) { private suspend fun updateSeriesRating(track: Track) {
if (track.score != 0f) { if (track.score < 0.0) return
if (track.score != 0.0) {
val body = buildJsonObject { val body = buildJsonObject {
put("rating", track.score) put("rating", track.score)
} }

View File

@ -9,7 +9,7 @@ import kotlinx.serialization.Serializable
data class ListItem( data class ListItem(
val series: Series? = null, val series: Series? = null,
@SerialName("list_id") @SerialName("list_id")
val listId: Int? = null, val listId: Long? = null,
val status: Status? = null, val status: Status? = null,
val priority: Int? = null, val priority: Int? = null,
) )
@ -17,6 +17,6 @@ data class ListItem(
fun ListItem.copyTo(track: Track): Track { fun ListItem.copyTo(track: Track): Track {
return track.apply { return track.apply {
this.status = listId ?: READING_LIST 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
} }
} }

View File

@ -5,11 +5,11 @@ import kotlinx.serialization.Serializable
@Serializable @Serializable
data class Rating( data class Rating(
val rating: Float? = null, val rating: Double? = null,
) )
fun Rating.copyTo(track: Track): Track { fun Rating.copyTo(track: Track): Track {
return track.apply { return track.apply {
this.score = rating ?: 0f this.score = rating ?: 0.0
} }
} }

View File

@ -8,7 +8,7 @@ class TrackSearch : Track {
override var manga_id: Long = 0 override var manga_id: Long = 0
override var tracker_id: Int = 0 override var tracker_id: Long = 0
override var remote_id: Long = 0 override var remote_id: Long = 0
@ -16,13 +16,13 @@ class TrackSearch : Track {
override lateinit var title: String 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 override var started_reading_date: Long = 0
@ -55,14 +55,14 @@ class TrackSearch : Track {
override fun hashCode(): Int { override fun hashCode(): Int {
var result = manga_id.hashCode() var result = manga_id.hashCode()
result = 31 * result + tracker_id result = 31 * result + tracker_id.hashCode()
result = 31 * result + remote_id.hashCode() result = 31 * result + remote_id.hashCode()
return result return result
} }
companion object { companion object {
fun create(serviceId: Long): TrackSearch = TrackSearch().apply { fun create(serviceId: Long): TrackSearch = TrackSearch().apply {
tracker_id = serviceId.toInt() tracker_id = serviceId
} }
} }
} }

View File

@ -18,12 +18,12 @@ import tachiyomi.domain.track.model.Track as DomainTrack
class MyAnimeList(id: Long) : BaseTracker(id, "MyAnimeList"), DeletableTracker { class MyAnimeList(id: Long) : BaseTracker(id, "MyAnimeList"), DeletableTracker {
companion object { companion object {
const val READING = 1 const val READING = 1L
const val COMPLETED = 2 const val COMPLETED = 2L
const val ON_HOLD = 3 const val ON_HOLD = 3L
const val DROPPED = 4 const val DROPPED = 4L
const val PLAN_TO_READ = 6 const val PLAN_TO_READ = 6L
const val REREADING = 7 const val REREADING = 7L
private const val SEARCH_ID_PREFIX = "id:" private const val SEARCH_ID_PREFIX = "id:"
private const val SEARCH_LIST_PREFIX = "my:" 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 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) } private val api by lazy { MyAnimeListApi(id, client, interceptor) }
override val supportsReadingDates: Boolean = true 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 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) 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 READING -> MR.strings.reading
PLAN_TO_READ -> MR.strings.plan_to_read PLAN_TO_READ -> MR.strings.plan_to_read
COMPLETED -> MR.strings.completed COMPLETED -> MR.strings.completed
@ -58,11 +58,11 @@ class MyAnimeList(id: Long) : BaseTracker(id, "MyAnimeList"), DeletableTracker {
else -> null 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 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 { override suspend fun update(track: Track, didReadChapter: Boolean): Track {
if (track.status != COMPLETED) { if (track.status != COMPLETED) {
if (didReadChapter) { 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.status = COMPLETED
track.finished_reading_date = System.currentTimeMillis() track.finished_reading_date = System.currentTimeMillis()
} else if (track.status != REREADING) { } else if (track.status != REREADING) {
track.status = READING track.status = READING
if (track.last_chapter_read == 1F) { if (track.last_chapter_read == 1.0) {
track.started_reading_date = System.currentTimeMillis() track.started_reading_date = System.currentTimeMillis()
} }
} }
@ -104,14 +104,14 @@ class MyAnimeList(id: Long) : BaseTracker(id, "MyAnimeList"), DeletableTracker {
if (track.status != COMPLETED) { if (track.status != COMPLETED) {
val isRereading = track.status == REREADING 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) update(track)
} else { } else {
// Set default fields if it's not found in the list // Set default fields if it's not found in the list
track.status = if (hasReadChapters) READING else PLAN_TO_READ track.status = if (hasReadChapters) READING else PLAN_TO_READ
track.score = 0F track.score = 0.0
add(track) add(track)
} }
} }
@ -155,6 +155,14 @@ class MyAnimeList(id: Long) : BaseTracker(id, "MyAnimeList"), DeletableTracker {
interceptor.setAuth(null) interceptor.setAuth(null)
} }
fun getIfAuthExpired(): Boolean {
return trackPreferences.trackAuthExpired(this).get()
}
fun setAuthExpired() {
trackPreferences.trackAuthExpired(this).set(true)
}
fun saveOAuth(oAuth: OAuth?) { fun saveOAuth(oAuth: OAuth?) {
trackPreferences.trackToken(this).set(json.encodeToString(oAuth)) trackPreferences.trackToken(this).set(json.encodeToString(oAuth))
} }

View File

@ -16,8 +16,8 @@ import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.boolean import kotlinx.serialization.json.boolean
import kotlinx.serialization.json.contentOrNull import kotlinx.serialization.json.contentOrNull
import kotlinx.serialization.json.float import kotlinx.serialization.json.double
import kotlinx.serialization.json.floatOrNull import kotlinx.serialization.json.doubleOrNull
import kotlinx.serialization.json.int import kotlinx.serialization.json.int
import kotlinx.serialization.json.jsonArray import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject import kotlinx.serialization.json.jsonObject
@ -47,13 +47,13 @@ class MyAnimeListApi(
suspend fun getAccessToken(authCode: String): OAuth { suspend fun getAccessToken(authCode: String): OAuth {
return withIOContext { return withIOContext {
val formBody: RequestBody = FormBody.Builder() val formBody: RequestBody = FormBody.Builder()
.add("client_id", clientId) .add("client_id", CLIENT_ID)
.add("code", authCode) .add("code", authCode)
.add("code_verifier", codeVerifier) .add("code_verifier", codeVerifier)
.add("grant_type", "authorization_code") .add("grant_type", "authorization_code")
.build() .build()
with(json) { with(json) {
client.newCall(POST("$baseOAuthUrl/token", body = formBody)) client.newCall(POST("$BASE_OAUTH_URL/token", body = formBody))
.awaitSuccess() .awaitSuccess()
.parseAs() .parseAs()
} }
@ -63,7 +63,7 @@ class MyAnimeListApi(
suspend fun getCurrentUser(): String { suspend fun getCurrentUser(): String {
return withIOContext { return withIOContext {
val request = Request.Builder() val request = Request.Builder()
.url("$baseApiUrl/users/@me") .url("$BASE_API_URL/users/@me")
.get() .get()
.build() .build()
with(json) { with(json) {
@ -77,7 +77,7 @@ class MyAnimeListApi(
suspend fun search(query: String): List<TrackSearch> { suspend fun search(query: String): List<TrackSearch> {
return withIOContext { 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... // MAL API throws a 400 when the query is over 64 characters...
.appendQueryParameter("q", query.take(64)) .appendQueryParameter("q", query.take(64))
.appendQueryParameter("nsfw", "true") .appendQueryParameter("nsfw", "true")
@ -102,7 +102,7 @@ class MyAnimeListApi(
suspend fun getMangaDetails(id: Int): TrackSearch { suspend fun getMangaDetails(id: Int): TrackSearch {
return withIOContext { return withIOContext {
val url = "$baseApiUrl/manga".toUri().buildUpon() val url = "$BASE_API_URL/manga".toUri().buildUpon()
.appendPath(id.toString()) .appendPath(id.toString())
.appendQueryParameter( .appendQueryParameter(
"fields", "fields",
@ -119,8 +119,8 @@ class MyAnimeListApi(
remote_id = obj["id"]!!.jsonPrimitive.long remote_id = obj["id"]!!.jsonPrimitive.long
title = obj["title"]!!.jsonPrimitive.content title = obj["title"]!!.jsonPrimitive.content
summary = obj["synopsis"]?.jsonPrimitive?.content ?: "" summary = obj["synopsis"]?.jsonPrimitive?.content ?: ""
total_chapters = obj["num_chapters"]!!.jsonPrimitive.int total_chapters = obj["num_chapters"]!!.jsonPrimitive.long
score = obj["mean"]?.jsonPrimitive?.floatOrNull ?: -1f score = obj["mean"]?.jsonPrimitive?.doubleOrNull ?: -1.0
cover_url = cover_url =
obj["main_picture"]?.jsonObject?.get("large")?.jsonPrimitive?.content obj["main_picture"]?.jsonObject?.get("large")?.jsonPrimitive?.content
?: "" ?: ""
@ -178,7 +178,7 @@ class MyAnimeListApi(
suspend fun findListItem(track: Track): Track? { suspend fun findListItem(track: Track): Track? {
return withIOContext { return withIOContext {
val uri = "$baseApiUrl/manga".toUri().buildUpon() val uri = "$BASE_API_URL/manga".toUri().buildUpon()
.appendPath(track.remote_id.toString()) .appendPath(track.remote_id.toString())
.appendQueryParameter("fields", "num_chapters,my_list_status{start_date,finish_date}") .appendQueryParameter("fields", "num_chapters,my_list_status{start_date,finish_date}")
.build() .build()
@ -187,7 +187,7 @@ class MyAnimeListApi(
.awaitSuccess() .awaitSuccess()
.parseAs<JsonObject>() .parseAs<JsonObject>()
.let { obj -> .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 { obj.jsonObject["my_list_status"]?.jsonObject?.let {
parseMangaItem(it, track) parseMangaItem(it, track)
} }
@ -216,7 +216,7 @@ class MyAnimeListApi(
// Check next page if there's more // Check next page if there's more
if (!obj["paging"]!!.jsonObject["next"]?.jsonPrimitive?.contentOrNull.isNullOrBlank()) { if (!obj["paging"]!!.jsonObject["next"]?.jsonPrimitive?.contentOrNull.isNullOrBlank()) {
matches + findListItems(query, offset + listPaginationAmount) matches + findListItems(query, offset + LIST_PAGINATION_AMOUNT)
} else { } else {
matches matches
} }
@ -225,9 +225,9 @@ class MyAnimeListApi(
private suspend fun getListPage(offset: Int): JsonObject { private suspend fun getListPage(offset: Int): JsonObject {
return withIOContext { 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("fields", "list_status{start_date,finish_date}")
.appendQueryParameter("limit", listPaginationAmount.toString()) .appendQueryParameter("limit", LIST_PAGINATION_AMOUNT.toString())
if (offset > 0) { if (offset > 0) {
urlBuilder.appendQueryParameter("offset", offset.toString()) urlBuilder.appendQueryParameter("offset", offset.toString())
} }
@ -249,8 +249,8 @@ class MyAnimeListApi(
return track.apply { return track.apply {
val isRereading = obj["is_rereading"]!!.jsonPrimitive.boolean val isRereading = obj["is_rereading"]!!.jsonPrimitive.boolean
status = if (isRereading) MyAnimeList.REREADING else getStatus(obj["status"]?.jsonPrimitive?.content) status = if (isRereading) MyAnimeList.REREADING else getStatus(obj["status"]?.jsonPrimitive?.content)
last_chapter_read = obj["num_chapters_read"]!!.jsonPrimitive.float last_chapter_read = obj["num_chapters_read"]!!.jsonPrimitive.double
score = obj["score"]!!.jsonPrimitive.int.toFloat() score = obj["score"]!!.jsonPrimitive.int.toDouble()
obj["start_date"]?.let { obj["start_date"]?.let {
started_reading_date = parseDate(it.jsonPrimitive.content) started_reading_date = parseDate(it.jsonPrimitive.content)
} }
@ -277,30 +277,29 @@ class MyAnimeListApi(
} }
companion object { companion object {
// Registered under arkon's MAL account private const val CLIENT_ID = "c46c9e24640a64dad5be5ca7a1a53a0f"
private const val clientId = "f46004a9c16483b6d87b5bf10de56d97"
private const val baseOAuthUrl = "https://myanimelist.net/v1/oauth2" private const val BASE_OAUTH_URL = "https://myanimelist.net/v1/oauth2"
private const val baseApiUrl = "https://api.myanimelist.net/v2" 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 = "" private var codeVerifier: String = ""
fun authUrl(): Uri = "$baseOAuthUrl/authorize".toUri().buildUpon() fun authUrl(): Uri = "$BASE_OAUTH_URL/authorize".toUri().buildUpon()
.appendQueryParameter("client_id", clientId) .appendQueryParameter("client_id", CLIENT_ID)
.appendQueryParameter("code_challenge", getPkceChallengeCode()) .appendQueryParameter("code_challenge", getPkceChallengeCode())
.appendQueryParameter("response_type", "code") .appendQueryParameter("response_type", "code")
.build() .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(id.toString())
.appendPath("my_list_status") .appendPath("my_list_status")
.build() .build()
fun refreshTokenRequest(oauth: OAuth): Request { fun refreshTokenRequest(oauth: OAuth): Request {
val formBody: RequestBody = FormBody.Builder() val formBody: RequestBody = FormBody.Builder()
.add("client_id", clientId) .add("client_id", CLIENT_ID)
.add("refresh_token", oauth.refresh_token) .add("refresh_token", oauth.refresh_token)
.add("grant_type", "refresh_token") .add("grant_type", "refresh_token")
.build() .build()
@ -312,7 +311,7 @@ class MyAnimeListApi(
.add("Authorization", "Bearer ${oauth.access_token}") .add("Authorization", "Bearer ${oauth.access_token}")
.build() .build()
return POST("$baseOAuthUrl/token", body = formBody, headers = headers) return POST("$BASE_OAUTH_URL/token", body = formBody, headers = headers)
} }
private fun getPkceChallengeCode(): String { private fun getPkceChallengeCode(): String {

View File

@ -1,5 +1,6 @@
package eu.kanade.tachiyomi.data.track.myanimelist package eu.kanade.tachiyomi.data.track.myanimelist
import eu.kanade.tachiyomi.BuildConfig
import eu.kanade.tachiyomi.network.parseAs import eu.kanade.tachiyomi.network.parseAs
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import okhttp3.Interceptor import okhttp3.Interceptor
@ -7,33 +8,32 @@ import okhttp3.Response
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import java.io.IOException 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 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 { override fun intercept(chain: Interceptor.Chain): Response {
if (tokenExpired) {
throw MALTokenExpired()
}
val originalRequest = chain.request() val originalRequest = chain.request()
if (token.isNullOrEmpty()) {
throw IOException("Not authenticated with MyAnimeList")
}
if (oauth == null) {
oauth = myanimelist.loadOAuth()
}
// Refresh access token if expired // Refresh access token if expired
if (oauth != null && oauth!!.isExpired()) { if (oauth != null && oauth!!.isExpired()) {
setAuth(refreshToken(chain)) setAuth(refreshToken(chain))
} }
if (oauth == null) { if (oauth == null) {
throw IOException("No authentication token") throw IOException("MAL: User is not authenticated")
} }
// Add the authorization header to the original request // Add the authorization header to the original request
val authRequest = originalRequest.newBuilder() val authRequest = originalRequest.newBuilder()
.addHeader("Authorization", "Bearer ${oauth!!.access_token}") .addHeader("Authorization", "Bearer ${oauth!!.access_token}")
.header("User-Agent", "Mihon v${BuildConfig.VERSION_NAME} (${BuildConfig.APPLICATION_ID})")
.build() .build()
val response = chain.proceed(authRequest) val response = chain.proceed(authRequest)
@ -50,6 +50,7 @@ class MyAnimeListInterceptor(private val myanimelist: MyAnimeList, private var t
val newRequest = originalRequest.newBuilder() val newRequest = originalRequest.newBuilder()
.addHeader("Authorization", "Bearer ${newToken.access_token}") .addHeader("Authorization", "Bearer ${newToken.access_token}")
.header("User-Agent", "Mihon v${BuildConfig.VERSION_NAME} (${BuildConfig.APPLICATION_ID})")
.build() .build()
return chain.proceed(newRequest) return chain.proceed(newRequest)
@ -63,15 +64,16 @@ class MyAnimeListInterceptor(private val myanimelist: MyAnimeList, private var t
* and the oauth object. * and the oauth object.
*/ */
fun setAuth(oauth: OAuth?) { fun setAuth(oauth: OAuth?) {
token = oauth?.access_token
this.oauth = oauth this.oauth = oauth
myanimelist.saveOAuth(oauth) myanimelist.saveOAuth(oauth)
} }
private fun refreshToken(chain: Interceptor.Chain): OAuth { private fun refreshToken(chain: Interceptor.Chain): OAuth {
val newOauth = runCatching { return runCatching {
val oauthResponse = chain.proceed(MyAnimeListApi.refreshTokenRequest(oauth!!)) val oauthResponse = chain.proceed(MyAnimeListApi.refreshTokenRequest(oauth!!))
if (oauthResponse.code == 401) {
myanimelist.setAuthExpired()
}
if (oauthResponse.isSuccessful) { if (oauthResponse.isSuccessful) {
with(json) { oauthResponse.parseAs<OAuth>() } with(json) { oauthResponse.parseAs<OAuth>() }
} else { } else {
@ -79,11 +81,9 @@ class MyAnimeListInterceptor(private val myanimelist: MyAnimeList, private var t
null null
} }
} }
.getOrNull()
if (newOauth.getOrNull() == null) { ?: throw MALTokenExpired()
throw IOException("Failed to refresh the access token")
}
return newOauth.getOrNull()!!
} }
} }
class MALTokenExpired : IOException("MAL: Login has expired")

View File

@ -18,12 +18,12 @@ import tachiyomi.domain.track.model.Track as DomainTrack
class Shikimori(id: Long) : BaseTracker(id, "Shikimori"), DeletableTracker { class Shikimori(id: Long) : BaseTracker(id, "Shikimori"), DeletableTracker {
companion object { companion object {
const val READING = 1 const val READING = 1L
const val COMPLETED = 2 const val COMPLETED = 2L
const val ON_HOLD = 3 const val ON_HOLD = 3L
const val DROPPED = 4 const val DROPPED = 4L
const val PLAN_TO_READ = 5 const val PLAN_TO_READ = 5L
const val REREADING = 6 const val REREADING = 6L
private val SCORE_LIST = IntRange(0, 10) private val SCORE_LIST = IntRange(0, 10)
.map(Int::toString) .map(Int::toString)
@ -49,7 +49,7 @@ class Shikimori(id: Long) : BaseTracker(id, "Shikimori"), DeletableTracker {
override suspend fun update(track: Track, didReadChapter: Boolean): Track { override suspend fun update(track: Track, didReadChapter: Boolean): Track {
if (track.status != COMPLETED) { if (track.status != COMPLETED) {
if (didReadChapter) { 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.status = COMPLETED
} else if (track.status != REREADING) { } else if (track.status != REREADING) {
track.status = READING track.status = READING
@ -72,14 +72,14 @@ class Shikimori(id: Long) : BaseTracker(id, "Shikimori"), DeletableTracker {
if (track.status != COMPLETED) { if (track.status != COMPLETED) {
val isRereading = track.status == REREADING 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) update(track)
} else { } else {
// Set default fields if it's not found in the list // Set default fields if it's not found in the list
track.status = if (hasReadChapters) READING else PLAN_TO_READ track.status = if (hasReadChapters) READING else PLAN_TO_READ
track.score = 0F track.score = 0.0
add(track) add(track)
} }
} }
@ -101,11 +101,11 @@ class Shikimori(id: Long) : BaseTracker(id, "Shikimori"), DeletableTracker {
override fun getLogoColor() = Color.rgb(40, 40, 40) 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) 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 READING -> MR.strings.reading
PLAN_TO_READ -> MR.strings.plan_to_read PLAN_TO_READ -> MR.strings.plan_to_read
COMPLETED -> MR.strings.completed COMPLETED -> MR.strings.completed
@ -115,11 +115,11 @@ class Shikimori(id: Long) : BaseTracker(id, "Shikimori"), DeletableTracker {
else -> null 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) override suspend fun login(username: String, password: String) = login(password)

View File

@ -15,7 +15,7 @@ import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.buildJsonObject import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.contentOrNull import kotlinx.serialization.json.contentOrNull
import kotlinx.serialization.json.float import kotlinx.serialization.json.double
import kotlinx.serialization.json.int import kotlinx.serialization.json.int
import kotlinx.serialization.json.jsonObject import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive import kotlinx.serialization.json.jsonPrimitive
@ -102,10 +102,10 @@ class ShikimoriApi(
return TrackSearch.create(trackId).apply { return TrackSearch.create(trackId).apply {
remote_id = obj["id"]!!.jsonPrimitive.long remote_id = obj["id"]!!.jsonPrimitive.long
title = obj["name"]!!.jsonPrimitive.content 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 cover_url = baseUrl + obj["image"]!!.jsonObject["preview"]!!.jsonPrimitive.content
summary = "" summary = ""
score = obj["score"]!!.jsonPrimitive.float score = obj["score"]!!.jsonPrimitive.double
tracking_url = baseUrl + obj["url"]!!.jsonPrimitive.content tracking_url = baseUrl + obj["url"]!!.jsonPrimitive.content
publishing_status = obj["status"]!!.jsonPrimitive.content publishing_status = obj["status"]!!.jsonPrimitive.content
publishing_type = obj["kind"]!!.jsonPrimitive.content publishing_type = obj["kind"]!!.jsonPrimitive.content
@ -117,10 +117,10 @@ class ShikimoriApi(
return Track.create(trackId).apply { return Track.create(trackId).apply {
title = mangas["name"]!!.jsonPrimitive.content title = mangas["name"]!!.jsonPrimitive.content
remote_id = obj["id"]!!.jsonPrimitive.long remote_id = obj["id"]!!.jsonPrimitive.long
total_chapters = mangas["chapters"]!!.jsonPrimitive.int total_chapters = mangas["chapters"]!!.jsonPrimitive.long
library_id = obj["id"]!!.jsonPrimitive.long library_id = obj["id"]!!.jsonPrimitive.long
last_chapter_read = obj["chapters"]!!.jsonPrimitive.float last_chapter_read = obj["chapters"]!!.jsonPrimitive.double
score = (obj["score"]!!.jsonPrimitive.int).toFloat() score = obj["score"]!!.jsonPrimitive.int.toDouble()
status = toTrackStatus(obj["status"]!!.jsonPrimitive.content) status = toTrackStatus(obj["status"]!!.jsonPrimitive.content)
tracking_url = baseUrl + mangas["url"]!!.jsonPrimitive.content tracking_url = baseUrl + mangas["url"]!!.jsonPrimitive.content
} }
@ -192,8 +192,8 @@ class ShikimoriApi(
) )
companion object { companion object {
private const val clientId = "1aaf4cf232372708e98b5abc813d795b539c5a916dbbfe9ac61bf02a360832cc" private const val clientId = "PB9dq8DzI405s7wdtwTdirYqHiyVMh--djnP7lBUqSA"
private const val clientSecret = "229942c742dd4cde803125d17d64501d91c0b12e14cb1e5120184d77d67024c0" private const val clientSecret = "NajpZcOBKB9sJtgNcejf8OB9jBN1OYYoo-k4h2WWZus"
private const val baseUrl = "https://shikimori.one" private const val baseUrl = "https://shikimori.one"
private const val apiUrl = "$baseUrl/api" private const val apiUrl = "$baseUrl/api"

View File

@ -1,5 +1,6 @@
package eu.kanade.tachiyomi.data.track.shikimori package eu.kanade.tachiyomi.data.track.shikimori
import eu.kanade.tachiyomi.BuildConfig
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import okhttp3.Interceptor import okhttp3.Interceptor
import okhttp3.Response import okhttp3.Response
@ -33,7 +34,7 @@ class ShikimoriInterceptor(private val shikimori: Shikimori) : Interceptor {
// Add the authorization header to the original request. // Add the authorization header to the original request.
val authRequest = originalRequest.newBuilder() val authRequest = originalRequest.newBuilder()
.addHeader("Authorization", "Bearer ${oauth!!.access_token}") .addHeader("Authorization", "Bearer ${oauth!!.access_token}")
.header("User-Agent", "Tachiyomi") .header("User-Agent", "Mihon v${BuildConfig.VERSION_NAME} (${BuildConfig.APPLICATION_ID})")
.build() .build()
return chain.proceed(authRequest) return chain.proceed(authRequest)

View File

@ -23,25 +23,25 @@ class Suwayomi(id: Long) : BaseTracker(id, "Suwayomi"), EnhancedTracker {
override fun getLogoColor() = Color.rgb(255, 35, 35) // TODO override fun getLogoColor() = Color.rgb(255, 35, 35) // TODO
companion object { companion object {
const val UNREAD = 1 const val UNREAD = 1L
const val READING = 2 const val READING = 2L
const val COMPLETED = 3 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 UNREAD -> MR.strings.unread
READING -> MR.strings.reading READING -> MR.strings.reading
COMPLETED -> MR.strings.completed COMPLETED -> MR.strings.completed
else -> null 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() 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 { override suspend fun update(track: Track, didReadChapter: Boolean): Track {
if (track.status != COMPLETED) { if (track.status != COMPLETED) {
if (didReadChapter) { 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.status = COMPLETED
} else { } else {
track.status = READING track.status = READING

View File

@ -66,9 +66,9 @@ class SuwayomiApi(private val trackId: Long) {
cover_url = "$url/thumbnail" cover_url = "$url/thumbnail"
summary = manga.description.orEmpty() summary = manga.description.orEmpty()
tracking_url = url tracking_url = url
total_chapters = manga.chapterCount.toInt() total_chapters = manga.chapterCount
publishing_status = manga.status publishing_status = manga.status
last_chapter_read = manga.lastChapterRead?.chapterNumber ?: 0F last_chapter_read = manga.lastChapterRead?.chapterNumber ?: 0.0
status = when (manga.unreadCount) { status = when (manga.unreadCount) {
manga.chapterCount -> Suwayomi.UNREAD manga.chapterCount -> Suwayomi.UNREAD
0L -> Suwayomi.COMPLETED 0L -> Suwayomi.COMPLETED

View File

@ -64,7 +64,7 @@ data class ChapterDataClass(
val url: String, val url: String,
val name: String, val name: String,
val uploadDate: Long, val uploadDate: Long,
val chapterNumber: Float, val chapterNumber: Double,
val scanlator: String?, val scanlator: String?,
val mangaId: Int, val mangaId: Int,

View File

@ -338,6 +338,10 @@ class ExtensionManager(
} }
override fun onExtensionUntrusted(extension: Extension.Untrusted) { override fun onExtensionUntrusted(extension: Extension.Untrusted) {
val installedExtension = _installedExtensionsFlow.value
.find { it.pkgName == extension.pkgName }
?: return
_installedExtensionsFlow.value -= installedExtension
_untrustedExtensionsFlow.value += extension _untrustedExtensionsFlow.value += extension
} }

View File

@ -70,8 +70,7 @@ internal class ExtensionInstallReceiver(private val listener: Listener) :
launchNow { launchNow {
when (val result = getExtensionFromIntent(context, intent)) { when (val result = getExtensionFromIntent(context, intent)) {
is LoadResult.Success -> listener.onExtensionUpdated(result.extension) is LoadResult.Success -> listener.onExtensionUpdated(result.extension)
// Not needed as a package can't be upgraded if the signature is different is LoadResult.Untrusted -> listener.onExtensionUntrusted(result.extension)
// is LoadResult.Untrusted -> {}
else -> {} else -> {}
} }
} }

View File

@ -285,13 +285,13 @@ private data class TrackStatusSelectorScreen(
private class Model( private class Model(
private val track: Track, private val track: Track,
private val tracker: Tracker, 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) } return tracker.getStatusList().associateWith { tracker.getStatus(it) }
} }
fun setSelection(selection: Int) { fun setSelection(selection: Long) {
mutableState.update { it.copy(selection = selection) } mutableState.update { it.copy(selection = selection) }
} }
@ -303,7 +303,7 @@ private data class TrackStatusSelectorScreen(
@Immutable @Immutable
data class State( data class State(
val selection: Int, val selection: Long,
) )
} }
} }

View File

@ -90,7 +90,7 @@ abstract class PagerViewer(val activity: ReaderActivity) : Viewer {
pager.addOnPageChangeListener( pager.addOnPageChangeListener(
object : ViewPager.SimpleOnPageChangeListener() { object : ViewPager.SimpleOnPageChangeListener() {
override fun onPageSelected(position: Int) { override fun onPageSelected(position: Int) {
if (activity.isScrollingThroughPages.not()) { if (!activity.isScrollingThroughPages) {
activity.hideMenu() activity.hideMenu()
} }
onPageChange(position) onPageChange(position)

View File

@ -21,7 +21,7 @@ class CrashLogUtil(
suspend fun dumpLogs() = withNonCancellableContext { suspend fun dumpLogs() = withNonCancellableContext {
try { try {
val file = context.createFileInCacheDir("tachiyomi_crash_logs.txt") val file = context.createFileInCacheDir("mihon_crash_logs.txt")
file.appendText(getDebugInfo() + "\n\n") file.appendText(getDebugInfo() + "\n\n")
getExtensionsInfo()?.let { file.appendText("$it\n\n") } getExtensionsInfo()?.let { file.appendText("$it\n\n") }

View File

@ -18,10 +18,10 @@ data class DummyTracker(
override val isLoggedIn: Boolean = false, override val isLoggedIn: Boolean = false,
val valLogoColor: Int = Color.rgb(18, 25, 35), val valLogoColor: Int = Color.rgb(18, 25, 35),
val valLogo: Int = R.drawable.ic_tracker_anilist, val valLogo: Int = R.drawable.ic_tracker_anilist,
val valStatuses: List<Int> = (1..6).toList(), val valStatuses: List<Long> = (1L..6L).toList(),
val valReadingStatus: Int = 1, val valReadingStatus: Long = 1L,
val valRereadingStatus: Int = 1, val valRereadingStatus: Long = 1L,
val valCompletionStatus: Int = 2, val valCompletionStatus: Long = 2L,
val valScoreList: ImmutableList<String> = (0..10).map(Int::toString).toImmutableList(), val valScoreList: ImmutableList<String> = (0..10).map(Int::toString).toImmutableList(),
val val10PointScore: Double = 5.4, val val10PointScore: Double = 5.4,
val valSearchResults: List<TrackSearch> = listOf(), val valSearchResults: List<TrackSearch> = listOf(),
@ -34,29 +34,29 @@ data class DummyTracker(
override fun getLogo(): Int = valLogo 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) { override fun getStatus(status: Long): StringResource? = when (status) {
1 -> MR.strings.reading 1L -> MR.strings.reading
2 -> MR.strings.plan_to_read 2L -> MR.strings.plan_to_read
3 -> MR.strings.completed 3L -> MR.strings.completed
4 -> MR.strings.on_hold 4L -> MR.strings.on_hold
5 -> MR.strings.dropped 5L -> MR.strings.dropped
6 -> MR.strings.repeating 6L -> MR.strings.repeating
else -> null 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 getScoreList(): ImmutableList<String> = valScoreList
override fun get10PointScore(track: Track): Double = val10PointScore 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 = override fun displayScore(track: Track): String =
track.score.toString() track.score.toString()
@ -94,7 +94,7 @@ data class DummyTracker(
override suspend fun setRemoteStatus( override suspend fun setRemoteStatus(
track: eu.kanade.tachiyomi.data.database.models.Track, track: eu.kanade.tachiyomi.data.database.models.Track,
status: Int, status: Long,
) = Unit ) = Unit
override suspend fun setRemoteLastChapterRead( override suspend fun setRemoteLastChapterRead(

View File

@ -9,5 +9,12 @@
<path <path
android:pathData="M0,0h432v432h-432z" android:pathData="M0,0h432v432h-432z"
android:fillColor="#FAFAFA"/> android:fillColor="#FAFAFA"/>
<path
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.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> </group>
</vector> </vector>

View File

@ -4,13 +4,6 @@
android:viewportWidth="432" android:viewportWidth="432"
android:viewportHeight="432"> android:viewportHeight="432">
<path <path
android:pathData="M337,216C337,282.83 282.83,337 216,337C149.17,337 95,282.83 95,216C95,149.17 149.17,95 216,95C282.83,95 337,149.17 337,216Z" 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="#F2FAFF"/>
<path
android:pathData="M216,314.31C270.3,314.31 314.31,270.3 314.31,216C314.31,161.7 270.3,117.69 216,117.69C161.7,117.69 117.69,161.7 117.69,216C117.69,270.3 161.7,314.31 216,314.31ZM216,337C282.83,337 337,282.83 337,216C337,149.17 282.83,95 216,95C149.17,95 95,149.17 95,216C95,282.83 149.17,337 216,337Z"
android:fillColor="#0058A0"
android:fillType="evenOdd"/>
<path
android:pathData="M163.71,174.79L162.66,150.56C165.82,151.16 169.58,151.31 178.16,151.31C188.55,151.31 202.24,150.71 209.32,149.81C212.32,149.51 213.53,149.05 215.49,148L231.74,161.85C230.23,163.95 229.78,164.86 228.13,169.07C226.77,172.38 220.6,191.49 218.2,199.62C229.33,201.88 235.5,203.53 243.93,206.99C244.98,199.62 245.13,195.71 245.13,182.31C245.13,178.85 244.98,176.9 244.53,173.74L270.72,174.64C269.96,178.25 269.82,179.76 269.66,185.62C269.06,199.77 268.46,206.54 266.95,216.78C277.34,222.04 277.34,222.04 282.61,224.9C285.32,226.41 285.92,226.71 287.72,227.31L278.99,255.45C274.78,251.69 268.91,247.63 260.33,242.81C252.81,260.72 240.32,273.82 221.35,284.2C215.03,275.77 210.22,270.65 202.54,264.63C213.68,259.37 218.8,256.06 224.67,250.49C230.38,244.92 234.15,239.5 237.76,231.38C228.13,227.01 221.96,225.05 211.27,222.8C205.1,241.16 200.13,252.9 195.77,259.97C189.9,269.45 181.93,274.42 172.74,274.42C165.67,274.42 158.45,271.26 153.18,265.84C147.16,259.67 144,251.09 144,241.16C144,226.41 151.07,213.62 163.41,205.64C171.39,200.52 179.82,198.27 193.21,197.51C195.92,188.63 198.18,180.96 200.29,172.38C193.66,172.98 185.39,173.43 175.3,173.88C169.88,174.04 168.08,174.19 163.71,174.79ZM186.59,220.54C179.52,221.74 175.3,224 171.54,228.82C168.68,232.13 167.33,236.04 167.33,240.25C167.33,244.92 169.58,248.38 172.44,248.38C175.9,248.38 179.82,240.55 186.59,220.54Z"
android:fillColor="#031019"/> android:fillColor="#031019"/>
</vector> </vector>

View File

@ -14,7 +14,7 @@ android {
} }
dependencies { dependencies {
implementation(project(":source-api")) implementation(projects.sourceApi)
implementation(kotlinx.bundles.serialization) implementation(kotlinx.bundles.serialization)
} }

View File

@ -17,7 +17,7 @@ android {
} }
dependencies { dependencies {
implementation(project(":i18n")) implementation(projects.i18n)
api(libs.logcat) api(libs.logcat)

View File

@ -89,7 +89,7 @@ internal class RateLimitInterceptor(
while (requestQueue.size >= permits) { // queue is full, remove expired entries while (requestQueue.size >= permits) { // queue is full, remove expired entries
val periodStart = SystemClock.elapsedRealtime() - rateLimitMillis val periodStart = SystemClock.elapsedRealtime() - rateLimitMillis
var hasRemovedExpired = false var hasRemovedExpired = false
while (requestQueue.isEmpty().not() && requestQueue.first <= periodStart) { while (!requestQueue.isEmpty() && requestQueue.first <= periodStart) {
requestQueue.removeFirst() requestQueue.removeFirst()
hasRemovedExpired = true hasRemovedExpired = true
} }

View File

@ -24,9 +24,9 @@ android {
} }
dependencies { dependencies {
implementation(project(":source-api")) implementation(projects.sourceApi)
implementation(project(":domain")) implementation(projects.domain)
implementation(project(":core")) implementation(projects.core)
api(libs.bundles.sqldelight) api(libs.bundles.sqldelight)
} }

View File

@ -75,7 +75,7 @@ WHERE _id IN :chapterIds;
insert: 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) 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:
UPDATE chapters UPDATE chapters

View File

@ -117,7 +117,7 @@ AND source IN :sourceIds;
insert: 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) 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:
UPDATE mangas SET UPDATE mangas SET

View File

@ -20,7 +20,7 @@ END;
insert: insert:
INSERT INTO mangas_categories(manga_id, category_id, last_modified_at) INSERT INTO mangas_categories(manga_id, category_id, last_modified_at)
VALUES (:mangaId, :categoryId, strftime('%s', 'now')); VALUES (:mangaId, :categoryId, 0);
deleteMangaCategoryByMangaId: deleteMangaCategoryByMangaId:
DELETE FROM mangas_categories DELETE FROM mangas_categories

View File

@ -0,0 +1,4 @@
-- MangaUpdates score fixing --
UPDATE manga_sync
SET score = max(score, 0)
WHERE sync_id = 7;

View File

@ -14,8 +14,8 @@ android {
} }
dependencies { dependencies {
implementation(project(":source-api")) implementation(projects.sourceApi)
implementation(project(":core")) implementation(projects.core)
implementation(platform(kotlinx.coroutines.bom)) implementation(platform(kotlinx.coroutines.bom))
implementation(kotlinx.bundles.coroutines) implementation(kotlinx.bundles.coroutines)

View File

@ -20,7 +20,7 @@ class GetApplicationRelease(
val now = Instant.now() val now = Instant.now()
// Limit checks to once every 3 days at most // 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), Instant.ofEpochMilli(lastChecked.get()).plus(3, ChronoUnit.DAYS),
) )
) { ) {

View File

@ -22,7 +22,7 @@ class StubSource(
throw SourceNotInstalledException() throw SourceNotInstalledException()
override fun toString(): String = override fun toString(): String =
if (isInvalid.not()) "$name (${lang.uppercase()})" else id.toString() if (!isInvalid) "$name (${lang.uppercase()})" else id.toString()
companion object { companion object {
fun from(source: Source): StubSource { fun from(source: Source): StubSource {

View File

@ -128,4 +128,12 @@
<item quantity="many">%d يومًا</item> <item quantity="many">%d يومًا</item>
<item quantity="other">%d يوم</item> <item quantity="other">%d يوم</item>
</plurals> </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> </resources>

View File

@ -232,7 +232,7 @@
<string name="label_extensions">الإضافات</string> <string name="label_extensions">الإضافات</string>
<string name="label_extension_info">معلومات الإضافة</string> <string name="label_extension_info">معلومات الإضافة</string>
<string name="action_display_download_badge">الفصول المحملة</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_install">تثبيت</string>
<string name="ext_pending">المُعلقة</string> <string name="ext_pending">المُعلقة</string>
<string name="ext_downloading">جارى التنزيل</string> <string name="ext_downloading">جارى التنزيل</string>
@ -242,11 +242,9 @@
<string name="ext_untrusted">غير موثوق فيه</string> <string name="ext_untrusted">غير موثوق فيه</string>
<string name="ext_uninstall">إلغاء التثبيت</string> <string name="ext_uninstall">إلغاء التثبيت</string>
<string name="untrusted_extension">إضافة ذات ريبة</string> <string name="untrusted_extension">إضافة ذات ريبة</string>
<string name="untrusted_extension_message">هذه اﻹضافة موقَّعة بشهادة ذات ريبة ولم تفعَّل. <string name="untrusted_extension_message">يمكن للملحقات الخبيثة قراءة أي بيانات اعتماد مخزنة لتسجيل الدخول أو تنفيذ تعليمات برمجية عشوائية.
\n \n
\nيمكن لأي إضافة خبيثة قراءة بيانات اعتماد تسجيل الدخول المخزَّنة أو تنفيذ تعليمات برمجية عشوائية. \nمن خلال الوثوق بهذا الامتداد، فإنك تقبل هذه المخاطر.</string>
\n
\nأنت تقبل هذه المخاطر إن وثقت بالشهادة.</string>
<string name="pref_double_tap_anim_speed">سرعة مؤثر النقر المزدوج</string> <string name="pref_double_tap_anim_speed">سرعة مؤثر النقر المزدوج</string>
<string name="pager_viewer">عارض الصفحات</string> <string name="pager_viewer">عارض الصفحات</string>
<string name="double_tap_anim_speed_0">لا مؤثرات</string> <string name="double_tap_anim_speed_0">لا مؤثرات</string>
@ -372,7 +370,7 @@
<string name="label_data">البيانات</string> <string name="label_data">البيانات</string>
<string name="backup_restore_missing_sources">المصادر المفقودة:</string> <string name="backup_restore_missing_sources">المصادر المفقودة:</string>
<string name="invalid_backup_file_missing_manga">النسخة الإحتياطية لا تحتوي على أيّة إدخالات المكتبة.</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="tracking_info">مزامنة أحادية تُحدِّث قراءة الفصول في المتتبعات الخارجية، ولك تعيين التتبِّع لكلِّ مدخلة على حدى، وذلك من زرِّ التتبع فيهم.</string>
<string name="pref_library_update_refresh_metadata_summary">تحقق من وجود غلاف جديد وتفاصيل جديدة عند تحديث المكتبة</string> <string name="pref_library_update_refresh_metadata_summary">تحقق من وجود غلاف جديد وتفاصيل جديدة عند تحديث المكتبة</string>
<string name="pref_library_update_refresh_metadata">تحديث البيانات الوصفية تلقائياً</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_upload_date">حسب تاريخ الرفع</string>
<string name="action_order_by_chapter_number">حسب رقم الفصل</string> <string name="action_order_by_chapter_number">حسب رقم الفصل</string>
<string name="pref_dns_over_https">إستخدام DNS عبر HTTPS (DoH)</string> <string name="pref_dns_over_https">إستخدام DNS عبر HTTPS (DoH)</string>
<string name="backup_restore_content_full">تم استيراد البيانات من ملف نسخ الاحتياطي. <string name="backup_restore_content_full">قد تحتاج إلى تثبيت أي إضافات مفقودة وتسجيل الدخول إلى خدمات التتبع بعد ذلك لاستخدامها.</string>
\n
\nيتوجب تثبيت الإضافات المفقودة و تسجيل الدخول إلى منصات التعقب تالياً لاستعمالهم.</string>
<string name="nav_zone_right">يمين</string> <string name="nav_zone_right">يمين</string>
<string name="nav_zone_left">يسار</string> <string name="nav_zone_left">يسار</string>
<string name="nav_zone_next">التالي</string> <string name="nav_zone_next">التالي</string>
@ -773,4 +769,33 @@
\nوالأحسن أن يكون المجلَّد مخصوصًا لذلك. \nوالأحسن أن يكون المجلَّد مخصوصًا لذلك.
\n \n
\nالمجلَّد المحدَّد: %2$s</string> \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> </resources>

View File

@ -29,12 +29,12 @@
<item quantity="other">Ҫӗнӗ сыпӑксем %d хайлав валли тупӑннӑ</item> <item quantity="other">Ҫӗнӗ сыпӑксем %d хайлав валли тупӑннӑ</item>
</plurals> </plurals>
<plurals name="manga_num_chapters"> <plurals name="manga_num_chapters">
<item quantity="one">1 сыпӑк</item> <item quantity="one">%1$s сыпӑк</item>
<item quantity="other">%1$s сыпӑк</item> <item quantity="other">%1$s сыпӑк</item>
</plurals> </plurals>
<plurals name="download_queue_summary"> <plurals name="download_queue_summary">
<item quantity="one">1 юлчӗ</item> <item quantity="one">%1$s йулчӗ</item>
<item quantity="other">%1$s юлчӗ</item> <item quantity="other">%1$s йулчӗ</item>
</plurals> </plurals>
<plurals name="num_trackers"> <plurals name="num_trackers">
<item quantity="one">1 сӑнану</item> <item quantity="one">1 сӑнану</item>
@ -48,4 +48,20 @@
<item quantity="one">Ӗнер</item> <item quantity="one">Ӗнер</item>
<item quantity="other">%1$d кун кайалла</item> <item quantity="other">%1$d кун кайалла</item>
</plurals> </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> </resources>

View File

@ -128,16 +128,16 @@
<string name="fifth_to_last">Ab fünftletzt gelesenem Kapitel</string> <string name="fifth_to_last">Ab fünftletzt gelesenem Kapitel</string>
<string name="pref_download_new">Neue Kapitel herunterladen</string> <string name="pref_download_new">Neue Kapitel herunterladen</string>
<string name="services">Tracker</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_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_restore_backup_summ">Bibliothek mit Hilfe einer Datensicherung wiederherstellen</string>
<string name="pref_backup_interval">Automatische Sicherungshäufigkeit</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="restore_completed">Wiederherstellen abgeschlossen</string>
<string name="backup_choice">Was möchtest du sichern\?</string> <string name="backup_choice">Was möchtest du sichern\?</string>
<string name="restoring_backup">Sicherung wird wiederhergestellt</string> <string name="restoring_backup">Datensicherung wird wiederhergestellt</string>
<string name="creating_backup">Sicherung wird erstellt</string> <string name="creating_backup">Datensicherung wird erstellt</string>
<string name="pref_clear_chapter_cache">Kapitel-Zwischenspeicher leeren</string> <string name="pref_clear_chapter_cache">Kapitel-Zwischenspeicher leeren</string>
<string name="used_cache">Belegt: %1$s</string> <string name="used_cache">Belegt: %1$s</string>
<string name="cache_deleted">Zwischenspeicher geleert, %1$d Dateien gelöscht</string> <string name="cache_deleted">Zwischenspeicher geleert, %1$d Dateien gelöscht</string>
@ -351,11 +351,11 @@
<string name="website">Webseite</string> <string name="website">Webseite</string>
<string name="label_downloaded_only">Nur Heruntergeladenes</string> <string name="label_downloaded_only">Nur Heruntergeladenes</string>
<string name="recent_manga_time">Kap. %1$s - %2$s</string> <string name="recent_manga_time">Kap. %1$s - %2$s</string>
<string name="restoring_backup_error">Sicherungswiederherstellung fehlgeschlagen</string> <string name="restoring_backup_error">Datensicherungswiederherstellung fehlgeschlagen</string>
<string name="creating_backup_error">Sicherung fehlgeschlagen</string> <string name="creating_backup_error">Datensicherung fehlgeschlagen</string>
<string name="restoring_backup_canceled">Wiederherstellung abgebrochen</string> <string name="restoring_backup_canceled">Wiederherstellung abgebrochen</string>
<string name="restore_in_progress">Wiederherstellung wird bereits durchgeführt</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="check_for_updates">Nach Aktualisierungen suchen</string>
<string name="last_used_source">Zuletzt genutzt</string> <string name="last_used_source">Zuletzt genutzt</string>
<string name="local_source_help_guide">Anleitung für lokale Quellen</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="sort_by_upload_date">Nach Uploaddatum</string>
<string name="label_data">Daten</string> <string name="label_data">Daten</string>
<string name="backup_restore_missing_sources">Fehlende Quellen:</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="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_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> <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="action_display_language_badge">Sprache</string>
<string name="label_warning">Warnung</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="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="connected_to_wifi">Nur über WLAN</string>
<string name="update_72hour">Alle 3 Tage</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> <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="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="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="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="theme_nord">Nord</string>
<string name="action_open_repo">Open-Source-Repository</string> <string name="action_open_repo">Open-Source-Repository</string>
<string name="manga_interval_expected_update_soon">Bald</string> <string name="manga_interval_expected_update_soon">Bald</string>

View File

@ -622,4 +622,40 @@
<string name="updates_last_update_info">آخرین به روز رسانی کتابخانه: %s</string> <string name="updates_last_update_info">آخرین به روز رسانی کتابخانه: %s</string>
<string name="pref_update_only_in_release_period">خارج از دوره انتشار موزد انتظار</string> <string name="pref_update_only_in_release_period">خارج از دوره انتشار موزد انتظار</string>
<string name="pref_double_tap_zoom">برای بزرگ نمایی دوبار ضربه بزنید</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> </resources>

View File

@ -72,7 +72,7 @@
<string name="label_extensions">Laajennukset</string> <string name="label_extensions">Laajennukset</string>
<string name="label_extension_info">Laajennuksen tiedot</string> <string name="label_extension_info">Laajennuksen tiedot</string>
<string name="action_filter">Suodatus</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_unread">Lukemattomat</string>
<string name="action_filter_empty">Poista suodattimet</string> <string name="action_filter_empty">Poista suodattimet</string>
<string name="action_sort_alpha">Aakkosjärjestyksessä</string> <string name="action_sort_alpha">Aakkosjärjestyksessä</string>
@ -103,7 +103,7 @@
<string name="action_install">Asenna</string> <string name="action_install">Asenna</string>
<string name="action_share">Jaa</string> <string name="action_share">Jaa</string>
<string name="action_save">Tallenna</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_undo">Kumoa</string>
<string name="action_open_log">Avaa loki</string> <string name="action_open_log">Avaa loki</string>
<string name="action_restore">Palauta</string> <string name="action_restore">Palauta</string>
@ -447,7 +447,7 @@
<string name="pref_dump_crash_logs_summary">Tallentaa virhelokit tiedostoon jaettavaksi kehittäjien kanssa</string> <string name="pref_dump_crash_logs_summary">Tallentaa virhelokit tiedostoon jaettavaksi kehittäjien kanssa</string>
<string name="action_desc">Laskeva</string> <string name="action_desc">Laskeva</string>
<string name="action_asc">Nouseva</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_order_by_upload_date">Lisäyspäivämäärän mukaan</string>
<string name="action_filter_tracked">Seuratut</string> <string name="action_filter_tracked">Seuratut</string>
<string name="action_display_show_number_of_items">Näytä kohteiden määrä</string> <string name="action_display_show_number_of_items">Näytä kohteiden määrä</string>
@ -615,4 +615,35 @@
<string name="backup_info">Varmuuskopioita kannattaa säilyttää myös muissa paikoissa.</string> <string name="backup_info">Varmuuskopioita kannattaa säilyttää myös muissa paikoissa.</string>
<string name="wish_list">Toivelista</string> <string name="wish_list">Toivelista</string>
<string name="cant_open_last_read_chapter">Viimeksi luettua lukua ei voitu avata</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> </resources>

View File

@ -38,7 +38,7 @@
</plurals> </plurals>
<plurals name="num_trackers"> <plurals name="num_trackers">
<item quantity="one">%d tracker</item> <item quantity="one">%d tracker</item>
<item quantity="other">%d mga tracker</item> <item quantity="other">%d na tracker</item>
</plurals> </plurals>
<plurals name="missing_chapters_warning"> <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> <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>
<plurals name="relative_time"> <plurals name="relative_time">
<item quantity="one">Kahapon</item> <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>
<plurals name="next_unread_chapters"> <plurals name="next_unread_chapters">
<item quantity="one">Susunod na hindi pa nababasa na kabanata</item> <item quantity="one">Susunod na hindi pa nababasang kabanata</item>
<item quantity="other">Susunod na %d di pa nababasa na kabanata</item> <item quantity="other">Susunod na %d hindi pa nababasang kabanata</item>
</plurals> </plurals>
<plurals name="download_amount"> <plurals name="download_amount">
<item quantity="one">Sunod na kabanata</item> <item quantity="one">Sunod na kabanata</item>
@ -58,11 +58,11 @@
</plurals> </plurals>
<plurals name="missing_chapters"> <plurals name="missing_chapters">
<item quantity="one">Nawawalang %1$s na kabanata</item> <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>
<plurals name="day"> <plurals name="day">
<item quantity="one">1 araw</item> <item quantity="one">1 araw</item>
<item quantity="other">%d (mga) araw</item> <item quantity="other">%d (na) araw</item>
</plurals> </plurals>
<plurals name="num_repos"> <plurals name="num_repos">
<item quantity="one">%d na repo</item> <item quantity="one">%d na repo</item>

View File

@ -22,7 +22,7 @@
<string name="action_disable">Isara</string> <string name="action_disable">Isara</string>
<string name="action_display_show_tabs">Ipakita ang mga tab ng kategorya</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_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_list">Listahan</string>
<string name="action_display_grid">Siksik na grid</string> <string name="action_display_grid">Siksik na grid</string>
<string name="action_display">Pagpapakita</string> <string name="action_display">Pagpapakita</string>
@ -114,7 +114,7 @@
<string name="second_to_last">Pangalawa sa huling nabasa</string> <string name="second_to_last">Pangalawa sa huling nabasa</string>
<string name="last_read_chapter">Huling nabasang kabanata</string> <string name="last_read_chapter">Huling nabasang kabanata</string>
<string name="disabled">Sarado</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_remove_after_read">Pagkatapos basahin, awtomatikong burahin</string>
<string name="pref_webtoon_side_padding">Kapal ng gilid</string> <string name="pref_webtoon_side_padding">Kapal ng gilid</string>
<string name="pref_category_reading">Pagbabasa</string> <string name="pref_category_reading">Pagbabasa</string>
@ -243,7 +243,7 @@
<string name="channel_ext_updates">Mga update sa extension</string> <string name="channel_ext_updates">Mga update sa extension</string>
<string name="tapping_inverted_none">Wala</string> <string name="tapping_inverted_none">Wala</string>
<string name="channel_new_chapters">Mga update sa kabanata</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_download_paused">Nakahinto ang mga pag-download</string>
<string name="download_notifier_no_network">Walang koneksyon sa Internet</string> <string name="download_notifier_no_network">Walang koneksyon sa Internet</string>
<string name="download_notifier_text_only_wifi">Walang koneksyon sa Wifi</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="decode_image_error">Di mai-load ang larawan</string>
<string name="no_next_chapter">Di makita ang susunod na kabanata</string> <string name="no_next_chapter">Di makita ang susunod na kabanata</string>
<string name="chapter_progress">Pahina: %1$d</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="set_as_cover">Gawin itong cover</string>
<string name="custom_filter">Pinili kong filter</string> <string name="custom_filter">Pinili kong filter</string>
<string name="picture_saved">Na-save na ang larawan</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="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="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="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="on_hiatus">Naka-hiatus</string>
<string name="cancelled">Kinansela</string> <string name="cancelled">Kinansela</string>
<string name="action_show_manga">Ipakita ang entry</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_library_summary">Mga kategorya, panlahatang update, pag-swipe ng kabanata</string>
<string name="pref_browse_summary">Mga source, extension, panlahatang paghanap</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_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="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="unknown_title">Di alam na pamagat</string>
<string name="error_user_agent_string_invalid">Di-wastong string ng user agent</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> <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">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="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="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_debug_info">Impormasyon sa pag-debug</string>
<string name="pref_chapter_swipe_start">Mag-swipe ng pakaliwang pagkilos</string> <string name="pref_chapter_swipe_start">Mag-swipe ng pakaliwang pagkilos</string>
<string name="pref_chapter_swipe">Mag swipe ng kabanata</string> <string name="pref_chapter_swipe">Mag swipe ng kabanata</string>

View File

@ -412,7 +412,7 @@
<string name="pref_category_delete_chapters">Suppression des chapitres</string> <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_warning">Les sources de cette extension peuvent contenir du contenu NSFW (18+)</string>
<string name="ext_nsfw_short">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="no_chapters_error">Aucun chapitre trouvé</string>
<string name="confirm_set_chapter_settings">Appliquer ce paramétrage par défaut \?</string> <string name="confirm_set_chapter_settings">Appliquer ce paramétrage par défaut \?</string>
<string name="chapter_settings">Paramètres du chapitre</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_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_shizuku_stopped">Shizuku n\'est pas en cours d\'exécution</string>
<string name="ext_installer_legacy">Legacy</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="ext_install_service_notif">Installation de l\'extension…</string>
<string name="action_sort_count">Entrées totales</string> <string name="action_sort_count">Entrées totales</string>
<string name="pref_verbose_logging">Rapports détaillés</string> <string name="pref_verbose_logging">Rapports détaillés</string>

View File

@ -728,7 +728,7 @@
<string name="action_move_to_bottom_all_for_series">Pindahkan seri ke bawah</string> <string name="action_move_to_bottom_all_for_series">Pindahkan seri ke bawah</string>
<string name="pref_relative_format">Penanda waktu</string> <string name="pref_relative_format">Penanda waktu</string>
<string name="pref_relative_format_summary">\"%1$s\" seharusnya \"%2$s\"</string> <string name="pref_relative_format_summary">\"%1$s\" seharusnya \"%2$s\"</string>
<string name="action_sort_category">Mengurutkan kategori</string> <string name="action_sort_category">Urutkan kategori</string>
<string name="notification_updating_progress">Memperbarui pustaka... (%s)</string> <string name="notification_updating_progress">Memperbarui pustaka... (%s)</string>
<string name="sort_category_confirmation">Apakah Anda ingin mengurutkan kategori menurut abjad\?</string> <string name="sort_category_confirmation">Apakah Anda ingin mengurutkan kategori menurut abjad\?</string>
<string name="file_null_uri_error">Tidak ada file yang dipilih</string> <string name="file_null_uri_error">Tidak ada file yang dipilih</string>

View File

@ -199,7 +199,7 @@
<string name="update_weekly">Ogni settimana</string> <string name="update_weekly">Ogni settimana</string>
<string name="default_category">Categoria predefinita</string> <string name="default_category">Categoria predefinita</string>
<string name="track">Tracking</string> <string name="track">Tracking</string>
<string name="pref_category_tracking">Tracking</string> <string name="pref_category_tracking">Tracciamento</string>
<string name="default_category_summary">Chiedi sempre</string> <string name="default_category_summary">Chiedi sempre</string>
<string name="pref_crop_borders">Ritaglia bordi</string> <string name="pref_crop_borders">Ritaglia bordi</string>
<string name="pref_read_with_volume_keys_inverted">Inverti i tasti del volume</string> <string name="pref_read_with_volume_keys_inverted">Inverti i tasti del volume</string>
@ -236,11 +236,9 @@
<string name="ext_untrusted">Non attendibile</string> <string name="ext_untrusted">Non attendibile</string>
<string name="ext_uninstall">Disinstalla</string> <string name="ext_uninstall">Disinstalla</string>
<string name="untrusted_extension">Estensione non attendibile</string> <string name="untrusted_extension">Estensione non attendibile</string>
<string name="untrusted_extension_message">Questa estensione è stata firmata con un certificato non attendibile e non è stata attivata. <string name="untrusted_extension_message">Le estensioni dannose possono leggere le credenziali di accesso memorizzate o eseguire codice arbitrario.
\n \n
\nUn\'estensione maliziosa potrebbe leggere credenziali di accesso salvate o eseguire codice dannoso. \nFidandoti di questa estensione, accetti questi rischi.</string>
\n
\nFidandoti di questo certificato accetti questi rischi.</string>
<string name="pref_double_tap_anim_speed">Velocità animazioni doppio tocco</string> <string name="pref_double_tap_anim_speed">Velocità animazioni doppio tocco</string>
<string name="pager_viewer">Per pagina</string> <string name="pager_viewer">Per pagina</string>
<string name="double_tap_anim_speed_0">Senza animazione</string> <string name="double_tap_anim_speed_0">Senza animazione</string>
@ -783,4 +781,23 @@
<string name="private_settings">Includi impostazioni sensibili (es. token di login dei tracker)</string> <string name="private_settings">Includi impostazioni sensibili (es. token di login dei tracker)</string>
<string name="onboarding_permission_action_grant">Consenti</string> <string name="onboarding_permission_action_grant">Consenti</string>
<string name="exclude_scanlators">Escludi scanlator</string> <string name="exclude_scanlators">Escludi scanlator</string>
<string name="onboarding_storage_help_action">Guida all\'archiviazione</string>
<string name="label_extension_repos">Repository delle estensioni</string>
<string name="action_add_repo">Aggiungi repository</string>
<string name="label_add_repo_input">URL repository</string>
<string name="error_repo_exists">Questa repository esiste già!</string>
<string name="action_delete_repo">Elimina repository</string>
<string name="invalid_repo_name">URL repository non valida</string>
<string name="action_open_repo">Repository open source</string>
<string name="manga_interval_expected_update_soon">Presto</string>
<string name="manga_interval_custom_amount">Frequenza di aggiornamento personalizzata:</string>
<string name="onboarding_storage_help_info">State aggiornando da una versione precedente e non siete sicuri di cosa selezionare? Per ulteriori informazioni, consultare la guida all\'archiviazione</string>
<string name="theme_nord">Nord</string>
<string name="pref_library_update_smart_update">Aggiornamento intelligente</string>
<string name="ext_revoke_trust">Revoca le estensioni sconosciute affidabili</string>
<string name="information_empty_repos">Non hai repository impostate.</string>
<string name="action_add_repo_message">Aggiungi altri repository a Mihon. Questo dovrebbe essere un URL che termina con \"index.min.json\".</string>
<string name="delete_repo_confirmation">Vuoi eleminare la repository \"%s\"?</string>
<string name="invalid_backup_file_error">Errore completo:</string>
<string name="manga_interval_expected_update">Si prevede che i nuovi capitoli saranno rilasciati tra circa %1$s, con controlli ogni %2$s.</string>
</resources> </resources>

View File

@ -500,7 +500,7 @@
<string name="rotation_landscape">横向き</string> <string name="rotation_landscape">横向き</string>
<string name="rotation_portrait">縦向き</string> <string name="rotation_portrait">縦向き</string>
<string name="pref_dark_theme_pure_black">真っ黒モード</string> <string name="pref_dark_theme_pure_black">真っ黒モード</string>
<string name="theme_yotsuba">Yotsuba</string> <string name="theme_yotsuba">四つ葉</string>
<string name="theme_yinyang">陰陽</string> <string name="theme_yinyang">陰陽</string>
<string name="theme_tako">Tako</string> <string name="theme_tako">Tako</string>
<string name="theme_strawberrydaiquiri">ストロベリーダイキリ</string> <string name="theme_strawberrydaiquiri">ストロベリーダイキリ</string>
@ -620,7 +620,7 @@
<string name="theme_tidalwave">津波</string> <string name="theme_tidalwave">津波</string>
<string name="download_ahead">事前ダウンロード</string> <string name="download_ahead">事前ダウンロード</string>
<string name="auto_download_while_reading">読書中に自動でダウンロード</string> <string name="auto_download_while_reading">読書中に自動でダウンロード</string>
<string name="download_ahead_info">現在のと次の章は既にダウンロード済みの場合のみ有効です</string> <string name="download_ahead_info">現在のと次の章は既にダウンロード済みの場合のみ有効です</string>
<string name="are_you_sure">本当に実行しますか?</string> <string name="are_you_sure">本当に実行しますか?</string>
<string name="multi_lang">多言語</string> <string name="multi_lang">多言語</string>
<string name="updates_last_update_info">前回のライブラリ更新: %s</string> <string name="updates_last_update_info">前回のライブラリ更新: %s</string>

View File

@ -210,7 +210,7 @@
<string name="tapping_inverted_both">दुबै</string> <string name="tapping_inverted_both">दुबै</string>
<string name="pref_reader_actions">कार्यहरू</string> <string name="pref_reader_actions">कार्यहरू</string>
<string name="pref_read_with_long_tap">लामो ट्यापमा कार्यहरू देखाउनुहोस्</string> <string name="pref_read_with_long_tap">लामो ट्यापमा कार्यहरू देखाउनुहोस्</string>
<string name="pref_reader_theme">पृष्ठभूमि रङ</string> <string name="pref_reader_theme">ब्याकग्राउण्ड रङ</string>
<string name="gray_background">खैरो</string> <string name="gray_background">खैरो</string>
<string name="black_background">कालो</string> <string name="black_background">कालो</string>
<string name="automatic_background">स्वत</string> <string name="automatic_background">स्वत</string>
@ -595,7 +595,7 @@
<string name="multi_lang">थुप्रै</string> <string name="multi_lang">थुप्रै</string>
<string name="pref_library_update_show_tab_badge">अपडेटहरू आइकनमा नपढिएको गणना देखाउनुहोस्</string> <string name="pref_library_update_show_tab_badge">अपडेटहरू आइकनमा नपढिएको गणना देखाउनुहोस्</string>
<string name="pref_skip_dupe_chapters">डुप्लिकेट अध्यायहरू छोड्नुहोस्</string> <string name="pref_skip_dupe_chapters">डुप्लिकेट अध्यायहरू छोड्नुहोस्</string>
<string name="action_display_show_continue_reading_button">जारी राख्नुहोस् बटन</string> <string name="action_display_show_continue_reading_button">जारी राख्न बटन</string>
<string name="label_stats">तथ्याङ्क</string> <string name="label_stats">तथ्याङ्क</string>
<string name="label_started">सुरु गरिएको</string> <string name="label_started">सुरु गरिएको</string>
<string name="label_local">लोकल</string> <string name="label_local">लोकल</string>
@ -705,7 +705,7 @@
<string name="pref_double_tap_zoom">जूम गर्न डबल ट्याप गर्नुहोस्</string> <string name="pref_double_tap_zoom">जूम गर्न डबल ट्याप गर्नुहोस्</string>
<string name="pref_library_columns_per_row">%d प्रति पङ्क्ति</string> <string name="pref_library_columns_per_row">%d प्रति पङ्क्ति</string>
<string name="action_set_interval">अन्तराल सेट गर्नुहोस्</string> <string name="action_set_interval">अन्तराल सेट गर्नुहोस्</string>
<string name="action_filter_interval_custom">कस्टम गरिएको ल्याउने अन्तराल</string> <string name="action_filter_interval_custom">कस्टम गरिएको अपडेट फ्रिक्वेन्सी</string>
<string name="manga_display_modified_interval_title">प्रत्येक अपडेट गर्न सेट गर्नुहोस्</string> <string name="manga_display_modified_interval_title">प्रत्येक अपडेट गर्न सेट गर्नुहोस्</string>
<string name="skipped_reason_not_in_release_period">छोडियो किनभने आज कुनै रिलीज अपेक्षित थिएन</string> <string name="skipped_reason_not_in_release_period">छोडियो किनभने आज कुनै रिलीज अपेक्षित थिएन</string>
<string name="intervals_header">अन्तरालहरू</string> <string name="intervals_header">अन्तरालहरू</string>
@ -715,8 +715,8 @@
<string name="track_delete_title">%s ट्र्याकिङ हटाउने हो\?</string> <string name="track_delete_title">%s ट्र्याकिङ हटाउने हो\?</string>
<string name="track_delete_remote_text">%s बाट पनि हटाउनुहोस्</string> <string name="track_delete_remote_text">%s बाट पनि हटाउनुहोस्</string>
<string name="track_delete_text">यसले लोकल रूपमा ट्र्याकिङ हटाउनेछ।</string> <string name="track_delete_text">यसले लोकल रूपमा ट्र्याकिङ हटाउनेछ।</string>
<string name="action_ok">क छ</string> <string name="action_ok">िक छ</string>
<string name="delete_downloaded">डाउनलोड गरिएको मेट्नुहोस्</string> <string name="delete_downloaded">डाउनलोड गरिएको हटाउनुहोस्</string>
<string name="has_results">परिणामहरू भएको</string> <string name="has_results">परिणामहरू भएको</string>
<string name="library_sync_complete">पुस्तकालय सिङ्क सम्पन्न भयो</string> <string name="library_sync_complete">पुस्तकालय सिङ्क सम्पन्न भयो</string>
<string name="syncing_library">पुस्तकालय सिङ्क गर्दै</string> <string name="syncing_library">पुस्तकालय सिङ्क गर्दै</string>
@ -745,11 +745,61 @@
<string name="file_null_uri_error">फाइल पिकर एपमा फाइल फर्काउन असफल भयो</string> <string name="file_null_uri_error">फाइल पिकर एपमा फाइल फर्काउन असफल भयो</string>
<string name="selected">चयन गरिएको</string> <string name="selected">चयन गरिएको</string>
<string name="not_selected">चयन नगरिएको</string> <string name="not_selected">चयन नगरिएको</string>
<string name="action_sort_tracker_score">ट्र्याकर मल्य्कन</string> <string name="action_sort_tracker_score">ट्र्याकर मल्याङ्कन</string>
<string name="scanlator">स्क्यानले</string> <string name="scanlator">स्क्यानले</string>
<string name="action_menu_overflow_description">अरु मेनु</string> <string name="action_menu_overflow_description">अरु मेनु</string>
<string name="onboarding_heading">स्वागत!</string> <string name="onboarding_heading">स्वागत!</string>
<string name="onboarding_action_next">अर्को</string> <string name="onboarding_action_next">अर्को</string>
<string name="onboarding_action_finish">सुरु</string> <string name="onboarding_action_finish">सुरु</string>
<string name="onboarding_action_skip">छोड</string> <string name="onboarding_action_skip">छोड</string>
<string name="onboarding_storage_selection_required">एउटा फोल्डर चयन गर्नुपर्छ</string>
<string name="onboarding_storage_help_action">भण्डारण गाइड</string>
<string name="onboarding_permission_notifications">सूचना अनुमति</string>
<string name="onboarding_permission_action_grant">प्रदान गर्नुहोस्</string>
<string name="onboarding_guides_new_user">%s मा नयाँ हुनुहुन्छ? हामी स्टार्टिङ गाइड जाँच गर्न सिफारिस गर्छौं।</string>
<string name="theme_nord">नोर्ड</string>
<string name="pref_library_update_smart_update">स्मार्ट अपडेट</string>
<string name="error_repo_exists">यो रिपो पहिले नै अवस्थित छ!</string>
<string name="action_delete_repo">रिपो हटाउनुहोस्</string>
<string name="invalid_repo_name">रिपो URL अवैध छ</string>
<string name="action_bar_up_description">माथि नेभिगेट गर्नुहोस्</string>
<string name="action_revert_to_default">पूर्वनिर्धारितमा फर्कनुहोस्</string>
<string name="pref_onboarding_guide">अनबोर्डिङ गाइड</string>
<string name="onboarding_description">पहिले केहि चीजहरू सेट गरौं। तपाईं पछि पनि सेटिङहरूमा गएर यसलाई परिवर्तन गर्न सक्नुहुन्छ।</string>
<string name="onboarding_storage_info">एउटा फोल्डर चयन गर्नुहोस् जहाँ %1$s ले अध्याय डाउनलोड, ब्याकअप, र थप भण्डारण गर्नेछ।
\n
\nएक समर्पित फोल्डर सिफारिस गरिएको छ।
\n
\nचयन गरिएको फोल्डर: %2$s</string>
<string name="onboarding_storage_action_select">फोल्डर चयन गर्नुहोस्</string>
<string name="onboarding_permission_install_apps">एप स्थापना गर्ने अनुमति</string>
<string name="onboarding_permission_install_apps_description">स्रोत एक्सटेन्शन स्थापना गर्न को लागि।</string>
<string name="onboarding_permission_notifications_description">पुस्तकालय अपडेट र थपका लागि सूचना प्राप्त गर्नुहोस्।</string>
<string name="onboarding_permission_ignore_battery_opts_description">लामो समयदेखि चलिरहेको पुस्तकालय अपडेटहरू, डाउनलोडहरू, र ब्याकअप रिस्टोरमा अवरोधहरू बेवास्ता गर्नुहोस्।</string>
<string name="onboarding_storage_help_info">पुरानो संस्करणबाट अपडेट गर्दै र के चयन गर्ने निश्चित छैन? थप जानकारीको लागि भण्डारण गाइड हेर्नुहोस्।</string>
<string name="onboarding_permission_ignore_battery_opts">ब्याकग्राउण्ड ब्याट्री प्रयोग</string>
<string name="onboarding_guides_returning_user">%s पुन: स्थापना गर्दै हुनुहुन्छ?</string>
<string name="ext_permission_install_apps_warning">एक्सटेन्शनहरू स्थापना गर्न अनुमति आवश्यक छ। प्रदान गर्न यहाँ ट्याप गर्नुहोस्।</string>
<string name="ext_revoke_trust">विश्वसनीय अज्ञात एक्सटेन्शनहरू रद्द गर्नुहोस्</string>
<string name="information_empty_repos">तपाईंले कुनै पनि रिपो सेट गर्नु भएको छैन।</string>
<string name="label_add_repo_input">रिपो URL</string>
<string name="label_extension_repos">एक्सटेन्शन रिपो</string>
<string name="action_add_repo">रिपो थप्नुहोस्</string>
<string name="action_add_repo_message">Mihon मा अतिरिक्त रिपो थप्नुहोस्। URL को अन्त्यमा \"index.min.json\" हुनुपर्छ।</string>
<string name="action_apply">अप्लाई</string>
<string name="delete_repo_confirmation">के तपाइँ रिपो \"%s\" हटाउन चाहनुहुन्छ?</string>
<string name="action_open_repo">खुला स्रोत रिपो</string>
<string name="pref_storage_location">भण्डारण स्थान</string>
<string name="available_disk_space_info">उपलब्ध: %1$s / कुल: %2$s</string>
<string name="manga_interval_expected_update">नयाँ अध्यायहरू लगभग %1$s मा रिलीज हुने भविष्यवाणी गरिएको छ, हरेक %2$s को वरिपरि जाँच गर्दै।</string>
<string name="manga_interval_expected_update_soon">छिटै</string>
<string name="manga_interval_custom_amount">कस्टम अपडेट फ्रिक्वेन्सी:</string>
<string name="exclude_scanlators">स्क्यालेटरहरू समावेश नगर्नुहोस्</string>
<string name="no_scanlators_found">कुनै स्क्यालेटर फेला परेन</string>
<string name="pref_storage_usage">भण्डारण प्रयोग</string>
<string name="no_location_set">कुनै भण्डारण स्थान सेट गरिएको छैन</string>
<string name="pref_storage_location_info">स्वचालित ब्याकअप, अध्याय डाउनलोड, र स्थानीय स्रोतको लागि प्रयोग गरिन्छ।</string>
<string name="action_create">सिर्जना गर्नुहोस्</string>
<string name="invalid_backup_file_error">पूर्ण त्रुटि:</string>
<string name="private_settings">संवेदनशील सेटिङहरू समावेश गर्नुहोस् (जस्तै, ट्र्याकर लगइन टोकनहरू)</string>
</resources> </resources>

View File

@ -745,4 +745,61 @@
<string name="app_settings">Ustawienia aplikacji</string> <string name="app_settings">Ustawienia aplikacji</string>
<string name="action_delete_repo">Usuń repozytorium</string> <string name="action_delete_repo">Usuń repozytorium</string>
<string name="action_add_repo">Dodaj repozytorium</string> <string name="action_add_repo">Dodaj repozytorium</string>
<string name="selected">Zaznaczone</string>
<string name="action_bar_up_description">Przewiń w górę</string>
<string name="onboarding_action_finish">Zaczynajmy</string>
<string name="onboarding_storage_help_action">Przewodnik po pamięci</string>
<string name="onboarding_permission_install_apps">Zainstaluj uprawnienia aplikacji</string>
<string name="onboarding_permission_install_apps_description">Do instalacji rozszerzeń.</string>
<string name="onboarding_permission_action_grant">Zezwól</string>
<string name="onboarding_guides_new_user">Pierwszy raz w %s? Zalecamy zapoznać się z poradnikiem.</string>
<string name="onboarding_guides_returning_user">Ponowna instalacja %s?</string>
<string name="label_extension_repos">Repozytoria rozszerzeń</string>
<string name="label_add_repo_input">Link repozytorium</string>
<string name="pref_storage_usage">Zużycie pamięci</string>
<string name="manga_interval_custom_amount">Niestandardowa częstotliwość aktualizacji:</string>
<string name="no_scanlators_found">Nie znaleziono skanlatorów</string>
<string name="file_null_uri_error">Nie wybrano pliku</string>
<string name="not_selected">Nie zaznaczone</string>
<string name="action_sort_tracker_score">Serwisy śledzące</string>
<string name="label_data_storage">Dane i pamięć</string>
<string name="pref_onboarding_guide">Szybka konfiguracja</string>
<string name="onboarding_permission_ignore_battery_opts_description">Unikaj przerywania długich operacji takich jak pobieranie rozdziałów, aktualizacje bibliotek lub przywracanie kopii zapasowej.</string>
<string name="onboarding_permission_notifications_description">Otrzymuj powiadomienia ze swojej biblioteki i więcej.</string>
<string name="theme_nord">Nord</string>
<string name="pref_relative_format">Użyj dat względnych</string>
<string name="pref_relative_format_summary">\"%1$s\" zamiast \"%2$s\"</string>
<string name="pref_library_update_smart_update">Inteligentna aktualizacja</string>
<string name="onboarding_description">Na początek ustawmy kilka rzeczy. Zawsze możesz je później zmienić w ustawieniach.</string>
<string name="onboarding_storage_help_info">Aktualizujesz ze starszej wersji i nie jesteś pewien co wybrać? Zobacz na przewodnik po pamięci.</string>
<string name="pref_update_only_in_release_period">Przewiduj następną aktualizację</string>
<string name="ext_permission_install_apps_warning">Uprawnienia są wymagane aby zainstalować rozszerzenia. Kliknij tutaj aby je przyznać.</string>
<string name="onboarding_storage_info">Wybierz folder gdzie %1$s będzie zapisywał pobrane rozdziały, kopie zapasowe i inne.
\n
\nZalecany jest dedykowany folder.
\n
\nWybrany folder:%2$s</string>
<string name="ext_revoke_trust">Unieważnij zaufanie nieznanym rozszerzeniom</string>
<string name="information_empty_repos">Nie masz ustawionych repozytoriów.</string>
<string name="action_add_repo_message">Dodaj repozytoria do Mihon. Powinien to być link z końcówką \"index.min.json\".</string>
<string name="error_repo_exists">To repozytorium jest już dodane!</string>
<string name="delete_repo_confirmation">Na pewno chcesz usunąć repozytorium \"%s\"?</string>
<string name="action_open_repo">Repozytorium open source</string>
<string name="pref_flash_page_summ">Redukuje ghosting na wyświetlaczach e-ink</string>
<string name="no_location_set">Brak ustawionej ścieżki pamięci</string>
<string name="pref_storage_location">Miejsce przechowywania danych</string>
<string name="pref_storage_location_info">Używane do automatycznych kopii zapasowych, pobierania rozdziałów i lokalnych źródeł.</string>
<string name="invalid_backup_file_error">Pełny błąd:</string>
<string name="track_activity_name">Login serwisu śledzącego</string>
<string name="action_create">Utwórz</string>
<string name="pref_flash_page">Włącz przerwy między stronami</string>
<string name="private_settings">Dołącz wrażliwe ustawienia (np. tokeny serwisu śledzącego)</string>
<string name="last_auto_backup_info">Ostatnia automatyczna kopia zapasowa: %s</string>
<string name="has_results">Ma wyniki</string>
<string name="manga_display_interval_title">Oszacuj co</string>
<string name="available_disk_space_info">Wolne: %1$s / Ogółem: %2$s</string>
<string name="manga_interval_expected_update_soon">Wkrótce</string>
<string name="licensed_manga_chapters_error">Licencjonowany - Brak rozdziałów</string>
<string name="skipped_reason_not_in_release_period">Pominięto, ponieważ nie spodziewano się dzisiaj żadnej publikacji</string>
<string name="exclude_scanlators">Wyklucz skanlatorów</string>
</resources> </resources>

View File

@ -64,4 +64,8 @@
<item quantity="one">Nästa kapitel</item> <item quantity="one">Nästa kapitel</item>
<item quantity="other">Nästa %d kapitel</item> <item quantity="other">Nästa %d kapitel</item>
</plurals> </plurals>
<plurals name="num_repos">
<item quantity="one">%d förråd</item>
<item quantity="other">%d flera förråd</item>
</plurals>
</resources> </resources>

View File

@ -445,7 +445,7 @@
<string name="track_started_reading_date">Start datum</string> <string name="track_started_reading_date">Start datum</string>
<string name="pref_viewer_nav">Tryckzoner</string> <string name="pref_viewer_nav">Tryckzoner</string>
<string name="edge_nav">Kant</string> <string name="edge_nav">Kant</string>
<string name="kindlish_nav">Kindle-ish</string> <string name="kindlish_nav">Kindle-lik</string>
<string name="l_nav">L-formad</string> <string name="l_nav">L-formad</string>
<string name="action_filter_tracked">Spåras</string> <string name="action_filter_tracked">Spåras</string>
<string name="right_and_left_nav">Höger och Vänster</string> <string name="right_and_left_nav">Höger och Vänster</string>
@ -616,7 +616,7 @@
<string name="appwidget_updates_description">Se dina nyligen uppdaterade biblioteket inlägg</string> <string name="appwidget_updates_description">Se dina nyligen uppdaterade biblioteket inlägg</string>
<string name="appwidget_unavailable_locked">Widget är inte tillgänglig när applåset är aktiverat</string> <string name="appwidget_unavailable_locked">Widget är inte tillgänglig när applåset är aktiverat</string>
<string name="update_already_running">En uppdatering pågår redan</string> <string name="update_already_running">En uppdatering pågår redan</string>
<string name="multi_lang">Multi</string> <string name="multi_lang">Flerspråkig</string>
<string name="error_user_agent_string_blank">Strängen för användaragent kan inte vara tom</string> <string name="error_user_agent_string_blank">Strängen för användaragent kan inte vara tom</string>
<string name="are_you_sure">Är du säker\?</string> <string name="are_you_sure">Är du säker\?</string>
<string name="updates_last_update_info">Biblioteket uppdaterades senast: %s</string> <string name="updates_last_update_info">Biblioteket uppdaterades senast: %s</string>
@ -740,7 +740,7 @@
<string name="action_revert_to_default">Återgå till standard</string> <string name="action_revert_to_default">Återgå till standard</string>
<string name="last_auto_backup_info">Senaste automatiska säkerhetskopieringen: %s</string> <string name="last_auto_backup_info">Senaste automatiska säkerhetskopieringen: %s</string>
<string name="no_scanlators_found">Inga scanlatorer hittades</string> <string name="no_scanlators_found">Inga scanlatorer hittades</string>
<string name="scanlator">Scanlator</string> <string name="scanlator">Översättare</string>
<string name="pref_flash_page">Blinka vid sidbyte</string> <string name="pref_flash_page">Blinka vid sidbyte</string>
<string name="pref_storage_usage">Lagringsanvändning</string> <string name="pref_storage_usage">Lagringsanvändning</string>
<string name="action_sort_tracker_score">Spårares betyg</string> <string name="action_sort_tracker_score">Spårares betyg</string>
@ -784,4 +784,18 @@
<string name="onboarding_storage_help_info">Osäker på vad du ska välja då du uppdaterar från en äldre version? Se lagerhanteringsguiden för mer information.</string> <string name="onboarding_storage_help_info">Osäker på vad du ska välja då du uppdaterar från en äldre version? Se lagerhanteringsguiden för mer information.</string>
<string name="pref_library_update_smart_update">Smart uppdatering</string> <string name="pref_library_update_smart_update">Smart uppdatering</string>
<string name="onboarding_storage_help_action">Lagerhanteringsguide</string> <string name="onboarding_storage_help_action">Lagerhanteringsguide</string>
<string name="theme_nord">Nord</string>
<string name="delete_repo_confirmation">Vill du ta bort förråd \"%s\"?</string>
<string name="ext_revoke_trust">Återkalla tillförlitliga okända tillägg</string>
<string name="label_extension_repos">Tilläggsförråd</string>
<string name="information_empty_repos">Du har inga förråd inställda.</string>
<string name="action_add_repo">Lägg till förråd</string>
<string name="label_add_repo_input">Förråd URL</string>
<string name="action_add_repo_message">Lägg till ytterliga förråd till Mihon. Detta ska vara en URL som slutar med \"index.min.json\".</string>
<string name="error_repo_exists">Detta förråd existerar redan!</string>
<string name="action_delete_repo">Ta bort förråd</string>
<string name="invalid_repo_name">Ogiltig förråds URL</string>
<string name="action_open_repo">Öppenkällkods förråd</string>
<string name="manga_interval_expected_update_soon">Snart</string>
<string name="manga_interval_custom_amount">Anpassad uppdateringsfrekvens:</string>
</resources> </resources>

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<string name="name">Ad</string> <string name="name">Ad</string>
<string name="categories">Ulamlar</string> <string name="categories">Kategoriler</string>
<string name="manga">Kitaplık girdileri</string> <string name="manga">Kitaplık girdileri</string>
<string name="chapters">Bölümler</string> <string name="chapters">Bölümler</string>
<string name="track">İzleme</string> <string name="track">İzleme</string>
@ -544,7 +544,7 @@
<string name="pref_verbose_logging">Ayrıntılı günlük kaydı</string> <string name="pref_verbose_logging">Ayrıntılı günlük kaydı</string>
<string name="pref_verbose_logging_summary">Ayrıntılı günlükleri sistem günlüğüne yaz (uygulama performansını düşürür)</string> <string name="pref_verbose_logging_summary">Ayrıntılı günlükleri sistem günlüğüne yaz (uygulama performansını düşürür)</string>
<string name="connected_to_wifi">Yalnızca kablosuz ağda</string> <string name="connected_to_wifi">Yalnızca kablosuz ağda</string>
<string name="download_queue_size_warning">Uyarı: Büyük toplu indirmeler kaynakların yavaşlamasına ve/veya Mihon\'yi engellemesine neden olabilir. Daha çok öğrenmek için dokunun.</string> <string name="download_queue_size_warning">Uyarı: Büyük toplu indirmeler kaynakların yavaşlamasına ve/veya Mihon\'u engellemesine neden olabilir. Daha çok öğrenmek için dokunun.</string>
<string name="update_72hour">3 günde bir</string> <string name="update_72hour">3 günde bir</string>
<string name="ext_update_all">Tümünü güncelle</string> <string name="ext_update_all">Tümünü güncelle</string>
<string name="channel_app_updates">Uygulama güncellemeleri</string> <string name="channel_app_updates">Uygulama güncellemeleri</string>
@ -755,11 +755,11 @@
<string name="action_bar_up_description">Yukarı git</string> <string name="action_bar_up_description">Yukarı git</string>
<string name="onboarding_storage_action_select">Klasör seç</string> <string name="onboarding_storage_action_select">Klasör seç</string>
<string name="pref_onboarding_guide">Başlangıç rehberi</string> <string name="pref_onboarding_guide">Başlangıç rehberi</string>
<string name="onboarding_guides_new_user">%s\'de yeni misiniz? Başlangıç rehberine göz atmanızı tavsiye ederiz.</string> <string name="onboarding_guides_new_user">%s\'da yeni misiniz? Başlangıç rehberine göz atmanızı tavsiye ederiz.</string>
<string name="onboarding_action_finish">Başlayın</string> <string name="onboarding_action_finish">Başlayın</string>
<string name="onboarding_storage_selection_required">Bir klasör seçilmelidir</string> <string name="onboarding_storage_selection_required">Bir klasör seçilmelidir</string>
<string name="onboarding_heading">Hoş geldiniz!</string> <string name="onboarding_heading">Hoş geldiniz!</string>
<string name="onboarding_guides_returning_user">%s\'yi yeniden mi kuruyorsunuz?</string> <string name="onboarding_guides_returning_user">%s\'u yeniden mi kuruyorsunuz?</string>
<string name="onboarding_action_skip">Atla</string> <string name="onboarding_action_skip">Atla</string>
<string name="onboarding_action_next">Sonraki</string> <string name="onboarding_action_next">Sonraki</string>
<string name="onboarding_description">Önce bazı şeyleri ayarlayalım. Bunları daha sonra ayarlardan da değiştirebilirsiniz.</string> <string name="onboarding_description">Önce bazı şeyleri ayarlayalım. Bunları daha sonra ayarlardan da değiştirebilirsiniz.</string>
@ -785,7 +785,7 @@
<string name="onboarding_storage_help_action">Depolama kılavuzu</string> <string name="onboarding_storage_help_action">Depolama kılavuzu</string>
<string name="action_add_repo">Depo ekle</string> <string name="action_add_repo">Depo ekle</string>
<string name="label_add_repo_input">Depo URL\'si</string> <string name="label_add_repo_input">Depo URL\'si</string>
<string name="action_add_repo_message">Mihon\'ye ek depolar ekleyin. Bu, \"index.min.json\" ile biten bir URL olmalıdır.</string> <string name="action_add_repo_message">Mihon\'a ek depolar ekleyin. Bu, \"index.min.json\" ile biten bir URL olmalıdır.</string>
<string name="error_repo_exists">Bu depo zaten var!</string> <string name="error_repo_exists">Bu depo zaten var!</string>
<string name="action_delete_repo">Depoyu sil</string> <string name="action_delete_repo">Depoyu sil</string>
<string name="invalid_repo_name">Geçersiz depo URL\'si</string> <string name="invalid_repo_name">Geçersiz depo URL\'si</string>

View File

@ -95,4 +95,10 @@
<item quantity="many">%d днів</item> <item quantity="many">%d днів</item>
<item quantity="other">%d днів</item> <item quantity="other">%d днів</item>
</plurals> </plurals>
<plurals name="num_repos">
<item quantity="one">%d репозиторій</item>
<item quantity="few">%d репозиторії</item>
<item quantity="many">%d репозиторіїв</item>
<item quantity="other">%d репозиторіїв</item>
</plurals>
</resources> </resources>

Some files were not shown because too many files have changed in this diff Show More