refactor: Use DB instead of file to store custom manga info

This commit is contained in:
Ahmad Ansori Palembani 2024-06-05 07:29:04 +07:00
parent 716cc1fac8
commit 87b8430089
Signed by: null2264
GPG key ID: BA64F8B60AF3EFB6
16 changed files with 354 additions and 61 deletions

View file

@ -17,3 +17,4 @@ Please backup your data before updating to this version.
## Other ## Other
- Migrate to SQLDelight - Migrate to SQLDelight
- Custom manga info is now stored in the database

View file

@ -2,6 +2,7 @@ package dev.yokai.core.di
import dev.yokai.domain.extension.repo.ExtensionRepoRepository import dev.yokai.domain.extension.repo.ExtensionRepoRepository
import dev.yokai.data.extension.repo.ExtensionRepoRepositoryImpl import dev.yokai.data.extension.repo.ExtensionRepoRepositoryImpl
import dev.yokai.data.library.custom.CustomMangaRepositoryImpl
import dev.yokai.domain.extension.interactor.TrustExtension import dev.yokai.domain.extension.interactor.TrustExtension
import dev.yokai.domain.extension.repo.interactor.CreateExtensionRepo import dev.yokai.domain.extension.repo.interactor.CreateExtensionRepo
import dev.yokai.domain.extension.repo.interactor.DeleteExtensionRepo import dev.yokai.domain.extension.repo.interactor.DeleteExtensionRepo
@ -9,6 +10,10 @@ import dev.yokai.domain.extension.repo.interactor.GetExtensionRepo
import dev.yokai.domain.extension.repo.interactor.GetExtensionRepoCount import dev.yokai.domain.extension.repo.interactor.GetExtensionRepoCount
import dev.yokai.domain.extension.repo.interactor.ReplaceExtensionRepo import dev.yokai.domain.extension.repo.interactor.ReplaceExtensionRepo
import dev.yokai.domain.extension.repo.interactor.UpdateExtensionRepo import dev.yokai.domain.extension.repo.interactor.UpdateExtensionRepo
import dev.yokai.domain.library.custom.CustomMangaRepository
import dev.yokai.domain.library.custom.interactor.CreateCustomManga
import dev.yokai.domain.library.custom.interactor.DeleteCustomManga
import dev.yokai.domain.library.custom.interactor.GetCustomManga
import uy.kohesive.injekt.api.InjektModule import uy.kohesive.injekt.api.InjektModule
import uy.kohesive.injekt.api.InjektRegistrar import uy.kohesive.injekt.api.InjektRegistrar
import uy.kohesive.injekt.api.addFactory import uy.kohesive.injekt.api.addFactory
@ -17,6 +22,8 @@ import uy.kohesive.injekt.api.get
class DomainModule : InjektModule { class DomainModule : InjektModule {
override fun InjektRegistrar.registerInjectables() { override fun InjektRegistrar.registerInjectables() {
addFactory { TrustExtension(get(), get()) }
addSingletonFactory<ExtensionRepoRepository> { ExtensionRepoRepositoryImpl(get()) } addSingletonFactory<ExtensionRepoRepository> { ExtensionRepoRepositoryImpl(get()) }
addFactory { CreateExtensionRepo(get()) } addFactory { CreateExtensionRepo(get()) }
addFactory { DeleteExtensionRepo(get()) } addFactory { DeleteExtensionRepo(get()) }
@ -25,6 +32,9 @@ class DomainModule : InjektModule {
addFactory { ReplaceExtensionRepo(get()) } addFactory { ReplaceExtensionRepo(get()) }
addFactory { UpdateExtensionRepo(get(), get()) } addFactory { UpdateExtensionRepo(get(), get()) }
addFactory { TrustExtension(get(), get()) } addSingletonFactory<CustomMangaRepository> { CustomMangaRepositoryImpl(get()) }
addFactory { CreateCustomManga(get()) }
addFactory { DeleteCustomManga(get()) }
addFactory { GetCustomManga(get()) }
} }
} }

View file

@ -0,0 +1,75 @@
package dev.yokai.data.library.custom
import android.database.sqlite.SQLiteException
import dev.yokai.data.DatabaseHandler
import dev.yokai.domain.library.custom.CustomMangaRepository
import dev.yokai.domain.library.custom.exception.SaveCustomMangaException
import dev.yokai.domain.library.custom.model.CustomMangaInfo
import kotlinx.coroutines.flow.Flow
import timber.log.Timber
class CustomMangaRepositoryImpl(private val handler: DatabaseHandler) : CustomMangaRepository {
override fun subscribeAll(): Flow<List<CustomMangaInfo>> =
handler.subscribeToList { custom_manga_infoQueries.findAll(::mapCustomMangaInfo) }
override suspend fun getAll(): List<CustomMangaInfo> =
handler.awaitList { custom_manga_infoQueries.findAll(::mapCustomMangaInfo) }
override suspend fun insertCustomManga(
mangaId: Long,
title: String?,
author: String?,
artist: String?,
description: String?,
genre: String?,
status: Int?
) {
try {
handler.await { custom_manga_infoQueries.insert(mangaId, title, author, artist, description, genre, status?.toLong()) }
} catch (exc: SQLiteException) {
Timber.e(exc)
throw SaveCustomMangaException(exc)
}
}
override suspend fun insertBulkCustomManga(mangaList: List<CustomMangaInfo>) {
try {
handler.await(true) {
for (customMangaInfo in mangaList) {
custom_manga_infoQueries.insert(
customMangaInfo.mangaId,
customMangaInfo.title,
customMangaInfo.author,
customMangaInfo.artist,
customMangaInfo.description,
customMangaInfo.genre,
customMangaInfo.status?.toLong(),
)
}
}
} catch (exc: SQLiteException) {
Timber.e(exc)
throw SaveCustomMangaException(exc)
}
}
override suspend fun deleteCustomManga(mangaId: Long) =
handler.await { custom_manga_infoQueries.delete(mangaId) }
override suspend fun deleteBulkCustomManga(mangaIds: List<Long>) =
handler.await(true) {
for (mangaId in mangaIds) {
custom_manga_infoQueries.delete(mangaId)
}
}
private fun mapCustomMangaInfo(
mangaId: Long,
title: String?,
author: String?,
artist: String?,
description: String?,
genre: String?,
status: Long?
): CustomMangaInfo = CustomMangaInfo(mangaId, title, author, artist, description, genre, status?.toInt())
}

View file

@ -0,0 +1,31 @@
package dev.yokai.domain.library.custom
import dev.yokai.domain.library.custom.model.CustomMangaInfo
import kotlinx.coroutines.flow.Flow
interface CustomMangaRepository {
fun subscribeAll(): Flow<List<CustomMangaInfo>>
suspend fun getAll(): List<CustomMangaInfo>
suspend fun insertCustomManga(
mangaId: Long,
title: String? = null,
author: String? = null,
artist: String? = null,
description: String? = null,
genre: String? = null,
status: Int? = null,
)
suspend fun insertCustomManga(mangaInfo: CustomMangaInfo) =
insertCustomManga(
mangaInfo.mangaId,
mangaInfo.title,
mangaInfo.author,
mangaInfo.artist,
mangaInfo.description,
mangaInfo.genre,
mangaInfo.status,
)
suspend fun insertBulkCustomManga(mangaList: List<CustomMangaInfo>)
suspend fun deleteCustomManga(mangaId: Long)
suspend fun deleteBulkCustomManga(mangaIds: List<Long>)
}

View file

@ -0,0 +1,10 @@
package dev.yokai.domain.library.custom.exception
import java.io.IOException
/**
* Exception to abstract over SQLiteException and SQLiteConstraintException for multiplatform.
*
* @param throwable the source throwable to include for tracing.
*/
class SaveCustomMangaException(throwable: Throwable) : IOException("Error Saving Custom Manga Info to Database", throwable)

View file

@ -0,0 +1,32 @@
package dev.yokai.domain.library.custom.interactor
import dev.yokai.domain.library.custom.CustomMangaRepository
import dev.yokai.domain.library.custom.exception.SaveCustomMangaException
import dev.yokai.domain.library.custom.model.CustomMangaInfo
class CreateCustomManga(
private val customMangaRepository: CustomMangaRepository,
) {
suspend fun await(mangaInfo: CustomMangaInfo): Result {
try {
customMangaRepository.insertCustomManga(mangaInfo)
return Result.Success
} catch (exc: SaveCustomMangaException) {
return Result.Error
}
}
suspend fun bulk(mangaList: List<CustomMangaInfo>): Result {
try {
customMangaRepository.insertBulkCustomManga(mangaList)
return Result.Success
} catch (exc: SaveCustomMangaException) {
return Result.Error
}
}
sealed interface Result {
data object Success : Result
data object Error : Result
}
}

View file

@ -0,0 +1,10 @@
package dev.yokai.domain.library.custom.interactor
import dev.yokai.domain.library.custom.CustomMangaRepository
class DeleteCustomManga(
private val customMangaRepository: CustomMangaRepository,
) {
suspend fun await(mangaId: Long) = customMangaRepository.deleteCustomManga(mangaId)
suspend fun bulk(mangaIds: List<Long>) = customMangaRepository.deleteBulkCustomManga(mangaIds)
}

View file

@ -0,0 +1,11 @@
package dev.yokai.domain.library.custom.interactor
import dev.yokai.domain.library.custom.CustomMangaRepository
class GetCustomManga(
private val customMangaRepository: CustomMangaRepository,
) {
fun subscribeAll() = customMangaRepository.subscribeAll()
suspend fun getAll() = customMangaRepository.getAll()
}

View file

@ -0,0 +1,37 @@
package dev.yokai.domain.library.custom.model
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.database.models.MangaImpl
data class CustomMangaInfo(
var mangaId: Long,
val title: String? = null,
val author: String? = null,
val artist: String? = null,
val description: String? = null,
val genre: String? = null,
val status: Int? = null,
) {
fun toManga() = MangaImpl().apply {
id = this@CustomMangaInfo.mangaId
title = this@CustomMangaInfo.title ?: ""
author = this@CustomMangaInfo.author
artist = this@CustomMangaInfo.artist
description = this@CustomMangaInfo.description
genre = this@CustomMangaInfo.genre
status = this@CustomMangaInfo.status ?: -1
}
companion object {
fun Manga.getMangaInfo() =
CustomMangaInfo(
mangaId = id!!,
title = title,
author = author,
artist = artist,
description = description,
genre = genre,
status = status,
)
}
}

View file

@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.data.backup
import android.content.Context import android.content.Context
import android.net.Uri import android.net.Uri
import dev.yokai.domain.library.custom.model.CustomMangaInfo
import dev.yokai.domain.ui.settings.ReaderPreferences import dev.yokai.domain.ui.settings.ReaderPreferences
import dev.yokai.domain.ui.settings.ReaderPreferences.CutoutBehaviour import dev.yokai.domain.ui.settings.ReaderPreferences.CutoutBehaviour
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
@ -37,6 +38,7 @@ import eu.kanade.tachiyomi.util.BackupUtil
import eu.kanade.tachiyomi.util.chapter.ChapterUtil import eu.kanade.tachiyomi.util.chapter.ChapterUtil
import eu.kanade.tachiyomi.util.manga.MangaUtil import eu.kanade.tachiyomi.util.manga.MangaUtil
import eu.kanade.tachiyomi.util.system.createFileInCacheDir import eu.kanade.tachiyomi.util.system.createFileInCacheDir
import eu.kanade.tachiyomi.util.system.launchNow
import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.isActive import kotlinx.coroutines.isActive
import kotlinx.serialization.protobuf.ProtoBuf import kotlinx.serialization.protobuf.ProtoBuf
@ -198,7 +200,7 @@ class BackupRestorer(val context: Context, val notifier: BackupNotifier) {
tracks: List<Track>, tracks: List<Track>,
backupCategories: List<BackupCategory>, backupCategories: List<BackupCategory>,
filteredScanlators: List<String>, filteredScanlators: List<String>,
customManga: CustomMangaManager.ComicList.ComicInfoYokai?, customManga: CustomMangaInfo?,
) { ) {
val fetchedManga = manga.also { val fetchedManga = manga.also {
it.initialized = it.description != null it.initialized = it.description != null
@ -218,7 +220,7 @@ class BackupRestorer(val context: Context, val notifier: BackupNotifier) {
tracks: List<Track>, tracks: List<Track>,
backupCategories: List<BackupCategory>, backupCategories: List<BackupCategory>,
filteredScanlators: List<String>, filteredScanlators: List<String>,
customManga: CustomMangaManager.ComicList.ComicInfoYokai?, customManga: CustomMangaInfo?,
) { ) {
restoreChapters(backupManga, chapters) restoreChapters(backupManga, chapters)
restoreExtras(backupManga, categories, history, tracks, backupCategories, filteredScanlators, customManga) restoreExtras(backupManga, categories, history, tracks, backupCategories, filteredScanlators, customManga)
@ -258,14 +260,18 @@ class BackupRestorer(val context: Context, val notifier: BackupNotifier) {
tracks: List<Track>, tracks: List<Track>,
backupCategories: List<BackupCategory>, backupCategories: List<BackupCategory>,
filteredScanlators: List<String>, filteredScanlators: List<String>,
customManga: CustomMangaManager.ComicList.ComicInfoYokai?, customManga: CustomMangaInfo?,
) { ) {
restoreCategories(manga, categories, backupCategories) restoreCategories(manga, categories, backupCategories)
restoreHistoryForManga(history) restoreHistoryForManga(history)
restoreTrackForManga(manga, tracks) restoreTrackForManga(manga, tracks)
restoreFilteredScanlatorsForManga(manga, filteredScanlators) restoreFilteredScanlatorsForManga(manga, filteredScanlators)
customManga?.id = manga.id!! customManga?.let {
customManga?.let { customMangaManager.saveMangaInfo(it) } it.mangaId = manga.id!!
launchNow {
customMangaManager.saveMangaInfo(it)
}
}
} }
/** /**

View file

@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.data.backup.models
import dev.yokai.core.metadata.ComicInfo import dev.yokai.core.metadata.ComicInfo
import dev.yokai.core.metadata.ComicInfoPublishingStatus import dev.yokai.core.metadata.ComicInfoPublishingStatus
import dev.yokai.domain.library.custom.model.CustomMangaInfo
import eu.kanade.tachiyomi.data.database.models.ChapterImpl import eu.kanade.tachiyomi.data.database.models.ChapterImpl
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.database.models.MangaImpl import eu.kanade.tachiyomi.data.database.models.MangaImpl
@ -85,7 +86,7 @@ data class BackupManga(
} }
} }
fun getCustomMangaInfo(): CustomMangaManager.ComicList.ComicInfoYokai? { fun getCustomMangaInfo(): CustomMangaInfo? {
if (customTitle != null || if (customTitle != null ||
customArtist != null || customArtist != null ||
customAuthor != null || customAuthor != null ||
@ -93,13 +94,13 @@ data class BackupManga(
customGenre != null || customGenre != null ||
customStatus != 0 customStatus != 0
) { ) {
return CustomMangaManager.ComicList.ComicInfoYokai.create( return CustomMangaInfo(
id = 0L, mangaId= 0L,
title = customTitle, title = customTitle,
author = customAuthor, author = customAuthor,
artist = customArtist, artist = customArtist,
description = customDescription, description = customDescription,
genre = customGenre?.toTypedArray(), genre = customGenre?.joinToString(),
status = customStatus, status = customStatus,
) )
} }

View file

@ -6,9 +6,16 @@ import dev.yokai.core.metadata.COMIC_INFO_EDITS_FILE
import dev.yokai.core.metadata.ComicInfo import dev.yokai.core.metadata.ComicInfo
import dev.yokai.core.metadata.ComicInfoPublishingStatus import dev.yokai.core.metadata.ComicInfoPublishingStatus
import dev.yokai.core.metadata.copyFromComicInfo import dev.yokai.core.metadata.copyFromComicInfo
import dev.yokai.domain.library.custom.interactor.CreateCustomManga
import dev.yokai.domain.library.custom.interactor.DeleteCustomManga
import dev.yokai.domain.library.custom.interactor.GetCustomManga
import dev.yokai.domain.library.custom.model.CustomMangaInfo
import dev.yokai.domain.library.custom.model.CustomMangaInfo.Companion.getMangaInfo
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.database.models.MangaImpl import eu.kanade.tachiyomi.data.database.models.MangaImpl
import eu.kanade.tachiyomi.util.system.writeText import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.json.decodeFromStream import kotlinx.serialization.json.decodeFromStream
@ -17,44 +24,46 @@ import nl.adaptivity.xmlutil.serialization.XML
import nl.adaptivity.xmlutil.serialization.XmlSerialName import nl.adaptivity.xmlutil.serialization.XmlSerialName
import nl.adaptivity.xmlutil.serialization.XmlValue import nl.adaptivity.xmlutil.serialization.XmlValue
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import java.io.InputStream
import java.nio.charset.StandardCharsets import java.nio.charset.StandardCharsets
class CustomMangaManager(val context: Context) { class CustomMangaManager(val context: Context) {
private val scope = CoroutineScope(Dispatchers.IO)
private val xml: XML by injectLazy() private val xml: XML by injectLazy()
private val externalDir = UniFile.fromFile(context.getExternalFilesDir(null)) private val externalDir = UniFile.fromFile(context.getExternalFilesDir(null))
private var removedCustomManga = mutableListOf<Long>()
private var customMangaMap = mutableMapOf<Long, Manga>() private var customMangaMap = mutableMapOf<Long, Manga>()
private val createCustomManga: CreateCustomManga by injectLazy()
private val deleteCustomManga: DeleteCustomManga by injectLazy()
private val getCustomManga: GetCustomManga by injectLazy()
init { init {
fetchCustomData() scope.launch {
fetchCustomData()
}
} }
companion object { companion object {
const val EDIT_JSON_FILE = "edits.json" const val EDIT_JSON_FILE = "edits.json"
fun Manga.toComicInfo(): ComicList.ComicInfoYokai {
return ComicList.ComicInfoYokai.create(
id = id!!,
title = title,
author = author,
artist = artist,
description = description,
genre = genre.orEmpty(),
status = status,
)
}
} }
fun getManga(manga: Manga): Manga? = customMangaMap[manga.id] fun getManga(manga: Manga): Manga? = customMangaMap[manga.id]
private fun fetchCustomData() { private suspend fun fetchCustomData() {
val comicInfoEdits = externalDir?.findFile(COMIC_INFO_EDITS_FILE) val comicInfoEdits = externalDir?.findFile(COMIC_INFO_EDITS_FILE)
val editJson = externalDir?.findFile(EDIT_JSON_FILE) val editJson = externalDir?.findFile(EDIT_JSON_FILE)
val dbMangaInfo = getCustomManga.getAll()
customMangaMap = dbMangaInfo.associate { info ->
val id = info.mangaId
id to info.toManga()
}.toMutableMap()
if (comicInfoEdits != null && comicInfoEdits.exists() && comicInfoEdits.isFile) { if (comicInfoEdits != null && comicInfoEdits.exists() && comicInfoEdits.isFile) {
fetchFromComicInfo(comicInfoEdits.openInputStream()) fetchFromComicInfo(comicInfoEdits)
return return
} }
@ -65,20 +74,22 @@ class CustomMangaManager(val context: Context) {
} }
} }
private fun fetchFromComicInfo(stream: InputStream) { private suspend fun fetchFromComicInfo(comicInfoFile: UniFile) {
val comicInfoEdits = AndroidXmlReader(stream, StandardCharsets.UTF_8.name()).use { val comicInfoEdits = AndroidXmlReader(comicInfoFile.openInputStream(), StandardCharsets.UTF_8.name()).use {
xml.decodeFromReader<ComicList>(it) xml.decodeFromReader<ComicList>(it)
} }
if (comicInfoEdits.comics == null) return if (comicInfoEdits.comics == null) return
customMangaMap = comicInfoEdits.comics.mapNotNull { obj -> comicInfoEdits.comics.mapNotNull { obj ->
val id = obj.id ?: return@mapNotNull null val id = obj.id ?: return@mapNotNull null
id to mangaFromComicInfoObject(id, obj.value) customMangaMap[id] = mangaFromComicInfoObject(id, obj.value)
}.toMap().toMutableMap() }
saveCustomInfo { comicInfoFile.delete() }
} }
private fun fetchFromLegacyJson(jsonFile: UniFile) { private suspend fun fetchFromLegacyJson(jsonFile: UniFile) {
val json = try { val json = try {
Json.decodeFromStream<MangaList>(jsonFile.openInputStream()) Json.decodeFromStream<MangaList>(jsonFile.openInputStream())
} catch (e: Exception) { } catch (e: Exception) {
@ -86,36 +97,41 @@ class CustomMangaManager(val context: Context) {
} ?: return } ?: return
val mangasJson = json.mangas ?: return val mangasJson = json.mangas ?: return
customMangaMap = mangasJson.mapNotNull { mangaObject -> mangasJson.mapNotNull { mangaObject ->
val id = mangaObject.id ?: return@mapNotNull null val id = mangaObject.id ?: return@mapNotNull null
id to mangaObject.toManga() customMangaMap[id] = mangaObject.toManga()
}.toMap().toMutableMap() }
saveCustomInfo { jsonFile.delete() } saveCustomInfo { jsonFile.delete() }
} }
fun saveMangaInfo(manga: ComicList.ComicInfoYokai) { suspend fun saveMangaInfo(manga: CustomMangaInfo) {
val mangaId = manga.id ?: return val mangaId = manga.mangaId ?: return
if (manga.value.series == null && if (manga.title == null &&
manga.value.writer == null && manga.author == null &&
manga.value.penciller == null && manga.artist == null &&
manga.value.summary == null && manga.description == null &&
manga.value.genre == null && manga.genre == null &&
(manga.value.publishingStatus?.value ?: "Invalid") == "Invalid" (manga.status ?: -1) == -1
) { ) {
customMangaMap.remove(mangaId) customMangaMap[mangaId]?.let {
removedCustomManga.add(mangaId)
customMangaMap.remove(mangaId)
}
} else { } else {
customMangaMap[mangaId] = mangaFromComicInfoObject(mangaId, manga.value) customMangaMap[mangaId] = manga.toManga()
} }
saveCustomInfo() saveCustomInfo()
} }
private fun saveCustomInfo(onComplete: () -> Unit = {}) { private suspend fun saveCustomInfo(onComplete: () -> Unit = {}) {
val comicInfoEdits = externalDir?.createFile(COMIC_INFO_EDITS_FILE) ?: return deleteCustomManga.bulk(removedCustomManga)
removedCustomManga = mutableListOf()
val edits = customMangaMap.values.map { it.toComicInfo() } val edits = customMangaMap.values.map { it.getMangaInfo() }
if (edits.isNotEmpty() && comicInfoEdits.exists()) { if (edits.isNotEmpty()) {
comicInfoEdits.writeText(xml.encodeToString(ComicList.serializer(), ComicList(edits)), onComplete = onComplete) createCustomManga.bulk(edits)
onComplete()
} }
} }

View file

@ -10,6 +10,7 @@ import coil3.request.CachePolicy
import coil3.request.ImageRequest import coil3.request.ImageRequest
import coil3.request.SuccessResult import coil3.request.SuccessResult
import com.hippo.unifile.UniFile import com.hippo.unifile.UniFile
import dev.yokai.domain.library.custom.model.CustomMangaInfo
import dev.yokai.domain.storage.StorageManager import dev.yokai.domain.storage.StorageManager
import eu.kanade.tachiyomi.BuildConfig import eu.kanade.tachiyomi.BuildConfig
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
@ -55,6 +56,7 @@ import eu.kanade.tachiyomi.util.storage.DiskUtil
import eu.kanade.tachiyomi.util.system.ImageUtil import eu.kanade.tachiyomi.util.system.ImageUtil
import eu.kanade.tachiyomi.util.system.executeOnIO import eu.kanade.tachiyomi.util.system.executeOnIO
import eu.kanade.tachiyomi.util.system.launchIO import eu.kanade.tachiyomi.util.system.launchIO
import eu.kanade.tachiyomi.util.system.launchNow
import eu.kanade.tachiyomi.util.system.launchUI import eu.kanade.tachiyomi.util.system.launchUI
import eu.kanade.tachiyomi.util.system.withUIContext import eu.kanade.tachiyomi.util.system.withUIContext
import eu.kanade.tachiyomi.widget.TriStateCheckBox import eu.kanade.tachiyomi.widget.TriStateCheckBox
@ -712,13 +714,13 @@ class MangaDetailsPresenter(
fun confirmDeletion() { fun confirmDeletion() {
launchIO { launchIO {
coverCache.deleteFromCache(manga) coverCache.deleteFromCache(manga)
customMangaManager.saveMangaInfo(CustomMangaManager.ComicList.ComicInfoYokai.create( customMangaManager.saveMangaInfo(CustomMangaInfo(
id = manga.id!!, mangaId = manga.id!!,
title = null, title = null,
author = null, author = null,
artist = null, artist = null,
description = null, description = null,
genre = null as String?, genre = null,
status = null, status = null,
)) ))
downloadManager.deleteManga(manga, source) downloadManager.deleteManga(manga, source)
@ -801,20 +803,22 @@ class MangaDetailsPresenter(
null null
} }
if (seriesType != null) { if (seriesType != null) {
genre = setSeriesType(seriesType, genre?.joinToString(", ")) genre = setSeriesType(seriesType, genre?.joinToString())
manga.viewer_flags = -1 manga.viewer_flags = -1
db.updateViewerFlags(manga).executeAsBlocking() db.updateViewerFlags(manga).executeAsBlocking()
} }
val manga = CustomMangaManager.ComicList.ComicInfoYokai.create( val manga = CustomMangaInfo(
id = manga.id!!, mangaId = manga.id!!,
title?.trimOrNull(), title?.trimOrNull(),
author?.trimOrNull(), author?.trimOrNull(),
artist?.trimOrNull(), artist?.trimOrNull(),
description?.trimOrNull(), description?.trimOrNull(),
genre, genre?.joinToString(),
if (status != this.manga.originalStatus) status else null, if (status != this.manga.originalStatus) status else null,
) )
customMangaManager.saveMangaInfo(manga) launchNow {
customMangaManager.saveMangaInfo(manga)
}
} }
if (uri != null) { if (uri != null) {
editCoverWithStream(uri) editCoverWithStream(uri)

View file

@ -1,6 +1,7 @@
package eu.kanade.tachiyomi.ui.migration.manga.process package eu.kanade.tachiyomi.ui.migration.manga.process
import android.view.MenuItem import android.view.MenuItem
import dev.yokai.domain.library.custom.model.CustomMangaInfo.Companion.getMangaInfo
import dev.yokai.domain.ui.UiPreferences import dev.yokai.domain.ui.UiPreferences
import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.kanade.tachiyomi.data.cache.CoverCache import eu.kanade.tachiyomi.data.cache.CoverCache
@ -9,13 +10,13 @@ import eu.kanade.tachiyomi.data.database.models.History
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.database.models.MangaCategory import eu.kanade.tachiyomi.data.database.models.MangaCategory
import eu.kanade.tachiyomi.data.library.CustomMangaManager import eu.kanade.tachiyomi.data.library.CustomMangaManager
import eu.kanade.tachiyomi.data.library.CustomMangaManager.Companion.toComicInfo
import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.track.EnhancedTrackService import eu.kanade.tachiyomi.data.track.EnhancedTrackService
import eu.kanade.tachiyomi.data.track.TrackManager import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.ui.migration.MigrationFlags import eu.kanade.tachiyomi.ui.migration.MigrationFlags
import eu.kanade.tachiyomi.util.system.launchNow
import eu.kanade.tachiyomi.util.system.launchUI import eu.kanade.tachiyomi.util.system.launchUI
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.cancel import kotlinx.coroutines.cancel
@ -229,7 +230,9 @@ class MigrationProcessAdapter(
} }
customMangaManager.getManga(prevManga)?.let { customManga -> customMangaManager.getManga(prevManga)?.let { customManga ->
customManga.id = manga.id!! customManga.id = manga.id!!
customMangaManager.saveMangaInfo(customManga.toComicInfo()) launchNow {
customMangaManager.saveMangaInfo(customManga.getMangaInfo())
}
} }
} }

View file

@ -0,0 +1,34 @@
CREATE TABLE custom_manga_info (
manga_id INTEGER NOT NULL PRIMARY KEY,
title TEXT,
author TEXT,
artist TEXT,
description TEXT,
genre TEXT,
status INTEGER,
UNIQUE (manga_id) ON CONFLICT REPLACE,
FOREIGN KEY(manga_id) REFERENCES mangas (_id)
ON DELETE CASCADE
);
findAll:
SELECT *
FROM custom_manga_info;
insert:
INSERT INTO custom_manga_info(manga_id, title, author, artist, description, genre, status)
VALUES (:manga_id, :title, :author, :artist, :description, :genre, :status)
ON CONFLICT (manga_id)
DO UPDATE
SET
title = :title,
author = :author,
artist = :artist,
description = :description,
genre = :genre,
status = :status
WHERE manga_id = :manga_id;
delete:
DELETE FROM custom_manga_info
WHERE manga_id = :manga_id;

View file

@ -0,0 +1,12 @@
CREATE TABLE custom_manga_info (
manga_id INTEGER NOT NULL PRIMARY KEY,
title TEXT,
author TEXT,
artist TEXT,
description TEXT,
genre TEXT,
status INTEGER,
UNIQUE (manga_id) ON CONFLICT REPLACE,
FOREIGN KEY(manga_id) REFERENCES mangas (_id)
ON DELETE CASCADE
);