Compare commits

...

20 Commits

Author SHA1 Message Date
624d65d9fd refactor: add more error guard for gdrive.
Also changed it to be app specific, we don't want them to use sync data from SY or other forks as some of the model and backup is different. So if people using other forks they should use the same data and not mismatch.
2024-01-25 17:39:44 +11:00
86805651f1 Merge branch 'main' of https://github.com/mihonapp/mihon into feat/add-cross-device-sync 2024-01-25 16:16:46 +11: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
60 changed files with 322 additions and 208 deletions

View File

@ -1,11 +1,5 @@
blank_issues_enabled: false
contact_links:
- name: ⚠️ Extension/source issue
url: https://github.com/tachiyomiorg/extensions/issues/new/choose
about: Issues and requests for official extensions and sources should be opened in the extensions repository instead
- name: 📦 Mihon extensions
url: https://mihon.app/extensions/
about: List of all available extensions with download links
- name: 🖥️ Mihon website
url: https://mihon.app/
about: Guides, troubleshooting, and answers to common questions

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 |
|-------|--------|-------------|----------------|
| [![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) |
<div align="center">
# ![app icon](./.github/readme-images/app-icon.png)Mihon
Mihon is a free and open source manga reader for Android 8.0 and above.
<a href="https://mihon.app">
<img src="./.github/assets/logo.png" alt="Mihon logo" title="Mihon logo" width="80"/>
</a>
# Mihon [App](#)
### Full-featured reader
Discover and read manga, webtoons, comics, and more easier than ever on your Android device.
[![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 include:
* Online reading from a variety of sources
* Local reading of downloaded content
<div align="left">
* Local reading of content.
* A configurable reader with multiple viewers, reading directions and other settings.
* Tracker support: [MyAnimeList](https://myanimelist.net/), [AniList](https://anilist.co/), [Kitsu](https://kitsu.io/), [MangaUpdates](https://mangaupdates.com), [Shikimori](https://shikimori.one), and [Bangumi](https://bgm.tv/) support
* Categories to organize your library
* Light and dark themes
* Schedule updating your library for new chapters
* Create backups locally to read offline or to your desired cloud service
* Tracker support: [MyAnimeList](https://myanimelist.net/), [AniList](https://anilist.co/), [Kitsu](https://kitsu.io/), [MangaUpdates](https://mangaupdates.com), [Shikimori](https://shikimori.one), and [Bangumi](https://bgm.tv/) support.
* Categories to organize your library.
* Light and dark themes.
* Schedule updating your library for new chapters.
* Create backups locally to read offline or to your desired cloud service.
* Plus much more...
## Download
Get the app from our [releases page](https://github.com/mihonapp/mihon/releases).
</div>
If you want to try new features before they get to the stable release, you can download the 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).**
2. If you are unsure, ask here: [![Discord](https://img.shields.io/discord/1195734228319617024.svg)](https://discord.gg/mihon)
<details align="center"><summary>Issues</summary><div align="left">
</details>
Before reporting a new issue, take a look at the [FAQ](https://mihon.app/docs/faq/general), the [changelog](https://mihon.app/changelogs/) and the already opened [issues](https://github.com/mihonapp/mihon/issues).
<details><summary>Bugs</summary>
</div></details>
* Include version (More → About → Version)
* If not latest, try updating, it may have already been solved
* Beta version is equal to the number of commits as seen on the main page
* Include steps to reproduce (if not obvious from description)
* Include screenshot (if needed)
* If it could be device-dependent, try reproducing on another device (if possible)
<details align="center"><summary>Bugs</summary><div align="left">
* Include version (**More → About → Version**).
* If not latest, try updating, it may have already been solved.
* Beta version is equal to the number of commits as seen on the main page.
* Include steps to reproduce (if not obvious from description).
* Include screenshot (if needed).
* If it could be device-dependent, try reproducing on another device (if possible).
* Don't group unrelated requests into one issue
- **DO:** [#24](https://git.mihon.dev/tachiyomi/tachiyomi/issues/24), [#71](https://git.mihon.dev/tachiyomi/tachiyomi/issues/71)
- **DON'T:** [#75](https://git.mihon.dev/tachiyomi/tachiyomi/issues/75)
</details>
</div></details>
<details><summary>Feature Requests</summary>
<details align="center"><summary>Feature requests</summary><div align="left">
* Write a detailed issue, explaining what it should do or how. Avoid writing just "like X app does"
* Write a detailed issue, explaining what it should do or how.
* Avoid writing just "like X app does";
* Include screenshot (if needed)
* Source requests are not accepted.
Source requests are not accepted.
</details>
</div></details>
<details><summary>Contributing</summary>
### Repositories
See [CONTRIBUTING.md](./CONTRIBUTING.md).
</details>
[![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><summary>Code of Conduct</summary>
### Credits
See [CODE_OF_CONDUCT.md](./CODE_OF_CONDUCT.md).
</details>
Thank you to all the people who have contributed!
## FAQ
<a href="https://github.com/mihonapp/mihon/graphs/contributors">
<img src="https://contrib.rocks/image?repo=mihonapp/mihon" alt="Mihon app contributors" title="Mihon app contributors" width="800"/>
</a>
[See our website.](https://mihon.app/)
You can also reach out to us on [Discord](https://discord.gg/mihon).
### Disclaimer
## License
The developer(s) of this application does not have any affiliation with the content providers available, and this application hosts zero content.
Copyright 2015 Javier Tomás
### License
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
```
Copyright © 2015 Javier Tomás
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
## Disclaimer
Modifications Copyright © 2024 The Mihon Open Source Project
```
The developer of this application does not have any affiliation with the content providers available.
</div>

View File

@ -139,15 +139,15 @@ android {
}
dependencies {
implementation(project(":i18n"))
implementation(project(":core"))
implementation(project(":core-metadata"))
implementation(project(":source-api"))
implementation(project(":source-local"))
implementation(project(":data"))
implementation(project(":domain"))
implementation(project(":presentation-core"))
implementation(project(":presentation-widget"))
implementation(projects.i18n)
implementation(projects.core)
implementation(projects.coreMetadata)
implementation(projects.sourceApi)
implementation(projects.sourceLocal)
implementation(projects.data)
implementation(projects.domain)
implementation(projects.presentationCore)
implementation(projects.presentationWidget)
// Compose
implementation(platform(compose.bom))

View File

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

View File

@ -4,13 +4,6 @@
android:viewportWidth="432"
android:viewportHeight="432">
<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: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:pathData="M182.03,188.7L181.33,172.69C183.42,173.09 185.91,173.19 191.57,173.19C198.44,173.19 207.49,172.79 212.16,172.19C214.15,171.99 214.95,171.7 216.24,171L226.98,180.15C225.98,181.54 225.68,182.14 224.59,184.92C223.7,187.11 219.62,199.74 218.03,205.11C225.39,206.6 229.46,207.7 235.03,209.98C235.73,205.11 235.83,202.52 235.83,193.67C235.83,191.39 235.73,190.09 235.43,188.01L252.74,188.6C252.24,190.99 252.14,191.98 252.04,195.86C251.64,205.21 251.24,209.68 250.25,216.45C257.11,219.93 257.11,219.93 260.59,221.82C262.38,222.81 262.78,223.01 263.97,223.41L258.2,242.01C255.42,239.52 251.54,236.83 245.87,233.65C240.9,245.49 232.65,254.14 220.12,261C215.94,255.43 212.76,252.05 207.68,248.07C215.04,244.59 218.43,242.4 222.3,238.72C226.08,235.04 228.57,231.46 230.96,226.09C224.59,223.21 220.51,221.92 213.45,220.43C209.38,232.56 206.09,240.32 203.21,244.99C199.33,251.25 194.06,254.54 187.99,254.54C183.32,254.54 178.55,252.45 175.07,248.87C171.09,244.79 169,239.12 169,232.56C169,222.81 173.67,214.36 181.83,209.09C187.1,205.71 192.67,204.21 201.52,203.72C203.31,197.85 204.8,192.78 206.19,187.11C201.82,187.51 196.35,187.81 189.68,188.1C186.1,188.2 184.91,188.3 182.03,188.7ZM197.14,218.93C192.47,219.73 189.68,221.22 187.2,224.4C185.31,226.59 184.41,229.18 184.41,231.96C184.41,235.04 185.91,237.33 187.8,237.33C190.08,237.33 192.67,232.16 197.14,218.93Z"
android:fillColor="#031019"/>
</vector>

View File

@ -163,7 +163,7 @@ class SyncChaptersWithSource(
var updatedToAdd = newChapters.map { toAddItem ->
var chapter = toAddItem.copy(dateFetch = nowMillis + itemCount--)
if (chapter.isRecognizedNumber.not() || chapter.chapterNumber !in deletedChapterNumbers) return@map chapter
if (!chapter.isRecognizedNumber || chapter.chapterNumber !in deletedChapterNumbers) return@map chapter
chapter = chapter.copy(
read = chapter.chapterNumber in deletedReadChapterNumbers,

View File

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

View File

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

View File

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

View File

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

View File

@ -29,7 +29,7 @@ fun Track.toDbTrack(): DbTrack = DbTrack.create(trackerId).also {
}
fun DbTrack.toDomainTrack(idRequired: Boolean = true): Track? {
val trackId = id ?: if (idRequired.not()) -1 else return null
val trackId = id ?: if (!idRequired) -1 else return null
return Track(
id = trackId,
mangaId = manga_id,

View File

@ -203,7 +203,13 @@ private fun ExtensionContent(
items(
items = items,
contentType = { "item" },
key = { "extension-${it.hashCode()}" },
key = { item ->
when (item.extension) {
is Extension.Untrusted -> "extension-untrusted-${item.hashCode()}"
is Extension.Installed -> "extension-installed-${item.hashCode()}"
is Extension.Available -> "extension-available-${item.hashCode()}"
}
},
) { item ->
ExtensionItem(
modifier = Modifier.animateItemPlacement(),

View File

@ -7,8 +7,11 @@ import android.net.Uri
import androidx.activity.compose.ManagedActivityResultLauncher
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.layout.IntrinsicSize
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.outlined.HelpOutline
@ -203,9 +206,11 @@ object SettingsDataScreen : SearchableSettings {
MultiChoiceSegmentedButtonRow(
modifier = Modifier
.fillMaxWidth()
.height(intrinsicSize = IntrinsicSize.Min)
.padding(horizontal = PrefsHorizontalPadding),
) {
SegmentedButton(
modifier = Modifier.fillMaxHeight(),
checked = false,
onCheckedChange = { navigator.push(CreateBackupScreen()) },
shape = SegmentedButtonDefaults.itemShape(0, 2),
@ -213,6 +218,7 @@ object SettingsDataScreen : SearchableSettings {
Text(stringResource(MR.strings.pref_create_backup))
}
SegmentedButton(
modifier = Modifier.fillMaxHeight(),
checked = false,
onCheckedChange = {
if (!BackupRestoreJob.isRunning(context)) {
@ -394,12 +400,19 @@ object SettingsDataScreen : SearchableSettings {
when (result) {
GoogleDriveSyncService.DeleteSyncDataStatus.NOT_INITIALIZED -> context.toast(
MR.strings.google_drive_not_signed_in,
duration = 5000,
)
GoogleDriveSyncService.DeleteSyncDataStatus.NO_FILES -> context.toast(
MR.strings.google_drive_sync_data_not_found,
duration = 5000,
)
GoogleDriveSyncService.DeleteSyncDataStatus.SUCCESS -> context.toast(
MR.strings.google_drive_sync_data_purged,
duration = 5000,
)
GoogleDriveSyncService.DeleteSyncDataStatus.ERROR -> context.toast(
MR.strings.google_drive_sync_data_purge_error,
duration = 10000,
)
}
}

View File

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

View File

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

View File

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

View File

@ -74,6 +74,7 @@ class MangaRestorer(
backupCategories = backupCategories,
history = backupManga.history + backupManga.brokenHistory.map { it.toBackupHistory() },
tracks = backupManga.tracking,
excludedScanlators = backupManga.excludedScanlators,
)
}
}
@ -265,11 +266,13 @@ class MangaRestorer(
backupCategories: List<BackupCategory>,
history: List<BackupHistory>,
tracks: List<BackupTracking>,
excludedScanlators: List<String>,
): Manga {
restoreCategories(manga, categories, backupCategories)
restoreChapters(manga, chapters)
restoreTracking(manga, tracks)
restoreHistory(history)
restoreExcludedScanlators(manga, excludedScanlators)
updateManga.awaitUpdateFetchInterval(manga, now, currentFetchWindow)
return manga
}
@ -402,4 +405,25 @@ class MangaRestorer(
}
private fun Track.forComparison() = this.copy(id = 0L, mangaId = 0L)
/**
* Restores the excluded scanlators for the manga.
*
* @param manga the manga whose excluded scanlators have to be restored.
* @param excludedScanlators the excluded scanlators to restore.
*/
private suspend fun restoreExcludedScanlators(manga: Manga, excludedScanlators: List<String>) {
if (excludedScanlators.isEmpty()) return
val existingExcludedScanlators = handler.awaitList {
excluded_scanlatorsQueries.getExcludedScanlatorsByMangaId(manga.id)
}
val toInsert = excludedScanlators.filter { it !in existingExcludedScanlators }
if (toInsert.isNotEmpty()) {
handler.await {
toInsert.forEach {
excluded_scanlatorsQueries.insert(manga.id, it)
}
}
}
}
}

View File

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

View File

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

View File

@ -56,18 +56,22 @@ class GoogleDriveSyncService(context: Context, json: Json, syncPreferences: Sync
NOT_INITIALIZED,
NO_FILES,
SUCCESS,
ERROR,
}
private val remoteFileName = "tachiyomi_sync_data.gz"
private val appName = context.stringResource(MR.strings.app_name)
private val lockFileName = "tachiyomi_sync.lock"
private val remoteFileName = "${appName}_sync_data.gz"
private val lockFileName = "${appName}_sync.lock"
private val googleDriveService = GoogleDriveService(context)
override suspend fun beforeSync() {
try {
googleDriveService.refreshToken()
val drive = googleDriveService.driveService ?: throw Exception("Google Drive service not initialized")
val drive = googleDriveService.driveService
?: throw Exception(context.stringResource(MR.strings.google_drive_not_signed_in))
var backoff = 1000L
@ -105,6 +109,7 @@ class GoogleDriveSyncService(context: Context, json: Json, syncPreferences: Sync
}
} catch (e: Exception) {
logcat(LogPriority.ERROR) { "Error in GoogleDrive beforeSync: ${e.message}" }
throw Exception(context.stringResource(MR.strings.error_before_sync_gdrive) + ": ${e.message}")
}
}
@ -234,6 +239,7 @@ class GoogleDriveSyncService(context: Context, json: Json, syncPreferences: Sync
Log.d("GoogleDrive", "Created lock file with ID: ${file.id}")
} catch (e: Exception) {
Log.e("GoogleDrive", "Error creating lock file: ${e.message}")
throw Exception(e.message)
}
}
@ -282,19 +288,24 @@ class GoogleDriveSyncService(context: Context, json: Json, syncPreferences: Sync
googleDriveService.refreshToken()
return withContext(Dispatchers.IO) {
val appDataFileList = getAppDataFileList(drive)
try {
val appDataFileList = getAppDataFileList(drive)
if (appDataFileList.isEmpty()) {
logcat(LogPriority.DEBUG) { "No sync data file found in appData folder of Google Drive" }
DeleteSyncDataStatus.NO_FILES
} else {
for (file in appDataFileList) {
drive.files().delete(file.id).execute()
logcat(
LogPriority.DEBUG,
) { "Deleted sync data file in appData folder of Google Drive with file ID: ${file.id}" }
if (appDataFileList.isEmpty()) {
logcat(LogPriority.DEBUG) { "No sync data file found in appData folder of Google Drive" }
DeleteSyncDataStatus.NO_FILES
} else {
for (file in appDataFileList) {
drive.files().delete(file.id).execute()
logcat(
LogPriority.DEBUG,
) { "Deleted sync data file in appData folder of Google Drive with file ID: ${file.id}" }
}
DeleteSyncDataStatus.SUCCESS
}
DeleteSyncDataStatus.SUCCESS
} catch (e: Exception) {
logcat(LogPriority.ERROR) { "Error occurred while interacting with Google Drive: ${e.message}" }
DeleteSyncDataStatus.ERROR
}
}
}
@ -441,7 +452,7 @@ class GoogleDriveService(private val context: Context) {
NetHttpTransport(),
jsonFactory,
credential,
).setApplicationName("Tachiyomi")
).setApplicationName(context.stringResource(MR.strings.app_name))
.build()
}

View File

@ -185,7 +185,7 @@ class Anilist(id: Long) : BaseTracker(id, "AniList"), DeletableTracker {
if (track.status != COMPLETED) {
val isRereading = track.status == REREADING
track.status = if (isRereading.not() && hasReadChapters) READING else track.status
track.status = if (!isRereading && hasReadChapters) READING else track.status
}
update(track)

View File

@ -1,5 +1,6 @@
package eu.kanade.tachiyomi.data.track.anilist
import eu.kanade.tachiyomi.BuildConfig
import okhttp3.Interceptor
import okhttp3.Response
import java.io.IOException
@ -40,6 +41,7 @@ class AnilistInterceptor(val anilist: Anilist, private var token: String?) : Int
// Add the authorization header to the original request.
val authRequest = originalRequest.newBuilder()
.addHeader("Authorization", "Bearer ${oauth!!.access_token}")
.header("User-Agent", "Mihon v${BuildConfig.VERSION_NAME} (${BuildConfig.APPLICATION_ID})")
.build()
return chain.proceed(authRequest)

View File

@ -182,8 +182,8 @@ class BangumiApi(
)
companion object {
private const val clientId = "bgm10555cda0762e80ca"
private const val clientSecret = "8fff394a8627b4c388cbf349ec865775"
private const val clientId = "bgm291665acbd06a4c28"
private const val clientSecret = "43e5ce36b207de16e5d3cfd3e79118db"
private const val apiUrl = "https://api.bgm.tv"
private const val oauthUrl = "https://bgm.tv/oauth/access_token"

View File

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

View File

@ -18,7 +18,7 @@ class KavitaInterceptor(private val kavita: Kavita) : Interceptor {
// Add the authorization header to the original request.
val authRequest = originalRequest.newBuilder()
.addHeader("Authorization", "Bearer $jwtToken")
.header("User-Agent", "Tachiyomi Kavita v${BuildConfig.VERSION_NAME}")
.header("User-Agent", "Mihon v${BuildConfig.VERSION_NAME} (${BuildConfig.APPLICATION_ID})")
.build()
return chain.proceed(authRequest)

View File

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

View File

@ -1,5 +1,6 @@
package eu.kanade.tachiyomi.data.track.komga
import eu.kanade.tachiyomi.BuildConfig
import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.data.track.model.TrackSearch
import eu.kanade.tachiyomi.network.GET
@ -8,6 +9,7 @@ import eu.kanade.tachiyomi.network.parseAs
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import logcat.LogPriority
import okhttp3.Headers
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.Request
@ -23,6 +25,12 @@ class KomgaApi(
private val client: OkHttpClient,
) {
private val headers: Headers by lazy {
Headers.Builder()
.add("User-Agent", "Mihon v${BuildConfig.VERSION_NAME} (${BuildConfig.APPLICATION_ID})")
.build()
}
private val json: Json by injectLazy()
suspend fun getTrackSearch(url: String): TrackSearch =
@ -30,12 +38,12 @@ class KomgaApi(
try {
val track = with(json) {
if (url.contains(READLIST_API)) {
client.newCall(GET(url))
client.newCall(GET(url, headers))
.awaitSuccess()
.parseAs<ReadListDto>()
.toTrack()
} else {
client.newCall(GET(url))
client.newCall(GET(url, headers))
.awaitSuccess()
.parseAs<SeriesDto>()
.toTrack()
@ -43,7 +51,9 @@ class KomgaApi(
}
val progress = client
.newCall(GET("${url.replace("/api/v1/series/", "/api/v2/series/")}/read-progress/tachiyomi"))
.newCall(
GET("${url.replace("/api/v1/series/", "/api/v2/series/")}/read-progress/tachiyomi", headers),
)
.awaitSuccess().let {
with(json) {
if (url.contains("/api/v1/series/")) {
@ -80,6 +90,7 @@ class KomgaApi(
client.newCall(
Request.Builder()
.url("${track.tracking_url.replace("/api/v1/series/", "/api/v2/series/")}/read-progress/tachiyomi")
.headers(headers)
.put(payload.toRequestBody("application/json".toMediaType()))
.build(),
)

View File

@ -104,7 +104,7 @@ class MyAnimeList(id: Long) : BaseTracker(id, "MyAnimeList"), DeletableTracker {
if (track.status != COMPLETED) {
val isRereading = track.status == REREADING
track.status = if (isRereading.not() && hasReadChapters) READING else track.status
track.status = if (!isRereading && hasReadChapters) READING else track.status
}
update(track)

View File

@ -1,5 +1,6 @@
package eu.kanade.tachiyomi.data.track.myanimelist
import eu.kanade.tachiyomi.BuildConfig
import eu.kanade.tachiyomi.network.parseAs
import kotlinx.serialization.json.Json
import okhttp3.Interceptor
@ -34,6 +35,7 @@ class MyAnimeListInterceptor(private val myanimelist: MyAnimeList, private var t
// Add the authorization header to the original request
val authRequest = originalRequest.newBuilder()
.addHeader("Authorization", "Bearer ${oauth!!.access_token}")
.header("User-Agent", "Mihon v${BuildConfig.VERSION_NAME} (${BuildConfig.APPLICATION_ID})")
.build()
val response = chain.proceed(authRequest)
@ -50,6 +52,7 @@ class MyAnimeListInterceptor(private val myanimelist: MyAnimeList, private var t
val newRequest = originalRequest.newBuilder()
.addHeader("Authorization", "Bearer ${newToken.access_token}")
.header("User-Agent", "Mihon v${BuildConfig.VERSION_NAME} (${BuildConfig.APPLICATION_ID})")
.build()
return chain.proceed(newRequest)

View File

@ -72,7 +72,7 @@ class Shikimori(id: Long) : BaseTracker(id, "Shikimori"), DeletableTracker {
if (track.status != COMPLETED) {
val isRereading = track.status == REREADING
track.status = if (isRereading.not() && hasReadChapters) READING else track.status
track.status = if (!isRereading && hasReadChapters) READING else track.status
}
update(track)

View File

@ -192,8 +192,8 @@ class ShikimoriApi(
)
companion object {
private const val clientId = "1aaf4cf232372708e98b5abc813d795b539c5a916dbbfe9ac61bf02a360832cc"
private const val clientSecret = "229942c742dd4cde803125d17d64501d91c0b12e14cb1e5120184d77d67024c0"
private const val clientId = "PB9dq8DzI405s7wdtwTdirYqHiyVMh--djnP7lBUqSA"
private const val clientSecret = "NajpZcOBKB9sJtgNcejf8OB9jBN1OYYoo-k4h2WWZus"
private const val baseUrl = "https://shikimori.one"
private const val apiUrl = "$baseUrl/api"

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -4,13 +4,6 @@
android:viewportWidth="432"
android:viewportHeight="432">
<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: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:pathData="M182.03,188.7L181.33,172.69C183.42,173.09 185.91,173.19 191.57,173.19C198.44,173.19 207.49,172.79 212.16,172.19C214.15,171.99 214.95,171.7 216.24,171L226.98,180.15C225.98,181.54 225.68,182.14 224.59,184.92C223.7,187.11 219.62,199.74 218.03,205.11C225.39,206.6 229.46,207.7 235.03,209.98C235.73,205.11 235.83,202.52 235.83,193.67C235.83,191.39 235.73,190.09 235.43,188.01L252.74,188.6C252.24,190.99 252.14,191.98 252.04,195.86C251.64,205.21 251.24,209.68 250.25,216.45C257.11,219.93 257.11,219.93 260.59,221.82C262.38,222.81 262.78,223.01 263.97,223.41L258.2,242.01C255.42,239.52 251.54,236.83 245.87,233.65C240.9,245.49 232.65,254.14 220.12,261C215.94,255.43 212.76,252.05 207.68,248.07C215.04,244.59 218.43,242.4 222.3,238.72C226.08,235.04 228.57,231.46 230.96,226.09C224.59,223.21 220.51,221.92 213.45,220.43C209.38,232.56 206.09,240.32 203.21,244.99C199.33,251.25 194.06,254.54 187.99,254.54C183.32,254.54 178.55,252.45 175.07,248.87C171.09,244.79 169,239.12 169,232.56C169,222.81 173.67,214.36 181.83,209.09C187.1,205.71 192.67,204.21 201.52,203.72C203.31,197.85 204.8,192.78 206.19,187.11C201.82,187.51 196.35,187.81 189.68,188.1C186.1,188.2 184.91,188.3 182.03,188.7ZM197.14,218.93C192.47,219.73 189.68,221.22 187.2,224.4C185.31,226.59 184.41,229.18 184.41,231.96C184.41,235.04 185.91,237.33 187.8,237.33C190.08,237.33 192.67,232.16 197.14,218.93Z"
android:fillColor="#031019"/>
</vector>

View File

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

View File

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

View File

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

View File

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

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 {
implementation(project(":source-api"))
implementation(project(":core"))
implementation(projects.sourceApi)
implementation(projects.core)
implementation(platform(kotlinx.coroutines.bom))
implementation(kotlinx.bundles.coroutines)

View File

@ -20,7 +20,7 @@ class GetApplicationRelease(
val now = Instant.now()
// Limit checks to once every 3 days at most
if (arguments.forceCheck.not() && now.isBefore(
if (!arguments.forceCheck && now.isBefore(
Instant.ofEpochMilli(lastChecked.get()).plus(3, ChronoUnit.DAYS),
)
) {

View File

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

View File

@ -577,11 +577,13 @@
<string name="pref_google_drive_purge_sync_data">Clear Sync Data from Google Drive</string>
<string name="google_drive_sync_data_purged">Sync data purged from Google Drive</string>
<string name="google_drive_sync_data_not_found">No sync data found in Google Drive</string>
<string name="google_drive_sync_data_purge_error">Error purging sync data from Google Drive, Try to sign in again.</string>
<string name="google_drive_login_success">Logged in to Google Drive</string>
<string name="google_drive_login_failed">Failed to log in to Google Drive: %s</string>
<string name="google_drive_not_signed_in">Not signed in to Google Drive</string>
<string name="error_uploading_sync_data">Error uploading sync data to Google Drive</string>
<string name="error_deleting_google_drive_lock_file">Error Deleting Google Drive Lock File</string>
<string name="error_before_sync_gdrive">Error before sync: %s</string>
<string name="pref_purge_confirmation_title">Purge confirmation</string>
<string name="pref_purge_confirmation_message">Purging sync data will delete all your sync data from Google Drive. Are you sure you want to continue?</string>
<string name="pref_sync_options">Create sync triggers</string>

View File

@ -38,7 +38,7 @@
</plurals>
<plurals name="num_trackers">
<item quantity="one">%d tracker</item>
<item quantity="other">%d mga tracker</item>
<item quantity="other">%d na tracker</item>
</plurals>
<plurals name="missing_chapters_warning">
<item quantity="one">Nilaktawan ang %d na kabanata, maaaring ito ay wala sa source o na-filter ang mga ito</item>
@ -46,11 +46,11 @@
</plurals>
<plurals name="relative_time">
<item quantity="one">Kahapon</item>
<item quantity="other">%1$d araw na ang makalipas</item>
<item quantity="other">%1$d araw na ang nakakalipas</item>
</plurals>
<plurals name="next_unread_chapters">
<item quantity="one">Susunod na hindi pa nababasa na kabanata</item>
<item quantity="other">Susunod na %d di pa nababasa na kabanata</item>
<item quantity="one">Susunod na hindi pa nababasang kabanata</item>
<item quantity="other">Susunod na %d hindi pa nababasang kabanata</item>
</plurals>
<plurals name="download_amount">
<item quantity="one">Sunod na kabanata</item>
@ -58,14 +58,14 @@
</plurals>
<plurals name="missing_chapters">
<item quantity="one">Nawawalang %1$s na kabanata</item>
<item quantity="other">Nawawalang %1$s mga kabanata</item>
<item quantity="other">Nawawalang %1$s na mga kabanata</item>
</plurals>
<plurals name="day">
<item quantity="one">1 araw</item>
<item quantity="other">%d (mga) araw</item>
<item quantity="other">%d (na) araw</item>
</plurals>
<plurals name="num_repos">
<item quantity="one">%d na repo</item>
<item quantity="other">%d na mga repo</item>
</plurals>
</resources>
</resources>

View File

@ -22,7 +22,7 @@
<string name="action_disable">Isara</string>
<string name="action_display_show_tabs">Ipakita ang mga tab ng kategorya</string>
<string name="action_display_download_badge">Bilang ng kabanatang na-download</string>
<string name="action_display_comfortable_grid">Maalwan na grid</string>
<string name="action_display_comfortable_grid">Kumportableng grid</string>
<string name="action_display_list">Listahan</string>
<string name="action_display_grid">Siksik na grid</string>
<string name="action_display">Pagpapakita</string>
@ -114,7 +114,7 @@
<string name="second_to_last">Pangalawa sa huling nabasa</string>
<string name="last_read_chapter">Huling nabasang kabanata</string>
<string name="disabled">Sarado</string>
<string name="pref_remove_after_marked_as_read">Pagkamarkahang nabasa na</string>
<string name="pref_remove_after_marked_as_read">Markahang nabasa na</string>
<string name="pref_remove_after_read">Pagkatapos basahin, awtomatikong burahin</string>
<string name="pref_webtoon_side_padding">Kapal ng gilid</string>
<string name="pref_category_reading">Pagbabasa</string>
@ -243,7 +243,7 @@
<string name="channel_ext_updates">Mga update sa extension</string>
<string name="tapping_inverted_none">Wala</string>
<string name="channel_new_chapters">Mga update sa kabanata</string>
<string name="channel_common">Komon</string>
<string name="channel_common">Pangkaraniwan</string>
<string name="download_notifier_download_paused">Nakahinto ang mga pag-download</string>
<string name="download_notifier_no_network">Walang koneksyon sa Internet</string>
<string name="download_notifier_text_only_wifi">Walang koneksyon sa Wifi</string>
@ -291,7 +291,7 @@
<string name="decode_image_error">Di mai-load ang larawan</string>
<string name="no_next_chapter">Di makita ang susunod na kabanata</string>
<string name="chapter_progress">Pahina: %1$d</string>
<string name="cover_updated">Napalitan na ang cover</string>
<string name="cover_updated">Napalitan ang cover</string>
<string name="set_as_cover">Gawin itong cover</string>
<string name="custom_filter">Pinili kong filter</string>
<string name="picture_saved">Na-save na ang larawan</string>
@ -556,7 +556,7 @@
<string name="library_errors_help">Para sa tulong sa pag-aayos ng mga error sa pag-update ng aklatan, tingnan ang %1$s</string>
<string name="pref_update_only_completely_read">Laktawan ang mga entry na hindi pa nababasang kabanata</string>
<string name="save_chapter_as_cbz">I-save bilang CBZ archive</string>
<string name="publishing_finished">Tapos na\'ng mailathala</string>
<string name="publishing_finished">Tapos na ang paglalathala</string>
<string name="on_hiatus">Naka-hiatus</string>
<string name="cancelled">Kinansela</string>
<string name="action_show_manga">Ipakita ang entry</string>
@ -639,9 +639,9 @@
<string name="pref_library_summary">Mga kategorya, panlahatang update, pag-swipe ng kabanata</string>
<string name="pref_browse_summary">Mga source, extension, panlahatang paghanap</string>
<string name="crash_screen_description">Nagkaroon ng hindi inaasahang error ang %s. Iminumungkahi naming ibahagi mo ang mga crash log sa aming support channel sa Discord.</string>
<string name="crash_screen_title">Ay!</string>
<string name="crash_screen_title">Ay Naku!</string>
<string name="crash_screen_restart_application">Buksan muli ang app</string>
<string name="invalid_location">Invalid na lugar: %s</string>
<string name="invalid_location">Imbalidong lugar: %s</string>
<string name="unknown_title">Di alam na pamagat</string>
<string name="error_user_agent_string_invalid">Di-wastong string ng user agent</string>
<string name="updates_last_update_info_just_now">Ngayon lang</string>
@ -693,7 +693,7 @@
<string name="pref_page_rotate">I-rotate ang malalawak na pahina upang magkasya</string>
<string name="pref_page_rotate_invert">I-flip ang oryentasyon ng mga pinaikot na malalawak na pahina</string>
<string name="overlay_header">Nakapatong (Overlay)</string>
<string name="split_tall_images">Hatiin ang mga matatangkad na larawan</string>
<string name="split_tall_images">Hatiin ang mga matataas na larawan</string>
<string name="pref_debug_info">Impormasyon sa pag-debug</string>
<string name="pref_chapter_swipe_start">Mag-swipe ng pakaliwang pagkilos</string>
<string name="pref_chapter_swipe">Mag swipe ng kabanata</string>
@ -798,4 +798,4 @@
<string name="manga_interval_expected_update_soon">Malapit na</string>
<string name="ext_revoke_trust">Bawiin ang mga pinagkakatiwalaang hindi kilalang extension</string>
<string name="action_open_repo">Open source na repo</string>
</resources>
</resources>

View File

@ -412,7 +412,7 @@
<string name="pref_category_delete_chapters">Suppression des chapitres</string>
<string name="ext_nsfw_warning">Les sources de cette extension peuvent contenir du contenu NSFW (18+)</string>
<string name="ext_nsfw_short">18+</string>
<string name="parental_controls_info">Ceci n\'empêche pas les extensions non officielles ou potentiellement mal signalées de diffuser du contenu +18 dans l\'application.</string>
<string name="parental_controls_info">Ceci n\'empêche pas les extensions de diffuser du contenu +18 dans l\'application.</string>
<string name="no_chapters_error">Aucun chapitre trouvé</string>
<string name="confirm_set_chapter_settings">Appliquer ce paramétrage par défaut \?</string>
<string name="chapter_settings">Paramètres du chapitre</string>
@ -534,7 +534,7 @@
<string name="ext_installer_shizuku_unavailable_dialog">Installez et démarrez Shizuku pour utiliser Shizuku comme installateur d\'extensions.</string>
<string name="ext_installer_shizuku_stopped">Shizuku n\'est pas en cours d\'exécution</string>
<string name="ext_installer_legacy">Legacy</string>
<string name="ext_installer_pref">installeur</string>
<string name="ext_installer_pref">Installeur</string>
<string name="ext_install_service_notif">Installation de l\'extension…</string>
<string name="action_sort_count">Entrées totales</string>
<string name="pref_verbose_logging">Rapports détaillés</string>
@ -798,4 +798,4 @@
<string name="create_backup_file_error">Impossible de créer un fichier de sauvegarde</string>
<string name="last_auto_backup_info">Dernière sauvegarde automatique: %s</string>
<string name="source_settings">Paramètres sources</string>
</resources>
</resources>

View File

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

View File

@ -544,7 +544,7 @@
<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="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="ext_update_all">Tümünü güncelle</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="onboarding_storage_action_select">Klasör seç</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_storage_selection_required">Bir klasör seçilmelidir</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_next">Sonraki</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="action_add_repo">Depo ekle</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="action_delete_repo">Depoyu sil</string>
<string name="invalid_repo_name">Geçersiz depo URL\'si</string>
@ -798,4 +798,4 @@
<string name="ext_revoke_trust">Güvenilen bilinmeyen uzantıları iptal et</string>
<string name="manga_interval_expected_update_soon">Yakında</string>
<string name="action_open_repo">ık kaynaklı depo</string>
</resources>
</resources>

View File

@ -7,7 +7,7 @@
<item quantity="other">%d 個擴充套件可更新</item>
</plurals>
<plurals name="download_queue_summary">
<item quantity="other">剩餘 %1$s </item>
<item quantity="other">剩餘 %1$s </item>
</plurals>
<plurals name="manga_num_chapters">
<item quantity="other">共 %1$s 章</item>

View File

@ -594,11 +594,11 @@
<string name="ext_info_version">版本</string>
<string name="ext_info_language">語言</string>
<string name="ext_info_age_rating">分級</string>
<string name="unfinished_list">未成清單</string>
<string name="on_hold_list">擱置清單</string>
<string name="complete_list">閱畢清單</string>
<string name="wish_list">願望清單</string>
<string name="reading_list">閱讀清單</string>
<string name="unfinished_list">已拋棄</string>
<string name="on_hold_list">擱置</string>
<string name="complete_list">已完結</string>
<string name="wish_list">準備讀</string>
<string name="reading_list">閱讀</string>
<string name="network_not_metered">僅透過非計量付費網路</string>
<string name="cant_open_last_read_chapter">無法開啟上次閱讀章節</string>
<string name="custom_cover">自訂封面</string>
@ -784,7 +784,7 @@
<string name="manga_interval_custom_amount">自訂更新頻率:</string>
<string name="label_add_repo_input">儲存庫網址</string>
<string name="error_repo_exists">已有該儲存庫!</string>
<string name="pref_library_update_smart_update">智慧更新</string>
<string name="pref_library_update_smart_update">智慧更新</string>
<string name="invalid_repo_name">無效的儲存庫網址</string>
<string name="action_add_repo_message">將額外的擴充套件儲存庫新增至 Mihon。此處應填入一個結尾為「index.min.json」的網址。</string>
<string name="delete_repo_confirmation">確定要刪除「%s」儲存庫嗎</string>

View File

@ -21,8 +21,8 @@ android {
}
dependencies {
api(project(":core"))
api(project(":i18n"))
api(projects.core)
api(projects.i18n)
// Compose
implementation(platform(compose.bom))

View File

@ -21,10 +21,10 @@ android {
}
dependencies {
implementation(project(":core"))
implementation(project(":domain"))
implementation(project(":presentation-core"))
api(project(":i18n"))
implementation(projects.core)
implementation(projects.domain)
implementation(projects.presentationCore)
api(projects.i18n)
implementation(compose.glance)
lintChecks(compose.lintchecks)

View File

@ -35,6 +35,8 @@ dependencyResolutionManagement {
}
}
enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
rootProject.name = "Mihon"
include(":app")
include(":i18n")

View File

@ -17,7 +17,7 @@ kotlin {
}
val androidMain by getting {
dependencies {
implementation(project(":core"))
implementation(projects.core)
api(libs.preferencektx)
// Workaround for https://youtrack.jetbrains.com/issue/KT-57605

View File

@ -8,8 +8,8 @@ kotlin {
sourceSets {
val commonMain by getting {
dependencies {
implementation(project(":source-api"))
api(project(":i18n"))
implementation(projects.sourceApi)
api(projects.i18n)
implementation(libs.unifile)
implementation(libs.junrar)
@ -17,11 +17,11 @@ kotlin {
}
val androidMain by getting {
dependencies {
implementation(project(":core"))
implementation(project(":core-metadata"))
implementation(projects.core)
implementation(projects.coreMetadata)
// Move ChapterRecognition to separate module?
implementation(project(":domain"))
implementation(projects.domain)
implementation(kotlinx.bundles.serialization)
}