feat: add smart download feature for series

Automatically manages downloaded episodes per series — keeps 5 unwatched
episodes downloaded, removing watched ones and fetching new ones on
HomeScreen open or pull-to-refresh. A single download button on the
Series screen opens a dialog to choose between downloading all episodes
or enabling smart download.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-03 22:21:21 +01:00
parent 2d278bd348
commit 3941c67d8b
9 changed files with 230 additions and 12 deletions

View File

@@ -11,6 +11,7 @@ import hu.bbara.purefin.core.data.room.dao.EpisodeDao
import hu.bbara.purefin.core.data.room.dao.MovieDao
import hu.bbara.purefin.core.data.room.dao.SeasonDao
import hu.bbara.purefin.core.data.room.dao.SeriesDao
import hu.bbara.purefin.core.data.room.dao.SmartDownloadDao
import hu.bbara.purefin.core.data.room.offline.OfflineMediaDatabase
import hu.bbara.purefin.core.data.room.offline.OfflineRoomMediaLocalDataSource
import javax.inject.Singleton
@@ -39,6 +40,9 @@ object MediaDatabaseModule {
@Provides
fun provideOfflineEpisodeDao(db: OfflineMediaDatabase) = db.episodeDao()
@Provides
fun provideSmartDownloadDao(db: OfflineMediaDatabase): SmartDownloadDao = db.smartDownloadDao()
@Provides
@Singleton
fun provideOfflineDataSource(

View File

@@ -0,0 +1,30 @@
package hu.bbara.purefin.core.data.room.dao
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import hu.bbara.purefin.core.data.room.entity.SmartDownloadEntity
import kotlinx.coroutines.flow.Flow
import java.util.UUID
@Dao
interface SmartDownloadDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(entity: SmartDownloadEntity)
@Query("DELETE FROM smart_downloads WHERE seriesId = :seriesId")
suspend fun delete(seriesId: UUID)
@Query("SELECT * FROM smart_downloads")
suspend fun getAll(): List<SmartDownloadEntity>
@Query("SELECT EXISTS(SELECT 1 FROM smart_downloads WHERE seriesId = :seriesId)")
suspend fun exists(seriesId: UUID): Boolean
@Query("SELECT EXISTS(SELECT 1 FROM smart_downloads WHERE seriesId = :seriesId)")
fun observe(seriesId: UUID): Flow<Boolean>
@Query("SELECT * FROM smart_downloads")
fun observeAll(): Flow<List<SmartDownloadEntity>>
}

View File

@@ -0,0 +1,10 @@
package hu.bbara.purefin.core.data.room.entity
import androidx.room.Entity
import androidx.room.PrimaryKey
import java.util.UUID
@Entity(tableName = "smart_downloads")
data class SmartDownloadEntity(
@PrimaryKey val seriesId: UUID
)

View File

@@ -8,10 +8,12 @@ import hu.bbara.purefin.core.data.room.dao.EpisodeDao
import hu.bbara.purefin.core.data.room.dao.MovieDao
import hu.bbara.purefin.core.data.room.dao.SeasonDao
import hu.bbara.purefin.core.data.room.dao.SeriesDao
import hu.bbara.purefin.core.data.room.dao.SmartDownloadDao
import hu.bbara.purefin.core.data.room.entity.EpisodeEntity
import hu.bbara.purefin.core.data.room.entity.MovieEntity
import hu.bbara.purefin.core.data.room.entity.SeasonEntity
import hu.bbara.purefin.core.data.room.entity.SeriesEntity
import hu.bbara.purefin.core.data.room.entity.SmartDownloadEntity
@Database(
entities = [
@@ -19,8 +21,9 @@ import hu.bbara.purefin.core.data.room.entity.SeriesEntity
SeriesEntity::class,
SeasonEntity::class,
EpisodeEntity::class,
SmartDownloadEntity::class,
],
version = 5,
version = 6,
exportSchema = false
)
@TypeConverters(UuidConverters::class)
@@ -29,4 +32,5 @@ abstract class OfflineMediaDatabase : RoomDatabase() {
abstract fun seriesDao(): SeriesDao
abstract fun seasonDao(): SeasonDao
abstract fun episodeDao(): EpisodeDao
abstract fun smartDownloadDao(): SmartDownloadDao
}