mirror of
https://github.com/bbara04/Purefin.git
synced 2026-07-24 03:36:51 +00:00
feat: add smart download management with enable and delete options
This commit is contained in:
@@ -60,6 +60,7 @@ fun SeriesScreen(
|
|||||||
series = seriesData,
|
series = seriesData,
|
||||||
seriesDownloadState = viewModel.seriesDownloadState.collectAsState().value,
|
seriesDownloadState = viewModel.seriesDownloadState.collectAsState().value,
|
||||||
seasonDownloadState = viewModel.seasonDownloadState.collectAsState().value,
|
seasonDownloadState = viewModel.seasonDownloadState.collectAsState().value,
|
||||||
|
isSmartDownloadEnabled = viewModel.isSmartDownloadEnabled.collectAsState().value,
|
||||||
onDownloadOptionSelected = { option, selectedSeason ->
|
onDownloadOptionSelected = { option, selectedSeason ->
|
||||||
when (option) {
|
when (option) {
|
||||||
SeriesDownloadOption.SEASON ->
|
SeriesDownloadOption.SEASON ->
|
||||||
@@ -70,6 +71,9 @@ fun SeriesScreen(
|
|||||||
|
|
||||||
SeriesDownloadOption.SMART ->
|
SeriesDownloadOption.SMART ->
|
||||||
viewModel.enableSmartDownload(seriesData.id)
|
viewModel.enableSmartDownload(seriesData.id)
|
||||||
|
|
||||||
|
SeriesDownloadOption.DELETE_SMART ->
|
||||||
|
viewModel.deleteSmartDownloads(seriesData.id)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onLoadSeasonEpisodes = viewModel::loadSeasonEpisodes,
|
onLoadSeasonEpisodes = viewModel::loadSeasonEpisodes,
|
||||||
@@ -88,6 +92,7 @@ private fun SeriesScreenInternal(
|
|||||||
series: Series,
|
series: Series,
|
||||||
seriesDownloadState: DownloadState,
|
seriesDownloadState: DownloadState,
|
||||||
seasonDownloadState: DownloadState,
|
seasonDownloadState: DownloadState,
|
||||||
|
isSmartDownloadEnabled: Boolean,
|
||||||
onDownloadOptionSelected: (SeriesDownloadOption, Season) -> Unit,
|
onDownloadOptionSelected: (SeriesDownloadOption, Season) -> Unit,
|
||||||
onLoadSeasonEpisodes: (UUID, UUID) -> Unit,
|
onLoadSeasonEpisodes: (UUID, UUID) -> Unit,
|
||||||
onObserveSeasonDownloadState: (List<Episode>) -> Unit,
|
onObserveSeasonDownloadState: (List<Episode>) -> Unit,
|
||||||
@@ -130,6 +135,7 @@ private fun SeriesScreenInternal(
|
|||||||
seriesDownloadState = seriesDownloadState,
|
seriesDownloadState = seriesDownloadState,
|
||||||
selectedSeason = selectedSeason,
|
selectedSeason = selectedSeason,
|
||||||
seasonDownloadState = seasonDownloadState,
|
seasonDownloadState = seasonDownloadState,
|
||||||
|
isSmartDownloadEnabled = isSmartDownloadEnabled,
|
||||||
offline = offline,
|
offline = offline,
|
||||||
onDownloadOptionSelected = { option ->
|
onDownloadOptionSelected = { option ->
|
||||||
onDownloadOptionSelected(option, selectedSeason)
|
onDownloadOptionSelected(option, selectedSeason)
|
||||||
@@ -188,6 +194,7 @@ private fun SeriesScreenPreview() {
|
|||||||
series = previewSeries(),
|
series = previewSeries(),
|
||||||
seriesDownloadState = DownloadState.Downloading(progressPercent = 0.58f),
|
seriesDownloadState = DownloadState.Downloading(progressPercent = 0.58f),
|
||||||
seasonDownloadState = DownloadState.NotDownloaded,
|
seasonDownloadState = DownloadState.NotDownloaded,
|
||||||
|
isSmartDownloadEnabled = true,
|
||||||
onDownloadOptionSelected = { _, _ -> },
|
onDownloadOptionSelected = { _, _ -> },
|
||||||
onLoadSeasonEpisodes = { _, _ -> },
|
onLoadSeasonEpisodes = { _, _ -> },
|
||||||
onObserveSeasonDownloadState = {},
|
onObserveSeasonDownloadState = {},
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import androidx.compose.material.icons.outlined.DownloadDone
|
|||||||
import androidx.compose.material.icons.outlined.MoreVert
|
import androidx.compose.material.icons.outlined.MoreVert
|
||||||
import androidx.compose.material.icons.outlined.PlayCircle
|
import androidx.compose.material.icons.outlined.PlayCircle
|
||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material3.AlertDialog
|
||||||
|
import androidx.compose.material3.ButtonDefaults
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
@@ -112,6 +113,7 @@ internal fun SeriesActionButtons(
|
|||||||
seriesDownloadState: DownloadState,
|
seriesDownloadState: DownloadState,
|
||||||
selectedSeason: Season,
|
selectedSeason: Season,
|
||||||
seasonDownloadState: DownloadState,
|
seasonDownloadState: DownloadState,
|
||||||
|
isSmartDownloadEnabled: Boolean,
|
||||||
offline: Boolean,
|
offline: Boolean,
|
||||||
onDownloadOptionSelected: (SeriesDownloadOption) -> Unit,
|
onDownloadOptionSelected: (SeriesDownloadOption) -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
@@ -171,6 +173,7 @@ internal fun SeriesActionButtons(
|
|||||||
DownloadOptionsDialog(
|
DownloadOptionsDialog(
|
||||||
selectedSeasonName = selectedSeason.name,
|
selectedSeasonName = selectedSeason.name,
|
||||||
seasonDownloadState = seasonDownloadState,
|
seasonDownloadState = seasonDownloadState,
|
||||||
|
isSmartDownloadEnabled = isSmartDownloadEnabled,
|
||||||
onDownloadOptionSelected = {
|
onDownloadOptionSelected = {
|
||||||
showDownloadDialog = false
|
showDownloadDialog = false
|
||||||
onDownloadOptionSelected(it)
|
onDownloadOptionSelected(it)
|
||||||
@@ -183,13 +186,15 @@ internal fun SeriesActionButtons(
|
|||||||
internal enum class SeriesDownloadOption {
|
internal enum class SeriesDownloadOption {
|
||||||
SEASON,
|
SEASON,
|
||||||
SERIES,
|
SERIES,
|
||||||
SMART
|
SMART,
|
||||||
|
DELETE_SMART
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun DownloadOptionsDialog(
|
private fun DownloadOptionsDialog(
|
||||||
selectedSeasonName: String,
|
selectedSeasonName: String,
|
||||||
seasonDownloadState: DownloadState,
|
seasonDownloadState: DownloadState,
|
||||||
|
isSmartDownloadEnabled: Boolean,
|
||||||
onDownloadOptionSelected: (SeriesDownloadOption) -> Unit,
|
onDownloadOptionSelected: (SeriesDownloadOption) -> Unit,
|
||||||
onDismiss: () -> Unit,
|
onDismiss: () -> Unit,
|
||||||
) {
|
) {
|
||||||
@@ -221,10 +226,21 @@ private fun DownloadOptionsDialog(
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
dismissButton = {
|
dismissButton = {
|
||||||
|
if (isSmartDownloadEnabled) {
|
||||||
|
TextButton(
|
||||||
|
onClick = { onDownloadOptionSelected(SeriesDownloadOption.DELETE_SMART) },
|
||||||
|
colors = ButtonDefaults.textButtonColors(
|
||||||
|
contentColor = MaterialTheme.colorScheme.error
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
Text("Delete Smart Downloads")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
TextButton(onClick = { onDownloadOptionSelected(SeriesDownloadOption.SMART) }) {
|
TextButton(onClick = { onDownloadOptionSelected(SeriesDownloadOption.SMART) }) {
|
||||||
Text("Smart Download")
|
Text("Smart Download")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,5 +13,7 @@ interface MediaDownloadController {
|
|||||||
suspend fun downloadEpisodes(episodeIds: List<UUID>)
|
suspend fun downloadEpisodes(episodeIds: List<UUID>)
|
||||||
suspend fun cancelEpisodeDownload(episodeId: UUID)
|
suspend fun cancelEpisodeDownload(episodeId: UUID)
|
||||||
suspend fun enableSmartDownload(seriesId: UUID)
|
suspend fun enableSmartDownload(seriesId: UUID)
|
||||||
|
suspend fun deleteSmartDownloads(seriesId: UUID)
|
||||||
|
fun isSmartDownloadEnabled(seriesId: UUID): Flow<Boolean>
|
||||||
suspend fun syncSmartDownloads()
|
suspend fun syncSmartDownloads()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -206,11 +206,16 @@ class MediaDownloadManager @Inject constructor(
|
|||||||
syncSmartDownloadsForSeries(seriesId)
|
syncSmartDownloadsForSeries(seriesId)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun disableSmartDownload(seriesId: UUID) {
|
override suspend fun deleteSmartDownloads(seriesId: UUID) {
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
smartDownloadStore.disable(seriesId)
|
smartDownloadStore.disable(seriesId)
|
||||||
|
offlineCatalogStore.getEpisodesBySeries(seriesId).forEach { episode ->
|
||||||
|
cancelEpisodeDownload(episode.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isSmartDownloadEnabled(seriesId: UUID): Flow<Boolean> = smartDownloadStore.observe(seriesId)
|
override fun isSmartDownloadEnabled(seriesId: UUID): Flow<Boolean> = smartDownloadStore.observe(seriesId)
|
||||||
|
|
||||||
override suspend fun syncSmartDownloads() {
|
override suspend fun syncSmartDownloads() {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
|
|||||||
@@ -88,9 +88,15 @@ class EpisodeScreenViewModel @Inject constructor(
|
|||||||
is DownloadState.NotDownloaded, is DownloadState.Failed -> {
|
is DownloadState.NotDownloaded, is DownloadState.Failed -> {
|
||||||
mediaDownloadManager.downloadEpisode(episodeId)
|
mediaDownloadManager.downloadEpisode(episodeId)
|
||||||
}
|
}
|
||||||
is DownloadState.Downloading, is DownloadState.Downloaded -> {
|
is DownloadState.Downloading -> {
|
||||||
mediaDownloadManager.cancelEpisodeDownload(episodeId)
|
mediaDownloadManager.cancelEpisodeDownload(episodeId)
|
||||||
}
|
}
|
||||||
|
is DownloadState.Downloaded -> {
|
||||||
|
mediaDownloadManager.cancelEpisodeDownload(episodeId)
|
||||||
|
if (_episode.value?.offline == true) {
|
||||||
|
navigationManager.pop()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,6 +80,9 @@ class MovieScreenViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
is DownloadState.Downloaded -> {
|
is DownloadState.Downloaded -> {
|
||||||
mediaDownloadManager.cancelDownload(movieId)
|
mediaDownloadManager.cancelDownload(movieId)
|
||||||
|
if (_movie.value?.offline == true) {
|
||||||
|
navigationManager.pop()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,6 +48,17 @@ class SeriesViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), null)
|
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), null)
|
||||||
|
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
val isSmartDownloadEnabled: StateFlow<Boolean> = _series
|
||||||
|
.flatMapLatest { series ->
|
||||||
|
if (series == null) {
|
||||||
|
flowOf(false)
|
||||||
|
} else {
|
||||||
|
mediaDownloadManager.isSmartDownloadEnabled(series.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), false)
|
||||||
|
|
||||||
private val _seriesDownloadState = MutableStateFlow<DownloadState>(DownloadState.NotDownloaded)
|
private val _seriesDownloadState = MutableStateFlow<DownloadState>(DownloadState.NotDownloaded)
|
||||||
val seriesDownloadState: StateFlow<DownloadState> = _seriesDownloadState
|
val seriesDownloadState: StateFlow<DownloadState> = _seriesDownloadState
|
||||||
|
|
||||||
@@ -111,6 +122,15 @@ class SeriesViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun deleteSmartDownloads(seriesId: UUID) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
mediaDownloadManager.deleteSmartDownloads(seriesId)
|
||||||
|
if (_series.value?.offline == true) {
|
||||||
|
navigationManager.pop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun downloadSeries(seriesData: Series) {
|
fun downloadSeries(seriesData: Series) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
val mediaCatalogReader = selectedMediaCatalogReader()
|
val mediaCatalogReader = selectedMediaCatalogReader()
|
||||||
|
|||||||
Reference in New Issue
Block a user