Skip to content

Commit

Permalink
ItemAddShowViewHolder: build poster URL before passing to adapters
Browse files Browse the repository at this point in the history
  • Loading branch information
UweTrottmann committed Oct 17, 2024
1 parent a793a8f commit a49a1d8
Show file tree
Hide file tree
Showing 7 changed files with 139 additions and 125 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ abstract class BaseShowResultsDataSource(
nextKey = null
)
} else {
val searchResults = SearchTools.mapTvShowsToSearchResults(languageCode, shows)
SearchTools.markLocalShowsAsAddedAndPreferLocalPoster(context, searchResults)
val searchResults = TmdbSearchResultMapper(context, languageCode)
.mapToSearchResults(shows)
LoadResult.Page(
data = searchResults,
prevKey = null, // Only paging forward.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,18 +73,10 @@ class ItemAddShowViewHolder(
isVisible = true
}

// image
// If item is provided from Trakt source, it does not provide images,
// so need to resolve them.
val posterUrl = ImageTools.posterUrlOrResolve(
item.posterPath,
item.tmdbId,
item.language,
context
)
// poster
ImageTools.loadShowPosterUrlResizeCrop(
context, binding.imageViewAddPoster,
posterUrl
item.posterPath // actually the poster URL
)

// context/long press listener and more options button
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2019-2024 Uwe Trottmann

package com.battlelancer.seriesguide.shows.search.discover

import android.content.Context
import com.battlelancer.seriesguide.SgApp
import com.battlelancer.seriesguide.util.ImageTools
import com.uwetrottmann.tmdb2.entities.BaseTvShow
import com.uwetrottmann.trakt5.entities.BaseShow

/**
* See [mapToSearchResults].
*/
abstract class SearchResultMapper<SHOW>(
private val context: Context
) {

/**
* Maps to a list of [SearchResult] with [mapToSearchResult].
*
* For added shows, changes [SearchResult.state] to [SearchResult.STATE_ADDED] and if available
* uses the poster path of that show (which might differ if the added show is set to a different
* language).
*
* Builds the final poster URL with [buildPosterUrl].
*/
fun mapToSearchResults(shows: List<SHOW>): List<SearchResult> {
val localShowsToPoster =
SgApp.getServicesComponent(context).showTools().getTmdbIdsToPoster()
return shows.mapNotNull { show ->
val searchResult = mapToSearchResult(show)
?: return@mapNotNull null

if (localShowsToPoster.indexOfKey(searchResult.tmdbId) >= 0) {
// Is already in local database.
searchResult.state = SearchResult.STATE_ADDED
// Use the poster already fetched for it.
val posterOrNull = localShowsToPoster[searchResult.tmdbId]
if (posterOrNull != null) {
searchResult.posterPath = posterOrNull
}
}

// It may take some time to build the image cache URL, so do this here instead of when
// binding to the view.
searchResult.posterPath = buildPosterUrl(searchResult)

searchResult
}
}

abstract fun mapToSearchResult(show: SHOW): SearchResult?

abstract fun buildPosterUrl(searchResult: SearchResult): String?

}

class TmdbSearchResultMapper(
private val context: Context,
private val languageCode: String
) : SearchResultMapper<BaseTvShow>(context) {

override fun mapToSearchResult(show: BaseTvShow): SearchResult? {
val tmdbId = show.id ?: return null
return SearchResult().also {
it.tmdbId = tmdbId
it.title = show.name
it.overview = show.overview
it.language = languageCode
it.posterPath = show.poster_path
}
}

override fun buildPosterUrl(searchResult: SearchResult): String? {
return ImageTools.tmdbOrTvdbPosterUrl(searchResult.posterPath, context)
}

}

/**
* Maps Trakt shows to a list of [SearchResult].
*/
class TraktSearchResultMapper(
private val context: Context,
private val languageCode: String
) : SearchResultMapper<BaseShow>(context) {

override fun mapToSearchResult(show: BaseShow): SearchResult? {
val traktShow = show.show
val tmdbId = traktShow?.ids?.tmdb
?: return null // has no TMDB id
return SearchResult().also {
it.tmdbId = tmdbId
it.title = traktShow.title
// Trakt might not return an overview, so use the year if available
it.overview = if (!traktShow.overview.isNullOrEmpty()) {
traktShow.overview
} else if (traktShow.year != null) {
traktShow.year!!.toString()
} else {
""
}
it.language = languageCode
}
}

/**
* Uses [ImageTools.posterUrlOrResolve] to build the final poster URL or a special resolve URL.
*
* Trakt does not return posters, so sets a special URL that makes the image loader resolve
* them. But for added shows uses their TMDB poster path to build a regular URL.
*/
override fun buildPosterUrl(searchResult: SearchResult): String? {
return ImageTools.posterUrlOrResolve(
searchResult.posterPath,
searchResult.tmdbId,
searchResult.language,
context
)
}

}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ class ShowsDiscoverLiveData(
}

private suspend fun getShowsWithNewEpisodes(
language: String,
languageCode: String,
watchProviderIds: List<Int>?,
watchRegion: String?,
firstReleaseYear: Int?,
Expand All @@ -76,7 +76,7 @@ class ShowsDiscoverLiveData(
val tmdb = SgApp.getServicesComponent(context.applicationContext).tmdb()
val results = TmdbTools2().getShowsWithNewEpisodes(
tmdb = tmdb,
language = language,
language = languageCode,
page = 1,
firstReleaseYear = firstReleaseYear,
originalLanguage = originalLanguage,
Expand All @@ -85,8 +85,8 @@ class ShowsDiscoverLiveData(
)?.results

val result = if (results != null) {
val searchResults = SearchTools.mapTvShowsToSearchResults(language, results)
SearchTools.markLocalShowsAsAddedAndPreferLocalPoster(context, searchResults)
val searchResults = TmdbSearchResultMapper(context, languageCode)
.mapToSearchResults(results)
buildResultSuccess(searchResults, R.string.add_empty)
} else {
buildResultFailure()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ package com.battlelancer.seriesguide.shows.search.discover

import android.content.Context
import androidx.annotation.StringRes
import androidx.collection.SparseArrayCompat
import com.battlelancer.seriesguide.R
import com.battlelancer.seriesguide.SgApp
import com.battlelancer.seriesguide.shows.ShowsSettings
Expand Down Expand Up @@ -97,13 +96,11 @@ class TraktAddLoader(
return buildResultSuccess(emptyList())
}

return buildResultSuccess(
parseTraktShowsToSearchResults(
shows,
SgApp.getServicesComponent(context).showTools().getTmdbIdsToPoster(),
ShowsSettings.getShowsSearchLanguage(context)
)
)

val searchResults =
TraktSearchResultMapper(context, ShowsSettings.getShowsSearchLanguage(context))
.mapToSearchResults(shows)
return buildResultSuccess(searchResults)
}

private fun buildResultSuccess(results: List<SearchResult>): Result {
Expand All @@ -124,45 +121,4 @@ class TraktAddLoader(
return Result(LinkedList(), context, errorResId)
}


/**
* Transforms a list of Trakt shows to a list of [SearchResult], marks shows already in
* the local database as added.
*/
private fun parseTraktShowsToSearchResults(
traktShows: List<BaseShow>,
existingPosterPaths: SparseArrayCompat<String>,
overrideLanguage: String
): List<SearchResult> {
val results: MutableList<SearchResult> = ArrayList()

// build list
for (baseShow in traktShows) {
val show = baseShow.show
val tmdbId = show?.ids?.tmdb
?: continue // has no TMDB id

val result = SearchResult().also {
it.tmdbId = tmdbId
it.title = show.title
// Trakt might not return an overview, so use the year if available
it.overview = if (!show.overview.isNullOrEmpty()) {
show.overview
} else if (show.year != null) {
show.year!!.toString()
} else {
""
}
if (existingPosterPaths.indexOfKey(tmdbId) >= 0) {
// is already in local database
it.state = SearchResult.STATE_ADDED
// use the poster fetched for it (or null if there is none)
it.posterPath = existingPosterPaths[tmdbId]
}
it.language = overrideLanguage
}
results.add(result)
}
return results
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import com.battlelancer.seriesguide.R
import com.battlelancer.seriesguide.SgApp
import com.battlelancer.seriesguide.shows.ShowsSettings
import com.battlelancer.seriesguide.shows.search.discover.SearchResult
import com.battlelancer.seriesguide.shows.search.discover.SearchTools
import com.battlelancer.seriesguide.shows.search.discover.TmdbSearchResultMapper
import com.battlelancer.seriesguide.util.Errors
import com.uwetrottmann.androidutils.AndroidUtils
import kotlinx.coroutines.Dispatchers
Expand Down Expand Up @@ -63,10 +63,8 @@ class SimilarShowsViewModel(
page.results
}

val searchResults = SearchTools.mapTvShowsToSearchResults(languageCode, results)
// Mark local shows and use existing posters.
SearchTools.markLocalShowsAsAddedAndPreferLocalPoster(context, searchResults)

val searchResults = TmdbSearchResultMapper(context, languageCode)
.mapToSearchResults(results)
postSuccessfulResult(searchResults)
}
}
Expand Down

0 comments on commit a49a1d8

Please sign in to comment.