add chapter select preview

This commit is contained in:
Christian Schabesberger 2024-08-30 12:13:10 +02:00
parent cb122306c6
commit 22d7bcf552
6 changed files with 109 additions and 50 deletions

View File

@ -24,7 +24,7 @@ import android.net.Uri
import androidx.media3.common.PlaybackException
import net.newpipe.newplayer.utils.Thumbnail
data class Chapter(val chapterStartInMs: Long, val chapterTitle: String?)
data class Chapter(val chapterStartInMs: Long, val chapterTitle: String?, val thumbnail: Thumbnail?)
data class MetaInfo(
val title: String,
@ -46,7 +46,6 @@ interface MediaRepository {
suspend fun getPreviewThumbnails(item: String): HashMap<Long, Thumbnail>?
suspend fun getChapters(item: String): List<Chapter>
suspend fun getChapterThumbnail(item: String, chapter: Long): Thumbnail?
suspend fun getTimestampLink(item: String, timestampInSeconds: Long): String

View File

@ -24,6 +24,7 @@ import android.os.Bundle
import androidx.media3.common.Player
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.StateFlow
import net.newpipe.newplayer.Chapter
import net.newpipe.newplayer.NewPlayer
import net.newpipe.newplayer.ui.ContentScale
import net.newpipe.newplayer.utils.Thumbnail
@ -58,4 +59,6 @@ interface VideoPlayerViewModel {
fun volumeChange(changeRate: Float)
fun openStreamSelection(selectChapter: Boolean, embeddedUiConfig: EmbeddedUiConfig)
fun closeStreamSelection()
fun chapterSelected(chapter: Chapter)
fun streamSelected(streamId: Int)
}

View File

@ -43,6 +43,7 @@ import javax.inject.Inject
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import net.newpipe.newplayer.Chapter
import net.newpipe.newplayer.utils.VideoSize
import net.newpipe.newplayer.NewPlayer
import net.newpipe.newplayer.playerInternals.getPlaylistItemsFromItemList
@ -399,6 +400,14 @@ class VideoPlayerViewModelImpl @Inject constructor(
updateUiMode(UIModeState.FULLSCREEN_VIDEO)
}
override fun chapterSelected(chapter: Chapter) {
println("gurken chapter selectd: $chapter")
}
override fun streamSelected(streamId: Int) {
println("stream selected: $streamId")
}
private fun updateUiMode(newState: UIModeState) {
val newPlayMode = newState.toPlayMode()
val currentPlayMode = mutableUiState.value.uiMode.toPlayMode()

View File

@ -6,6 +6,7 @@ import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.asSharedFlow
import net.newpipe.newplayer.Chapter
import net.newpipe.newplayer.NewPlayer
import net.newpipe.newplayer.ui.ContentScale
@ -83,6 +84,14 @@ open class VideoPlayerViewModelDummy : VideoPlayerViewModel {
println("dummy impl")
}
override fun chapterSelected(chapter: Chapter) {
println("dummp impl chapter selected: $chapter")
}
override fun streamSelected(streamId: Int) {
println("dummy impl stream selected: $streamId")
}
override fun pause() {
println("dummy pause")
}

View File

@ -21,29 +21,32 @@
package net.newpipe.newplayer.ui.videoplayer
import android.view.MotionEvent
import android.view.Surface
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.filled.DragHandle
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults.topAppBarColors
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
@ -65,6 +68,7 @@ import net.newpipe.newplayer.ui.theme.VideoPlayerTheme
import net.newpipe.newplayer.utils.getLocale
import net.newpipe.newplayer.utils.getTimeStringFromMs
import coil.compose.AsyncImage
import net.newpipe.newplayer.Chapter
import net.newpipe.newplayer.utils.BitmapThumbnail
import net.newpipe.newplayer.utils.OnlineThumbnail
import net.newpipe.newplayer.utils.Thumbnail
@ -79,12 +83,14 @@ fun StreamSelectUI(
) {
val insets = getInsets()
Surface(
modifier = Modifier
.fillMaxSize()
.windowInsetsPadding(insets),
modifier = Modifier.fillMaxSize(),
color = CONTROLLER_UI_BACKGROUND_COLOR
) {
Scaffold(
modifier = Modifier
.fillMaxSize()
.windowInsetsPadding(insets),
containerColor = Color.Transparent,
topBar = {
if (isChapterSelect) {
ChapterSelectTopBar(onClose = {
@ -95,37 +101,50 @@ fun StreamSelectUI(
}
}
) { innerPadding ->
Surface(
LazyColumn(
modifier = Modifier
.padding(innerPadding)
.fillMaxSize(), color = Color.Red
.fillMaxSize(),
) {
if (isChapterSelect) {
items(uiState.chapters.size) { chapterIndex ->
val chapter = uiState.chapters[chapterIndex]
ChapterItem(
id = chapterIndex,
chapterTitle = chapter.chapterTitle ?: "",
chapterStartInMs = chapter.chapterStartInMs,
thumbnail = chapter.thumbnail,
onClicked = {
viewModel.chapterSelected(chapter)
}
)
}
} else {
}
}
}
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun ChapterSelectTopBar(modifier: Modifier = Modifier, onClose: () -> Unit) {
Row(
modifier = modifier,
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween
) {
Spacer(modifier = Modifier.width(20.dp))
//Text(stringResource(R.string.chapter))
Text("Chapter TODO")
Spacer(modifier = Modifier.weight(1F))
IconButton(
onClick = onClose
) {
Icon(
imageVector = Icons.Filled.Close,
contentDescription = stringResource(R.string.close_chapter_selection)
)
}
}
TopAppBar(modifier = modifier,
colors = topAppBarColors(containerColor = Color.Transparent),
title = {
Text("Chapter TODO")
//Text(stringResource(R.string.chapter))
}, actions = {
IconButton(
onClick = onClose
) {
Icon(
imageVector = Icons.Filled.Close,
contentDescription = stringResource(R.string.close_chapter_selection)
)
}
})
}
@Composable
@ -150,7 +169,7 @@ private fun ChapterItem(
top = 4.dp,
bottom = 4.dp,
end = 4.dp
)
).height(80.dp)
.clickable { onClicked(id) }
) {
val contentDescription = stringResource(R.string.chapter)
@ -348,11 +367,29 @@ fun ChapterTopBarPreview() {
@Composable
fun VideoPlayerStreamSelectUIPreview() {
VideoPlayerTheme {
Surface(modifier = Modifier.fillMaxSize(), color = Color.Green) {
Surface(modifier = Modifier.fillMaxSize(), color = Color.Red) {
StreamSelectUI(
isChapterSelect = true,
viewModel = VideoPlayerViewModelDummy(),
uiState = VideoPlayerUIState.DEFAULT
uiState = VideoPlayerUIState.DEFAULT.copy(
chapters = arrayListOf(
Chapter(
chapterStartInMs = 5000,
chapterTitle = "First Chapter",
thumbnail = null
),
Chapter(
chapterStartInMs = 10000,
chapterTitle = "Second Chapter",
thumbnail = null
),
Chapter(
chapterStartInMs = 20000,
chapterTitle = "Third Chapter",
thumbnail = null
),
)
)
)
}
}

View File

@ -30,12 +30,14 @@ class TestMediaRepository(val context: Context) : MediaRepository {
thumbnail = OnlineThumbnail(context.getString(R.string.ccc_6502_thumbnail)),
lengthInS = context.resources.getInteger(R.integer.ccc_6502_length)
)
"imu" -> MetaInfo(
title = context.getString(R.string.ccc_imu_title),
channelName = context.getString(R.string.ccc_imu_channel),
thumbnail = OnlineThumbnail(context.getString(R.string.ccc_imu_thumbnail)),
lengthInS = context.resources.getInteger(R.integer.ccc_imu_length)
)
"portrait" -> MetaInfo(
title = context.getString(R.string.portrait_title),
channelName = context.getString(R.string.portrait_channel),
@ -113,29 +115,29 @@ class TestMediaRepository(val context: Context) : MediaRepository {
override suspend fun getChapters(item: String) =
when (item) {
"6502" -> context.resources.getIntArray(R.array.ccc_6502_chapters)
"imu" -> TODO()
"imu" -> context.resources.getIntArray(R.array.ccc_imu_chapters)
else -> intArrayOf()
}.map {
Chapter(it.toLong(), "Dummy Chapter at timestamp $it")
}
Chapter(
it.toLong(), chapterTitle = "Dummy Chapter at timestamp $it",
thumbnail = when (item) {
"6502" -> OnlineThumbnail(
String.format(
context.getString(R.string.ccc_6502_preview_thumbnails),
it / (10 * 1000)
)
)
override suspend fun getChapterThumbnail(item: String, chapter: Long) =
when (item) {
"6502" -> OnlineThumbnail(
String.format(
context.getString(R.string.ccc_6502_preview_thumbnails),
chapter / (10 * 1000)
)
"imu" -> OnlineThumbnail(
String.format(
context.getString(R.string.ccc_imu_preview_thumbnails),
it / (10 * 1000)
)
)
else -> null
}
)
"imu" -> OnlineThumbnail(
String.format(
context.getString(R.string.ccc_imu_preview_thumbnails),
chapter / (10 * 1000)
)
)
else -> null
}
override suspend fun getTimestampLink(item: String, timestampInSeconds: Long) =