migrate more api calls to v2

This commit is contained in:
Jannik 2023-02-19 15:13:31 +01:00
parent 8b7fb3ac5f
commit 2e7db26d1d
Signed by: Seil0
GPG Key ID: E8459F3723C52C24
4 changed files with 58 additions and 78 deletions

View File

@ -322,6 +322,8 @@ 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
* @return A **[SearchResult]** object * @return A **[SearchResult]** object
@ -370,22 +372,6 @@ object Crunchyroll {
} }
} }
/**
* List all available seasons as **[SeasonListItem]**.
*/
@Suppress("unused")
suspend fun seasonList(): DiscSeasonList {
val seasonListEndpoint = "/content/v1/season_list"
val parameters = listOf("locale" to Preferences.preferredSubtitleLocale.toLanguageTag())
return try {
requestGet(seasonListEndpoint, parameters)
} catch (ex: Exception) {
Log.e(TAG, "Exception in seasonList().", ex)
NoneDiscSeasonList
}
}
/** /**
* Main media functions: series, season, episodes, playback * Main media functions: series, season, episodes, playback
*/ */
@ -394,12 +380,10 @@ object Crunchyroll {
* series id == crunchyroll id? * series id == crunchyroll id?
*/ */
suspend fun series(seriesId: String): Series { suspend fun series(seriesId: String): Series {
val seriesEndpoint = "/cms/v2/${token.country}/M3/crunchyroll/series/$seriesId" val seriesEndpoint = "/content/v2/cms/series/$seriesId"
val parameters = listOf( val parameters = listOf(
"locale" to Preferences.preferredSubtitleLocale.toLanguageTag(), "preferred_audio_language" to Preferences.preferredAudioLocale.toLanguageTag(),
"Signature" to signature, "locale" to Preferences.preferredSubtitleLocale.toLanguageTag()
"Policy" to policy,
"Key-Pair-Id" to keyPairID
) )
return try { return try {
@ -413,6 +397,8 @@ object Crunchyroll {
/** /**
* Get the next episode for a series. * Get the next episode for a series.
* *
* FIXME up_next returns no content if the is no next episode
*
* @param seriesId The series id for which to call up next * @param seriesId The series id for which to call up next
* @return A **[UpNextSeriesItem]** with a Panel representing the up next episode * @return A **[UpNextSeriesItem]** with a Panel representing the up next episode
*/ */
@ -425,6 +411,9 @@ object Crunchyroll {
return try { return try {
requestGet(upNextSeriesEndpoint, parameters) requestGet(upNextSeriesEndpoint, parameters)
} catch (ex: NoTransformationFoundException) {
// should be 204 No Content
NoneUpNextSeriesList
} catch (ex: JsonConvertException) { } catch (ex: JsonConvertException) {
Log.e(TAG, "JsonConvertException in upNextSeries() with seriesId=$seriesId", ex) Log.e(TAG, "JsonConvertException in upNextSeries() with seriesId=$seriesId", ex)
NoneUpNextSeriesList NoneUpNextSeriesList
@ -512,12 +501,16 @@ object Crunchyroll {
* @return **[Boolean]**: ture if it was found, else false * @return **[Boolean]**: ture if it was found, else false
*/ */
suspend fun isWatchlist(seriesId: String): Boolean { suspend fun isWatchlist(seriesId: String): Boolean {
val watchlistSeriesEndpoint = "/content/v1/watchlist/$accountID/$seriesId" val watchlistSeriesEndpoint = "/content/v2/$accountID/watchlist"
val parameters = listOf("locale" to Preferences.preferredSubtitleLocale.toLanguageTag()) val parameters = listOf(
"content_ids" to seriesId,
"preferred_audio_language" to Preferences.preferredAudioLocale.toLanguageTag(),
"locale" to Preferences.preferredSubtitleLocale.toLanguageTag()
)
return try { return try {
(requestGet(watchlistSeriesEndpoint, parameters) as JsonObject) (requestGet(watchlistSeriesEndpoint, parameters) as Collection2<IsWatchlistItem>)
.containsKey(seriesId) .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)
false false
@ -530,8 +523,11 @@ object Crunchyroll {
* @param seriesId The crunchyroll series id of the media to check * @param seriesId The crunchyroll series id of the media to check
*/ */
suspend fun postWatchlist(seriesId: String) { suspend fun postWatchlist(seriesId: String) {
val watchlistPostEndpoint = "/content/v1/watchlist/$accountID" val watchlistPostEndpoint = "/content/v2/$accountID/watchlist"
val parameters = listOf("locale" to Preferences.preferredSubtitleLocale.toLanguageTag()) val parameters = listOf(
"preferred_audio_language" to Preferences.preferredAudioLocale.toLanguageTag(),
"locale" to Preferences.preferredSubtitleLocale.toLanguageTag()
)
val json = buildJsonObject { val json = buildJsonObject {
put("content_id", seriesId) put("content_id", seriesId)
@ -542,7 +538,6 @@ object Crunchyroll {
} catch (ex: Exception) { } catch (ex: Exception) {
Log.e(TAG, "Exception in postWatchlist() with seriesId = $seriesId", ex) Log.e(TAG, "Exception in postWatchlist() with seriesId = $seriesId", ex)
} }
} }
/** /**
@ -551,15 +546,17 @@ object Crunchyroll {
* @param seriesId The crunchyroll series id of the media to check * @param seriesId The crunchyroll series id of the media to check
*/ */
suspend fun deleteWatchlist(seriesId: String) { suspend fun deleteWatchlist(seriesId: String) {
val watchlistDeleteEndpoint = "/content/v1/watchlist/$accountID/$seriesId" val watchlistDeleteEndpoint = "/content/v2/$accountID/watchlist/$seriesId"
val parameters = listOf("locale" to Preferences.preferredSubtitleLocale.toLanguageTag()) val parameters = listOf(
"preferred_audio_language" to Preferences.preferredAudioLocale.toLanguageTag(),
"locale" to Preferences.preferredSubtitleLocale.toLanguageTag()
)
try { try {
requestDelete(watchlistDeleteEndpoint, parameters) requestDelete(watchlistDeleteEndpoint, parameters)
} catch (ex: Exception) { } catch (ex: Exception) {
Log.e(TAG, "Exception in deleteWatchlist() with seriesId = $seriesId", ex) Log.e(TAG, "Exception in deleteWatchlist() with seriesId = $seriesId", ex)
} }
} }
/** /**

View File

@ -127,7 +127,6 @@ typealias SearchResult = Collection<SearchCollection>
typealias SearchCollection = Collection<Item> typealias SearchCollection = Collection<Item>
typealias BrowseResult = Collection<Item> typealias BrowseResult = Collection<Item>
typealias SimilarToResult = Collection<Item> typealias SimilarToResult = Collection<Item>
typealias DiscSeasonList = Collection<SeasonListItem>
typealias Watchlist = Collection2<WatchlistItem> typealias Watchlist = Collection2<WatchlistItem>
typealias HistoryList = Collection2<UpNextAccountItem> typealias HistoryList = Collection2<UpNextAccountItem>
typealias UpNextSeriesList = Collection2<UpNextSeriesItem> typealias UpNextSeriesList = Collection2<UpNextSeriesItem>
@ -159,21 +158,6 @@ data class Images(val poster_tall: List<List<Poster>>, val poster_wide: List<Lis
@Serializable @Serializable
data class Poster(val height: Int, val width: Int, val source: String, val type: String) data class Poster(val height: Int, val width: Int, val source: String, val type: String)
/**
* season list data classes
*/
@Serializable
data class SeasonListItem(
@SerialName("id") val id: String,
@SerialName("localization") val localization: SeasonListLocalization
)
@Serializable
data class SeasonListLocalization(
@SerialName("title") val title: String,
@SerialName("description") val description: String,
)
/** /**
* continue_watching_item data classes * continue_watching_item data classes
*/ */
@ -188,6 +172,13 @@ data class WatchlistItem(
@SerialName("is_favorite") val isFavorite: Boolean, @SerialName("is_favorite") val isFavorite: Boolean,
) )
@Serializable
data class IsWatchlistItem(
@SerialName("id") val id: String,
@SerialName("is_favorite") val isFavorite: Boolean,
@SerialName("date_added") val dateAdded: String
)
@Serializable @Serializable
data class UpNextAccountItem( data class UpNextAccountItem(
@SerialName("panel") val panel: EpisodePanel, @SerialName("panel") val panel: EpisodePanel,
@ -229,15 +220,10 @@ data class EpisodeMetadata(
@SerialName("series_title") val seriesTitle: String, @SerialName("series_title") val seriesTitle: String,
) )
val NoneItem = Item("", "", "", "", "", Images(emptyList(), emptyList()))
val NoneEpisodeMetadata = EpisodeMetadata(0, 0, "", 0, "", "", "")
val NoneEpisodePanel = EpisodePanel("", "", "", "", "", NoneEpisodeMetadata, Thumbnail(listOf())) //, "")
val NoneCollection = Collection<Item>(0, emptyList()) val NoneCollection = Collection<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())
val NoneDiscSeasonList = DiscSeasonList(0, emptyList())
val NoneWatchlist = Watchlist(0, emptyList()) val NoneWatchlist = Watchlist(0, emptyList())
val NoneHistoryList = HistoryList(0, emptyList()) val NoneHistoryList = HistoryList(0, emptyList())
val NoneUpNextSeriesList = UpNextSeriesList(0, emptyList()) val NoneUpNextSeriesList = UpNextSeriesList(0, emptyList())
@ -247,15 +233,23 @@ val NoneBenefits = Benefits(0, emptyList())
/** /**
* series data class * series data class
*/ */
typealias Series = Collection2<SeriesItem>
@Serializable @Serializable
data class Series( data class SeriesItem(
@SerialName("id") val id: String, @SerialName("id") val id: String,
@SerialName("title") val title: String, @SerialName("title") val title: String,
@SerialName("description") val description: String, @SerialName("description") val description: String,
@SerialName("images") val images: Images, @SerialName("images") val images: Images,
@SerialName("maturity_ratings") val maturityRatings: List<String> @SerialName("is_simulcast") val isSimulcast: Boolean,
@SerialName("maturity_ratings") val maturityRatings: List<String>,
@SerialName("audio_locales") val audioLocales: List<String>
) )
val NoneSeries = Series("", "", "", Images(emptyList(), emptyList()), emptyList())
val NoneSeriesItem = SeriesItem("", "", "", Images(emptyList(), emptyList()), false, emptyList(), emptyList())
val NoneSeries = Series(1, listOf(NoneSeriesItem))
/** /**
* Seasons data classes * Seasons data classes
@ -264,17 +258,7 @@ val NoneSeries = Series("", "", "", Images(emptyList(), emptyList()), emptyList(
data class Seasons( data class Seasons(
@SerialName("total") val total: Int, @SerialName("total") val total: Int,
@SerialName("data") val data: List<Season> @SerialName("data") val data: List<Season>
) { )
fun getPreferredSeasonByLocal(local: Locale): Season {
return data.firstOrNull { season ->
// try to get the the first seasons which matches the preferred local
season.slugTitle.endsWith("${local.getDisplayLanguage(Locale.ENGLISH)}-dub", true)
} ?: data.firstOrNull { season ->
// if there is no season with the preferred local, try to find a subbed season
season.isSubbed
} ?: data.first() // if no preferred language and no sub, use the first season
}
}
@Serializable @Serializable
data class Season( data class Season(

View File

@ -6,7 +6,6 @@ import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.joinAll import kotlinx.coroutines.joinAll
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.mosad.teapod.parser.crunchyroll.* import org.mosad.teapod.parser.crunchyroll.*
import org.mosad.teapod.preferences.Preferences
import org.mosad.teapod.util.DataTypes.MediaType import org.mosad.teapod.util.DataTypes.MediaType
import org.mosad.teapod.util.tmdb.* import org.mosad.teapod.util.tmdb.*
@ -16,7 +15,7 @@ import org.mosad.teapod.util.tmdb.*
*/ */
class MediaFragmentViewModel(application: Application) : AndroidViewModel(application) { class MediaFragmentViewModel(application: Application) : AndroidViewModel(application) {
var seriesCrunchy = NoneSeries // movies are also series var seriesCrunchy = NoneSeriesItem // movies are also series
internal set internal set
var seasonsCrunchy = NoneSeasons var seasonsCrunchy = NoneSeasons
internal set internal set
@ -50,7 +49,7 @@ class MediaFragmentViewModel(application: Application) : AndroidViewModel(applic
suspend fun loadCrunchy(crunchyId: String) { suspend fun loadCrunchy(crunchyId: String) {
// load series and seasons info in parallel // load series and seasons info in parallel
listOf( listOf(
viewModelScope.launch { seriesCrunchy = Crunchyroll.series(crunchyId) }, viewModelScope.launch { seriesCrunchy = Crunchyroll.series(crunchyId).data.first() },
viewModelScope.launch { seasonsCrunchy = Crunchyroll.seasons(crunchyId) }, viewModelScope.launch { seasonsCrunchy = Crunchyroll.seasons(crunchyId) },
viewModelScope.launch { isWatchlist = Crunchyroll.isWatchlist(crunchyId) }, viewModelScope.launch { isWatchlist = Crunchyroll.isWatchlist(crunchyId) },
viewModelScope.launch { upNextSeries = Crunchyroll.upNextSeries(crunchyId) }, viewModelScope.launch { upNextSeries = Crunchyroll.upNextSeries(crunchyId) },
@ -58,10 +57,15 @@ class MediaFragmentViewModel(application: Application) : AndroidViewModel(applic
).joinAll() ).joinAll()
// load the preferred season: // load the preferred season:
// next episode > preferred language (language per season, not per stream) // next episode > first season
currentSeasonCrunchy = seasonsCrunchy.data.firstOrNull{ season -> currentSeasonCrunchy = if (upNextSeries != NoneUpNextSeriesList) {
season.id == upNextSeries.data.first().panel.episodeMetadata.seasonId seasonsCrunchy.data.firstOrNull{ season ->
} ?: seasonsCrunchy.getPreferredSeasonByLocal(Preferences.preferredSubtitleLocale) season.id == upNextSeries.data.first().panel.episodeMetadata.seasonId
} ?: seasonsCrunchy.data.first()
} else {
seasonsCrunchy.data.first()
}
// Note: if we need to query metaDB, do it now // Note: if we need to query metaDB, do it now
// load episodes and metaDB in parallel (tmdb needs mediaType, which is set via episodes) // load episodes and metaDB in parallel (tmdb needs mediaType, which is set via episodes)

View File

@ -171,10 +171,6 @@ class PlayerViewModel(application: Application) : AndroidViewModel(application)
playCurrentMedia(player.currentPosition) playCurrentMedia(player.currentPosition)
} }
println(newSubtitleLocale != currentSubtitleLocale)
println("currentSubtitleLocale: $currentSubtitleLocale")
println("newSubtitleLocale: $newSubtitleLocale")
// else nothing has changed so no need do do anything // else nothing has changed so no need do do anything
} }
@ -235,7 +231,6 @@ class PlayerViewModel(application: Application) : AndroidViewModel(application)
currentStreams = Crunchyroll.streamsFromMediaGUID(currentVersion.mediaGUID) currentStreams = Crunchyroll.streamsFromMediaGUID(currentVersion.mediaGUID)
Log.d(classTag, currentVersion.toString()) Log.d(classTag, currentVersion.toString())
println("stream: $currentStreams")
}, },
viewModelScope.launch(Dispatchers.IO) { viewModelScope.launch(Dispatchers.IO) {
Crunchyroll.playheads(listOf(currentEpisode.id))[currentEpisode.id]?.let { Crunchyroll.playheads(listOf(currentEpisode.id))[currentEpisode.id]?.let {