Rewrite Migrations (#577)
* Rewrite Migrations * Fix Detekt errors * Do migrations synchronous * Filter and sort migrations * Review changes * Review changes 2 * Fix Detekt errors (cherry picked from commit 666d6aa117756f0a9a57b31f91b7acb0ee5d7409) # Conflicts: # app/src/main/java/eu/kanade/tachiyomi/Migrations.kt # app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt
This commit is contained in:
parent
ceff887a10
commit
90d5104bdc
@ -1,71 +0,0 @@
|
||||
package eu.kanade.tachiyomi
|
||||
|
||||
import android.content.Context
|
||||
import eu.kanade.domain.source.service.SourcePreferences
|
||||
import eu.kanade.tachiyomi.data.backup.create.BackupCreateJob
|
||||
import eu.kanade.tachiyomi.data.library.LibraryUpdateJob
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import logcat.LogPriority
|
||||
import mihon.domain.extensionrepo.exception.SaveExtensionRepoException
|
||||
import mihon.domain.extensionrepo.repository.ExtensionRepoRepository
|
||||
import tachiyomi.core.common.preference.Preference
|
||||
import tachiyomi.core.common.preference.PreferenceStore
|
||||
import tachiyomi.core.common.util.lang.launchIO
|
||||
import tachiyomi.core.common.util.system.logcat
|
||||
|
||||
object Migrations {
|
||||
|
||||
// TODO NATIVE TACHIYOMI MIGRATIONS ARE FUCKED UP DUE TO DIFFERING VERSION NUMBERS
|
||||
|
||||
/**
|
||||
* Performs a migration when the application is updated.
|
||||
*
|
||||
* @return true if a migration is performed, false otherwise.
|
||||
*/
|
||||
@Suppress("SameReturnValue", "MagicNumber")
|
||||
fun upgrade(
|
||||
context: Context,
|
||||
preferenceStore: PreferenceStore,
|
||||
sourcePreferences: SourcePreferences,
|
||||
extensionRepoRepository: ExtensionRepoRepository,
|
||||
): Boolean {
|
||||
val lastVersionCode = preferenceStore.getInt(Preference.appStateKey("last_version_code"), 0)
|
||||
val oldVersion = lastVersionCode.get()
|
||||
if (oldVersion < BuildConfig.VERSION_CODE) {
|
||||
lastVersionCode.set(BuildConfig.VERSION_CODE)
|
||||
|
||||
// Always set up background tasks to ensure they're running
|
||||
LibraryUpdateJob.setupTask(context)
|
||||
BackupCreateJob.setupTask(context)
|
||||
|
||||
// Fresh install
|
||||
if (oldVersion == 0) {
|
||||
return false
|
||||
}
|
||||
|
||||
val coroutineScope = CoroutineScope(Dispatchers.IO)
|
||||
|
||||
if (oldVersion < 6) {
|
||||
coroutineScope.launchIO {
|
||||
for ((index, source) in sourcePreferences.extensionRepos().get().withIndex()) {
|
||||
try {
|
||||
extensionRepoRepository.upsertRepo(
|
||||
source,
|
||||
"Repo #${index + 1}",
|
||||
null,
|
||||
source,
|
||||
"NOFINGERPRINT-${index + 1}",
|
||||
)
|
||||
} catch (e: SaveExtensionRepoException) {
|
||||
logcat(LogPriority.ERROR, e) { "Error Migrating Extension Repo with baseUrl: $source" }
|
||||
}
|
||||
}
|
||||
sourcePreferences.extensionRepos().delete()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
@ -50,8 +50,6 @@ import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
||||
import com.google.firebase.analytics.ktx.analytics
|
||||
import com.google.firebase.ktx.Firebase
|
||||
import eu.kanade.domain.base.BasePreferences
|
||||
import eu.kanade.domain.source.service.SourcePreferences
|
||||
import eu.kanade.domain.ui.UiPreferences
|
||||
import eu.kanade.presentation.components.AppStateBanners
|
||||
import eu.kanade.presentation.components.DownloadedOnlyBannerBackgroundColor
|
||||
import eu.kanade.presentation.components.IncognitoModeBannerBackgroundColor
|
||||
@ -80,7 +78,6 @@ import eu.kanade.tachiyomi.util.system.dpToPx
|
||||
import eu.kanade.tachiyomi.util.system.isNavigationBarNeedsScrim
|
||||
import eu.kanade.tachiyomi.util.system.isPreviewBuildType
|
||||
import eu.kanade.tachiyomi.util.view.setComposeContent
|
||||
import exh.EXHMigrations
|
||||
import exh.debug.DebugToggles
|
||||
import exh.eh.EHentaiUpdateWorker
|
||||
import exh.log.DebugModeOverlay
|
||||
@ -97,7 +94,11 @@ import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.launch
|
||||
import logcat.LogPriority
|
||||
import mihon.core.migration.Migrator
|
||||
import mihon.core.migration.migrations.migrations
|
||||
import tachiyomi.core.common.Constants
|
||||
import tachiyomi.core.common.preference.Preference
|
||||
import tachiyomi.core.common.preference.PreferenceStore
|
||||
import tachiyomi.core.common.util.lang.launchIO
|
||||
import tachiyomi.core.common.util.system.logcat
|
||||
import tachiyomi.domain.UnsortedPreferences
|
||||
@ -113,9 +114,7 @@ import androidx.compose.ui.graphics.Color.Companion as ComposeColor
|
||||
|
||||
class MainActivity : BaseActivity() {
|
||||
|
||||
private val sourcePreferences: SourcePreferences by injectLazy()
|
||||
private val libraryPreferences: LibraryPreferences by injectLazy()
|
||||
private val uiPreferences: UiPreferences by injectLazy()
|
||||
private val preferences: BasePreferences by injectLazy()
|
||||
|
||||
// SY -->
|
||||
@ -165,21 +164,7 @@ class MainActivity : BaseActivity() {
|
||||
|
||||
val didMigration = if (isLaunch) {
|
||||
addAnalytics()
|
||||
EXHMigrations.upgrade(
|
||||
context = applicationContext,
|
||||
basePreferences = preferences,
|
||||
uiPreferences = uiPreferences,
|
||||
preferenceStore = Injekt.get(),
|
||||
networkPreferences = Injekt.get(),
|
||||
sourcePreferences = sourcePreferences,
|
||||
securityPreferences = Injekt.get(),
|
||||
libraryPreferences = libraryPreferences,
|
||||
readerPreferences = Injekt.get(),
|
||||
backupPreferences = Injekt.get(),
|
||||
trackerManager = Injekt.get(),
|
||||
pagePreviewCache = Injekt.get(),
|
||||
extensionRepoRepository = Injekt.get(),
|
||||
)
|
||||
migrate()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
@ -423,6 +408,23 @@ class MainActivity : BaseActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun migrate(): Boolean {
|
||||
val preferenceStore = Injekt.get<PreferenceStore>()
|
||||
// SY -->
|
||||
val preference = preferenceStore.getInt(Preference.appStateKey("eh_last_version_code"), 0)
|
||||
// SY <--
|
||||
logcat { "Migration from ${preference.get()} to ${BuildConfig.VERSION_CODE}" }
|
||||
return Migrator.migrate(
|
||||
old = preference.get(),
|
||||
new = BuildConfig.VERSION_CODE,
|
||||
migrations = migrations,
|
||||
onMigrationComplete = {
|
||||
logcat { "Updating last version to ${BuildConfig.VERSION_CODE}" }
|
||||
preference.set(BuildConfig.VERSION_CODE)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets custom splash screen exit animation on devices prior to Android 12.
|
||||
*
|
||||
|
@ -1,723 +1,15 @@
|
||||
package exh
|
||||
|
||||
import android.content.Context
|
||||
import android.widget.Toast
|
||||
import androidx.core.content.edit
|
||||
import androidx.preference.PreferenceManager
|
||||
import eu.kanade.domain.base.BasePreferences
|
||||
import eu.kanade.domain.manga.interactor.UpdateManga
|
||||
import eu.kanade.domain.source.service.SourcePreferences
|
||||
import eu.kanade.domain.ui.UiPreferences
|
||||
import eu.kanade.tachiyomi.BuildConfig
|
||||
import eu.kanade.tachiyomi.core.security.SecurityPreferences
|
||||
import eu.kanade.tachiyomi.data.backup.create.BackupCreateJob
|
||||
import eu.kanade.tachiyomi.data.cache.PagePreviewCache
|
||||
import eu.kanade.tachiyomi.data.library.LibraryUpdateJob
|
||||
import eu.kanade.tachiyomi.data.sync.SyncDataJob
|
||||
import eu.kanade.tachiyomi.data.track.TrackerManager
|
||||
import eu.kanade.tachiyomi.network.NetworkPreferences
|
||||
import eu.kanade.tachiyomi.network.PREF_DOH_CLOUDFLARE
|
||||
import eu.kanade.tachiyomi.source.Source
|
||||
import eu.kanade.tachiyomi.source.online.all.NHentai
|
||||
import eu.kanade.tachiyomi.ui.reader.setting.ReaderOrientation
|
||||
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
|
||||
import eu.kanade.tachiyomi.util.system.DeviceUtil
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
import eu.kanade.tachiyomi.util.system.workManager
|
||||
import exh.eh.EHentaiUpdateWorker
|
||||
import exh.log.xLogE
|
||||
import exh.source.BlacklistedSources
|
||||
import exh.source.EH_SOURCE_ID
|
||||
import exh.source.HBROWSE_SOURCE_ID
|
||||
import exh.source.MERGED_SOURCE_ID
|
||||
import exh.source.TSUMINO_SOURCE_ID
|
||||
import exh.util.nullIfBlank
|
||||
import exh.util.under
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import kotlinx.serialization.json.contentOrNull
|
||||
import kotlinx.serialization.json.jsonArray
|
||||
import kotlinx.serialization.json.jsonPrimitive
|
||||
import logcat.LogPriority
|
||||
import mihon.domain.extensionrepo.exception.SaveExtensionRepoException
|
||||
import mihon.domain.extensionrepo.repository.ExtensionRepoRepository
|
||||
import tachiyomi.core.common.preference.Preference
|
||||
import tachiyomi.core.common.preference.PreferenceStore
|
||||
import tachiyomi.core.common.preference.TriState
|
||||
import tachiyomi.core.common.preference.getAndSet
|
||||
import tachiyomi.core.common.preference.getEnum
|
||||
import tachiyomi.core.common.preference.minusAssign
|
||||
import tachiyomi.core.common.util.system.logcat
|
||||
import tachiyomi.data.DatabaseHandler
|
||||
import tachiyomi.data.category.CategoryMapper
|
||||
import tachiyomi.data.chapter.ChapterMapper
|
||||
import tachiyomi.domain.backup.service.BackupPreferences
|
||||
import tachiyomi.domain.chapter.interactor.DeleteChapters
|
||||
import tachiyomi.domain.chapter.interactor.UpdateChapter
|
||||
import tachiyomi.domain.chapter.model.ChapterUpdate
|
||||
import tachiyomi.domain.library.service.LibraryPreferences
|
||||
import tachiyomi.domain.library.service.LibraryPreferences.Companion.MANGA_NON_COMPLETED
|
||||
import tachiyomi.domain.manga.interactor.GetManga
|
||||
import tachiyomi.domain.manga.interactor.GetMangaBySource
|
||||
import tachiyomi.domain.manga.interactor.InsertMergedReference
|
||||
import tachiyomi.domain.manga.model.Manga
|
||||
import tachiyomi.domain.manga.model.MangaUpdate
|
||||
import tachiyomi.domain.manga.model.MergedMangaReference
|
||||
import tachiyomi.domain.source.interactor.InsertFeedSavedSearch
|
||||
import tachiyomi.domain.source.interactor.InsertSavedSearch
|
||||
import tachiyomi.domain.source.model.FeedSavedSearch
|
||||
import tachiyomi.domain.source.model.SavedSearch
|
||||
import tachiyomi.domain.source.service.SourceManager
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.io.File
|
||||
import java.net.URI
|
||||
import java.net.URISyntaxException
|
||||
|
||||
object EXHMigrations {
|
||||
private val handler: DatabaseHandler by injectLazy()
|
||||
private val sourceManager: SourceManager by injectLazy()
|
||||
private val getManga: GetManga by injectLazy()
|
||||
private val getMangaBySource: GetMangaBySource by injectLazy()
|
||||
private val updateManga: UpdateManga by injectLazy()
|
||||
private val updateChapter: UpdateChapter by injectLazy()
|
||||
private val deleteChapters: DeleteChapters by injectLazy()
|
||||
private val insertMergedReference: InsertMergedReference by injectLazy()
|
||||
private val insertSavedSearch: InsertSavedSearch by injectLazy()
|
||||
private val insertFeedSavedSearch: InsertFeedSavedSearch by injectLazy()
|
||||
|
||||
/**
|
||||
* Performs a migration when the application is updated.
|
||||
*
|
||||
* @return true if a migration is performed, false otherwise.
|
||||
*/
|
||||
fun upgrade(
|
||||
context: Context,
|
||||
preferenceStore: PreferenceStore,
|
||||
basePreferences: BasePreferences,
|
||||
uiPreferences: UiPreferences,
|
||||
networkPreferences: NetworkPreferences,
|
||||
sourcePreferences: SourcePreferences,
|
||||
securityPreferences: SecurityPreferences,
|
||||
libraryPreferences: LibraryPreferences,
|
||||
readerPreferences: ReaderPreferences,
|
||||
backupPreferences: BackupPreferences,
|
||||
trackerManager: TrackerManager,
|
||||
pagePreviewCache: PagePreviewCache,
|
||||
extensionRepoRepository: ExtensionRepoRepository,
|
||||
): Boolean {
|
||||
val lastVersionCode = preferenceStore.getInt("eh_last_version_code", 0)
|
||||
val oldVersion = lastVersionCode.get()
|
||||
try {
|
||||
if (oldVersion < BuildConfig.VERSION_CODE) {
|
||||
lastVersionCode.set(BuildConfig.VERSION_CODE)
|
||||
|
||||
LibraryUpdateJob.setupTask(context)
|
||||
BackupCreateJob.setupTask(context)
|
||||
EHentaiUpdateWorker.scheduleBackground(context)
|
||||
SyncDataJob.setupTask(context)
|
||||
|
||||
// Fresh install
|
||||
if (oldVersion == 0) {
|
||||
return false
|
||||
}
|
||||
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
if (oldVersion under 4) {
|
||||
updateSourceId(HBROWSE_SOURCE_ID, 6912)
|
||||
// Migrate BHrowse URLs
|
||||
val hBrowseManga = runBlocking { getMangaBySource.await(HBROWSE_SOURCE_ID) }
|
||||
val mangaUpdates = hBrowseManga.map {
|
||||
MangaUpdate(it.id, url = it.url + "/c00001/")
|
||||
}
|
||||
|
||||
runBlocking {
|
||||
updateManga.awaitAll(mangaUpdates)
|
||||
}
|
||||
}
|
||||
if (oldVersion under 6) {
|
||||
updateSourceId(NHentai.otherId, 6907)
|
||||
}
|
||||
if (oldVersion under 7) {
|
||||
val mergedMangas = runBlocking { getMangaBySource.await(MERGED_SOURCE_ID) }
|
||||
|
||||
if (mergedMangas.isNotEmpty()) {
|
||||
val mangaConfigs = mergedMangas.mapNotNull { mergedManga ->
|
||||
readMangaConfig(mergedManga)?.let { mergedManga to it }
|
||||
}
|
||||
if (mangaConfigs.isNotEmpty()) {
|
||||
val mangaToUpdate = mutableListOf<MangaUpdate>()
|
||||
val mergedMangaReferences = mutableListOf<MergedMangaReference>()
|
||||
mangaConfigs.onEach { mergedManga ->
|
||||
val newFirst = mergedManga.second.children.firstOrNull()?.url?.let {
|
||||
if (runBlocking { getManga.await(it, MERGED_SOURCE_ID) } != null) return@onEach
|
||||
mangaToUpdate += MangaUpdate(id = mergedManga.first.id, url = it)
|
||||
mergedManga.first.copy(url = it)
|
||||
} ?: mergedManga.first
|
||||
mergedMangaReferences += MergedMangaReference(
|
||||
id = -1,
|
||||
isInfoManga = false,
|
||||
getChapterUpdates = false,
|
||||
chapterSortMode = 0,
|
||||
chapterPriority = 0,
|
||||
downloadChapters = false,
|
||||
mergeId = newFirst.id,
|
||||
mergeUrl = newFirst.url,
|
||||
mangaId = newFirst.id,
|
||||
mangaUrl = newFirst.url,
|
||||
mangaSourceId = MERGED_SOURCE_ID,
|
||||
)
|
||||
mergedManga.second.children.distinct().forEachIndexed { index, mangaSource ->
|
||||
val load = mangaSource.load() ?: return@forEachIndexed
|
||||
mergedMangaReferences += MergedMangaReference(
|
||||
id = -1,
|
||||
isInfoManga = index == 0,
|
||||
getChapterUpdates = true,
|
||||
chapterSortMode = 0,
|
||||
chapterPriority = 0,
|
||||
downloadChapters = true,
|
||||
mergeId = newFirst.id,
|
||||
mergeUrl = newFirst.url,
|
||||
mangaId = load.manga.id,
|
||||
mangaUrl = load.manga.url,
|
||||
mangaSourceId = load.source.id,
|
||||
)
|
||||
}
|
||||
}
|
||||
runBlocking {
|
||||
updateManga.awaitAll(mangaToUpdate)
|
||||
insertMergedReference.awaitAll(mergedMangaReferences)
|
||||
}
|
||||
|
||||
val loadedMangaList = mangaConfigs
|
||||
.map { it.second.children }
|
||||
.flatten()
|
||||
.mapNotNull { it.load() }
|
||||
.distinct()
|
||||
val chapters =
|
||||
runBlocking {
|
||||
handler.awaitList {
|
||||
ehQueries.getChaptersByMangaIds(
|
||||
mergedMangas.map { it.id },
|
||||
ChapterMapper::mapChapter,
|
||||
)
|
||||
}
|
||||
}
|
||||
val mergedMangaChapters =
|
||||
runBlocking {
|
||||
handler.awaitList {
|
||||
ehQueries.getChaptersByMangaIds(
|
||||
loadedMangaList.map { it.manga.id },
|
||||
ChapterMapper::mapChapter,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val mergedMangaChaptersMatched = mergedMangaChapters.mapNotNull { chapter ->
|
||||
loadedMangaList.firstOrNull {
|
||||
it.manga.id == chapter.id
|
||||
}?.let { it to chapter }
|
||||
}
|
||||
val parsedChapters = chapters.filter {
|
||||
it.read || it.lastPageRead != 0L
|
||||
}.mapNotNull { chapter -> readUrlConfig(chapter.url)?.let { chapter to it } }
|
||||
val chaptersToUpdate = mutableListOf<ChapterUpdate>()
|
||||
parsedChapters.forEach { parsedChapter ->
|
||||
mergedMangaChaptersMatched.firstOrNull {
|
||||
it.second.url == parsedChapter.second.url &&
|
||||
it.first.source.id == parsedChapter.second.source &&
|
||||
it.first.manga.url == parsedChapter.second.mangaUrl
|
||||
}?.let {
|
||||
chaptersToUpdate += ChapterUpdate(
|
||||
it.second.id,
|
||||
read = parsedChapter.first.read,
|
||||
lastPageRead = parsedChapter.first.lastPageRead,
|
||||
)
|
||||
}
|
||||
}
|
||||
runBlocking {
|
||||
deleteChapters.await(mergedMangaChapters.map { it.id })
|
||||
updateChapter.awaitAll(chaptersToUpdate)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (oldVersion under 12) {
|
||||
// Force MAL log out due to login flow change
|
||||
trackerManager.myAnimeList.logout()
|
||||
}
|
||||
if (oldVersion under 14) {
|
||||
// Migrate DNS over HTTPS setting
|
||||
val wasDohEnabled = prefs.getBoolean("enable_doh", false)
|
||||
if (wasDohEnabled) {
|
||||
prefs.edit {
|
||||
putInt(networkPreferences.dohProvider().key(), PREF_DOH_CLOUDFLARE)
|
||||
remove("enable_doh")
|
||||
}
|
||||
}
|
||||
}
|
||||
if (oldVersion under 16) {
|
||||
// Reset rotation to Free after replacing Lock
|
||||
if (prefs.contains("pref_rotation_type_key")) {
|
||||
prefs.edit {
|
||||
putInt("pref_rotation_type_key", 1)
|
||||
}
|
||||
}
|
||||
// Disable update check for Android 5.x users
|
||||
// if (BuildConfig.INCLUDE_UPDATER && Build.VERSION.SDK_INT under Build.VERSION_CODES.M) {
|
||||
// UpdaterJob.cancelTask(context)
|
||||
// }
|
||||
}
|
||||
if (oldVersion under 17) {
|
||||
// Migrate Rotation and Viewer values to default values for viewer_flags
|
||||
val newOrientation = when (prefs.getInt("pref_rotation_type_key", 1)) {
|
||||
1 -> ReaderOrientation.FREE.flagValue
|
||||
2 -> ReaderOrientation.PORTRAIT.flagValue
|
||||
3 -> ReaderOrientation.LANDSCAPE.flagValue
|
||||
4 -> ReaderOrientation.LOCKED_PORTRAIT.flagValue
|
||||
5 -> ReaderOrientation.LOCKED_LANDSCAPE.flagValue
|
||||
else -> ReaderOrientation.FREE.flagValue
|
||||
}
|
||||
|
||||
// Reading mode flag and prefValue is the same value
|
||||
val newReadingMode = prefs.getInt("pref_default_viewer_key", 1)
|
||||
|
||||
prefs.edit {
|
||||
putInt("pref_default_orientation_type_key", newOrientation)
|
||||
remove("pref_rotation_type_key")
|
||||
putInt("pref_default_reading_mode_key", newReadingMode)
|
||||
remove("pref_default_viewer_key")
|
||||
}
|
||||
|
||||
// Delete old mangadex trackers
|
||||
runBlocking {
|
||||
handler.await { ehQueries.deleteBySyncId(6) }
|
||||
}
|
||||
}
|
||||
if (oldVersion under 18) {
|
||||
val readerTheme = readerPreferences.readerTheme().get()
|
||||
if (readerTheme == 4) {
|
||||
readerPreferences.readerTheme().set(3)
|
||||
}
|
||||
val updateInterval = libraryPreferences.autoUpdateInterval().get()
|
||||
if (updateInterval == 1 || updateInterval == 2) {
|
||||
libraryPreferences.autoUpdateInterval().set(3)
|
||||
}
|
||||
}
|
||||
if (oldVersion under 20) {
|
||||
try {
|
||||
val oldSortingMode = prefs.getInt(libraryPreferences.sortingMode().key(), 0 /* ALPHABETICAL */)
|
||||
val oldSortingDirection = prefs.getBoolean("library_sorting_ascending", true)
|
||||
|
||||
val newSortingMode = when (oldSortingMode) {
|
||||
0 -> "ALPHABETICAL"
|
||||
1 -> "LAST_READ"
|
||||
2 -> "LAST_MANGA_UPDATE"
|
||||
3 -> "UNREAD_COUNT"
|
||||
4 -> "TOTAL_CHAPTERS"
|
||||
6 -> "LATEST_CHAPTER"
|
||||
7 -> "DRAG_AND_DROP"
|
||||
8 -> "DATE_ADDED"
|
||||
9 -> "TAG_LIST"
|
||||
10 -> "CHAPTER_FETCH_DATE"
|
||||
else -> "ALPHABETICAL"
|
||||
}
|
||||
|
||||
val newSortingDirection = when (oldSortingDirection) {
|
||||
true -> "ASCENDING"
|
||||
else -> "DESCENDING"
|
||||
}
|
||||
|
||||
prefs.edit(commit = true) {
|
||||
remove(libraryPreferences.sortingMode().key())
|
||||
remove("library_sorting_ascending")
|
||||
}
|
||||
|
||||
prefs.edit {
|
||||
putString(libraryPreferences.sortingMode().key(), newSortingMode)
|
||||
putString("library_sorting_ascending", newSortingDirection)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
logcat(throwable = e) { "Already done migration" }
|
||||
}
|
||||
}
|
||||
if (oldVersion under 22) {
|
||||
// Handle removed every 3, 4, 6, and 8 hour library updates
|
||||
val updateInterval = libraryPreferences.autoUpdateInterval().get()
|
||||
if (updateInterval in listOf(3, 4, 6, 8)) {
|
||||
libraryPreferences.autoUpdateInterval().set(12)
|
||||
}
|
||||
}
|
||||
if (oldVersion under 23) {
|
||||
val oldUpdateOngoingOnly = prefs.getBoolean("pref_update_only_non_completed_key", true)
|
||||
if (!oldUpdateOngoingOnly) {
|
||||
libraryPreferences.autoUpdateMangaRestrictions() -= MANGA_NON_COMPLETED
|
||||
}
|
||||
}
|
||||
if (oldVersion under 24) {
|
||||
try {
|
||||
sequenceOf(
|
||||
"fav-sync",
|
||||
"fav-sync.management",
|
||||
"fav-sync.lock",
|
||||
"fav-sync.note",
|
||||
).map {
|
||||
File(context.filesDir, it)
|
||||
}.filter(File::exists).forEach {
|
||||
if (it.isDirectory) {
|
||||
it.deleteRecursively()
|
||||
} else {
|
||||
it.delete()
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
xLogE("Failed to delete old favorites database", e)
|
||||
}
|
||||
}
|
||||
if (oldVersion under 27) {
|
||||
val oldSecureScreen = prefs.getBoolean("secure_screen", false)
|
||||
if (oldSecureScreen) {
|
||||
securityPreferences.secureScreen().set(SecurityPreferences.SecureScreenMode.ALWAYS)
|
||||
}
|
||||
if (
|
||||
DeviceUtil.isMiui &&
|
||||
basePreferences.extensionInstaller().get() == BasePreferences.ExtensionInstaller
|
||||
.PACKAGEINSTALLER
|
||||
) {
|
||||
basePreferences.extensionInstaller().set(BasePreferences.ExtensionInstaller.LEGACY)
|
||||
}
|
||||
}
|
||||
if (oldVersion under 28) {
|
||||
if (prefs.getString("pref_display_mode_library", null) == "NO_TITLE_GRID") {
|
||||
prefs.edit(commit = true) {
|
||||
putString("pref_display_mode_library", "COVER_ONLY_GRID")
|
||||
}
|
||||
}
|
||||
}
|
||||
if (oldVersion under 29) {
|
||||
if (prefs.getString("pref_display_mode_catalogue", null) == "NO_TITLE_GRID") {
|
||||
prefs.edit(commit = true) {
|
||||
putString("pref_display_mode_catalogue", "COMPACT_GRID")
|
||||
}
|
||||
}
|
||||
}
|
||||
if (oldVersion under 31) {
|
||||
runBlocking {
|
||||
val savedSearch = prefs.getStringSet("eh_saved_searches", emptySet())?.mapNotNull {
|
||||
runCatching {
|
||||
val content = Json.decodeFromString<JsonObject>(it.substringAfter(':'))
|
||||
SavedSearch(
|
||||
id = -1,
|
||||
source = it.substringBefore(':').toLongOrNull()
|
||||
?: return@runCatching null,
|
||||
name = content["name"]!!.jsonPrimitive.content,
|
||||
query = content["query"]!!.jsonPrimitive.contentOrNull?.nullIfBlank(),
|
||||
filtersJson = Json.encodeToString(content["filters"]!!.jsonArray),
|
||||
)
|
||||
}.getOrNull()
|
||||
}
|
||||
if (!savedSearch.isNullOrEmpty()) {
|
||||
insertSavedSearch.awaitAll(savedSearch)
|
||||
}
|
||||
val feedSavedSearch = prefs.getStringSet("latest_tab_sources", emptySet())?.map {
|
||||
FeedSavedSearch(
|
||||
id = -1,
|
||||
source = it.toLong(),
|
||||
savedSearch = null,
|
||||
global = true,
|
||||
)
|
||||
}
|
||||
if (!feedSavedSearch.isNullOrEmpty()) {
|
||||
insertFeedSavedSearch.awaitAll(feedSavedSearch)
|
||||
}
|
||||
}
|
||||
prefs.edit(commit = true) {
|
||||
remove("eh_saved_searches")
|
||||
remove("latest_tab_sources")
|
||||
}
|
||||
}
|
||||
if (oldVersion under 32) {
|
||||
val oldReaderTap = prefs.getBoolean("reader_tap", false)
|
||||
if (!oldReaderTap) {
|
||||
readerPreferences.navigationModePager().set(5)
|
||||
readerPreferences.navigationModeWebtoon().set(5)
|
||||
}
|
||||
}
|
||||
if (oldVersion under 38) {
|
||||
// Handle renamed enum values
|
||||
val newSortingMode = when (
|
||||
val oldSortingMode = prefs.getString(libraryPreferences.sortingMode().key(), "ALPHABETICAL")
|
||||
) {
|
||||
"LAST_CHECKED" -> "LAST_MANGA_UPDATE"
|
||||
"UNREAD" -> "UNREAD_COUNT"
|
||||
"DATE_FETCHED" -> "CHAPTER_FETCH_DATE"
|
||||
"DRAG_AND_DROP" -> "ALPHABETICAL"
|
||||
else -> oldSortingMode
|
||||
}
|
||||
prefs.edit {
|
||||
putString(libraryPreferences.sortingMode().key(), newSortingMode)
|
||||
}
|
||||
runBlocking {
|
||||
handler.await(true) {
|
||||
categoriesQueries.getCategories(CategoryMapper::mapCategory).executeAsList()
|
||||
.filter { (it.flags and 0b00111100L) == 0b00100000L }
|
||||
.forEach {
|
||||
categoriesQueries.update(
|
||||
categoryId = it.id,
|
||||
flags = it.flags and 0b00111100L.inv(),
|
||||
name = null,
|
||||
order = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (oldVersion under 39) {
|
||||
prefs.edit {
|
||||
val sort = prefs.getString(libraryPreferences.sortingMode().key(), null) ?: return@edit
|
||||
val direction = prefs.getString("library_sorting_ascending", "ASCENDING")!!
|
||||
putString(libraryPreferences.sortingMode().key(), "$sort,$direction")
|
||||
remove("library_sorting_ascending")
|
||||
}
|
||||
}
|
||||
if (oldVersion under 40) {
|
||||
if (backupPreferences.backupInterval().get() == 0) {
|
||||
backupPreferences.backupInterval().set(12)
|
||||
}
|
||||
}
|
||||
if (oldVersion under 41) {
|
||||
val preferences = listOf(
|
||||
libraryPreferences.filterChapterByRead(),
|
||||
libraryPreferences.filterChapterByDownloaded(),
|
||||
libraryPreferences.filterChapterByBookmarked(),
|
||||
libraryPreferences.sortChapterBySourceOrNumber(),
|
||||
libraryPreferences.displayChapterByNameOrNumber(),
|
||||
libraryPreferences.sortChapterByAscendingOrDescending(),
|
||||
)
|
||||
|
||||
prefs.edit {
|
||||
preferences.forEach { preference ->
|
||||
val key = preference.key()
|
||||
val value = prefs.getInt(key, Int.MIN_VALUE)
|
||||
if (value == Int.MIN_VALUE) return@forEach
|
||||
remove(key)
|
||||
putLong(key, value.toLong())
|
||||
}
|
||||
}
|
||||
}
|
||||
if (oldVersion under 42) {
|
||||
if (uiPreferences.themeMode().isSet()) {
|
||||
prefs.edit {
|
||||
val themeMode = prefs.getString(uiPreferences.themeMode().key(), null) ?: return@edit
|
||||
putString(uiPreferences.themeMode().key(), themeMode.uppercase())
|
||||
}
|
||||
}
|
||||
}
|
||||
if (oldVersion under 43) {
|
||||
if (preferenceStore.getBoolean("start_reading_button").get()) {
|
||||
libraryPreferences.showContinueReadingButton().set(true)
|
||||
}
|
||||
}
|
||||
if (oldVersion under 44) {
|
||||
val trackingQueuePref = context.getSharedPreferences("tracking_queue", Context.MODE_PRIVATE)
|
||||
trackingQueuePref.all.forEach {
|
||||
val (_, lastChapterRead) = it.value.toString().split(":")
|
||||
trackingQueuePref.edit {
|
||||
remove(it.key)
|
||||
putFloat(it.key, lastChapterRead.toFloat())
|
||||
}
|
||||
}
|
||||
}
|
||||
if (oldVersion under 45) {
|
||||
// Force MangaDex log out due to login flow change
|
||||
trackerManager.mdList.logout()
|
||||
}
|
||||
if (oldVersion under 52) {
|
||||
// Removed background jobs
|
||||
context.workManager.cancelAllWorkByTag("UpdateChecker")
|
||||
context.workManager.cancelAllWorkByTag("ExtensionUpdate")
|
||||
prefs.edit {
|
||||
remove("automatic_ext_updates")
|
||||
}
|
||||
val prefKeys = listOf(
|
||||
"pref_filter_library_downloaded",
|
||||
"pref_filter_library_unread",
|
||||
"pref_filter_library_started",
|
||||
"pref_filter_library_bookmarked",
|
||||
"pref_filter_library_completed",
|
||||
"pref_filter_library_lewd",
|
||||
) + trackerManager.trackers.map { "pref_filter_library_tracked_${it.id}" }
|
||||
|
||||
prefKeys.forEach { key ->
|
||||
val pref = preferenceStore.getInt(key, 0)
|
||||
prefs.edit {
|
||||
remove(key)
|
||||
|
||||
val newValue = when (pref.get()) {
|
||||
1 -> TriState.ENABLED_IS
|
||||
2 -> TriState.ENABLED_NOT
|
||||
else -> TriState.DISABLED
|
||||
}
|
||||
|
||||
preferenceStore.getEnum("${key}_v2", TriState.DISABLED).set(newValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
// if (oldVersion under 53) {
|
||||
// // This was accidentally visible from the reader settings sheet, but should always
|
||||
// // be disabled in release builds.
|
||||
// if (isReleaseBuildType) {
|
||||
// readerPreferences.longStripSplitWebtoon().set(false)
|
||||
// }
|
||||
// }
|
||||
if (oldVersion under 56) {
|
||||
val pref = libraryPreferences.autoUpdateDeviceRestrictions()
|
||||
if (pref.isSet() && "battery_not_low" in pref.get()) {
|
||||
pref.getAndSet { it - "battery_not_low" }
|
||||
}
|
||||
}
|
||||
if (oldVersion under 57) {
|
||||
val pref = preferenceStore.getInt("relative_time", 7)
|
||||
if (pref.get() == 0) {
|
||||
uiPreferences.relativeTime().set(false)
|
||||
}
|
||||
}
|
||||
if (oldVersion under 58) {
|
||||
pagePreviewCache.clear()
|
||||
File(context.cacheDir, PagePreviewCache.PARAMETER_CACHE_DIRECTORY).listFiles()?.forEach {
|
||||
if (it.name == "journal" || it.name.startsWith("journal.")) {
|
||||
return@forEach
|
||||
}
|
||||
|
||||
try {
|
||||
it.delete()
|
||||
} catch (e: Exception) {
|
||||
logcat(LogPriority.WARN, e) { "Failed to remove file from cache" }
|
||||
}
|
||||
}
|
||||
}
|
||||
if (oldVersion under 59) {
|
||||
val prefsToReplace = listOf(
|
||||
"pref_download_only",
|
||||
"incognito_mode",
|
||||
"last_catalogue_source",
|
||||
"trusted_signatures",
|
||||
"last_app_closed",
|
||||
"library_update_last_timestamp",
|
||||
"library_unseen_updates_count",
|
||||
"last_used_category",
|
||||
"last_app_check",
|
||||
"last_ext_check",
|
||||
"last_version_code",
|
||||
"skip_pre_migration",
|
||||
"eh_auto_update_stats",
|
||||
"storage_dir",
|
||||
)
|
||||
replacePreferences(
|
||||
preferenceStore = preferenceStore,
|
||||
filterPredicate = { it.key in prefsToReplace },
|
||||
newKey = { Preference.appStateKey(it) },
|
||||
)
|
||||
|
||||
val privatePrefsToReplace = listOf(
|
||||
"sql_password",
|
||||
"encrypt_database",
|
||||
"cbz_password",
|
||||
"password_protect_downloads",
|
||||
"eh_ipb_member_id",
|
||||
"enable_exhentai",
|
||||
"eh_ipb_member_id",
|
||||
"eh_ipb_pass_hash",
|
||||
"eh_igneous",
|
||||
"eh_ehSettingsProfile",
|
||||
"eh_exhSettingsProfile",
|
||||
"eh_settingsKey",
|
||||
"eh_sessionCookie",
|
||||
"eh_hathPerksCookie",
|
||||
)
|
||||
|
||||
replacePreferences(
|
||||
preferenceStore = preferenceStore,
|
||||
filterPredicate = { it.key in privatePrefsToReplace },
|
||||
newKey = { Preference.privateKey(it) },
|
||||
)
|
||||
|
||||
// Deleting old download cache index files, but might as well clear it all out
|
||||
context.cacheDir.deleteRecursively()
|
||||
}
|
||||
if (oldVersion under 60) {
|
||||
sourcePreferences.extensionRepos().getAndSet {
|
||||
it.map { "https://raw.githubusercontent.com/$it/repo" }.toSet()
|
||||
}
|
||||
replacePreferences(
|
||||
preferenceStore = preferenceStore,
|
||||
filterPredicate = { it.key.startsWith("pref_mangasync_") || it.key.startsWith("track_token_") },
|
||||
newKey = { Preference.privateKey(it) },
|
||||
)
|
||||
prefs.edit {
|
||||
remove(Preference.appStateKey("trusted_signatures"))
|
||||
}
|
||||
}
|
||||
if (oldVersion under 66) {
|
||||
val cacheImagesToDisk = prefs.getBoolean("cache_archive_manga_on_disk", false)
|
||||
if (cacheImagesToDisk) {
|
||||
readerPreferences.archiveReaderMode().set(ReaderPreferences.ArchiveReaderMode.CACHE_TO_DISK)
|
||||
}
|
||||
}
|
||||
|
||||
if (oldVersion under 66) {
|
||||
if (prefs.getBoolean(Preference.privateKey("encrypt_database"), false)) {
|
||||
context.toast(
|
||||
"Restart the app to load your encrypted library",
|
||||
Toast.LENGTH_LONG
|
||||
)
|
||||
}
|
||||
|
||||
val appStatePrefsToReplace = listOf(
|
||||
"__PRIVATE_sql_password",
|
||||
"__PRIVATE_encrypt_database",
|
||||
"__PRIVATE_cbz_password",
|
||||
)
|
||||
|
||||
replacePreferences(
|
||||
preferenceStore = preferenceStore,
|
||||
filterPredicate = { it.key in appStatePrefsToReplace },
|
||||
newKey = { Preference.appStateKey(it.replace("__PRIVATE_", "").trim()) },
|
||||
)
|
||||
}
|
||||
|
||||
if (oldVersion under 67) {
|
||||
runBlocking {
|
||||
for ((index, source) in sourcePreferences.extensionRepos().get().withIndex()) {
|
||||
try {
|
||||
extensionRepoRepository.upsertRepo(
|
||||
source,
|
||||
"Repo #${index + 1}",
|
||||
null,
|
||||
source,
|
||||
"NOFINGERPRINT-${index + 1}",
|
||||
)
|
||||
} catch (e: SaveExtensionRepoException) {
|
||||
logcat(LogPriority.ERROR, e) { "Error Migrating Extension Repo with baseUrl: $source" }
|
||||
}
|
||||
}
|
||||
sourcePreferences.extensionRepos().delete()
|
||||
}
|
||||
}
|
||||
|
||||
// if (oldVersion under 1) { } (1 is current release version)
|
||||
// do stuff here when releasing changed crap
|
||||
|
||||
return true
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
xLogE("Failed to migrate app from $oldVersion -> ${BuildConfig.VERSION_CODE}!", e)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun migrateBackupEntry(manga: Manga): Manga {
|
||||
var newManga = manga
|
||||
@ -769,102 +61,4 @@ object EXHMigrations {
|
||||
orig
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
private data class UrlConfig(
|
||||
@SerialName("s")
|
||||
val source: Long,
|
||||
@SerialName("u")
|
||||
val url: String,
|
||||
@SerialName("m")
|
||||
val mangaUrl: String,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
private data class MangaConfig(
|
||||
@SerialName("c")
|
||||
val children: List<MangaSource>,
|
||||
) {
|
||||
companion object {
|
||||
fun readFromUrl(url: String): MangaConfig? {
|
||||
return try {
|
||||
Json.decodeFromString(url)
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun readMangaConfig(manga: Manga): MangaConfig? {
|
||||
return MangaConfig.readFromUrl(manga.url)
|
||||
}
|
||||
|
||||
@Serializable
|
||||
private data class MangaSource(
|
||||
@SerialName("s")
|
||||
val source: Long,
|
||||
@SerialName("u")
|
||||
val url: String,
|
||||
) {
|
||||
fun load(): LoadedMangaSource? {
|
||||
val manga = runBlocking { getManga.await(url, source) } ?: return null
|
||||
val source = sourceManager.getOrStub(source)
|
||||
return LoadedMangaSource(source, manga)
|
||||
}
|
||||
}
|
||||
|
||||
private fun readUrlConfig(url: String): UrlConfig? {
|
||||
return try {
|
||||
Json.decodeFromString(url)
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private data class LoadedMangaSource(val source: Source, val manga: Manga)
|
||||
|
||||
private fun updateSourceId(newId: Long, oldId: Long) {
|
||||
runBlocking {
|
||||
handler.await { ehQueries.migrateSource(newId, oldId) }
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
private fun replacePreferences(
|
||||
preferenceStore: PreferenceStore,
|
||||
filterPredicate: (Map.Entry<String, Any?>) -> Boolean,
|
||||
newKey: (String) -> String,
|
||||
) {
|
||||
preferenceStore.getAll()
|
||||
.filter(filterPredicate)
|
||||
.forEach { (key, value) ->
|
||||
when (value) {
|
||||
is Int -> {
|
||||
preferenceStore.getInt(newKey(key)).set(value)
|
||||
preferenceStore.getInt(key).delete()
|
||||
}
|
||||
is Long -> {
|
||||
preferenceStore.getLong(newKey(key)).set(value)
|
||||
preferenceStore.getLong(key).delete()
|
||||
}
|
||||
is Float -> {
|
||||
preferenceStore.getFloat(newKey(key)).set(value)
|
||||
preferenceStore.getFloat(key).delete()
|
||||
}
|
||||
is String -> {
|
||||
preferenceStore.getString(newKey(key)).set(value)
|
||||
preferenceStore.getString(key).delete()
|
||||
}
|
||||
is Boolean -> {
|
||||
preferenceStore.getBoolean(newKey(key)).set(value)
|
||||
preferenceStore.getBoolean(key).delete()
|
||||
}
|
||||
is Set<*> -> (value as? Set<String>)?.let {
|
||||
preferenceStore.getStringSet(newKey(key)).set(value)
|
||||
preferenceStore.getStringSet(key).delete()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,23 +1,15 @@
|
||||
package exh.debug
|
||||
|
||||
import android.app.Application
|
||||
import eu.kanade.domain.base.BasePreferences
|
||||
import eu.kanade.domain.manga.interactor.UpdateManga
|
||||
import eu.kanade.domain.manga.model.toSManga
|
||||
import eu.kanade.domain.source.service.SourcePreferences
|
||||
import eu.kanade.domain.ui.UiPreferences
|
||||
import eu.kanade.tachiyomi.core.security.SecurityPreferences
|
||||
import eu.kanade.tachiyomi.BuildConfig
|
||||
import eu.kanade.tachiyomi.data.backup.models.Backup
|
||||
import eu.kanade.tachiyomi.data.cache.PagePreviewCache
|
||||
import eu.kanade.tachiyomi.data.library.LibraryUpdateJob
|
||||
import eu.kanade.tachiyomi.data.sync.SyncDataJob
|
||||
import eu.kanade.tachiyomi.data.track.TrackerManager
|
||||
import eu.kanade.tachiyomi.network.NetworkPreferences
|
||||
import eu.kanade.tachiyomi.source.AndroidSourceManager
|
||||
import eu.kanade.tachiyomi.source.online.all.NHentai
|
||||
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
|
||||
import eu.kanade.tachiyomi.util.system.workManager
|
||||
import exh.EXHMigrations
|
||||
import exh.eh.EHentaiThrottleManager
|
||||
import exh.eh.EHentaiUpdateWorker
|
||||
import exh.metadata.metadata.EHentaiSearchMetadata
|
||||
@ -27,11 +19,9 @@ import exh.source.nHentaiSourceIds
|
||||
import exh.util.jobScheduler
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.serialization.protobuf.schema.ProtoBufSchemaGenerator
|
||||
import mihon.domain.extensionrepo.repository.ExtensionRepoRepository
|
||||
import tachiyomi.core.common.preference.PreferenceStore
|
||||
import mihon.core.migration.Migrator
|
||||
import mihon.core.migration.migrations.migrations
|
||||
import tachiyomi.data.DatabaseHandler
|
||||
import tachiyomi.domain.backup.service.BackupPreferences
|
||||
import tachiyomi.domain.library.service.LibraryPreferences
|
||||
import tachiyomi.domain.manga.interactor.GetAllManga
|
||||
import tachiyomi.domain.manga.interactor.GetExhFavoriteMangaWithMetadata
|
||||
import tachiyomi.domain.manga.interactor.GetFavorites
|
||||
@ -48,16 +38,6 @@ import java.util.UUID
|
||||
object DebugFunctions {
|
||||
private val app: Application by injectLazy()
|
||||
private val handler: DatabaseHandler by injectLazy()
|
||||
private val prefsStore: PreferenceStore by injectLazy()
|
||||
private val basePrefs: BasePreferences by injectLazy()
|
||||
private val uiPrefs: UiPreferences by injectLazy()
|
||||
private val networkPrefs: NetworkPreferences by injectLazy()
|
||||
private val sourcePrefs: SourcePreferences by injectLazy()
|
||||
private val securityPrefs: SecurityPreferences by injectLazy()
|
||||
private val libraryPrefs: LibraryPreferences by injectLazy()
|
||||
private val readerPrefs: ReaderPreferences by injectLazy()
|
||||
private val backupPrefs: BackupPreferences by injectLazy()
|
||||
private val trackerManager: TrackerManager by injectLazy()
|
||||
private val sourceManager: SourceManager by injectLazy()
|
||||
private val updateManga: UpdateManga by injectLazy()
|
||||
private val getFavorites: GetFavorites by injectLazy()
|
||||
@ -66,46 +46,22 @@ object DebugFunctions {
|
||||
private val getExhFavoriteMangaWithMetadata: GetExhFavoriteMangaWithMetadata by injectLazy()
|
||||
private val getSearchMetadata: GetSearchMetadata by injectLazy()
|
||||
private val getAllManga: GetAllManga by injectLazy()
|
||||
private val pagePreviewCache: PagePreviewCache by injectLazy()
|
||||
private val extensionRepoRepository: ExtensionRepoRepository by injectLazy()
|
||||
|
||||
fun forceUpgradeMigration() {
|
||||
val lastVersionCode = prefsStore.getInt("eh_last_version_code", 0)
|
||||
lastVersionCode.set(1)
|
||||
EXHMigrations.upgrade(
|
||||
context = app,
|
||||
preferenceStore = prefsStore,
|
||||
basePreferences = basePrefs,
|
||||
uiPreferences = uiPrefs,
|
||||
networkPreferences = networkPrefs,
|
||||
sourcePreferences = sourcePrefs,
|
||||
securityPreferences = securityPrefs,
|
||||
libraryPreferences = libraryPrefs,
|
||||
readerPreferences = readerPrefs,
|
||||
backupPreferences = backupPrefs,
|
||||
trackerManager = trackerManager,
|
||||
pagePreviewCache = pagePreviewCache,
|
||||
extensionRepoRepository = extensionRepoRepository,
|
||||
Migrator.migrate(
|
||||
old = 1,
|
||||
new = BuildConfig.VERSION_CODE,
|
||||
migrations = migrations,
|
||||
onMigrationComplete = {}
|
||||
)
|
||||
}
|
||||
|
||||
fun forceSetupJobs() {
|
||||
val lastVersionCode = prefsStore.getInt("eh_last_version_code", 0)
|
||||
lastVersionCode.set(0)
|
||||
EXHMigrations.upgrade(
|
||||
context = app,
|
||||
preferenceStore = prefsStore,
|
||||
basePreferences = basePrefs,
|
||||
uiPreferences = uiPrefs,
|
||||
networkPreferences = networkPrefs,
|
||||
sourcePreferences = sourcePrefs,
|
||||
securityPreferences = securityPrefs,
|
||||
libraryPreferences = libraryPrefs,
|
||||
readerPreferences = readerPrefs,
|
||||
backupPreferences = backupPrefs,
|
||||
trackerManager = trackerManager,
|
||||
pagePreviewCache = pagePreviewCache,
|
||||
extensionRepoRepository = extensionRepoRepository,
|
||||
Migrator.migrate(
|
||||
old = 0,
|
||||
new = BuildConfig.VERSION_CODE,
|
||||
migrations = migrations,
|
||||
onMigrationComplete = {}
|
||||
)
|
||||
}
|
||||
|
||||
|
53
app/src/main/java/mihon/core/migration/MigrateUtils.kt
Normal file
53
app/src/main/java/mihon/core/migration/MigrateUtils.kt
Normal file
@ -0,0 +1,53 @@
|
||||
package mihon.core.migration
|
||||
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import tachiyomi.core.common.preference.PreferenceStore
|
||||
import tachiyomi.data.DatabaseHandler
|
||||
|
||||
object MigrateUtils {
|
||||
fun updateSourceId(migrationContext: MigrationContext, newId: Long, oldId: Long) {
|
||||
val handler = migrationContext.get<DatabaseHandler>() ?: return
|
||||
runBlocking {
|
||||
handler.await { ehQueries.migrateSource(newId, oldId) }
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun replacePreferences(
|
||||
preferenceStore: PreferenceStore,
|
||||
filterPredicate: (Map.Entry<String, Any?>) -> Boolean,
|
||||
newKey: (String) -> String,
|
||||
) {
|
||||
preferenceStore.getAll()
|
||||
.filter(filterPredicate)
|
||||
.forEach { (key, value) ->
|
||||
when (value) {
|
||||
is Int -> {
|
||||
preferenceStore.getInt(newKey(key)).set(value)
|
||||
preferenceStore.getInt(key).delete()
|
||||
}
|
||||
is Long -> {
|
||||
preferenceStore.getLong(newKey(key)).set(value)
|
||||
preferenceStore.getLong(key).delete()
|
||||
}
|
||||
is Float -> {
|
||||
preferenceStore.getFloat(newKey(key)).set(value)
|
||||
preferenceStore.getFloat(key).delete()
|
||||
}
|
||||
is String -> {
|
||||
preferenceStore.getString(newKey(key)).set(value)
|
||||
preferenceStore.getString(key).delete()
|
||||
}
|
||||
is Boolean -> {
|
||||
preferenceStore.getBoolean(newKey(key)).set(value)
|
||||
preferenceStore.getBoolean(key).delete()
|
||||
}
|
||||
is Set<*> -> (value as? Set<String>)?.let {
|
||||
preferenceStore.getStringSet(newKey(key)).set(value)
|
||||
preferenceStore.getStringSet(key).delete()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
19
app/src/main/java/mihon/core/migration/Migration.kt
Normal file
19
app/src/main/java/mihon/core/migration/Migration.kt
Normal file
@ -0,0 +1,19 @@
|
||||
package mihon.core.migration
|
||||
|
||||
interface Migration {
|
||||
val version: Float
|
||||
|
||||
suspend operator fun invoke(migrationContext: MigrationContext): Boolean
|
||||
|
||||
companion object {
|
||||
const val ALWAYS = -1f
|
||||
|
||||
fun of(version: Float, action: suspend (MigrationContext) -> Boolean): Migration = object : Migration {
|
||||
override val version: Float = version
|
||||
|
||||
override suspend operator fun invoke(migrationContext: MigrationContext): Boolean {
|
||||
return action(migrationContext)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
10
app/src/main/java/mihon/core/migration/MigrationContext.kt
Normal file
10
app/src/main/java/mihon/core/migration/MigrationContext.kt
Normal file
@ -0,0 +1,10 @@
|
||||
package mihon.core.migration
|
||||
|
||||
import uy.kohesive.injekt.Injekt
|
||||
|
||||
class MigrationContext {
|
||||
|
||||
inline fun <reified T> get(): T? {
|
||||
return Injekt.getInstanceOrNull(T::class.java)
|
||||
}
|
||||
}
|
53
app/src/main/java/mihon/core/migration/Migrator.kt
Normal file
53
app/src/main/java/mihon/core/migration/Migrator.kt
Normal file
@ -0,0 +1,53 @@
|
||||
package mihon.core.migration
|
||||
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import tachiyomi.core.common.util.system.logcat
|
||||
|
||||
object Migrator {
|
||||
|
||||
@SuppressWarnings("ReturnCount")
|
||||
fun migrate(
|
||||
old: Int,
|
||||
new: Int,
|
||||
migrations: List<Migration>,
|
||||
dryrun: Boolean = false,
|
||||
onMigrationComplete: () -> Unit
|
||||
): Boolean {
|
||||
val migrationContext = MigrationContext()
|
||||
|
||||
if (old == 0) {
|
||||
return migrationContext.migrate(
|
||||
migrations = migrations.filter { it.isAlways() },
|
||||
dryrun = dryrun
|
||||
)
|
||||
.also { onMigrationComplete() }
|
||||
}
|
||||
|
||||
if (old >= new) {
|
||||
return false
|
||||
}
|
||||
|
||||
return migrationContext.migrate(
|
||||
migrations = migrations.filter { it.isAlways() || it.version.toInt() in (old + 1)..new },
|
||||
dryrun = dryrun
|
||||
)
|
||||
.also { onMigrationComplete() }
|
||||
}
|
||||
|
||||
private fun Migration.isAlways() = version == Migration.ALWAYS
|
||||
|
||||
@SuppressWarnings("MaxLineLength")
|
||||
private fun MigrationContext.migrate(migrations: List<Migration>, dryrun: Boolean): Boolean {
|
||||
return migrations.sortedBy { it.version }
|
||||
.map { migration ->
|
||||
if (!dryrun) {
|
||||
logcat { "Running migration: { name = ${migration::class.simpleName}, version = ${migration.version} }" }
|
||||
runBlocking { migration(this@migrate) }
|
||||
} else {
|
||||
logcat { "(Dry-run) Running migration: { name = ${migration::class.simpleName}, version = ${migration.version} }" }
|
||||
true
|
||||
}
|
||||
}
|
||||
.reduce { acc, b -> acc || b }
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package mihon.core.migration.migrations
|
||||
|
||||
import mihon.core.migration.Migration
|
||||
import mihon.core.migration.MigrationContext
|
||||
import tachiyomi.core.common.util.lang.withIOContext
|
||||
import tachiyomi.domain.backup.service.BackupPreferences
|
||||
|
||||
class AlwaysBackupMigration : Migration {
|
||||
override val version: Float = 40f
|
||||
|
||||
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||
val backupPreferences = migrationContext.get<BackupPreferences>() ?: return@withIOContext false
|
||||
if (backupPreferences.backupInterval().get() == 0) {
|
||||
backupPreferences.backupInterval().set(12)
|
||||
}
|
||||
|
||||
return@withIOContext true
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package mihon.core.migration.migrations
|
||||
|
||||
import eu.kanade.domain.base.BasePreferences
|
||||
import eu.kanade.tachiyomi.util.system.DeviceUtil
|
||||
import mihon.core.migration.Migration
|
||||
import mihon.core.migration.MigrationContext
|
||||
import tachiyomi.core.common.util.lang.withIOContext
|
||||
|
||||
class ChangeMiuiExtensionInstallerMigration : Migration {
|
||||
override val version: Float = 27f
|
||||
|
||||
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||
val basePreferences = migrationContext.get<BasePreferences>() ?: return@withIOContext false
|
||||
if (
|
||||
DeviceUtil.isMiui &&
|
||||
basePreferences.extensionInstaller().get() == BasePreferences.ExtensionInstaller
|
||||
.PACKAGEINSTALLER
|
||||
) {
|
||||
basePreferences.extensionInstaller().set(BasePreferences.ExtensionInstaller.LEGACY)
|
||||
}
|
||||
|
||||
return@withIOContext true
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package mihon.core.migration.migrations
|
||||
|
||||
import androidx.core.content.edit
|
||||
import androidx.preference.PreferenceManager
|
||||
import eu.kanade.domain.ui.UiPreferences
|
||||
import eu.kanade.tachiyomi.App
|
||||
import mihon.core.migration.Migration
|
||||
import mihon.core.migration.MigrationContext
|
||||
import tachiyomi.core.common.util.lang.withIOContext
|
||||
|
||||
class ChangeThemeModeToUppercaseMigration : Migration {
|
||||
override val version: Float = 42f
|
||||
|
||||
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||
val context = migrationContext.get<App>() ?: return@withIOContext false
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
val uiPreferences = migrationContext.get<UiPreferences>() ?: return@withIOContext false
|
||||
if (uiPreferences.themeMode().isSet()) {
|
||||
prefs.edit {
|
||||
val themeMode = prefs.getString(uiPreferences.themeMode().key(), null) ?: return@edit
|
||||
putString(uiPreferences.themeMode().key(), themeMode.uppercase())
|
||||
}
|
||||
}
|
||||
|
||||
return@withIOContext true
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package mihon.core.migration.migrations
|
||||
|
||||
import android.content.Context
|
||||
import androidx.core.content.edit
|
||||
import eu.kanade.tachiyomi.App
|
||||
import mihon.core.migration.Migration
|
||||
import mihon.core.migration.MigrationContext
|
||||
import tachiyomi.core.common.util.lang.withIOContext
|
||||
|
||||
class ChangeTrackingQueueTypeMigration : Migration {
|
||||
override val version: Float = 44f
|
||||
|
||||
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||
val context = migrationContext.get<App>() ?: return@withIOContext false
|
||||
val trackingQueuePref = context.getSharedPreferences("tracking_queue", Context.MODE_PRIVATE)
|
||||
trackingQueuePref.all.forEach {
|
||||
val (_, lastChapterRead) = it.value.toString().split(":")
|
||||
trackingQueuePref.edit {
|
||||
remove(it.key)
|
||||
putFloat(it.key, lastChapterRead.toFloat())
|
||||
}
|
||||
}
|
||||
|
||||
return@withIOContext true
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
package mihon.core.migration.migrations
|
||||
|
||||
import eu.kanade.tachiyomi.App
|
||||
import eu.kanade.tachiyomi.data.cache.PagePreviewCache
|
||||
import logcat.LogPriority
|
||||
import mihon.core.migration.Migration
|
||||
import mihon.core.migration.MigrationContext
|
||||
import tachiyomi.core.common.util.lang.withIOContext
|
||||
import tachiyomi.core.common.util.system.logcat
|
||||
import java.io.File
|
||||
|
||||
class ClearBrokenPagePreviewCacheMigration : Migration {
|
||||
override val version: Float = 58f
|
||||
|
||||
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||
val context = migrationContext.get<App>() ?: return@withIOContext false
|
||||
val pagePreviewCache = migrationContext.get<PagePreviewCache>() ?: return@withIOContext false
|
||||
pagePreviewCache.clear()
|
||||
File(context.cacheDir, PagePreviewCache.PARAMETER_CACHE_DIRECTORY).listFiles()?.forEach {
|
||||
if (it.name == "journal" || it.name.startsWith("journal.")) {
|
||||
return@forEach
|
||||
}
|
||||
|
||||
try {
|
||||
it.delete()
|
||||
} catch (e: Exception) {
|
||||
logcat(LogPriority.WARN, e) { "Failed to remove file from cache" }
|
||||
}
|
||||
}
|
||||
|
||||
return@withIOContext true
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package mihon.core.migration.migrations
|
||||
|
||||
import eu.kanade.domain.manga.interactor.UpdateManga
|
||||
import exh.source.HBROWSE_SOURCE_ID
|
||||
import mihon.core.migration.MigrateUtils
|
||||
import mihon.core.migration.Migration
|
||||
import mihon.core.migration.MigrationContext
|
||||
import tachiyomi.core.common.util.lang.withIOContext
|
||||
import tachiyomi.domain.manga.interactor.GetMangaBySource
|
||||
import tachiyomi.domain.manga.model.MangaUpdate
|
||||
|
||||
class DelegateHBrowseMigration : Migration {
|
||||
override val version: Float = 4f
|
||||
|
||||
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||
val getMangaBySource = migrationContext.get<GetMangaBySource>() ?: return@withIOContext false
|
||||
val updateManga = migrationContext.get<UpdateManga>() ?: return@withIOContext false
|
||||
MigrateUtils.updateSourceId(migrationContext, HBROWSE_SOURCE_ID, 6912)
|
||||
|
||||
// Migrate BHrowse URLs
|
||||
val hBrowseManga = getMangaBySource.await(HBROWSE_SOURCE_ID)
|
||||
val mangaUpdates = hBrowseManga.map {
|
||||
MangaUpdate(it.id, url = it.url + "/c00001/")
|
||||
}
|
||||
updateManga.awaitAll(mangaUpdates)
|
||||
return@withIOContext true
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package mihon.core.migration.migrations
|
||||
|
||||
import eu.kanade.tachiyomi.source.online.all.NHentai
|
||||
import mihon.core.migration.MigrateUtils
|
||||
import mihon.core.migration.Migration
|
||||
import mihon.core.migration.MigrationContext
|
||||
import tachiyomi.core.common.util.lang.withIOContext
|
||||
|
||||
class DelegateNHentaiMigration : Migration {
|
||||
override val version: Float = 6f
|
||||
|
||||
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||
MigrateUtils.updateSourceId(migrationContext, NHentai.otherId, 6907)
|
||||
|
||||
return@withIOContext true
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
package mihon.core.migration.migrations
|
||||
|
||||
import eu.kanade.tachiyomi.App
|
||||
import exh.log.xLogE
|
||||
import mihon.core.migration.Migration
|
||||
import mihon.core.migration.MigrationContext
|
||||
import tachiyomi.core.common.util.lang.withIOContext
|
||||
import java.io.File
|
||||
|
||||
class DeleteOldEhFavoritesDatabaseMigration : Migration {
|
||||
override val version: Float = 24f
|
||||
|
||||
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||
val context = migrationContext.get<App>() ?: return@withIOContext false
|
||||
try {
|
||||
sequenceOf(
|
||||
"fav-sync",
|
||||
"fav-sync.management",
|
||||
"fav-sync.lock",
|
||||
"fav-sync.note",
|
||||
).map {
|
||||
File(context.filesDir, it)
|
||||
}.filter(File::exists).forEach {
|
||||
if (it.isDirectory) {
|
||||
it.deleteRecursively()
|
||||
} else {
|
||||
it.delete()
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
xLogE("Failed to delete old favorites database", e)
|
||||
}
|
||||
|
||||
return@withIOContext true
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package mihon.core.migration.migrations
|
||||
|
||||
import mihon.core.migration.Migration
|
||||
import mihon.core.migration.MigrationContext
|
||||
import tachiyomi.core.common.util.lang.withIOContext
|
||||
import tachiyomi.data.DatabaseHandler
|
||||
|
||||
class DeleteOldMangaDexTracksMigration : Migration {
|
||||
override val version: Float = 17f
|
||||
|
||||
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||
val handler = migrationContext.get<DatabaseHandler>() ?: return@withIOContext false
|
||||
// Delete old mangadex trackers
|
||||
handler.await { ehQueries.deleteBySyncId(6) }
|
||||
return@withIOContext true
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package mihon.core.migration.migrations
|
||||
|
||||
import eu.kanade.tachiyomi.data.track.TrackerManager
|
||||
import mihon.core.migration.Migration
|
||||
import mihon.core.migration.MigrationContext
|
||||
import tachiyomi.core.common.util.lang.withIOContext
|
||||
|
||||
class LogoutFromMALMigration : Migration {
|
||||
override val version: Float = 12f
|
||||
|
||||
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||
// Force MAL log out due to login flow change
|
||||
migrationContext.get<TrackerManager>()?.myAnimeList?.logout()
|
||||
|
||||
return@withIOContext true
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package mihon.core.migration.migrations
|
||||
|
||||
import eu.kanade.tachiyomi.data.track.TrackerManager
|
||||
import mihon.core.migration.Migration
|
||||
import mihon.core.migration.MigrationContext
|
||||
import tachiyomi.core.common.util.lang.withIOContext
|
||||
|
||||
class LogoutFromMangaDexMigration : Migration {
|
||||
override val version: Float = 45f
|
||||
|
||||
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||
// Force MangaDex log out due to login flow change
|
||||
migrationContext.get<TrackerManager>()?.mdList?.logout()
|
||||
|
||||
return@withIOContext true
|
||||
}
|
||||
}
|
@ -0,0 +1,191 @@
|
||||
package mihon.core.migration.migrations
|
||||
|
||||
import eu.kanade.domain.manga.interactor.UpdateManga
|
||||
import eu.kanade.tachiyomi.source.Source
|
||||
import exh.source.MERGED_SOURCE_ID
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.Json
|
||||
import mihon.core.migration.Migration
|
||||
import mihon.core.migration.MigrationContext
|
||||
import tachiyomi.core.common.util.lang.withIOContext
|
||||
import tachiyomi.data.DatabaseHandler
|
||||
import tachiyomi.data.chapter.ChapterMapper
|
||||
import tachiyomi.domain.chapter.interactor.DeleteChapters
|
||||
import tachiyomi.domain.chapter.interactor.UpdateChapter
|
||||
import tachiyomi.domain.chapter.model.ChapterUpdate
|
||||
import tachiyomi.domain.manga.interactor.GetManga
|
||||
import tachiyomi.domain.manga.interactor.GetMangaBySource
|
||||
import tachiyomi.domain.manga.interactor.InsertMergedReference
|
||||
import tachiyomi.domain.manga.model.Manga
|
||||
import tachiyomi.domain.manga.model.MangaUpdate
|
||||
import tachiyomi.domain.manga.model.MergedMangaReference
|
||||
import tachiyomi.domain.source.service.SourceManager
|
||||
|
||||
class MergedMangaRewriteMigration : Migration {
|
||||
override val version: Float = 7f
|
||||
|
||||
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||
val handler = migrationContext.get<DatabaseHandler>() ?: return@withIOContext false
|
||||
val getMangaBySource = migrationContext.get<GetMangaBySource>() ?: return@withIOContext false
|
||||
val getManga = migrationContext.get<GetManga>() ?: return@withIOContext false
|
||||
val updateManga = migrationContext.get<UpdateManga>() ?: return@withIOContext false
|
||||
val insertMergedReference = migrationContext.get<InsertMergedReference>() ?: return@withIOContext false
|
||||
val sourceManager = migrationContext.get<SourceManager>() ?: return@withIOContext false
|
||||
val deleteChapters = migrationContext.get<DeleteChapters>() ?: return@withIOContext false
|
||||
val updateChapter = migrationContext.get<UpdateChapter>() ?: return@withIOContext false
|
||||
val mergedMangas = getMangaBySource.await(MERGED_SOURCE_ID)
|
||||
|
||||
if (mergedMangas.isNotEmpty()) {
|
||||
val mangaConfigs = mergedMangas.mapNotNull { mergedManga ->
|
||||
readMangaConfig(mergedManga)?.let { mergedManga to it }
|
||||
}
|
||||
if (mangaConfigs.isNotEmpty()) {
|
||||
val mangaToUpdate = mutableListOf<MangaUpdate>()
|
||||
val mergedMangaReferences = mutableListOf<MergedMangaReference>()
|
||||
mangaConfigs.onEach { mergedManga ->
|
||||
val newFirst = mergedManga.second.children.firstOrNull()?.url?.let {
|
||||
if (getManga.await(it, MERGED_SOURCE_ID) != null) return@onEach
|
||||
mangaToUpdate += MangaUpdate(id = mergedManga.first.id, url = it)
|
||||
mergedManga.first.copy(url = it)
|
||||
} ?: mergedManga.first
|
||||
mergedMangaReferences += MergedMangaReference(
|
||||
id = -1,
|
||||
isInfoManga = false,
|
||||
getChapterUpdates = false,
|
||||
chapterSortMode = 0,
|
||||
chapterPriority = 0,
|
||||
downloadChapters = false,
|
||||
mergeId = newFirst.id,
|
||||
mergeUrl = newFirst.url,
|
||||
mangaId = newFirst.id,
|
||||
mangaUrl = newFirst.url,
|
||||
mangaSourceId = MERGED_SOURCE_ID,
|
||||
)
|
||||
mergedManga.second.children.distinct().forEachIndexed { index, mangaSource ->
|
||||
val load = mangaSource.load(getManga, sourceManager) ?: return@forEachIndexed
|
||||
mergedMangaReferences += MergedMangaReference(
|
||||
id = -1,
|
||||
isInfoManga = index == 0,
|
||||
getChapterUpdates = true,
|
||||
chapterSortMode = 0,
|
||||
chapterPriority = 0,
|
||||
downloadChapters = true,
|
||||
mergeId = newFirst.id,
|
||||
mergeUrl = newFirst.url,
|
||||
mangaId = load.manga.id,
|
||||
mangaUrl = load.manga.url,
|
||||
mangaSourceId = load.source.id,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
updateManga.awaitAll(mangaToUpdate)
|
||||
insertMergedReference.awaitAll(mergedMangaReferences)
|
||||
|
||||
val loadedMangaList = mangaConfigs
|
||||
.map { it.second.children }
|
||||
.flatten()
|
||||
.mapNotNull { it.load(getManga, sourceManager) }
|
||||
.distinct()
|
||||
val chapters =
|
||||
handler.awaitList {
|
||||
ehQueries.getChaptersByMangaIds(
|
||||
mergedMangas.map { it.id },
|
||||
ChapterMapper::mapChapter,
|
||||
)
|
||||
}
|
||||
|
||||
val mergedMangaChapters =
|
||||
handler.awaitList {
|
||||
ehQueries.getChaptersByMangaIds(
|
||||
loadedMangaList.map { it.manga.id },
|
||||
ChapterMapper::mapChapter,
|
||||
)
|
||||
}
|
||||
|
||||
val mergedMangaChaptersMatched = mergedMangaChapters.mapNotNull { chapter ->
|
||||
loadedMangaList.firstOrNull {
|
||||
it.manga.id == chapter.id
|
||||
}?.let { it to chapter }
|
||||
}
|
||||
val parsedChapters = chapters.filter {
|
||||
it.read || it.lastPageRead != 0L
|
||||
}.mapNotNull { chapter -> readUrlConfig(chapter.url)?.let { chapter to it } }
|
||||
val chaptersToUpdate = mutableListOf<ChapterUpdate>()
|
||||
parsedChapters.forEach { parsedChapter ->
|
||||
mergedMangaChaptersMatched.firstOrNull {
|
||||
it.second.url == parsedChapter.second.url &&
|
||||
it.first.source.id == parsedChapter.second.source &&
|
||||
it.first.manga.url == parsedChapter.second.mangaUrl
|
||||
}?.let {
|
||||
chaptersToUpdate += ChapterUpdate(
|
||||
it.second.id,
|
||||
read = parsedChapter.first.read,
|
||||
lastPageRead = parsedChapter.first.lastPageRead,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
deleteChapters.await(mergedMangaChapters.map { it.id })
|
||||
updateChapter.awaitAll(chaptersToUpdate)
|
||||
}
|
||||
}
|
||||
return@withIOContext true
|
||||
}
|
||||
|
||||
|
||||
@Serializable
|
||||
private data class UrlConfig(
|
||||
@SerialName("s")
|
||||
val source: Long,
|
||||
@SerialName("u")
|
||||
val url: String,
|
||||
@SerialName("m")
|
||||
val mangaUrl: String,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
private data class MangaConfig(
|
||||
@SerialName("c")
|
||||
val children: List<MangaSource>,
|
||||
) {
|
||||
companion object {
|
||||
fun readFromUrl(url: String): MangaConfig? {
|
||||
return try {
|
||||
Json.decodeFromString(url)
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun readMangaConfig(manga: Manga): MangaConfig? {
|
||||
return MangaConfig.readFromUrl(manga.url)
|
||||
}
|
||||
|
||||
@Serializable
|
||||
private data class MangaSource(
|
||||
@SerialName("s")
|
||||
val source: Long,
|
||||
@SerialName("u")
|
||||
val url: String,
|
||||
) {
|
||||
suspend fun load(getManga: GetManga, sourceManager: SourceManager): LoadedMangaSource? {
|
||||
val manga = getManga.await(url, source) ?: return null
|
||||
val source = sourceManager.getOrStub(source)
|
||||
return LoadedMangaSource(source, manga)
|
||||
}
|
||||
}
|
||||
|
||||
private fun readUrlConfig(url: String): UrlConfig? {
|
||||
return try {
|
||||
Json.decodeFromString(url)
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private data class LoadedMangaSource(val source: Source, val manga: Manga)
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
package mihon.core.migration.migrations
|
||||
|
||||
import mihon.core.migration.Migration
|
||||
|
||||
val migrations: List<Migration>
|
||||
get() = listOf(
|
||||
SetupBackupCreateMigration(),
|
||||
SetupLibraryUpdateMigration(),
|
||||
SetupEHentaiUpdateMigration(),
|
||||
SetupSyncDataMigration(),
|
||||
DelegateHBrowseMigration(),
|
||||
DelegateNHentaiMigration(),
|
||||
MergedMangaRewriteMigration(),
|
||||
LogoutFromMALMigration(),
|
||||
MoveDOHSettingMigration(),
|
||||
ResetRotationSettingMigration(),
|
||||
ResetReaderSettingsMigration(),
|
||||
DeleteOldMangaDexTracksMigration(),
|
||||
RemoveOldReaderThemeMigration(),
|
||||
RemoveShorterLibraryUpdatesMigration(),
|
||||
MoveLibrarySortingSettingsMigration(),
|
||||
RemoveShortLibraryUpdatesMigration(),
|
||||
MoveLibraryNonCompleteSettingMigration(),
|
||||
DeleteOldEhFavoritesDatabaseMigration(),
|
||||
MoveSecureScreenSettingMigration(),
|
||||
ChangeMiuiExtensionInstallerMigration(),
|
||||
MoveCoverOnlyGridSettingMigration(),
|
||||
MoveCatalogueCoverOnlyGridSettingMigration(),
|
||||
MoveLatestToFeedMigration(),
|
||||
MoveReaderTapSettingMigration(),
|
||||
MoveSortingModeSettingsMigration(),
|
||||
MoveSortingModeSettingMigration(),
|
||||
AlwaysBackupMigration(),
|
||||
ResetFilterAndSortSettingsMigration(),
|
||||
ChangeThemeModeToUppercaseMigration(),
|
||||
MoveReadingButtonSettingMigration(),
|
||||
ChangeTrackingQueueTypeMigration(),
|
||||
LogoutFromMangaDexMigration(),
|
||||
RemoveUpdateCheckerJobsMigration(),
|
||||
RemoveBatteryNotLowRestrictionMigration(),
|
||||
MoveRelativeTimeSettingMigration(),
|
||||
ClearBrokenPagePreviewCacheMigration(),
|
||||
MoveSettingsToPrivateOrAppStateMigration(),
|
||||
MoveExtensionRepoSettingsMigration(),
|
||||
MoveCacheToDiskSettingMigration(),
|
||||
MoveEncryptionSettingsToAppStateMigration(),
|
||||
TrustExtensionRepositoryMigration(),
|
||||
)
|
@ -0,0 +1,24 @@
|
||||
package mihon.core.migration.migrations
|
||||
|
||||
import androidx.preference.PreferenceManager
|
||||
import eu.kanade.tachiyomi.App
|
||||
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
|
||||
import mihon.core.migration.Migration
|
||||
import mihon.core.migration.MigrationContext
|
||||
import tachiyomi.core.common.util.lang.withIOContext
|
||||
|
||||
class MoveCacheToDiskSettingMigration : Migration {
|
||||
override val version: Float = 66f
|
||||
|
||||
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||
val context = migrationContext.get<App>() ?: return@withIOContext false
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
val readerPreferences = migrationContext.get<ReaderPreferences>() ?: return@withIOContext false
|
||||
val cacheImagesToDisk = prefs.getBoolean("cache_archive_manga_on_disk", false)
|
||||
if (cacheImagesToDisk) {
|
||||
readerPreferences.archiveReaderMode().set(ReaderPreferences.ArchiveReaderMode.CACHE_TO_DISK)
|
||||
}
|
||||
|
||||
return@withIOContext true
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package mihon.core.migration.migrations
|
||||
|
||||
import androidx.core.content.edit
|
||||
import androidx.preference.PreferenceManager
|
||||
import eu.kanade.tachiyomi.App
|
||||
import mihon.core.migration.Migration
|
||||
import mihon.core.migration.MigrationContext
|
||||
import tachiyomi.core.common.util.lang.withIOContext
|
||||
|
||||
class MoveCatalogueCoverOnlyGridSettingMigration : Migration {
|
||||
override val version: Float = 29f
|
||||
|
||||
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||
val context = migrationContext.get<App>() ?: return@withIOContext false
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
if (prefs.getString("pref_display_mode_catalogue", null) == "NO_TITLE_GRID") {
|
||||
prefs.edit(commit = true) {
|
||||
putString("pref_display_mode_catalogue", "COMPACT_GRID")
|
||||
}
|
||||
}
|
||||
|
||||
return@withIOContext true
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package mihon.core.migration.migrations
|
||||
|
||||
import androidx.core.content.edit
|
||||
import androidx.preference.PreferenceManager
|
||||
import eu.kanade.tachiyomi.App
|
||||
import mihon.core.migration.Migration
|
||||
import mihon.core.migration.MigrationContext
|
||||
import tachiyomi.core.common.util.lang.withIOContext
|
||||
|
||||
class MoveCoverOnlyGridSettingMigration : Migration {
|
||||
override val version: Float = 28f
|
||||
|
||||
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||
val context = migrationContext.get<App>() ?: return@withIOContext false
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
if (prefs.getString("pref_display_mode_library", null) == "NO_TITLE_GRID") {
|
||||
prefs.edit(commit = true) {
|
||||
putString("pref_display_mode_library", "COVER_ONLY_GRID")
|
||||
}
|
||||
}
|
||||
|
||||
return@withIOContext true
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package mihon.core.migration.migrations
|
||||
|
||||
import androidx.core.content.edit
|
||||
import androidx.preference.PreferenceManager
|
||||
import eu.kanade.tachiyomi.App
|
||||
import eu.kanade.tachiyomi.network.NetworkPreferences
|
||||
import eu.kanade.tachiyomi.network.PREF_DOH_CLOUDFLARE
|
||||
import mihon.core.migration.Migration
|
||||
import mihon.core.migration.MigrationContext
|
||||
import tachiyomi.core.common.util.lang.withIOContext
|
||||
|
||||
class MoveDOHSettingMigration : Migration {
|
||||
override val version: Float = 14f
|
||||
|
||||
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||
val context = migrationContext.get<App>() ?: return@withIOContext false
|
||||
val networkPreferences = migrationContext.get<NetworkPreferences>() ?: return@withIOContext false
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
// Migrate DNS over HTTPS setting
|
||||
val wasDohEnabled = prefs.getBoolean("enable_doh", false)
|
||||
if (wasDohEnabled) {
|
||||
prefs.edit {
|
||||
putInt(networkPreferences.dohProvider().key(), PREF_DOH_CLOUDFLARE)
|
||||
remove("enable_doh")
|
||||
}
|
||||
}
|
||||
|
||||
return@withIOContext true
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
package mihon.core.migration.migrations
|
||||
|
||||
import android.widget.Toast
|
||||
import androidx.preference.PreferenceManager
|
||||
import eu.kanade.tachiyomi.App
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
import mihon.core.migration.MigrateUtils
|
||||
import mihon.core.migration.Migration
|
||||
import mihon.core.migration.MigrationContext
|
||||
import tachiyomi.core.common.preference.Preference
|
||||
import tachiyomi.core.common.preference.PreferenceStore
|
||||
import tachiyomi.core.common.util.lang.withIOContext
|
||||
import tachiyomi.core.common.util.lang.withUIContext
|
||||
|
||||
class MoveEncryptionSettingsToAppStateMigration : Migration {
|
||||
override val version: Float = 66f
|
||||
|
||||
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||
val context = migrationContext.get<App>() ?: return@withIOContext false
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
val preferenceStore = migrationContext.get<PreferenceStore>() ?: return@withIOContext false
|
||||
if (prefs.getBoolean(Preference.privateKey("encrypt_database"), false)) {
|
||||
withUIContext {
|
||||
context.toast(
|
||||
"Restart the app to load your encrypted library",
|
||||
Toast.LENGTH_LONG
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val appStatePrefsToReplace = listOf(
|
||||
"__PRIVATE_sql_password",
|
||||
"__PRIVATE_encrypt_database",
|
||||
"__PRIVATE_cbz_password",
|
||||
)
|
||||
|
||||
MigrateUtils.replacePreferences(
|
||||
preferenceStore = preferenceStore,
|
||||
filterPredicate = { it.key in appStatePrefsToReplace },
|
||||
newKey = { Preference.appStateKey(it.replace("__PRIVATE_", "").trim()) },
|
||||
)
|
||||
|
||||
return@withIOContext true
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
package mihon.core.migration.migrations
|
||||
|
||||
import androidx.core.content.edit
|
||||
import androidx.preference.PreferenceManager
|
||||
import eu.kanade.domain.source.service.SourcePreferences
|
||||
import eu.kanade.tachiyomi.App
|
||||
import mihon.core.migration.MigrateUtils
|
||||
import mihon.core.migration.Migration
|
||||
import mihon.core.migration.MigrationContext
|
||||
import tachiyomi.core.common.preference.Preference
|
||||
import tachiyomi.core.common.preference.PreferenceStore
|
||||
import tachiyomi.core.common.preference.getAndSet
|
||||
import tachiyomi.core.common.util.lang.withIOContext
|
||||
|
||||
class MoveExtensionRepoSettingsMigration : Migration {
|
||||
override val version: Float = 60f
|
||||
|
||||
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||
val context = migrationContext.get<App>() ?: return@withIOContext false
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
val preferenceStore = migrationContext.get<PreferenceStore>() ?: return@withIOContext false
|
||||
val sourcePreferences = migrationContext.get<SourcePreferences>() ?: return@withIOContext false
|
||||
sourcePreferences.extensionRepos().getAndSet {
|
||||
it.map { "https://raw.githubusercontent.com/$it/repo" }.toSet()
|
||||
}
|
||||
MigrateUtils.replacePreferences(
|
||||
preferenceStore = preferenceStore,
|
||||
filterPredicate = { it.key.startsWith("pref_mangasync_") || it.key.startsWith("track_token_") },
|
||||
newKey = { Preference.privateKey(it) },
|
||||
)
|
||||
prefs.edit {
|
||||
remove(Preference.appStateKey("trusted_signatures"))
|
||||
}
|
||||
|
||||
return@withIOContext true
|
||||
}
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
package mihon.core.migration.migrations
|
||||
|
||||
import androidx.core.content.edit
|
||||
import androidx.preference.PreferenceManager
|
||||
import eu.kanade.tachiyomi.App
|
||||
import exh.util.nullIfBlank
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import kotlinx.serialization.json.contentOrNull
|
||||
import kotlinx.serialization.json.jsonArray
|
||||
import kotlinx.serialization.json.jsonPrimitive
|
||||
import mihon.core.migration.Migration
|
||||
import mihon.core.migration.MigrationContext
|
||||
import tachiyomi.core.common.util.lang.withIOContext
|
||||
import tachiyomi.domain.source.interactor.InsertFeedSavedSearch
|
||||
import tachiyomi.domain.source.interactor.InsertSavedSearch
|
||||
import tachiyomi.domain.source.model.FeedSavedSearch
|
||||
import tachiyomi.domain.source.model.SavedSearch
|
||||
|
||||
class MoveLatestToFeedMigration : Migration {
|
||||
override val version: Float = 31f
|
||||
|
||||
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||
val context = migrationContext.get<App>() ?: return@withIOContext false
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
val insertSavedSearch = migrationContext.get<InsertSavedSearch>() ?: return@withIOContext false
|
||||
val insertFeedSavedSearch = migrationContext.get<InsertFeedSavedSearch>() ?: return@withIOContext false
|
||||
val savedSearch = prefs.getStringSet("eh_saved_searches", emptySet())?.mapNotNull {
|
||||
runCatching {
|
||||
val content = Json.decodeFromString<JsonObject>(it.substringAfter(':'))
|
||||
SavedSearch(
|
||||
id = -1,
|
||||
source = it.substringBefore(':').toLongOrNull()
|
||||
?: return@runCatching null,
|
||||
name = content["name"]!!.jsonPrimitive.content,
|
||||
query = content["query"]!!.jsonPrimitive.contentOrNull?.nullIfBlank(),
|
||||
filtersJson = Json.encodeToString(content["filters"]!!.jsonArray),
|
||||
)
|
||||
}.getOrNull()
|
||||
}
|
||||
if (!savedSearch.isNullOrEmpty()) {
|
||||
insertSavedSearch.awaitAll(savedSearch)
|
||||
}
|
||||
val feedSavedSearch = prefs.getStringSet("latest_tab_sources", emptySet())?.map {
|
||||
FeedSavedSearch(
|
||||
id = -1,
|
||||
source = it.toLong(),
|
||||
savedSearch = null,
|
||||
global = true,
|
||||
)
|
||||
}
|
||||
if (!feedSavedSearch.isNullOrEmpty()) {
|
||||
insertFeedSavedSearch.awaitAll(feedSavedSearch)
|
||||
}
|
||||
prefs.edit(commit = true) {
|
||||
remove("eh_saved_searches")
|
||||
remove("latest_tab_sources")
|
||||
}
|
||||
|
||||
return@withIOContext true
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package mihon.core.migration.migrations
|
||||
|
||||
import androidx.preference.PreferenceManager
|
||||
import eu.kanade.tachiyomi.App
|
||||
import mihon.core.migration.Migration
|
||||
import mihon.core.migration.MigrationContext
|
||||
import tachiyomi.core.common.preference.minusAssign
|
||||
import tachiyomi.core.common.util.lang.withIOContext
|
||||
import tachiyomi.domain.library.service.LibraryPreferences
|
||||
|
||||
class MoveLibraryNonCompleteSettingMigration : Migration {
|
||||
override val version: Float = 23f
|
||||
|
||||
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||
val context = migrationContext.get<App>() ?: return@withIOContext false
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
val libraryPreferences = migrationContext.get<LibraryPreferences>() ?: return@withIOContext false
|
||||
val oldUpdateOngoingOnly = prefs.getBoolean("pref_update_only_non_completed_key", true)
|
||||
if (!oldUpdateOngoingOnly) {
|
||||
libraryPreferences.autoUpdateMangaRestrictions() -= LibraryPreferences.MANGA_NON_COMPLETED
|
||||
}
|
||||
|
||||
return@withIOContext true
|
||||
}
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
package mihon.core.migration.migrations
|
||||
|
||||
import androidx.core.content.edit
|
||||
import androidx.preference.PreferenceManager
|
||||
import eu.kanade.tachiyomi.App
|
||||
import mihon.core.migration.Migration
|
||||
import mihon.core.migration.MigrationContext
|
||||
import tachiyomi.core.common.util.lang.withIOContext
|
||||
import tachiyomi.core.common.util.system.logcat
|
||||
import tachiyomi.domain.library.service.LibraryPreferences
|
||||
|
||||
class MoveLibrarySortingSettingsMigration : Migration {
|
||||
override val version: Float = 20f
|
||||
|
||||
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||
val context = migrationContext.get<App>() ?: return@withIOContext false
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
val libraryPreferences = migrationContext.get<LibraryPreferences>() ?: return@withIOContext false
|
||||
try {
|
||||
val oldSortingMode = prefs.getInt(libraryPreferences.sortingMode().key(), 0 /* ALPHABETICAL */)
|
||||
val oldSortingDirection = prefs.getBoolean("library_sorting_ascending", true)
|
||||
|
||||
val newSortingMode = when (oldSortingMode) {
|
||||
0 -> "ALPHABETICAL"
|
||||
1 -> "LAST_READ"
|
||||
2 -> "LAST_MANGA_UPDATE"
|
||||
3 -> "UNREAD_COUNT"
|
||||
4 -> "TOTAL_CHAPTERS"
|
||||
6 -> "LATEST_CHAPTER"
|
||||
7 -> "DRAG_AND_DROP"
|
||||
8 -> "DATE_ADDED"
|
||||
9 -> "TAG_LIST"
|
||||
10 -> "CHAPTER_FETCH_DATE"
|
||||
else -> "ALPHABETICAL"
|
||||
}
|
||||
|
||||
val newSortingDirection = when (oldSortingDirection) {
|
||||
true -> "ASCENDING"
|
||||
else -> "DESCENDING"
|
||||
}
|
||||
|
||||
prefs.edit(commit = true) {
|
||||
remove(libraryPreferences.sortingMode().key())
|
||||
remove("library_sorting_ascending")
|
||||
}
|
||||
|
||||
prefs.edit {
|
||||
putString(libraryPreferences.sortingMode().key(), newSortingMode)
|
||||
putString("library_sorting_ascending", newSortingDirection)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
logcat(throwable = e) { "Already done migration" }
|
||||
}
|
||||
|
||||
return@withIOContext true
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package mihon.core.migration.migrations
|
||||
|
||||
import androidx.preference.PreferenceManager
|
||||
import eu.kanade.tachiyomi.App
|
||||
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
|
||||
import mihon.core.migration.Migration
|
||||
import mihon.core.migration.MigrationContext
|
||||
import tachiyomi.core.common.util.lang.withIOContext
|
||||
|
||||
class MoveReaderTapSettingMigration : Migration {
|
||||
override val version: Float = 32f
|
||||
|
||||
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||
val context = migrationContext.get<App>() ?: return@withIOContext false
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
val readerPreferences = migrationContext.get<ReaderPreferences>() ?: return@withIOContext false
|
||||
val oldReaderTap = prefs.getBoolean("reader_tap", false)
|
||||
if (!oldReaderTap) {
|
||||
readerPreferences.navigationModePager().set(5)
|
||||
readerPreferences.navigationModeWebtoon().set(5)
|
||||
}
|
||||
|
||||
return@withIOContext true
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package mihon.core.migration.migrations
|
||||
|
||||
import androidx.preference.PreferenceManager
|
||||
import eu.kanade.tachiyomi.App
|
||||
import mihon.core.migration.Migration
|
||||
import mihon.core.migration.MigrationContext
|
||||
import tachiyomi.core.common.util.lang.withIOContext
|
||||
import tachiyomi.domain.library.service.LibraryPreferences
|
||||
|
||||
class MoveReadingButtonSettingMigration : Migration {
|
||||
override val version: Float = 43f
|
||||
|
||||
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||
val context = migrationContext.get<App>() ?: return@withIOContext false
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
val libraryPreferences = migrationContext.get<LibraryPreferences>() ?: return@withIOContext false
|
||||
if (prefs.getBoolean("start_reading_button", false)) {
|
||||
libraryPreferences.showContinueReadingButton().set(true)
|
||||
}
|
||||
|
||||
return@withIOContext true
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package mihon.core.migration.migrations
|
||||
|
||||
import eu.kanade.domain.ui.UiPreferences
|
||||
import mihon.core.migration.Migration
|
||||
import mihon.core.migration.MigrationContext
|
||||
import tachiyomi.core.common.preference.PreferenceStore
|
||||
import tachiyomi.core.common.util.lang.withIOContext
|
||||
|
||||
class MoveRelativeTimeSettingMigration : Migration {
|
||||
override val version: Float = 57f
|
||||
|
||||
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||
val preferenceStore = migrationContext.get<PreferenceStore>() ?: return@withIOContext false
|
||||
val uiPreferences = migrationContext.get<UiPreferences>() ?: return@withIOContext false
|
||||
val pref = preferenceStore.getInt("relative_time", 7)
|
||||
if (pref.get() == 0) {
|
||||
uiPreferences.relativeTime().set(false)
|
||||
}
|
||||
|
||||
return@withIOContext true
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package mihon.core.migration.migrations
|
||||
|
||||
import androidx.preference.PreferenceManager
|
||||
import eu.kanade.tachiyomi.App
|
||||
import eu.kanade.tachiyomi.core.security.SecurityPreferences
|
||||
import mihon.core.migration.Migration
|
||||
import mihon.core.migration.MigrationContext
|
||||
import tachiyomi.core.common.util.lang.withIOContext
|
||||
|
||||
class MoveSecureScreenSettingMigration : Migration {
|
||||
override val version: Float = 27f
|
||||
|
||||
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||
val context = migrationContext.get<App>() ?: return@withIOContext false
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
val securityPreferences = migrationContext.get<SecurityPreferences>() ?: return@withIOContext false
|
||||
val oldSecureScreen = prefs.getBoolean("secure_screen", false)
|
||||
if (oldSecureScreen) {
|
||||
securityPreferences.secureScreen().set(SecurityPreferences.SecureScreenMode.ALWAYS)
|
||||
}
|
||||
|
||||
return@withIOContext true
|
||||
}
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
package mihon.core.migration.migrations
|
||||
|
||||
import eu.kanade.tachiyomi.App
|
||||
import mihon.core.migration.MigrateUtils
|
||||
import mihon.core.migration.Migration
|
||||
import mihon.core.migration.MigrationContext
|
||||
import tachiyomi.core.common.preference.Preference
|
||||
import tachiyomi.core.common.preference.PreferenceStore
|
||||
import tachiyomi.core.common.util.lang.withIOContext
|
||||
|
||||
class MoveSettingsToPrivateOrAppStateMigration : Migration {
|
||||
override val version: Float = 59f
|
||||
|
||||
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||
val context = migrationContext.get<App>() ?: return@withIOContext false
|
||||
val preferenceStore = migrationContext.get<PreferenceStore>() ?: return@withIOContext false
|
||||
val prefsToReplace = listOf(
|
||||
"pref_download_only",
|
||||
"incognito_mode",
|
||||
"last_catalogue_source",
|
||||
"trusted_signatures",
|
||||
"last_app_closed",
|
||||
"library_update_last_timestamp",
|
||||
"library_unseen_updates_count",
|
||||
"last_used_category",
|
||||
"last_app_check",
|
||||
"last_ext_check",
|
||||
"last_version_code",
|
||||
"skip_pre_migration",
|
||||
"eh_auto_update_stats",
|
||||
"storage_dir",
|
||||
)
|
||||
MigrateUtils.replacePreferences(
|
||||
preferenceStore = preferenceStore,
|
||||
filterPredicate = { it.key in prefsToReplace },
|
||||
newKey = { Preference.appStateKey(it) },
|
||||
)
|
||||
|
||||
val privatePrefsToReplace = listOf(
|
||||
"sql_password",
|
||||
"encrypt_database",
|
||||
"cbz_password",
|
||||
"password_protect_downloads",
|
||||
"eh_ipb_member_id",
|
||||
"enable_exhentai",
|
||||
"eh_ipb_member_id",
|
||||
"eh_ipb_pass_hash",
|
||||
"eh_igneous",
|
||||
"eh_ehSettingsProfile",
|
||||
"eh_exhSettingsProfile",
|
||||
"eh_settingsKey",
|
||||
"eh_sessionCookie",
|
||||
"eh_hathPerksCookie",
|
||||
)
|
||||
|
||||
MigrateUtils.replacePreferences(
|
||||
preferenceStore = preferenceStore,
|
||||
filterPredicate = { it.key in privatePrefsToReplace },
|
||||
newKey = { Preference.privateKey(it) },
|
||||
)
|
||||
|
||||
// Deleting old download cache index files, but might as well clear it all out
|
||||
context.cacheDir.deleteRecursively()
|
||||
|
||||
return@withIOContext true
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package mihon.core.migration.migrations
|
||||
|
||||
import androidx.core.content.edit
|
||||
import androidx.preference.PreferenceManager
|
||||
import eu.kanade.tachiyomi.App
|
||||
import mihon.core.migration.Migration
|
||||
import mihon.core.migration.MigrationContext
|
||||
import tachiyomi.core.common.util.lang.withIOContext
|
||||
import tachiyomi.domain.library.service.LibraryPreferences
|
||||
|
||||
class MoveSortingModeSettingMigration : Migration {
|
||||
override val version: Float = 39f
|
||||
|
||||
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||
val context = migrationContext.get<App>() ?: return@withIOContext false
|
||||
val libraryPreferences = migrationContext.get<LibraryPreferences>() ?: return@withIOContext false
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
prefs.edit {
|
||||
val sort = prefs.getString(libraryPreferences.sortingMode().key(), null) ?: return@edit
|
||||
val direction = prefs.getString("library_sorting_ascending", "ASCENDING")!!
|
||||
putString(libraryPreferences.sortingMode().key(), "$sort,$direction")
|
||||
remove("library_sorting_ascending")
|
||||
}
|
||||
|
||||
return@withIOContext true
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
package mihon.core.migration.migrations
|
||||
|
||||
import androidx.core.content.edit
|
||||
import androidx.preference.PreferenceManager
|
||||
import eu.kanade.tachiyomi.App
|
||||
import mihon.core.migration.Migration
|
||||
import mihon.core.migration.MigrationContext
|
||||
import tachiyomi.core.common.util.lang.withIOContext
|
||||
import tachiyomi.data.DatabaseHandler
|
||||
import tachiyomi.data.category.CategoryMapper
|
||||
import tachiyomi.domain.library.service.LibraryPreferences
|
||||
|
||||
class MoveSortingModeSettingsMigration : Migration {
|
||||
override val version: Float = 38f
|
||||
|
||||
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||
val context = migrationContext.get<App>() ?: return@withIOContext false
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
val libraryPreferences = migrationContext.get<LibraryPreferences>() ?: return@withIOContext false
|
||||
val handler = migrationContext.get<DatabaseHandler>() ?: return@withIOContext false
|
||||
// Handle renamed enum values
|
||||
val newSortingMode = when (
|
||||
val oldSortingMode = prefs.getString(libraryPreferences.sortingMode().key(), "ALPHABETICAL")
|
||||
) {
|
||||
"LAST_CHECKED" -> "LAST_MANGA_UPDATE"
|
||||
"UNREAD" -> "UNREAD_COUNT"
|
||||
"DATE_FETCHED" -> "CHAPTER_FETCH_DATE"
|
||||
"DRAG_AND_DROP" -> "ALPHABETICAL"
|
||||
else -> oldSortingMode
|
||||
}
|
||||
prefs.edit {
|
||||
putString(libraryPreferences.sortingMode().key(), newSortingMode)
|
||||
}
|
||||
handler.await(true) {
|
||||
categoriesQueries.getCategories(CategoryMapper::mapCategory).executeAsList()
|
||||
.filter { (it.flags and 0b00111100L) == 0b00100000L }
|
||||
.forEach {
|
||||
categoriesQueries.update(
|
||||
categoryId = it.id,
|
||||
flags = it.flags and 0b00111100L.inv(),
|
||||
name = null,
|
||||
order = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return@withIOContext true
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package mihon.core.migration.migrations
|
||||
|
||||
import mihon.core.migration.Migration
|
||||
import mihon.core.migration.MigrationContext
|
||||
import tachiyomi.core.common.preference.getAndSet
|
||||
import tachiyomi.core.common.util.lang.withIOContext
|
||||
import tachiyomi.domain.library.service.LibraryPreferences
|
||||
|
||||
class RemoveBatteryNotLowRestrictionMigration : Migration {
|
||||
override val version: Float = 56f
|
||||
|
||||
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||
val libraryPreferences = migrationContext.get<LibraryPreferences>() ?: return@withIOContext false
|
||||
val pref = libraryPreferences.autoUpdateDeviceRestrictions()
|
||||
if (pref.isSet() && "battery_not_low" in pref.get()) {
|
||||
pref.getAndSet { it - "battery_not_low" }
|
||||
}
|
||||
|
||||
return@withIOContext true
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package mihon.core.migration.migrations
|
||||
|
||||
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
|
||||
import mihon.core.migration.Migration
|
||||
import mihon.core.migration.MigrationContext
|
||||
import tachiyomi.core.common.util.lang.withIOContext
|
||||
|
||||
class RemoveOldReaderThemeMigration : Migration {
|
||||
override val version: Float = 18f
|
||||
|
||||
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||
val readerPreferences = migrationContext.get<ReaderPreferences>() ?: return@withIOContext false
|
||||
val readerTheme = readerPreferences.readerTheme().get()
|
||||
if (readerTheme == 4) {
|
||||
readerPreferences.readerTheme().set(3)
|
||||
}
|
||||
|
||||
return@withIOContext true
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package mihon.core.migration.migrations
|
||||
|
||||
import mihon.core.migration.Migration
|
||||
import mihon.core.migration.MigrationContext
|
||||
import tachiyomi.core.common.util.lang.withIOContext
|
||||
import tachiyomi.domain.library.service.LibraryPreferences
|
||||
|
||||
class RemoveShortLibraryUpdatesMigration : Migration {
|
||||
override val version: Float = 22f
|
||||
|
||||
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||
val libraryPreferences = migrationContext.get<LibraryPreferences>() ?: return@withIOContext false
|
||||
val updateInterval = libraryPreferences.autoUpdateInterval().get()
|
||||
if (updateInterval in listOf(3, 4, 6, 8)) {
|
||||
libraryPreferences.autoUpdateInterval().set(12)
|
||||
}
|
||||
|
||||
return@withIOContext true
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package mihon.core.migration.migrations
|
||||
|
||||
import mihon.core.migration.Migration
|
||||
import mihon.core.migration.MigrationContext
|
||||
import tachiyomi.core.common.util.lang.withIOContext
|
||||
import tachiyomi.domain.library.service.LibraryPreferences
|
||||
|
||||
class RemoveShorterLibraryUpdatesMigration : Migration {
|
||||
override val version: Float = 18f
|
||||
|
||||
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||
val libraryPreferences = migrationContext.get<LibraryPreferences>() ?: return@withIOContext false
|
||||
val updateInterval = libraryPreferences.autoUpdateInterval().get()
|
||||
if (updateInterval == 1 || updateInterval == 2) {
|
||||
libraryPreferences.autoUpdateInterval().set(3)
|
||||
}
|
||||
|
||||
return@withIOContext true
|
||||
}
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
package mihon.core.migration.migrations
|
||||
|
||||
import androidx.core.content.edit
|
||||
import androidx.preference.PreferenceManager
|
||||
import eu.kanade.tachiyomi.App
|
||||
import eu.kanade.tachiyomi.data.track.TrackerManager
|
||||
import eu.kanade.tachiyomi.util.system.workManager
|
||||
import mihon.core.migration.Migration
|
||||
import mihon.core.migration.MigrationContext
|
||||
import tachiyomi.core.common.preference.PreferenceStore
|
||||
import tachiyomi.core.common.preference.TriState
|
||||
import tachiyomi.core.common.preference.getEnum
|
||||
import tachiyomi.core.common.util.lang.withIOContext
|
||||
|
||||
class RemoveUpdateCheckerJobsMigration : Migration {
|
||||
override val version: Float = 52f
|
||||
|
||||
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||
val context = migrationContext.get<App>() ?: return@withIOContext false
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
val preferenceStore = migrationContext.get<PreferenceStore>() ?: return@withIOContext false
|
||||
val trackerManager = migrationContext.get<TrackerManager>() ?: return@withIOContext false
|
||||
// Removed background jobs
|
||||
context.workManager.cancelAllWorkByTag("UpdateChecker")
|
||||
context.workManager.cancelAllWorkByTag("ExtensionUpdate")
|
||||
prefs.edit {
|
||||
remove("automatic_ext_updates")
|
||||
}
|
||||
val prefKeys = listOf(
|
||||
"pref_filter_library_downloaded",
|
||||
"pref_filter_library_unread",
|
||||
"pref_filter_library_started",
|
||||
"pref_filter_library_bookmarked",
|
||||
"pref_filter_library_completed",
|
||||
"pref_filter_library_lewd",
|
||||
) + trackerManager.trackers.map { "pref_filter_library_tracked_${it.id}" }
|
||||
|
||||
prefKeys.forEach { key ->
|
||||
val pref = prefs.getInt(key, 0)
|
||||
prefs.edit {
|
||||
remove(key)
|
||||
|
||||
val newValue = when (pref) {
|
||||
1 -> TriState.ENABLED_IS
|
||||
2 -> TriState.ENABLED_NOT
|
||||
else -> TriState.DISABLED
|
||||
}
|
||||
|
||||
preferenceStore.getEnum("${key}_v2", TriState.DISABLED).set(newValue)
|
||||
}
|
||||
}
|
||||
|
||||
return@withIOContext true
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
package mihon.core.migration.migrations
|
||||
|
||||
import androidx.core.content.edit
|
||||
import androidx.preference.PreferenceManager
|
||||
import eu.kanade.tachiyomi.App
|
||||
import mihon.core.migration.Migration
|
||||
import mihon.core.migration.MigrationContext
|
||||
import tachiyomi.core.common.util.lang.withIOContext
|
||||
import tachiyomi.domain.library.service.LibraryPreferences
|
||||
|
||||
class ResetFilterAndSortSettingsMigration : Migration {
|
||||
override val version: Float = 41f
|
||||
|
||||
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||
val context = migrationContext.get<App>() ?: return@withIOContext false
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
val libraryPreferences = migrationContext.get<LibraryPreferences>() ?: return@withIOContext false
|
||||
val preferences = listOf(
|
||||
libraryPreferences.filterChapterByRead(),
|
||||
libraryPreferences.filterChapterByDownloaded(),
|
||||
libraryPreferences.filterChapterByBookmarked(),
|
||||
libraryPreferences.sortChapterBySourceOrNumber(),
|
||||
libraryPreferences.displayChapterByNameOrNumber(),
|
||||
libraryPreferences.sortChapterByAscendingOrDescending(),
|
||||
)
|
||||
|
||||
prefs.edit {
|
||||
preferences.forEach { preference ->
|
||||
val key = preference.key()
|
||||
val value = prefs.getInt(key, Int.MIN_VALUE)
|
||||
if (value == Int.MIN_VALUE) return@forEach
|
||||
remove(key)
|
||||
putLong(key, value.toLong())
|
||||
}
|
||||
}
|
||||
|
||||
return@withIOContext true
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
package mihon.core.migration.migrations
|
||||
|
||||
import androidx.core.content.edit
|
||||
import androidx.preference.PreferenceManager
|
||||
import eu.kanade.tachiyomi.App
|
||||
import eu.kanade.tachiyomi.ui.reader.setting.ReaderOrientation
|
||||
import mihon.core.migration.Migration
|
||||
import mihon.core.migration.MigrationContext
|
||||
import tachiyomi.core.common.util.lang.withIOContext
|
||||
|
||||
class ResetReaderSettingsMigration : Migration {
|
||||
override val version: Float = 17f
|
||||
|
||||
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||
val context = migrationContext.get<App>() ?: return@withIOContext false
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
// Migrate Rotation and Viewer values to default values for viewer_flags
|
||||
val newOrientation = when (prefs.getInt("pref_rotation_type_key", 1)) {
|
||||
1 -> ReaderOrientation.FREE.flagValue
|
||||
2 -> ReaderOrientation.PORTRAIT.flagValue
|
||||
3 -> ReaderOrientation.LANDSCAPE.flagValue
|
||||
4 -> ReaderOrientation.LOCKED_PORTRAIT.flagValue
|
||||
5 -> ReaderOrientation.LOCKED_LANDSCAPE.flagValue
|
||||
else -> ReaderOrientation.FREE.flagValue
|
||||
}
|
||||
|
||||
// Reading mode flag and prefValue is the same value
|
||||
val newReadingMode = prefs.getInt("pref_default_viewer_key", 1)
|
||||
|
||||
prefs.edit {
|
||||
putInt("pref_default_orientation_type_key", newOrientation)
|
||||
remove("pref_rotation_type_key")
|
||||
putInt("pref_default_reading_mode_key", newReadingMode)
|
||||
remove("pref_default_viewer_key")
|
||||
}
|
||||
|
||||
return@withIOContext true
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package mihon.core.migration.migrations
|
||||
|
||||
import androidx.core.content.edit
|
||||
import androidx.preference.PreferenceManager
|
||||
import eu.kanade.tachiyomi.App
|
||||
import mihon.core.migration.Migration
|
||||
import mihon.core.migration.MigrationContext
|
||||
import tachiyomi.core.common.util.lang.withIOContext
|
||||
|
||||
class ResetRotationSettingMigration : Migration {
|
||||
override val version: Float = 16f
|
||||
|
||||
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||
val context = migrationContext.get<App>() ?: return@withIOContext false
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
// Reset rotation to Free after replacing Lock
|
||||
if (prefs.contains("pref_rotation_type_key")) {
|
||||
prefs.edit {
|
||||
putInt("pref_rotation_type_key", 1)
|
||||
}
|
||||
}
|
||||
|
||||
return@withIOContext true
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package mihon.core.migration.migrations
|
||||
|
||||
import eu.kanade.tachiyomi.App
|
||||
import eu.kanade.tachiyomi.data.backup.create.BackupCreateJob
|
||||
import mihon.core.migration.Migration
|
||||
import mihon.core.migration.MigrationContext
|
||||
|
||||
class SetupBackupCreateMigration : Migration {
|
||||
override val version: Float = Migration.ALWAYS
|
||||
|
||||
override suspend fun invoke(migrationContext: MigrationContext): Boolean {
|
||||
val context = migrationContext.get<App>() ?: return false
|
||||
BackupCreateJob.setupTask(context)
|
||||
return true
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package mihon.core.migration.migrations
|
||||
|
||||
import eu.kanade.tachiyomi.App
|
||||
import exh.eh.EHentaiUpdateWorker
|
||||
import mihon.core.migration.Migration
|
||||
import mihon.core.migration.MigrationContext
|
||||
|
||||
class SetupEHentaiUpdateMigration : Migration {
|
||||
override val version: Float = Migration.ALWAYS
|
||||
|
||||
override suspend fun invoke(migrationContext: MigrationContext): Boolean {
|
||||
val context = migrationContext.get<App>() ?: return false
|
||||
EHentaiUpdateWorker.scheduleBackground(context)
|
||||
return true
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package mihon.core.migration.migrations
|
||||
|
||||
import eu.kanade.tachiyomi.App
|
||||
import eu.kanade.tachiyomi.data.library.LibraryUpdateJob
|
||||
import mihon.core.migration.Migration
|
||||
import mihon.core.migration.MigrationContext
|
||||
|
||||
class SetupLibraryUpdateMigration : Migration {
|
||||
override val version: Float = Migration.ALWAYS
|
||||
|
||||
override suspend fun invoke(migrationContext: MigrationContext): Boolean {
|
||||
val context = migrationContext.get<App>() ?: return false
|
||||
LibraryUpdateJob.setupTask(context)
|
||||
return true
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package mihon.core.migration.migrations
|
||||
|
||||
import eu.kanade.tachiyomi.App
|
||||
import eu.kanade.tachiyomi.data.sync.SyncDataJob
|
||||
import mihon.core.migration.Migration
|
||||
import mihon.core.migration.MigrationContext
|
||||
|
||||
class SetupSyncDataMigration : Migration {
|
||||
override val version: Float = Migration.ALWAYS
|
||||
|
||||
override suspend fun invoke(migrationContext: MigrationContext): Boolean {
|
||||
val context = migrationContext.get<App>() ?: return false
|
||||
SyncDataJob.setupTask(context)
|
||||
return true
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
package mihon.core.migration.migrations
|
||||
|
||||
import eu.kanade.domain.source.service.SourcePreferences
|
||||
import logcat.LogPriority
|
||||
import mihon.core.migration.Migration
|
||||
import mihon.core.migration.MigrationContext
|
||||
import mihon.domain.extensionrepo.exception.SaveExtensionRepoException
|
||||
import mihon.domain.extensionrepo.repository.ExtensionRepoRepository
|
||||
import tachiyomi.core.common.util.lang.withIOContext
|
||||
import tachiyomi.core.common.util.system.logcat
|
||||
|
||||
class TrustExtensionRepositoryMigration : Migration {
|
||||
override val version: Float = 67f
|
||||
|
||||
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||
val sourcePreferences = migrationContext.get<SourcePreferences>() ?: return@withIOContext false
|
||||
val extensionRepositoryRepository =
|
||||
migrationContext.get<ExtensionRepoRepository>() ?: return@withIOContext false
|
||||
for ((index, source) in sourcePreferences.extensionRepos().get().withIndex()) {
|
||||
try {
|
||||
extensionRepositoryRepository.upsertRepo(
|
||||
source,
|
||||
"Repo #${index + 1}",
|
||||
null,
|
||||
source,
|
||||
"NOFINGERPRINT-${index + 1}",
|
||||
)
|
||||
} catch (e: SaveExtensionRepoException) {
|
||||
logcat(LogPriority.ERROR, e) { "Error Migrating Extension Repo with baseUrl: $source" }
|
||||
}
|
||||
}
|
||||
sourcePreferences.extensionRepos().delete()
|
||||
return@withIOContext true
|
||||
}
|
||||
}
|
96
app/src/test/java/mihon/core/migration/MigratorTest.kt
Normal file
96
app/src/test/java/mihon/core/migration/MigratorTest.kt
Normal file
@ -0,0 +1,96 @@
|
||||
package mihon.core.migration
|
||||
|
||||
import io.mockk.Called
|
||||
import io.mockk.spyk
|
||||
import io.mockk.verify
|
||||
import org.junit.jupiter.api.Assertions
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class MigratorTest {
|
||||
|
||||
@Test
|
||||
fun initialVersion() {
|
||||
val onMigrationComplete: () -> Unit = {}
|
||||
val onMigrationCompleteSpy = spyk(onMigrationComplete)
|
||||
val didMigration = Migrator.migrate(
|
||||
old = 0,
|
||||
new = 1,
|
||||
migrations = listOf(Migration.of(Migration.ALWAYS) { true }, Migration.of(2f) { false }),
|
||||
onMigrationComplete = onMigrationCompleteSpy
|
||||
)
|
||||
verify { onMigrationCompleteSpy() }
|
||||
Assertions.assertTrue(didMigration)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun sameVersion() {
|
||||
val onMigrationComplete: () -> Unit = {}
|
||||
val onMigrationCompleteSpy = spyk(onMigrationComplete)
|
||||
val didMigration = Migrator.migrate(
|
||||
old = 1,
|
||||
new = 1,
|
||||
migrations = listOf(Migration.of(Migration.ALWAYS) { true }, Migration.of(2f) { true }),
|
||||
onMigrationComplete = onMigrationCompleteSpy
|
||||
)
|
||||
verify { onMigrationCompleteSpy wasNot Called }
|
||||
Assertions.assertFalse(didMigration)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun smallMigration() {
|
||||
val onMigrationComplete: () -> Unit = {}
|
||||
val onMigrationCompleteSpy = spyk(onMigrationComplete)
|
||||
val didMigration = Migrator.migrate(
|
||||
old = 1,
|
||||
new = 2,
|
||||
migrations = listOf(Migration.of(Migration.ALWAYS) { true }, Migration.of(2f) { true }),
|
||||
onMigrationComplete = onMigrationCompleteSpy
|
||||
)
|
||||
verify { onMigrationCompleteSpy() }
|
||||
Assertions.assertTrue(didMigration)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun largeMigration() {
|
||||
val onMigrationComplete: () -> Unit = {}
|
||||
val onMigrationCompleteSpy = spyk(onMigrationComplete)
|
||||
val input = listOf(
|
||||
Migration.of(Migration.ALWAYS) { true },
|
||||
Migration.of(2f) { true },
|
||||
Migration.of(3f) { true },
|
||||
Migration.of(4f) { true },
|
||||
Migration.of(5f) { true },
|
||||
Migration.of(6f) { true },
|
||||
Migration.of(7f) { true },
|
||||
Migration.of(8f) { true },
|
||||
Migration.of(9f) { true },
|
||||
Migration.of(10f) { true },
|
||||
)
|
||||
val didMigration = Migrator.migrate(
|
||||
old = 1,
|
||||
new = 10,
|
||||
migrations = input,
|
||||
onMigrationComplete = onMigrationCompleteSpy
|
||||
)
|
||||
verify { onMigrationCompleteSpy() }
|
||||
Assertions.assertTrue(didMigration)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun withinRangeMigration() {
|
||||
val onMigrationComplete: () -> Unit = {}
|
||||
val onMigrationCompleteSpy = spyk(onMigrationComplete)
|
||||
val didMigration = Migrator.migrate(
|
||||
old = 1,
|
||||
new = 2,
|
||||
migrations = listOf(
|
||||
Migration.of(Migration.ALWAYS) { true },
|
||||
Migration.of(2f) { true },
|
||||
Migration.of(3f) { false }
|
||||
),
|
||||
onMigrationComplete = onMigrationCompleteSpy
|
||||
)
|
||||
verify { onMigrationCompleteSpy() }
|
||||
Assertions.assertTrue(didMigration)
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user