From 58c5da7d8f2e493437f85a67e19130057e9aee5d Mon Sep 17 00:00:00 2001 From: Eugene Date: Wed, 19 Aug 2026 21:47:34 +0300 Subject: [PATCH] add CacheRecentlyWatchedRepository --- .../{SessionVideoApi.kt => CacheVideoApi.kt} | 2 +- .../BackendRecentlyWatchedRepository.kt | 16 +---- .../recent/CacheRecentlyWatchedRepository.kt | 59 +++++++++++++++++++ .../vbox/backend/di/HttpModule.kt | 4 +- .../RecentlyWatchedRepositoryFactory.kt | 17 ++++-- .../routing/RecentlyWatchedRouting.kt | 7 --- .../domain/RecentlyWatchedRepository.kt | 1 - 7 files changed, 77 insertions(+), 29 deletions(-) rename apps/vbox/backend/src/main/kotlin/ru/shadowsparky/vbox/backend/data/http/{SessionVideoApi.kt => CacheVideoApi.kt} (98%) rename apps/vbox/backend/src/main/kotlin/ru/shadowsparky/vbox/backend/data/{ => recent}/BackendRecentlyWatchedRepository.kt (86%) create mode 100644 apps/vbox/backend/src/main/kotlin/ru/shadowsparky/vbox/backend/data/recent/CacheRecentlyWatchedRepository.kt diff --git a/apps/vbox/backend/src/main/kotlin/ru/shadowsparky/vbox/backend/data/http/SessionVideoApi.kt b/apps/vbox/backend/src/main/kotlin/ru/shadowsparky/vbox/backend/data/http/CacheVideoApi.kt similarity index 98% rename from apps/vbox/backend/src/main/kotlin/ru/shadowsparky/vbox/backend/data/http/SessionVideoApi.kt rename to apps/vbox/backend/src/main/kotlin/ru/shadowsparky/vbox/backend/data/http/CacheVideoApi.kt index ae4d302..82dde4e 100644 --- a/apps/vbox/backend/src/main/kotlin/ru/shadowsparky/vbox/backend/data/http/SessionVideoApi.kt +++ b/apps/vbox/backend/src/main/kotlin/ru/shadowsparky/vbox/backend/data/http/CacheVideoApi.kt @@ -8,7 +8,7 @@ import ru.shadowsparky.vbox.shared.domain.model.VideoLinksResponse import ru.shadowsparky.vbox.shared.domain.model.VideosResponse import kotlin.time.Duration.Companion.days -class SessionVideoApi( +class CacheVideoApi( private val wrapper: VideoApi, private val filter: FuzzyMovieSearchFilter, private val redis: RedisCache diff --git a/apps/vbox/backend/src/main/kotlin/ru/shadowsparky/vbox/backend/data/BackendRecentlyWatchedRepository.kt b/apps/vbox/backend/src/main/kotlin/ru/shadowsparky/vbox/backend/data/recent/BackendRecentlyWatchedRepository.kt similarity index 86% rename from apps/vbox/backend/src/main/kotlin/ru/shadowsparky/vbox/backend/data/BackendRecentlyWatchedRepository.kt rename to apps/vbox/backend/src/main/kotlin/ru/shadowsparky/vbox/backend/data/recent/BackendRecentlyWatchedRepository.kt index 5a76984..1272091 100644 --- a/apps/vbox/backend/src/main/kotlin/ru/shadowsparky/vbox/backend/data/BackendRecentlyWatchedRepository.kt +++ b/apps/vbox/backend/src/main/kotlin/ru/shadowsparky/vbox/backend/data/recent/BackendRecentlyWatchedRepository.kt @@ -1,4 +1,4 @@ -package ru.shadowsparky.vbox.backend.data +package ru.shadowsparky.vbox.backend.data.recent import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow @@ -38,18 +38,6 @@ class BackendRecentlyWatchedRepository( } } - suspend fun toggleInfo(info: RecentlyWatchedInfo) = - withContext(dispatcherProvider.io) { - val hasInfo = - db.recentQueries.selectByIds(info.movieId, info.episode, info.season, userId) - .executeAsOneOrNull() != null - if (hasInfo) { - remove(info) - } else { - write(info) - } - } - override fun queryRecentlyWatched( movieId: Long, seasonId: Long? @@ -100,4 +88,4 @@ class BackendRecentlyWatchedRepository( ) ) } -} +} \ No newline at end of file diff --git a/apps/vbox/backend/src/main/kotlin/ru/shadowsparky/vbox/backend/data/recent/CacheRecentlyWatchedRepository.kt b/apps/vbox/backend/src/main/kotlin/ru/shadowsparky/vbox/backend/data/recent/CacheRecentlyWatchedRepository.kt new file mode 100644 index 0000000..60278e6 --- /dev/null +++ b/apps/vbox/backend/src/main/kotlin/ru/shadowsparky/vbox/backend/data/recent/CacheRecentlyWatchedRepository.kt @@ -0,0 +1,59 @@ +package ru.shadowsparky.vbox.backend.data.recent + +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.flow +import ru.shadowsparky.vbox.backend.data.RedisCache +import ru.shadowsparky.vbox.shared.domain.RecentlyWatchedRepository +import ru.shadowsparky.vbox.shared.domain.model.RecentlyWatchedInfo +import kotlin.time.Duration.Companion.days + +class CacheRecentlyWatchedRepository( + private val wrapper: RecentlyWatchedRepository, + private val redis: RedisCache, + userId: Long +) : RecentlyWatchedRepository { + private val prefix = "$PREFIX:$userId" + + override suspend fun remove(info: RecentlyWatchedInfo) { + info.obtainKeys().forEach { redis.delete(it) } + wrapper.remove(info) + } + + override suspend fun write(info: RecentlyWatchedInfo) { + info.obtainKeys().forEach { redis.delete(it) } + wrapper.write(info) + } + + private fun RecentlyWatchedInfo.obtainKeys(): Set { + return setOf( + "$prefix:$movieId:$season", + prefix + ) + } + + override fun queryRecentlyWatched( + movieId: Long, + seasonId: Long? + ): Flow> { + return flow { + emit( + redis.exec( + "$prefix:$movieId:$seasonId", + 7.days + ) { wrapper.queryRecentlyWatched(movieId, seasonId).first() } + ) + } + } + + override suspend fun getAllRecentlyWatched(): List { + return redis.exec( + prefix, + 7.days + ) { wrapper.getAllRecentlyWatched() } + } + + companion object { + const val PREFIX = "recently_watched" + } +} diff --git a/apps/vbox/backend/src/main/kotlin/ru/shadowsparky/vbox/backend/di/HttpModule.kt b/apps/vbox/backend/src/main/kotlin/ru/shadowsparky/vbox/backend/di/HttpModule.kt index 5642da6..d4af85e 100644 --- a/apps/vbox/backend/src/main/kotlin/ru/shadowsparky/vbox/backend/di/HttpModule.kt +++ b/apps/vbox/backend/src/main/kotlin/ru/shadowsparky/vbox/backend/di/HttpModule.kt @@ -5,7 +5,7 @@ import org.koin.core.annotation.Module import ru.shadowsparky.vbox.backend.data.FuzzyMovieSearchFilter import ru.shadowsparky.vbox.backend.data.RedisCache import ru.shadowsparky.vbox.backend.data.http.ExternalBackendApi -import ru.shadowsparky.vbox.backend.data.http.SessionVideoApi +import ru.shadowsparky.vbox.backend.data.http.CacheVideoApi import ru.shadowsparky.vbox.shared.domain.VideoApi @Module @@ -17,6 +17,6 @@ class HttpModule { filter: FuzzyMovieSearchFilter, redisCache: RedisCache ): VideoApi { - return SessionVideoApi(impl, filter, redisCache) + return CacheVideoApi(impl, filter, redisCache) } } diff --git a/apps/vbox/backend/src/main/kotlin/ru/shadowsparky/vbox/backend/di/factory/RecentlyWatchedRepositoryFactory.kt b/apps/vbox/backend/src/main/kotlin/ru/shadowsparky/vbox/backend/di/factory/RecentlyWatchedRepositoryFactory.kt index cc09ae4..d285ecf 100644 --- a/apps/vbox/backend/src/main/kotlin/ru/shadowsparky/vbox/backend/di/factory/RecentlyWatchedRepositoryFactory.kt +++ b/apps/vbox/backend/src/main/kotlin/ru/shadowsparky/vbox/backend/di/factory/RecentlyWatchedRepositoryFactory.kt @@ -2,17 +2,26 @@ package ru.shadowsparky.vbox.backend.di.factory import org.koin.core.annotation.Factory import ru.shadowsparky.vbox.backend.AppDatabase -import ru.shadowsparky.vbox.backend.data.BackendRecentlyWatchedRepository +import ru.shadowsparky.vbox.backend.data.RedisCache +import ru.shadowsparky.vbox.backend.data.recent.BackendRecentlyWatchedRepository +import ru.shadowsparky.vbox.backend.data.recent.CacheRecentlyWatchedRepository import ru.shadowsparky.vbox.shared.di.factory.DispatcherProvider +import ru.shadowsparky.vbox.shared.domain.RecentlyWatchedRepository import ru.shadowsparky.vbox.shared.domain.RemoteEventHandler @Factory class RecentlyWatchedRepositoryFactory( private val db: AppDatabase, private val dispatcherProvider: DispatcherProvider, - private val remoteEventHandler: RemoteEventHandler + private val remoteEventHandler: RemoteEventHandler, + private val redisCache: RedisCache ) { - fun create(userId: Long): BackendRecentlyWatchedRepository { - return BackendRecentlyWatchedRepository(db, userId, dispatcherProvider, remoteEventHandler) + fun create(userId: Long): RecentlyWatchedRepository { + val impl = BackendRecentlyWatchedRepository(db, userId, dispatcherProvider, remoteEventHandler) + return CacheRecentlyWatchedRepository( + impl, + redisCache, + userId + ) } } diff --git a/apps/vbox/backend/src/main/kotlin/ru/shadowsparky/vbox/backend/presentation/routing/RecentlyWatchedRouting.kt b/apps/vbox/backend/src/main/kotlin/ru/shadowsparky/vbox/backend/presentation/routing/RecentlyWatchedRouting.kt index 166f7d1..c6d765c 100644 --- a/apps/vbox/backend/src/main/kotlin/ru/shadowsparky/vbox/backend/presentation/routing/RecentlyWatchedRouting.kt +++ b/apps/vbox/backend/src/main/kotlin/ru/shadowsparky/vbox/backend/presentation/routing/RecentlyWatchedRouting.kt @@ -38,11 +38,4 @@ fun Route.setupRecentlyWatched(recentlyFactory: RecentlyWatchedRepositoryFactory } call.respond(HttpStatusCode.OK) } - post(RecentlyWatchedRepository.TOGGLE) { - val repo = recentlyFactory.create(call.obtainUserId()) - call.receive().info.forEach { - repo.toggleInfo(it) - } - call.respond(HttpStatusCode.OK) - } } diff --git a/apps/vbox/common/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/domain/RecentlyWatchedRepository.kt b/apps/vbox/common/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/domain/RecentlyWatchedRepository.kt index c304ba2..232d264 100644 --- a/apps/vbox/common/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/domain/RecentlyWatchedRepository.kt +++ b/apps/vbox/common/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/domain/RecentlyWatchedRepository.kt @@ -28,7 +28,6 @@ interface RecentlyWatchedRepository { const val REMOVE = "$PREFIX/delete" const val WRITE = "$PREFIX/add" - const val TOGGLE = "$PREFIX/toggle" const val GET_INFO = "$PREFIX/query" const val MOVIE_ID_ARG = "movieId"