Skip to content

Commit

Permalink
Feat: Auto refresh programmes.
Browse files Browse the repository at this point in the history
  • Loading branch information
oxyroid committed Jun 1, 2024
1 parent f48f2dc commit 06aaf90
Show file tree
Hide file tree
Showing 25 changed files with 145 additions and 55 deletions.
27 changes: 27 additions & 0 deletions androidApp/src/main/java/com/m3u/androidApp/ui/AppViewModel.kt
Original file line number Diff line number Diff line change
@@ -1,21 +1,26 @@
package com.m3u.androidApp.ui

import android.util.Log
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.work.WorkManager
import com.m3u.androidApp.ui.sheet.RemoteControlSheetValue
import com.m3u.core.architecture.Publisher
import com.m3u.core.architecture.dispatcher.Dispatcher
import com.m3u.core.architecture.dispatcher.M3uDispatchers.IO
import com.m3u.core.architecture.preferences.Preferences
import com.m3u.data.api.LocalPreparedService
import com.m3u.data.repository.playlist.PlaylistRepository
import com.m3u.data.repository.programme.ProgrammeRepository
import com.m3u.data.repository.television.ConnectionToTelevisionValue
import com.m3u.data.repository.television.TelevisionRepository
import com.m3u.data.service.Messager
import com.m3u.data.television.model.RemoteDirection
import com.m3u.data.worker.SubscriptionWorker
import com.m3u.ui.Destination
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.CoroutineDispatcher
Expand All @@ -37,12 +42,21 @@ import javax.inject.Inject
@HiltViewModel
class AppViewModel @Inject constructor(
messager: Messager,
private val playlistRepository: PlaylistRepository,
private val programmeRepository: ProgrammeRepository,
private val televisionRepository: TelevisionRepository,
private val localService: LocalPreparedService,
private val workManager: WorkManager,
private val preferences: Preferences,
private val publisher: Publisher,
@Dispatcher(IO) private val ioDispatcher: CoroutineDispatcher,
) : ViewModel() {
init {
if (preferences.autoRefreshProgrammes) {
refreshAllProgrammes()
}
}

val broadcastCodeOnTelevision: StateFlow<String?> = televisionRepository
.broadcastCodeOnTelevision
.map { code -> code?.let { convertToPaddedString(it) } }
Expand Down Expand Up @@ -145,6 +159,19 @@ class AppViewModel @Inject constructor(
}
}

private fun refreshAllProgrammes() {
viewModelScope.launch {
val playlists = playlistRepository.getAll()
playlists.forEach { playlist ->
SubscriptionWorker.epg(
workManager = workManager,
playlistUrl = playlist.url,
ignoreCache = true
)
}
}
}

