Skip to content

Commit

Permalink
finished video player on android and desktop
Browse files Browse the repository at this point in the history
  • Loading branch information
DatL4g committed Nov 21, 2023
1 parent 2051b2e commit f9eff37
Show file tree
Hide file tree
Showing 9 changed files with 99 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ actual fun VideoScreen(component: VideoComponent) {
val mediaItem = remember(streamList, streamIndex, sourceIndex) {
MediaItem.fromUri(streamList[streamIndex].list[sourceIndex])
}
val startingPos by component.startingPos.collectAsStateWithLifecycle()

val castState by remember(castContext) { mutableStateOf(castContext?.castState) }
val casting by remember(castState) { mutableStateOf(castState == CastState.CONNECTED || castState == CastState.CONNECTING) }
Expand Down Expand Up @@ -221,7 +222,7 @@ actual fun VideoScreen(component: VideoComponent) {
} else {
mediaItem
}
usingPlayer.setMediaItem(media)
usingPlayer.setMediaItem(media, startingPos)
usingPlayer.prepare()

withIOContext {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package dev.datlag.burningseries.common

fun Long.toDuration(): String {
val duration = this / 1000
val hours = duration / 3600
val minutes = (duration - hours * 3600) / 60
val seconds = duration - (hours * 3600 + minutes * 60)
return if (hours > 0) {
"%02d:%02d:%02d".format(hours.toInt(), minutes.toInt(), seconds.toInt())
} else {
"%02d:%02d".format(minutes.toInt(), seconds.toInt())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.withStyle
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
Expand All @@ -32,18 +33,24 @@ import dev.datlag.burningseries.common.*
import dev.datlag.burningseries.database.Episode
import dev.datlag.burningseries.model.Series
import dev.datlag.burningseries.ui.theme.TopLeftBottomRightRoundedShape
import dev.icerock.moko.resources.compose.stringResource
import io.github.aakira.napier.Napier
import kotlinx.coroutines.delay
import kotlin.math.roundToInt
import dev.datlag.burningseries.SharedRes

@Composable
fun EpisodeItem(content: Series.Episode, dbEpisode: Episode?, isLoading: Boolean, onClick: () -> Unit) {
val blurHash = remember(content.href) { BlurHash.random() }
val enabled = content.hosters.isNotEmpty()
val isFinished = remember(dbEpisode) {
val length = dbEpisode?.length ?: 0L
val progress = dbEpisode?.progress ?: 0L

val length = remember(dbEpisode) {
dbEpisode?.length ?: 0L
}
val progress = remember(dbEpisode) {
dbEpisode?.progress ?: 0L
}
val isFinished = remember(length, progress) {
if (length != 0L || progress != 0L) {
Napier.e("Length: $length")
Napier.e("Progress: $progress")
Expand Down Expand Up @@ -110,9 +117,24 @@ fun EpisodeItem(content: Series.Episode, dbEpisode: Episode?, isLoading: Boolean
CircularProgressIndicator()
}
}
Text(
text = content.episodeTitle,
maxLines = 3
)
Column(
modifier = Modifier.fillMaxHeight().weight(1F)
) {
Box(
modifier = Modifier.fillMaxWidth().weight(1F),
contentAlignment = Alignment.CenterStart
) {
Text(
text = content.episodeTitle,
maxLines = 3
)
}
if (length != 0L && progress != 0L) {
Text(
text = stringResource(SharedRes.strings.episode_progress, progress.toDuration(), length.toDuration()),
style = MaterialTheme.typography.labelSmall
)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ interface VideoComponent : Component {
val episode: StateFlow<Series.Episode>
val streams: List<Stream>

val startingPos: StateFlow<Long>

fun back()
fun ended()
fun lengthUpdate(millis: Long)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package dev.datlag.burningseries.ui.screen.video

import androidx.compose.runtime.Composable
import app.cash.sqldelight.coroutines.asFlow
import app.cash.sqldelight.coroutines.mapToOneOrNull
import com.arkivanov.decompose.ComponentContext
import com.arkivanov.essenty.backhandler.BackCallback
import dev.datlag.burningseries.common.ioScope
Expand All @@ -12,6 +14,7 @@ import dev.datlag.burningseries.model.Stream
import dev.datlag.burningseries.model.state.EpisodeState
import dev.datlag.burningseries.network.state.EpisodeStateMachine
import dev.datlag.burningseries.ui.theme.SchemeTheme
import kotlinx.coroutines.currentCoroutineContext
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.*
import org.kodein.di.DI
Expand All @@ -32,6 +35,15 @@ class VideoScreenComponent(
override val episode: StateFlow<Series.Episode> = episodeStateMachine.state.mapNotNull { it as? EpisodeState.EpisodeHolder }.map { it.episode }.stateIn(ioScope(), SharingStarted.WhileSubscribed(), initialEpisode)

private val database by di.instance<BurningSeries>()
private val dbEpisode = episode.transform {
return@transform emitAll(database.burningSeriesQueries.selectEpisodeByHref(it.href).asFlow().mapToOneOrNull(
currentCoroutineContext()
))
}.stateIn(ioScope(), SharingStarted.Lazily, database.burningSeriesQueries.selectEpisodeByHref(episode.value.href).executeAsOneOrNull())

override val startingPos: StateFlow<Long> = dbEpisode.transform {
return@transform emit(it?.progress ?: 0L)
}.stateIn(ioScope(), SharingStarted.Lazily, dbEpisode.value?.progress ?: 0L)

private val backPressCounter = MutableStateFlow(0)

Expand Down
2 changes: 2 additions & 0 deletions app/shared/src/commonMain/resources/MR/base/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,6 @@
<string name="pause">Pause</string>
<string name="play">Play</string>
<string name="fullscreen">Fullscreen</string>
<string name="exit_fullscreen">Exit Fullscreen</string>
<string name="episode_progress">%s - %s</string>
</resources>
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@ import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.WindowPlacement
import dev.datlag.burningseries.LocalWindow
import dev.datlag.burningseries.SharedRes
import dev.datlag.burningseries.common.toDuration
import dev.icerock.moko.resources.compose.stringResource

@Composable
Expand All @@ -29,7 +31,7 @@ fun VideoControls(
derivedStateOf { mediaPlayer.length.value }
}
val window = LocalWindow.current
val originalPlacement = remember(window) { window.placement }
var originalPlacement = remember(window) { window.placement }

Row(
modifier = Modifier.fillMaxWidth(),
Expand Down Expand Up @@ -83,6 +85,11 @@ fun VideoControls(
tint = Color.White
)
}
Text(
text = time.toDuration(),
textAlign = TextAlign.Center,
color = Color.White
)
Slider(
modifier = Modifier.weight(1F),
value = time.toDouble().toFloat(),
Expand All @@ -96,6 +103,11 @@ fun VideoControls(
inactiveTrackColor = Color.White.copy(alpha = 0.2F)
)
)
Text(
text = length.toDuration(),
textAlign = TextAlign.Center,
color = Color.White
)
if (window.placement == WindowPlacement.Fullscreen) {
IconButton(
onClick = {
Expand All @@ -111,6 +123,7 @@ fun VideoControls(
} else {
IconButton(
onClick = {
originalPlacement = window.placement
window.placement = WindowPlacement.Fullscreen
}
) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
package dev.datlag.burningseries.ui.screen.video

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.awt.SwingPanel
import androidx.compose.ui.graphics.Color
import dev.datlag.burningseries.common.lifecycle.collectAsStateWithLifecycle
import org.apache.commons.lang3.SystemUtils
import uk.co.caprica.vlcj.factory.discovery.NativeDiscovery
import uk.co.caprica.vlcj.player.base.MediaPlayer
Expand All @@ -25,7 +23,7 @@ fun VideoPlayer(
val foundVlc = NativeDiscovery().discover()

if (foundVlc) {
val mediaPlayer = remember {
val mediaPlayerComponent = remember {
if (SystemUtils.IS_OS_MAC) {
CallbackMediaPlayerComponent()
} else {
Expand All @@ -40,7 +38,7 @@ fun VideoPlayer(
background = Color.Black,
modifier = Modifier.fillMaxSize(),
factory = {
mediaPlayer
mediaPlayerComponent
}
)
}
Expand All @@ -53,6 +51,7 @@ fun VideoPlayer(
val headers = remember(streamIndex) {
streamList[streamIndex].headers
}
val startingPos by component.startingPos.collectAsStateWithLifecycle()

val isPlaying = remember { mutableStateOf(false) }
val length = remember { mutableLongStateOf(0) }
Expand Down Expand Up @@ -100,46 +99,52 @@ fun VideoPlayer(

isPlaying.value = false
}

override fun opening(mediaPlayer: MediaPlayer?) {
super.opening(mediaPlayer)

(mediaPlayer ?: mediaPlayerComponent.mediaPlayer())?.controls()?.setTime(startingPos)
}
} }

LaunchedEffect(mediaPlayer, eventListener) {
mediaPlayer.mediaPlayer()?.events()?.addMediaPlayerEventListener(eventListener)
LaunchedEffect(mediaPlayerComponent, eventListener) {
mediaPlayerComponent.mediaPlayer()?.events()?.addMediaPlayerEventListener(eventListener)
}

SideEffect {
applyHeaders(headers, mediaPlayer.mediaPlayer())
mediaPlayer.mediaPlayer()?.media()?.play(url)
applyHeaders(headers, mediaPlayerComponent.mediaPlayer())
mediaPlayerComponent.mediaPlayer()?.media()?.play(url)
}

DisposableEffect(mediaPlayer) {
DisposableEffect(mediaPlayerComponent) {
onDispose {
mediaPlayer.mediaPlayer()?.release()
mediaPlayerComponent.mediaPlayer()?.release()
}
}

return remember(mediaPlayer) { object : dev.datlag.burningseries.ui.screen.video.MediaPlayer {
return remember(mediaPlayerComponent) { object : dev.datlag.burningseries.ui.screen.video.MediaPlayer {
override val isPlaying: MutableState<Boolean> = isPlaying
override val length: MutableLongState = length
override val time: MutableLongState = time

override fun play() {
mediaPlayer.mediaPlayer()?.controls()?.play()
mediaPlayerComponent.mediaPlayer()?.controls()?.play()
}

override fun pause() {
mediaPlayer.mediaPlayer()?.controls()?.pause()
mediaPlayerComponent.mediaPlayer()?.controls()?.pause()
}

override fun rewind() {
mediaPlayer.mediaPlayer()?.controls()?.skipTime(-10000)
mediaPlayerComponent.mediaPlayer()?.controls()?.skipTime(-10000)
}

override fun forward() {
mediaPlayer.mediaPlayer()?.controls()?.skipTime(10000)
mediaPlayerComponent.mediaPlayer()?.controls()?.skipTime(10000)
}

override fun seekTo(millis: Long) {
mediaPlayer.mediaPlayer()?.controls()?.setTime(millis)
mediaPlayerComponent.mediaPlayer()?.controls()?.setTime(millis)
}
} }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,5 +49,8 @@ updateEpisodeProgress {
INSERT OR IGNORE INTO Episode (href, number, title, length, progress, seriesHref) VALUES (:href, :number, :title, :length, :progress, :seriesHref);
}

selectEpisodeByHref:
SELECT * FROM Episode WHERE href = :href OR href LIKE :href;

selectEpisodesBySeriesHref:
SELECT * FROM Episode WHERE seriesHref = :href OR seriesHref LIKE :href;

0 comments on commit f9eff37

Please sign in to comment.