refactor(offline): rename OfflineCatalogStore to OfflineMediaManager and consolidate into repository

- Rename interface OfflineCatalogStore → OfflineMediaManager for clarity

- Merge RoomOfflineCatalogStore functionality into OfflineLocalMediaRepository

- Remove RoomOfflineCatalogStore; update DI binding and consumers
This commit is contained in:
2026-06-12 16:11:34 +00:00
parent 2853609cbb
commit 4727cdcab1
5 changed files with 55 additions and 68 deletions

View File

@@ -6,7 +6,7 @@ import hu.bbara.purefin.model.Season
import hu.bbara.purefin.model.Series import hu.bbara.purefin.model.Series
import java.util.UUID import java.util.UUID
interface OfflineCatalogStore { interface OfflineMediaManager {
suspend fun saveMovies(movies: List<Movie>) suspend fun saveMovies(movies: List<Movie>)
suspend fun saveSeries(series: List<Series>) suspend fun saveSeries(series: List<Series>)
suspend fun saveSeason(season: Season) suspend fun saveSeason(season: Season)

View File

@@ -10,7 +10,7 @@ import androidx.media3.exoplayer.offline.DownloadManager
import androidx.media3.exoplayer.offline.DownloadRequest import androidx.media3.exoplayer.offline.DownloadRequest
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
import hu.bbara.purefin.core.data.DownloadMediaSourceResolver import hu.bbara.purefin.core.data.DownloadMediaSourceResolver
import hu.bbara.purefin.core.data.OfflineCatalogStore import hu.bbara.purefin.core.data.OfflineMediaManager
import hu.bbara.purefin.core.data.SmartDownloadStore import hu.bbara.purefin.core.data.SmartDownloadStore
import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@@ -36,7 +36,7 @@ class MediaDownloadManager @Inject constructor(
@ApplicationContext private val context: Context, @ApplicationContext private val context: Context,
private val downloadManager: DownloadManager, private val downloadManager: DownloadManager,
private val downloadMediaSourceResolver: DownloadMediaSourceResolver, private val downloadMediaSourceResolver: DownloadMediaSourceResolver,
private val offlineCatalogStore: OfflineCatalogStore, private val offlineMediaManager: OfflineMediaManager,
private val smartDownloadStore: SmartDownloadStore, private val smartDownloadStore: SmartDownloadStore,
) : MediaDownloadController { ) : MediaDownloadController {
@@ -125,7 +125,7 @@ class MediaDownloadManager @Inject constructor(
return@withContext return@withContext
} }
offlineCatalogStore.saveMovies(listOf(source.movie)) offlineMediaManager.saveMovies(listOf(source.movie))
Timber.tag(TAG).d("Starting download for '${source.movie.title}' from: ${source.playbackUrl}") Timber.tag(TAG).d("Starting download for '${source.movie.title}' from: ${source.playbackUrl}")
val request = buildDownloadRequest( val request = buildDownloadRequest(
@@ -147,7 +147,7 @@ class MediaDownloadManager @Inject constructor(
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
PurefinDownloadService.sendRemoveDownload(context, movieId.toString()) PurefinDownloadService.sendRemoveDownload(context, movieId.toString())
try { try {
offlineCatalogStore.deleteMovie(movieId) offlineMediaManager.deleteMovie(movieId)
} catch (e: Exception) { } catch (e: Exception) {
Timber.tag(TAG).e(e, "Failed to remove movie from offline DB") Timber.tag(TAG).e(e, "Failed to remove movie from offline DB")
} }
@@ -162,15 +162,15 @@ class MediaDownloadManager @Inject constructor(
return@withContext return@withContext
} }
if (offlineCatalogStore.getSeriesBasic(source.series.id) == null) { if (offlineMediaManager.getSeriesBasic(source.series.id) == null) {
offlineCatalogStore.saveSeries(listOf(source.series)) offlineMediaManager.saveSeries(listOf(source.series))
} }
if (offlineCatalogStore.getSeason(source.season.id) == null) { if (offlineMediaManager.getSeason(source.season.id) == null) {
offlineCatalogStore.saveSeason(source.season) offlineMediaManager.saveSeason(source.season)
} }
offlineCatalogStore.saveEpisode(source.episode) offlineMediaManager.saveEpisode(source.episode)
Timber.tag(TAG).d("Starting download for episode '${source.episode.title}' from: ${source.playbackUrl}") Timber.tag(TAG).d("Starting download for episode '${source.episode.title}' from: ${source.playbackUrl}")
val request = buildDownloadRequest( val request = buildDownloadRequest(
@@ -200,7 +200,7 @@ class MediaDownloadManager @Inject constructor(
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
PurefinDownloadService.sendRemoveDownload(context, episodeId.toString()) PurefinDownloadService.sendRemoveDownload(context, episodeId.toString())
try { try {
offlineCatalogStore.deleteEpisodeAndCleanup(episodeId) offlineMediaManager.deleteEpisodeAndCleanup(episodeId)
} catch (e: Exception) { } catch (e: Exception) {
Timber.tag(TAG).e(e, "Failed to remove episode from offline DB") Timber.tag(TAG).e(e, "Failed to remove episode from offline DB")
} }
@@ -217,7 +217,7 @@ class MediaDownloadManager @Inject constructor(
override suspend fun deleteSmartDownloads(seriesId: UUID) { override suspend fun deleteSmartDownloads(seriesId: UUID) {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
smartDownloadStore.disable(seriesId) smartDownloadStore.disable(seriesId)
offlineCatalogStore.getEpisodesBySeries(seriesId).forEach { episode -> offlineMediaManager.getEpisodesBySeries(seriesId).forEach { episode ->
cancelEpisodeDownload(episode.id) cancelEpisodeDownload(episode.id)
} }
} }
@@ -243,7 +243,7 @@ class MediaDownloadManager @Inject constructor(
private suspend fun syncSmartDownloadsForSeries(seriesId: UUID) { private suspend fun syncSmartDownloadsForSeries(seriesId: UUID) {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
// 1. Get currently downloaded episodes for this series // 1. Get currently downloaded episodes for this series
val downloadedEpisodes = offlineCatalogStore.getEpisodesBySeries(seriesId) val downloadedEpisodes = offlineMediaManager.getEpisodesBySeries(seriesId)
// 2. Check watched status from server and delete watched downloads // 2. Check watched status from server and delete watched downloads
val unwatchedDownloaded = mutableListOf<UUID>() val unwatchedDownloaded = mutableListOf<UUID>()

View File

@@ -1,9 +1,11 @@
package hu.bbara.purefin.data.catalog package hu.bbara.purefin.data.catalog
import hu.bbara.purefin.core.data.LocalMediaRepository import hu.bbara.purefin.core.data.LocalMediaRepository
import hu.bbara.purefin.core.data.OfflineMediaManager
import hu.bbara.purefin.data.offline.room.offline.OfflineRoomMediaLocalDataSource import hu.bbara.purefin.data.offline.room.offline.OfflineRoomMediaLocalDataSource
import hu.bbara.purefin.model.Episode import hu.bbara.purefin.model.Episode
import hu.bbara.purefin.model.Movie import hu.bbara.purefin.model.Movie
import hu.bbara.purefin.model.Season
import hu.bbara.purefin.model.Series import hu.bbara.purefin.model.Series
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@@ -20,7 +22,7 @@ import javax.inject.Singleton
@Singleton @Singleton
class OfflineLocalMediaRepository @Inject constructor( class OfflineLocalMediaRepository @Inject constructor(
private val localDataSource: OfflineRoomMediaLocalDataSource, private val localDataSource: OfflineRoomMediaLocalDataSource,
) : LocalMediaRepository { ) : LocalMediaRepository, OfflineMediaManager {
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO) private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
override val movies: StateFlow<Map<UUID, Movie>> = localDataSource.moviesFlow override val movies: StateFlow<Map<UUID, Movie>> = localDataSource.moviesFlow
@@ -68,4 +70,40 @@ class OfflineLocalMediaRepository @Inject constructor(
override suspend fun markAsWatched(mediaId: UUID, watched: Boolean) { override suspend fun markAsWatched(mediaId: UUID, watched: Boolean) {
// Do nothing // Do nothing
} }
override suspend fun saveMovies(movies: List<Movie>) {
localDataSource.saveMovies(movies)
}
override suspend fun saveSeries(series: List<Series>) {
localDataSource.saveSeries(series)
}
override suspend fun saveSeason(season: Season) {
localDataSource.saveSeason(season)
}
override suspend fun saveEpisode(episode: Episode) {
localDataSource.saveEpisode(episode)
}
override suspend fun getSeriesBasic(seriesId: UUID): Series? {
return localDataSource.getSeriesBasic(seriesId)
}
override suspend fun getSeason(seasonId: UUID): Season? {
return localDataSource.getSeason(seasonId)
}
override suspend fun deleteMovie(movieId: UUID) {
localDataSource.deleteMovie(movieId)
}
override suspend fun deleteEpisodeAndCleanup(episodeId: UUID) {
localDataSource.deleteEpisodeAndCleanup(episodeId)
}
override suspend fun getEpisodesBySeries(seriesId: UUID): List<Episode> {
return localDataSource.getEpisodesBySeries(seriesId)
}
} }

View File

@@ -4,15 +4,16 @@ import dagger.Binds
import dagger.Module import dagger.Module
import dagger.hilt.InstallIn import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent import dagger.hilt.components.SingletonComponent
import hu.bbara.purefin.core.data.OfflineCatalogStore import hu.bbara.purefin.core.data.OfflineMediaManager
import hu.bbara.purefin.core.data.SmartDownloadStore import hu.bbara.purefin.core.data.SmartDownloadStore
import hu.bbara.purefin.data.catalog.OfflineLocalMediaRepository
@Module @Module
@InstallIn(SingletonComponent::class) @InstallIn(SingletonComponent::class)
abstract class OfflineBindingsModule { abstract class OfflineBindingsModule {
@Binds @Binds
abstract fun bindOfflineCatalogStore(impl: RoomOfflineCatalogStore): OfflineCatalogStore abstract fun bindOfflineMediaManager(impl: OfflineLocalMediaRepository): OfflineMediaManager
@Binds @Binds
abstract fun bindSmartDownloadStore(impl: RoomSmartDownloadStore): SmartDownloadStore abstract fun bindSmartDownloadStore(impl: RoomSmartDownloadStore): SmartDownloadStore

View File

@@ -1,52 +0,0 @@
package hu.bbara.purefin.data.offline
import hu.bbara.purefin.core.data.OfflineCatalogStore
import hu.bbara.purefin.data.offline.room.offline.OfflineRoomMediaLocalDataSource
import hu.bbara.purefin.model.Episode
import hu.bbara.purefin.model.Movie
import hu.bbara.purefin.model.Season
import hu.bbara.purefin.model.Series
import java.util.UUID
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class RoomOfflineCatalogStore @Inject constructor(
private val localDataSource: OfflineRoomMediaLocalDataSource,
) : OfflineCatalogStore {
override suspend fun saveMovies(movies: List<Movie>) {
localDataSource.saveMovies(movies)
}
override suspend fun saveSeries(series: List<Series>) {
localDataSource.saveSeries(series)
}
override suspend fun saveSeason(season: Season) {
localDataSource.saveSeason(season)
}
override suspend fun saveEpisode(episode: Episode) {
localDataSource.saveEpisode(episode)
}
override suspend fun getSeriesBasic(seriesId: UUID): Series? {
return localDataSource.getSeriesBasic(seriesId)
}
override suspend fun getSeason(seasonId: UUID): Season? {
return localDataSource.getSeason(seasonId)
}
override suspend fun deleteMovie(movieId: UUID) {
localDataSource.deleteMovie(movieId)
}
override suspend fun deleteEpisodeAndCleanup(episodeId: UUID) {
localDataSource.deleteEpisodeAndCleanup(episodeId)
}
override suspend fun getEpisodesBySeries(seriesId: UUID): List<Episode> {
return localDataSource.getEpisodesBySeries(seriesId)
}
}