migrate more Crunchyroll API endpoints to v2
This commit is contained in:
parent
0662d656ac
commit
59a457430e
|
@ -58,7 +58,7 @@ object Crunchyroll {
|
||||||
|
|
||||||
private lateinit var token: Token
|
private lateinit var token: Token
|
||||||
private var tokenValidUntil: Long = 0
|
private var tokenValidUntil: Long = 0
|
||||||
@OptIn(DelicateCoroutinesApi::class)
|
@OptIn(DelicateCoroutinesApi::class, ExperimentalCoroutinesApi::class)
|
||||||
private val tokenRefreshContext = newSingleThreadContext("TokenRefreshContext")
|
private val tokenRefreshContext = newSingleThreadContext("TokenRefreshContext")
|
||||||
|
|
||||||
private var accountID = ""
|
private var accountID = ""
|
||||||
|
@ -260,26 +260,30 @@ object Crunchyroll {
|
||||||
/**
|
/**
|
||||||
* Browse the media available on crunchyroll.
|
* Browse the media available on crunchyroll.
|
||||||
*
|
*
|
||||||
* TODO migrate to v2
|
* @param start start of the item list, used for pagination, default = 0
|
||||||
*
|
* @param n number of items to return, default = 10
|
||||||
* @param sortBy
|
* @param sortBy the sort order, see **[SortBy]**
|
||||||
* @param n Number of items to return, defaults to 10
|
* @param ratings add user rating to the objects, default = false
|
||||||
*
|
* @param seasonTag filter by season tag, if present
|
||||||
|
* @param categories filter by category, if present
|
||||||
* @return A **[BrowseResult]** object is returned.
|
* @return A **[BrowseResult]** object is returned.
|
||||||
*/
|
*/
|
||||||
suspend fun browse(
|
suspend fun browse(
|
||||||
categories: List<Categories> = emptyList(),
|
|
||||||
sortBy: SortBy = SortBy.ALPHABETICAL,
|
|
||||||
seasonTag: String = "",
|
|
||||||
start: Int = 0,
|
start: Int = 0,
|
||||||
n: Int = 10
|
n: Int = 10,
|
||||||
|
sortBy: SortBy = SortBy.ALPHABETICAL,
|
||||||
|
ratings: Boolean = false,
|
||||||
|
seasonTag: String = "",
|
||||||
|
categories: List<Categories> = emptyList()
|
||||||
): BrowseResult {
|
): BrowseResult {
|
||||||
val browseEndpoint = "/content/v1/browse"
|
val browseEndpoint = "/content/v2/discover/browse"
|
||||||
val parameters = mutableListOf(
|
val parameters = mutableListOf(
|
||||||
"locale" to Preferences.preferredSubtitleLocale.toLanguageTag(),
|
|
||||||
"sort_by" to sortBy.str,
|
|
||||||
"start" to start,
|
"start" to start,
|
||||||
"n" to n
|
"n" to n,
|
||||||
|
"sort_by" to sortBy.str,
|
||||||
|
"ratings" to ratings,
|
||||||
|
"preferred_audio_language" to Preferences.preferredSubtitleLocale.toLanguageTag(),
|
||||||
|
"locale" to Preferences.preferredSubtitleLocale.toLanguageTag(),
|
||||||
)
|
)
|
||||||
|
|
||||||
// if a season tag is present add it to the parameters
|
// if a season tag is present add it to the parameters
|
||||||
|
@ -304,9 +308,10 @@ object Crunchyroll {
|
||||||
NoneBrowseResult
|
NoneBrowseResult
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the cache has more than 100 entries clear it, so it doesn't become a memory problem
|
|
||||||
|
|
||||||
|
// if the cache has more than 10 entries clear it, so it doesn't become a memory problem
|
||||||
// Note: this value is totally guessed and should be replaced by a properly researched value
|
// Note: this value is totally guessed and should be replaced by a properly researched value
|
||||||
// TODO 100 is way to high as it's not the number of items but BrowseResults
|
|
||||||
if (browsingCache.size > 10) {
|
if (browsingCache.size > 10) {
|
||||||
browsingCache.clear()
|
browsingCache.clear()
|
||||||
}
|
}
|
||||||
|
@ -322,19 +327,20 @@ object Crunchyroll {
|
||||||
* Search fo a query term.
|
* Search fo a query term.
|
||||||
* Note: currently this function only supports series/tv shows.
|
* Note: currently this function only supports series/tv shows.
|
||||||
*
|
*
|
||||||
* TODO migrate to v2
|
|
||||||
*
|
|
||||||
* @param query The query term as String
|
* @param query The query term as String
|
||||||
* @param n The maximum number of results to return, default = 10
|
* @param n The maximum number of results to return, default = 10
|
||||||
|
* @param ratings add user rating to the objects, default = false
|
||||||
* @return A **[SearchResult]** object
|
* @return A **[SearchResult]** object
|
||||||
*/
|
*/
|
||||||
suspend fun search(query: String, n: Int = 10): SearchResult {
|
suspend fun search(query: String, n: Int = 10, ratings: Boolean = false): SearchResult {
|
||||||
val searchEndpoint = "/content/v1/search"
|
val searchEndpoint = "/content/v2/discover/search"
|
||||||
val parameters = listOf(
|
val parameters = listOf(
|
||||||
"locale" to Preferences.preferredSubtitleLocale.toLanguageTag(),
|
|
||||||
"q" to query,
|
"q" to query,
|
||||||
"n" to n,
|
"n" to n,
|
||||||
"type" to "series"
|
"type" to "series",
|
||||||
|
"ratings" to ratings,
|
||||||
|
"preferred_audio_language" to Preferences.preferredSubtitleLocale.toLanguageTag(),
|
||||||
|
"locale" to Preferences.preferredSubtitleLocale.toLanguageTag()
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO episodes have thumbnails as image, and not poster_tall/poster_tall,
|
// TODO episodes have thumbnails as image, and not poster_tall/poster_tall,
|
||||||
|
@ -353,10 +359,10 @@ object Crunchyroll {
|
||||||
* Note: episode objects are currently not supported
|
* Note: episode objects are currently not supported
|
||||||
*
|
*
|
||||||
* @param objects The object IDs as list of Strings
|
* @param objects The object IDs as list of Strings
|
||||||
* @param ratings the user rating of the object
|
* @param ratings add user rating to the objects
|
||||||
* @return A **[Collection]** of Panels
|
* @return A **[Collection]** of Panels
|
||||||
*/
|
*/
|
||||||
suspend fun objects(objects: List<String>, ratings: Boolean = false): Collection2<Item> {
|
suspend fun objects(objects: List<String>, ratings: Boolean = false): CollectionV2<Item> {
|
||||||
val episodesEndpoint = "/content/v2/cms/objects/${objects.joinToString(",")}"
|
val episodesEndpoint = "/content/v2/cms/objects/${objects.joinToString(",")}"
|
||||||
val parameters = listOf(
|
val parameters = listOf(
|
||||||
"ratings" to ratings,
|
"ratings" to ratings,
|
||||||
|
@ -368,7 +374,7 @@ object Crunchyroll {
|
||||||
requestGet(episodesEndpoint, parameters)
|
requestGet(episodesEndpoint, parameters)
|
||||||
} catch (ex: Exception) {
|
} catch (ex: Exception) {
|
||||||
Log.e(TAG, "Exception in objects().", ex)
|
Log.e(TAG, "Exception in objects().", ex)
|
||||||
NoneCollection2
|
NoneCollectionV2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -509,7 +515,7 @@ object Crunchyroll {
|
||||||
)
|
)
|
||||||
|
|
||||||
return try {
|
return try {
|
||||||
(requestGet(watchlistSeriesEndpoint, parameters) as Collection2<IsWatchlistItem>)
|
(requestGet(watchlistSeriesEndpoint, parameters) as CollectionV2<IsWatchlistItem>)
|
||||||
.total == 1
|
.total == 1
|
||||||
} catch (ex: Exception) {
|
} catch (ex: Exception) {
|
||||||
Log.e(TAG, "Exception in isWatchlist() with seriesId $seriesId", ex)
|
Log.e(TAG, "Exception in isWatchlist() with seriesId $seriesId", ex)
|
||||||
|
@ -631,14 +637,16 @@ object Crunchyroll {
|
||||||
*
|
*
|
||||||
* @param seriesId The crunchyroll series id of the media
|
* @param seriesId The crunchyroll series id of the media
|
||||||
* @param n The maximum number of results to return, default = 10
|
* @param n The maximum number of results to return, default = 10
|
||||||
|
* @param ratings add user rating to the objects
|
||||||
* @return A **[SimilarToResult]** object
|
* @return A **[SimilarToResult]** object
|
||||||
*/
|
*/
|
||||||
suspend fun similarTo(seriesId: String, n: Int = 10): SimilarToResult {
|
suspend fun similarTo(seriesId: String, n: Int = 10, ratings: Boolean = false): SimilarToResult {
|
||||||
val similarToEndpoint = "/content/v1/$accountID/similar_to"
|
val similarToEndpoint = "/content/v2/discover/$accountID/similar_to/$seriesId"
|
||||||
val parameters = listOf(
|
val parameters = listOf(
|
||||||
"guid" to seriesId,
|
"n" to n,
|
||||||
|
"ratings" to ratings,
|
||||||
|
"preferred_audio_language" to Preferences.preferredSubtitleLocale.toLanguageTag(),
|
||||||
"locale" to Preferences.preferredSubtitleLocale.toLanguageTag(),
|
"locale" to Preferences.preferredSubtitleLocale.toLanguageTag(),
|
||||||
"n" to n
|
|
||||||
)
|
)
|
||||||
|
|
||||||
return try {
|
return try {
|
||||||
|
@ -659,7 +667,7 @@ object Crunchyroll {
|
||||||
* @param n Number of items to return, defaults to 20.
|
* @param n Number of items to return, defaults to 20.
|
||||||
* @return A **[Collection]** containing up to n **[Item]**.
|
* @return A **[Collection]** containing up to n **[Item]**.
|
||||||
*/
|
*/
|
||||||
suspend fun watchlist(n: Int = 20): Collection2<Item> {
|
suspend fun watchlist(n: Int = 20): CollectionV2<Item> {
|
||||||
val watchlistEndpoint = "/content/v2/discover/$accountID/watchlist"
|
val watchlistEndpoint = "/content/v2/discover/$accountID/watchlist"
|
||||||
val parameters = listOf(
|
val parameters = listOf(
|
||||||
"locale" to Preferences.preferredSubtitleLocale.toLanguageTag(),
|
"locale" to Preferences.preferredSubtitleLocale.toLanguageTag(),
|
||||||
|
@ -681,10 +689,10 @@ object Crunchyroll {
|
||||||
/**
|
/**
|
||||||
* List the next up episodes for the logged in account.
|
* List the next up episodes for the logged in account.
|
||||||
*
|
*
|
||||||
* @param n Number of items to return, defaults to 20.
|
* @param n Number of items to return, default = 20
|
||||||
* @return A **[HistoryList]** containing up to n **[UpNextAccountItem]**.
|
* @return A **[HistoryList]** containing up to n **[UpNextAccountItem]**.
|
||||||
*/
|
*/
|
||||||
suspend fun upNextAccount(n: Int = 20): HistoryList {
|
suspend fun upNextAccount(n: Int = 10): HistoryList {
|
||||||
val watchlistEndpoint = "/content/v2/discover/$accountID/history"
|
val watchlistEndpoint = "/content/v2/discover/$accountID/history"
|
||||||
val parameters = listOf(
|
val parameters = listOf(
|
||||||
"locale" to Preferences.preferredSubtitleLocale.toLanguageTag(),
|
"locale" to Preferences.preferredSubtitleLocale.toLanguageTag(),
|
||||||
|
@ -699,13 +707,21 @@ object Crunchyroll {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun recommendations(n: Int = 20, start: Int = 0): RecommendationsList {
|
/**
|
||||||
val recommendationsEndpoint = "/content/v1/$accountID/recommendations"
|
* Returns a collection of recommendations for the currently logged in account.
|
||||||
|
*
|
||||||
|
* @param start start of the item list, used for pagination, default = 0
|
||||||
|
* @param n number of items to return, default = 10
|
||||||
|
* @param ratings add user rating to the objects, default = false
|
||||||
|
* @return A **[RecommendationsList]** containing up to n **[Item]**.
|
||||||
|
*/
|
||||||
|
suspend fun recommendations(start: Int = 0, n: Int = 10, ratings: Boolean = false): RecommendationsList {
|
||||||
|
val recommendationsEndpoint = "/content/v2/discover/$accountID/recommendations"
|
||||||
val parameters = listOf(
|
val parameters = listOf(
|
||||||
"locale" to Preferences.preferredSubtitleLocale.toLanguageTag(),
|
|
||||||
"n" to n,
|
|
||||||
"start" to start,
|
"start" to start,
|
||||||
"variant_id" to 0
|
"n" to n,
|
||||||
|
"ratings" to ratings,
|
||||||
|
"locale" to Preferences.preferredSubtitleLocale.toLanguageTag(),
|
||||||
)
|
)
|
||||||
|
|
||||||
return try {
|
return try {
|
||||||
|
|
|
@ -24,7 +24,7 @@ package org.mosad.teapod.parser.crunchyroll
|
||||||
|
|
||||||
import kotlinx.serialization.SerialName
|
import kotlinx.serialization.SerialName
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import java.util.*
|
import java.util.Locale
|
||||||
|
|
||||||
val supportedLocals = listOf(
|
val supportedLocals = listOf(
|
||||||
Locale.forLanguageTag("ar-SA"),
|
Locale.forLanguageTag("ar-SA"),
|
||||||
|
@ -44,6 +44,10 @@ val supportedLocals = listOf(
|
||||||
* data classes for browse
|
* data classes for browse
|
||||||
* TODO make class names more clear/possibly overlapping for now
|
* TODO make class names more clear/possibly overlapping for now
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enum of all supported sorting orders.
|
||||||
|
*/
|
||||||
enum class SortBy(val str: String) {
|
enum class SortBy(val str: String) {
|
||||||
ALPHABETICAL("alphabetical"),
|
ALPHABETICAL("alphabetical"),
|
||||||
NEWLY_ADDED("newly_added"),
|
NEWLY_ADDED("newly_added"),
|
||||||
|
@ -112,23 +116,22 @@ val NoneAccount = Account("", "", false, "")
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class Collection<T>(
|
data class CollectionV1<T>(
|
||||||
@SerialName("total") val total: Int,
|
@SerialName("total") val total: Int,
|
||||||
@SerialName("items") val items: List<T>
|
@SerialName("items") val items: List<T>
|
||||||
)
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class Collection2<T>(
|
data class CollectionV2<T>(
|
||||||
@SerialName("total") val total: Int,
|
@SerialName("total") val total: Int,
|
||||||
@SerialName("data") val data: List<T>
|
@SerialName("data") val data: List<T>
|
||||||
)
|
)
|
||||||
|
|
||||||
typealias SearchResult = Collection<SearchCollection>
|
typealias SearchResult = CollectionV2<SearchTypedList<Item>>
|
||||||
typealias SearchCollection = Collection<Item>
|
typealias BrowseResult = CollectionV2<Item>
|
||||||
typealias BrowseResult = Collection<Item>
|
typealias SimilarToResult = CollectionV2<Item>
|
||||||
typealias SimilarToResult = Collection<Item>
|
typealias RecommendationsList = CollectionV2<Item>
|
||||||
typealias RecommendationsList = Collection<Item>
|
typealias Benefits = CollectionV1<Benefit>
|
||||||
typealias Benefits = Collection<Benefit>
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* panel data classes
|
* panel data classes
|
||||||
|
@ -159,9 +162,9 @@ data class Poster(val height: Int, val width: Int, val source: String, val type:
|
||||||
* up next & watchlist data classes
|
* up next & watchlist data classes
|
||||||
*/
|
*/
|
||||||
|
|
||||||
typealias Watchlist = Collection2<WatchlistItem>
|
typealias Watchlist = CollectionV2<WatchlistItem>
|
||||||
typealias HistoryList = Collection2<UpNextAccountItem>
|
typealias HistoryList = CollectionV2<UpNextAccountItem>
|
||||||
typealias UpNextSeriesList = Collection2<UpNextSeriesItem>
|
typealias UpNextSeriesList = CollectionV2<UpNextSeriesItem>
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class WatchlistItem(
|
data class WatchlistItem(
|
||||||
|
@ -221,8 +224,7 @@ data class EpisodeMetadata(
|
||||||
@SerialName("series_title") val seriesTitle: String,
|
@SerialName("series_title") val seriesTitle: String,
|
||||||
)
|
)
|
||||||
|
|
||||||
val NoneCollection = Collection<Item>(0, emptyList())
|
val NoneCollectionV2 = CollectionV2<Item>(0, emptyList())
|
||||||
val NoneCollection2 = Collection2<Item>(0, emptyList())
|
|
||||||
val NoneSearchResult = SearchResult(0, emptyList())
|
val NoneSearchResult = SearchResult(0, emptyList())
|
||||||
val NoneBrowseResult = BrowseResult(0, emptyList())
|
val NoneBrowseResult = BrowseResult(0, emptyList())
|
||||||
val NoneSimilarToResult = SimilarToResult(0, emptyList())
|
val NoneSimilarToResult = SimilarToResult(0, emptyList())
|
||||||
|
@ -236,7 +238,7 @@ val NoneBenefits = Benefits(0, emptyList())
|
||||||
* series data class
|
* series data class
|
||||||
*/
|
*/
|
||||||
|
|
||||||
typealias Series = Collection2<SeriesItem>
|
typealias Series = CollectionV2<SeriesItem>
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class SeriesItem(
|
data class SeriesItem(
|
||||||
|
@ -354,7 +356,7 @@ val NoneVersion = Version(
|
||||||
variant = ""
|
variant = ""
|
||||||
)
|
)
|
||||||
|
|
||||||
typealias Playheads = Collection2<PlayheadObject>
|
typealias Playheads = CollectionV2<PlayheadObject>
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class PlayheadObject(
|
data class PlayheadObject(
|
||||||
|
@ -450,7 +452,18 @@ data class Benefit(
|
||||||
@SerialName("benefit") val benefit: String,
|
@SerialName("benefit") val benefit: String,
|
||||||
@SerialName("source") val source: String,
|
@SerialName("source") val source: String,
|
||||||
)
|
)
|
||||||
|
@Suppress("unused")
|
||||||
val NoneBenefit = Benefit(
|
val NoneBenefit = Benefit(
|
||||||
benefit = "",
|
benefit = "",
|
||||||
source = ""
|
source = ""
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* search result typed list data class
|
||||||
|
*/
|
||||||
|
@Serializable
|
||||||
|
data class SearchTypedList<T>(
|
||||||
|
@SerialName("type") val type: String,
|
||||||
|
@SerialName("count") val count: Int,
|
||||||
|
@SerialName("items") val items: List<T>
|
||||||
|
)
|
||||||
|
|
|
@ -66,16 +66,16 @@ class HomeViewModel : ViewModel() {
|
||||||
uiState.emit(UiState.Loading)
|
uiState.emit(UiState.Loading)
|
||||||
try {
|
try {
|
||||||
// run the loading in parallel to speed up the process
|
// run the loading in parallel to speed up the process
|
||||||
val upNextJob = viewModelScope.async { Crunchyroll.upNextAccount().data }
|
val upNextJob = viewModelScope.async { Crunchyroll.upNextAccount(n = 20).data }
|
||||||
val watchlistJob = viewModelScope.async { Crunchyroll.watchlist(WATCHLIST_LENGTH).data }
|
val watchlistJob = viewModelScope.async { Crunchyroll.watchlist(WATCHLIST_LENGTH).data }
|
||||||
val recommendationsJob = viewModelScope.async {
|
val recommendationsJob = viewModelScope.async {
|
||||||
Crunchyroll.recommendations(20).items
|
Crunchyroll.recommendations(n = 20).data
|
||||||
}
|
}
|
||||||
val recentlyAddedJob = viewModelScope.async {
|
val recentlyAddedJob = viewModelScope.async {
|
||||||
Crunchyroll.browse(sortBy = SortBy.NEWLY_ADDED, n = 50).items
|
Crunchyroll.browse(sortBy = SortBy.NEWLY_ADDED, n = 50).data
|
||||||
}
|
}
|
||||||
val topTenJob = viewModelScope.async {
|
val topTenJob = viewModelScope.async {
|
||||||
Crunchyroll.browse(sortBy = SortBy.POPULARITY, n = 10).items
|
Crunchyroll.browse(sortBy = SortBy.POPULARITY, n = 10).data
|
||||||
}
|
}
|
||||||
|
|
||||||
val recentlyAddedItems = recentlyAddedJob.await()
|
val recentlyAddedItems = recentlyAddedJob.await()
|
||||||
|
@ -133,7 +133,7 @@ class HomeViewModel : ViewModel() {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
uiState.update { currentUiState ->
|
uiState.update { currentUiState ->
|
||||||
if (currentUiState is UiState.Normal) {
|
if (currentUiState is UiState.Normal) {
|
||||||
val upNextItems = Crunchyroll.upNextAccount().data
|
val upNextItems = Crunchyroll.upNextAccount(n = 20).data
|
||||||
currentUiState.copy(upNextItems = upNextItems)
|
currentUiState.copy(upNextItems = upNextItems)
|
||||||
} else {
|
} else {
|
||||||
currentUiState
|
currentUiState
|
||||||
|
|
|
@ -90,7 +90,7 @@ class LibraryFragmentViewModel : ViewModel() {
|
||||||
delay(250)
|
delay(250)
|
||||||
|
|
||||||
val results = Crunchyroll.search(query, 50)
|
val results = Crunchyroll.search(query, 50)
|
||||||
.items.firstOrNull()?.items?.toItemMediaList()
|
.data.firstOrNull()?.items?.toItemMediaList()
|
||||||
?: listOf()
|
?: listOf()
|
||||||
uiState.emit(UiState.Search(results))
|
uiState.emit(UiState.Search(results))
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,12 +9,11 @@ import androidx.core.view.WindowInsetsCompat
|
||||||
import androidx.core.view.WindowInsetsControllerCompat
|
import androidx.core.view.WindowInsetsControllerCompat
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import org.mosad.teapod.R
|
import org.mosad.teapod.R
|
||||||
import org.mosad.teapod.parser.crunchyroll.Collection
|
import org.mosad.teapod.parser.crunchyroll.CollectionV2
|
||||||
import org.mosad.teapod.parser.crunchyroll.Collection2
|
|
||||||
import org.mosad.teapod.parser.crunchyroll.Item
|
import org.mosad.teapod.parser.crunchyroll.Item
|
||||||
import org.mosad.teapod.parser.crunchyroll.PlayheadObject
|
import org.mosad.teapod.parser.crunchyroll.PlayheadObject
|
||||||
import org.mosad.teapod.ui.activity.player.PlayerActivity
|
import org.mosad.teapod.ui.activity.player.PlayerActivity
|
||||||
import java.util.*
|
import java.util.Locale
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a Intent for PlayerActivity with season and episode id.
|
* Create a Intent for PlayerActivity with season and episode id.
|
||||||
|
@ -36,13 +35,7 @@ fun <T> concatenate(vararg lists: List<T>): List<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO move to correct location
|
// TODO move to correct location
|
||||||
fun Collection<Item>.toItemMediaList(): List<ItemMedia> {
|
fun CollectionV2<Item>.toItemMediaList(): List<ItemMedia> {
|
||||||
return this.items.map {
|
|
||||||
ItemMedia(it.id, it.title, it.images.poster_wide[0][0].source)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Collection2<Item>.toItemMediaList(): List<ItemMedia> {
|
|
||||||
return this.data.map {
|
return this.data.map {
|
||||||
ItemMedia(it.id, it.title, it.images.poster_wide[0][0].source)
|
ItemMedia(it.id, it.title, it.images.poster_wide[0][0].source)
|
||||||
}
|
}
|
||||||
|
@ -65,7 +58,7 @@ fun Locale.toDisplayString(fallback: String): String {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Collection2<PlayheadObject>.toPlayheadsMap(): Map<String, PlayheadObject> {
|
fun CollectionV2<PlayheadObject>.toPlayheadsMap(): Map<String, PlayheadObject> {
|
||||||
return this.data.associateBy { it.contentId }
|
return this.data.associateBy { it.contentId }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue