Skip to content

Commit

Permalink
Remove playlist preview dependency on external HTTP calls
Browse files Browse the repository at this point in the history
  • Loading branch information
Isira-Seneviratne committed Jul 28, 2024
1 parent 60f9459 commit 0f50b8b
Show file tree
Hide file tree
Showing 7 changed files with 92 additions and 52 deletions.
76 changes: 43 additions & 33 deletions app/src/main/java/org/schabi/newpipe/compose/playlist/Playlist.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,62 +10,72 @@ import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.tooling.preview.Preview
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.paging.PagingData
import androidx.paging.compose.collectAsLazyPagingItems
import org.schabi.newpipe.DownloaderImpl
import org.schabi.newpipe.compose.status.LoadingIndicator
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
import org.schabi.newpipe.compose.common.LoadingIndicator
import org.schabi.newpipe.compose.stream.StreamInfoItem
import org.schabi.newpipe.compose.stream.StreamList
import org.schabi.newpipe.compose.theme.AppTheme
import org.schabi.newpipe.extractor.NewPipe
import org.schabi.newpipe.extractor.ServiceList
import org.schabi.newpipe.util.KEY_SERVICE_ID
import org.schabi.newpipe.util.KEY_URL
import org.schabi.newpipe.extractor.stream.Description
import org.schabi.newpipe.extractor.stream.StreamInfoItem
import org.schabi.newpipe.extractor.stream.StreamType
import org.schabi.newpipe.viewmodels.PlaylistViewModel

@Composable
fun Playlist(playlistViewModel: PlaylistViewModel = viewModel()) {
Surface(color = MaterialTheme.colorScheme.background) {
val playlistInfo by playlistViewModel.playlistInfo.collectAsState()
Playlist(playlistInfo, playlistViewModel.streamItems)
}
}

