refactor: update image handling in Movie dto to use imageUrlPrefix and JellyfinImageHelper

This commit is contained in:
2026-04-01 22:30:32 +02:00
parent d6d05be93f
commit ce5266f17c
16 changed files with 46 additions and 297 deletions

View File

@@ -65,7 +65,7 @@ class MovieScreenContentTest {
runtime = "164m", runtime = "164m",
format = "4K", format = "4K",
synopsis = "Officer K uncovers a secret that sends him searching for Rick Deckard.", synopsis = "Officer K uncovers a secret that sends him searching for Rick Deckard.",
heroImageUrl = "https://images.unsplash.com/photo-1500530855697-b586d89ba3ee", imageUrlPrefix = "https://images.unsplash.com/photo-1500530855697-b586d89ba3ee",
audioTrack = "ENG 5.1", audioTrack = "ENG 5.1",
subtitles = "ENG", subtitles = "ENG",
cast = listOf( cast = listOf(

View File

@@ -19,9 +19,11 @@ import hu.bbara.purefin.common.ui.components.MediaDetailOverviewSection
import hu.bbara.purefin.common.ui.components.MediaDetailPlaybackSection import hu.bbara.purefin.common.ui.components.MediaDetailPlaybackSection
import hu.bbara.purefin.common.ui.components.MediaDetailSectionTitle import hu.bbara.purefin.common.ui.components.MediaDetailSectionTitle
import hu.bbara.purefin.common.ui.components.TvMediaDetailScaffold import hu.bbara.purefin.common.ui.components.TvMediaDetailScaffold
import hu.bbara.purefin.core.data.image.JellyfinImageHelper
import hu.bbara.purefin.core.data.navigation.MovieDto import hu.bbara.purefin.core.data.navigation.MovieDto
import hu.bbara.purefin.core.model.Movie import hu.bbara.purefin.core.model.Movie
import hu.bbara.purefin.feature.shared.content.movie.MovieScreenViewModel import hu.bbara.purefin.feature.shared.content.movie.MovieScreenViewModel
import org.jellyfin.sdk.model.api.ImageType
@Composable @Composable
fun MovieScreen( fun MovieScreen(
@@ -60,7 +62,7 @@ internal fun MovieScreenContent(
} }
TvMediaDetailScaffold( TvMediaDetailScaffold(
heroImageUrl = movie.heroImageUrl, heroImageUrl = JellyfinImageHelper.finishImageUrl(movie.imageUrlPrefix, ImageType.PRIMARY),
resetScrollKey = movie.id, resetScrollKey = movie.id,
modifier = modifier, modifier = modifier,
topBar = { topBar = {

View File

@@ -39,11 +39,13 @@ import androidx.compose.ui.unit.sp
import hu.bbara.purefin.common.ui.PosterCard import hu.bbara.purefin.common.ui.PosterCard
import hu.bbara.purefin.common.ui.components.MediaProgressBar import hu.bbara.purefin.common.ui.components.MediaProgressBar
import hu.bbara.purefin.common.ui.components.PurefinAsyncImage import hu.bbara.purefin.common.ui.components.PurefinAsyncImage
import hu.bbara.purefin.core.data.image.JellyfinImageHelper
import hu.bbara.purefin.feature.shared.home.ContinueWatchingItem import hu.bbara.purefin.feature.shared.home.ContinueWatchingItem
import hu.bbara.purefin.feature.shared.home.NextUpItem import hu.bbara.purefin.feature.shared.home.NextUpItem
import hu.bbara.purefin.feature.shared.home.PosterItem import hu.bbara.purefin.feature.shared.home.PosterItem
import org.jellyfin.sdk.model.UUID import org.jellyfin.sdk.model.UUID
import org.jellyfin.sdk.model.api.BaseItemKind import org.jellyfin.sdk.model.api.BaseItemKind
import org.jellyfin.sdk.model.api.ImageType
import kotlin.math.nextUp import kotlin.math.nextUp
@Composable @Composable
@@ -67,8 +69,6 @@ fun TvContinueWatchingSection(
itemsIndexed(items = items) { index, item -> itemsIndexed(items = items) { index, item ->
TvContinueWatchingCard( TvContinueWatchingCard(
item = item, item = item,
isFirstItem = index == 0,
isLastItem = index == items.lastIndex,
onMovieSelected = onMovieSelected, onMovieSelected = onMovieSelected,
onEpisodeSelected = onEpisodeSelected onEpisodeSelected = onEpisodeSelected
) )
@@ -80,8 +80,6 @@ fun TvContinueWatchingSection(
fun TvContinueWatchingCard( fun TvContinueWatchingCard(
item: ContinueWatchingItem, item: ContinueWatchingItem,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
isFirstItem: Boolean = false,
isLastItem: Boolean = false,
onMovieSelected: (UUID) -> Unit, onMovieSelected: (UUID) -> Unit,
onEpisodeSelected: (UUID, UUID, UUID) -> Unit, onEpisodeSelected: (UUID, UUID, UUID) -> Unit,
) { ) {
@@ -91,7 +89,10 @@ fun TvContinueWatchingCard(
val scale by animateFloatAsState(targetValue = if (isFocused) 1.07f else 1.0f, label = "scale") val scale by animateFloatAsState(targetValue = if (isFocused) 1.07f else 1.0f, label = "scale")
val imageUrl = when (item.type) { val imageUrl = when (item.type) {
BaseItemKind.MOVIE -> item.movie?.heroImageUrl BaseItemKind.MOVIE -> JellyfinImageHelper.finishImageUrl(
prefixImageUrl = item.movie?.imageUrlPrefix,
imageType = ImageType.PRIMARY
)
BaseItemKind.EPISODE -> item.episode?.heroImageUrl BaseItemKind.EPISODE -> item.episode?.heroImageUrl
else -> null else -> null
} }

View File

@@ -35,12 +35,14 @@ import androidx.hilt.navigation.compose.hiltViewModel
import hu.bbara.purefin.common.ui.MediaMetaChip import hu.bbara.purefin.common.ui.MediaMetaChip
import hu.bbara.purefin.common.ui.PurefinWaitingScreen import hu.bbara.purefin.common.ui.PurefinWaitingScreen
import hu.bbara.purefin.common.ui.components.MediaHero import hu.bbara.purefin.common.ui.components.MediaHero
import hu.bbara.purefin.core.data.image.JellyfinImageHelper
import hu.bbara.purefin.core.data.navigation.MovieDto import hu.bbara.purefin.core.data.navigation.MovieDto
import hu.bbara.purefin.core.model.CastMember import hu.bbara.purefin.core.model.CastMember
import hu.bbara.purefin.core.model.Movie import hu.bbara.purefin.core.model.Movie
import hu.bbara.purefin.feature.download.DownloadState import hu.bbara.purefin.feature.download.DownloadState
import hu.bbara.purefin.feature.shared.content.movie.MovieScreenViewModel import hu.bbara.purefin.feature.shared.content.movie.MovieScreenViewModel
import hu.bbara.purefin.ui.theme.AppTheme import hu.bbara.purefin.ui.theme.AppTheme
import org.jellyfin.sdk.model.api.ImageType
import java.util.UUID import java.util.UUID
@Composable @Composable
@@ -135,7 +137,7 @@ fun MediaHeroSection(
.height(sectionHeight) .height(sectionHeight)
) { ) {
MediaHero( MediaHero(
imageUrl = movie.heroImageUrl, imageUrl = JellyfinImageHelper.finishImageUrl(movie.imageUrlPrefix, ImageType.PRIMARY),
backgroundColor = MaterialTheme.colorScheme.background, backgroundColor = MaterialTheme.colorScheme.background,
modifier = Modifier.fillMaxSize() modifier = Modifier.fillMaxSize()
) )
@@ -209,7 +211,7 @@ private fun previewMovie(): Movie =
runtime = "2h 44m", runtime = "2h 44m",
format = "Dolby Vision", format = "Dolby Vision",
synopsis = "A new blade runner uncovers a buried secret that forces him to trace the vanished footsteps of Rick Deckard.", synopsis = "A new blade runner uncovers a buried secret that forces him to trace the vanished footsteps of Rick Deckard.",
heroImageUrl = "https://images.unsplash.com/photo-1519608487953-e999c86e7455", imageUrlPrefix = "https://images.unsplash.com/photo-1519608487953-e999c86e7455",
audioTrack = "English 5.1", audioTrack = "English 5.1",
subtitles = "English CC", subtitles = "English CC",
cast = listOf( cast = listOf(

View File

@@ -21,26 +21,19 @@ import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import hu.bbara.purefin.app.home.ui.continuewatching.ContinueWatchingSection import hu.bbara.purefin.app.home.ui.continuewatching.ContinueWatchingSection
import hu.bbara.purefin.app.home.ui.featured.SuggestionsSection import hu.bbara.purefin.app.home.ui.featured.SuggestionsSection
import hu.bbara.purefin.app.home.ui.library.LibraryPosterSection import hu.bbara.purefin.app.home.ui.library.LibraryPosterSection
import hu.bbara.purefin.app.home.ui.nextup.NextUpSection import hu.bbara.purefin.app.home.ui.nextup.NextUpSection
import hu.bbara.purefin.app.home.ui.shared.HomeEmptyState import hu.bbara.purefin.app.home.ui.shared.HomeEmptyState
import hu.bbara.purefin.core.model.Episode
import hu.bbara.purefin.core.model.Movie
import hu.bbara.purefin.core.model.Series
import hu.bbara.purefin.feature.shared.home.ContinueWatchingItem import hu.bbara.purefin.feature.shared.home.ContinueWatchingItem
import hu.bbara.purefin.feature.shared.home.LibraryItem import hu.bbara.purefin.feature.shared.home.LibraryItem
import hu.bbara.purefin.feature.shared.home.NextUpItem import hu.bbara.purefin.feature.shared.home.NextUpItem
import hu.bbara.purefin.feature.shared.home.PosterItem import hu.bbara.purefin.feature.shared.home.PosterItem
import hu.bbara.purefin.feature.shared.home.SuggestedItem import hu.bbara.purefin.feature.shared.home.SuggestedItem
import hu.bbara.purefin.ui.theme.AppTheme
import org.jellyfin.sdk.model.UUID import org.jellyfin.sdk.model.UUID
import org.jellyfin.sdk.model.api.BaseItemKind import org.jellyfin.sdk.model.api.BaseItemKind
import org.jellyfin.sdk.model.api.CollectionType
import java.util.UUID as JavaUuid
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
@@ -169,266 +162,4 @@ fun HomeContent(
} }
} }
} }
} }
@Preview(name = "Home Full", showBackground = true, widthDp = 412, heightDp = 915)
@Composable
private fun HomeContentPreview() {
AppTheme(darkTheme = true) {
HomeContent(
libraries = homePreviewLibraries(),
libraryContent = homePreviewLibraryContent(),
suggestions = emptyList(),
continueWatching = homePreviewContinueWatching(),
nextUp = homePreviewNextUp(),
isRefreshing = false,
onRefresh = {},
onMovieSelected = {},
onSeriesSelected = {},
onEpisodeSelected = { _, _, _ -> },
onLibrarySelected = {},
onBrowseLibrariesClick = {}
)
}
}
@Preview(name = "Home Libraries Only", showBackground = true, widthDp = 412, heightDp = 915)
@Composable
private fun HomeLibrariesOnlyPreview() {
AppTheme(darkTheme = true) {
HomeContent(
libraries = homePreviewLibraries(),
libraryContent = homePreviewLibraryContent(),
suggestions = emptyList(),
continueWatching = emptyList(),
nextUp = emptyList(),
isRefreshing = false,
onRefresh = {},
onMovieSelected = {},
onSeriesSelected = {},
onEpisodeSelected = { _, _, _ -> },
onLibrarySelected = {},
onBrowseLibrariesClick = {}
)
}
}
@Preview(name = "Home Empty", showBackground = true, widthDp = 412, heightDp = 915)
@Composable
private fun HomeEmptyPreview() {
AppTheme(darkTheme = false) {
HomeContent(
libraries = emptyList(),
libraryContent = emptyMap(),
suggestions = emptyList(),
continueWatching = emptyList(),
nextUp = emptyList(),
isRefreshing = false,
onRefresh = {},
onMovieSelected = {},
onSeriesSelected = {},
onEpisodeSelected = { _, _, _ -> },
onLibrarySelected = {},
onBrowseLibrariesClick = {}
)
}
}
internal fun homePreviewLibraries(): List<LibraryItem> {
return listOf(
LibraryItem(
id = JavaUuid.fromString("11111111-1111-1111-1111-111111111111"),
name = "Movies",
type = CollectionType.MOVIES,
posterUrl = "https://images.unsplash.com/photo-1517604931442-7e0c8ed2963c",
isEmpty = false
),
LibraryItem(
id = JavaUuid.fromString("22222222-2222-2222-2222-222222222222"),
name = "Series",
type = CollectionType.TVSHOWS,
posterUrl = "https://images.unsplash.com/photo-1489599849927-2ee91cede3ba",
isEmpty = false
)
)
}
internal fun homePreviewLibraryContent(): Map<UUID, List<PosterItem>> {
val movie = homePreviewMovie(
id = "33333333-3333-3333-3333-333333333333",
title = "Blade Runner 2049",
year = "2017",
runtime = "2h 44m",
rating = "16+",
format = "Dolby Vision",
synopsis = "A young blade runner uncovers a buried secret that pulls him toward a vanished legend.",
heroImageUrl = "https://images.unsplash.com/photo-1519608487953-e999c86e7455",
progress = 42.0,
watched = false
)
val secondMovie = homePreviewMovie(
id = "44444444-4444-4444-4444-444444444444",
title = "Arrival",
year = "2016",
runtime = "1h 56m",
rating = "12+",
format = "4K",
synopsis = "A linguist is recruited when mysterious spacecraft touch down around the world.",
heroImageUrl = "https://images.unsplash.com/photo-1500534314209-a25ddb2bd429",
progress = null,
watched = false
)
val series = homePreviewSeries()
val episode = homePreviewEpisode(
id = "66666666-6666-6666-6666-666666666666",
title = "Signals",
index = 2,
releaseDate = "2025",
runtime = "48m",
rating = "16+",
progress = 18.0,
watched = false,
heroImageUrl = "https://images.unsplash.com/photo-1520034475321-cbe63696469a",
synopsis = "Anomalies around the station point to a cover-up."
)
return mapOf(
homePreviewLibraries()[0].id to listOf(
PosterItem(type = BaseItemKind.MOVIE, movie = movie),
PosterItem(type = BaseItemKind.MOVIE, movie = secondMovie)
),
homePreviewLibraries()[1].id to listOf(
PosterItem(type = BaseItemKind.SERIES, series = series),
PosterItem(type = BaseItemKind.EPISODE, episode = episode)
)
)
}
internal fun homePreviewContinueWatching(): List<ContinueWatchingItem> {
return listOf(
ContinueWatchingItem(
type = BaseItemKind.MOVIE,
movie = homePreviewMovie(
id = "77777777-7777-7777-7777-777777777777",
title = "Dune: Part Two",
year = "2024",
runtime = "2h 46m",
rating = "13+",
format = "IMAX",
synopsis = "Paul Atreides unites with the Fremen while seeking justice and revenge.",
heroImageUrl = "https://images.unsplash.com/photo-1446776811953-b23d57bd21aa",
progress = 58.0,
watched = false
)
),
ContinueWatchingItem(
type = BaseItemKind.EPISODE,
episode = homePreviewEpisode(
id = "88888888-8888-8888-8888-888888888888",
title = "A Fresh Start",
index = 1,
releaseDate = "2025",
runtime = "51m",
rating = "16+",
progress = 23.0,
watched = false,
heroImageUrl = "https://images.unsplash.com/photo-1497032205916-ac775f0649ae",
synopsis = "A fractured crew tries to reassemble after a year apart."
)
)
)
}
internal fun homePreviewNextUp(): List<NextUpItem> {
return listOf(
NextUpItem(
episode = homePreviewEpisode(
id = "99999999-9999-9999-9999-999999999999",
title = "Return Window",
index = 3,
releaseDate = "2025",
runtime = "54m",
rating = "16+",
progress = null,
watched = false,
heroImageUrl = "https://images.unsplash.com/photo-1500530855697-b586d89ba3ee",
synopsis = "A high-risk jump changes the rules of the mission."
)
)
)
}
internal fun homePreviewMovie(
id: String,
title: String,
year: String,
runtime: String,
rating: String,
format: String,
synopsis: String,
heroImageUrl: String,
progress: Double?,
watched: Boolean
): Movie {
return Movie(
id = JavaUuid.fromString(id),
libraryId = JavaUuid.fromString("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"),
title = title,
progress = progress,
watched = watched,
year = year,
rating = rating,
runtime = runtime,
format = format,
synopsis = synopsis,
heroImageUrl = heroImageUrl,
audioTrack = "English 5.1",
subtitles = "English CC",
cast = emptyList()
)
}
internal fun homePreviewSeries(): Series {
return Series(
id = JavaUuid.fromString("bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"),
libraryId = JavaUuid.fromString("cccccccc-cccc-cccc-cccc-cccccccccccc"),
name = "Orbital",
synopsis = "A reluctant crew returns to a damaged station as political pressure mounts on Earth.",
year = "2025",
heroImageUrl = "https://images.unsplash.com/photo-1520034475321-cbe63696469a",
unwatchedEpisodeCount = 4,
seasonCount = 2,
seasons = emptyList(),
cast = emptyList()
)
}
internal fun homePreviewEpisode(
id: String,
title: String,
index: Int,
releaseDate: String,
runtime: String,
rating: String,
progress: Double?,
watched: Boolean,
heroImageUrl: String,
synopsis: String
): Episode {
return Episode(
id = JavaUuid.fromString(id),
seriesId = JavaUuid.fromString("dddddddd-dddd-dddd-dddd-dddddddddddd"),
seasonId = JavaUuid.fromString("eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee"),
index = index,
title = title,
synopsis = synopsis,
releaseDate = releaseDate,
rating = rating,
runtime = runtime,
progress = progress,
watched = watched,
format = "4K",
heroImageUrl = heroImageUrl,
cast = emptyList()
)
}

View File

@@ -25,9 +25,11 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import hu.bbara.purefin.common.ui.components.MediaProgressBar import hu.bbara.purefin.common.ui.components.MediaProgressBar
import hu.bbara.purefin.common.ui.components.PurefinAsyncImage import hu.bbara.purefin.common.ui.components.PurefinAsyncImage
import hu.bbara.purefin.core.data.image.JellyfinImageHelper
import hu.bbara.purefin.feature.shared.home.ContinueWatchingItem import hu.bbara.purefin.feature.shared.home.ContinueWatchingItem
import org.jellyfin.sdk.model.UUID import org.jellyfin.sdk.model.UUID
import org.jellyfin.sdk.model.api.BaseItemKind import org.jellyfin.sdk.model.api.BaseItemKind
import org.jellyfin.sdk.model.api.ImageType
@Composable @Composable
internal fun ContinueWatchingCard( internal fun ContinueWatchingCard(
@@ -51,7 +53,10 @@ internal fun ContinueWatchingCard(
else -> "" else -> ""
} }
val imageUrl = when (item.type) { val imageUrl = when (item.type) {
BaseItemKind.MOVIE -> item.movie?.heroImageUrl BaseItemKind.MOVIE -> JellyfinImageHelper.finishImageUrl(
prefixImageUrl = item.movie?.imageUrlPrefix,
imageType = ImageType.PRIMARY
)
BaseItemKind.EPISODE -> item.episode?.heroImageUrl BaseItemKind.EPISODE -> item.episode?.heroImageUrl
else -> null else -> null
} }

View File

@@ -433,10 +433,9 @@ class InMemoryAppContentRepository @Inject constructor(
runtime = formatRuntime(this.runTimeTicks), runtime = formatRuntime(this.runTimeTicks),
synopsis = this.overview ?: "No synopsis available", synopsis = this.overview ?: "No synopsis available",
format = container?.uppercase() ?: "VIDEO", format = container?.uppercase() ?: "VIDEO",
heroImageUrl = JellyfinImageHelper.toImageUrl( imageUrlPrefix = JellyfinImageHelper.toPrefixImageUrl(
url = serverUrl, url = serverUrl,
itemId = this.id, itemId = this.id,
type = ImageType.PRIMARY
), ),
subtitles = "ENG", subtitles = "ENG",
audioTrack = "ENG", audioTrack = "ENG",

View File

@@ -31,7 +31,10 @@ class JellyfinImageHelper {
.toString() .toString()
} }
fun finishImageUrl(prefixImageUrl: String, imageType: ImageType): String { fun finishImageUrl(prefixImageUrl: String?, imageType: ImageType): String {
if (prefixImageUrl.isNullOrEmpty()) {
return ""
}
return StringBuilder() return StringBuilder()
.append(prefixImageUrl) .append(prefixImageUrl)
.append(imageType.serialName) .append(imageType.serialName)

View File

@@ -1,7 +1,6 @@
package hu.bbara.purefin.core.data.room.entity package hu.bbara.purefin.core.data.room.entity
import androidx.room.Entity import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.Index import androidx.room.Index
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
import java.util.UUID import java.util.UUID
@@ -21,7 +20,7 @@ data class MovieEntity(
val runtime: String, val runtime: String,
val format: String, val format: String,
val synopsis: String, val synopsis: String,
val heroImageUrl: String, val imageUrlPrefix: String,
val audioTrack: String, val audioTrack: String,
val subtitles: String val subtitles: String
) )

View File

@@ -23,7 +23,7 @@ import hu.bbara.purefin.core.data.room.entity.SmartDownloadEntity
EpisodeEntity::class, EpisodeEntity::class,
SmartDownloadEntity::class, SmartDownloadEntity::class,
], ],
version = 6, version = 7,
exportSchema = false exportSchema = false
) )
@TypeConverters(UuidConverters::class) @TypeConverters(UuidConverters::class)

View File

@@ -208,7 +208,7 @@ class OfflineRoomMediaLocalDataSource(
runtime = runtime, runtime = runtime,
format = format, format = format,
synopsis = synopsis, synopsis = synopsis,
heroImageUrl = heroImageUrl, imageUrlPrefix = imageUrlPrefix,
audioTrack = audioTrack, audioTrack = audioTrack,
subtitles = subtitles subtitles = subtitles
) )
@@ -260,7 +260,7 @@ class OfflineRoomMediaLocalDataSource(
runtime = runtime, runtime = runtime,
format = format, format = format,
synopsis = synopsis, synopsis = synopsis,
heroImageUrl = heroImageUrl, imageUrlPrefix = imageUrlPrefix,
audioTrack = audioTrack, audioTrack = audioTrack,
subtitles = subtitles, subtitles = subtitles,
cast = emptyList() cast = emptyList()

View File

@@ -13,7 +13,7 @@ data class Movie(
val runtime: String, val runtime: String,
val format: String, val format: String,
val synopsis: String, val synopsis: String,
val heroImageUrl: String, val imageUrlPrefix: String,
val audioTrack: String, val audioTrack: String,
val subtitles: String, val subtitles: String,
val cast: List<CastMember> val cast: List<CastMember>

View File

@@ -124,6 +124,7 @@ class PlayerMediaRepository @Inject constructor(
} }
} }
@OptIn(UnstableApi::class)
private suspend fun buildOfflineMediaItem(mediaId: UUID): Pair<MediaItem, Long?>? { private suspend fun buildOfflineMediaItem(mediaId: UUID): Pair<MediaItem, Long?>? {
val download = downloadManager.downloadIndex.getDownload(mediaId.toString()) val download = downloadManager.downloadIndex.getDownload(mediaId.toString())
?.takeIf { it.state == Download.STATE_COMPLETED } ?.takeIf { it.state == Download.STATE_COMPLETED }
@@ -138,7 +139,7 @@ class PlayerMediaRepository @Inject constructor(
} }
val subtitle = episode?.let { episodeSubtitle(null, it.index) } val subtitle = episode?.let { episodeSubtitle(null, it.index) }
val artworkUrl = when { val artworkUrl = when {
movie != null -> movie.heroImageUrl movie != null -> JellyfinImageHelper.finishImageUrl(movie.imageUrlPrefix, ImageType.PRIMARY)
episode != null -> episode.heroImageUrl episode != null -> episode.heroImageUrl
else -> JellyfinImageHelper.toImageUrl(serverUrl, mediaId, ImageType.PRIMARY) else -> JellyfinImageHelper.toImageUrl(serverUrl, mediaId, ImageType.PRIMARY)
} }

View File

@@ -23,7 +23,6 @@ import hu.bbara.purefin.core.model.Season
import hu.bbara.purefin.core.model.Series import hu.bbara.purefin.core.model.Series
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
@@ -32,6 +31,7 @@ import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.jellyfin.sdk.model.api.BaseItemDto import org.jellyfin.sdk.model.api.BaseItemDto
import org.jellyfin.sdk.model.api.ImageType import org.jellyfin.sdk.model.api.ImageType
@@ -156,10 +156,9 @@ class MediaDownloadManager @Inject constructor(
runtime = formatRuntime(itemInfo.runTimeTicks), runtime = formatRuntime(itemInfo.runTimeTicks),
synopsis = itemInfo.overview ?: "No synopsis available", synopsis = itemInfo.overview ?: "No synopsis available",
format = itemInfo.container?.uppercase() ?: "VIDEO", format = itemInfo.container?.uppercase() ?: "VIDEO",
heroImageUrl = JellyfinImageHelper.toImageUrl( imageUrlPrefix = JellyfinImageHelper.toPrefixImageUrl(
url = serverUrl, url = serverUrl,
itemId = itemInfo.id, itemId = itemInfo.id
type = ImageType.PRIMARY
), ),
subtitles = "ENG", subtitles = "ENG",
audioTrack = "ENG", audioTrack = "ENG",

View File

@@ -4,6 +4,7 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import hu.bbara.purefin.core.data.OfflineMediaRepository import hu.bbara.purefin.core.data.OfflineMediaRepository
import hu.bbara.purefin.core.data.image.JellyfinImageHelper
import hu.bbara.purefin.core.data.navigation.MovieDto import hu.bbara.purefin.core.data.navigation.MovieDto
import hu.bbara.purefin.core.data.navigation.NavigationManager import hu.bbara.purefin.core.data.navigation.NavigationManager
import hu.bbara.purefin.core.data.navigation.Route import hu.bbara.purefin.core.data.navigation.Route
@@ -16,6 +17,7 @@ import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.jellyfin.sdk.model.UUID import org.jellyfin.sdk.model.UUID
import org.jellyfin.sdk.model.api.BaseItemKind import org.jellyfin.sdk.model.api.BaseItemKind
import org.jellyfin.sdk.model.api.ImageType
import javax.inject.Inject import javax.inject.Inject
@HiltViewModel @HiltViewModel
@@ -70,7 +72,7 @@ class DownloadsViewModel @Inject constructor(
contentId = contentId, contentId = contentId,
title = movie.title, title = movie.title,
subtitle = "", subtitle = "",
imageUrl = movie.heroImageUrl, imageUrl = JellyfinImageHelper.finishImageUrl(movie.imageUrlPrefix, ImageType.PRIMARY),
progress = progress progress = progress
) )
} else { } else {

View File

@@ -1,11 +1,13 @@
package hu.bbara.purefin.feature.shared.home package hu.bbara.purefin.feature.shared.home
import hu.bbara.purefin.core.data.image.JellyfinImageHelper
import hu.bbara.purefin.core.model.Episode import hu.bbara.purefin.core.model.Episode
import hu.bbara.purefin.core.model.Movie import hu.bbara.purefin.core.model.Movie
import hu.bbara.purefin.core.model.Series import hu.bbara.purefin.core.model.Series
import org.jellyfin.sdk.model.UUID import org.jellyfin.sdk.model.UUID
import org.jellyfin.sdk.model.api.BaseItemKind import org.jellyfin.sdk.model.api.BaseItemKind
import org.jellyfin.sdk.model.api.CollectionType import org.jellyfin.sdk.model.api.CollectionType
import org.jellyfin.sdk.model.api.ImageType
sealed interface SuggestedItem { sealed interface SuggestedItem {
@@ -74,7 +76,10 @@ data class SuggestedMovie (
override val id: UUID = movie.id, override val id: UUID = movie.id,
override val title: String = movie.title, override val title: String = movie.title,
override val description: String = movie.synopsis, override val description: String = movie.synopsis,
override val imageUrl: String = movie.heroImageUrl override val imageUrl: String = JellyfinImageHelper.finishImageUrl(
prefixImageUrl = movie.imageUrlPrefix,
imageType = ImageType.PRIMARY
)
) : SuggestedItem ) : SuggestedItem
data class ContinueWatchingItem( data class ContinueWatchingItem(
@@ -139,7 +144,7 @@ data class PosterItem(
else -> throw IllegalArgumentException("Invalid type: $type") else -> throw IllegalArgumentException("Invalid type: $type")
} }
val imageUrl: String = when (type) { val imageUrl: String = when (type) {
BaseItemKind.MOVIE -> movie!!.heroImageUrl BaseItemKind.MOVIE -> movie!!.imageUrlPrefix
BaseItemKind.EPISODE -> episode!!.heroImageUrl BaseItemKind.EPISODE -> episode!!.heroImageUrl
BaseItemKind.SERIES -> series!!.heroImageUrl BaseItemKind.SERIES -> series!!.heroImageUrl
else -> throw IllegalArgumentException("Invalid type: $type") else -> throw IllegalArgumentException("Invalid type: $type")