Fix downloader stopping after failing to create download directory of a manga (mihonapp/mihon#2068)

Co-authored-by: Cuong-Tran <cuongtran.tm@gmail.com>
(cherry picked from commit 536393a6d9941fac282f10b825aa611b91e1fcdb)
This commit is contained in:
AntsyLich 2025-05-05 01:02:08 +06:00 committed by Cuong-Tran
parent 23565be68c
commit e39354926c
No known key found for this signature in database
GPG key ID: 733AA7624B9315C2
5 changed files with 47 additions and 23 deletions

View file

@ -47,7 +47,8 @@ The format is a modified version of [Keep a Changelog](https://keepachangelog.co
- Fix content under source browse screen top appbar is interactable ([@AntsyLich](https://github.com/AntsyLich)) ([#2026](https://github.com/mihonapp/mihon/pull/2026))
- Fix crash when trying use source sort filter without a pre-selection ([@AntsyLich](https://github.com/AntsyLich)) ([#2036](https://github.com/mihonapp/mihon/pull/2036))
- Fix empty layout not appearing in browse source screen in some cases ([@NarwhalHorns](https://github.com/NarwhalHorns)) ([#2043](https://github.com/mihonapp/mihon/pull/2043))
- Fix Pill not following the local text style ([@AntsyLich](https://github.com/AntsyLich))
- Fix Pill not following the local text style ([@AntsyLich](https://github.com/AntsyLich)) ([`f8cb506`](https://github.com/mihonapp/mihon/commit/f8cb506))
- Fix downloader stopping after failing to create download directory of a manga ([@AntsyLich](https://github.com/AntsyLich)) ([#2068](https://github.com/mihonapp/mihon/pull/2068))
### Removed
- Remove Okhttp networking from WebView Screen ([@AwkwardPeak7](https://github.com/AwkwardPeak7)) ([#2020](https://github.com/mihonapp/mihon/pull/2020))

View file

@ -329,7 +329,10 @@ class DownloadManager(
var cleaned = 0
if (removeNonFavorite && !manga.favorite) {
val mangaFolder = provider.getMangaDir(/* SY --> */ manga.ogTitle /* SY <-- */, source)
val mangaFolder = provider.getMangaDir(/* SY --> */ manga.ogTitle /* SY <-- */, source).getOrElse { e ->
logcat(LogPriority.ERROR, e) { "Manga download folder doesn't exist." }
return 0
}
cleaned += 1 + mangaFolder.listFiles().orEmpty().size
mangaFolder.delete()
cache.removeManga(manga)
@ -350,7 +353,10 @@ class DownloadManager(
}
if (cache.getDownloadCount(manga) == 0) {
val mangaFolder = provider.getMangaDir(/* SY --> */ manga.ogTitle /* SY <-- */, source)
val mangaFolder = provider.getMangaDir(/* SY --> */ manga.ogTitle /* SY <-- */, source).getOrElse { e ->
logcat(LogPriority.ERROR, e) { "Manga download folder doesn't exist." }
return cleaned
}
if (!mangaFolder.listFiles().isNullOrEmpty()) {
mangaFolder.delete()
cache.removeManga(manga)
@ -419,7 +425,10 @@ class DownloadManager(
*/
suspend fun renameChapter(source: Source, manga: Manga, oldChapter: Chapter, newChapter: Chapter) {
val oldNames = provider.getValidChapterDirNames(oldChapter.name, oldChapter.scanlator)
val mangaDir = provider.getMangaDir(/* SY --> */ manga.ogTitle /* SY <-- */, source)
val mangaDir = provider.getMangaDir(/* SY --> */ manga.ogTitle /* SY <-- */, source).getOrElse { e ->
logcat(LogPriority.ERROR, e) { "Manga download folder doesn't exist. Skipping renaming after source sync" }
return
}
// Assume there's only 1 version of the chapter name formats present
val oldDownload = oldNames.asSequence()

View file

@ -14,6 +14,7 @@ import tachiyomi.domain.storage.service.StorageManager
import tachiyomi.i18n.MR
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.io.IOException
/**
* This class is used to provide the directories where the downloads should be saved.
@ -35,21 +36,36 @@ class DownloadProvider(
* @param mangaTitle the title of the manga to query.
* @param source the source of the manga.
*/
internal fun getMangaDir(mangaTitle: String, source: Source): UniFile {
try {
return downloadsDir!!
.createDirectory(getSourceDirName(source))!!
.createDirectory(getMangaDirName(mangaTitle))!!
} catch (e: Throwable) {
logcat(LogPriority.ERROR, e) { "Invalid download directory" }
throw Exception(
context.stringResource(
MR.strings.invalid_location,
(downloadsDir?.displayablePath ?: "") +
"/${getSourceDirName(source)}/${getMangaDirName(mangaTitle)}",
),
internal fun getMangaDir(mangaTitle: String, source: Source): Result<UniFile> {
val downloadsDir = downloadsDir
if (downloadsDir == null) {
logcat(LogPriority.ERROR) { "Failed to create download directory" }
return Result.failure(
IOException(context.stringResource(MR.strings.storage_failed_to_create_download_directory)),
)
}
val sourceDirName = getSourceDirName(source)
val sourceDir = downloadsDir.createDirectory(sourceDirName)
if (sourceDir == null) {
val displayablePath = downloadsDir.displayablePath + "/$sourceDirName"
logcat(LogPriority.ERROR) { "Failed to create source download directory: $displayablePath" }
return Result.failure(
IOException(context.stringResource(MR.strings.storage_failed_to_create_directory, displayablePath)),
)
}
val mangaDirName = getMangaDirName(mangaTitle)
val mangaDir = sourceDir.createDirectory(mangaDirName)
if (mangaDir == null) {
val displayablePath = sourceDir.displayablePath + "/$mangaDirName"
logcat(LogPriority.ERROR) { "Failed to create manga download directory: $displayablePath" }
return Result.failure(
IOException(context.stringResource(MR.strings.storage_failed_to_create_directory, displayablePath)),
)
}
return Result.success(mangaDir)
}
/**

View file

@ -327,13 +327,9 @@ class Downloader(
* @param download the chapter to be downloaded.
*/
private suspend fun downloadChapter(download: Download) {
val mangaDir: UniFile
try {
mangaDir = provider.getMangaDir(/* SY --> */ download.manga.ogTitle /* SY <-- */, download.source)
} catch (error: Exception) {
logcat(LogPriority.ERROR, error)
val mangaDir = provider.getMangaDir(/* SY --> */ download.manga.ogTitle /* SY <-- */, download.source).getOrElse { e ->
download.status = Download.State.ERROR
notifier.onError(error.message, download.chapter.name, download.manga.title, download.manga.id)
notifier.onError(e.message, download.chapter.name, download.manga.title, download.manga.id)
return
}

View file

@ -500,6 +500,8 @@
<string name="pref_remove_exclude_categories">Excluded categories</string>
<string name="no_location_set">No storage location set</string>
<string name="invalid_location">Invalid location: %s</string>
<string name="storage_failed_to_create_download_directory">Failed to create download directory</string>
<string name="storage_failed_to_create_directory">Failed to create directory: %s</string>
<string name="disabled">Disabled</string>
<string name="last_read_chapter">Last read chapter</string>
<string name="second_to_last">Second to last read chapter</string>