diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/backups/data/BackupRepository.kt b/app/src/main/kotlin/org/koitharu/kotatsu/backups/data/BackupRepository.kt index ae57a3fc9..948453887 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/backups/data/BackupRepository.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/backups/data/BackupRepository.kt @@ -26,6 +26,7 @@ import org.koitharu.kotatsu.backups.data.model.CategoryBackup import org.koitharu.kotatsu.backups.data.model.FavouriteBackup import org.koitharu.kotatsu.backups.data.model.HistoryBackup import org.koitharu.kotatsu.backups.data.model.MangaBackup +import org.koitharu.kotatsu.backups.data.model.ScrobblingBackup import org.koitharu.kotatsu.backups.data.model.SourceBackup import org.koitharu.kotatsu.backups.domain.BackupSection import org.koitharu.kotatsu.core.db.MangaDatabase @@ -109,6 +110,12 @@ class BackupRepository @Inject constructor( data = database.getSourcesDao().dumpEnabled().map { SourceBackup(it) }, serializer = serializer(), ) + + BackupSection.SCROBBLING -> output.writeJsonArray( + section = BackupSection.SCROBBLING, + data = database.getScrobblingDao().dumpEnabled().map { ScrobblingBackup(it) }, + serializer = serializer(), + ) } progress?.emit(commonProgress) commonProgress++ @@ -163,6 +170,10 @@ class BackupRepository @Inject constructor( getSourcesDao().upsert(it.toEntity()) } + BackupSection.SCROBBLING -> input.readJsonArray(serializer()).restoreToDb { + getScrobblingDao().upsert(it.toEntity()) + } + null -> CompositeResult.EMPTY // skip unknown entries } progress?.emit(commonProgress) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/backups/data/model/ScrobblingBackup.kt b/app/src/main/kotlin/org/koitharu/kotatsu/backups/data/model/ScrobblingBackup.kt new file mode 100644 index 000000000..0b02846b3 --- /dev/null +++ b/app/src/main/kotlin/org/koitharu/kotatsu/backups/data/model/ScrobblingBackup.kt @@ -0,0 +1,40 @@ +package org.koitharu.kotatsu.backups.data.model + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import org.koitharu.kotatsu.scrobbling.common.data.ScrobblingEntity + +@Serializable +class ScrobblingBackup( + @SerialName("scrobbler") val scrobbler: Int, + @SerialName("id") val id: Int, + @SerialName("manga_id") val mangaId: Long, + @SerialName("target_id") val targetId: Long, + @SerialName("status") val status: String?, + @SerialName("chapter") val chapter: Int, + @SerialName("comment") val comment: String?, + @SerialName("rating") val rating: Float, + ) { + + constructor(entity: ScrobblingEntity) : this( + scrobbler = entity.scrobbler, + id = entity.id, + mangaId = entity.mangaId, + targetId = entity.targetId, + status = entity.status, + chapter = entity.chapter, + comment = entity.comment, + rating = entity.rating, + ) + + fun toEntity() = ScrobblingEntity( + scrobbler = scrobbler, + id = id, + mangaId = mangaId, + targetId = targetId, + status = status, + chapter = chapter, + comment = comment, + rating = rating, + ) +} diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/backups/domain/BackupSection.kt b/app/src/main/kotlin/org/koitharu/kotatsu/backups/domain/BackupSection.kt index 05762cd68..c5cfeb695 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/backups/domain/BackupSection.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/backups/domain/BackupSection.kt @@ -15,6 +15,7 @@ enum class BackupSection( SETTINGS_READER_GRID("reader_grid"), BOOKMARKS("bookmarks"), SOURCES("sources"), + SCROBBLING("scrobbling"), ; companion object { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/backups/ui/restore/BackupSectionModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/backups/ui/restore/BackupSectionModel.kt index 906e2bd25..a552d6d48 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/backups/ui/restore/BackupSectionModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/backups/ui/restore/BackupSectionModel.kt @@ -23,6 +23,7 @@ data class BackupSectionModel( BackupSection.SETTINGS_READER_GRID -> R.string.reader_actions BackupSection.BOOKMARKS -> R.string.bookmarks BackupSection.SOURCES -> R.string.remote_sources + BackupSection.SCROBBLING -> R.string.tracking } override fun areItemsTheSame(other: ListModel): Boolean { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/scrobbling/common/data/ScrobblingDao.kt b/app/src/main/kotlin/org/koitharu/kotatsu/scrobbling/common/data/ScrobblingDao.kt index c24a93b55..3b72c153c 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/scrobbling/common/data/ScrobblingDao.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/scrobbling/common/data/ScrobblingDao.kt @@ -1,7 +1,10 @@ package org.koitharu.kotatsu.scrobbling.common.data import androidx.room.* +import kotlinx.coroutines.currentCoroutineContext import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.isActive @Dao abstract class ScrobblingDao { @@ -20,4 +23,20 @@ abstract class ScrobblingDao { @Query("DELETE FROM scrobblings WHERE scrobbler = :scrobbler AND manga_id = :mangaId") abstract suspend fun delete(scrobbler: Int, mangaId: Long) + + @Query("SELECT * FROM scrobblings ORDER BY scrobbler LIMIT :limit OFFSET :offset") + protected abstract suspend fun findAll(offset: Int, limit: Int): List + + fun dumpEnabled(): Flow = flow { + val window = 10 + var offset = 0 + while (currentCoroutineContext().isActive) { + val list = findAll(offset, window) + if (list.isEmpty()) { + break + } + offset += window + list.forEach { emit(it) } + } + } }