var rootDestination: Destination.Root by mutableStateOf(
when (preferences.rootDestination) {
0 -> Destination.Root.Foryou
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,11 @@ class Preferences @Inject constructor(
var clipMode: Int by
sharedPreferences.intAsState(DEFAULT_CLIP_MODE, CLIP_MODE)

var autoRefresh: Boolean by
sharedPreferences.booleanAsState(DEFAULT_AUTO_REFRESH, AUTO_REFRESH)
var autoRefreshChannels: Boolean by
sharedPreferences.booleanAsState(DEFAULT_AUTO_REFRESH_CHANNELS, AUTO_REFRESH_CHANNELS)

var autoRefreshProgrammes: Boolean by
sharedPreferences.booleanAsState(DEFAULT_AUTO_REFRESH_PROGRAMMES, AUTO_REFRESH_PROGRAMMES)

var fullInfoPlayer: Boolean by
sharedPreferences.booleanAsState(DEFAULT_FULL_INFO_PLAYER, FULL_INFO_PLAYER)
Expand Down Expand Up @@ -131,7 +134,8 @@ class Preferences @Inject constructor(

@ClipMode
const val DEFAULT_CLIP_MODE = ClipMode.ADAPTIVE
const val DEFAULT_AUTO_REFRESH = false
const val DEFAULT_AUTO_REFRESH_CHANNELS = false
const val DEFAULT_AUTO_REFRESH_PROGRAMMES = false
const val DEFAULT_FULL_INFO_PLAYER = false
const val DEFAULT_ROOT_DESTINATION = 0
const val DEFAULT_NO_PICTURE_MODE = false
Expand Down Expand Up @@ -170,7 +174,8 @@ class Preferences @Inject constructor(
const val GOD_MODE = "god-mode"

const val CLIP_MODE = "clip-mode"
const val AUTO_REFRESH = "auto-refresh"
const val AUTO_REFRESH_CHANNELS = "auto-refresh-channels"
const val AUTO_REFRESH_PROGRAMMES = "auto-refresh-programmes"
const val FULL_INFO_PLAYER = "full-info-player"
const val ROOT_DESTINATION = "root-destination"
const val NO_PICTURE_MODE = "no-picture-mode"
Expand Down
4 changes: 4 additions & 0 deletions data/src/main/java/com/m3u/data/database/dao/PlaylistDao.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import androidx.room.Transaction
import com.m3u.data.database.model.DataSource
import com.m3u.data.database.model.Playlist
import com.m3u.data.database.model.PlaylistWithCount
import com.m3u.data.database.model.PlaylistWithStreams
Expand All @@ -28,6 +29,9 @@ internal interface PlaylistDao {
@Query("SELECT * FROM playlists WHERE url = :url")
suspend fun getByUrl(url: String): Playlist?

@Query("SELECT * FROM playlists WHERE source = :source")
suspend fun getBySource(source: DataSource): List<Playlist>

@Query("SELECT * FROM playlists ORDER BY title")
fun observeAll(): Flow<List<Playlist>>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.m3u.data.repository.playlist

import android.net.Uri
import androidx.compose.runtime.Immutable
import com.m3u.data.database.model.DataSource
import com.m3u.data.database.model.Playlist
import com.m3u.data.database.model.PlaylistWithCount
import com.m3u.data.database.model.PlaylistWithStreams
Expand All @@ -14,6 +15,8 @@ interface PlaylistRepository {
fun observeAllEpgs(): Flow<List<Playlist>>
fun observePlaylistUrls(): Flow<List<String>>
suspend fun get(url: String): Playlist?
suspend fun getAll(): List<Playlist>
suspend fun getBySource(source: DataSource): List<Playlist>
suspend fun getCategoriesByPlaylistUrlIgnoreHidden(url: String, query: String): List<String>
fun observeCategoriesByPlaylistUrlIgnoreHidden(url: String, query: String): Flow<List<String>>
fun observe(url: String): Flow<Playlist?>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ import com.m3u.core.architecture.logger.execute
import com.m3u.core.architecture.logger.install
import com.m3u.core.architecture.logger.post
import com.m3u.core.architecture.logger.sandBox
import com.m3u.core.architecture.preferences.Preferences
import com.m3u.core.architecture.preferences.PlaylistStrategy
import com.m3u.core.architecture.preferences.Preferences
import com.m3u.core.util.basic.startsWithAny
import com.m3u.core.util.readFileContent
import com.m3u.core.util.readFileName
Expand Down Expand Up @@ -517,6 +517,14 @@ internal class PlaylistRepositoryImpl @Inject constructor(
playlistDao.getByUrl(url)
}

override suspend fun getAll(): List<Playlist> = logger.execute {
playlistDao.getAll()
} ?: emptyList()

override suspend fun getBySource(source: DataSource): List<Playlist> = logger.execute {
playlistDao.getBySource(source)
} ?: emptyList()

override suspend fun getCategoriesByPlaylistUrlIgnoreHidden(
url: String,
query: String
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ interface ProgrammeRepository {

val refreshingEpgUrls: StateFlow<List<String>>
fun checkOrRefreshProgrammesOrThrow(
playlistUrl: String,
vararg playlistUrls: String,
ignoreCache: Boolean
): Flow<Int>
}
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,15 @@ internal class ProgrammeRepositoryImpl @Inject constructor(
}

override fun checkOrRefreshProgrammesOrThrow(
playlistUrl: String,
vararg playlistUrls: String,
ignoreCache: Boolean
): Flow<Int> = channelFlow {
val playlist = playlistDao.getByUrl(playlistUrl) ?: return@channelFlow
val epgUrls = playlist.epgUrlsOrXtreamXmlUrl()
val epgUrls = playlistUrls.flatMap { playlistUrl ->
val playlist = playlistDao.getByUrl(playlistUrl) ?: return@flatMap emptyList()
playlist.epgUrlsOrXtreamXmlUrl()
}
.toSet()
.toList()
val producer = checkOrRefreshProgrammesOrThrowImpl(
epgUrls = epgUrls,
ignoreCache = ignoreCache
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ class SubscriptionWorker @AssistedInject constructor(
val ignoreCache = epgIgnoreCache
try {
programmeRepository.checkOrRefreshProgrammesOrThrow(
playlistUrl = playlistUrl,
playlistUrl,
ignoreCache = ignoreCache
)
.onEach { count ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ fun ForyouRoute(
val series: Stream? by viewModel.series.collectAsStateWithLifecycle()
val subscribingPlaylistUrls by
viewModel.subscribingPlaylistUrls.collectAsStateWithLifecycle()
val refreshingEpgUrls by viewModel.refreshingEpgUrls.collectAsStateWithLifecycle(emptyList())

if (isPageInfoVisible) {
LifecycleResumeEffect(title) {
Expand All @@ -105,6 +106,7 @@ fun ForyouRoute(
ForyouScreen(
playlistCountsResource = playlistCountsResource,
subscribingPlaylistUrls = subscribingPlaylistUrls,
refreshingEpgUrls = refreshingEpgUrls,
recommend = recommend,
rowCount = preferences.rowCount,
contentPadding = contentPadding,
Expand Down Expand Up @@ -169,6 +171,7 @@ private fun ForyouScreen(
rowCount: Int,
playlistCountsResource: Resource<List<PlaylistWithCount>>,
subscribingPlaylistUrls: List<String>,
refreshingEpgUrls: List<String>,
recommend: Recommend,
contentPadding: PaddingValues,
navigateToPlaylist: (Playlist) -> Unit,
Expand Down Expand Up @@ -214,6 +217,7 @@ private fun ForyouScreen(
rowCount = actualRowCount,
playlistCounts = playlistCountsResource.data,
subscribingPlaylistUrls = subscribingPlaylistUrls,
refreshingEpgUrls = refreshingEpgUrls,
onClick = navigateToPlaylist,
onLongClick = { mediaSheetValue = MediaSheetValue.ForyouScreen(it) },
header = header.takeIf { recommend.isNotEmpty() },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,13 @@ import com.m3u.data.database.model.PlaylistWithCount
import com.m3u.data.database.model.Stream
import com.m3u.data.parser.xtream.XtreamStreamInfo
import com.m3u.data.repository.playlist.PlaylistRepository
import com.m3u.data.repository.programme.ProgrammeRepository
import com.m3u.data.repository.stream.StreamRepository
import com.m3u.data.worker.SubscriptionWorker
import com.m3u.features.foryou.components.recommend.Recommend
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
Expand All @@ -45,6 +47,7 @@ import kotlin.time.toDuration
class ForyouViewModel @Inject constructor(
private val playlistRepository: PlaylistRepository,
streamRepository: StreamRepository,
programmeRepository: ProgrammeRepository,
preferences: Preferences,
@Dispatcher(IO) ioDispatcher: CoroutineDispatcher,
workManager: WorkManager,
Expand Down Expand Up @@ -81,6 +84,8 @@ class ForyouViewModel @Inject constructor(
started = SharingStarted.WhileSubscribed(5_000L)
)

internal val refreshingEpgUrls: Flow<List<String>> = programmeRepository.refreshingEpgUrls

private val unseensDuration = snapshotFlow { preferences.unseensMilliseconds }
.map { it.toDuration(DurationUnit.MILLISECONDS) }
.flowOn(ioDispatcher)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import androidx.compose.ui.unit.dp
import com.m3u.data.database.model.DataSource
import com.m3u.data.database.model.Playlist
import com.m3u.data.database.model.PlaylistWithCount
import com.m3u.data.database.model.epgUrlsOrXtreamXmlUrl
import com.m3u.data.database.model.fromLocal
import com.m3u.data.database.model.type
import com.m3u.i18n.R.string
Expand All @@ -27,6 +28,7 @@ internal fun PlaylistGallery(
rowCount: Int,
playlistCounts: List<PlaylistWithCount>,
subscribingPlaylistUrls: List<String>,
refreshingEpgUrls: List<String>,
onClick: (Playlist) -> Unit,
onLongClick: (Playlist) -> Unit,
modifier: Modifier = Modifier,
Expand All @@ -53,6 +55,9 @@ internal fun PlaylistGallery(
val playlist = playlistCount.playlist
val count = playlistCount.count
val subscribing = playlist.url in subscribingPlaylistUrls
val refreshing = playlist
.epgUrlsOrXtreamXmlUrl()
.any { it in refreshingEpgUrls }
PlaylistItem(
label = PlaylistGalleryDefaults.calculateUiTitle(
title = playlist.title,
Expand All @@ -66,7 +71,7 @@ internal fun PlaylistGallery(
}
},
count = count,
subscribing = subscribing,
subscribingOrRefreshing = subscribing || refreshing,
local = playlist.fromLocal,
onClick = { onClick(playlist) },
onLongClick = { onLongClick(playlist) },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ internal fun PlaylistItem(
type: String?,
count: Int,
local: Boolean,
subscribing: Boolean,
subscribingOrRefreshing: Boolean,
onClick: () -> Unit,
onLongClick: () -> Unit,
modifier: Modifier = Modifier
Expand All @@ -58,7 +58,7 @@ internal fun PlaylistItem(
type = type,
count = count,
local = local,
subscribing = subscribing,
subscribing = subscribingOrRefreshing,
onClick = onClick,
onLongClick = onLongClick,
modifier = modifier
Expand All @@ -68,7 +68,7 @@ internal fun PlaylistItem(
label = label,
type = type,
count = count,
subscribing = subscribing,
subscribing = subscribingOrRefreshing,
onClick = onClick,
onLongClick = onLongClick,
modifier = modifier
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,8 +179,8 @@ internal fun PlaylistRoute(
}
}

LaunchedEffect(preferences.autoRefresh, playlistUrl) {
if (playlistUrl.isNotEmpty() && preferences.autoRefresh) {
LaunchedEffect(preferences.autoRefreshChannels, playlistUrl) {
if (playlistUrl.isNotEmpty() && preferences.autoRefreshChannels) {
viewModel.refresh()
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ import androidx.compose.foundation.lazy.staggeredgrid.LazyStaggeredGridState
import androidx.compose.foundation.lazy.staggeredgrid.LazyVerticalStaggeredGrid
import androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridCells
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
Expand Down Expand Up @@ -58,9 +63,13 @@ internal fun SmartphoneStreamGallery(
items(streams?.itemCount ?: 0) { index ->
val stream = streams?.get(index)
if (stream != null) {
var programme: Programme? by remember { mutableStateOf(null) }
LaunchedEffect(stream.channelId) {
programme = getProgrammeCurrently(stream.channelId.orEmpty())
}
SmartphoneStreamItem(
stream = stream,
getProgrammeCurrently = { getProgrammeCurrently(stream.channelId.orEmpty()) },
programme = programme,
recently = recently,
zapping = zapping == stream,
isVodOrSeriesPlaylist = isVodOrSeriesPlaylist,
Expand Down
Loading

0 comments on commit 06aaf90

Please sign in to comment.