komikku/source-local/src/androidMain/kotlin/tachiyomi/source/local/LocalSource.kt

470 lines
19 KiB
Kotlin
Raw Normal View History

package tachiyomi.source.local
import android.content.Context
import com.hippo.unifile.UniFile
import eu.kanade.tachiyomi.source.CatalogueSource
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.UnmeteredSource
2020-02-27 00:12:44 +01:00
import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.Page
2020-02-27 00:12:44 +01:00
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
2020-02-03 04:22:54 +01:00
import eu.kanade.tachiyomi.util.lang.compareToCaseInsensitiveNaturalOrder
import eu.kanade.tachiyomi.util.storage.CbzCrypto
2020-02-03 04:04:11 +01:00
import eu.kanade.tachiyomi.util.storage.EpubFile
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.decodeFromStream
import logcat.LogPriority
import nl.adaptivity.xmlutil.AndroidXmlReader
import nl.adaptivity.xmlutil.serialization.XML
Move `:core` to `:core:common` (cherry picked from commit aa498360db90350f2642e6320dc55e7d474df1fd) # Conflicts: # app/build.gradle.kts # app/src/main/java/eu/kanade/domain/base/BasePreferences.kt # app/src/main/java/eu/kanade/domain/chapter/interactor/SetReadStatus.kt # app/src/main/java/eu/kanade/presentation/library/LibrarySettingsDialog.kt # app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAdvancedScreen.kt # app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsBrowseScreen.kt # app/src/main/java/eu/kanade/tachiyomi/App.kt # app/src/main/java/eu/kanade/tachiyomi/Migrations.kt # app/src/main/java/eu/kanade/tachiyomi/data/backup/create/BackupCreator.kt # app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateJob.kt # app/src/main/java/eu/kanade/tachiyomi/data/updater/AppUpdateChecker.kt # app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/MigrateDialog.kt # app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/SourceSearchScreen.kt # app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/SourcesScreenModel.kt # app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreenModel.kt # app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceFilterDialog.kt # app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryScreenModel.kt # app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryTab.kt # app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt # app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt # app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt # app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt # app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt # app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ChapterLoader.kt # app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/RarPageLoader.kt # app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ZipPageLoader.kt # app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderPreferences.kt # app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerPageHolder.kt # app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerViewerAdapter.kt # app/src/main/java/eu/kanade/tachiyomi/ui/stats/StatsScreenModel.kt # app/src/main/java/eu/kanade/tachiyomi/util/CrashLogUtil.kt # core/common/src/main/kotlin/eu/kanade/tachiyomi/util/storage/CbzCrypto.kt # data/src/main/java/tachiyomi/data/manga/MangaRepositoryImpl.kt # data/src/main/java/tachiyomi/data/source/SourcePagingSource.kt # domain/src/main/java/tachiyomi/domain/library/service/LibraryPreferences.kt # domain/src/main/java/tachiyomi/domain/manga/model/Manga.kt # settings.gradle.kts # source-api/src/commonMain/kotlin/eu/kanade/tachiyomi/source/online/HttpSource.kt # source-local/src/androidMain/kotlin/tachiyomi/source/local/image/LocalCoverManager.kt
2024-01-29 10:11:28 +01:00
import tachiyomi.core.common.i18n.stringResource
import tachiyomi.core.common.storage.addStreamToZip
Move `:core` to `:core:common` (cherry picked from commit aa498360db90350f2642e6320dc55e7d474df1fd) # Conflicts: # app/build.gradle.kts # app/src/main/java/eu/kanade/domain/base/BasePreferences.kt # app/src/main/java/eu/kanade/domain/chapter/interactor/SetReadStatus.kt # app/src/main/java/eu/kanade/presentation/library/LibrarySettingsDialog.kt # app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAdvancedScreen.kt # app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsBrowseScreen.kt # app/src/main/java/eu/kanade/tachiyomi/App.kt # app/src/main/java/eu/kanade/tachiyomi/Migrations.kt # app/src/main/java/eu/kanade/tachiyomi/data/backup/create/BackupCreator.kt # app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateJob.kt # app/src/main/java/eu/kanade/tachiyomi/data/updater/AppUpdateChecker.kt # app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/MigrateDialog.kt # app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/SourceSearchScreen.kt # app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/SourcesScreenModel.kt # app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreenModel.kt # app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceFilterDialog.kt # app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryScreenModel.kt # app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryTab.kt # app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt # app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt # app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt # app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt # app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt # app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ChapterLoader.kt # app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/RarPageLoader.kt # app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ZipPageLoader.kt # app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderPreferences.kt # app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerPageHolder.kt # app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerViewerAdapter.kt # app/src/main/java/eu/kanade/tachiyomi/ui/stats/StatsScreenModel.kt # app/src/main/java/eu/kanade/tachiyomi/util/CrashLogUtil.kt # core/common/src/main/kotlin/eu/kanade/tachiyomi/util/storage/CbzCrypto.kt # data/src/main/java/tachiyomi/data/manga/MangaRepositoryImpl.kt # data/src/main/java/tachiyomi/data/source/SourcePagingSource.kt # domain/src/main/java/tachiyomi/domain/library/service/LibraryPreferences.kt # domain/src/main/java/tachiyomi/domain/manga/model/Manga.kt # settings.gradle.kts # source-api/src/commonMain/kotlin/eu/kanade/tachiyomi/source/online/HttpSource.kt # source-local/src/androidMain/kotlin/tachiyomi/source/local/image/LocalCoverManager.kt
2024-01-29 10:11:28 +01:00
import tachiyomi.core.common.storage.extension
import tachiyomi.core.common.storage.getCoverStreamFromZip
import tachiyomi.core.common.storage.getZipInputStream
import tachiyomi.core.common.storage.isEncryptedZip
Move `:core` to `:core:common` (cherry picked from commit aa498360db90350f2642e6320dc55e7d474df1fd) # Conflicts: # app/build.gradle.kts # app/src/main/java/eu/kanade/domain/base/BasePreferences.kt # app/src/main/java/eu/kanade/domain/chapter/interactor/SetReadStatus.kt # app/src/main/java/eu/kanade/presentation/library/LibrarySettingsDialog.kt # app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAdvancedScreen.kt # app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsBrowseScreen.kt # app/src/main/java/eu/kanade/tachiyomi/App.kt # app/src/main/java/eu/kanade/tachiyomi/Migrations.kt # app/src/main/java/eu/kanade/tachiyomi/data/backup/create/BackupCreator.kt # app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateJob.kt # app/src/main/java/eu/kanade/tachiyomi/data/updater/AppUpdateChecker.kt # app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/MigrateDialog.kt # app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/SourceSearchScreen.kt # app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/SourcesScreenModel.kt # app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreenModel.kt # app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceFilterDialog.kt # app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryScreenModel.kt # app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryTab.kt # app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt # app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt # app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt # app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt # app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt # app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ChapterLoader.kt # app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/RarPageLoader.kt # app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ZipPageLoader.kt # app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderPreferences.kt # app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerPageHolder.kt # app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerViewerAdapter.kt # app/src/main/java/eu/kanade/tachiyomi/ui/stats/StatsScreenModel.kt # app/src/main/java/eu/kanade/tachiyomi/util/CrashLogUtil.kt # core/common/src/main/kotlin/eu/kanade/tachiyomi/util/storage/CbzCrypto.kt # data/src/main/java/tachiyomi/data/manga/MangaRepositoryImpl.kt # data/src/main/java/tachiyomi/data/source/SourcePagingSource.kt # domain/src/main/java/tachiyomi/domain/library/service/LibraryPreferences.kt # domain/src/main/java/tachiyomi/domain/manga/model/Manga.kt # settings.gradle.kts # source-api/src/commonMain/kotlin/eu/kanade/tachiyomi/source/online/HttpSource.kt # source-local/src/androidMain/kotlin/tachiyomi/source/local/image/LocalCoverManager.kt
2024-01-29 10:11:28 +01:00
import tachiyomi.core.common.storage.nameWithoutExtension
import tachiyomi.core.common.util.lang.withIOContext
import tachiyomi.core.common.util.system.ImageUtil
import tachiyomi.core.common.util.system.logcat
import tachiyomi.core.metadata.comicinfo.COMIC_INFO_FILE
import tachiyomi.core.metadata.comicinfo.ComicInfo
import tachiyomi.core.metadata.comicinfo.ComicInfoPublishingStatus
import tachiyomi.core.metadata.comicinfo.copyFromComicInfo
import tachiyomi.core.metadata.comicinfo.getComicInfo
import tachiyomi.core.metadata.tachiyomi.MangaDetails
import tachiyomi.domain.chapter.service.ChapterRecognition
import tachiyomi.domain.manga.model.Manga
Migrate to multiplatform string resources (#10147) * Migrate to multiplatform string resources * Move plurals translations into separate files * Fix lint check on generated files (cherry picked from commit 46e734fc8eefb8c06a880194e702559ca3fa769d) # Conflicts: # app/src/main/java/eu/kanade/presentation/browse/BrowseSourceScreen.kt # app/src/main/java/eu/kanade/presentation/browse/ExtensionDetailsScreen.kt # app/src/main/java/eu/kanade/presentation/browse/ExtensionsScreen.kt # app/src/main/java/eu/kanade/presentation/browse/SourcesScreen.kt # app/src/main/java/eu/kanade/presentation/browse/components/BrowseSourceDialogs.kt # app/src/main/java/eu/kanade/presentation/browse/components/BrowseSourceToolbar.kt # app/src/main/java/eu/kanade/presentation/category/components/CategoryDialogs.kt # app/src/main/java/eu/kanade/presentation/library/LibrarySettingsDialog.kt # app/src/main/java/eu/kanade/presentation/manga/components/MangaBottomActionMenu.kt # app/src/main/java/eu/kanade/presentation/manga/components/MangaDialogs.kt # app/src/main/java/eu/kanade/presentation/more/MoreScreen.kt # app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAdvancedScreen.kt # app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsBrowseScreen.kt # app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsLibraryScreen.kt # app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsMainScreen.kt # app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsReaderScreen.kt # app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsSecurityScreen.kt # app/src/main/java/eu/kanade/presentation/more/settings/screen/about/AboutScreen.kt # app/src/main/java/eu/kanade/presentation/more/settings/screen/advanced/ClearDatabaseScreen.kt # app/src/main/java/eu/kanade/presentation/more/settings/screen/data/CreateBackupScreen.kt # app/src/main/java/eu/kanade/presentation/reader/ReaderPageActionsDialog.kt # app/src/main/java/eu/kanade/presentation/reader/appbars/BottomReaderBar.kt # app/src/main/java/eu/kanade/presentation/reader/appbars/ReaderAppBars.kt # app/src/main/java/eu/kanade/presentation/reader/components/ChapterNavigator.kt # app/src/main/java/eu/kanade/presentation/reader/settings/GeneralSettingsPage.kt # app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadManager.kt # app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt # app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateJob.kt # app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateNotifier.kt # app/src/main/java/eu/kanade/tachiyomi/ui/browse/BrowseTab.kt # app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/MigrationFlags.kt # app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/manga/MigrateMangaScreen.kt # app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/MigrateDialog.kt # app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/SourceSearchScreen.kt # app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/sources/MigrateSourceTab.kt # app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/SourcesTab.kt # app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreen.kt # app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceFilterDialog.kt # app/src/main/java/eu/kanade/tachiyomi/ui/history/HistoryTab.kt # app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt # app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt # app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt # app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt # app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ChapterLoader.kt # app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderPreferences.kt # app/src/main/java/eu/kanade/tachiyomi/ui/stats/StatsScreen.kt # app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesTab.kt # build.gradle.kts # i18n/build.gradle.kts # i18n/src/commonMain/resources/MR/base/strings_sy.xml # i18n/src/commonMain/resources/MR/fr/strings_sy.xml # i18n/src/commonMain/resources/MR/in/strings_sy.xml # i18n/src/commonMain/resources/MR/pt-rBR/strings_sy.xml # i18n/src/commonMain/resources/MR/ru/strings_sy.xml # i18n/src/commonMain/resources/MR/zh-rCN/strings_sy.xml # i18n/src/commonMain/resources/MR/zh-rTW/strings_sy.xml
2023-11-18 19:54:56 +01:00
import tachiyomi.i18n.MR
import tachiyomi.source.local.filter.OrderBy
import tachiyomi.source.local.image.LocalCoverManager
import tachiyomi.source.local.io.Archive
import tachiyomi.source.local.io.Format
import tachiyomi.source.local.io.LocalSourceFileSystem
import tachiyomi.source.local.metadata.fillMetadata
import uy.kohesive.injekt.injectLazy
import java.io.InputStream
import java.nio.charset.StandardCharsets
import kotlin.time.Duration.Companion.days
import com.github.junrar.Archive as JunrarArchive
import tachiyomi.domain.source.model.Source as DomainSource
actual class LocalSource(
private val context: Context,
private val fileSystem: LocalSourceFileSystem,
private val coverManager: LocalCoverManager,
// SY -->
private val allowHiddenFiles: () -> Boolean,
// SY <--
) : CatalogueSource, UnmeteredSource {
private val json: Json by injectLazy()
private val xml: XML by injectLazy()
2021-09-11 17:48:32 +02:00
private val POPULAR_FILTERS = FilterList(OrderBy.Popular(context))
private val LATEST_FILTERS = FilterList(OrderBy.Latest(context))
Replace remaining Android-specific strings Also renaming the helper composables so it's a bit easier to find/replace everything in forks. (cherry picked from commit 0d1bced1226878336f8156671ae0c224c02f2604) # Conflicts: # app/src/main/java/eu/kanade/presentation/browse/ExtensionDetailsScreen.kt # app/src/main/java/eu/kanade/presentation/browse/ExtensionsScreen.kt # app/src/main/java/eu/kanade/presentation/browse/SourcesScreen.kt # app/src/main/java/eu/kanade/presentation/browse/components/BrowseSourceToolbar.kt # app/src/main/java/eu/kanade/presentation/category/components/CategoryDialogs.kt # app/src/main/java/eu/kanade/presentation/library/LibrarySettingsDialog.kt # app/src/main/java/eu/kanade/presentation/manga/components/MangaBottomActionMenu.kt # app/src/main/java/eu/kanade/presentation/manga/components/MangaDialogs.kt # app/src/main/java/eu/kanade/presentation/more/MoreScreen.kt # app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAdvancedScreen.kt # app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsBrowseScreen.kt # app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsLibraryScreen.kt # app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsMainScreen.kt # app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsReaderScreen.kt # app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsSecurityScreen.kt # app/src/main/java/eu/kanade/presentation/more/settings/screen/about/AboutScreen.kt # app/src/main/java/eu/kanade/presentation/more/settings/screen/advanced/ClearDatabaseScreen.kt # app/src/main/java/eu/kanade/presentation/more/settings/screen/data/CreateBackupScreen.kt # app/src/main/java/eu/kanade/presentation/reader/ReaderPageActionsDialog.kt # app/src/main/java/eu/kanade/presentation/reader/appbars/BottomReaderBar.kt # app/src/main/java/eu/kanade/presentation/reader/appbars/ReaderAppBars.kt # app/src/main/java/eu/kanade/presentation/reader/components/ChapterNavigator.kt # app/src/main/java/eu/kanade/presentation/reader/settings/GeneralSettingsPage.kt # app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadManager.kt # app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt # app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateJob.kt # app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateNotifier.kt # app/src/main/java/eu/kanade/tachiyomi/ui/browse/BrowseTab.kt # app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/MigrateDialog.kt # app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/SourceSearchScreen.kt # app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/sources/MigrateSourceTab.kt # app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/SourcesTab.kt # app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreen.kt # app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceFilterDialog.kt # app/src/main/java/eu/kanade/tachiyomi/ui/history/HistoryTab.kt # app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt # app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt # app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt # app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt # app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ChapterLoader.kt # app/src/main/java/eu/kanade/tachiyomi/ui/stats/StatsScreen.kt # app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesTab.kt
2023-11-19 01:41:33 +01:00
override val name: String = context.stringResource(MR.strings.local_source)
override val id: Long = ID
override val lang: String = "other"
override fun toString() = name
override val supportsLatest: Boolean = true
// Browse related
override suspend fun getPopularManga(page: Int) = getSearchManga(page, "", POPULAR_FILTERS)
override suspend fun getLatestUpdates(page: Int) = getSearchManga(page, "", LATEST_FILTERS)
override suspend fun getSearchManga(page: Int, query: String, filters: FilterList): MangasPage = withIOContext {
val lastModifiedLimit = if (filters === LATEST_FILTERS) {
System.currentTimeMillis() - LATEST_THRESHOLD
} else {
0L
}
// SY -->
val allowLocalSourceHiddenFolders = allowHiddenFiles()
// SY <--
var mangaDirs = fileSystem.getFilesInBaseDirectory()
// Filter out files that are hidden and is not a folder
.filter {
it.isDirectory &&
/* SY --> */ (
!it.name.orEmpty().startsWith('.') ||
allowLocalSourceHiddenFolders
) /* SY <-- */
}
2020-04-25 20:24:45 +02:00
.distinctBy { it.name }
.filter {
if (lastModifiedLimit == 0L && query.isBlank()) {
true
} else if (lastModifiedLimit == 0L) {
it.name.orEmpty().contains(query, ignoreCase = true)
} else {
it.lastModified() >= lastModifiedLimit
}
}
filters.forEach { filter ->
when (filter) {
is OrderBy.Popular -> {
mangaDirs = if (filter.state!!.ascending) {
mangaDirs.sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER) { it.name.orEmpty() })
} else {
mangaDirs.sortedWith(compareByDescending(String.CASE_INSENSITIVE_ORDER) { it.name.orEmpty() })
}
}
is OrderBy.Latest -> {
mangaDirs = if (filter.state!!.ascending) {
mangaDirs.sortedBy(UniFile::lastModified)
} else {
mangaDirs.sortedByDescending(UniFile::lastModified)
}
2020-04-25 20:24:45 +02:00
}
else -> {
/* Do nothing */
}
}
}
val mangas = mangaDirs
.map { mangaDir ->
async {
SManga.create().apply {
title = mangaDir.name.orEmpty()
url = mangaDir.name.orEmpty()
// Try to find the cover
coverManager.find(mangaDir.name.orEmpty())?.let {
thumbnail_url = it.uri.toString()
}
}
}
}
.awaitAll()
MangasPage(mangas, false)
}
// SY -->
fun updateMangaInfo(manga: SManga) {
val mangaDirFiles = fileSystem.getFilesInMangaDirectory(manga.url)
val existingFile = mangaDirFiles
.firstOrNull { it.name == COMIC_INFO_FILE }
val comicInfoArchiveFile = mangaDirFiles
.firstOrNull { it.name == COMIC_INFO_ARCHIVE }
val existingComicInfo = (existingFile?.openInputStream() ?: comicInfoArchiveFile?.getZipInputStream(COMIC_INFO_FILE))?.use {
AndroidXmlReader(it, StandardCharsets.UTF_8.name()).use {
xml.decodeFromReader<ComicInfo>(it)
}
}
val newComicInfo = if (existingComicInfo != null) {
manga.run {
existingComicInfo.copy(
series = ComicInfo.Series(title),
summary = description?.let { ComicInfo.Summary(it) },
writer = author?.let { ComicInfo.Writer(it) },
penciller = artist?.let { ComicInfo.Penciller(it) },
genre = genre?.let { ComicInfo.Genre(it) },
publishingStatus = ComicInfo.PublishingStatusTachiyomi(
ComicInfoPublishingStatus.toComicInfoValue(status.toLong()),
),
)
}
} else {
manga.getComicInfo()
}
fileSystem.getMangaDirectory(manga.url)?.let {
copyComicInfoFile(
xml.encodeToString(ComicInfo.serializer(), newComicInfo).byteInputStream(),
it
)
}
}
// SY <--
// Manga details related
override suspend fun getMangaDetails(manga: SManga): SManga = withIOContext {
coverManager.find(manga.url)?.let {
manga.thumbnail_url = it.uri.toString()
}
// Augment manga details based on metadata files
try {
val mangaDir by lazy { fileSystem.getMangaDirectory(manga.url) }
val mangaDirFiles = fileSystem.getFilesInMangaDirectory(manga.url)
val comicInfoFile = mangaDirFiles
.firstOrNull { it.name == COMIC_INFO_FILE }
val noXmlFile = mangaDirFiles
.firstOrNull { it.name == ".noxml" }
val legacyJsonDetailsFile = mangaDirFiles
.firstOrNull { it.extension == "json" }
// SY -->
val comicInfoArchiveFile = mangaDirFiles
.firstOrNull { it.name == COMIC_INFO_ARCHIVE }
// SY <--
when {
// Top level ComicInfo.xml
comicInfoFile != null -> {
noXmlFile?.delete()
setMangaDetailsFromComicInfoFile(comicInfoFile.openInputStream(), manga)
}
// SY -->
comicInfoArchiveFile != null -> {
noXmlFile?.delete()
comicInfoArchiveFile.getZipInputStream(COMIC_INFO_FILE)
?.let { setMangaDetailsFromComicInfoFile(it, manga) }
}
// SY <--
// Old custom JSON format
// TODO: remove support for this entirely after a while
legacyJsonDetailsFile != null -> {
json.decodeFromStream<MangaDetails>(legacyJsonDetailsFile.openInputStream()).run {
title?.let { manga.title = it }
author?.let { manga.author = it }
artist?.let { manga.artist = it }
description?.let { manga.description = it }
genre?.let { manga.genre = it.joinToString() }
status?.let { manga.status = it }
}
// Replace with ComicInfo.xml file
val comicInfo = manga.getComicInfo()
mangaDir
?.createFile(COMIC_INFO_FILE)
?.openOutputStream()
?.use {
val comicInfoString = xml.encodeToString(ComicInfo.serializer(), comicInfo)
it.write(comicInfoString.toByteArray())
legacyJsonDetailsFile.delete()
}
}
// Copy ComicInfo.xml from chapter archive to top level if found
noXmlFile == null -> {
val chapterArchives = mangaDirFiles
.filter(Archive::isSupported)
.toList()
val copiedFile = mangaDir?.let { copyComicInfoFileFromArchive(chapterArchives, it) }
// SY -->
if (copiedFile != null && copiedFile.name != COMIC_INFO_ARCHIVE) {
setMangaDetailsFromComicInfoFile(copiedFile.openInputStream(), manga)
} else if (copiedFile != null && copiedFile.name == COMIC_INFO_ARCHIVE) {
copiedFile.getZipInputStream(COMIC_INFO_FILE)?.let { setMangaDetailsFromComicInfoFile(it, manga) }
} // SY <--
else {
// Avoid re-scanning
mangaDir?.createFile(".noxml")
}
}
}
} catch (e: Throwable) {
logcat(LogPriority.ERROR, e) { "Error setting manga details from local metadata for ${manga.title}" }
}
return@withIOContext manga
}
private fun copyComicInfoFileFromArchive(chapterArchives: List<UniFile>, folder: UniFile): UniFile? {
for (chapter in chapterArchives) {
when (Format.valueOf(chapter)) {
is Format.Zip -> {
// SY -->
chapter.getZipInputStream(COMIC_INFO_FILE)?.buffered().use { stream ->
return stream?.let { copyComicInfoFile(it, folder) }
}
}
is Format.Rar -> {
2024-06-10 13:51:26 +02:00
val archive = JunrarArchive(chapter.openInputStream())
archive.use { rar ->
rar.fileHeaders.firstOrNull { it.fileName == COMIC_INFO_FILE }?.let { comicInfoFile ->
rar.getInputStream(comicInfoFile).buffered().use { stream ->
return copyComicInfoFile(stream, folder)
}
}
}
}
else -> {}
}
}
return null
}
private fun copyComicInfoFile(comicInfoFileStream: InputStream, folder: UniFile): UniFile? {
// SY -->
if (
CbzCrypto.getPasswordProtectDlPref() &&
CbzCrypto.isPasswordSet()
) {
val comicInfoArchive = folder.createFile(COMIC_INFO_ARCHIVE)
comicInfoArchive?.addStreamToZip(comicInfoFileStream, COMIC_INFO_FILE, CbzCrypto.getDecryptedPasswordCbz())
return comicInfoArchive
} else {
// SY <--
return folder.createFile(COMIC_INFO_FILE)?.apply {
openOutputStream().use { outputStream ->
comicInfoFileStream.use { it.copyTo(outputStream) }
}
}
}
}
private fun setMangaDetailsFromComicInfoFile(stream: InputStream, manga: SManga) {
val comicInfo = AndroidXmlReader(stream, StandardCharsets.UTF_8.name()).use {
xml.decodeFromReader<ComicInfo>(it)
}
2022-11-16 19:52:17 +01:00
manga.copyFromComicInfo(comicInfo)
}
2017-02-05 12:01:58 +01:00
// Chapters
override suspend fun getChapterList(manga: SManga): List<SChapter> = withIOContext {
val chapters = fileSystem.getFilesInMangaDirectory(manga.url)
// Only keep supported formats
.filter { it.isDirectory || Archive.isSupported(it) }
2020-04-25 20:24:45 +02:00
.map { chapterFile ->
SChapter.create().apply {
url = "${manga.url}/${chapterFile.name}"
name = if (chapterFile.isDirectory) {
chapterFile.name
2020-04-25 20:24:45 +02:00
} else {
chapterFile.nameWithoutExtension
}.orEmpty()
2020-04-25 20:24:45 +02:00
date_upload = chapterFile.lastModified()
chapter_number = ChapterRecognition
.parseChapterNumber(manga.title, this.name, this.chapter_number.toDouble())
.toFloat()
val format = Format.valueOf(chapterFile)
if (format is Format.Epub) {
EpubFile(format.file, context).use { epub ->
epub.fillMetadata(manga, this)
}
}
2017-02-05 12:01:58 +01:00
}
2020-04-25 20:24:45 +02:00
}
.sortedWith { c1, c2 ->
val c = c2.chapter_number.compareTo(c1.chapter_number)
if (c == 0) c2.name.compareToCaseInsensitiveNaturalOrder(c1.name) else c
}
// Copy the cover from the first chapter found if not available
if (manga.thumbnail_url.isNullOrBlank()) {
chapters.lastOrNull()?.let { chapter ->
updateCover(chapter, manga)
}
}
chapters
2017-02-05 12:01:58 +01:00
}
// Filters
override fun getFilterList() = FilterList(OrderBy.Popular(context))
// Unused stuff
override suspend fun getPageList(chapter: SChapter): List<Page> = throw UnsupportedOperationException("Unused")
fun getFormat(chapter: SChapter): Format {
try {
val (mangaDirName, chapterName) = chapter.url.split('/', limit = 2)
return fileSystem.getBaseDirectory()
?.findFile(mangaDirName)
?.findFile(chapterName)
?.let(Format.Companion::valueOf)
Replace remaining Android-specific strings Also renaming the helper composables so it's a bit easier to find/replace everything in forks. (cherry picked from commit 0d1bced1226878336f8156671ae0c224c02f2604) # Conflicts: # app/src/main/java/eu/kanade/presentation/browse/ExtensionDetailsScreen.kt # app/src/main/java/eu/kanade/presentation/browse/ExtensionsScreen.kt # app/src/main/java/eu/kanade/presentation/browse/SourcesScreen.kt # app/src/main/java/eu/kanade/presentation/browse/components/BrowseSourceToolbar.kt # app/src/main/java/eu/kanade/presentation/category/components/CategoryDialogs.kt # app/src/main/java/eu/kanade/presentation/library/LibrarySettingsDialog.kt # app/src/main/java/eu/kanade/presentation/manga/components/MangaBottomActionMenu.kt # app/src/main/java/eu/kanade/presentation/manga/components/MangaDialogs.kt # app/src/main/java/eu/kanade/presentation/more/MoreScreen.kt # app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAdvancedScreen.kt # app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsBrowseScreen.kt # app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsLibraryScreen.kt # app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsMainScreen.kt # app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsReaderScreen.kt # app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsSecurityScreen.kt # app/src/main/java/eu/kanade/presentation/more/settings/screen/about/AboutScreen.kt # app/src/main/java/eu/kanade/presentation/more/settings/screen/advanced/ClearDatabaseScreen.kt # app/src/main/java/eu/kanade/presentation/more/settings/screen/data/CreateBackupScreen.kt # app/src/main/java/eu/kanade/presentation/reader/ReaderPageActionsDialog.kt # app/src/main/java/eu/kanade/presentation/reader/appbars/BottomReaderBar.kt # app/src/main/java/eu/kanade/presentation/reader/appbars/ReaderAppBars.kt # app/src/main/java/eu/kanade/presentation/reader/components/ChapterNavigator.kt # app/src/main/java/eu/kanade/presentation/reader/settings/GeneralSettingsPage.kt # app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadManager.kt # app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt # app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateJob.kt # app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateNotifier.kt # app/src/main/java/eu/kanade/tachiyomi/ui/browse/BrowseTab.kt # app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/MigrateDialog.kt # app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/SourceSearchScreen.kt # app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/sources/MigrateSourceTab.kt # app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/SourcesTab.kt # app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreen.kt # app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceFilterDialog.kt # app/src/main/java/eu/kanade/tachiyomi/ui/history/HistoryTab.kt # app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt # app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt # app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt # app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt # app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ChapterLoader.kt # app/src/main/java/eu/kanade/tachiyomi/ui/stats/StatsScreen.kt # app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesTab.kt
2023-11-19 01:41:33 +01:00
?: throw Exception(context.stringResource(MR.strings.chapter_not_found))
} catch (e: Format.UnknownFormatException) {
Replace remaining Android-specific strings Also renaming the helper composables so it's a bit easier to find/replace everything in forks. (cherry picked from commit 0d1bced1226878336f8156671ae0c224c02f2604) # Conflicts: # app/src/main/java/eu/kanade/presentation/browse/ExtensionDetailsScreen.kt # app/src/main/java/eu/kanade/presentation/browse/ExtensionsScreen.kt # app/src/main/java/eu/kanade/presentation/browse/SourcesScreen.kt # app/src/main/java/eu/kanade/presentation/browse/components/BrowseSourceToolbar.kt # app/src/main/java/eu/kanade/presentation/category/components/CategoryDialogs.kt # app/src/main/java/eu/kanade/presentation/library/LibrarySettingsDialog.kt # app/src/main/java/eu/kanade/presentation/manga/components/MangaBottomActionMenu.kt # app/src/main/java/eu/kanade/presentation/manga/components/MangaDialogs.kt # app/src/main/java/eu/kanade/presentation/more/MoreScreen.kt # app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAdvancedScreen.kt # app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsBrowseScreen.kt # app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsLibraryScreen.kt # app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsMainScreen.kt # app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsReaderScreen.kt # app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsSecurityScreen.kt # app/src/main/java/eu/kanade/presentation/more/settings/screen/about/AboutScreen.kt # app/src/main/java/eu/kanade/presentation/more/settings/screen/advanced/ClearDatabaseScreen.kt # app/src/main/java/eu/kanade/presentation/more/settings/screen/data/CreateBackupScreen.kt # app/src/main/java/eu/kanade/presentation/reader/ReaderPageActionsDialog.kt # app/src/main/java/eu/kanade/presentation/reader/appbars/BottomReaderBar.kt # app/src/main/java/eu/kanade/presentation/reader/appbars/ReaderAppBars.kt # app/src/main/java/eu/kanade/presentation/reader/components/ChapterNavigator.kt # app/src/main/java/eu/kanade/presentation/reader/settings/GeneralSettingsPage.kt # app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadManager.kt # app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt # app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateJob.kt # app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateNotifier.kt # app/src/main/java/eu/kanade/tachiyomi/ui/browse/BrowseTab.kt # app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/MigrateDialog.kt # app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/SourceSearchScreen.kt # app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/sources/MigrateSourceTab.kt # app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/SourcesTab.kt # app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreen.kt # app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceFilterDialog.kt # app/src/main/java/eu/kanade/tachiyomi/ui/history/HistoryTab.kt # app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt # app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt # app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt # app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt # app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ChapterLoader.kt # app/src/main/java/eu/kanade/tachiyomi/ui/stats/StatsScreen.kt # app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesTab.kt
2023-11-19 01:41:33 +01:00
throw Exception(context.stringResource(MR.strings.local_invalid_format))
} catch (e: Exception) {
throw e
2017-02-05 12:01:58 +01:00
}
}
private fun updateCover(chapter: SChapter, manga: SManga): UniFile? {
return try {
when (val format = getFormat(chapter)) {
is Format.Directory -> {
val entry = format.file.listFiles()
?.sortedWith { f1, f2 ->
f1.name.orEmpty().compareToCaseInsensitiveNaturalOrder(
f2.name.orEmpty(),
)
}
?.find {
!it.isDirectory && ImageUtil.isImage(it.name) { it.openInputStream() }
}
entry?.let { coverManager.update(manga, it.openInputStream()) }
}
is Format.Zip -> {
// SY -->
format.file.getCoverStreamFromZip()?.let { inputStream ->
coverManager.update(
manga,
inputStream,
format.file.isEncryptedZip()
)
}
}
is Format.Rar -> {
2024-06-10 13:51:26 +02:00
val rarArchive = JunrarArchive(format.file.openInputStream())
rarArchive.use { archive ->
// SY <--
val entry = archive.fileHeaders
.sortedWith { f1, f2 -> f1.fileName.compareToCaseInsensitiveNaturalOrder(f2.fileName) }
.find { !it.isDirectory && ImageUtil.isImage(it.fileName) { archive.getInputStream(it) } }
2017-02-08 22:12:00 +01:00
entry?.let { coverManager.update(manga, archive.getInputStream(it)) }
}
}
is Format.Epub -> {
// SY -->
EpubFile(format.file, context).use { epub ->
// SY <--
val entry = epub.getImagesFromPages()
.firstOrNull()
?.let { epub.getEntry(it) }
2017-02-05 12:01:58 +01:00
entry?.let { coverManager.update(manga, epub.getInputStream(it)) }
}
}
2017-02-05 12:01:58 +01:00
}
} catch (e: Throwable) {
logcat(LogPriority.ERROR, e) { "Error updating cover for ${manga.title}" }
null
2017-02-05 12:01:58 +01:00
}
}
2017-02-05 12:01:58 +01:00
companion object {
const val ID = 0L
const val HELP_URL = "https://mihon.app/docs/guides/local-source/"
// SY -->
const val COMIC_INFO_ARCHIVE = "ComicInfo.cbm"
// SY <--
private val LATEST_THRESHOLD = 7.days.inWholeMilliseconds
}
}
fun Manga.isLocal(): Boolean = source == LocalSource.ID
fun Source.isLocal(): Boolean = id == LocalSource.ID
fun DomainSource.isLocal(): Boolean = id == LocalSource.ID