refactor(backup/restore/manga): Use SQLDelight for manga backup restorer

This commit is contained in:
Ahmad Ansori Palembani 2024-06-29 13:54:20 +07:00
parent 76414e60c1
commit 5ddc44dcd7
Signed by: null2264
GPG key ID: BA64F8B60AF3EFB6
19 changed files with 276 additions and 133 deletions

View file

@ -1,8 +1,6 @@
package eu.kanade.tachiyomi.data.backup.models
import eu.kanade.tachiyomi.data.database.models.ChapterImpl
import eu.kanade.tachiyomi.data.database.models.MangaImpl
import eu.kanade.tachiyomi.data.database.models.TrackImpl
import eu.kanade.tachiyomi.data.library.CustomMangaManager
import eu.kanade.tachiyomi.source.model.UpdateStrategy
import eu.kanade.tachiyomi.util.chapter.ChapterUtil
@ -11,6 +9,7 @@ import kotlinx.serialization.protobuf.ProtoNumber
import yokai.data.manga.models.readingModeType
import yokai.domain.library.custom.model.CustomMangaInfo
import yokai.domain.manga.models.Manga
import yokai.domain.track.models.Track
@Suppress("DEPRECATION")
@Serializable
@ -56,27 +55,28 @@ data class BackupManga(
@ProtoNumber(804) var customDescription: String? = null,
@ProtoNumber(805) var customGenre: List<String>? = null,
) {
fun getMangaImpl(): MangaImpl {
return MangaImpl().apply {
url = this@BackupManga.url
title = this@BackupManga.title
artist = this@BackupManga.artist
author = this@BackupManga.author
description = this@BackupManga.description
genre = this@BackupManga.genre.joinToString()
status = this@BackupManga.status
thumbnail_url = this@BackupManga.thumbnailUrl
favorite = this@BackupManga.favorite
source = this@BackupManga.source
date_added = this@BackupManga.dateAdded
viewer_flags = (
fun getMangaImpl(): Manga {
return Manga(
id = null,
url = this@BackupManga.url,
ogTitle = this@BackupManga.title,
ogArtist = this@BackupManga.artist,
ogAuthor = this@BackupManga.author,
ogDescription = this@BackupManga.description,
ogGenres = this@BackupManga.genre,
ogStatus = this@BackupManga.status,
thumbnailUrl = this@BackupManga.thumbnailUrl,
favorite = this@BackupManga.favorite,
source = this@BackupManga.source,
dateAdded = this@BackupManga.dateAdded,
viewerFlags = (
this@BackupManga.viewer_flags
?: this@BackupManga.viewer
).takeIf { it != 0 }
?: -1
chapter_flags = this@BackupManga.chapterFlags
update_strategy = this@BackupManga.updateStrategy
}
?: -1,
chapterFlags = this@BackupManga.chapterFlags,
updateStrategy = this@BackupManga.updateStrategy,
)
}
fun getChaptersImpl(): List<ChapterImpl> {
@ -106,7 +106,7 @@ data class BackupManga(
return null
}
fun getTrackingImpl(): List<TrackImpl> {
fun getTrackingImpl(): List<Track> {
return tracking.map {
it.getTrackingImpl()
}

View file

@ -1,6 +1,5 @@
package eu.kanade.tachiyomi.data.backup.models
import eu.kanade.tachiyomi.data.database.models.TrackImpl
import kotlinx.serialization.Serializable
import kotlinx.serialization.protobuf.ProtoNumber
import yokai.domain.track.models.Track
@ -30,24 +29,25 @@ data class BackupTracking(
@ProtoNumber(100) var mediaId: Long = 0,
) {
fun getTrackingImpl(): TrackImpl {
return TrackImpl().apply {
sync_id = this@BackupTracking.syncId
media_id = if (this@BackupTracking.mediaIdInt != 0) {
fun getTrackingImpl(): Track {
return Track(
id = -1L,
syncId = this@BackupTracking.syncId,
mediaId = if (this@BackupTracking.mediaIdInt != 0) {
this@BackupTracking.mediaIdInt.toLong()
} else {
this@BackupTracking.mediaId
}
library_id = this@BackupTracking.libraryId
title = this@BackupTracking.title
last_chapter_read = this@BackupTracking.lastChapterRead
total_chapters = this@BackupTracking.totalChapters
score = this@BackupTracking.score
status = this@BackupTracking.status
started_reading_date = this@BackupTracking.startedReadingDate
finished_reading_date = this@BackupTracking.finishedReadingDate
tracking_url = this@BackupTracking.trackingUrl
}
},
libraryId = this@BackupTracking.libraryId,
title = this@BackupTracking.title,
lastChapterRead = this@BackupTracking.lastChapterRead,
totalChapters = this@BackupTracking.totalChapters,
score = this@BackupTracking.score,
status = this@BackupTracking.status,
startedReadingDate = this@BackupTracking.startedReadingDate,
finishedReadingDate = this@BackupTracking.finishedReadingDate,
trackingUrl = this@BackupTracking.trackingUrl,
)
}
companion object {

View file

@ -3,11 +3,7 @@ package eu.kanade.tachiyomi.data.backup.restore.restorers
import eu.kanade.tachiyomi.data.backup.models.BackupCategory
import eu.kanade.tachiyomi.data.backup.models.BackupHistory
import eu.kanade.tachiyomi.data.backup.models.BackupManga
import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Chapter
import eu.kanade.tachiyomi.data.database.models.History
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.data.library.CustomMangaManager
import eu.kanade.tachiyomi.data.library.LibraryUpdateJob
import eu.kanade.tachiyomi.source.model.SChapter
@ -16,23 +12,39 @@ import eu.kanade.tachiyomi.util.manga.MangaUtil
import eu.kanade.tachiyomi.util.system.launchNow
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import yokai.data.DatabaseHandler
import yokai.data.manga.models.copyFrom
import yokai.domain.category.interactor.GetCategories
import yokai.domain.chapter.interactor.GetChapter
import yokai.domain.chapter.interactor.UpdateChapter
import yokai.domain.history.interactor.GetHistory
import yokai.domain.history.interactor.UpsertHistory
import yokai.domain.history.models.HistoryUpdate
import yokai.domain.library.custom.model.CustomMangaInfo
import yokai.domain.manga.category.interactor.DeleteMangaCategory
import yokai.domain.manga.interactor.GetManga
import yokai.domain.manga.interactor.InsertManga
import yokai.domain.manga.interactor.UpdateManga
import yokai.domain.manga.models.Manga
import yokai.domain.manga.models.MangaCategory
import yokai.domain.track.interactor.GetTrack
import yokai.domain.track.models.Track
import yokai.domain.track.models.TrackUpdate
import kotlin.math.max
class MangaBackupRestorer(
private val db: DatabaseHelper = Injekt.get(),
private val handler: DatabaseHandler = Injekt.get(),
private val customMangaManager: CustomMangaManager = Injekt.get(),
private val getCategories: GetCategories = Injekt.get(),
private val getChapter: GetChapter = Injekt.get(),
private val updateChapter: UpdateChapter = Injekt.get(),
private val getHistory: GetHistory = Injekt.get(),
private val upsertHistory: UpsertHistory = Injekt.get(),
private val getManga: GetManga = Injekt.get(),
private val insertManga: InsertManga = Injekt.get(),
private val updateManga: UpdateManga = Injekt.get(),
private val deleteMangaCategory: DeleteMangaCategory = Injekt.get(),
private val getTrack: GetTrack = Injekt.get(),
) {
suspend fun restoreManga(
backupManga: BackupManga,
@ -57,12 +69,13 @@ class MangaBackupRestorer(
} else {
// Manga in database
// Copy information from manga already in database
manga.id = dbManga.id
manga.filtered_scanlators = dbManga.filtered_scanlators
manga.copyFrom(dbManga)
updateManga.await(manga.toMangaUpdate())
val copy = manga.copy(
id = dbManga.id,
filteredScanlators = dbManga.filteredScanlators
).copyFrom(dbManga)
updateManga.await(copy.toMangaUpdate())
// Fetch rest of manga information
restoreExistingManga(manga, chapters, categories, history, tracks, backupCategories, filteredScanlators, customManga)
restoreExistingManga(copy, chapters, categories, history, tracks, backupCategories, filteredScanlators, customManga)
}
} catch (e: Exception) {
onError(manga, e)
@ -89,10 +102,10 @@ class MangaBackupRestorer(
filteredScanlators: List<String>,
customManga: CustomMangaInfo?,
) {
val fetchedManga = manga.also {
it.initialized = it.description != null
it.id = insertManga.await(it)
}
val fetchedManga = manga.copy(
initialized = manga.ogDescription != null,
id = insertManga.await(manga),
)
fetchedManga.id ?: return
restoreChapters(fetchedManga, chapters)
@ -136,10 +149,30 @@ class MangaBackupRestorer(
}
val newChapters = chapters.groupBy { it.id != null }
newChapters[true]?.let { db.updateKnownChaptersBackup(it).executeAsBlocking() }
newChapters[false]?.let { db.insertChapters(it).executeAsBlocking() }
newChapters[true]?.let { updateChapter.awaitAll(it.map{ c -> c.toChapterUpdate() }) }
newChapters[false]?.let { insertChapters(it) }
}
private suspend fun insertChapters(chapters: List<Chapter>) =
handler.await(true) {
chapters.forEach { chapter ->
chaptersQueries.insert(
mangaId = chapter.manga_id!!,
url = chapter.url,
name = chapter.name,
scanlator = chapter.scanlator,
read = chapter.read,
bookmark = chapter.bookmark,
lastPageRead = chapter.last_page_read.toLong(),
pagesLeft = chapter.pages_left.toLong(),
chapterNumber = chapter.chapter_number.toDouble(),
sourceOrder = chapter.source_order.toLong(),
dateFetch = chapter.date_fetch,
dateUpload = chapter.date_upload,
)
}
}
private suspend fun restoreExtras(
manga: Manga,
categories: List<Int>,
@ -177,15 +210,19 @@ class MangaBackupRestorer(
dbCategories.firstOrNull { dbCategory ->
dbCategory.name == backupCategory.name
}?.let { dbCategory ->
mangaCategoriesToUpdate += MangaCategory.create(manga, dbCategory)
mangaCategoriesToUpdate += MangaCategory(manga.id!!, dbCategory.id?.toLong()!!)
}
}
}
// Update database
if (mangaCategoriesToUpdate.isNotEmpty()) {
db.deleteOldMangasCategories(listOf(manga)).executeAsBlocking()
db.insertMangasCategories(mangaCategoriesToUpdate).executeAsBlocking()
deleteMangaCategory.awaitByMangaId(manga.id!!)
handler.await(true) {
mangaCategoriesToUpdate.forEach { update ->
mangas_categoriesQueries.insert(update.mangaId, update.categoryId.toLong())
}
}
}
}
@ -196,28 +233,32 @@ class MangaBackupRestorer(
*/
internal suspend fun restoreHistoryForManga(history: List<BackupHistory>) {
// List containing history to be updated
val historyToBeUpdated = ArrayList<History>(history.size)
val historyToBeUpdated = ArrayList<HistoryUpdate>(history.size)
for ((url, lastRead, readDuration) in history) {
val dbHistory = db.getHistoryByChapterUrl(url).executeAsBlocking()
val dbHistory = getHistory.awaitByChapterUrl(url)
// Check if history already in database and update
if (dbHistory != null) {
dbHistory.apply {
last_read = max(lastRead, dbHistory.last_read)
time_read = max(readDuration, dbHistory.time_read)
}
historyToBeUpdated.add(dbHistory)
historyToBeUpdated.add(
HistoryUpdate(
chapterId = dbHistory.chapterId,
readAt = max(lastRead, dbHistory.timeRead),
sessionReadDuration = max(readDuration, dbHistory.timeRead),
)
)
} else {
// If not in database create
db.getChapter(url).executeAsBlocking()?.let {
val historyToAdd = History.create(it).apply {
last_read = lastRead
time_read = readDuration
}
historyToBeUpdated.add(historyToAdd)
getChapter.await(url)?.let {
historyToBeUpdated.add(
HistoryUpdate(
chapterId = it.id!!,
readAt = lastRead,
sessionReadDuration = readDuration,
)
)
}
}
}
db.upsertHistoryLastRead(historyToBeUpdated).executeAsBlocking()
upsertHistory.awaitAll(historyToBeUpdated)
}
/**
@ -228,43 +269,67 @@ class MangaBackupRestorer(
*/
private suspend fun restoreTrackForManga(manga: Manga, tracks: List<Track>) {
// Fix foreign keys with the current manga id
tracks.map { it.manga_id = manga.id!! }
val actualTracks = tracks.map { it.copy(mangaId = manga.id!!) }
// Get tracks from database
val dbTracks = db.getTracks(manga).executeAsBlocking()
val trackToUpdate = mutableListOf<Track>()
val dbTracks = getTrack.awaitAllByMangaId(manga.id!!)
val trackToUpdate = mutableListOf<TrackUpdate>()
val trackToAdd = mutableListOf<Track>()
tracks.forEach { track ->
actualTracks.forEach { track ->
var isInDatabase = false
for (dbTrack in dbTracks) {
if (track.sync_id == dbTrack.sync_id) {
// The sync is already in the db, only update its fields
if (track.media_id != dbTrack.media_id) {
dbTrack.media_id = track.media_id
}
if (track.library_id != dbTrack.library_id) {
dbTrack.library_id = track.library_id
}
dbTrack.last_chapter_read = max(dbTrack.last_chapter_read, track.last_chapter_read)
if (track.syncId == dbTrack.syncId) {
val update = TrackUpdate(
id = dbTrack.id,
lastChapterRead = max(dbTrack.lastChapterRead, track.lastChapterRead),
// The sync is already in the db, only update its fields
mediaId = if (track.mediaId != dbTrack.mediaId) track.mediaId else null,
libraryId = if (track.libraryId != dbTrack.libraryId) track.libraryId else null,
)
isInDatabase = true
trackToUpdate.add(dbTrack)
trackToUpdate.add(update)
break
}
}
if (!isInDatabase) {
// Insert new sync. Let the db assign the id
track.id = null
trackToUpdate.add(track)
trackToAdd.add(track.copy(id = -1L))
}
}
// Update database
if (trackToUpdate.isNotEmpty()) {
db.insertTracks(trackToUpdate).executeAsBlocking()
handler.await(true) {
trackToUpdate.forEach { update ->
manga_syncQueries.update(
trackId = update.id,
mangaId = update.mangaId,
trackingUrl = update.trackingUrl,
lastChapterRead = update.lastChapterRead?.toDouble(),
mediaId = update.mediaId,
libraryId = update.libraryId,
)
}
trackToAdd.forEach { track ->
manga_syncQueries.insert(
mangaId = track.mangaId,
syncId = track.syncId.toLong(),
lastChapterRead = track.lastChapterRead.toDouble(),
mediaId = track.mediaId,
libraryId = track.libraryId,
title = track.title,
totalChapters = track.totalChapters.toLong(),
status = track.status.toLong(),
score = track.score.toDouble(),
trackingUrl = track.trackingUrl,
startDate = track.startedReadingDate,
finishDate = track.finishedReadingDate,
)
}
}
}
private suspend fun restoreFilteredScanlatorsForManga(manga: Manga, filteredScanlators: List<String>) {
val actualList = ChapterUtil.getScanlators(manga.filtered_scanlators) + filteredScanlators
val actualList = ChapterUtil.getScanlators(manga.filteredScanlators) + filteredScanlators
MangaUtil.setScanlatorFilter(updateManga, manga, actualList.toSet())
}
}

View file

@ -1,6 +1,7 @@
package eu.kanade.tachiyomi.data.database.models
import eu.kanade.tachiyomi.source.model.SChapter
import yokai.domain.chapter.models.ChapterUpdate
import java.io.Serializable
interface Chapter : SChapter, Serializable {
@ -24,6 +25,23 @@ interface Chapter : SChapter, Serializable {
val isRecognizedNumber: Boolean
get() = chapter_number >= 0f
fun toChapterUpdate() =
ChapterUpdate(
id = id ?: -1,
mangaId = manga_id ?: -1,
url = url,
name = name,
scanlator = scanlator,
read = read,
bookmark = bookmark,
lastPageRead = last_page_read.toLong(),
pagesLeft = pages_left.toLong(),
chapterNumber = chapter_number.toDouble(),
sourceOrder = source_order.toLong(),
dateFetch = date_fetch,
dateUpload = date_upload
)
companion object {
fun create(): Chapter = ChapterImpl().apply {

View file

@ -5,6 +5,7 @@ import eu.kanade.tachiyomi.source.Source
import yokai.domain.manga.models.Manga
import yokai.domain.track.models.Track
import yokai.domain.track.models.TrackUpdate
import kotlin.math.max
/**
* An Enhanced Track Service will never prompt the user to match a manga with the remote.
@ -45,6 +46,7 @@ interface EnhancedTrackService {
id = track.id,
mangaId = manga.id!!,
trackingUrl = manga.url,
lastChapterRead = max(dbTrack.lastChapterRead, track.lastChapterRead),
)
} else {
null

View file

@ -1,17 +1,19 @@
package eu.kanade.tachiyomi.util.manga
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.util.chapter.ChapterUtil
import yokai.domain.manga.interactor.UpdateManga
import yokai.domain.manga.models.Manga
import yokai.domain.manga.models.MangaUpdate
object MangaUtil {
suspend fun setScanlatorFilter(updateManga: UpdateManga, manga: Manga, filteredScanlators: Set<String>) {
if (manga.id == null) return
manga.filtered_scanlators =
if (filteredScanlators.isEmpty()) null else ChapterUtil.getScanlatorString(filteredScanlators)
updateManga.await(MangaUpdate(manga.id!!, filteredScanlators = manga.filtered_scanlators))
updateManga.await(MangaUpdate(
manga.id!!,
filteredScanlators = if (filteredScanlators.isEmpty()) null else ChapterUtil.getScanlatorString(
filteredScanlators,
),
))
}
}

View file

@ -11,6 +11,8 @@ import yokai.domain.chapter.models.ChapterUpdate
class ChapterRepositoryImpl(private val handler: DatabaseHandler) : ChapterRepository {
override suspend fun getChapter(chapterId: Long): Chapter? =
handler.awaitOneOrNull { chaptersQueries.find(chapterId, Chapter::mapper) }
override suspend fun getChapter(url: String): Chapter? =
handler.awaitOneOrNull { chaptersQueries.findByUrl(url, Chapter::mapper) }
override suspend fun getChapters(mangaId: Long, filterScanlators: Boolean): List<Chapter> =
handler.awaitList { chaptersQueries.getChaptersByMangaId(mangaId, filterScanlators.toInt().toLong(), Chapter::mapper) }

View file

@ -7,12 +7,15 @@ import yokai.domain.history.models.History
import yokai.domain.history.models.HistoryUpdate
class HistoryRepositoryImpl(private val handler: DatabaseHandler) : HistoryRepository {
override suspend fun findByMangaId(mangaId: Long): List<History> =
override suspend fun findAllByMangaId(mangaId: Long): List<History> =
handler.awaitList { historyQueries.findByMangaId(mangaId, History::mapper) }
override suspend fun findBySourceUrl(url: String): List<History> =
override suspend fun findAllByChapterUrl(url: String): List<History> =
handler.awaitList { historyQueries.findByChapterUrl(url, History::mapper) }
override suspend fun findByChapterUrl(url: String): History? =
handler.awaitOneOrNull { historyQueries.findByChapterUrl(url, History::mapper) }
override suspend fun upsert(update: HistoryUpdate): Boolean {
return try {
partialUpsert(update)

View file

@ -6,6 +6,8 @@ import eu.kanade.tachiyomi.data.database.models.Manga.Companion.TYPE_MANGA
import eu.kanade.tachiyomi.data.database.models.Manga.Companion.TYPE_MANHUA
import eu.kanade.tachiyomi.data.database.models.Manga.Companion.TYPE_MANHWA
import eu.kanade.tachiyomi.data.database.models.Manga.Companion.TYPE_WEBTOON
import eu.kanade.tachiyomi.data.download.DownloadManager
import eu.kanade.tachiyomi.data.download.DownloadProvider
import eu.kanade.tachiyomi.data.library.CustomMangaManager
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.source.SourceManager
@ -20,23 +22,50 @@ import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
import yokai.domain.chapter.interactor.GetChapter
import yokai.domain.manga.models.Manga
import yokai.domain.manga.models.MangaUpdate
import yokai.i18n.MR
import yokai.util.lang.getString
import java.util.*
fun Manga.toSManga() = SManga.create().also {
it.url = url
it.title = title
it.artist = artist
it.author = author
it.description = description
it.genre = genres.joinToString()
it.status = status
it.title = ogTitle
it.artist = ogArtist
it.author = ogAuthor
it.description = ogDescription
it.genre = ogGenres.joinToString()
it.status = ogStatus
it.thumbnail_url = thumbnailUrl
it.initialized = initialized
}
fun Manga.copyFrom(other: Manga): Manga {
val title: String
if (other.ogTitle != ogTitle) {
title = other.ogTitle
val db: DownloadManager by injectLazy()
val provider = DownloadProvider(db.context)
provider.renameMangaFolder(ogTitle, other.title, source)
} else {
title = ogTitle
}
val author = other.ogAuthor ?: ogAuthor
val artist = other.ogArtist ?: ogArtist
val description = other.ogDescription ?: ogDescription
val genres = other.ogGenres.ifEmpty { ogGenres }
val status = other.ogStatus.takeIf { it != -1 } ?: ogStatus
val thumbnailUrl = other.thumbnailUrl ?: thumbnailUrl
return this.copy(
ogTitle = title,
ogAuthor = author,
ogArtist = artist,
ogDescription = description,
ogGenres = genres,
ogStatus = status,
thumbnailUrl = thumbnailUrl,
)
}
fun Manga.copyFrom(other: SManga): Manga {
val author = other.author ?: ogAuthor
val artist = other.artist ?: ogArtist
@ -124,30 +153,6 @@ var Manga.dominantCoverColors: Pair<Int, Int>?
MangaCoverMetadata.addCoverColor(this, value.first, value.second)
}
fun Manga.toMangaUpdate(): MangaUpdate {
return MangaUpdate(
id = id!!,
source = source,
url = url,
artist = artist,
author = author,
description = description,
genres = genres,
title = title,
status = status,
thumbnailUrl = thumbnailUrl,
favorite = favorite,
lastUpdate = lastUpdate,
initialized = initialized,
viewerFlags = viewerFlags,
hideTitle = hideTitle,
chapterFlags = chapterFlags,
dateAdded = dateAdded,
filteredScanlators = filteredScanlators,
updateStrategy = updateStrategy,
)
}
fun Manga.seriesType(context: Context, sourceManager: SourceManager? = null): String {
return context.getString(
when (seriesType(sourceManager = sourceManager)) {

View file

@ -40,6 +40,9 @@ class TrackRepositoryImpl(private val handler: DatabaseHandler) : TrackRepositor
trackId = update.id,
mangaId = update.mangaId,
trackingUrl = update.trackingUrl,
lastChapterRead = update.lastChapterRead?.toDouble(),
mediaId = update.mediaId,
libraryId = update.libraryId,
)
}
}

View file

@ -6,6 +6,7 @@ import yokai.domain.chapter.models.ChapterUpdate
interface ChapterRepository {
suspend fun getChapter(chapterId: Long): Chapter?
suspend fun getChapter(url: String): Chapter?
suspend fun getChapters(mangaId: Long, filterScanlators: Boolean): List<Chapter>
fun getChaptersAsFlow(mangaId: Long, filterScanlators: Boolean): Flow<List<Chapter>>

View file

@ -7,6 +7,7 @@ class GetChapter(
private val chapterRepository: ChapterRepository,
) {
suspend fun await(chapterId: Long) = chapterRepository.getChapter(chapterId)
suspend fun await(url: String) = chapterRepository.getChapter(url)
suspend fun awaitAll(mangaId: Long, filterScanlators: Boolean) = chapterRepository.getChapters(mangaId, filterScanlators)
suspend fun awaitAll(manga: Manga, filterScanlators: Boolean? = null) =

View file

@ -4,8 +4,9 @@ import yokai.domain.history.models.History
import yokai.domain.history.models.HistoryUpdate
interface HistoryRepository {
suspend fun findByMangaId(mangaId: Long): List<History>
suspend fun findBySourceUrl(url: String): List<History>
suspend fun findAllByMangaId(mangaId: Long): List<History>
suspend fun findAllByChapterUrl(url: String): List<History>
suspend fun findByChapterUrl(url: String): History?
suspend fun upsert(update: HistoryUpdate): Boolean
suspend fun upsertAll(updates: List<HistoryUpdate>): Boolean
}

View file

@ -5,6 +5,7 @@ import yokai.domain.history.HistoryRepository
class GetHistory(
private val historyRepository: HistoryRepository,
) {
suspend fun awaitAllByMangaId(mangaId: Long) = historyRepository.findByMangaId(mangaId)
suspend fun awaitAllBySourceUrl(url: String) = historyRepository.findBySourceUrl(url)
suspend fun awaitAllByMangaId(mangaId: Long) = historyRepository.findAllByMangaId(mangaId)
suspend fun awaitAllByChapterUrl(url: String) = historyRepository.findAllByChapterUrl(url)
suspend fun awaitByChapterUrl(url: String) = historyRepository.findByChapterUrl(url)
}

View file

@ -1,7 +1,7 @@
package yokai.domain.manga.interactor
import eu.kanade.tachiyomi.data.database.models.Manga
import yokai.domain.manga.MangaRepository
import yokai.domain.manga.models.Manga
class InsertManga (
private val mangaRepository: MangaRepository,

View file

@ -26,6 +26,11 @@ SELECT *
FROM chapters
WHERE _id = :chapterId;
findByUrl:
SELECT *
FROM chapters
WHERE url = :url;
getChaptersByMangaId:
SELECT C.*
FROM chapters AS C

View file

@ -29,5 +29,12 @@ WHERE manga_id = :mangaId;
update:
UPDATE manga_sync SET
manga_id = coalesce(:mangaId, manga_id),
remote_url = coalesce(:trackingUrl, remote_url)
remote_id = coalesce(:mediaId, remote_id),
library_id = coalesce(:libraryId, library_id),
remote_url = coalesce(:trackingUrl, remote_url),
last_chapter_read = coalesce(:lastChapterRead, last_chapter_read)
WHERE _id = :trackId;
insert:
INSERT INTO manga_sync (manga_id, sync_id, remote_id, library_id, title, last_chapter_read, total_chapters, status, score, remote_url, start_date, finish_date)
VALUES (:mangaId, :syncId, :mediaId, :libraryId, :title, :lastChapterRead, :totalChapters, :status, :score, :trackingUrl, :startDate, :finishDate);

View file

@ -126,6 +126,30 @@ data class Manga(
id?.let { vibrantCoverColorMap[it] = value }
}
fun toMangaUpdate(): MangaUpdate {
return MangaUpdate(
id = id!!,
source = source,
url = url,
artist = ogArtist,
author = ogAuthor,
description = ogDescription,
genres = ogGenres,
title = ogTitle,
status = ogStatus,
thumbnailUrl = thumbnailUrl,
favorite = favorite,
lastUpdate = lastUpdate,
initialized = initialized,
viewerFlags = viewerFlags,
hideTitle = hideTitle,
chapterFlags = chapterFlags,
dateAdded = dateAdded,
filteredScanlators = filteredScanlators,
updateStrategy = updateStrategy,
)
}
companion object {
// Generic filter that does not filter anything
const val SHOW_ALL = 0x00000000

View file

@ -4,4 +4,7 @@ data class TrackUpdate(
val id: Long,
val mangaId: Long? = null,
val trackingUrl: String? = null,
val lastChapterRead: Float? = null,
val mediaId: Long? = null,
val libraryId: Long? = null,
)