Remove storage permissions
Requires adjusting some file reading to first copy to a temporary file in cache that we have permissions to read from. This is only applicable for things like ZIP files where we need an actual File rather than just some Android content URI shenanigans. (cherry picked from commit 4fcdde4913df28bbd678ae1be4a2971ed77179d3) # Conflicts: # app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/RarPageLoader.kt # app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ZipPageLoader.kt # source-local/src/androidMain/kotlin/tachiyomi/source/local/LocalSource.kt
This commit is contained in:
parent
7e6d1196ac
commit
ab63f6036c
@ -165,7 +165,6 @@ dependencies {
|
|||||||
implementation(compose.ui.tooling.preview)
|
implementation(compose.ui.tooling.preview)
|
||||||
implementation(compose.ui.util)
|
implementation(compose.ui.util)
|
||||||
implementation(compose.accompanist.webview)
|
implementation(compose.accompanist.webview)
|
||||||
implementation(compose.accompanist.permissions)
|
|
||||||
implementation(compose.accompanist.systemuicontroller)
|
implementation(compose.accompanist.systemuicontroller)
|
||||||
lintChecks(compose.lintchecks)
|
lintChecks(compose.lintchecks)
|
||||||
|
|
||||||
|
@ -7,9 +7,6 @@
|
|||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||||
|
|
||||||
<!-- Storage -->
|
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
|
||||||
|
|
||||||
<!-- For background jobs -->
|
<!-- For background jobs -->
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||||
@ -40,7 +37,6 @@
|
|||||||
android:largeHeap="true"
|
android:largeHeap="true"
|
||||||
android:localeConfig="@xml/locales_config"
|
android:localeConfig="@xml/locales_config"
|
||||||
android:networkSecurityConfig="@xml/network_security_config"
|
android:networkSecurityConfig="@xml/network_security_config"
|
||||||
android:requestLegacyExternalStorage="true"
|
|
||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/Theme.Tachiyomi">
|
android:theme="@style/Theme.Tachiyomi">
|
||||||
|
@ -35,7 +35,6 @@ import eu.kanade.presentation.more.settings.Preference
|
|||||||
import eu.kanade.presentation.more.settings.screen.data.CreateBackupScreen
|
import eu.kanade.presentation.more.settings.screen.data.CreateBackupScreen
|
||||||
import eu.kanade.presentation.more.settings.widget.BasePreferenceWidget
|
import eu.kanade.presentation.more.settings.widget.BasePreferenceWidget
|
||||||
import eu.kanade.presentation.more.settings.widget.PrefsHorizontalPadding
|
import eu.kanade.presentation.more.settings.widget.PrefsHorizontalPadding
|
||||||
import eu.kanade.presentation.permissions.PermissionRequestHelper
|
|
||||||
import eu.kanade.presentation.util.relativeTimeSpanString
|
import eu.kanade.presentation.util.relativeTimeSpanString
|
||||||
import eu.kanade.tachiyomi.data.backup.BackupCreateJob
|
import eu.kanade.tachiyomi.data.backup.BackupCreateJob
|
||||||
import eu.kanade.tachiyomi.data.backup.BackupFileValidator
|
import eu.kanade.tachiyomi.data.backup.BackupFileValidator
|
||||||
@ -73,8 +72,6 @@ object SettingsDataScreen : SearchableSettings {
|
|||||||
val backupPreferences = Injekt.get<BackupPreferences>()
|
val backupPreferences = Injekt.get<BackupPreferences>()
|
||||||
val storagePreferences = Injekt.get<StoragePreferences>()
|
val storagePreferences = Injekt.get<StoragePreferences>()
|
||||||
|
|
||||||
PermissionRequestHelper.requestStoragePermission()
|
|
||||||
|
|
||||||
return listOf(
|
return listOf(
|
||||||
getStorageLocationPref(storagePreferences = storagePreferences),
|
getStorageLocationPref(storagePreferences = storagePreferences),
|
||||||
Preference.PreferenceItem.InfoPreference(stringResource(MR.strings.pref_storage_location_info)),
|
Preference.PreferenceItem.InfoPreference(stringResource(MR.strings.pref_storage_location_info)),
|
||||||
|
@ -1,20 +0,0 @@
|
|||||||
package eu.kanade.presentation.permissions
|
|
||||||
|
|
||||||
import android.Manifest
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
|
||||||
import com.google.accompanist.permissions.rememberPermissionState
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Launches request for [Manifest.permission.WRITE_EXTERNAL_STORAGE] permission
|
|
||||||
*/
|
|
||||||
object PermissionRequestHelper {
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun requestStoragePermission() {
|
|
||||||
val permissionState = rememberPermissionState(permission = Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
|
||||||
LaunchedEffect(Unit) {
|
|
||||||
permissionState.launchPermissionRequest()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -27,7 +27,6 @@ import com.elvishew.xlog.XLog
|
|||||||
import com.elvishew.xlog.printer.AndroidPrinter
|
import com.elvishew.xlog.printer.AndroidPrinter
|
||||||
import com.elvishew.xlog.printer.Printer
|
import com.elvishew.xlog.printer.Printer
|
||||||
import com.elvishew.xlog.printer.file.backup.NeverBackupStrategy
|
import com.elvishew.xlog.printer.file.backup.NeverBackupStrategy
|
||||||
import com.elvishew.xlog.printer.file.clean.FileLastModifiedCleanStrategy
|
|
||||||
import com.elvishew.xlog.printer.file.naming.DateFileNameGenerator
|
import com.elvishew.xlog.printer.file.naming.DateFileNameGenerator
|
||||||
import eu.kanade.domain.DomainModule
|
import eu.kanade.domain.DomainModule
|
||||||
import eu.kanade.domain.SYDomainModule
|
import eu.kanade.domain.SYDomainModule
|
||||||
@ -67,7 +66,6 @@ import logcat.LogPriority
|
|||||||
import logcat.LogcatLogger
|
import logcat.LogcatLogger
|
||||||
import org.conscrypt.Conscrypt
|
import org.conscrypt.Conscrypt
|
||||||
import tachiyomi.core.i18n.stringResource
|
import tachiyomi.core.i18n.stringResource
|
||||||
import tachiyomi.core.storage.toFile
|
|
||||||
import tachiyomi.core.util.system.logcat
|
import tachiyomi.core.util.system.logcat
|
||||||
import tachiyomi.domain.storage.service.StorageManager
|
import tachiyomi.domain.storage.service.StorageManager
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
@ -78,7 +76,6 @@ import uy.kohesive.injekt.injectLazy
|
|||||||
import java.security.Security
|
import java.security.Security
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import kotlin.time.Duration.Companion.days
|
|
||||||
|
|
||||||
class App : Application(), DefaultLifecycleObserver, ImageLoaderFactory {
|
class App : Application(), DefaultLifecycleObserver, ImageLoaderFactory {
|
||||||
|
|
||||||
@ -250,8 +247,6 @@ class App : Application(), DefaultLifecycleObserver, ImageLoaderFactory {
|
|||||||
val printers = mutableListOf<Printer>(AndroidPrinter())
|
val printers = mutableListOf<Printer>(AndroidPrinter())
|
||||||
|
|
||||||
val logFolder = Injekt.get<StorageManager>().getLogsDirectory()
|
val logFolder = Injekt.get<StorageManager>().getLogsDirectory()
|
||||||
?.toFile()
|
|
||||||
?.absolutePath
|
|
||||||
|
|
||||||
if (logFolder != null) {
|
if (logFolder != null) {
|
||||||
val dateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.getDefault())
|
val dateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.getDefault())
|
||||||
@ -269,7 +264,6 @@ class App : Application(), DefaultLifecycleObserver, ImageLoaderFactory {
|
|||||||
flattener { timeMillis, level, tag, message ->
|
flattener { timeMillis, level, tag, message ->
|
||||||
"${dateFormat.format(timeMillis)} ${LogLevel.getShortLevelName(level)}/$tag: $message"
|
"${dateFormat.format(timeMillis)} ${LogLevel.getShortLevelName(level)}/$tag: $message"
|
||||||
}
|
}
|
||||||
cleanStrategy = FileLastModifiedCleanStrategy(7.days.inWholeMilliseconds)
|
|
||||||
backupStrategy = NeverBackupStrategy()
|
backupStrategy = NeverBackupStrategy()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,6 @@ import cafe.adriel.voyager.navigator.tab.TabOptions
|
|||||||
import eu.kanade.core.preference.asState
|
import eu.kanade.core.preference.asState
|
||||||
import eu.kanade.domain.ui.UiPreferences
|
import eu.kanade.domain.ui.UiPreferences
|
||||||
import eu.kanade.presentation.components.TabbedScreen
|
import eu.kanade.presentation.components.TabbedScreen
|
||||||
import eu.kanade.presentation.permissions.PermissionRequestHelper
|
|
||||||
import eu.kanade.presentation.util.Tab
|
import eu.kanade.presentation.util.Tab
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.ui.browse.extension.ExtensionsScreenModel
|
import eu.kanade.tachiyomi.ui.browse.extension.ExtensionsScreenModel
|
||||||
@ -91,9 +90,6 @@ data class BrowseTab(
|
|||||||
onChangeSearchQuery = extensionsScreenModel::search,
|
onChangeSearchQuery = extensionsScreenModel::search,
|
||||||
)
|
)
|
||||||
|
|
||||||
// For local source
|
|
||||||
PermissionRequestHelper.requestStoragePermission()
|
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
(context as? MainActivity)?.ready = true
|
(context as? MainActivity)?.ready = true
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ import eu.kanade.tachiyomi.source.online.all.MergedSource
|
|||||||
import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter
|
import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter
|
||||||
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
|
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
|
||||||
import tachiyomi.core.i18n.stringResource
|
import tachiyomi.core.i18n.stringResource
|
||||||
|
import tachiyomi.core.storage.toTempFile
|
||||||
import tachiyomi.core.util.lang.withIOContext
|
import tachiyomi.core.util.lang.withIOContext
|
||||||
import tachiyomi.core.util.system.logcat
|
import tachiyomi.core.util.system.logcat
|
||||||
import tachiyomi.domain.manga.model.Manga
|
import tachiyomi.domain.manga.model.Manga
|
||||||
@ -124,13 +125,13 @@ class ChapterLoader(
|
|||||||
source is LocalSource -> source.getFormat(chapter.chapter).let { format ->
|
source is LocalSource -> source.getFormat(chapter.chapter).let { format ->
|
||||||
when (format) {
|
when (format) {
|
||||||
is Format.Directory -> DirectoryPageLoader(format.file)
|
is Format.Directory -> DirectoryPageLoader(format.file)
|
||||||
is Format.Zip -> ZipPageLoader(format.file)
|
is Format.Zip -> ZipPageLoader(format.file.toTempFile(context))
|
||||||
is Format.Rar -> try {
|
is Format.Rar -> try {
|
||||||
RarPageLoader(format.file)
|
RarPageLoader(format.file.toTempFile(context))
|
||||||
} catch (e: UnsupportedRarV5Exception) {
|
} catch (e: UnsupportedRarV5Exception) {
|
||||||
error(context.stringResource(MR.strings.loader_rar5_error))
|
error(context.stringResource(MR.strings.loader_rar5_error))
|
||||||
}
|
}
|
||||||
is Format.Epub -> EpubPageLoader(format.file)
|
is Format.Epub -> EpubPageLoader(format.file.toTempFile(context))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> error(context.stringResource(MR.strings.loader_not_implemented_error))
|
else -> error(context.stringResource(MR.strings.loader_not_implemented_error))
|
||||||
@ -141,13 +142,13 @@ class ChapterLoader(
|
|||||||
source is LocalSource -> source.getFormat(chapter.chapter).let { format ->
|
source is LocalSource -> source.getFormat(chapter.chapter).let { format ->
|
||||||
when (format) {
|
when (format) {
|
||||||
is Format.Directory -> DirectoryPageLoader(format.file)
|
is Format.Directory -> DirectoryPageLoader(format.file)
|
||||||
is Format.Zip -> ZipPageLoader(format.file)
|
is Format.Zip -> ZipPageLoader(format.file.toTempFile(context))
|
||||||
is Format.Rar -> try {
|
is Format.Rar -> try {
|
||||||
RarPageLoader(format.file)
|
RarPageLoader(format.file.toTempFile(context))
|
||||||
} catch (e: UnsupportedRarV5Exception) {
|
} catch (e: UnsupportedRarV5Exception) {
|
||||||
error(context.stringResource(MR.strings.loader_rar5_error))
|
error(context.stringResource(MR.strings.loader_rar5_error))
|
||||||
}
|
}
|
||||||
is Format.Epub -> EpubPageLoader(format.file)
|
is Format.Epub -> EpubPageLoader(format.file.toTempFile(context))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
source is HttpSource -> HttpPageLoader(chapter, source)
|
source is HttpSource -> HttpPageLoader(chapter, source)
|
||||||
|
@ -10,6 +10,7 @@ import eu.kanade.tachiyomi.source.Source
|
|||||||
import eu.kanade.tachiyomi.source.model.Page
|
import eu.kanade.tachiyomi.source.model.Page
|
||||||
import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter
|
import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter
|
||||||
import eu.kanade.tachiyomi.ui.reader.model.ReaderPage
|
import eu.kanade.tachiyomi.ui.reader.model.ReaderPage
|
||||||
|
import tachiyomi.core.storage.toTempFile
|
||||||
import tachiyomi.domain.manga.model.Manga
|
import tachiyomi.domain.manga.model.Manga
|
||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
|
|
||||||
@ -46,7 +47,7 @@ internal class DownloadPageLoader(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun getPagesFromArchive(chapterPath: UniFile): List<ReaderPage> {
|
private suspend fun getPagesFromArchive(chapterPath: UniFile): List<ReaderPage> {
|
||||||
val loader = ZipPageLoader(chapterPath).also { zipPageLoader = it }
|
val loader = ZipPageLoader(chapterPath.toTempFile(context)).also { zipPageLoader = it }
|
||||||
return loader.getPages()
|
return loader.getPages()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
package eu.kanade.tachiyomi.ui.reader.loader
|
package eu.kanade.tachiyomi.ui.reader.loader
|
||||||
|
|
||||||
import com.hippo.unifile.UniFile
|
|
||||||
import eu.kanade.tachiyomi.source.model.Page
|
import eu.kanade.tachiyomi.source.model.Page
|
||||||
import eu.kanade.tachiyomi.ui.reader.model.ReaderPage
|
import eu.kanade.tachiyomi.ui.reader.model.ReaderPage
|
||||||
import eu.kanade.tachiyomi.util.storage.EpubFile
|
import eu.kanade.tachiyomi.util.storage.EpubFile
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loader used to load a chapter from a .epub file.
|
* Loader used to load a chapter from a .epub file.
|
||||||
*/
|
*/
|
||||||
internal class EpubPageLoader(file: UniFile) : PageLoader() {
|
internal class EpubPageLoader(file: File) : PageLoader() {
|
||||||
|
|
||||||
private val epub = EpubFile(file)
|
private val epub = EpubFile(file)
|
||||||
|
|
||||||
|
@ -8,7 +8,6 @@ import eu.kanade.tachiyomi.source.model.Page
|
|||||||
import eu.kanade.tachiyomi.ui.reader.model.ReaderPage
|
import eu.kanade.tachiyomi.ui.reader.model.ReaderPage
|
||||||
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
|
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
|
||||||
import eu.kanade.tachiyomi.util.lang.compareToCaseInsensitiveNaturalOrder
|
import eu.kanade.tachiyomi.util.lang.compareToCaseInsensitiveNaturalOrder
|
||||||
import tachiyomi.core.storage.toFile
|
|
||||||
import tachiyomi.core.util.system.ImageUtil
|
import tachiyomi.core.util.system.ImageUtil
|
||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
import java.io.File
|
import java.io.File
|
||||||
@ -19,9 +18,9 @@ import java.io.PipedOutputStream
|
|||||||
/**
|
/**
|
||||||
* Loader used to load a chapter from a .rar or .cbr file.
|
* Loader used to load a chapter from a .rar or .cbr file.
|
||||||
*/
|
*/
|
||||||
internal class RarPageLoader(file: UniFile) : PageLoader() {
|
internal class RarPageLoader(file: File) : PageLoader() {
|
||||||
|
|
||||||
private val rar = Archive(file.toFile())
|
private val rar = Archive(file)
|
||||||
|
|
||||||
// SY -->
|
// SY -->
|
||||||
private val context: Application by injectLazy()
|
private val context: Application by injectLazy()
|
||||||
@ -33,7 +32,7 @@ internal class RarPageLoader(file: UniFile) : PageLoader() {
|
|||||||
init {
|
init {
|
||||||
if (readerPreferences.cacheArchiveMangaOnDisk().get()) {
|
if (readerPreferences.cacheArchiveMangaOnDisk().get()) {
|
||||||
tmpDir.mkdirs()
|
tmpDir.mkdirs()
|
||||||
Archive(file.toFile()).use { rar ->
|
Archive(file).use { rar ->
|
||||||
rar.fileHeaders.asSequence()
|
rar.fileHeaders.asSequence()
|
||||||
.filterNot { it.isDirectory }
|
.filterNot { it.isDirectory }
|
||||||
.forEach { header ->
|
.forEach { header ->
|
||||||
|
@ -9,7 +9,6 @@ import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
|
|||||||
import eu.kanade.tachiyomi.util.lang.compareToCaseInsensitiveNaturalOrder
|
import eu.kanade.tachiyomi.util.lang.compareToCaseInsensitiveNaturalOrder
|
||||||
import eu.kanade.tachiyomi.util.storage.CbzCrypto
|
import eu.kanade.tachiyomi.util.storage.CbzCrypto
|
||||||
import tachiyomi.core.i18n.stringResource
|
import tachiyomi.core.i18n.stringResource
|
||||||
import tachiyomi.core.storage.toFile
|
|
||||||
import tachiyomi.core.util.system.ImageUtil
|
import tachiyomi.core.util.system.ImageUtil
|
||||||
import tachiyomi.i18n.sy.SYMR
|
import tachiyomi.i18n.sy.SYMR
|
||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
@ -21,7 +20,7 @@ import net.lingala.zip4j.ZipFile as Zip4jFile
|
|||||||
/**
|
/**
|
||||||
* Loader used to load a chapter from a .zip or .cbz file.
|
* Loader used to load a chapter from a .zip or .cbz file.
|
||||||
*/
|
*/
|
||||||
internal class ZipPageLoader(file: UniFile) : PageLoader() {
|
internal class ZipPageLoader(file: File) : PageLoader() {
|
||||||
|
|
||||||
// SY -->
|
// SY -->
|
||||||
private val context: Application by injectLazy()
|
private val context: Application by injectLazy()
|
||||||
@ -29,12 +28,12 @@ internal class ZipPageLoader(file: UniFile) : PageLoader() {
|
|||||||
private val tmpDir = File(context.externalCacheDir, "reader_${file.hashCode()}").also {
|
private val tmpDir = File(context.externalCacheDir, "reader_${file.hashCode()}").also {
|
||||||
it.deleteRecursively()
|
it.deleteRecursively()
|
||||||
}
|
}
|
||||||
private val zip4j: Zip4jFile = Zip4jFile(file.toFile())
|
private val zip4j: Zip4jFile = Zip4jFile(file)
|
||||||
private val zip: ZipFile? =
|
private val zip: ZipFile? =
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||||
if (!zip4j.isEncrypted) ZipFile(file.toFile(), StandardCharsets.ISO_8859_1) else null
|
if (!zip4j.isEncrypted) ZipFile(file, StandardCharsets.ISO_8859_1) else null
|
||||||
} else {
|
} else {
|
||||||
if (!zip4j.isEncrypted) ZipFile(file.toFile()) else null
|
if (!zip4j.isEncrypted) ZipFile(file) else null
|
||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
@ -42,7 +41,7 @@ internal class ZipPageLoader(file: UniFile) : PageLoader() {
|
|||||||
zip4j.charset = StandardCharsets.ISO_8859_1
|
zip4j.charset = StandardCharsets.ISO_8859_1
|
||||||
}
|
}
|
||||||
|
|
||||||
Zip4jFile(file.toFile()).use { zip ->
|
Zip4jFile(file).use { zip ->
|
||||||
if (zip.isEncrypted) {
|
if (zip.isEncrypted) {
|
||||||
if (!CbzCrypto.checkCbzPassword(zip, CbzCrypto.getDecryptedPasswordCbz())) {
|
if (!CbzCrypto.checkCbzPassword(zip, CbzCrypto.getDecryptedPasswordCbz())) {
|
||||||
this.recycle()
|
this.recycle()
|
||||||
|
@ -3,14 +3,14 @@ package exh.log
|
|||||||
import com.elvishew.xlog.internal.DefaultsFactory
|
import com.elvishew.xlog.internal.DefaultsFactory
|
||||||
import com.elvishew.xlog.printer.Printer
|
import com.elvishew.xlog.printer.Printer
|
||||||
import com.elvishew.xlog.printer.file.backup.BackupStrategy
|
import com.elvishew.xlog.printer.file.backup.BackupStrategy
|
||||||
import com.elvishew.xlog.printer.file.clean.CleanStrategy
|
|
||||||
import com.elvishew.xlog.printer.file.naming.FileNameGenerator
|
import com.elvishew.xlog.printer.file.naming.FileNameGenerator
|
||||||
|
import com.hippo.unifile.UniFile
|
||||||
|
import exh.log.EnhancedFilePrinter.Builder
|
||||||
import java.io.BufferedWriter
|
import java.io.BufferedWriter
|
||||||
import java.io.File
|
|
||||||
import java.io.FileWriter
|
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.util.concurrent.BlockingQueue
|
import java.util.concurrent.BlockingQueue
|
||||||
import java.util.concurrent.LinkedBlockingQueue
|
import java.util.concurrent.LinkedBlockingQueue
|
||||||
|
import kotlin.time.Duration.Companion.days
|
||||||
import com.elvishew.xlog.flattener.Flattener2 as Flattener
|
import com.elvishew.xlog.flattener.Flattener2 as Flattener
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -18,7 +18,7 @@ import com.elvishew.xlog.flattener.Flattener2 as Flattener
|
|||||||
*
|
*
|
||||||
* Use the [Builder] to construct a [EnhancedFilePrinter] object.
|
* Use the [Builder] to construct a [EnhancedFilePrinter] object.
|
||||||
*
|
*
|
||||||
* @param folderPath The folder path of log file.
|
* @param folder The folder path of log file.
|
||||||
* @param fileNameGenerator the file name generator for log file.
|
* @param fileNameGenerator the file name generator for log file.
|
||||||
* @param backupStrategy the backup strategy for log file.
|
* @param backupStrategy the backup strategy for log file.
|
||||||
* @param cleanStrategy The clean strategy for log file.
|
* @param cleanStrategy The clean strategy for log file.
|
||||||
@ -27,10 +27,9 @@ import com.elvishew.xlog.flattener.Flattener2 as Flattener
|
|||||||
*/
|
*/
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
class EnhancedFilePrinter internal constructor(
|
class EnhancedFilePrinter internal constructor(
|
||||||
private val folderPath: String,
|
private val folder: UniFile,
|
||||||
private val fileNameGenerator: FileNameGenerator,
|
private val fileNameGenerator: FileNameGenerator,
|
||||||
private val backupStrategy: BackupStrategy,
|
private val backupStrategy: BackupStrategy,
|
||||||
private val cleanStrategy: CleanStrategy,
|
|
||||||
private val flattener: Flattener,
|
private val flattener: Flattener,
|
||||||
) : Printer {
|
) : Printer {
|
||||||
/**
|
/**
|
||||||
@ -41,16 +40,6 @@ class EnhancedFilePrinter internal constructor(
|
|||||||
@Volatile
|
@Volatile
|
||||||
private var worker: Worker? = null
|
private var worker: Worker? = null
|
||||||
|
|
||||||
/**
|
|
||||||
* Make sure the folder of log file exists.
|
|
||||||
*/
|
|
||||||
private fun checkLogFolder() {
|
|
||||||
val folder = File(folderPath)
|
|
||||||
if (!folder.exists()) {
|
|
||||||
folder.mkdirs()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun println(logLevel: Int, tag: String, msg: String) {
|
override fun println(logLevel: Int, tag: String, msg: String) {
|
||||||
val timeMillis = System.currentTimeMillis()
|
val timeMillis = System.currentTimeMillis()
|
||||||
if (USE_WORKER) {
|
if (USE_WORKER) {
|
||||||
@ -68,8 +57,8 @@ class EnhancedFilePrinter internal constructor(
|
|||||||
* Do the real job of writing log to file.
|
* Do the real job of writing log to file.
|
||||||
*/
|
*/
|
||||||
private fun doPrintln(timeMillis: Long, logLevel: Int, tag: String, msg: String) {
|
private fun doPrintln(timeMillis: Long, logLevel: Int, tag: String, msg: String) {
|
||||||
var lastFileName = writer.lastFileName
|
val lastFileName = writer.lastFileName
|
||||||
if (lastFileName == null || fileNameGenerator.isFileNameChangeable) {
|
if (fileNameGenerator.isFileNameChangeable) {
|
||||||
val newFileName = fileNameGenerator.generateFileName(logLevel, System.currentTimeMillis())
|
val newFileName = fileNameGenerator.generateFileName(logLevel, System.currentTimeMillis())
|
||||||
require(!(newFileName == null || newFileName.trim { it <= ' ' }.isEmpty())) { "File name should not be empty." }
|
require(!(newFileName == null || newFileName.trim { it <= ' ' }.isEmpty())) { "File name should not be empty." }
|
||||||
if (newFileName != lastFileName) {
|
if (newFileName != lastFileName) {
|
||||||
@ -77,37 +66,29 @@ class EnhancedFilePrinter internal constructor(
|
|||||||
writer.close()
|
writer.close()
|
||||||
}
|
}
|
||||||
cleanLogFilesIfNecessary()
|
cleanLogFilesIfNecessary()
|
||||||
if (writer.open(newFileName).not()) {
|
if (writer.open(folder.createFile(newFileName)!!).not()) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
lastFileName = newFileName
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val lastFile = writer.file ?: return
|
|
||||||
if (backupStrategy.shouldBackup(lastFile)) {
|
|
||||||
// Backup the log file, and create a new log file.
|
|
||||||
writer.close()
|
|
||||||
val backupFile = File(folderPath, "$lastFileName.bak")
|
|
||||||
if (backupFile.exists()) {
|
|
||||||
backupFile.delete()
|
|
||||||
}
|
|
||||||
lastFile.renameTo(backupFile)
|
|
||||||
if (writer.open(lastFileName).not()) {
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val flattenedLog = flattener.flatten(timeMillis, logLevel, tag, msg).toString()
|
val flattenedLog = flattener.flatten(timeMillis, logLevel, tag, msg).toString()
|
||||||
writer.appendLog(flattenedLog)
|
writer.appendLog(flattenedLog)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val maxTimeMillis = 7.days.inWholeMilliseconds
|
||||||
|
private fun shouldClean(file: UniFile): Boolean {
|
||||||
|
val currentTimeMillis = System.currentTimeMillis()
|
||||||
|
val lastModified = file.lastModified()
|
||||||
|
return currentTimeMillis - lastModified > maxTimeMillis
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clean log files if should clean follow strategy
|
* Clean log files if should clean follow strategy
|
||||||
*/
|
*/
|
||||||
private fun cleanLogFilesIfNecessary() {
|
private fun cleanLogFilesIfNecessary() {
|
||||||
val logDir = File(folderPath)
|
folder.listFiles().orEmpty()
|
||||||
logDir.listFiles().orEmpty()
|
|
||||||
.asSequence()
|
.asSequence()
|
||||||
.filter { cleanStrategy.shouldClean(it) }
|
.filter { shouldClean(it) }
|
||||||
.forEach { it.delete() }
|
.forEach { it.delete() }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,7 +96,7 @@ class EnhancedFilePrinter internal constructor(
|
|||||||
* Builder for [EnhancedFilePrinter].
|
* Builder for [EnhancedFilePrinter].
|
||||||
* @param folderPath the folder path of log file
|
* @param folderPath the folder path of log file
|
||||||
*/
|
*/
|
||||||
class Builder(private val folderPath: String) {
|
class Builder(private val folder: UniFile) {
|
||||||
/**
|
/**
|
||||||
* The file name generator for log file.
|
* The file name generator for log file.
|
||||||
*/
|
*/
|
||||||
@ -126,11 +107,6 @@ class EnhancedFilePrinter internal constructor(
|
|||||||
*/
|
*/
|
||||||
var backupStrategy: BackupStrategy? = null
|
var backupStrategy: BackupStrategy? = null
|
||||||
|
|
||||||
/**
|
|
||||||
* The clean strategy for log file.
|
|
||||||
*/
|
|
||||||
var cleanStrategy: CleanStrategy? = null
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The flattener when print a log.
|
* The flattener when print a log.
|
||||||
*/
|
*/
|
||||||
@ -158,17 +134,6 @@ class EnhancedFilePrinter internal constructor(
|
|||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the clean strategy for log file.
|
|
||||||
*
|
|
||||||
* @param cleanStrategy the clean strategy for log file
|
|
||||||
* @return the builder
|
|
||||||
*/
|
|
||||||
fun cleanStrategy(cleanStrategy: CleanStrategy): Builder {
|
|
||||||
this.cleanStrategy = cleanStrategy
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the flattener when print a log.
|
* Set the flattener when print a log.
|
||||||
*
|
*
|
||||||
@ -187,17 +152,16 @@ class EnhancedFilePrinter internal constructor(
|
|||||||
*/
|
*/
|
||||||
fun build(): EnhancedFilePrinter {
|
fun build(): EnhancedFilePrinter {
|
||||||
return EnhancedFilePrinter(
|
return EnhancedFilePrinter(
|
||||||
folderPath,
|
folder,
|
||||||
fileNameGenerator ?: DefaultsFactory.createFileNameGenerator(),
|
fileNameGenerator ?: DefaultsFactory.createFileNameGenerator(),
|
||||||
backupStrategy ?: DefaultsFactory.createBackupStrategy(),
|
backupStrategy ?: DefaultsFactory.createBackupStrategy(),
|
||||||
cleanStrategy ?: DefaultsFactory.createCleanStrategy(),
|
|
||||||
flattener ?: DefaultsFactory.createFlattener2(),
|
flattener ?: DefaultsFactory.createFlattener2(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
operator fun invoke(folderPath: String, block: Builder.() -> Unit): EnhancedFilePrinter {
|
operator fun invoke(folder: UniFile, block: Builder.() -> Unit): EnhancedFilePrinter {
|
||||||
return Builder(folderPath).apply(block).build()
|
return Builder(folder).apply(block).build()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -271,9 +235,6 @@ class EnhancedFilePrinter internal constructor(
|
|||||||
* Get the name of last used log file.
|
* Get the name of last used log file.
|
||||||
* @return the name of last used log file, maybe null
|
* @return the name of last used log file, maybe null
|
||||||
*/
|
*/
|
||||||
/**
|
|
||||||
* The file name of last used log file.
|
|
||||||
*/
|
|
||||||
var lastFileName: String? = null
|
var lastFileName: String? = null
|
||||||
private set
|
private set
|
||||||
/**
|
/**
|
||||||
@ -281,10 +242,7 @@ class EnhancedFilePrinter internal constructor(
|
|||||||
*
|
*
|
||||||
* @return the current log file, maybe null
|
* @return the current log file, maybe null
|
||||||
*/
|
*/
|
||||||
/**
|
var file: UniFile? = null
|
||||||
* The current log file.
|
|
||||||
*/
|
|
||||||
var file: File? = null
|
|
||||||
private set
|
private set
|
||||||
|
|
||||||
private var bufferedWriter: BufferedWriter? = null
|
private var bufferedWriter: BufferedWriter? = null
|
||||||
@ -303,15 +261,10 @@ class EnhancedFilePrinter internal constructor(
|
|||||||
* @param newFileName the specific file name
|
* @param newFileName the specific file name
|
||||||
* @return true if opened successfully, false otherwise
|
* @return true if opened successfully, false otherwise
|
||||||
*/
|
*/
|
||||||
fun open(newFileName: String): Boolean {
|
fun open(file: UniFile): Boolean {
|
||||||
return try {
|
return try {
|
||||||
val file = File(folderPath, newFileName)
|
bufferedWriter = file.openOutputStream().bufferedWriter()
|
||||||
if (file.exists().not()) {
|
lastFileName = file.name
|
||||||
(file.parentFile ?: File(file.absolutePath.substringBeforeLast(File.separatorChar))).mkdirs()
|
|
||||||
file.createNewFile()
|
|
||||||
}
|
|
||||||
bufferedWriter = FileWriter(file, true).buffered()
|
|
||||||
lastFileName = newFileName
|
|
||||||
this.file = file
|
this.file = file
|
||||||
true
|
true
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
@ -370,6 +323,5 @@ class EnhancedFilePrinter internal constructor(
|
|||||||
if (USE_WORKER) {
|
if (USE_WORKER) {
|
||||||
worker = Worker()
|
worker = Worker()
|
||||||
}
|
}
|
||||||
checkLogFolder()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
package eu.kanade.tachiyomi.util.storage
|
package eu.kanade.tachiyomi.util.storage
|
||||||
|
|
||||||
import com.hippo.unifile.UniFile
|
|
||||||
import org.jsoup.Jsoup
|
import org.jsoup.Jsoup
|
||||||
import org.jsoup.nodes.Document
|
import org.jsoup.nodes.Document
|
||||||
import tachiyomi.core.storage.toFile
|
|
||||||
import java.io.Closeable
|
import java.io.Closeable
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
@ -13,12 +11,12 @@ import java.util.zip.ZipFile
|
|||||||
/**
|
/**
|
||||||
* Wrapper over ZipFile to load files in epub format.
|
* Wrapper over ZipFile to load files in epub format.
|
||||||
*/
|
*/
|
||||||
class EpubFile(file: UniFile) : Closeable {
|
class EpubFile(file: File) : Closeable {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Zip file of this epub.
|
* Zip file of this epub.
|
||||||
*/
|
*/
|
||||||
private val zip = ZipFile(file.toFile())
|
private val zip = ZipFile(file)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Path separator used by this epub.
|
* Path separator used by this epub.
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
package tachiyomi.core.storage
|
package tachiyomi.core.storage
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Build
|
||||||
|
import android.os.FileUtils
|
||||||
import com.hippo.unifile.UniFile
|
import com.hippo.unifile.UniFile
|
||||||
|
import java.io.BufferedOutputStream
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
val UniFile.extension: String?
|
val UniFile.extension: String?
|
||||||
@ -9,4 +13,26 @@ val UniFile.extension: String?
|
|||||||
val UniFile.nameWithoutExtension: String?
|
val UniFile.nameWithoutExtension: String?
|
||||||
get() = name?.substringBeforeLast('.')
|
get() = name?.substringBeforeLast('.')
|
||||||
|
|
||||||
fun UniFile.toFile(): File? = filePath?.let { File(it) }
|
fun UniFile.toTempFile(context: Context): File {
|
||||||
|
val inputStream = context.contentResolver.openInputStream(uri)!!
|
||||||
|
val tempFile = File.createTempFile(
|
||||||
|
nameWithoutExtension.orEmpty(),
|
||||||
|
null,
|
||||||
|
)
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||||
|
FileUtils.copy(inputStream, tempFile.outputStream())
|
||||||
|
} else {
|
||||||
|
BufferedOutputStream(tempFile.outputStream()).use { tmpOut ->
|
||||||
|
inputStream.use { input ->
|
||||||
|
val buffer = ByteArray(8192)
|
||||||
|
var count: Int
|
||||||
|
while (input.read(buffer).also { count = it } > 0) {
|
||||||
|
tmpOut.write(buffer, 0, count)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return tempFile
|
||||||
|
}
|
||||||
|
@ -22,7 +22,6 @@ material-core = { module = "androidx.compose.material:material" }
|
|||||||
glance = "androidx.glance:glance-appwidget:1.0.0"
|
glance = "androidx.glance:glance-appwidget:1.0.0"
|
||||||
|
|
||||||
accompanist-webview = { module = "com.google.accompanist:accompanist-webview", version.ref = "accompanist" }
|
accompanist-webview = { module = "com.google.accompanist:accompanist-webview", version.ref = "accompanist" }
|
||||||
accompanist-permissions = { module = "com.google.accompanist:accompanist-permissions", version.ref = "accompanist" }
|
|
||||||
accompanist-systemuicontroller = { module = "com.google.accompanist:accompanist-systemuicontroller", version.ref = "accompanist" }
|
accompanist-systemuicontroller = { module = "com.google.accompanist:accompanist-systemuicontroller", version.ref = "accompanist" }
|
||||||
|
|
||||||
lintchecks = { module = "com.slack.lint.compose:compose-lint-checks", version = "1.2.0" }
|
lintchecks = { module = "com.slack.lint.compose:compose-lint-checks", version = "1.2.0" }
|
@ -30,7 +30,7 @@ import tachiyomi.core.metadata.comicinfo.getComicInfo
|
|||||||
import tachiyomi.core.metadata.tachiyomi.MangaDetails
|
import tachiyomi.core.metadata.tachiyomi.MangaDetails
|
||||||
import tachiyomi.core.storage.extension
|
import tachiyomi.core.storage.extension
|
||||||
import tachiyomi.core.storage.nameWithoutExtension
|
import tachiyomi.core.storage.nameWithoutExtension
|
||||||
import tachiyomi.core.storage.toFile
|
import tachiyomi.core.storage.toTempFile
|
||||||
import tachiyomi.core.util.lang.withIOContext
|
import tachiyomi.core.util.lang.withIOContext
|
||||||
import tachiyomi.core.util.system.ImageUtil
|
import tachiyomi.core.util.system.ImageUtil
|
||||||
import tachiyomi.core.util.system.logcat
|
import tachiyomi.core.util.system.logcat
|
||||||
@ -154,12 +154,12 @@ actual class LocalSource(
|
|||||||
|
|
||||||
// SY -->
|
// SY -->
|
||||||
fun updateMangaInfo(manga: SManga) {
|
fun updateMangaInfo(manga: SManga) {
|
||||||
val directory = fileSystem.getFilesInBaseDirectory().map { File(it.toFile(), manga.url) }.find {
|
val directory = fileSystem.getFilesInBaseDirectory().map { it.createDirectory(manga.url) }.find {
|
||||||
it.exists()
|
it?.exists() == true
|
||||||
} ?: return
|
} ?: return
|
||||||
val existingFileName = directory.listFiles()?.find { it.extension == "json" }?.name
|
val existingFile = directory.listFiles()?.find { it.extension == "json" }
|
||||||
val file = File(directory, existingFileName ?: "info.json")
|
val file = existingFile ?: directory.createFile("info.json") ?: return
|
||||||
file.outputStream().use {
|
file.openOutputStream().use {
|
||||||
json.encodeToStream(manga.toJson(), it)
|
json.encodeToStream(manga.toJson(), it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -199,7 +199,7 @@ actual class LocalSource(
|
|||||||
}
|
}
|
||||||
// SY -->
|
// SY -->
|
||||||
comicInfoArchiveFile != null -> {
|
comicInfoArchiveFile != null -> {
|
||||||
val comicInfoArchive = ZipFile(comicInfoArchiveFile.toFile())
|
val comicInfoArchive = ZipFile(comicInfoArchiveFile.toTempFile(context))
|
||||||
noXmlFile?.delete()
|
noXmlFile?.delete()
|
||||||
|
|
||||||
if (CbzCrypto.checkCbzPassword(comicInfoArchive, CbzCrypto.getDecryptedPasswordCbz())) {
|
if (CbzCrypto.checkCbzPassword(comicInfoArchive, CbzCrypto.getDecryptedPasswordCbz())) {
|
||||||
@ -269,7 +269,7 @@ actual class LocalSource(
|
|||||||
for (chapter in chapterArchives) {
|
for (chapter in chapterArchives) {
|
||||||
when (Format.valueOf(chapter)) {
|
when (Format.valueOf(chapter)) {
|
||||||
is Format.Zip -> {
|
is Format.Zip -> {
|
||||||
ZipFile(chapter.toFile()).use { zip: ZipFile ->
|
ZipFile(chapter.toTempFile(context)).use { zip: ZipFile ->
|
||||||
// SY -->
|
// SY -->
|
||||||
if (zip.isEncrypted && !CbzCrypto.checkCbzPassword(zip, CbzCrypto.getDecryptedPasswordCbz())
|
if (zip.isEncrypted && !CbzCrypto.checkCbzPassword(zip, CbzCrypto.getDecryptedPasswordCbz())
|
||||||
) {
|
) {
|
||||||
@ -288,7 +288,7 @@ actual class LocalSource(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
is Format.Rar -> {
|
is Format.Rar -> {
|
||||||
JunrarArchive(chapter.toFile()).use { rar ->
|
JunrarArchive(chapter.toTempFile(context)).use { rar ->
|
||||||
rar.fileHeaders.firstOrNull { it.fileName == COMIC_INFO_FILE }?.let { comicInfoFile ->
|
rar.fileHeaders.firstOrNull { it.fileName == COMIC_INFO_FILE }?.let { comicInfoFile ->
|
||||||
rar.getInputStream(comicInfoFile).buffered().use { stream ->
|
rar.getInputStream(comicInfoFile).buffered().use { stream ->
|
||||||
return copyComicInfoFile(stream, folderPath)
|
return copyComicInfoFile(stream, folderPath)
|
||||||
@ -354,7 +354,7 @@ actual class LocalSource(
|
|||||||
|
|
||||||
val format = Format.valueOf(chapterFile)
|
val format = Format.valueOf(chapterFile)
|
||||||
if (format is Format.Epub) {
|
if (format is Format.Epub) {
|
||||||
EpubFile(format.file).use { epub ->
|
EpubFile(format.file.toTempFile(context)).use { epub ->
|
||||||
epub.fillMetadata(manga, this)
|
epub.fillMetadata(manga, this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -413,7 +413,7 @@ actual class LocalSource(
|
|||||||
entry?.let { coverManager.update(manga, it.openInputStream()) }
|
entry?.let { coverManager.update(manga, it.openInputStream()) }
|
||||||
}
|
}
|
||||||
is Format.Zip -> {
|
is Format.Zip -> {
|
||||||
ZipFile(format.file.toFile()).use { zip ->
|
ZipFile(format.file.toTempFile(context)).use { zip ->
|
||||||
// SY -->
|
// SY -->
|
||||||
var encrypted = false
|
var encrypted = false
|
||||||
if (zip.isEncrypted) {
|
if (zip.isEncrypted) {
|
||||||
@ -428,7 +428,7 @@ actual class LocalSource(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
is Format.Rar -> {
|
is Format.Rar -> {
|
||||||
JunrarArchive(format.file.toFile()).use { archive ->
|
JunrarArchive(format.file.toTempFile(context)).use { archive ->
|
||||||
val entry = archive.fileHeaders
|
val entry = archive.fileHeaders
|
||||||
.sortedWith { f1, f2 -> f1.fileName.compareToCaseInsensitiveNaturalOrder(f2.fileName) }
|
.sortedWith { f1, f2 -> f1.fileName.compareToCaseInsensitiveNaturalOrder(f2.fileName) }
|
||||||
.find { !it.isDirectory && ImageUtil.isImage(it.fileName) { archive.getInputStream(it) } }
|
.find { !it.isDirectory && ImageUtil.isImage(it.fileName) { archive.getInputStream(it) } }
|
||||||
@ -437,7 +437,7 @@ actual class LocalSource(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
is Format.Epub -> {
|
is Format.Epub -> {
|
||||||
EpubFile(format.file).use { epub ->
|
EpubFile(format.file.toTempFile(context)).use { epub ->
|
||||||
val entry = epub.getImagesFromPages()
|
val entry = epub.getImagesFromPages()
|
||||||
.firstOrNull()
|
.firstOrNull()
|
||||||
?.let { epub.getEntry(it) }
|
?.let { epub.getEntry(it) }
|
||||||
|
@ -8,7 +8,6 @@ import eu.kanade.tachiyomi.util.storage.DiskUtil
|
|||||||
import net.lingala.zip4j.ZipFile
|
import net.lingala.zip4j.ZipFile
|
||||||
import net.lingala.zip4j.model.ZipParameters
|
import net.lingala.zip4j.model.ZipParameters
|
||||||
import tachiyomi.core.storage.nameWithoutExtension
|
import tachiyomi.core.storage.nameWithoutExtension
|
||||||
import tachiyomi.core.storage.toFile
|
|
||||||
import tachiyomi.core.util.system.ImageUtil
|
import tachiyomi.core.util.system.ImageUtil
|
||||||
import tachiyomi.source.local.io.LocalSourceFileSystem
|
import tachiyomi.source.local.io.LocalSourceFileSystem
|
||||||
import java.io.File
|
import java.io.File
|
||||||
@ -61,12 +60,22 @@ actual class LocalCoverManager(
|
|||||||
inputStream.use { input ->
|
inputStream.use { input ->
|
||||||
// SY -->
|
// SY -->
|
||||||
if (encrypted) {
|
if (encrypted) {
|
||||||
val zip4j = ZipFile(targetFile.toFile())
|
val tempFile = File.createTempFile(
|
||||||
|
targetFile.nameWithoutExtension.orEmpty(),
|
||||||
|
null,
|
||||||
|
)
|
||||||
|
val zip4j = ZipFile(tempFile)
|
||||||
val zipParameters = ZipParameters()
|
val zipParameters = ZipParameters()
|
||||||
zip4j.setPassword(CbzCrypto.getDecryptedPasswordCbz())
|
zip4j.setPassword(CbzCrypto.getDecryptedPasswordCbz())
|
||||||
CbzCrypto.setZipParametersEncrypted(zipParameters)
|
CbzCrypto.setZipParametersEncrypted(zipParameters)
|
||||||
zipParameters.fileNameInZip = DEFAULT_COVER_NAME
|
zipParameters.fileNameInZip = DEFAULT_COVER_NAME
|
||||||
zip4j.addStream(input, zipParameters)
|
zip4j.addStream(input, zipParameters)
|
||||||
|
zip4j.close()
|
||||||
|
targetFile.openOutputStream().use { output ->
|
||||||
|
tempFile.inputStream().use { input ->
|
||||||
|
input.copyTo(output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
DiskUtil.createNoMediaFile(directory, context)
|
DiskUtil.createNoMediaFile(directory, context)
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user