mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-09 08:01:20 +02:00
Show indicator for multiple versions of media file (#652)
## Description Adds an indicator to cards on grid pages if there are more than a single version of the item. Also adds the count to the video stream label, much like for multiple audio/subtitle tracks. No behavior changes, just visual changes. You still need to select the version from More->Choose Version. ### Related issues Closes #612 ### Screenshots <img width="535" height="476" alt="image" src="https://github.com/user-attachments/assets/5bf80d36-7abd-4a0e-a14d-062439f024d9" /> <img width="958" height="581" alt="image" src="https://github.com/user-attachments/assets/e3a0ac4e-a283-4bcf-bc16-e61d81401207" />
This commit is contained in:
parent
d2e0d527dd
commit
2cd34692e9
13 changed files with 67 additions and 19 deletions
|
|
@ -51,6 +51,7 @@ val DefaultItemFields =
|
||||||
ItemFields.SORT_NAME,
|
ItemFields.SORT_NAME,
|
||||||
ItemFields.CHAPTERS,
|
ItemFields.CHAPTERS,
|
||||||
ItemFields.MEDIA_SOURCES,
|
ItemFields.MEDIA_SOURCES,
|
||||||
|
ItemFields.MEDIA_SOURCE_COUNT,
|
||||||
)
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -63,6 +64,7 @@ val SlimItemFields =
|
||||||
ItemFields.CHILD_COUNT,
|
ItemFields.CHILD_COUNT,
|
||||||
ItemFields.OVERVIEW,
|
ItemFields.OVERVIEW,
|
||||||
ItemFields.SORT_NAME,
|
ItemFields.SORT_NAME,
|
||||||
|
ItemFields.MEDIA_SOURCE_COUNT,
|
||||||
)
|
)
|
||||||
|
|
||||||
object Cards {
|
object Cards {
|
||||||
|
|
|
||||||
|
|
@ -97,6 +97,7 @@ fun EpisodeCard(
|
||||||
watched = dto?.userData?.played ?: false,
|
watched = dto?.userData?.played ?: false,
|
||||||
unwatchedCount = dto?.userData?.unplayedItemCount ?: -1,
|
unwatchedCount = dto?.userData?.unplayedItemCount ?: -1,
|
||||||
watchedPercent = dto?.userData?.playedPercentage,
|
watchedPercent = dto?.userData?.playedPercentage,
|
||||||
|
numberOfVersions = dto?.mediaSourceCount ?: 0,
|
||||||
useFallbackText = false,
|
useFallbackText = false,
|
||||||
modifier =
|
modifier =
|
||||||
Modifier
|
Modifier
|
||||||
|
|
|
||||||
|
|
@ -68,6 +68,7 @@ fun ExtrasRow(
|
||||||
isPlayed = false,
|
isPlayed = false,
|
||||||
unplayedItemCount = -1,
|
unplayedItemCount = -1,
|
||||||
playedPercentage = -1.0,
|
playedPercentage = -1.0,
|
||||||
|
numberOfVersions = -1,
|
||||||
aspectRatio = AspectRatios.FOUR_THREE, // TODO
|
aspectRatio = AspectRatios.FOUR_THREE, // TODO
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,7 @@ fun GridCard(
|
||||||
watched = dto?.userData?.played ?: false,
|
watched = dto?.userData?.played ?: false,
|
||||||
unwatchedCount = dto?.userData?.unplayedItemCount ?: -1,
|
unwatchedCount = dto?.userData?.unplayedItemCount ?: -1,
|
||||||
watchedPercent = dto?.userData?.playedPercentage,
|
watchedPercent = dto?.userData?.playedPercentage,
|
||||||
|
numberOfVersions = dto?.mediaSourceCount ?: 0,
|
||||||
useFallbackText = false,
|
useFallbackText = false,
|
||||||
contentScale = imageContentScale,
|
contentScale = imageContentScale,
|
||||||
modifier =
|
modifier =
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,7 @@ fun ItemCardImage(
|
||||||
watched: Boolean,
|
watched: Boolean,
|
||||||
unwatchedCount: Int,
|
unwatchedCount: Int,
|
||||||
watchedPercent: Double?,
|
watchedPercent: Double?,
|
||||||
|
numberOfVersions: Int,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
imageType: ImageType = ImageType.PRIMARY,
|
imageType: ImageType = ImageType.PRIMARY,
|
||||||
useFallbackText: Boolean = true,
|
useFallbackText: Boolean = true,
|
||||||
|
|
@ -86,6 +87,7 @@ fun ItemCardImage(
|
||||||
watched = watched,
|
watched = watched,
|
||||||
unwatchedCount = unwatchedCount,
|
unwatchedCount = unwatchedCount,
|
||||||
watchedPercent = watchedPercent,
|
watchedPercent = watchedPercent,
|
||||||
|
numberOfVersions = numberOfVersions,
|
||||||
modifier =
|
modifier =
|
||||||
modifier.onLayoutRectChanged(
|
modifier.onLayoutRectChanged(
|
||||||
throttleMillis = 100,
|
throttleMillis = 100,
|
||||||
|
|
@ -107,6 +109,7 @@ fun ItemCardImage(
|
||||||
watched: Boolean,
|
watched: Boolean,
|
||||||
unwatchedCount: Int,
|
unwatchedCount: Int,
|
||||||
watchedPercent: Double?,
|
watchedPercent: Double?,
|
||||||
|
numberOfVersions: Int,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
useFallbackText: Boolean = true,
|
useFallbackText: Boolean = true,
|
||||||
contentScale: ContentScale = ContentScale.Fit,
|
contentScale: ContentScale = ContentScale.Fit,
|
||||||
|
|
@ -143,6 +146,7 @@ fun ItemCardImage(
|
||||||
watched = watched,
|
watched = watched,
|
||||||
unwatchedCount = unwatchedCount,
|
unwatchedCount = unwatchedCount,
|
||||||
watchedPercent = watchedPercent,
|
watchedPercent = watchedPercent,
|
||||||
|
numberOfVersions = numberOfVersions,
|
||||||
modifier = Modifier,
|
modifier = Modifier,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -198,21 +202,46 @@ fun ItemCardImageOverlay(
|
||||||
watched: Boolean,
|
watched: Boolean,
|
||||||
unwatchedCount: Int,
|
unwatchedCount: Int,
|
||||||
watchedPercent: Double?,
|
watchedPercent: Double?,
|
||||||
|
numberOfVersions: Int,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
Box(modifier = modifier.fillMaxSize()) {
|
Box(modifier = modifier.fillMaxSize()) {
|
||||||
if (favorite) {
|
Row(
|
||||||
Text(
|
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
modifier =
|
modifier =
|
||||||
Modifier
|
Modifier
|
||||||
.align(Alignment.TopStart)
|
.align(Alignment.TopStart)
|
||||||
.padding(8.dp),
|
.padding(4.dp),
|
||||||
|
) {
|
||||||
|
if (numberOfVersions > 1) {
|
||||||
|
Box(
|
||||||
|
modifier =
|
||||||
|
Modifier
|
||||||
|
.background(
|
||||||
|
AppColors.TransparentBlack50,
|
||||||
|
shape = RoundedCornerShape(25),
|
||||||
|
),
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = numberOfVersions.toString(),
|
||||||
|
color = MaterialTheme.colorScheme.onSurface,
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
// fontSize = 16.sp,
|
||||||
|
modifier = Modifier.padding(4.dp),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (favorite) {
|
||||||
|
Text(
|
||||||
color = colorResource(android.R.color.holo_red_light),
|
color = colorResource(android.R.color.holo_red_light),
|
||||||
text = stringResource(R.string.fa_heart),
|
text = stringResource(R.string.fa_heart),
|
||||||
fontSize = 20.sp,
|
fontSize = 20.sp,
|
||||||
fontFamily = FontAwesome,
|
fontFamily = FontAwesome,
|
||||||
|
modifier = Modifier,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Row(
|
Row(
|
||||||
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,7 @@ fun PersonCard(
|
||||||
favorite = item.favorite,
|
favorite = item.favorite,
|
||||||
watched = false,
|
watched = false,
|
||||||
unwatchedCount = -1,
|
unwatchedCount = -1,
|
||||||
|
numberOfVersions = -1,
|
||||||
watchedPercent = null,
|
watchedPercent = null,
|
||||||
useFallbackText = false,
|
useFallbackText = false,
|
||||||
modifier =
|
modifier =
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,7 @@ fun SeasonCard(
|
||||||
isPlayed = item?.data?.userData?.played ?: false,
|
isPlayed = item?.data?.userData?.played ?: false,
|
||||||
unplayedItemCount = item?.data?.userData?.unplayedItemCount ?: 0,
|
unplayedItemCount = item?.data?.userData?.unplayedItemCount ?: 0,
|
||||||
playedPercentage = item?.data?.userData?.playedPercentage ?: 0.0,
|
playedPercentage = item?.data?.userData?.playedPercentage ?: 0.0,
|
||||||
|
numberOfVersions = item?.data?.mediaSourceCount ?: 0,
|
||||||
onClick = onClick,
|
onClick = onClick,
|
||||||
onLongClick = onLongClick,
|
onLongClick = onLongClick,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
|
|
@ -112,6 +113,7 @@ fun SeasonCard(
|
||||||
isPlayed: Boolean,
|
isPlayed: Boolean,
|
||||||
unplayedItemCount: Int,
|
unplayedItemCount: Int,
|
||||||
playedPercentage: Double,
|
playedPercentage: Double,
|
||||||
|
numberOfVersions: Int,
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
onLongClick: () -> Unit,
|
onLongClick: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
|
|
@ -172,6 +174,7 @@ fun SeasonCard(
|
||||||
watched = isPlayed,
|
watched = isPlayed,
|
||||||
unwatchedCount = unplayedItemCount,
|
unwatchedCount = unplayedItemCount,
|
||||||
watchedPercent = playedPercentage,
|
watchedPercent = playedPercentage,
|
||||||
|
numberOfVersions = numberOfVersions,
|
||||||
useFallbackText = false,
|
useFallbackText = false,
|
||||||
modifier =
|
modifier =
|
||||||
Modifier
|
Modifier
|
||||||
|
|
|
||||||
|
|
@ -47,12 +47,14 @@ import org.jellyfin.sdk.model.api.VideoRangeType
|
||||||
@NonRestartableComposable
|
@NonRestartableComposable
|
||||||
fun VideoStreamDetails(
|
fun VideoStreamDetails(
|
||||||
chosenStreams: ChosenStreams?,
|
chosenStreams: ChosenStreams?,
|
||||||
|
numberOfVersions: Int,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) = VideoStreamDetails(
|
) = VideoStreamDetails(
|
||||||
chosenStreams?.source,
|
chosenStreams?.source,
|
||||||
chosenStreams?.videoStream,
|
chosenStreams?.videoStream,
|
||||||
chosenStreams?.audioStream,
|
chosenStreams?.audioStream,
|
||||||
chosenStreams?.subtitleStream,
|
chosenStreams?.subtitleStream,
|
||||||
|
numberOfVersions,
|
||||||
modifier,
|
modifier,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -62,6 +64,7 @@ fun VideoStreamDetails(
|
||||||
videoStream: MediaStream?,
|
videoStream: MediaStream?,
|
||||||
audioStream: MediaStream?,
|
audioStream: MediaStream?,
|
||||||
subtitleStream: MediaStream?,
|
subtitleStream: MediaStream?,
|
||||||
|
numberOfVersions: Int = 0,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
|
@ -86,13 +89,16 @@ fun VideoStreamDetails(
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
val range = formatVideoRange(context, it.videoRange, it.videoRangeType, it.videoDoViTitle)
|
val range = formatVideoRange(context, it.videoRange, it.videoRangeType, it.videoDoViTitle)
|
||||||
listOfNotNull(
|
resName.concatWithSpace(range)
|
||||||
resName.concatWithSpace(range),
|
|
||||||
it.codec?.uppercase(),
|
|
||||||
)
|
|
||||||
}.orEmpty()
|
|
||||||
}
|
}
|
||||||
video.forEach {
|
}
|
||||||
|
video?.let {
|
||||||
|
StreamLabel(
|
||||||
|
text = it,
|
||||||
|
count = numberOfVersions,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
videoStream?.codec?.uppercase()?.let {
|
||||||
StreamLabel(it)
|
StreamLabel(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -444,6 +444,7 @@ fun PlaylistItem(
|
||||||
watched = item?.data?.userData?.played ?: false,
|
watched = item?.data?.userData?.played ?: false,
|
||||||
unwatchedCount = item?.data?.userData?.unplayedItemCount ?: -1,
|
unwatchedCount = item?.data?.userData?.unplayedItemCount ?: -1,
|
||||||
watchedPercent = 0.0,
|
watchedPercent = 0.0,
|
||||||
|
numberOfVersions = item?.data?.mediaSourceCount ?: 0,
|
||||||
modifier = Modifier.width(160.dp),
|
modifier = Modifier.width(160.dp),
|
||||||
useFallbackText = false,
|
useFallbackText = false,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,7 @@ fun EpisodeDetailsHeader(
|
||||||
|
|
||||||
VideoStreamDetails(
|
VideoStreamDetails(
|
||||||
chosenStreams = chosenStreams,
|
chosenStreams = chosenStreams,
|
||||||
|
numberOfVersions = dto.mediaSourceCount ?: 0,
|
||||||
modifier = Modifier.padding(bottom = padding),
|
modifier = Modifier.padding(bottom = padding),
|
||||||
)
|
)
|
||||||
dto.taglines?.firstOrNull()?.let { tagline ->
|
dto.taglines?.firstOrNull()?.let { tagline ->
|
||||||
|
|
|
||||||
|
|
@ -73,6 +73,7 @@ fun MovieDetailsHeader(
|
||||||
|
|
||||||
VideoStreamDetails(
|
VideoStreamDetails(
|
||||||
chosenStreams = chosenStreams,
|
chosenStreams = chosenStreams,
|
||||||
|
numberOfVersions = movie.data.mediaSourceCount ?: 0,
|
||||||
modifier = Modifier.padding(bottom = padding),
|
modifier = Modifier.padding(bottom = padding),
|
||||||
)
|
)
|
||||||
dto.taglines?.firstOrNull()?.let { tagline ->
|
dto.taglines?.firstOrNull()?.let { tagline ->
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ fun FocusedEpisodeHeader(
|
||||||
if (dto != null) {
|
if (dto != null) {
|
||||||
VideoStreamDetails(
|
VideoStreamDetails(
|
||||||
chosenStreams = chosenStreams,
|
chosenStreams = chosenStreams,
|
||||||
|
numberOfVersions = dto.mediaSourceCount ?: 0,
|
||||||
modifier = Modifier,
|
modifier = Modifier,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,10 +30,8 @@ import com.github.damontecres.wholphin.R
|
||||||
import com.github.damontecres.wholphin.preferences.AppPreference
|
import com.github.damontecres.wholphin.preferences.AppPreference
|
||||||
import com.github.damontecres.wholphin.preferences.AppThemeColors
|
import com.github.damontecres.wholphin.preferences.AppThemeColors
|
||||||
import com.github.damontecres.wholphin.ui.AspectRatios
|
import com.github.damontecres.wholphin.ui.AspectRatios
|
||||||
import com.github.damontecres.wholphin.ui.cards.BannerCard
|
|
||||||
import com.github.damontecres.wholphin.ui.cards.SeasonCard
|
import com.github.damontecres.wholphin.ui.cards.SeasonCard
|
||||||
import com.github.damontecres.wholphin.ui.cards.WatchedIcon
|
import com.github.damontecres.wholphin.ui.cards.WatchedIcon
|
||||||
import com.github.damontecres.wholphin.ui.components.ExpandableFaButton
|
|
||||||
import com.github.damontecres.wholphin.ui.nav.NavDrawerItem
|
import com.github.damontecres.wholphin.ui.nav.NavDrawerItem
|
||||||
import com.github.damontecres.wholphin.ui.nav.NavItem
|
import com.github.damontecres.wholphin.ui.nav.NavItem
|
||||||
import com.github.damontecres.wholphin.ui.playback.PlaybackButton
|
import com.github.damontecres.wholphin.ui.playback.PlaybackButton
|
||||||
|
|
@ -109,6 +107,7 @@ private fun ThemeExample(theme: AppThemeColors) {
|
||||||
isPlayed = true,
|
isPlayed = true,
|
||||||
unplayedItemCount = 2,
|
unplayedItemCount = 2,
|
||||||
playedPercentage = 50.0,
|
playedPercentage = 50.0,
|
||||||
|
numberOfVersions = 2,
|
||||||
onClick = { },
|
onClick = { },
|
||||||
onLongClick = {},
|
onLongClick = {},
|
||||||
imageHeight = 120.dp,
|
imageHeight = 120.dp,
|
||||||
|
|
@ -125,6 +124,7 @@ private fun ThemeExample(theme: AppThemeColors) {
|
||||||
isPlayed = true,
|
isPlayed = true,
|
||||||
unplayedItemCount = 2,
|
unplayedItemCount = 2,
|
||||||
playedPercentage = 0.0,
|
playedPercentage = 0.0,
|
||||||
|
numberOfVersions = 0,
|
||||||
onClick = { },
|
onClick = { },
|
||||||
onLongClick = {},
|
onLongClick = {},
|
||||||
imageHeight = 120.dp,
|
imageHeight = 120.dp,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue