mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-11-05 18:30:59 +01:00
Fix showing live tv programs on home page (#1504)
## Description Fixes the inconsistent live tv programs shown on the home page by using a different API endpoint & filtering. Also some UI fixes: - Use the "Watch live" string consistently for the row instead of it sometimes switching to "Live TV" - Adjust the "Ends at" in the home page header to show the actual end time - Show the channel name in "quick details" - Show season & episode number, if applicable, in "quick details" ### Related issues Fixes #1080 ### Testing Nvidia shield w/ fake EPG data ## Screenshots N/A, just UI fixes, nothing new ## AI or LLM usage None
This commit is contained in:
parent
6c64470c28
commit
5ae452155e
7 changed files with 88 additions and 25 deletions
|
|
@ -92,7 +92,12 @@ data class BaseItem(
|
|||
|
||||
val favorite get() = data.userData?.isFavorite ?: false
|
||||
|
||||
val timeRemainingOrRuntime: Duration? get() = data.timeRemaining ?: data.runTimeTicks?.ticks
|
||||
val timeRemainingOrRuntime: Duration?
|
||||
get() =
|
||||
when (type) {
|
||||
BaseItemKind.PROGRAM -> null
|
||||
else -> data.timeRemaining ?: data.runTimeTicks?.ticks
|
||||
}
|
||||
|
||||
/**
|
||||
* Contains pre computed UI elements that would be expensive to create on the main thread
|
||||
|
|
@ -137,6 +142,17 @@ data class BaseItem(
|
|||
} else if (type == BaseItemKind.BOX_SET) {
|
||||
data.productionYear?.let { add(it.toString()) }
|
||||
data.childCount?.let { add("$it items") }
|
||||
} else if (type == BaseItemKind.PROGRAM) {
|
||||
data.channelName?.let(::add)
|
||||
if (data.isSeries == true) {
|
||||
// TV episode
|
||||
data.seasonEpisode?.let(::add)
|
||||
data.premiereDate?.let {
|
||||
add(getDateFormatter().format(it))
|
||||
}
|
||||
} else {
|
||||
data.productionYear?.let { add(it.toString()) }
|
||||
}
|
||||
} else {
|
||||
data.productionYear?.let { add(it.toString()) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import com.github.damontecres.wholphin.data.model.createStudioDestination
|
|||
import com.github.damontecres.wholphin.preferences.DefaultUserConfiguration
|
||||
import com.github.damontecres.wholphin.preferences.HomePagePreferences
|
||||
import com.github.damontecres.wholphin.ui.DefaultItemFields
|
||||
import com.github.damontecres.wholphin.ui.ProgramItemFields
|
||||
import com.github.damontecres.wholphin.ui.SlimItemFields
|
||||
import com.github.damontecres.wholphin.ui.components.getGenreImageMap
|
||||
import com.github.damontecres.wholphin.ui.main.settings.Library
|
||||
|
|
@ -30,6 +31,7 @@ import com.github.damontecres.wholphin.util.GetGenresRequestHandler
|
|||
import com.github.damontecres.wholphin.util.GetItemsRequestHandler
|
||||
import com.github.damontecres.wholphin.util.GetLiveTvChannelsRequestHandler
|
||||
import com.github.damontecres.wholphin.util.GetPersonsHandler
|
||||
import com.github.damontecres.wholphin.util.GetProgramsDtoHandler
|
||||
import com.github.damontecres.wholphin.util.GetRecordingsRequestHandler
|
||||
import com.github.damontecres.wholphin.util.GetStudiosRequestHandler
|
||||
import com.github.damontecres.wholphin.util.HomeRowLoadingState
|
||||
|
|
@ -54,9 +56,11 @@ import org.jellyfin.sdk.api.client.extensions.liveTvApi
|
|||
import org.jellyfin.sdk.api.client.extensions.userApi
|
||||
import org.jellyfin.sdk.api.client.extensions.userLibraryApi
|
||||
import org.jellyfin.sdk.api.client.extensions.userViewsApi
|
||||
import org.jellyfin.sdk.model.DateTime
|
||||
import org.jellyfin.sdk.model.UUID
|
||||
import org.jellyfin.sdk.model.api.BaseItemKind
|
||||
import org.jellyfin.sdk.model.api.CollectionType
|
||||
import org.jellyfin.sdk.model.api.GetProgramsDto
|
||||
import org.jellyfin.sdk.model.api.ImageType
|
||||
import org.jellyfin.sdk.model.api.ItemSortBy
|
||||
import org.jellyfin.sdk.model.api.SortOrder
|
||||
|
|
@ -66,7 +70,6 @@ import org.jellyfin.sdk.model.api.request.GetItemsRequest
|
|||
import org.jellyfin.sdk.model.api.request.GetLatestMediaRequest
|
||||
import org.jellyfin.sdk.model.api.request.GetLiveTvChannelsRequest
|
||||
import org.jellyfin.sdk.model.api.request.GetPersonsRequest
|
||||
import org.jellyfin.sdk.model.api.request.GetRecommendedProgramsRequest
|
||||
import org.jellyfin.sdk.model.api.request.GetRecordingsRequest
|
||||
import org.jellyfin.sdk.model.api.request.GetStudiosRequest
|
||||
import timber.log.Timber
|
||||
|
|
@ -281,7 +284,7 @@ class HomeSettingsService
|
|||
if (it.collectionType == CollectionType.LIVETV) {
|
||||
HomeRowConfigDisplay(
|
||||
id = index,
|
||||
title = ResStringProvider(R.string.live_tv),
|
||||
title = ResStringProvider(R.string.watch_live),
|
||||
config = HomeRowConfig.TvPrograms(),
|
||||
)
|
||||
} else {
|
||||
|
|
@ -367,7 +370,7 @@ class HomeSettingsService
|
|||
if (userDto.tvAccess) {
|
||||
HomeRowConfigDisplay(
|
||||
id = id++,
|
||||
title = ResStringProvider(R.string.live_tv),
|
||||
title = ResStringProvider(R.string.watch_live),
|
||||
config = HomeRowConfig.TvPrograms(),
|
||||
)
|
||||
} else {
|
||||
|
|
@ -520,7 +523,7 @@ class HomeSettingsService
|
|||
is HomeRowConfig.TvPrograms -> {
|
||||
HomeRowConfigDisplay(
|
||||
id = id,
|
||||
title = ResStringProvider(R.string.live_tv),
|
||||
title = ResStringProvider(R.string.watch_live),
|
||||
config,
|
||||
)
|
||||
}
|
||||
|
|
@ -1037,29 +1040,39 @@ class HomeSettingsService
|
|||
|
||||
is HomeRowConfig.TvPrograms -> {
|
||||
val request =
|
||||
GetRecommendedProgramsRequest(
|
||||
GetProgramsDto(
|
||||
userId = userDto.id,
|
||||
fields = DefaultItemFields,
|
||||
fields = ProgramItemFields,
|
||||
limit = limit,
|
||||
enableUserData = true,
|
||||
enableImages = true,
|
||||
enableImageTypes = listOf(ImageType.PRIMARY, ImageType.LOGO),
|
||||
imageTypeLimit = 1,
|
||||
isAiring = true,
|
||||
minEndDate = DateTime.now().plusMinutes(1),
|
||||
)
|
||||
// paging not supported
|
||||
api.liveTvApi
|
||||
.getRecommendedPrograms(request)
|
||||
.content.items
|
||||
.map { BaseItem(it, row.viewOptions.useSeries) }
|
||||
.let {
|
||||
Success(
|
||||
ResStringProvider(R.string.live_tv),
|
||||
it,
|
||||
row.viewOptions,
|
||||
rowType = row,
|
||||
showViewMore = it.size >= limit,
|
||||
)
|
||||
}
|
||||
if (usePaging) {
|
||||
ApiRequestPager(
|
||||
api,
|
||||
request,
|
||||
GetProgramsDtoHandler,
|
||||
scope,
|
||||
useSeriesForPrimary = row.viewOptions.useSeries,
|
||||
).init()
|
||||
} else {
|
||||
api.liveTvApi
|
||||
.getPrograms(request)
|
||||
.content.items
|
||||
.map { BaseItem(it, row.viewOptions.useSeries) }
|
||||
}.let {
|
||||
Success(
|
||||
ResStringProvider(R.string.watch_live),
|
||||
it,
|
||||
row.viewOptions,
|
||||
rowType = row,
|
||||
showViewMore = it.size >= limit,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
is HomeRowConfig.TvChannels -> {
|
||||
|
|
|
|||
|
|
@ -83,6 +83,8 @@ val PhotoItemFields =
|
|||
ItemFields.HEIGHT,
|
||||
)
|
||||
|
||||
val ProgramItemFields = DefaultItemFields + listOf(ItemFields.CHANNEL_INFO)
|
||||
|
||||
object Cards {
|
||||
const val HEIGHT_2X3_DP = 172
|
||||
val height2x3 = HEIGHT_2X3_DP.dp
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ import androidx.compose.runtime.getValue
|
|||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalResources
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
|
|
@ -29,6 +28,7 @@ import com.github.damontecres.wholphin.ui.dot
|
|||
import com.github.damontecres.wholphin.ui.getTimeFormatter
|
||||
import com.github.damontecres.wholphin.ui.util.LocalClock
|
||||
import com.github.damontecres.wholphin.ui.util.LocalInterfaceCustomization
|
||||
import org.jellyfin.sdk.model.DateTime
|
||||
import kotlin.time.Duration
|
||||
|
||||
@Composable
|
||||
|
|
@ -37,6 +37,7 @@ fun QuickDetails(
|
|||
timeRemaining: Duration?,
|
||||
modifier: Modifier = Modifier,
|
||||
textStyle: TextStyle = MaterialTheme.typography.titleSmall,
|
||||
endsAt: DateTime? = null,
|
||||
) {
|
||||
val enabled = LocalInterfaceCustomization.current.enabledDisplayToggles
|
||||
val inlineContentMap = rememberQuickDetailsContentMap(textStyle)
|
||||
|
|
@ -53,7 +54,11 @@ fun QuickDetails(
|
|||
QuickDetailsText(details.criticRating, Modifier, textStyle, inlineContentMap)
|
||||
}
|
||||
}
|
||||
timeRemaining?.let { TimeRemaining(it, textStyle = textStyle) }
|
||||
if (timeRemaining != null) {
|
||||
TimeRemaining(timeRemaining, textStyle = textStyle)
|
||||
} else if (endsAt != null) {
|
||||
EndsAt(endsAt, textStyle = textStyle)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -150,3 +155,26 @@ fun TimeRemaining(
|
|||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun EndsAt(
|
||||
endsAt: DateTime,
|
||||
modifier: Modifier = Modifier,
|
||||
textStyle: TextStyle = MaterialTheme.typography.titleSmall,
|
||||
) {
|
||||
val resources = LocalResources.current
|
||||
val remainingStr =
|
||||
remember(endsAt, resources) {
|
||||
val endTimeStr = getTimeFormatter().format(endsAt)
|
||||
buildAnnotatedString {
|
||||
dot()
|
||||
append(resources.getString(R.string.ends_at, endTimeStr))
|
||||
}
|
||||
}
|
||||
Text(
|
||||
text = remainingStr,
|
||||
style = textStyle,
|
||||
maxLines = 1,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -300,6 +300,7 @@ fun SeerrDiscoverPage(
|
|||
overviewTwoLines = true,
|
||||
quickDetails = details,
|
||||
timeRemaining = null,
|
||||
endsAt = null,
|
||||
showLogo = preferences.appPreferences.interfacePreferences.showLogos,
|
||||
logoImageUrl = null, // TODO
|
||||
modifier =
|
||||
|
|
|
|||
|
|
@ -88,6 +88,7 @@ import com.github.damontecres.wholphin.ui.util.ScrollToTopBringIntoViewSpec
|
|||
import com.github.damontecres.wholphin.util.HomeRowLoadingState
|
||||
import com.github.damontecres.wholphin.util.LoadingState
|
||||
import kotlinx.coroutines.delay
|
||||
import org.jellyfin.sdk.model.DateTime
|
||||
import org.jellyfin.sdk.model.api.BaseItemKind
|
||||
import org.jellyfin.sdk.model.api.MediaType
|
||||
import timber.log.Timber
|
||||
|
|
@ -550,6 +551,7 @@ fun HomePageHeader(
|
|||
overviewTwoLines = isEpisode,
|
||||
quickDetails = item?.ui?.quickDetails,
|
||||
timeRemaining = item?.timeRemainingOrRuntime,
|
||||
endsAt = item?.data?.endDate,
|
||||
showLogo = showLogo,
|
||||
logoImageUrl = rememberLogoUrl(item),
|
||||
modifier = modifier,
|
||||
|
|
@ -564,6 +566,7 @@ fun HomePageHeader(
|
|||
overviewTwoLines: Boolean,
|
||||
quickDetails: QuickDetailsData?,
|
||||
timeRemaining: Duration?,
|
||||
endsAt: DateTime?,
|
||||
showLogo: Boolean,
|
||||
logoImageUrl: String?,
|
||||
modifier: Modifier = Modifier,
|
||||
|
|
@ -587,7 +590,7 @@ fun HomePageHeader(
|
|||
if (subtitle != null) {
|
||||
EpisodeName(subtitle)
|
||||
}
|
||||
QuickDetails(quickDetails, timeRemaining)
|
||||
QuickDetails(quickDetails, timeRemaining, endsAt = endsAt)
|
||||
val overviewModifier =
|
||||
Modifier
|
||||
.padding(0.dp)
|
||||
|
|
|
|||
|
|
@ -136,7 +136,7 @@ enum class LibraryRowType(
|
|||
GENRES(R.string.genres),
|
||||
STUDIOS(R.string.studios),
|
||||
TV_CHANNELS(R.string.channels),
|
||||
TV_PROGRAMS(R.string.live_tv),
|
||||
TV_PROGRAMS(R.string.watch_live),
|
||||
RECENTLY_RECORDED(R.string.recently_recorded),
|
||||
COLLECTION(R.string.collection),
|
||||
PLAYLIST(R.string.playlist),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue