Update an existing track in place instead of replacing it

Upserting a track for an entry and tracker that already have one updates that row, instead of the table's REPLACE deleting it and inserting a new one under a new id. Anything keyed to the old id, such as a queued tracking update, keeps working. The query, repository methods and interactor are renamed from insert to upsert to say so.
This commit is contained in:
AntsyLich
2026-09-27 23:02:26 +06:00
parent 2b4b8472bb
commit 9a77baedcc
12 changed files with 50 additions and 35 deletions
@@ -17,11 +17,11 @@ import tachiyomi.core.common.util.system.logcat
import tachiyomi.domain.chapter.interactor.GetChaptersByMangaId
import tachiyomi.domain.history.interactor.GetHistory
import tachiyomi.domain.manga.model.Manga
import tachiyomi.domain.track.interactor.InsertTrack
import tachiyomi.domain.track.interactor.UpsertTrack
@Inject
class AddTracks(
private val insertTrack: InsertTrack,
private val upsertTrack: UpsertTrack,
private val syncChapterProgressWithTrack: SyncChapterProgressWithTrack,
private val getChaptersByMangaId: GetChaptersByMangaId,
private val trackerManager: TrackerManager,
@@ -37,7 +37,7 @@ class AddTracks(
var track = item.toDomainTrack(idRequired = false) ?: return@withIOContext
insertTrack.await(track)
upsertTrack.await(track)
// TODO: merge into [SyncChapterProgressWithTrack]?
// Update chapter progress if newer chapters marked read locally
@@ -87,7 +87,7 @@ class AddTracks(
service.match(manga)?.let { track ->
track.manga_id = manga.id
(service as Tracker).bind(track)
insertTrack.await(track.toDomainTrack(idRequired = false)!!)
upsertTrack.await(track.toDomainTrack(idRequired = false)!!)
syncChapterProgressWithTrack.await(
manga.id,
@@ -9,13 +9,13 @@ import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.supervisorScope
import tachiyomi.domain.track.interactor.GetTracks
import tachiyomi.domain.track.interactor.InsertTrack
import tachiyomi.domain.track.interactor.UpsertTrack
@Inject
class RefreshTracks(
private val getTracks: GetTracks,
private val trackerManager: TrackerManager,
private val insertTrack: InsertTrack,
private val upsertTrack: UpsertTrack,
private val syncChapterProgressWithTrack: SyncChapterProgressWithTrack,
) {
@@ -33,7 +33,7 @@ class RefreshTracks(
async {
return@async try {
val updatedTrack = service!!.refresh(track.toDbTrack()).toDomainTrack()!!
insertTrack.await(updatedTrack)
upsertTrack.await(updatedTrack)
syncChapterProgressWithTrack.await(mangaId, updatedTrack, service)
null
} catch (e: Throwable) {
@@ -9,14 +9,14 @@ import tachiyomi.core.common.util.system.logcat
import tachiyomi.domain.chapter.interactor.GetChaptersByMangaId
import tachiyomi.domain.chapter.interactor.UpdateChapter
import tachiyomi.domain.chapter.model.ChapterUpdate
import tachiyomi.domain.track.interactor.InsertTrack
import tachiyomi.domain.track.interactor.UpsertTrack
import tachiyomi.domain.track.model.Track
import kotlin.math.max
@Inject
class SyncChapterProgressWithTrack(
private val updateChapter: UpdateChapter,
private val insertTrack: InsertTrack,
private val upsertTrack: UpsertTrack,
private val getChaptersByMangaId: GetChaptersByMangaId,
) {
@@ -45,7 +45,7 @@ class SyncChapterProgressWithTrack(
try {
tracker.update(updatedTrack.toDbTrack())
updateChapter.awaitAll(chapterUpdates)
insertTrack.await(updatedTrack)
upsertTrack.await(updatedTrack)
} catch (e: Throwable) {
logcat(LogPriority.WARN, e)
}
@@ -13,13 +13,13 @@ import logcat.LogPriority
import tachiyomi.core.common.util.lang.withNonCancellableContext
import tachiyomi.core.common.util.system.logcat
import tachiyomi.domain.track.interactor.GetTracks
import tachiyomi.domain.track.interactor.InsertTrack
import tachiyomi.domain.track.interactor.UpsertTrack
@Inject
class TrackChapter(
private val getTracks: GetTracks,
private val trackerManager: TrackerManager,
private val insertTrack: InsertTrack,
private val upsertTrack: UpsertTrack,
private val delayedTrackingStore: DelayedTrackingStore,
) {
@@ -41,7 +41,7 @@ class TrackChapter(
.toDomainTrack(idRequired = true)!!
.copy(lastChapterRead = chapterNumber)
service.update(updatedTrack.toDbTrack(), true)
insertTrack.await(updatedTrack)
upsertTrack.await(updatedTrack)
delayedTrackingStore.remove(track.id)
} catch (e: Exception) {
delayedTrackingStore.add(track.id, chapterNumber)
@@ -21,7 +21,7 @@ import okhttp3.OkHttpClient
import tachiyomi.core.common.util.lang.withIOContext
import tachiyomi.core.common.util.lang.withUIContext
import tachiyomi.core.common.util.system.logcat
import tachiyomi.domain.track.interactor.InsertTrack
import tachiyomi.domain.track.interactor.UpsertTrack
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import tachiyomi.domain.track.model.Track as DomainTrack
@@ -38,7 +38,7 @@ abstract class BaseTracker(
private val context: Context by lazy { appGraph.context }
private val addTracks: AddTracks by lazy { appGraph.addTracks }
private val insertTrack: InsertTrack by lazy { appGraph.insertTrack }
private val upsertTrack: UpsertTrack by lazy { appGraph.upsertTrack }
override val client: OkHttpClient
get() = networkService.client
@@ -169,7 +169,7 @@ abstract class BaseTracker(
try {
update(track)
track.toDomainTrack(idRequired = false)?.let {
insertTrack.await(it)
upsertTrack.await(it)
}
} catch (e: Exception) {
logcat(LogPriority.ERROR, e) { "Failed to update remote track data id=$id" }
+2 -2
View File
@@ -54,7 +54,7 @@ import tachiyomi.domain.manga.interactor.GetFavorites
import tachiyomi.domain.manga.interactor.ResetViewerFlags
import tachiyomi.domain.source.service.SourceManager
import tachiyomi.domain.storage.service.StoragePreferences
import tachiyomi.domain.track.interactor.InsertTrack
import tachiyomi.domain.track.interactor.UpsertTrack
@DependencyGraph(
scope = AppScope::class,
@@ -119,7 +119,7 @@ interface AppGraph : ViewModelGraph {
val resetViewerFlags: ResetViewerFlags
val resetCategoryFlags: ResetCategoryFlags
val addTracks: AddTracks
val insertTrack: InsertTrack
val upsertTrack: UpsertTrack
val getExtensionStoreCountAsFlow: GetExtensionStoreCountAsFlow
@@ -20,7 +20,7 @@ import tachiyomi.domain.manga.model.Manga
import tachiyomi.domain.manga.model.MangaUpdate
import tachiyomi.domain.source.service.SourceManager
import tachiyomi.domain.track.interactor.GetTracks
import tachiyomi.domain.track.interactor.InsertTrack
import tachiyomi.domain.track.interactor.UpsertTrack
import kotlin.time.Clock
@Inject
@@ -35,7 +35,7 @@ class MigrateMangaUseCase(
private val getCategories: GetCategories,
private val setMangaCategories: SetMangaCategories,
private val getTracks: GetTracks,
private val insertTrack: InsertTrack,
private val upsertTrack: UpsertTrack,
private val coverCache: CoverCache,
private val updateMangaFromRemote: UpdateMangaFromRemote,
) {
@@ -98,7 +98,7 @@ class MigrateMangaUseCase(
}
}
.takeIf { it.isNotEmpty() }
?.let { insertTrack.awaitAll(it) }
?.let { upsertTrack.awaitAll(it) }
// Delete downloaded
if (MigrationFlag.REMOVE_DOWNLOAD in flags && currentSource != null) {
@@ -272,7 +272,7 @@ class RestoreRepositoryImpl(
.partition { it.id > 0 }
if (newTracks.isNotEmpty()) {
trackRepository.insertAll(newTracks)
trackRepository.upsertAll(newTracks)
}
existingTracks.forEach { track ->
@@ -50,18 +50,18 @@ class TrackRepositoryImpl(
)
}
override suspend fun insert(track: Track) {
insertValues(track)
override suspend fun upsert(track: Track) {
upsertValues(track)
}
override suspend fun insertAll(tracks: List<Track>) {
insertValues(*tracks.toTypedArray())
override suspend fun upsertAll(tracks: List<Track>) {
upsertValues(*tracks.toTypedArray())
}
private suspend fun insertValues(vararg tracks: Track) {
private suspend fun upsertValues(vararg tracks: Track) {
database.transaction {
tracks.forEach { mangaTrack ->
database.manga_syncQueries.insert(
database.manga_syncQueries.upsert(
mangaId = mangaTrack.mangaId,
syncId = mangaTrack.trackerId,
remoteId = mangaTrack.remoteId,
@@ -40,9 +40,24 @@ SELECT *
FROM manga_sync
WHERE manga_id = :mangaId;
insert:
upsert:
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,private)
VALUES (:mangaId,:syncId,:remoteId,:libraryId,:title,:lastChapterRead,:totalChapters,:status,:score,:remoteUrl,:startDate,:finishDate,:private);
VALUES (:mangaId,:syncId,:remoteId,:libraryId,:title,:lastChapterRead,:totalChapters,:status,:score,:remoteUrl,:startDate,:finishDate,:private)
ON CONFLICT(manga_id, sync_id)
DO UPDATE
SET
remote_id = :remoteId,
library_id = :libraryId,
title = :title,
last_chapter_read = :lastChapterRead,
total_chapters = :totalChapters,
status = :status,
score = :score,
remote_url = :remoteUrl,
start_date = :startDate,
finish_date = :finishDate,
private = :private
WHERE manga_id = :mangaId AND sync_id = :syncId;
update:
UPDATE manga_sync
@@ -7,13 +7,13 @@ import tachiyomi.domain.track.model.Track
import tachiyomi.domain.track.repository.TrackRepository
@Inject
class InsertTrack(
class UpsertTrack(
private val trackRepository: TrackRepository,
) {
suspend fun await(track: Track) {
try {
trackRepository.insert(track)
trackRepository.upsert(track)
} catch (e: Exception) {
logcat(LogPriority.ERROR, e)
}
@@ -21,7 +21,7 @@ class InsertTrack(
suspend fun awaitAll(tracks: List<Track>) {
try {
trackRepository.insertAll(tracks)
trackRepository.upsertAll(tracks)
} catch (e: Exception) {
logcat(LogPriority.ERROR, e)
}
@@ -15,7 +15,7 @@ interface TrackRepository {
suspend fun delete(mangaId: Long, trackerId: Long)
suspend fun insert(track: Track)
suspend fun upsert(track: Track)
suspend fun insertAll(tracks: List<Track>)
suspend fun upsertAll(tracks: List<Track>)
}