Fix downloading edited manga (close #1493)

This commit is contained in:
Koitharu
2025-07-13 09:38:35 +03:00
parent d6350afe3a
commit de1a297338
3 changed files with 55 additions and 19 deletions

View File

@@ -1,5 +1,8 @@
package org.koitharu.kotatsu.core.util.ext
import android.content.ContentResolver
import android.net.Uri
import androidx.annotation.CheckResult
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.currentCoroutineContext
@@ -12,6 +15,7 @@ import okio.FileSystem
import okio.IOException
import okio.Path
import okio.Source
import okio.source
import org.koitharu.kotatsu.core.util.CancellableSource
import org.koitharu.kotatsu.core.util.progress.ProgressResponseBody
import java.io.ByteArrayOutputStream
@@ -57,3 +61,8 @@ fun FileSystem.isRegularFile(path: Path) = try {
} catch (_: IOException) {
false
}
@CheckResult
fun ContentResolver.openSource(uri: Uri): Source = checkNotNull(openInputStream(uri)) {
"Cannot open input stream from $uri"
}.source()

View File

@@ -6,6 +6,7 @@ import android.content.Context
import android.content.pm.ServiceInfo
import android.os.Build
import androidx.core.content.ContextCompat
import androidx.core.net.toUri
import androidx.hilt.work.HiltWorker
import androidx.work.BackoffPolicy
import androidx.work.Constraints
@@ -64,8 +65,11 @@ import org.koitharu.kotatsu.core.util.ext.ensureSuccess
import org.koitharu.kotatsu.core.util.ext.getDisplayMessage
import org.koitharu.kotatsu.core.util.ext.getWorkInputData
import org.koitharu.kotatsu.core.util.ext.getWorkSpec
import org.koitharu.kotatsu.core.util.ext.openSource
import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug
import org.koitharu.kotatsu.core.util.ext.toFileOrNull
import org.koitharu.kotatsu.core.util.ext.toMimeType
import org.koitharu.kotatsu.core.util.ext.toMimeTypeOrNull
import org.koitharu.kotatsu.core.util.ext.withTicker
import org.koitharu.kotatsu.core.util.ext.writeAllCancellable
import org.koitharu.kotatsu.core.util.progress.RealtimeEtaEstimator
@@ -371,6 +375,25 @@ class DownloadWorker @AssistedInject constructor(
destination: File,
source: MangaSource,
): File {
if (url.startsWith("content:", ignoreCase = true) || url.startsWith("file:", ignoreCase = true)) {
val uri = url.toUri()
val cr = applicationContext.contentResolver
val ext = uri.toFileOrNull()?.let {
MimeTypes.getNormalizedExtension(it.name)
} ?: cr.getType(uri)?.toMimeTypeOrNull()?.let { MimeTypes.getExtension(it) }
val file = destination.createTempFile(ext)
try {
cr.openSource(uri).use { input ->
file.sink(append = false).buffer().use {
it.writeAllCancellable(input)
}
}
} catch (e: Exception) {
file.delete()
throw e
}
return file
}
val request = PageLoader.createPageRequest(url, source)
slowdownDispatcher.delay(source)
return imageProxyInterceptor.interceptPageRequest(request, okHttp)
@@ -379,22 +402,14 @@ class DownloadWorker @AssistedInject constructor(
var file: File? = null
try {
response.requireBody().use { body ->
file = File(
destination,
buildString {
append(UUID.randomUUID().toString())
MimeTypes.getExtension(body.contentType()?.toMimeType())?.let { ext ->
append('.')
append(ext)
}
append(".tmp")
},
file = destination.createTempFile(
ext = MimeTypes.getExtension(body.contentType()?.toMimeType())
)
file.sink(append = false).buffer().use {
it.writeAllCancellable(body.source())
}
}
} catch (e: CancellationException) {
} catch (e: Exception) {
file?.delete()
throw e
}
@@ -402,6 +417,18 @@ class DownloadWorker @AssistedInject constructor(
}
}
private fun File.createTempFile(ext: String?) = File(
this,
buildString {
append(UUID.randomUUID().toString())
if (!ext.isNullOrEmpty()) {
append('.')
append(ext)
}
append(".tmp")
},
)
private suspend fun publishState(state: DownloadState) {
val previousState = currentState
lastPublishedState = state

View File

@@ -11,8 +11,8 @@ import kotlinx.coroutines.runInterruptible
import kotlinx.coroutines.withContext
import okio.buffer
import okio.sink
import okio.source
import org.koitharu.kotatsu.core.exceptions.UnsupportedFileException
import org.koitharu.kotatsu.core.util.ext.openSource
import org.koitharu.kotatsu.core.util.ext.resolveName
import org.koitharu.kotatsu.core.util.ext.writeAllCancellable
import org.koitharu.kotatsu.local.data.LocalStorageChanges
@@ -51,12 +51,12 @@ class SingleMangaImporter @Inject constructor(
}
val dest = File(getOutputDir(), name)
runInterruptible {
contentResolver.openInputStream(uri)
}?.use { source ->
contentResolver.openSource(uri)
}.use { source ->
dest.sink().buffer().use { output ->
output.writeAllCancellable(source.source())
output.writeAllCancellable(source)
}
} ?: throw IOException("Cannot open input stream: $uri")
}
LocalMangaParser(dest).getManga(withDetails = false)
}
@@ -80,7 +80,7 @@ class SingleMangaImporter @Inject constructor(
docFile.copyTo(subDir)
}
} else {
inputStream().source().use { input ->
source().use { input ->
File(destDir, requireName()).sink().buffer().use { output ->
output.writeAllCancellable(input)
}
@@ -92,8 +92,8 @@ class SingleMangaImporter @Inject constructor(
return storageManager.getDefaultWriteableDir() ?: throw IOException("External files dir unavailable")
}
private suspend fun DocumentFile.inputStream() = runInterruptible(Dispatchers.IO) {
contentResolver.openInputStream(uri) ?: throw IOException("Cannot open input stream: $uri")
private suspend fun DocumentFile.source() = runInterruptible(Dispatchers.IO) {
contentResolver.openSource(uri)
}
private fun DocumentFile.requireName(): String {