mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-08 23:51:21 +02:00
More options for movies
This commit is contained in:
parent
4d603d9d41
commit
4f3dbcac6e
3 changed files with 92 additions and 10 deletions
|
|
@ -9,6 +9,7 @@ import org.jellyfin.sdk.api.client.extensions.imageApi
|
||||||
import org.jellyfin.sdk.model.api.BaseItemDto
|
import org.jellyfin.sdk.model.api.BaseItemDto
|
||||||
import org.jellyfin.sdk.model.api.BaseItemKind
|
import org.jellyfin.sdk.model.api.BaseItemKind
|
||||||
import org.jellyfin.sdk.model.api.ImageType
|
import org.jellyfin.sdk.model.api.ImageType
|
||||||
|
import org.jellyfin.sdk.model.extensions.ticks
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class BaseItem(
|
data class BaseItem(
|
||||||
|
|
@ -25,6 +26,13 @@ data class BaseItem(
|
||||||
@Transient
|
@Transient
|
||||||
val indexNumber = data.indexNumber
|
val indexNumber = data.indexNumber
|
||||||
|
|
||||||
|
@Transient
|
||||||
|
val resumeMs =
|
||||||
|
data.userData
|
||||||
|
?.playbackPositionTicks
|
||||||
|
?.ticks
|
||||||
|
?.inWholeMilliseconds
|
||||||
|
|
||||||
fun destination(): Destination {
|
fun destination(): Destination {
|
||||||
val result =
|
val result =
|
||||||
// Redirect episodes & seasons to their series if possible
|
// Redirect episodes & seasons to their series if possible
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,9 @@ import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.relocation.BringIntoViewRequester
|
import androidx.compose.foundation.relocation.BringIntoViewRequester
|
||||||
import androidx.compose.foundation.relocation.bringIntoViewRequester
|
import androidx.compose.foundation.relocation.bringIntoViewRequester
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.PlayArrow
|
||||||
|
import androidx.compose.material.icons.filled.Settings
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
|
|
@ -41,6 +44,9 @@ import com.github.damontecres.dolphin.data.model.Video
|
||||||
import com.github.damontecres.dolphin.preferences.UserPreferences
|
import com.github.damontecres.dolphin.preferences.UserPreferences
|
||||||
import com.github.damontecres.dolphin.ui.cards.ChapterRow
|
import com.github.damontecres.dolphin.ui.cards.ChapterRow
|
||||||
import com.github.damontecres.dolphin.ui.cards.PersonRow
|
import com.github.damontecres.dolphin.ui.cards.PersonRow
|
||||||
|
import com.github.damontecres.dolphin.ui.components.DialogItem
|
||||||
|
import com.github.damontecres.dolphin.ui.components.DialogParams
|
||||||
|
import com.github.damontecres.dolphin.ui.components.DialogPopup
|
||||||
import com.github.damontecres.dolphin.ui.components.ErrorMessage
|
import com.github.damontecres.dolphin.ui.components.ErrorMessage
|
||||||
import com.github.damontecres.dolphin.ui.components.ExpandablePlayButtons
|
import com.github.damontecres.dolphin.ui.components.ExpandablePlayButtons
|
||||||
import com.github.damontecres.dolphin.ui.components.LoadingPage
|
import com.github.damontecres.dolphin.ui.components.LoadingPage
|
||||||
|
|
@ -55,9 +61,11 @@ import com.github.damontecres.dolphin.ui.tryRequestFocus
|
||||||
import com.github.damontecres.dolphin.util.ExceptionHandler
|
import com.github.damontecres.dolphin.util.ExceptionHandler
|
||||||
import com.github.damontecres.dolphin.util.LoadingState
|
import com.github.damontecres.dolphin.util.LoadingState
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.jellyfin.sdk.api.client.ApiClient
|
import org.jellyfin.sdk.api.client.ApiClient
|
||||||
|
import org.jellyfin.sdk.api.client.extensions.playStateApi
|
||||||
import org.jellyfin.sdk.model.extensions.ticks
|
import org.jellyfin.sdk.model.extensions.ticks
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
@ -69,14 +77,16 @@ class MovieViewModel
|
||||||
constructor(
|
constructor(
|
||||||
api: ApiClient,
|
api: ApiClient,
|
||||||
) : LoadingItemViewModel<Video>(api) {
|
) : LoadingItemViewModel<Video>(api) {
|
||||||
|
private lateinit var itemId: UUID
|
||||||
val people = MutableLiveData<List<Person>>(listOf())
|
val people = MutableLiveData<List<Person>>(listOf())
|
||||||
val chapters = MutableLiveData<List<Chapter>>(listOf())
|
val chapters = MutableLiveData<List<Chapter>>(listOf())
|
||||||
|
|
||||||
override fun init(
|
override fun init(
|
||||||
itemId: UUID,
|
itemId: UUID,
|
||||||
potential: BaseItem?,
|
potential: BaseItem?,
|
||||||
): Job? =
|
): Job? {
|
||||||
viewModelScope.launch(ExceptionHandler()) {
|
this.itemId = itemId
|
||||||
|
return viewModelScope.launch(ExceptionHandler()) {
|
||||||
super.init(itemId, potential)?.join()
|
super.init(itemId, potential)?.join()
|
||||||
item.value?.let { item ->
|
item.value?.let { item ->
|
||||||
people.value =
|
people.value =
|
||||||
|
|
@ -86,6 +96,17 @@ class MovieViewModel
|
||||||
chapters.value = Chapter.fromDto(item.data, api)
|
chapters.value = Chapter.fromDto(item.data, api)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setWatched(played: Boolean) =
|
||||||
|
viewModelScope.launch(ExceptionHandler() + Dispatchers.IO) {
|
||||||
|
if (played) {
|
||||||
|
api.playStateApi.markPlayedItem(itemId)
|
||||||
|
} else {
|
||||||
|
api.playStateApi.markUnplayedItem(itemId)
|
||||||
|
}
|
||||||
|
init(itemId, null)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
@ -105,6 +126,7 @@ fun MovieDetails(
|
||||||
val loading by viewModel.loading.observeAsState(LoadingState.Loading)
|
val loading by viewModel.loading.observeAsState(LoadingState.Loading)
|
||||||
|
|
||||||
var overviewDialog by remember { mutableStateOf<ItemDetailsDialogInfo?>(null) }
|
var overviewDialog by remember { mutableStateOf<ItemDetailsDialogInfo?>(null) }
|
||||||
|
var moreDialog by remember { mutableStateOf<DialogParams?>(null) }
|
||||||
|
|
||||||
when (val state = loading) {
|
when (val state = loading) {
|
||||||
is LoadingState.Error -> ErrorMessage(state)
|
is LoadingState.Error -> ErrorMessage(state)
|
||||||
|
|
@ -137,8 +159,46 @@ fun MovieDetails(
|
||||||
.orEmpty(),
|
.orEmpty(),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
moreOnClick = {},
|
moreOnClick = {
|
||||||
watchOnClick = {},
|
moreDialog =
|
||||||
|
DialogParams(
|
||||||
|
fromLongClick = false,
|
||||||
|
title = movie.name + " (${movie.data.productionYear ?: ""})",
|
||||||
|
items =
|
||||||
|
listOf(
|
||||||
|
DialogItem(
|
||||||
|
"Play",
|
||||||
|
Icons.Default.PlayArrow,
|
||||||
|
iconColor = Color.Green.copy(alpha = .8f),
|
||||||
|
) {
|
||||||
|
navigationManager.navigateTo(
|
||||||
|
Destination.Playback(
|
||||||
|
movie.id,
|
||||||
|
movie.resumeMs ?: 0L,
|
||||||
|
movie,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
DialogItem(
|
||||||
|
"Playback Settings",
|
||||||
|
Icons.Default.Settings,
|
||||||
|
// iconColor = Color.Green.copy(alpha = .8f),
|
||||||
|
) {
|
||||||
|
// TODO choose audio or subtitle tracks?
|
||||||
|
},
|
||||||
|
DialogItem(
|
||||||
|
"Play Version",
|
||||||
|
Icons.Default.PlayArrow,
|
||||||
|
iconColor = Color.Green.copy(alpha = .8f),
|
||||||
|
) {
|
||||||
|
// TODO only show for multiple files
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
watchOnClick = {
|
||||||
|
viewModel.setWatched((movie.data.userData?.played ?: false).not())
|
||||||
|
},
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -151,6 +211,16 @@ fun MovieDetails(
|
||||||
modifier = Modifier,
|
modifier = Modifier,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
moreDialog?.let { params ->
|
||||||
|
DialogPopup(
|
||||||
|
showDialog = true,
|
||||||
|
title = params.title,
|
||||||
|
dialogItems = params.items,
|
||||||
|
onDismissRequest = { moreDialog = null },
|
||||||
|
dismissOnClick = true,
|
||||||
|
waitToLoad = params.fromLongClick,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package com.github.damontecres.dolphin.ui.detail.series
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.automirrored.filled.ArrowForward
|
import androidx.compose.material.icons.automirrored.filled.ArrowForward
|
||||||
import androidx.compose.material.icons.filled.PlayArrow
|
import androidx.compose.material.icons.filled.PlayArrow
|
||||||
|
import androidx.compose.material.icons.filled.Settings
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
|
|
@ -192,11 +193,7 @@ fun SeriesOverview(
|
||||||
navigationManager.navigateTo(
|
navigationManager.navigateTo(
|
||||||
Destination.Playback(
|
Destination.Playback(
|
||||||
ep.id,
|
ep.id,
|
||||||
ep.data.userData
|
ep.resumeMs ?: 0L,
|
||||||
?.playbackPositionTicks
|
|
||||||
?.ticks
|
|
||||||
?.inWholeMilliseconds
|
|
||||||
?: 0L,
|
|
||||||
ep,
|
ep,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
@ -215,12 +212,19 @@ fun SeriesOverview(
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
DialogItem(
|
||||||
|
"Playback Settings",
|
||||||
|
Icons.Default.Settings,
|
||||||
|
// iconColor = Color.Green.copy(alpha = .8f),
|
||||||
|
) {
|
||||||
|
// TODO choose audio or subtitle tracks?
|
||||||
|
},
|
||||||
DialogItem(
|
DialogItem(
|
||||||
"Play Version",
|
"Play Version",
|
||||||
Icons.Default.PlayArrow,
|
Icons.Default.PlayArrow,
|
||||||
iconColor = Color.Green.copy(alpha = .8f),
|
iconColor = Color.Green.copy(alpha = .8f),
|
||||||
) {
|
) {
|
||||||
// TODO
|
// TODO only show for multiple files
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue