From 54ef02ad8855c037b07dec85fce526abe532be8d Mon Sep 17 00:00:00 2001 From: Koitharu Date: Thu, 1 Feb 2024 10:12:20 +0200 Subject: [PATCH] Fix downloading --- .../details/ui/adapter/ChapterListItemAD.kt | 1 + .../download/ui/worker/DownloadWorker.kt | 31 ++++++++++++------- .../koitharu/kotatsu/local/data/MangaIndex.kt | 22 ++++++------- .../local/data/input/LocalMangaZipInput.kt | 7 +++-- .../local/data/output/LocalMangaDirOutput.kt | 17 +++++----- .../local/data/output/LocalMangaOutput.kt | 2 +- .../local/data/output/LocalMangaZipOutput.kt | 6 ++-- 7 files changed, 47 insertions(+), 39 deletions(-) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/adapter/ChapterListItemAD.kt b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/adapter/ChapterListItemAD.kt index feba337f5..0d45e4585 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/adapter/ChapterListItemAD.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/adapter/ChapterListItemAD.kt @@ -5,6 +5,7 @@ import androidx.core.content.ContextCompat import androidx.core.view.isVisible import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.core.model.formatNumber import org.koitharu.kotatsu.core.ui.list.AdapterDelegateClickListenerAdapter import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener import org.koitharu.kotatsu.core.util.ext.drawableStart diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/worker/DownloadWorker.kt b/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/worker/DownloadWorker.kt index 64b5ef7de..93573ab75 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/worker/DownloadWorker.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/worker/DownloadWorker.kt @@ -193,12 +193,12 @@ class DownloadWorker @AssistedInject constructor( val chapters = getChapters(mangaDetails, includedIds) for ((chapterIndex, chapter) in chapters.withIndex()) { checkIsPaused() - if (chaptersToSkip.remove(chapter.id)) { + if (chaptersToSkip.remove(chapter.value.id)) { publishState(currentState.copy(downloadedChapters = currentState.downloadedChapters + 1)) continue } val pages = runFailsafe { - repo.getPages(chapter) + repo.getPages(chapter.value) } ?: continue val pageCounter = AtomicInteger(0) channelFlow { @@ -237,7 +237,7 @@ class DownloadWorker @AssistedInject constructor( ), ) } - if (output.flushChapter(chapter)) { + if (output.flushChapter(chapter.value)) { runCatchingCancellable { localStorageChanges.emit(LocalMangaInput.of(output.rootFile).getManga()) }.onFailure(Throwable::printStackTraceDebug) @@ -377,19 +377,26 @@ class DownloadWorker @AssistedInject constructor( private fun getChapters( manga: Manga, includedIds: LongArray?, - ): List { - val chapters = checkNotNull(manga.chapters) { - "Chapters list must not be null" - }.toMutableList() - if (includedIds != null) { - val chaptersIdsSet = includedIds.toMutableSet() - chapters.retainAll { x -> chaptersIdsSet.remove(x.id) } + ): List> { + val chapters = checkNotNull(manga.chapters) { "Chapters list must not be null" } + val chaptersIdsSet = includedIds?.toMutableSet() + val result = ArrayList>((chaptersIdsSet ?: chapters).size) + val counters = HashMap() + for (chapter in chapters) { + val index = counters[chapter.branch] ?: 0 + counters[chapter.branch] = index + 1 + if (chaptersIdsSet != null && !chaptersIdsSet.remove(chapter.id)) { + continue + } + result.add(IndexedValue(index, chapter)) + } + if (chaptersIdsSet != null) { check(chaptersIdsSet.isEmpty()) { "${chaptersIdsSet.size} of ${includedIds.size} requested chapters not found in manga" } } - check(chapters.isNotEmpty()) { "Chapters list must not be empty" } - return chapters + check(result.isNotEmpty()) { "Chapters list must not be empty" } + return result } private suspend inline fun withMangaLock(manga: Manga, block: () -> T) = try { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/local/data/MangaIndex.kt b/app/src/main/kotlin/org/koitharu/kotatsu/local/data/MangaIndex.kt index 5bb6be433..712e9ff37 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/local/data/MangaIndex.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/local/data/MangaIndex.kt @@ -88,20 +88,20 @@ class MangaIndex(source: String?) { fun getCoverEntry(): String? = json.getStringOrNull("cover_entry") - fun addChapter(chapter: MangaChapter, filename: String?) { + fun addChapter(chapter: IndexedValue, filename: String?) { val chapters = json.getJSONObject("chapters") - if (!chapters.has(chapter.id.toString())) { + if (!chapters.has(chapter.value.id.toString())) { val jo = JSONObject() - jo.put("number", chapter.number) - jo.put("volume", chapter.volume) - jo.put("url", chapter.url) - jo.put("name", chapter.name) - jo.put("uploadDate", chapter.uploadDate) - jo.put("scanlator", chapter.scanlator) - jo.put("branch", chapter.branch) - jo.put("entries", "%08d_%03d\\d{3}".format(chapter.branch.hashCode(), chapter.number)) + jo.put("number", chapter.value.number) + jo.put("volume", chapter.value.volume) + jo.put("url", chapter.value.url) + jo.put("name", chapter.value.name) + jo.put("uploadDate", chapter.value.uploadDate) + jo.put("scanlator", chapter.value.scanlator) + jo.put("branch", chapter.value.branch) + jo.put("entries", "%08d_%03d\\d{3}".format(chapter.value.branch.hashCode(), chapter.index + 1)) jo.put("file", filename) - chapters.put(chapter.id.toString(), jo) + chapters.put(chapter.value.id.toString(), jo) } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/local/data/input/LocalMangaZipInput.kt b/app/src/main/kotlin/org/koitharu/kotatsu/local/data/input/LocalMangaZipInput.kt index 02b4bcf8a..d2aa8f71d 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/local/data/input/LocalMangaZipInput.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/local/data/input/LocalMangaZipInput.kt @@ -7,6 +7,7 @@ import androidx.core.net.toFile import androidx.core.net.toUri import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runInterruptible +import org.koitharu.kotatsu.core.util.AlphanumComparator import org.koitharu.kotatsu.core.util.ext.longHashCode import org.koitharu.kotatsu.core.util.ext.readText import org.koitharu.kotatsu.core.util.ext.toListSorted @@ -71,7 +72,7 @@ class LocalMangaZipInput(root: File) : LocalMangaInput(root) { publicUrl = fileUri, source = MangaSource.LOCAL, coverUrl = zipUri(root, findFirstImageEntry(zip.entries())?.name.orEmpty()), - chapters = chapters.sortedWith(org.koitharu.kotatsu.core.util.AlphanumComparator()) + chapters = chapters.sortedWith(AlphanumComparator()) .mapIndexed { i, s -> MangaChapter( id = "$i$s".longHashCode(), @@ -127,7 +128,7 @@ class LocalMangaZipInput(root: File) : LocalMangaInput(root) { } } entries - .toListSorted(compareBy(org.koitharu.kotatsu.core.util.AlphanumComparator()) { x -> x.name }) + .toListSorted(compareBy(AlphanumComparator()) { x -> x.name }) .map { x -> val entryUri = zipUri(file, x.name) MangaPage( @@ -143,7 +144,7 @@ class LocalMangaZipInput(root: File) : LocalMangaInput(root) { private fun findFirstImageEntry(entries: Enumeration): ZipEntry? { val list = entries.toList() .filterNot { it.isDirectory } - .sortedWith(compareBy(org.koitharu.kotatsu.core.util.AlphanumComparator()) { x -> x.name }) + .sortedWith(compareBy(AlphanumComparator()) { x -> x.name }) val map = MimeTypeMap.getSingleton() return list.firstOrNull { map.getMimeTypeFromExtension(it.name.substringAfterLast('.')) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/local/data/output/LocalMangaDirOutput.kt b/app/src/main/kotlin/org/koitharu/kotatsu/local/data/output/LocalMangaDirOutput.kt index 9dd76c100..4310ba7ed 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/local/data/output/LocalMangaDirOutput.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/local/data/output/LocalMangaDirOutput.kt @@ -4,7 +4,6 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runInterruptible import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock -import org.koitharu.kotatsu.core.model.findById import org.koitharu.kotatsu.core.model.isLocal import org.koitharu.kotatsu.core.util.ext.deleteAwait import org.koitharu.kotatsu.core.util.ext.takeIfReadable @@ -47,12 +46,12 @@ class LocalMangaDirOutput( flushIndex() } - override suspend fun addPage(chapter: MangaChapter, file: File, pageNumber: Int, ext: String) = mutex.withLock { - val output = chaptersOutput.getOrPut(chapter) { + override suspend fun addPage(chapter: IndexedValue, file: File, pageNumber: Int, ext: String) = mutex.withLock { + val output = chaptersOutput.getOrPut(chapter.value) { ZipOutput(File(rootFile, chapterFileName(chapter) + SUFFIX_TMP)) } val name = buildString { - append(FILENAME_PATTERN.format(chapter.branch.hashCode(), chapter.number, pageNumber)) + append(FILENAME_PATTERN.format(chapter.value.branch.hashCode(), chapter.index + 1, pageNumber)) if (ext.isNotEmpty() && ext.length <= 4) { append('.') append(ext) @@ -92,9 +91,9 @@ class LocalMangaDirOutput( } suspend fun deleteChapter(chapterId: Long) = mutex.withLock { - val chapter = checkNotNull(index.getMangaInfo()?.chapters) { + val chapter = checkNotNull(index.getMangaInfo()?.chapters?.withIndex()) { "No chapters found" - }.findById(chapterId) ?: error("Chapter not found") + }.find { x -> x.value.id == chapterId } ?: error("Chapter not found") val chapterDir = File(rootFile, chapterFileName(chapter)) chapterDir.deleteAwait() index.removeChapter(chapterId) @@ -111,11 +110,11 @@ class LocalMangaDirOutput( file.renameTo(resFile) } - private fun chapterFileName(chapter: MangaChapter): String { - index.getChapterFileName(chapter.id)?.let { + private fun chapterFileName(chapter: IndexedValue): String { + index.getChapterFileName(chapter.value.id)?.let { return it } - val baseName = "${chapter.number}_${chapter.name.toFileNameSafe()}".take(18) + val baseName = "${chapter.index}_${chapter.value.name.toFileNameSafe()}".take(18) var i = 0 while (true) { val name = (if (i == 0) baseName else baseName + "_$i") + ".cbz" diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/local/data/output/LocalMangaOutput.kt b/app/src/main/kotlin/org/koitharu/kotatsu/local/data/output/LocalMangaOutput.kt index ded98e4f7..1f6cacc8c 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/local/data/output/LocalMangaOutput.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/local/data/output/LocalMangaOutput.kt @@ -21,7 +21,7 @@ sealed class LocalMangaOutput( abstract suspend fun addCover(file: File, ext: String) - abstract suspend fun addPage(chapter: MangaChapter, file: File, pageNumber: Int, ext: String) + abstract suspend fun addPage(chapter: IndexedValue, file: File, pageNumber: Int, ext: String) abstract suspend fun flushChapter(chapter: MangaChapter): Boolean diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/local/data/output/LocalMangaZipOutput.kt b/app/src/main/kotlin/org/koitharu/kotatsu/local/data/output/LocalMangaZipOutput.kt index 747296ceb..d4370c3f6 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/local/data/output/LocalMangaZipOutput.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/local/data/output/LocalMangaZipOutput.kt @@ -52,9 +52,9 @@ class LocalMangaZipOutput( index.setCoverEntry(name) } - override suspend fun addPage(chapter: MangaChapter, file: File, pageNumber: Int, ext: String) = mutex.withLock { + override suspend fun addPage(chapter: IndexedValue, file: File, pageNumber: Int, ext: String) = mutex.withLock { val name = buildString { - append(FILENAME_PATTERN.format(chapter.branch.hashCode(), chapter.number, pageNumber)) + append(FILENAME_PATTERN.format(chapter.value.branch.hashCode(), chapter.index + 1, pageNumber)) if (ext.isNotEmpty() && ext.length <= 4) { append('.') append(ext) @@ -104,7 +104,7 @@ class LocalMangaZipOutput( } } } - otherIndex?.getMangaInfo()?.chapters?.let { chapters -> + otherIndex?.getMangaInfo()?.chapters?.withIndex()?.let { chapters -> for (chapter in chapters) { index.addChapter(chapter, null) }