mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-05-16 23:12:12 +00:00
feat: add support for person and director (tmdb)
This commit is contained in:
parent
1781801ebe
commit
2771feab57
6 changed files with 232 additions and 3 deletions
|
|
@ -110,29 +110,38 @@
|
|||
<string name="collections_editor_tmdb_production_mode">Production</string>
|
||||
<string name="collections_editor_tmdb_network_mode">Network</string>
|
||||
<string name="collections_editor_tmdb_collection_mode">Collection</string>
|
||||
<string name="collections_editor_tmdb_person_mode">Person</string>
|
||||
<string name="collections_editor_tmdb_director_mode">Director</string>
|
||||
<string name="collections_editor_tmdb_custom_mode">Custom</string>
|
||||
<string name="collections_editor_tmdb_help_presets">Pick a ready-made source. You can edit or remove it after adding.</string>
|
||||
<string name="collections_editor_tmdb_help_list">Paste a public TMDB list URL or only the number from the URL.</string>
|
||||
<string name="collections_editor_tmdb_help_production">Search by studio name, or paste a TMDB company ID/URL and add it directly.</string>
|
||||
<string name="collections_editor_tmdb_help_network">Enter a network ID. Common networks are available in Presets and quick filters.</string>
|
||||
<string name="collections_editor_tmdb_help_collection">Search a movie collection name or paste the collection ID from TMDB.</string>
|
||||
<string name="collections_editor_tmdb_help_person">Enter a TMDB person ID or URL to build a row from cast credits.</string>
|
||||
<string name="collections_editor_tmdb_help_director">Enter a TMDB person ID or URL to build a row from director credits.</string>
|
||||
<string name="collections_editor_tmdb_help_discover">Build a live TMDB row using optional filters. Leave fields empty when you do not need that filter.</string>
|
||||
<string name="collections_editor_tmdb_public_list">Public TMDB list</string>
|
||||
<string name="collections_editor_tmdb_network_id">Network ID</string>
|
||||
<string name="collections_editor_tmdb_collection_id">Collection ID</string>
|
||||
<string name="collections_editor_tmdb_person_id">Person ID</string>
|
||||
<string name="collections_editor_tmdb_company_search">Production company name, ID, or URL</string>
|
||||
<string name="collections_editor_tmdb_id_or_url">TMDB ID or URL</string>
|
||||
<string name="collections_editor_tmdb_list_placeholder">https://www.themoviedb.org/list/8504994 or 8504994</string>
|
||||
<string name="collections_editor_tmdb_network_placeholder">213 for Netflix, 49 for HBO, 2739 for Disney+</string>
|
||||
<string name="collections_editor_tmdb_collection_placeholder">10 for Star Wars Collection</string>
|
||||
<string name="collections_editor_tmdb_company_placeholder">Marvel Studios, 420, or company URL</string>
|
||||
<string name="collections_editor_tmdb_person_placeholder">31 for Tom Hanks, or person URL</string>
|
||||
<string name="collections_editor_tmdb_search_helper">Examples: Marvel Studios, 420, or https://www.themoviedb.org/company/420.</string>
|
||||
<string name="collections_editor_tmdb_collection_helper">Example: Star Wars Collection, Harry Potter Collection, or a collection URL.</string>
|
||||
<string name="collections_editor_tmdb_network_helper">Example IDs: Netflix 213, HBO 49, Disney+ 2739.</string>
|
||||
<string name="collections_editor_tmdb_list_helper">Example: https://www.themoviedb.org/list/8504994 or 8504994.</string>
|
||||
<string name="collections_editor_tmdb_person_helper">Example: https://www.themoviedb.org/person/31-tom-hanks or 31.</string>
|
||||
<string name="collections_editor_tmdb_display_title">Display title</string>
|
||||
<string name="collections_editor_tmdb_title_helper">Shown as the row/tab name. If blank, Nuvio creates one from the source.</string>
|
||||
<string name="collections_editor_tmdb_title_placeholder">Marvel Movies, Netflix Originals, Pixar</string>
|
||||
<string name="collections_editor_tmdb_person_title_placeholder">Tom Hanks Movies, Favorite Actors</string>
|
||||
<string name="collections_editor_tmdb_director_title_placeholder">Christopher Nolan Movies, Favorite Directors</string>
|
||||
<string name="collections_editor_tmdb_discover_title_placeholder">Best Action Movies, Korean Dramas, 2024 Animation</string>
|
||||
<string name="collections_editor_tmdb_search_results">Search Results</string>
|
||||
<string name="collections_editor_tmdb_collection">TMDB Collection</string>
|
||||
|
|
@ -212,6 +221,7 @@
|
|||
<string name="collections_editor_tmdb_network_disney_plus">Disney+</string>
|
||||
<string name="collections_editor_tmdb_network_prime_video">Prime Video</string>
|
||||
<string name="collections_editor_tmdb_network_hulu">Hulu</string>
|
||||
<string name="collections_editor_tmdb_sort_original">Original</string>
|
||||
<string name="collections_editor_tmdb_sort_popular">Popular</string>
|
||||
<string name="collections_editor_tmdb_sort_top_rated">Top Rated</string>
|
||||
<string name="collections_editor_tmdb_sort_recent">Recent</string>
|
||||
|
|
@ -219,6 +229,8 @@
|
|||
<string name="collections_editor_tmdb_subtitle_movie_collection">TMDB Movie Collection</string>
|
||||
<string name="collections_editor_tmdb_subtitle_production">Production</string>
|
||||
<string name="collections_editor_tmdb_subtitle_network">Network</string>
|
||||
<string name="collections_editor_tmdb_subtitle_person">Person</string>
|
||||
<string name="collections_editor_tmdb_subtitle_director">Director</string>
|
||||
<string name="collections_editor_tmdb_subtitle_discover">TMDB Discover</string>
|
||||
<string name="collections_empty_subtitle">Create one to organize your catalogs.</string>
|
||||
<string name="collections_empty_title">No collections yet</string>
|
||||
|
|
|
|||
|
|
@ -46,6 +46,8 @@ enum class TmdbBuilderMode {
|
|||
PRODUCTION,
|
||||
NETWORK,
|
||||
COLLECTION,
|
||||
PERSON,
|
||||
DIRECTOR,
|
||||
DISCOVER,
|
||||
}
|
||||
|
||||
|
|
@ -340,9 +342,15 @@ object CollectionEditorRepository {
|
|||
} else {
|
||||
_uiState.value.tmdbMediaType
|
||||
}
|
||||
val sortBy = when (mode) {
|
||||
TmdbBuilderMode.LIST,
|
||||
TmdbBuilderMode.COLLECTION -> TmdbCollectionSort.ORIGINAL.value
|
||||
else -> TmdbCollectionSort.POPULAR_DESC.value
|
||||
}
|
||||
_uiState.value = _uiState.value.copy(
|
||||
tmdbBuilderMode = mode,
|
||||
tmdbMediaType = mediaType,
|
||||
tmdbSortBy = sortBy,
|
||||
tmdbMediaBoth = if (
|
||||
mode == TmdbBuilderMode.NETWORK ||
|
||||
mode == TmdbBuilderMode.LIST ||
|
||||
|
|
@ -459,6 +467,8 @@ object CollectionEditorRepository {
|
|||
TmdbBuilderMode.COLLECTION -> TmdbCollectionSourceType.COLLECTION
|
||||
TmdbBuilderMode.PRODUCTION -> TmdbCollectionSourceType.COMPANY
|
||||
TmdbBuilderMode.NETWORK -> TmdbCollectionSourceType.NETWORK
|
||||
TmdbBuilderMode.PERSON -> TmdbCollectionSourceType.PERSON
|
||||
TmdbBuilderMode.DIRECTOR -> TmdbCollectionSourceType.DIRECTOR
|
||||
TmdbBuilderMode.DISCOVER -> TmdbCollectionSourceType.DISCOVER
|
||||
}
|
||||
val id = TmdbCollectionSourceResolver.parseTmdbId(state.tmdbInput)
|
||||
|
|
@ -473,6 +483,8 @@ object CollectionEditorRepository {
|
|||
TmdbCollectionSourceType.COLLECTION -> "TMDB Collection ${id ?: ""}".trim()
|
||||
TmdbCollectionSourceType.COMPANY -> "TMDB Production ${id ?: ""}".trim()
|
||||
TmdbCollectionSourceType.NETWORK -> "TMDB Network ${id ?: ""}".trim()
|
||||
TmdbCollectionSourceType.PERSON -> "TMDB Person ${id ?: ""}".trim()
|
||||
TmdbCollectionSourceType.DIRECTOR -> "TMDB Director ${id ?: ""}".trim()
|
||||
TmdbCollectionSourceType.DISCOVER -> "TMDB Discover"
|
||||
}
|
||||
}
|
||||
|
|
@ -561,6 +573,8 @@ private val coverMetadataSourceTypes = setOf(
|
|||
TmdbCollectionSourceType.COLLECTION,
|
||||
TmdbCollectionSourceType.COMPANY,
|
||||
TmdbCollectionSourceType.NETWORK,
|
||||
TmdbCollectionSourceType.PERSON,
|
||||
TmdbCollectionSourceType.DIRECTOR,
|
||||
)
|
||||
|
||||
private fun CollectionCatalogSource.toCollectionSource(): CollectionSource =
|
||||
|
|
@ -591,6 +605,8 @@ private fun selectedMediaTypes(
|
|||
): List<TmdbCollectionMediaType> =
|
||||
when (sourceType) {
|
||||
TmdbCollectionSourceType.COMPANY,
|
||||
TmdbCollectionSourceType.PERSON,
|
||||
TmdbCollectionSourceType.DIRECTOR,
|
||||
TmdbCollectionSourceType.DISCOVER -> if (state.tmdbMediaBoth) {
|
||||
listOf(TmdbCollectionMediaType.MOVIE, TmdbCollectionMediaType.TV)
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -897,13 +897,19 @@ private fun TmdbSourcePickerScreen(
|
|||
TmdbBuilderMode.COLLECTION -> TmdbCollectionSourceType.COLLECTION
|
||||
TmdbBuilderMode.PRODUCTION -> TmdbCollectionSourceType.COMPANY
|
||||
TmdbBuilderMode.NETWORK -> TmdbCollectionSourceType.NETWORK
|
||||
TmdbBuilderMode.PERSON -> TmdbCollectionSourceType.PERSON
|
||||
TmdbBuilderMode.DIRECTOR -> TmdbCollectionSourceType.DIRECTOR
|
||||
TmdbBuilderMode.DISCOVER -> TmdbCollectionSourceType.DISCOVER
|
||||
}
|
||||
val requiresId = sourceType != TmdbCollectionSourceType.DISCOVER
|
||||
val showMediaControls = state.tmdbBuilderMode == TmdbBuilderMode.PRODUCTION ||
|
||||
state.tmdbBuilderMode == TmdbBuilderMode.PERSON ||
|
||||
state.tmdbBuilderMode == TmdbBuilderMode.DIRECTOR ||
|
||||
state.tmdbBuilderMode == TmdbBuilderMode.DISCOVER
|
||||
val showSortControls = state.tmdbBuilderMode == TmdbBuilderMode.PRODUCTION ||
|
||||
state.tmdbBuilderMode == TmdbBuilderMode.NETWORK ||
|
||||
state.tmdbBuilderMode == TmdbBuilderMode.PERSON ||
|
||||
state.tmdbBuilderMode == TmdbBuilderMode.DIRECTOR ||
|
||||
state.tmdbBuilderMode == TmdbBuilderMode.DISCOVER
|
||||
val showFilterControls = state.tmdbBuilderMode == TmdbBuilderMode.DISCOVER
|
||||
|
||||
|
|
@ -1892,6 +1898,8 @@ private fun tmdbBuilderModeLabel(mode: TmdbBuilderMode): String =
|
|||
TmdbBuilderMode.PRODUCTION -> stringResource(Res.string.collections_editor_tmdb_production_mode)
|
||||
TmdbBuilderMode.NETWORK -> stringResource(Res.string.collections_editor_tmdb_network_mode)
|
||||
TmdbBuilderMode.COLLECTION -> stringResource(Res.string.collections_editor_tmdb_collection_mode)
|
||||
TmdbBuilderMode.PERSON -> stringResource(Res.string.collections_editor_tmdb_person_mode)
|
||||
TmdbBuilderMode.DIRECTOR -> stringResource(Res.string.collections_editor_tmdb_director_mode)
|
||||
TmdbBuilderMode.DISCOVER -> stringResource(Res.string.collections_editor_tmdb_custom_mode)
|
||||
}
|
||||
|
||||
|
|
@ -1903,6 +1911,8 @@ private fun tmdbModeHelpText(mode: TmdbBuilderMode): String =
|
|||
TmdbBuilderMode.PRODUCTION -> stringResource(Res.string.collections_editor_tmdb_help_production)
|
||||
TmdbBuilderMode.NETWORK -> stringResource(Res.string.collections_editor_tmdb_help_network)
|
||||
TmdbBuilderMode.COLLECTION -> stringResource(Res.string.collections_editor_tmdb_help_collection)
|
||||
TmdbBuilderMode.PERSON -> stringResource(Res.string.collections_editor_tmdb_help_person)
|
||||
TmdbBuilderMode.DIRECTOR -> stringResource(Res.string.collections_editor_tmdb_help_director)
|
||||
TmdbBuilderMode.DISCOVER -> stringResource(Res.string.collections_editor_tmdb_help_discover)
|
||||
}
|
||||
|
||||
|
|
@ -1913,6 +1923,8 @@ private fun tmdbInputLabel(mode: TmdbBuilderMode): String =
|
|||
TmdbBuilderMode.NETWORK -> stringResource(Res.string.collections_editor_tmdb_network_id)
|
||||
TmdbBuilderMode.COLLECTION -> stringResource(Res.string.collections_editor_tmdb_collection_id)
|
||||
TmdbBuilderMode.PRODUCTION -> stringResource(Res.string.collections_editor_tmdb_company_search)
|
||||
TmdbBuilderMode.PERSON,
|
||||
TmdbBuilderMode.DIRECTOR -> stringResource(Res.string.collections_editor_tmdb_person_id)
|
||||
else -> stringResource(Res.string.collections_editor_tmdb_id_or_url)
|
||||
}
|
||||
|
||||
|
|
@ -1923,6 +1935,8 @@ private fun tmdbInputPlaceholder(mode: TmdbBuilderMode): String =
|
|||
TmdbBuilderMode.NETWORK -> stringResource(Res.string.collections_editor_tmdb_network_placeholder)
|
||||
TmdbBuilderMode.COLLECTION -> stringResource(Res.string.collections_editor_tmdb_collection_placeholder)
|
||||
TmdbBuilderMode.PRODUCTION -> stringResource(Res.string.collections_editor_tmdb_company_placeholder)
|
||||
TmdbBuilderMode.PERSON,
|
||||
TmdbBuilderMode.DIRECTOR -> stringResource(Res.string.collections_editor_tmdb_person_placeholder)
|
||||
else -> stringResource(Res.string.collections_editor_tmdb_id_or_url)
|
||||
}
|
||||
|
||||
|
|
@ -1933,6 +1947,8 @@ private fun tmdbInputHelper(mode: TmdbBuilderMode): String =
|
|||
TmdbBuilderMode.COLLECTION -> stringResource(Res.string.collections_editor_tmdb_collection_helper)
|
||||
TmdbBuilderMode.NETWORK -> stringResource(Res.string.collections_editor_tmdb_network_helper)
|
||||
TmdbBuilderMode.LIST -> stringResource(Res.string.collections_editor_tmdb_list_helper)
|
||||
TmdbBuilderMode.PERSON,
|
||||
TmdbBuilderMode.DIRECTOR -> stringResource(Res.string.collections_editor_tmdb_person_helper)
|
||||
else -> ""
|
||||
}
|
||||
|
||||
|
|
@ -1940,12 +1956,15 @@ private fun tmdbInputHelper(mode: TmdbBuilderMode): String =
|
|||
private fun tmdbTitlePlaceholder(mode: TmdbBuilderMode): String =
|
||||
when (mode) {
|
||||
TmdbBuilderMode.DISCOVER -> stringResource(Res.string.collections_editor_tmdb_discover_title_placeholder)
|
||||
TmdbBuilderMode.PERSON -> stringResource(Res.string.collections_editor_tmdb_person_title_placeholder)
|
||||
TmdbBuilderMode.DIRECTOR -> stringResource(Res.string.collections_editor_tmdb_director_title_placeholder)
|
||||
else -> stringResource(Res.string.collections_editor_tmdb_title_placeholder)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun tmdbSortLabel(sort: TmdbCollectionSort): String =
|
||||
when (sort) {
|
||||
TmdbCollectionSort.ORIGINAL -> stringResource(Res.string.collections_editor_tmdb_sort_original)
|
||||
TmdbCollectionSort.POPULAR_DESC -> stringResource(Res.string.collections_editor_tmdb_sort_popular)
|
||||
TmdbCollectionSort.VOTE_AVERAGE_DESC -> stringResource(Res.string.collections_editor_tmdb_sort_top_rated)
|
||||
TmdbCollectionSort.RELEASE_DATE_DESC -> stringResource(Res.string.collections_editor_tmdb_sort_recent)
|
||||
|
|
@ -1979,6 +1998,16 @@ private fun tmdbSourceSubtitle(source: CollectionSource): String {
|
|||
stringResource(Res.string.collections_editor_tmdb_series),
|
||||
sort,
|
||||
).joinToString(" • ")
|
||||
TmdbCollectionSourceType.PERSON -> listOf(
|
||||
stringResource(Res.string.collections_editor_tmdb_subtitle_person),
|
||||
media,
|
||||
sort,
|
||||
).joinToString(" • ")
|
||||
TmdbCollectionSourceType.DIRECTOR -> listOf(
|
||||
stringResource(Res.string.collections_editor_tmdb_subtitle_director),
|
||||
media,
|
||||
sort,
|
||||
).joinToString(" • ")
|
||||
TmdbCollectionSourceType.DISCOVER -> listOf(
|
||||
stringResource(Res.string.collections_editor_tmdb_subtitle_discover),
|
||||
media,
|
||||
|
|
|
|||
|
|
@ -69,6 +69,8 @@ enum class TmdbCollectionSourceType {
|
|||
COMPANY,
|
||||
NETWORK,
|
||||
DISCOVER,
|
||||
PERSON,
|
||||
DIRECTOR,
|
||||
}
|
||||
|
||||
@Serializable
|
||||
|
|
@ -86,6 +88,7 @@ enum class TmdbCollectionMediaType(val value: String) {
|
|||
}
|
||||
|
||||
enum class TmdbCollectionSort(val value: String) {
|
||||
ORIGINAL("original"),
|
||||
POPULAR_DESC("popularity.desc"),
|
||||
VOTE_AVERAGE_DESC("vote_average.desc"),
|
||||
RELEASE_DATE_DESC("primary_release_date.desc"),
|
||||
|
|
|
|||
|
|
@ -140,7 +140,11 @@ object FolderDetailRepository {
|
|||
source = source,
|
||||
type = type,
|
||||
catalogId = tmdbCatalogId(source),
|
||||
supportsPagination = source.tmdbSourceType != TmdbCollectionSourceType.COLLECTION.name,
|
||||
supportsPagination = source.tmdbSourceType !in setOf(
|
||||
TmdbCollectionSourceType.COLLECTION.name,
|
||||
TmdbCollectionSourceType.PERSON.name,
|
||||
TmdbCollectionSourceType.DIRECTOR.name,
|
||||
),
|
||||
isLoading = true,
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -29,6 +29,8 @@ object TmdbCollectionSourceResolver {
|
|||
when (sourceType) {
|
||||
TmdbCollectionSourceType.LIST -> resolveList(source, apiKey, language, page)
|
||||
TmdbCollectionSourceType.COLLECTION -> resolveCollection(source, apiKey, language)
|
||||
TmdbCollectionSourceType.PERSON,
|
||||
TmdbCollectionSourceType.DIRECTOR -> resolvePersonCredits(source, apiKey, language)
|
||||
TmdbCollectionSourceType.COMPANY,
|
||||
TmdbCollectionSourceType.NETWORK,
|
||||
TmdbCollectionSourceType.DISCOVER -> resolveDiscover(source, apiKey, language, page)
|
||||
|
|
@ -85,6 +87,19 @@ object TmdbCollectionSourceResolver {
|
|||
)
|
||||
}
|
||||
|
||||
TmdbCollectionSourceType.PERSON,
|
||||
TmdbCollectionSourceType.DIRECTOR -> {
|
||||
val body = fetch<TmdbPersonResponse>(
|
||||
endpoint = "person/$id",
|
||||
apiKey = apiKey,
|
||||
query = mapOf("language" to language),
|
||||
) ?: error("TMDB person not found")
|
||||
TmdbSourceImportMetadata(
|
||||
title = body.name?.takeIf { it.isNotBlank() },
|
||||
coverImageUrl = imageUrl(body.profilePath, "w500"),
|
||||
)
|
||||
}
|
||||
|
||||
TmdbCollectionSourceType.DISCOVER -> TmdbSourceImportMetadata(title = "TMDB Discover")
|
||||
}
|
||||
}
|
||||
|
|
@ -153,7 +168,7 @@ object TmdbCollectionSourceResolver {
|
|||
fun parseTmdbId(input: String): Int? {
|
||||
val trimmed = input.trim()
|
||||
trimmed.toIntOrNull()?.let { return it }
|
||||
return Regex("""(?:list|collection|company|network)/(\d+)""")
|
||||
return Regex("""(?:list|collection|company|network|person)/(\d+)""")
|
||||
.find(trimmed)
|
||||
?.groupValues
|
||||
?.getOrNull(1)
|
||||
|
|
@ -193,6 +208,7 @@ object TmdbCollectionSourceResolver {
|
|||
) ?: error("TMDB list not found")
|
||||
val items = body.items.orEmpty()
|
||||
.mapNotNull { it.toPreview() }
|
||||
.sortedFor(source.sortBy)
|
||||
.distinctBy { "${it.type}:${it.id}" }
|
||||
return CatalogPage(
|
||||
items = items,
|
||||
|
|
@ -213,12 +229,35 @@ object TmdbCollectionSourceResolver {
|
|||
query = mapOf("language" to language),
|
||||
) ?: error("TMDB collection not found")
|
||||
val items = body.parts.orEmpty()
|
||||
.sortedBy { it.releaseDate ?: "9999" }
|
||||
.mapNotNull { it.toPreview(TmdbCollectionMediaType.MOVIE) }
|
||||
.sortedFor(source.sortBy)
|
||||
.distinctBy { it.id }
|
||||
return CatalogPage(items = items, rawItemCount = items.size, nextSkip = null)
|
||||
}
|
||||
|
||||
private suspend fun resolvePersonCredits(
|
||||
source: CollectionSource,
|
||||
apiKey: String,
|
||||
language: String,
|
||||
): CatalogPage {
|
||||
val id = source.tmdbId ?: error("Missing TMDB person ID")
|
||||
val mediaType = source.tmdbMediaType()
|
||||
val body = fetch<TmdbPersonCreditsResponse>(
|
||||
endpoint = "person/$id/combined_credits",
|
||||
apiKey = apiKey,
|
||||
query = mapOf("language" to language),
|
||||
) ?: error("TMDB person credits not found")
|
||||
val items = when (source.tmdbType()) {
|
||||
TmdbCollectionSourceType.DIRECTOR -> body.crew.orEmpty()
|
||||
.filter { it.job.equals("Director", ignoreCase = true) }
|
||||
.mapNotNull { it.toPreview(mediaType) }
|
||||
else -> body.cast.orEmpty().mapNotNull { it.toPreview(mediaType) }
|
||||
}
|
||||
.distinctBy { "${it.type}:${it.id}" }
|
||||
.sortedFor(source.sortBy)
|
||||
return CatalogPage(items = items, rawItemCount = items.size, nextSkip = null)
|
||||
}
|
||||
|
||||
private suspend fun resolveDiscover(
|
||||
source: CollectionSource,
|
||||
apiKey: String,
|
||||
|
|
@ -312,6 +351,21 @@ object TmdbCollectionSourceResolver {
|
|||
}.getOrNull()
|
||||
}
|
||||
|
||||
private fun List<MetaPreview>.sortedFor(sortBy: String?): List<MetaPreview> =
|
||||
when (sortBy) {
|
||||
TmdbCollectionSort.ORIGINAL.value -> this
|
||||
TmdbCollectionSort.VOTE_AVERAGE_DESC.value -> sortedWith(
|
||||
compareByDescending<MetaPreview> { it.imdbRating?.toDoubleOrNull() ?: -1.0 }
|
||||
.thenByDescending { it.rawReleaseDate ?: it.releaseInfo.orEmpty() },
|
||||
)
|
||||
TmdbCollectionSort.RELEASE_DATE_DESC.value,
|
||||
TmdbCollectionSort.FIRST_AIR_DATE_DESC.value -> sortedByDescending { it.rawReleaseDate ?: it.releaseInfo.orEmpty() }
|
||||
TmdbCollectionSort.POPULAR_DESC.value,
|
||||
null,
|
||||
"" -> this
|
||||
else -> this
|
||||
}
|
||||
|
||||
private fun TmdbListItem.toPreview(): MetaPreview? {
|
||||
val media = mediaType?.lowercase()
|
||||
val contentType = if (media == "tv") TmdbCollectionMediaType.TV else TmdbCollectionMediaType.MOVIE
|
||||
|
|
@ -362,6 +416,62 @@ object TmdbCollectionSourceResolver {
|
|||
)
|
||||
}
|
||||
|
||||
private fun TmdbPersonCreditCast.toPreview(mediaType: TmdbCollectionMediaType): MetaPreview? {
|
||||
if (!matchesMediaType(mediaType, this.mediaType)) return null
|
||||
val title = title?.takeIf { it.isNotBlank() }
|
||||
?: name?.takeIf { it.isNotBlank() }
|
||||
?: originalTitle?.takeIf { it.isNotBlank() }
|
||||
?: originalName?.takeIf { it.isNotBlank() }
|
||||
?: return null
|
||||
return MetaPreview(
|
||||
id = "tmdb:$id",
|
||||
type = if (mediaType == TmdbCollectionMediaType.TV) "series" else "movie",
|
||||
name = title,
|
||||
poster = imageUrl(posterPath, "w500") ?: imageUrl(backdropPath, "w780"),
|
||||
banner = imageUrl(backdropPath, "w1280"),
|
||||
posterShape = PosterShape.Poster,
|
||||
description = overview?.takeIf { it.isNotBlank() },
|
||||
releaseInfo = when (mediaType) {
|
||||
TmdbCollectionMediaType.MOVIE -> releaseDate?.take(4)
|
||||
TmdbCollectionMediaType.TV -> firstAirDate?.take(4)
|
||||
},
|
||||
rawReleaseDate = when (mediaType) {
|
||||
TmdbCollectionMediaType.MOVIE -> releaseDate
|
||||
TmdbCollectionMediaType.TV -> firstAirDate
|
||||
},
|
||||
popularity = popularity,
|
||||
imdbRating = voteAverage?.let { ((it * 10).roundToInt() / 10.0).toString() },
|
||||
)
|
||||
}
|
||||
|
||||
private fun TmdbPersonCreditCrew.toPreview(mediaType: TmdbCollectionMediaType): MetaPreview? {
|
||||
if (!matchesMediaType(mediaType, this.mediaType)) return null
|
||||
val title = title?.takeIf { it.isNotBlank() }
|
||||
?: name?.takeIf { it.isNotBlank() }
|
||||
?: originalTitle?.takeIf { it.isNotBlank() }
|
||||
?: originalName?.takeIf { it.isNotBlank() }
|
||||
?: return null
|
||||
return MetaPreview(
|
||||
id = "tmdb:$id",
|
||||
type = if (mediaType == TmdbCollectionMediaType.TV) "series" else "movie",
|
||||
name = title,
|
||||
poster = imageUrl(posterPath, "w500") ?: imageUrl(backdropPath, "w780"),
|
||||
banner = imageUrl(backdropPath, "w1280"),
|
||||
posterShape = PosterShape.Poster,
|
||||
description = overview?.takeIf { it.isNotBlank() },
|
||||
releaseInfo = when (mediaType) {
|
||||
TmdbCollectionMediaType.MOVIE -> releaseDate?.take(4)
|
||||
TmdbCollectionMediaType.TV -> firstAirDate?.take(4)
|
||||
},
|
||||
rawReleaseDate = when (mediaType) {
|
||||
TmdbCollectionMediaType.MOVIE -> releaseDate
|
||||
TmdbCollectionMediaType.TV -> firstAirDate
|
||||
},
|
||||
popularity = popularity,
|
||||
imdbRating = voteAverage?.let { ((it * 10).roundToInt() / 10.0).toString() },
|
||||
)
|
||||
}
|
||||
|
||||
private fun CollectionSource.tmdbType(): TmdbCollectionSourceType =
|
||||
tmdbSourceType
|
||||
?.let { raw -> runCatching { TmdbCollectionSourceType.valueOf(raw.uppercase()) }.getOrNull() }
|
||||
|
|
@ -370,6 +480,12 @@ object TmdbCollectionSourceResolver {
|
|||
private fun CollectionSource.tmdbMediaType(): TmdbCollectionMediaType =
|
||||
TmdbCollectionMediaType.fromString(mediaType)
|
||||
|
||||
private fun matchesMediaType(expected: TmdbCollectionMediaType, actual: String?): Boolean =
|
||||
when (expected) {
|
||||
TmdbCollectionMediaType.MOVIE -> actual == "movie"
|
||||
TmdbCollectionMediaType.TV -> actual == "tv"
|
||||
}
|
||||
|
||||
private fun company(title: String, id: Int) = CollectionSource(
|
||||
provider = "tmdb",
|
||||
tmdbSourceType = TmdbCollectionSourceType.COMPANY.name,
|
||||
|
|
@ -391,6 +507,7 @@ object TmdbCollectionSourceResolver {
|
|||
private fun movieSort(sortBy: String?): String =
|
||||
when (sortBy) {
|
||||
TmdbCollectionSort.FIRST_AIR_DATE_DESC.value -> TmdbCollectionSort.RELEASE_DATE_DESC.value
|
||||
TmdbCollectionSort.ORIGINAL.value -> TmdbCollectionSort.POPULAR_DESC.value
|
||||
null, "" -> TmdbCollectionSort.POPULAR_DESC.value
|
||||
else -> sortBy
|
||||
}
|
||||
|
|
@ -398,6 +515,7 @@ object TmdbCollectionSourceResolver {
|
|||
private fun tvSort(sortBy: String?): String =
|
||||
when (sortBy) {
|
||||
TmdbCollectionSort.RELEASE_DATE_DESC.value -> TmdbCollectionSort.FIRST_AIR_DATE_DESC.value
|
||||
TmdbCollectionSort.ORIGINAL.value -> TmdbCollectionSort.POPULAR_DESC.value
|
||||
null, "" -> TmdbCollectionSort.POPULAR_DESC.value
|
||||
else -> sortBy
|
||||
}
|
||||
|
|
@ -449,6 +567,12 @@ private data class TmdbNetworkResponse(
|
|||
@SerialName("logo_path") val logoPath: String? = null,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
private data class TmdbPersonResponse(
|
||||
val name: String? = null,
|
||||
@SerialName("profile_path") val profilePath: String? = null,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class TmdbCompanySearchResult(
|
||||
val id: Int,
|
||||
|
|
@ -496,6 +620,47 @@ private data class TmdbGenreItem(
|
|||
val name: String,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
private data class TmdbPersonCreditsResponse(
|
||||
val cast: List<TmdbPersonCreditCast>? = null,
|
||||
val crew: List<TmdbPersonCreditCrew>? = null,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
private data class TmdbPersonCreditCast(
|
||||
val id: Int,
|
||||
@SerialName("media_type") val mediaType: String? = null,
|
||||
val title: String? = null,
|
||||
val name: String? = null,
|
||||
@SerialName("original_title") val originalTitle: String? = null,
|
||||
@SerialName("original_name") val originalName: String? = null,
|
||||
val overview: String? = null,
|
||||
@SerialName("poster_path") val posterPath: String? = null,
|
||||
@SerialName("backdrop_path") val backdropPath: String? = null,
|
||||
@SerialName("release_date") val releaseDate: String? = null,
|
||||
@SerialName("first_air_date") val firstAirDate: String? = null,
|
||||
@SerialName("vote_average") val voteAverage: Double? = null,
|
||||
val popularity: Double? = null,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
private data class TmdbPersonCreditCrew(
|
||||
val id: Int,
|
||||
@SerialName("media_type") val mediaType: String? = null,
|
||||
val title: String? = null,
|
||||
val name: String? = null,
|
||||
@SerialName("original_title") val originalTitle: String? = null,
|
||||
@SerialName("original_name") val originalName: String? = null,
|
||||
val overview: String? = null,
|
||||
@SerialName("poster_path") val posterPath: String? = null,
|
||||
@SerialName("backdrop_path") val backdropPath: String? = null,
|
||||
@SerialName("release_date") val releaseDate: String? = null,
|
||||
@SerialName("first_air_date") val firstAirDate: String? = null,
|
||||
val job: String? = null,
|
||||
@SerialName("vote_average") val voteAverage: Double? = null,
|
||||
val popularity: Double? = null,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
private data class TmdbListItem(
|
||||
val id: Int,
|
||||
|
|
|
|||
Loading…
Reference in a new issue