From 9a77baedcc700ddca619c622cd441460e78ebc1a Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Sun, 27 Sep 2026 23:02:26 +0600 Subject: [PATCH] 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. --- .../domain/track/interactor/AddTracks.kt | 8 ++++---- .../domain/track/interactor/RefreshTracks.kt | 6 +++--- .../SyncChapterProgressWithTrack.kt | 6 +++--- .../domain/track/interactor/TrackChapter.kt | 6 +++--- .../tachiyomi/data/track/BaseTracker.kt | 6 +++--- app/src/main/java/mihon/app/di/AppGraph.kt | 4 ++-- .../migration/usecases/MigrateMangaUseCase.kt | 6 +++--- .../data/backup/RestoreRepositoryImpl.kt | 2 +- .../data/track/TrackRepositoryImpl.kt | 12 ++++++------ .../sqldelight/tachiyomi/data/manga_sync.sq | 19 +++++++++++++++++-- .../{InsertTrack.kt => UpsertTrack.kt} | 6 +++--- .../track/repository/TrackRepository.kt | 4 ++-- 12 files changed, 50 insertions(+), 35 deletions(-) rename domain/src/main/java/tachiyomi/domain/track/interactor/{InsertTrack.kt => UpsertTrack.kt} (85%) diff --git a/app/src/main/java/eu/kanade/domain/track/interactor/AddTracks.kt b/app/src/main/java/eu/kanade/domain/track/interactor/AddTracks.kt index e40e2ec6c..5261331cf 100644 --- a/app/src/main/java/eu/kanade/domain/track/interactor/AddTracks.kt +++ b/app/src/main/java/eu/kanade/domain/track/interactor/AddTracks.kt @@ -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, diff --git a/app/src/main/java/eu/kanade/domain/track/interactor/RefreshTracks.kt b/app/src/main/java/eu/kanade/domain/track/interactor/RefreshTracks.kt index 10f671b20..6cda78d54 100644 --- a/app/src/main/java/eu/kanade/domain/track/interactor/RefreshTracks.kt +++ b/app/src/main/java/eu/kanade/domain/track/interactor/RefreshTracks.kt @@ -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) { diff --git a/app/src/main/java/eu/kanade/domain/track/interactor/SyncChapterProgressWithTrack.kt b/app/src/main/java/eu/kanade/domain/track/interactor/SyncChapterProgressWithTrack.kt index 29e6cdf54..4eb0e8ee5 100644 --- a/app/src/main/java/eu/kanade/domain/track/interactor/SyncChapterProgressWithTrack.kt +++ b/app/src/main/java/eu/kanade/domain/track/interactor/SyncChapterProgressWithTrack.kt @@ -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) } diff --git a/app/src/main/java/eu/kanade/domain/track/interactor/TrackChapter.kt b/app/src/main/java/eu/kanade/domain/track/interactor/TrackChapter.kt index 4f350a4fb..bae6bb794 100644 --- a/app/src/main/java/eu/kanade/domain/track/interactor/TrackChapter.kt +++ b/app/src/main/java/eu/kanade/domain/track/interactor/TrackChapter.kt @@ -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) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/BaseTracker.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/BaseTracker.kt index 0560e5a9a..a98aa3fad 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/BaseTracker.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/BaseTracker.kt @@ -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" } diff --git a/app/src/main/java/mihon/app/di/AppGraph.kt b/app/src/main/java/mihon/app/di/AppGraph.kt index 808f11dea..7d3acdb6c 100644 --- a/app/src/main/java/mihon/app/di/AppGraph.kt +++ b/app/src/main/java/mihon/app/di/AppGraph.kt @@ -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 diff --git a/app/src/main/java/mihon/domain/migration/usecases/MigrateMangaUseCase.kt b/app/src/main/java/mihon/domain/migration/usecases/MigrateMangaUseCase.kt index cfec40f57..85c4d3b8a 100644 --- a/app/src/main/java/mihon/domain/migration/usecases/MigrateMangaUseCase.kt +++ b/app/src/main/java/mihon/domain/migration/usecases/MigrateMangaUseCase.kt @@ -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) { diff --git a/data/src/main/java/tachiyomi/data/backup/RestoreRepositoryImpl.kt b/data/src/main/java/tachiyomi/data/backup/RestoreRepositoryImpl.kt index ed13765fb..d4a4ac414 100644 --- a/data/src/main/java/tachiyomi/data/backup/RestoreRepositoryImpl.kt +++ b/data/src/main/java/tachiyomi/data/backup/RestoreRepositoryImpl.kt @@ -272,7 +272,7 @@ class RestoreRepositoryImpl( .partition { it.id > 0 } if (newTracks.isNotEmpty()) { - trackRepository.insertAll(newTracks) + trackRepository.upsertAll(newTracks) } existingTracks.forEach { track -> diff --git a/data/src/main/java/tachiyomi/data/track/TrackRepositoryImpl.kt b/data/src/main/java/tachiyomi/data/track/TrackRepositoryImpl.kt index befd96324..02c27cb39 100644 --- a/data/src/main/java/tachiyomi/data/track/TrackRepositoryImpl.kt +++ b/data/src/main/java/tachiyomi/data/track/TrackRepositoryImpl.kt @@ -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) { - insertValues(*tracks.toTypedArray()) + override suspend fun upsertAll(tracks: List) { + 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, diff --git a/data/src/main/sqldelight/tachiyomi/data/manga_sync.sq b/data/src/main/sqldelight/tachiyomi/data/manga_sync.sq index f76b6bb7e..7c9d88575 100644 --- a/data/src/main/sqldelight/tachiyomi/data/manga_sync.sq +++ b/data/src/main/sqldelight/tachiyomi/data/manga_sync.sq @@ -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 diff --git a/domain/src/main/java/tachiyomi/domain/track/interactor/InsertTrack.kt b/domain/src/main/java/tachiyomi/domain/track/interactor/UpsertTrack.kt similarity index 85% rename from domain/src/main/java/tachiyomi/domain/track/interactor/InsertTrack.kt rename to domain/src/main/java/tachiyomi/domain/track/interactor/UpsertTrack.kt index 1a82bbd3c..d83ee595b 100644 --- a/domain/src/main/java/tachiyomi/domain/track/interactor/InsertTrack.kt +++ b/domain/src/main/java/tachiyomi/domain/track/interactor/UpsertTrack.kt @@ -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) { try { - trackRepository.insertAll(tracks) + trackRepository.upsertAll(tracks) } catch (e: Exception) { logcat(LogPriority.ERROR, e) } diff --git a/domain/src/main/java/tachiyomi/domain/track/repository/TrackRepository.kt b/domain/src/main/java/tachiyomi/domain/track/repository/TrackRepository.kt index 1814a7a08..214256ab1 100644 --- a/domain/src/main/java/tachiyomi/domain/track/repository/TrackRepository.kt +++ b/domain/src/main/java/tachiyomi/domain/track/repository/TrackRepository.kt @@ -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) + suspend fun upsertAll(tracks: List) }