mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-08 15:50:16 +02:00
Show last played date on overview dialog (#1248)
## Description Adds a line to the overview/media info dialog with the date when the item was last played. ### Related issues N/A ### Testing Emulator ## Screenshots <img width="1280" height="720" alt="last_played Large" src="https://github.com/user-attachments/assets/1c38141d-1000-43d0-bf28-a0845edfacc2" /> ## AI or LLM usage None
This commit is contained in:
parent
b18416c954
commit
b5422fe82b
8 changed files with 35 additions and 60 deletions
|
|
@ -47,6 +47,8 @@ fun formatDateTime(dateTime: LocalDateTime): String = getDateFormatter().format(
|
||||||
|
|
||||||
fun formatDate(dateTime: LocalDate): String = getDateFormatter().format(dateTime)
|
fun formatDate(dateTime: LocalDate): String = getDateFormatter().format(dateTime)
|
||||||
|
|
||||||
|
fun formatDate(dateTime: LocalDateTime): String = getDateFormatter().format(dateTime)
|
||||||
|
|
||||||
fun toLocalDate(date: String?): LocalDate? =
|
fun toLocalDate(date: String?): LocalDate? =
|
||||||
date?.let {
|
date?.let {
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -19,9 +19,12 @@ import androidx.compose.ui.unit.dp
|
||||||
import androidx.tv.material3.MaterialTheme
|
import androidx.tv.material3.MaterialTheme
|
||||||
import androidx.tv.material3.Text
|
import androidx.tv.material3.Text
|
||||||
import com.github.damontecres.wholphin.R
|
import com.github.damontecres.wholphin.R
|
||||||
|
import com.github.damontecres.wholphin.data.model.BaseItem
|
||||||
|
import com.github.damontecres.wholphin.data.model.studioNames
|
||||||
import com.github.damontecres.wholphin.ui.components.ScrollableDialog
|
import com.github.damontecres.wholphin.ui.components.ScrollableDialog
|
||||||
import com.github.damontecres.wholphin.ui.formatBitrate
|
import com.github.damontecres.wholphin.ui.formatBitrate
|
||||||
import com.github.damontecres.wholphin.ui.formatBytes
|
import com.github.damontecres.wholphin.ui.formatBytes
|
||||||
|
import com.github.damontecres.wholphin.ui.formatDate
|
||||||
import com.github.damontecres.wholphin.ui.isNotNullOrBlank
|
import com.github.damontecres.wholphin.ui.isNotNullOrBlank
|
||||||
import com.github.damontecres.wholphin.ui.letNotEmpty
|
import com.github.damontecres.wholphin.ui.letNotEmpty
|
||||||
import com.github.damontecres.wholphin.ui.util.StreamFormatting.formatAudioCodec
|
import com.github.damontecres.wholphin.ui.util.StreamFormatting.formatAudioCodec
|
||||||
|
|
@ -33,6 +36,7 @@ import org.jellyfin.sdk.model.api.MediaStreamType
|
||||||
import org.jellyfin.sdk.model.api.VideoRange
|
import org.jellyfin.sdk.model.api.VideoRange
|
||||||
import org.jellyfin.sdk.model.api.VideoRangeType
|
import org.jellyfin.sdk.model.api.VideoRangeType
|
||||||
import org.jellyfin.sdk.model.extensions.ticks
|
import org.jellyfin.sdk.model.extensions.ticks
|
||||||
|
import java.time.LocalDateTime
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
|
||||||
data class ItemDetailsDialogInfo(
|
data class ItemDetailsDialogInfo(
|
||||||
|
|
@ -41,7 +45,17 @@ data class ItemDetailsDialogInfo(
|
||||||
val genres: List<String>,
|
val genres: List<String>,
|
||||||
val files: List<MediaSourceInfo>,
|
val files: List<MediaSourceInfo>,
|
||||||
val studios: List<String> = emptyList(),
|
val studios: List<String> = emptyList(),
|
||||||
|
val lastPlayed: LocalDateTime? = null,
|
||||||
|
) {
|
||||||
|
constructor(item: BaseItem) : this(
|
||||||
|
title = item.name ?: "",
|
||||||
|
overview = item.data.overview,
|
||||||
|
genres = item.data.genres.orEmpty(),
|
||||||
|
files = item.data.mediaSources.orEmpty(),
|
||||||
|
studios = item.studioNames,
|
||||||
|
lastPlayed = item.data.userData?.lastPlayedDate,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dialog showing metadata about an item
|
* Dialog showing metadata about an item
|
||||||
|
|
@ -62,6 +76,7 @@ fun ItemDetailsDialog(
|
||||||
val bitrateLabel = stringResource(R.string.bitrate)
|
val bitrateLabel = stringResource(R.string.bitrate)
|
||||||
val unknown = stringResource(R.string.unknown)
|
val unknown = stringResource(R.string.unknown)
|
||||||
val runtimeLabel = stringResource(R.string.runtime_sort)
|
val runtimeLabel = stringResource(R.string.runtime_sort)
|
||||||
|
val lastPlayedLabel = stringResource(R.string.last_played)
|
||||||
|
|
||||||
ScrollableDialog(
|
ScrollableDialog(
|
||||||
onDismissRequest = onDismissRequest,
|
onDismissRequest = onDismissRequest,
|
||||||
|
|
@ -93,6 +108,14 @@ fun ItemDetailsDialog(
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
val lastPlayed =
|
||||||
|
remember(info.lastPlayed) { info.lastPlayed?.let { formatDate(it) } }
|
||||||
|
if (lastPlayed != null) {
|
||||||
|
Text(
|
||||||
|
text = "$lastPlayedLabel: $lastPlayed",
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -191,13 +191,7 @@ fun CollectionDetails(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
overviewOnClick = {
|
overviewOnClick = {
|
||||||
val collection = state.collection!!
|
val collection = state.collection!!
|
||||||
overviewDialog =
|
overviewDialog = ItemDetailsDialogInfo(collection)
|
||||||
ItemDetailsDialogInfo(
|
|
||||||
title = collection.title ?: "",
|
|
||||||
overview = collection.data.overview,
|
|
||||||
genres = collection.data.genres.orEmpty(),
|
|
||||||
files = emptyList(),
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
favoriteOnClick =
|
favoriteOnClick =
|
||||||
remember {
|
remember {
|
||||||
|
|
|
||||||
|
|
@ -149,12 +149,7 @@ fun EpisodeDetails(
|
||||||
},
|
},
|
||||||
overviewOnClick = {
|
overviewOnClick = {
|
||||||
overviewDialog =
|
overviewDialog =
|
||||||
ItemDetailsDialogInfo(
|
ItemDetailsDialogInfo(ep)
|
||||||
title = ep.name ?: context.getString(R.string.unknown),
|
|
||||||
overview = ep.data.overview,
|
|
||||||
genres = ep.data.genres.orEmpty(),
|
|
||||||
files = ep.data.mediaSources.orEmpty(),
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
moreOnClick = {
|
moreOnClick = {
|
||||||
moreDialog =
|
moreDialog =
|
||||||
|
|
@ -218,13 +213,7 @@ fun EpisodeDetails(
|
||||||
onShowOverview = {
|
onShowOverview = {
|
||||||
val source = chosenStreams?.source ?: ep.data.mediaSources?.firstOrNull()
|
val source = chosenStreams?.source ?: ep.data.mediaSources?.firstOrNull()
|
||||||
if (source != null) {
|
if (source != null) {
|
||||||
overviewDialog =
|
overviewDialog = ItemDetailsDialogInfo(ep)
|
||||||
ItemDetailsDialogInfo(
|
|
||||||
title = ep.name ?: context.getString(R.string.unknown),
|
|
||||||
overview = ep.data.overview,
|
|
||||||
genres = ep.data.genres.orEmpty(),
|
|
||||||
files = listOf(source),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onClearChosenStreams = {
|
onClearChosenStreams = {
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,6 @@ import com.github.damontecres.wholphin.data.model.DiscoverItem
|
||||||
import com.github.damontecres.wholphin.data.model.Person
|
import com.github.damontecres.wholphin.data.model.Person
|
||||||
import com.github.damontecres.wholphin.data.model.Trailer
|
import com.github.damontecres.wholphin.data.model.Trailer
|
||||||
import com.github.damontecres.wholphin.data.model.aspectRatioFloat
|
import com.github.damontecres.wholphin.data.model.aspectRatioFloat
|
||||||
import com.github.damontecres.wholphin.data.model.studioNames
|
|
||||||
import com.github.damontecres.wholphin.preferences.UserPreferences
|
import com.github.damontecres.wholphin.preferences.UserPreferences
|
||||||
import com.github.damontecres.wholphin.services.TrailerService
|
import com.github.damontecres.wholphin.services.TrailerService
|
||||||
import com.github.damontecres.wholphin.ui.AspectRatios
|
import com.github.damontecres.wholphin.ui.AspectRatios
|
||||||
|
|
@ -181,13 +180,7 @@ fun MovieDetails(
|
||||||
},
|
},
|
||||||
overviewOnClick = {
|
overviewOnClick = {
|
||||||
overviewDialog =
|
overviewDialog =
|
||||||
ItemDetailsDialogInfo(
|
ItemDetailsDialogInfo(movie)
|
||||||
title = movie.name ?: unknownStr,
|
|
||||||
overview = movie.data.overview,
|
|
||||||
genres = movie.data.genres.orEmpty(),
|
|
||||||
files = movie.data.mediaSources.orEmpty(),
|
|
||||||
studios = movie.studioNames,
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
moreOnClick = {
|
moreOnClick = {
|
||||||
moreDialog =
|
moreDialog =
|
||||||
|
|
@ -250,13 +243,7 @@ fun MovieDetails(
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onShowOverview = {
|
onShowOverview = {
|
||||||
overviewDialog =
|
overviewDialog = ItemDetailsDialogInfo(movie)
|
||||||
ItemDetailsDialogInfo(
|
|
||||||
title = movie.name ?: unknownStr,
|
|
||||||
overview = movie.data.overview,
|
|
||||||
genres = movie.data.genres.orEmpty(),
|
|
||||||
files = movie.data.mediaSources.orEmpty(),
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
onClearChosenStreams = {
|
onClearChosenStreams = {
|
||||||
viewModel.clearChosenStreams(chosenStreams)
|
viewModel.clearChosenStreams(chosenStreams)
|
||||||
|
|
|
||||||
|
|
@ -210,14 +210,7 @@ fun SeriesDetails(
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
overviewOnClick = {
|
overviewOnClick = {
|
||||||
overviewDialog =
|
overviewDialog = ItemDetailsDialogInfo(item)
|
||||||
ItemDetailsDialogInfo(
|
|
||||||
title = item.name ?: context.getString(R.string.unknown),
|
|
||||||
overview = item.data.overview,
|
|
||||||
genres = item.data.genres.orEmpty(),
|
|
||||||
studios = item.studioNames,
|
|
||||||
files = listOf(),
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
playOnClick = { shuffle ->
|
playOnClick = { shuffle ->
|
||||||
if (shuffle) {
|
if (shuffle) {
|
||||||
|
|
|
||||||
|
|
@ -265,13 +265,7 @@ fun SeriesOverview(
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onShowOverview = {
|
onShowOverview = {
|
||||||
overviewDialog =
|
overviewDialog = ItemDetailsDialogInfo(ep)
|
||||||
ItemDetailsDialogInfo(
|
|
||||||
title = ep.name ?: context.getString(R.string.unknown),
|
|
||||||
overview = ep.data.overview,
|
|
||||||
genres = ep.data.genres.orEmpty(),
|
|
||||||
files = ep.data.mediaSources.orEmpty(),
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
onClearChosenStreams = {
|
onClearChosenStreams = {
|
||||||
viewModel.clearChosenStreams(ep, chosenStreams)
|
viewModel.clearChosenStreams(ep, chosenStreams)
|
||||||
|
|
@ -357,15 +351,7 @@ fun SeriesOverview(
|
||||||
},
|
},
|
||||||
overviewOnClick = {
|
overviewOnClick = {
|
||||||
episodeList?.getOrNull(position.episodeRowIndex)?.let {
|
episodeList?.getOrNull(position.episodeRowIndex)?.let {
|
||||||
scope.launchDefault {
|
overviewDialog = ItemDetailsDialogInfo(it)
|
||||||
overviewDialog =
|
|
||||||
ItemDetailsDialogInfo(
|
|
||||||
title = it.name ?: context.getString(R.string.unknown),
|
|
||||||
overview = it.data.overview,
|
|
||||||
genres = it.data.genres.orEmpty(),
|
|
||||||
files = it.data.mediaSources.orEmpty(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
personOnClick = {
|
personOnClick = {
|
||||||
|
|
|
||||||
|
|
@ -756,6 +756,7 @@
|
||||||
<string name="discover_tv">Discover TV Shows</string>
|
<string name="discover_tv">Discover TV Shows</string>
|
||||||
<string name="discover_movies">Discover Movies</string>
|
<string name="discover_movies">Discover Movies</string>
|
||||||
<string name="studios_in">Studios in %1$s</string>
|
<string name="studios_in">Studios in %1$s</string>
|
||||||
|
<string name="last_played">Last played</string>
|
||||||
|
|
||||||
<string name="ass_subtitle_mode_libass">Direct play with libass</string>
|
<string name="ass_subtitle_mode_libass">Direct play with libass</string>
|
||||||
<string name="ass_subtitle_mode_exoplayer">Direct play with ExoPlayer built-in</string>
|
<string name="ass_subtitle_mode_exoplayer">Direct play with ExoPlayer built-in</string>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue