Skip to content

Commit

Permalink
[Podcast feed update] - Call API to refresh podcast (#3510)
Browse files Browse the repository at this point in the history
Co-authored-by: Philip Simpson <[email protected]>
  • Loading branch information
mebarbosa and geekygecko authored Jan 30, 2025
1 parent f456e70 commit 15a0d06
Show file tree
Hide file tree
Showing 10 changed files with 66 additions and 46 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -926,21 +926,20 @@ class PodcastFragment : BaseFragment(), Toolbar.OnMenuItemClickListener {
viewLifecycleOwner.lifecycleScope.launch {
viewModel.refreshState.collect { state ->
when (state) {
PodcastViewModel.RefreshState.Error -> {}
PodcastViewModel.RefreshState.NotStarted -> {}
PodcastViewModel.RefreshState.NewEpisodeFound -> {
binding?.swipeRefreshLayout?.isRefreshing = false
(activity as? FragmentHostListener)?.snackBarView()?.let { snackBarView ->
Snackbar.make(snackBarView, getString(LR.string.podcast_refresh_new_episode_found), Snackbar.LENGTH_LONG).show()
}
showSnackBar(getString(LR.string.podcast_refresh_new_episode_found))
}
PodcastViewModel.RefreshState.NoEpisodesFound -> {
binding?.swipeRefreshLayout?.isRefreshing = false
showSnackBar(getString(LR.string.podcast_refresh_no_episodes_found))
}
is PodcastViewModel.RefreshState.Refreshing -> {
if (state.type == PodcastViewModel.RefreshType.PULL_TO_REFRESH) {
binding?.swipeRefreshLayout?.isRefreshing = true
} else {
(activity as? FragmentHostListener)?.snackBarView()?.let { snackBarView ->
Snackbar.make(snackBarView, getString(LR.string.podcast_refreshing_episode_list), Snackbar.LENGTH_INDEFINITE).show()
}
showSnackBar(getString(LR.string.podcast_refreshing_episode_list), Snackbar.LENGTH_INDEFINITE)
}
}
}
Expand Down Expand Up @@ -1026,9 +1025,7 @@ class PodcastFragment : BaseFragment(), Toolbar.OnMenuItemClickListener {
analyticsTracker.track(AnalyticsEvent.PODCAST_SCREEN_SHARE_TAPPED)

if (!podcast.canShare) {
(activity as? FragmentHostListener)?.snackBarView()?.let { snackBarView ->
Snackbar.make(snackBarView, getString(LR.string.sharing_is_not_available_for_private_podcasts), Snackbar.LENGTH_LONG).show()
}
showSnackBar(getString(LR.string.sharing_is_not_available_for_private_podcasts))
return
}

Expand All @@ -1054,6 +1051,12 @@ class PodcastFragment : BaseFragment(), Toolbar.OnMenuItemClickListener {
dialog?.show(parentFragmentManager, "download_confirm")
}

private fun showSnackBar(message: String, duration: Int = Snackbar.LENGTH_LONG) {
(activity as? FragmentHostListener)?.snackBarView()?.let { snackBarView ->
Snackbar.make(snackBarView, message, duration).show()
}
}

override fun onBackPressed() = if (viewModel.multiSelectEpisodesHelper.isMultiSelecting) {
viewModel.multiSelectEpisodesHelper.isMultiSelecting = false
true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,9 @@ class PodcastSettingsFragment : BasePreferenceFragment(), FilterSelectFragment.L
lifecycleScope.launch {
analyticsTracker.track(AnalyticsEvent.PODCAST_SETTINGS_FEED_ERROR_UPDATE_TAPPED)
podcastManager.updateRefreshAvailable(podcastUuid = podcastUuid, refreshAvailable = false)
val success = podcastManager.refreshPodcastFeed(podcastUuid = podcastUuid)
val success = podcastManager.findPodcastByUuid(podcastUuid)?.let { podcast ->
podcastManager.refreshPodcastFeed(podcast = podcast)
} ?: false
analyticsTracker.track(
if (success) {
AnalyticsEvent.PODCAST_SETTINGS_FEED_ERROR_FIX_SUCCEEDED
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,10 @@ import io.reactivex.schedulers.Schedulers
import javax.inject.Inject
import kotlin.coroutines.CoroutineContext
import kotlin.math.min
import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.flatMapLatest
Expand Down Expand Up @@ -582,10 +580,10 @@ class PodcastViewModel

analyticsTracker.track(AnalyticsEvent.PODCAST_SCREEN_REFRESH_EPISODE_LIST, mapOf("podcast_uuid" to podcast.uuid, "action" to refreshType.analyticsValue))

launch {
viewModelScope.launch {
_refreshState.emit(RefreshState.Refreshing(refreshType))
delay(2.seconds)
_refreshState.emit(RefreshState.NewEpisodeFound)
val newEpisodeFound = podcastManager.refreshPodcastFeed(podcast = podcast)
_refreshState.emit(if (newEpisodeFound) RefreshState.NewEpisodeFound else RefreshState.NoEpisodesFound)
}
}

Expand Down Expand Up @@ -629,7 +627,7 @@ class PodcastViewModel
data object NotStarted : RefreshState()
data class Refreshing(val type: RefreshType) : RefreshState()
data object NewEpisodeFound : RefreshState()
data object Error : RefreshState()
data object NoEpisodesFound : RefreshState()
}

enum class RefreshType(val analyticsValue: String) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -629,6 +629,7 @@
<string name="podcast_refresh_episodes">Refresh episode list</string>
<string name="podcast_refreshing_episode_list">Refreshing episode list…</string>
<string name="podcast_refresh_new_episode_found">New episode found!</string>
<string name="podcast_refresh_no_episodes_found">No episodes found.</string>
<string name="podcast_sort_episodes">Sort episodes</string>
<string name="podcast_subscribed">Following</string>
<string name="podcast_not_subscribed">Not Following</string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ interface PodcastManager {
fun autoAddToUpNextPodcastsRxFlowable(): Flowable<List<Podcast>>
suspend fun findAutoAddToUpNextPodcasts(): List<Podcast>

suspend fun refreshPodcastFeed(podcastUuid: String): Boolean
suspend fun refreshPodcastFeed(podcast: Podcast): Boolean

suspend fun findRandomPodcasts(limit: Int): List<Podcast>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import javax.inject.Inject
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.emitAll
import kotlinx.coroutines.flow.flow
Expand All @@ -64,6 +65,7 @@ class PodcastManagerImpl @Inject constructor(

companion object {
private const val FIVE_MINUTES_IN_MILLIS = (5 * 60 * 1000).toLong()
private const val TAG = "PodcastManager"
}

override val coroutineContext: CoroutineContext
Expand Down Expand Up @@ -627,8 +629,31 @@ class PodcastManagerImpl @Inject constructor(
return podcastDao.findAutoAddToUpNextPodcasts()
}

override suspend fun refreshPodcastFeed(podcastUuid: String): Boolean {
return refreshServiceManager.refreshPodcastFeed(podcastUuid).isSuccessful
override suspend fun refreshPodcastFeed(podcast: Podcast): Boolean {
var response = refreshServiceManager.updatePodcast(
podcastUuid = podcast.uuid,
lastEpisodeUuid = podcast.latestEpisodeUuid,
)
LogBuffer.i(TAG, "Refresh podcast feed: ${response.code()}")

while (response.code() == 202) {
val location = response.headers()["Location"]
val retryAfter = response.headers()["Retry-After"]?.toIntOrNull()
if (location != null && retryAfter != null) {
delay(retryAfter * 1000L)
response = refreshServiceManager.pollUpdatePodcast(location)
LogBuffer.i(TAG, "Refresh podcast feed poll: ${response.code()}")
} else {
return false
}
}

if (response.code() == 200) {
refreshPodcasts("Refresh podcast feed")
return true
} else {
return false
}
}

override suspend fun findRandomPodcasts(limit: Int): List<Podcast> {
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,19 @@ package au.com.shiftyjelly.pocketcasts.servers.refresh

import retrofit2.Response
import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.POST
import retrofit2.http.Query
import retrofit2.http.Url

interface RefreshService {

@POST("import/opml")
suspend fun importOpml(@Body request: ImportOpmlRequest): Response<StatusResponse<ImportOpmlResponse>>

@POST("podcasts/refresh")
suspend fun refreshPodcastFeed(@Body request: RefreshPodcastFeedRequest): Response<StatusResponse<BasicResponse>>
@GET("api/v1/update_podcast")
suspend fun updatePodcast(@Query("podcast_uuid") podcastUuid: String, @Query("last_episode_uuid") lastEpisodeUuid: String?): Response<Unit>

@GET
suspend fun pollUpdatePodcast(@Url url: String): Response<Unit>
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,7 @@ interface RefreshServiceManager {

suspend fun pollImportOpml(pollUuids: List<String>): Response<StatusResponse<ImportOpmlResponse>>

suspend fun refreshPodcastFeed(podcastUuid: String): Response<StatusResponse<BasicResponse>>
suspend fun updatePodcast(podcastUuid: String, lastEpisodeUuid: String?): Response<Unit>

suspend fun pollUpdatePodcast(url: String): Response<Unit>
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,12 @@ class RefreshServiceManagerImpl @Inject constructor(
return service.importOpml(request)
}

override suspend fun refreshPodcastFeed(podcastUuid: String): Response<StatusResponse<BasicResponse>> {
val request = RefreshPodcastFeedRequest(podcastUuid = podcastUuid)
addDeviceParameters(request)
return service.refreshPodcastFeed(request)
override suspend fun updatePodcast(podcastUuid: String, lastEpisodeUuid: String?): Response<Unit> {
return service.updatePodcast(podcastUuid = podcastUuid, lastEpisodeUuid = lastEpisodeUuid)
}

override suspend fun pollUpdatePodcast(url: String): Response<Unit> {
return service.pollUpdatePodcast(url)
}

private fun addDeviceParameters(request: BaseRequest) {
Expand Down

0 comments on commit 15a0d06

Please sign in to comment.