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

(cherry picked from commit 536393a6d9941fac282f10b825aa611b91e1fcdb)

# Conflicts:
#	CHANGELOG.md
#	app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadManager.kt
#	app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt
This commit is contained in:
AntsyLich 2025-05-05 01:02:08 +06:00 committed by Jobobby04
parent 78f6a34339
commit b6e5943e15
4 changed files with 49 additions and 20 deletions

View File

@ -316,11 +316,14 @@ class DownloadManager(
if (removeNonFavorite && !manga.favorite) {
val mangaFolder = provider.getMangaDir(/* SY --> */ manga.ogTitle /* SY <-- */, source)
.getOrNull()
if (mangaFolder != null) {
cleaned += 1 + mangaFolder.listFiles().orEmpty().size
mangaFolder.delete()
cache.removeManga(manga)
return cleaned
}
}
val filesWithNoChapter = provider.findUnmatchedChapterDirs(allChapters, manga, source)
cleaned += filesWithNoChapter.size
@ -336,8 +339,8 @@ class DownloadManager(
}
if (cache.getDownloadCount(manga) == 0) {
val mangaFolder = provider.getMangaDir(/* SY --> */ manga.ogTitle /* SY <-- */, source)
if (!mangaFolder.listFiles().isNullOrEmpty()) {
val mangaFolder = provider.getMangaDir(/* SY --> */ manga.ogTitle /* SY <-- */, source).getOrNull()
if (mangaFolder != null && !mangaFolder.listFiles().isNullOrEmpty()) {
mangaFolder.delete()
cache.removeManga(manga)
} else {
@ -405,7 +408,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,20 +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 ?: "",
),
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,7 +327,11 @@ class Downloader(
* @param download the chapter to be downloaded.
*/
private suspend fun downloadChapter(download: Download) {
val mangaDir = provider.getMangaDir(/* SY --> */ download.manga.ogTitle /* SY <-- */, download.source)
val mangaDir = provider.getMangaDir(/* SY --> */ download.manga.ogTitle /* SY <-- */, download.source).getOrElse { e ->
download.status = Download.State.ERROR
notifier.onError(e.message, download.chapter.name, download.manga.title, download.manga.id)
return
}
val availSpace = DiskUtil.getAvailableStorageSpace(mangaDir)
if (availSpace != -1L && availSpace < MIN_DISK_SPACE) {

View File

@ -498,6 +498,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>