playlistInfo?.let {
val streams = playlistViewModel.streamItems.collectAsLazyPagingItems()
val totalDuration by remember {
derivedStateOf {
streams.itemSnapshotList.sumOf { it!!.duration }
}
@Composable
private fun Playlist(
playlistInfo: PlaylistInfo?,
streamFlow: Flow<PagingData<StreamInfoItem>>
) {
playlistInfo?.let {
val streams = streamFlow.collectAsLazyPagingItems()
val totalDuration by remember {
derivedStateOf {
streams.itemSnapshotList.sumOf { it!!.duration }
}
}

StreamList(
streams = streams,
gridHeader = {
item(span = { GridItemSpan(maxLineSpan) }) {
PlaylistHeader(it, totalDuration)
}
},
listHeader = {
item {
PlaylistHeader(it, totalDuration)
}
StreamList(
streams = streams,
gridHeader = {
item(span = { GridItemSpan(maxLineSpan) }) {
PlaylistHeader(it, totalDuration)
}
)
} ?: LoadingIndicator()
}
},
listHeader = {
item {
PlaylistHeader(it, totalDuration)
}
}
)
} ?: LoadingIndicator()
}

@Preview(name = "Light mode", uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(name = "Dark mode", uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun PlaylistPreview() {
NewPipe.init(DownloaderImpl.init(null))

val params = mapOf(
KEY_SERVICE_ID to ServiceList.YouTube.serviceId,
KEY_URL to "https://www.youtube.com/playlist?list=PLAIcZs9N4171hRrG_4v32Ca2hLvSuQ6QI"
val description = Description("Example description", Description.PLAIN_TEXT)
val playlistInfo = PlaylistInfo(
"", 1, "", "Example playlist", description, listOf(), 1L,
null, "Uploader", listOf(), null
)
val stream = StreamInfoItem(streamType = StreamType.VIDEO_STREAM)
val streamFlow = flowOf(PagingData.from(listOf(stream)))

AppTheme {
Surface(color = MaterialTheme.colorScheme.background) {
Playlist(PlaylistViewModel(SavedStateHandle(params)))
Playlist(playlistInfo, streamFlow)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,11 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.fragment.app.FragmentActivity
import coil.compose.AsyncImage
import org.schabi.newpipe.DownloaderImpl
import org.schabi.newpipe.R
import org.schabi.newpipe.compose.common.DescriptionText
import org.schabi.newpipe.compose.theme.AppTheme
import org.schabi.newpipe.error.ErrorUtil
import org.schabi.newpipe.extractor.NewPipe
import org.schabi.newpipe.extractor.ServiceList
import org.schabi.newpipe.extractor.playlist.PlaylistInfo
import org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper
import org.schabi.newpipe.extractor.stream.Description
import org.schabi.newpipe.util.Localization
Expand All @@ -67,7 +64,7 @@ fun PlaylistHeader(playlistInfo: PlaylistInfo, totalDuration: Long) {
NavigationHelper.openChannelFragment(
(context as FragmentActivity).supportFragmentManager,
playlistInfo.serviceId, playlistInfo.uploaderUrl,
playlistInfo.uploaderName
playlistInfo.uploaderName!!
)
} catch (e: Exception) {
ErrorUtil.showUiErrorSnackbar(context, "Opening channel fragment", e)
Expand Down Expand Up @@ -108,14 +105,13 @@ fun PlaylistHeader(playlistInfo: PlaylistInfo, totalDuration: Long) {
Text(text = "$count$formattedDuration", style = MaterialTheme.typography.bodySmall)
}

val description = playlistInfo.description ?: Description.EMPTY_DESCRIPTION
if (description != Description.EMPTY_DESCRIPTION) {
if (playlistInfo.description != Description.EMPTY_DESCRIPTION) {
var isExpanded by rememberSaveable { mutableStateOf(false) }
var isExpandable by rememberSaveable { mutableStateOf(false) }

DescriptionText(
modifier = Modifier.animateContentSize(),
description = description,
description = playlistInfo.description,
maxLines = if (isExpanded) Int.MAX_VALUE else 5,
style = MaterialTheme.typography.bodyMedium,
overflow = TextOverflow.Ellipsis,
Expand Down Expand Up @@ -144,10 +140,10 @@ fun PlaylistHeader(playlistInfo: PlaylistInfo, totalDuration: Long) {
@Preview(name = "Dark mode", uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun PlaylistHeaderPreview() {
NewPipe.init(DownloaderImpl.init(null))
val playlistInfo = PlaylistInfo.getInfo(
ServiceList.YouTube,
"https://www.youtube.com/playlist?list=PLAIcZs9N4171hRrG_4v32Ca2hLvSuQ6QI"
val description = Description("Example description", Description.PLAIN_TEXT)
val playlistInfo = PlaylistInfo(
"", 1, "", "Example playlist", description, listOf(), 1L,
null, "Uploader", listOf(), null
)

AppTheme {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.schabi.newpipe.compose.playlist

import androidx.compose.runtime.Immutable
import org.schabi.newpipe.extractor.Image
import org.schabi.newpipe.extractor.Page
import org.schabi.newpipe.extractor.stream.Description
import org.schabi.newpipe.extractor.stream.StreamInfoItem

@Immutable
class PlaylistInfo(
val id: String,
val serviceId: Int,
val url: String,
val name: String,
val description: Description,
val relatedItems: List<StreamInfoItem>,
val streamCount: Long,
val uploaderUrl: String?,
val uploaderName: String?,
val uploaderAvatars: List<Image>,
val nextPage: Page?
)
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,10 @@ import org.schabi.newpipe.util.NavigationHelper
@Composable
fun StreamList(
streams: LazyPagingItems<StreamInfoItem>,
itemViewMode: ItemViewMode = determineItemViewMode(),
gridHeader: LazyGridScope.() -> Unit = {},
listHeader: LazyListScope.() -> Unit = {}
) {
val mode = determineItemViewMode()
val context = LocalContext.current
val onClick = remember {
{ stream: StreamInfoItem ->
Expand All @@ -54,7 +54,7 @@ fun StreamList(
}
}

if (mode == ItemViewMode.GRID) {
if (itemViewMode == ItemViewMode.GRID) {
val gridState = rememberLazyGridState()

LazyVerticalGridScrollbar(state = gridState) {
Expand Down Expand Up @@ -82,7 +82,7 @@ fun StreamList(
val stream = streams[it]!!
val isSelected = selectedStream == stream

if (mode == ItemViewMode.CARD) {
if (itemViewMode == ItemViewMode.CARD) {
StreamCardItem(stream, isSelected, onClick, onLongClick, onDismissPopup)
} else {
StreamListItem(stream, isSelected, onClick, onLongClick, onDismissPopup)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ import androidx.paging.PagingSource
import androidx.paging.PagingState
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.schabi.newpipe.compose.playlist.PlaylistInfo
import org.schabi.newpipe.extractor.NewPipe
import org.schabi.newpipe.extractor.Page
import org.schabi.newpipe.extractor.playlist.PlaylistInfo
import org.schabi.newpipe.extractor.stream.StreamInfoItem
import org.schabi.newpipe.extractor.playlist.PlaylistInfo as ExtractorPlaylistInfo

class PlaylistItemsSource(
private val playlistInfo: PlaylistInfo,
Expand All @@ -17,7 +18,8 @@ class PlaylistItemsSource(
override suspend fun load(params: LoadParams<Page>): LoadResult<Page, StreamInfoItem> {
return params.key?.let {
withContext(Dispatchers.IO) {
val response = PlaylistInfo.getMoreItems(service, playlistInfo.url, playlistInfo.nextPage)
val response = ExtractorPlaylistInfo
.getMoreItems(service, playlistInfo.url, playlistInfo.nextPage)
LoadResult.Page(response.items, null, response.nextPage)
}
} ?: LoadResult.Page(playlistInfo.relatedItems, null, playlistInfo.nextPage)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ import androidx.compose.ui.Modifier
@Composable
fun LoadingIndicator(modifier: Modifier = Modifier) {
CircularProgressIndicator(
modifier = modifier.fillMaxSize().wrapContentSize(Alignment.Center),
modifier = modifier
.fillMaxSize()
.wrapContentSize(Alignment.Center),
color = MaterialTheme.colorScheme.primary,
trackColor = MaterialTheme.colorScheme.surfaceVariant,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,27 @@ import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.stateIn
import org.schabi.newpipe.compose.playlist.PlaylistInfo
import org.schabi.newpipe.extractor.NewPipe
import org.schabi.newpipe.extractor.playlist.PlaylistInfo
import org.schabi.newpipe.extractor.stream.Description
import org.schabi.newpipe.paging.PlaylistItemsSource
import org.schabi.newpipe.util.KEY_SERVICE_ID
import org.schabi.newpipe.util.KEY_URL
import org.schabi.newpipe.util.NO_SERVICE_ID
import org.schabi.newpipe.extractor.playlist.PlaylistInfo as ExtractorPlaylistInfo

class PlaylistViewModel(savedStateHandle: SavedStateHandle) : ViewModel() {
private val serviceIdState = savedStateHandle.getStateFlow(KEY_SERVICE_ID, NO_SERVICE_ID)
private val urlState = savedStateHandle.getStateFlow(KEY_URL, "")

val playlistInfo = serviceIdState.combine(urlState) { id, url ->
PlaylistInfo.getInfo(NewPipe.getService(id), url)
val info = ExtractorPlaylistInfo.getInfo(NewPipe.getService(id), url)
val description = info.description ?: Description.EMPTY_DESCRIPTION
PlaylistInfo(
info.id, info.serviceId, info.url, info.name, description, info.relatedItems,
info.streamCount, info.uploaderUrl, info.uploaderName, info.uploaderAvatars,
info.nextPage
)
}
.flowOn(Dispatchers.IO)
.stateIn(viewModelScope, SharingStarted.Eagerly, null)
Expand Down

0 comments on commit 0f50b8b

Please sign in to comment.