@@ -1,14 +1,11 @@
|
|||||||
package org.koitharu.kotatsu.core.backup
|
package org.koitharu.kotatsu.core.backup
|
||||||
|
|
||||||
import kotlinx.coroutines.CoroutineStart
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.coroutines.runInterruptible
|
import kotlinx.coroutines.runInterruptible
|
||||||
import okhttp3.internal.closeQuietly
|
import okhttp3.internal.closeQuietly
|
||||||
import okio.Closeable
|
import okio.Closeable
|
||||||
import org.json.JSONArray
|
import org.json.JSONArray
|
||||||
import org.koitharu.kotatsu.core.exceptions.BadBackupFormatException
|
import org.koitharu.kotatsu.core.exceptions.BadBackupFormatException
|
||||||
import org.koitharu.kotatsu.core.util.ext.processLifecycleScope
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.EnumSet
|
import java.util.EnumSet
|
||||||
import java.util.zip.ZipException
|
import java.util.zip.ZipException
|
||||||
@@ -36,13 +33,9 @@ class BackupZipInput private constructor(val file: File) : Closeable {
|
|||||||
zipFile.close()
|
zipFile.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun cleanupAsync() {
|
fun closeAndDelete() {
|
||||||
processLifecycleScope.launch(Dispatchers.IO, CoroutineStart.ATOMIC) {
|
closeQuietly()
|
||||||
runCatching {
|
file.delete()
|
||||||
closeQuietly()
|
|
||||||
file.delete()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@@ -55,7 +48,7 @@ class BackupZipInput private constructor(val file: File) : Closeable {
|
|||||||
throw BadBackupFormatException(null)
|
throw BadBackupFormatException(null)
|
||||||
}
|
}
|
||||||
res
|
res
|
||||||
} catch (exception: Exception) {
|
} catch (exception: Throwable) {
|
||||||
res?.closeQuietly()
|
res?.closeQuietly()
|
||||||
throw if (exception is ZipException) {
|
throw if (exception is ZipException) {
|
||||||
BadBackupFormatException(exception)
|
BadBackupFormatException(exception)
|
||||||
|
|||||||
@@ -39,14 +39,6 @@ fun ZipFile.readText(entry: ZipEntry) = getInputStream(entry).bufferedReader().u
|
|||||||
it.readText()
|
it.readText()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Blocking
|
|
||||||
fun ZipFile.getInputStreamOrClose(entry: ZipEntry): InputStream = try {
|
|
||||||
getInputStream(entry)
|
|
||||||
} catch (e: Throwable) {
|
|
||||||
closeQuietly()
|
|
||||||
throw e
|
|
||||||
}
|
|
||||||
|
|
||||||
fun File.getStorageName(context: Context): String = runCatching {
|
fun File.getStorageName(context: Context): String = runCatching {
|
||||||
val manager = context.getSystemService(Context.STORAGE_SERVICE) as StorageManager
|
val manager = context.getSystemService(Context.STORAGE_SERVICE) as StorageManager
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package org.koitharu.kotatsu.core.util.ext
|
|||||||
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import androidx.core.net.toFile
|
import androidx.core.net.toFile
|
||||||
|
import okhttp3.internal.closeQuietly
|
||||||
import okio.Source
|
import okio.Source
|
||||||
import okio.source
|
import okio.source
|
||||||
import okio.use
|
import okio.use
|
||||||
@@ -40,8 +41,13 @@ fun Uri.source(): Source = when (scheme) {
|
|||||||
URI_SCHEME_FILE -> toFile().source()
|
URI_SCHEME_FILE -> toFile().source()
|
||||||
URI_SCHEME_ZIP -> {
|
URI_SCHEME_ZIP -> {
|
||||||
val zip = ZipFile(schemeSpecificPart)
|
val zip = ZipFile(schemeSpecificPart)
|
||||||
val entry = zip.getEntry(fragment)
|
try {
|
||||||
zip.getInputStreamOrClose(entry).source().withExtraCloseable(zip)
|
val entry = zip.getEntry(fragment)
|
||||||
|
zip.getInputStream(entry).source().withExtraCloseable(zip)
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
zip.closeQuietly()
|
||||||
|
throw e
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> unsupportedUri(this)
|
else -> unsupportedUri(this)
|
||||||
|
|||||||
@@ -16,13 +16,13 @@ import dagger.hilt.android.qualifiers.ApplicationContext
|
|||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.runInterruptible
|
import kotlinx.coroutines.runInterruptible
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
|
import okhttp3.internal.closeQuietly
|
||||||
import okio.Path.Companion.toOkioPath
|
import okio.Path.Companion.toOkioPath
|
||||||
import okio.buffer
|
import okio.buffer
|
||||||
import okio.source
|
import okio.source
|
||||||
import org.koitharu.kotatsu.core.network.MangaHttpClient
|
import org.koitharu.kotatsu.core.network.MangaHttpClient
|
||||||
import org.koitharu.kotatsu.core.network.imageproxy.ImageProxyInterceptor
|
import org.koitharu.kotatsu.core.network.imageproxy.ImageProxyInterceptor
|
||||||
import org.koitharu.kotatsu.core.parser.MangaRepository
|
import org.koitharu.kotatsu.core.parser.MangaRepository
|
||||||
import org.koitharu.kotatsu.core.util.ext.getInputStreamOrClose
|
|
||||||
import org.koitharu.kotatsu.local.data.PagesCache
|
import org.koitharu.kotatsu.local.data.PagesCache
|
||||||
import org.koitharu.kotatsu.local.data.isFileUri
|
import org.koitharu.kotatsu.local.data.isFileUri
|
||||||
import org.koitharu.kotatsu.local.data.isZipUri
|
import org.koitharu.kotatsu.local.data.isZipUri
|
||||||
@@ -67,17 +67,22 @@ class MangaPageFetcher(
|
|||||||
return when {
|
return when {
|
||||||
uri.isZipUri() -> runInterruptible(Dispatchers.IO) {
|
uri.isZipUri() -> runInterruptible(Dispatchers.IO) {
|
||||||
val zip = ZipFile(uri.schemeSpecificPart)
|
val zip = ZipFile(uri.schemeSpecificPart)
|
||||||
val entry = zip.getEntry(uri.fragment)
|
try {
|
||||||
SourceResult(
|
val entry = zip.getEntry(uri.fragment)
|
||||||
source = ImageSource(
|
SourceResult(
|
||||||
source = zip.getInputStreamOrClose(entry).source().withExtraCloseable(zip).buffer(),
|
source = ImageSource(
|
||||||
context = context,
|
source = zip.getInputStream(entry).source().withExtraCloseable(zip).buffer(),
|
||||||
metadata = MangaPageMetadata(page),
|
context = context,
|
||||||
),
|
metadata = MangaPageMetadata(page),
|
||||||
mimeType = MimeTypeMap.getSingleton()
|
),
|
||||||
.getMimeTypeFromExtension(entry.name.substringAfterLast('.', "")),
|
mimeType = MimeTypeMap.getSingleton()
|
||||||
dataSource = DataSource.DISK,
|
.getMimeTypeFromExtension(entry.name.substringAfterLast('.', "")),
|
||||||
)
|
dataSource = DataSource.DISK,
|
||||||
|
)
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
zip.closeQuietly()
|
||||||
|
throw e
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uri.isFileUri() -> runInterruptible(Dispatchers.IO) {
|
uri.isFileUri() -> runInterruptible(Dispatchers.IO) {
|
||||||
|
|||||||
@@ -10,9 +10,9 @@ import coil.fetch.SourceResult
|
|||||||
import coil.request.Options
|
import coil.request.Options
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.runInterruptible
|
import kotlinx.coroutines.runInterruptible
|
||||||
|
import okhttp3.internal.closeQuietly
|
||||||
import okio.buffer
|
import okio.buffer
|
||||||
import okio.source
|
import okio.source
|
||||||
import org.koitharu.kotatsu.core.util.ext.getInputStreamOrClose
|
|
||||||
import org.koitharu.kotatsu.local.data.util.withExtraCloseable
|
import org.koitharu.kotatsu.local.data.util.withExtraCloseable
|
||||||
import java.util.zip.ZipFile
|
import java.util.zip.ZipFile
|
||||||
|
|
||||||
@@ -23,18 +23,23 @@ class CbzFetcher(
|
|||||||
|
|
||||||
override suspend fun fetch() = runInterruptible(Dispatchers.IO) {
|
override suspend fun fetch() = runInterruptible(Dispatchers.IO) {
|
||||||
val zip = ZipFile(uri.schemeSpecificPart)
|
val zip = ZipFile(uri.schemeSpecificPart)
|
||||||
val entry = zip.getEntry(uri.fragment)
|
try {
|
||||||
val ext = MimeTypeMap.getFileExtensionFromUrl(entry.name)
|
val entry = zip.getEntry(uri.fragment)
|
||||||
val bufferedSource = zip.getInputStreamOrClose(entry).source().withExtraCloseable(zip).buffer()
|
val ext = MimeTypeMap.getFileExtensionFromUrl(entry.name)
|
||||||
SourceResult(
|
val bufferedSource = zip.getInputStream(entry).source().withExtraCloseable(zip).buffer()
|
||||||
source = ImageSource(
|
SourceResult(
|
||||||
source = bufferedSource,
|
source = ImageSource(
|
||||||
context = options.context,
|
source = bufferedSource,
|
||||||
metadata = CbzMetadata(uri),
|
context = options.context,
|
||||||
),
|
metadata = CbzMetadata(uri),
|
||||||
mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(ext),
|
),
|
||||||
dataSource = DataSource.DISK,
|
mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(ext),
|
||||||
)
|
dataSource = DataSource.DISK,
|
||||||
|
)
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
zip.closeQuietly()
|
||||||
|
throw e
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Factory : Fetcher.Factory<Uri> {
|
class Factory : Fetcher.Factory<Uri> {
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import org.koitharu.kotatsu.core.backup.CompositeResult
|
|||||||
import org.koitharu.kotatsu.core.ui.BaseViewModel
|
import org.koitharu.kotatsu.core.ui.BaseViewModel
|
||||||
import org.koitharu.kotatsu.core.util.ext.MutableEventFlow
|
import org.koitharu.kotatsu.core.util.ext.MutableEventFlow
|
||||||
import org.koitharu.kotatsu.core.util.ext.call
|
import org.koitharu.kotatsu.core.util.ext.call
|
||||||
|
import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug
|
||||||
import org.koitharu.kotatsu.core.util.ext.toUriOrNull
|
import org.koitharu.kotatsu.core.util.ext.toUriOrNull
|
||||||
import org.koitharu.kotatsu.parsers.util.SuspendLazy
|
import org.koitharu.kotatsu.parsers.util.SuspendLazy
|
||||||
import java.io.File
|
import java.io.File
|
||||||
@@ -71,7 +72,11 @@ class RestoreViewModel @Inject constructor(
|
|||||||
|
|
||||||
override fun onCleared() {
|
override fun onCleared() {
|
||||||
super.onCleared()
|
super.onCleared()
|
||||||
backupInput.peek()?.cleanupAsync()
|
runCatching {
|
||||||
|
backupInput.peek()?.closeAndDelete()
|
||||||
|
}.onFailure {
|
||||||
|
it.printStackTraceDebug()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onItemClick(item: BackupEntryModel) {
|
fun onItemClick(item: BackupEntryModel) {
|
||||||
|
|||||||
Reference in New Issue
Block a user