From 8b45ef0e5d5d368e0925df9816ae423defaed4d9 Mon Sep 17 00:00:00 2001 From: FlaminSarge Date: Thu, 8 May 2025 15:09:32 -0700 Subject: [PATCH] Add advanced option to always update manga title from source (#1182) Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com> --- CHANGELOG.md | 1 + .../domain/manga/interactor/UpdateManga.kt | 19 ++++++++-- .../settings/screen/SettingsAdvancedScreen.kt | 13 +++++-- .../tachiyomi/data/download/DownloadCache.kt | 35 +++++++++++++++++++ .../data/download/DownloadManager.kt | 32 +++++++++++++++++ .../library/service/LibraryPreferences.kt | 2 ++ .../moko-resources/base/strings.xml | 2 ++ 7 files changed, 99 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f2dc18461..e9e1153b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ The format is a modified version of [Keep a Changelog](https://keepachangelog.co - Use Github markdown flavour for Github releases & fix bullet list alignment ([@Secozzi](https://github.com/Secozzi)) ([#2024](https://github.com/mihonapp/mihon/pull/2024)) - Add Nord Theme ([@Riztard](https://github.com/Riztard)) ([#1951](https://github.com/mihonapp/mihon/pull/1951)) - Option to keep read manga when clearing database ([@AwkwardPeak7](https://github.com/AwkwardPeak7)) ([#1979](https://github.com/mihonapp/mihon/pull/1979)) +- Add advanced option to always update manga title from source ([@FlaminSarge](https://github.com/FlaminSarge)) ([#1182](https://github.com/mihonapp/mihon/pull/1182)) ### Improved - Significantly improve browsing speed (near instantaneous) ([@AntsyLich](https://github.com/AntsyLich)) ([#1946](https://github.com/mihonapp/mihon/pull/1946)) diff --git a/app/src/main/java/eu/kanade/domain/manga/interactor/UpdateManga.kt b/app/src/main/java/eu/kanade/domain/manga/interactor/UpdateManga.kt index d5cfc248d..7117e07af 100644 --- a/app/src/main/java/eu/kanade/domain/manga/interactor/UpdateManga.kt +++ b/app/src/main/java/eu/kanade/domain/manga/interactor/UpdateManga.kt @@ -2,7 +2,9 @@ package eu.kanade.domain.manga.interactor import eu.kanade.domain.manga.model.hasCustomCover import eu.kanade.tachiyomi.data.cache.CoverCache +import eu.kanade.tachiyomi.data.download.DownloadManager import eu.kanade.tachiyomi.source.model.SManga +import tachiyomi.domain.library.service.LibraryPreferences import tachiyomi.domain.manga.interactor.FetchInterval import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.manga.model.MangaUpdate @@ -31,6 +33,8 @@ class UpdateManga( remoteManga: SManga, manualFetch: Boolean, coverCache: CoverCache = Injekt.get(), + libraryPreferences: LibraryPreferences = Injekt.get(), + downloadManager: DownloadManager = Injekt.get(), ): Boolean { val remoteTitle = try { remoteManga.title @@ -38,8 +42,13 @@ class UpdateManga( "" } - // if the manga isn't a favorite, set its title from source and update in db - val title = if (remoteTitle.isEmpty() || localManga.favorite) null else remoteTitle + // if the manga isn't a favorite (or 'update titles' preference is enabled), set its title from source and update in db + val title = + if (remoteTitle.isNotEmpty() && (!localManga.favorite || libraryPreferences.updateMangaTitles().get())) { + remoteTitle + } else { + null + } val coverLastModified = when { @@ -59,7 +68,7 @@ class UpdateManga( val thumbnailUrl = remoteManga.thumbnail_url?.takeIf { it.isNotEmpty() } - return mangaRepository.update( + val success = mangaRepository.update( MangaUpdate( id = localManga.id, title = title, @@ -74,6 +83,10 @@ class UpdateManga( initialized = true, ), ) + if (success && title != null) { + downloadManager.renameManga(localManga, title) + } + return success } suspend fun awaitUpdateFetchInterval( diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAdvancedScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAdvancedScreen.kt index 1d886769e..14323f491 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAdvancedScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAdvancedScreen.kt @@ -63,6 +63,7 @@ import tachiyomi.core.common.util.lang.launchNonCancellable import tachiyomi.core.common.util.lang.withUIContext import tachiyomi.core.common.util.system.ImageUtil import tachiyomi.core.common.util.system.logcat +import tachiyomi.domain.library.service.LibraryPreferences import tachiyomi.domain.manga.interactor.ResetViewerFlags import tachiyomi.i18n.MR import tachiyomi.presentation.core.i18n.stringResource @@ -85,6 +86,7 @@ object SettingsAdvancedScreen : SearchableSettings { val basePreferences = remember { Injekt.get() } val networkPreferences = remember { Injekt.get() } + val libraryPreferences = remember { Injekt.get() } return listOf( Preference.PreferenceItem.TextPreference( @@ -125,7 +127,7 @@ object SettingsAdvancedScreen : SearchableSettings { getBackgroundActivityGroup(), getDataGroup(), getNetworkGroup(networkPreferences = networkPreferences), - getLibraryGroup(), + getLibraryGroup(libraryPreferences = libraryPreferences), getReaderGroup(basePreferences = basePreferences), getExtensionsGroup(basePreferences = basePreferences), ) @@ -286,7 +288,9 @@ object SettingsAdvancedScreen : SearchableSettings { } @Composable - private fun getLibraryGroup(): Preference.PreferenceGroup { + private fun getLibraryGroup( + libraryPreferences: LibraryPreferences, + ): Preference.PreferenceGroup { val scope = rememberCoroutineScope() val context = LocalContext.current @@ -314,6 +318,11 @@ object SettingsAdvancedScreen : SearchableSettings { } }, ), + Preference.PreferenceItem.SwitchPreference( + preference = libraryPreferences.updateMangaTitles(), + title = stringResource(MR.strings.pref_update_library_manga_titles), + subtitle = stringResource(MR.strings.pref_update_library_manga_titles_summary), + ), ), ) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadCache.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadCache.kt index 64487bf5e..1e7d98326 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadCache.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadCache.kt @@ -282,6 +282,41 @@ class DownloadCache( notifyChanges() } + /** + * Renames a manga in this cache. + * + * @param manga the manga being renamed. + * @param mangaUniFile the manga's new directory. + * @param newTitle the manga's new title. + */ + suspend fun renameManga(manga: Manga, mangaUniFile: UniFile, newTitle: String) { + rootDownloadsDirMutex.withLock { + val sourceDir = rootDownloadsDir.sourceDirs[manga.source] ?: return + val oldMangaDirName = provider.getMangaDirName(manga.title) + var oldChapterDirs: MutableSet? = null + // Save the old name's cached chapter dirs + if (sourceDir.mangaDirs.containsKey(oldMangaDirName)) { + oldChapterDirs = sourceDir.mangaDirs[oldMangaDirName]?.chapterDirs + sourceDir.mangaDirs -= oldMangaDirName + } + + // Retrieve/create the cached manga directory for new name + val newMangaDirName = provider.getMangaDirName(newTitle) + var mangaDir = sourceDir.mangaDirs[newMangaDirName] + if (mangaDir == null) { + mangaDir = MangaDirectory(mangaUniFile) + sourceDir.mangaDirs += newMangaDirName to mangaDir + } + + // Add the old chapters to new name's cache + if (!oldChapterDirs.isNullOrEmpty()) { + mangaDir.chapterDirs += oldChapterDirs + } + } + + notifyChanges() + } + suspend fun removeSource(source: Source) { rootDownloadsDirMutex.withLock { rootDownloadsDir.sourceDirs -= source.id diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadManager.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadManager.kt index d842eb0b8..2e79061b5 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadManager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadManager.kt @@ -327,6 +327,38 @@ class DownloadManager( } } + /** + * Renames manga download folder + * + * @param manga the manga + * @param newTitle the new manga title. + */ + suspend fun renameManga(manga: Manga, newTitle: String) { + val source = sourceManager.getOrStub(manga.source) + val oldFolder = provider.findMangaDir(manga.title, source) ?: return + val newName = provider.getMangaDirName(newTitle) + + if (oldFolder.name == newName) return + + // just to be safe, don't allow downloads for this manga while renaming it + downloader.removeFromQueue(manga) + + val capitalizationChanged = oldFolder.name.equals(newName, ignoreCase = true) + if (capitalizationChanged) { + val tempName = newName + Downloader.TMP_DIR_SUFFIX + if (!oldFolder.renameTo(tempName)) { + logcat(LogPriority.ERROR) { "Failed to rename manga download folder: ${oldFolder.name}" } + return + } + } + + if (oldFolder.renameTo(newName)) { + cache.renameManga(manga, oldFolder, newTitle) + } else { + logcat(LogPriority.ERROR) { "Failed to rename manga download folder: ${oldFolder.name}" } + } + } + /** * Renames an already downloaded chapter * diff --git a/domain/src/main/java/tachiyomi/domain/library/service/LibraryPreferences.kt b/domain/src/main/java/tachiyomi/domain/library/service/LibraryPreferences.kt index 9a68baf7b..a0c77d05b 100644 --- a/domain/src/main/java/tachiyomi/domain/library/service/LibraryPreferences.kt +++ b/domain/src/main/java/tachiyomi/domain/library/service/LibraryPreferences.kt @@ -189,6 +189,8 @@ class LibraryPreferences( ChapterSwipeAction.ToggleRead, ) + fun updateMangaTitles() = preferenceStore.getBoolean("pref_update_library_manga_titles", false) + // endregion enum class ChapterSwipeAction { diff --git a/i18n/src/commonMain/moko-resources/base/strings.xml b/i18n/src/commonMain/moko-resources/base/strings.xml index 86bb54c39..7d1a4681c 100644 --- a/i18n/src/commonMain/moko-resources/base/strings.xml +++ b/i18n/src/commonMain/moko-resources/base/strings.xml @@ -626,6 +626,8 @@ Verbose logging Print verbose logs to system log (reduces app performance) Debug info + Update library manga titles to match source + Warning: if a manga is renamed, it will be removed from the download queue (if present). Website