1
0
mirror of https://github.com/TeamNewPipe/NewPipe synced 2025-01-09 17:00:32 +00:00

Remove playlist preview dependency on external HTTP calls

This commit is contained in:
Isira Seneviratne 2024-07-22 08:11:16 +05:30
parent b9556a1331
commit 82e5b6b1e9
7 changed files with 93 additions and 53 deletions

View File

@ -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)
}
},
listHeader = {
item {
PlaylistHeader(it, totalDuration)
}
}
)
} ?: LoadingIndicator()
}
)
} ?: 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)
}
}
}

View File

@ -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
@ -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)
@ -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,
@ -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 {

View File

@ -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?
)

View File

@ -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 ->
@ -54,7 +54,7 @@ fun StreamList(
}
}
if (mode == ItemViewMode.GRID) {
if (itemViewMode == ItemViewMode.GRID) {
val gridState = rememberLazyGridState()
LazyVerticalGridScrollbar(state = gridState) {
@ -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)

View File

@ -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,
@ -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)

View File

@ -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,
)

View File

@ -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)