Fix local chapters deletion

This commit is contained in:
Koitharu
2024-03-12 15:02:39 +02:00
parent 16027e3295
commit 7e581a5ed7
6 changed files with 49 additions and 30 deletions

View File

@@ -10,7 +10,6 @@ import android.provider.OpenableColumns
import androidx.core.database.getStringOrNull
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runInterruptible
import kotlinx.coroutines.withContext
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.fs.FileSequence
import java.io.File
@@ -53,7 +52,7 @@ fun File.getStorageName(context: Context): String = runCatching {
fun Uri.toFileOrNull() = if (scheme == URI_SCHEME_FILE) path?.let(::File) else null
suspend fun File.deleteAwait() = withContext(Dispatchers.IO) {
suspend fun File.deleteAwait() = runInterruptible(Dispatchers.IO) {
delete() || deleteRecursively()
}

View File

@@ -18,6 +18,7 @@ import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.plus
@@ -144,6 +145,7 @@ class DetailsViewModel @Inject constructor(
val localSize = details
.map { it?.local }
.distinctUntilChanged()
.combine(localStorageChanges.onStart { emit(null) }) { x, _ -> x }
.map { local ->
if (local != null) {
runCatchingCancellable {

View File

@@ -106,7 +106,7 @@ class MangaIndex(source: String?) {
}
fun removeChapter(id: Long): Boolean {
return json.getJSONObject("chapters").remove(id.toString()) != null
return json.has("chapters") && json.getJSONObject("chapters").remove(id.toString()) != null
}
fun getChapterFileName(chapterId: Long): String? {

View File

@@ -1,5 +1,7 @@
package org.koitharu.kotatsu.local.data.output
import androidx.core.net.toFile
import androidx.core.net.toUri
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runInterruptible
import kotlinx.coroutines.sync.Mutex
@@ -9,6 +11,7 @@ import org.koitharu.kotatsu.core.util.ext.deleteAwait
import org.koitharu.kotatsu.core.util.ext.takeIfReadable
import org.koitharu.kotatsu.core.zip.ZipOutput
import org.koitharu.kotatsu.local.data.MangaIndex
import org.koitharu.kotatsu.local.data.input.LocalMangaDirInput
import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.parsers.model.MangaChapter
import org.koitharu.kotatsu.parsers.util.toFileNameSafe
@@ -46,22 +49,23 @@ class LocalMangaDirOutput(
flushIndex()
}
override suspend fun addPage(chapter: IndexedValue<MangaChapter>, 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.value.branch.hashCode(), chapter.index + 1, pageNumber))
if (ext.isNotEmpty() && ext.length <= 4) {
append('.')
append(ext)
override suspend fun addPage(chapter: IndexedValue<MangaChapter>, 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.value.branch.hashCode(), chapter.index + 1, pageNumber))
if (ext.isNotEmpty() && ext.length <= 4) {
append('.')
append(ext)
}
}
runInterruptible(Dispatchers.IO) {
output.put(name, file)
}
index.addChapter(chapter, chapterFileName(chapter))
}
runInterruptible(Dispatchers.IO) {
output.put(name, file)
}
index.addChapter(chapter, chapterFileName(chapter))
}
override suspend fun flushChapter(chapter: MangaChapter): Boolean = mutex.withLock {
val output = chaptersOutput.remove(chapter) ?: return@withLock false
@@ -90,13 +94,24 @@ class LocalMangaDirOutput(
}
}
suspend fun deleteChapter(chapterId: Long) = mutex.withLock {
val chapter = checkNotNull(index.getMangaInfo()?.chapters?.withIndex()) {
suspend fun deleteChapters(ids: Set<Long>) = mutex.withLock {
val chapters = checkNotNull((index.getMangaInfo() ?: LocalMangaDirInput(rootFile).getManga().manga).chapters) {
"No chapters found"
}.find { x -> x.value.id == chapterId } ?: error("Chapter not found")
val chapterDir = File(rootFile, chapterFileName(chapter))
chapterDir.deleteAwait()
index.removeChapter(chapterId)
}.withIndex()
val victimsIds = ids.toMutableSet()
for (chapter in chapters) {
if (!victimsIds.remove(chapter.value.id)) {
continue
}
val chapterFile = index.getChapterFileName(chapter.value.id)?.let {
File(rootFile, it)
} ?: chapter.value.url.toUri().toFile()
chapterFile.deleteAwait()
index.removeChapter(chapter.value.id)
}
check(victimsIds.isEmpty()) {
"${victimsIds.size} of ${ids.size} chapters was not removed: not found"
}
}
fun setIndex(newIndex: MangaIndex) {

View File

@@ -25,9 +25,7 @@ class LocalMangaUtil(
}
is LocalMangaDirOutput -> {
for (id in ids) {
output.deleteChapter(id)
}
output.deleteChapters(ids)
output.finish()
}
}

View File

@@ -11,6 +11,7 @@ import androidx.core.content.ContextCompat
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.MutableSharedFlow
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.ErrorReporterReceiver
import org.koitharu.kotatsu.core.model.parcelable.ParcelableManga
import org.koitharu.kotatsu.core.ui.CoroutineIntentService
import org.koitharu.kotatsu.core.util.ext.getDisplayMessage
@@ -45,10 +46,13 @@ class LocalChaptersRemoveService : CoroutineIntentService() {
val manga = intent.getParcelableExtraCompat<ParcelableManga>(EXTRA_MANGA)?.manga ?: return
val chaptersIds = intent.getLongArrayExtra(EXTRA_CHAPTERS_IDS)?.toSet() ?: return
startForeground()
val mangaWithChapters = localMangaRepository.getDetails(manga)
localMangaRepository.deleteChapters(mangaWithChapters, chaptersIds)
localStorageChanges.emit(LocalManga(localMangaRepository.getDetails(manga)))
ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_REMOVE)
try {
val mangaWithChapters = localMangaRepository.getDetails(manga)
localMangaRepository.deleteChapters(mangaWithChapters, chaptersIds)
localStorageChanges.emit(LocalManga(localMangaRepository.getDetails(manga)))
} finally {
ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_REMOVE)
}
}
override fun onError(startId: Int, error: Throwable) {
@@ -60,6 +64,7 @@ class LocalChaptersRemoveService : CoroutineIntentService() {
.setContentText(error.getDisplayMessage(resources))
.setSmallIcon(android.R.drawable.stat_notify_error)
.setAutoCancel(true)
.setContentIntent(ErrorReporterReceiver.getPendingIntent(this, error))
.build()
val nm = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
nm.notify(NOTIFICATION_ID + startId, notification)