migrate playheads() to crunchyroll v2 api
This commit is contained in:
parent
2e7db26d1d
commit
d40ab9519c
|
@ -567,18 +567,20 @@ object Crunchyroll {
|
||||||
* @param episodeIDs A **[List]** of episodes IDs as strings.
|
* @param episodeIDs A **[List]** of episodes IDs as strings.
|
||||||
* @return A **[Map]**<String, **[PlayheadObject]**> containing playback info.
|
* @return A **[Map]**<String, **[PlayheadObject]**> containing playback info.
|
||||||
*/
|
*/
|
||||||
suspend fun playheads(episodeIDs: List<String>): PlayheadsMap {
|
suspend fun playheads(episodeIDs: List<String>): Playheads {
|
||||||
val playheadsEndpoint = "/content/v1/playheads/$accountID/${episodeIDs.joinToString(",")}"
|
val playheadsEndpoint = "/content/v2/$accountID/playheads"
|
||||||
val parameters = listOf("locale" to Preferences.preferredSubtitleLocale.toLanguageTag())
|
val parameters = listOf(
|
||||||
|
"content_ids" to episodeIDs.joinToString(","),
|
||||||
|
"preferred_audio_language" to Preferences.preferredAudioLocale.toLanguageTag(),
|
||||||
|
"locale" to Preferences.preferredSubtitleLocale.toLanguageTag()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
return try {
|
return try {
|
||||||
requestGet(playheadsEndpoint, parameters)
|
requestGet(playheadsEndpoint, parameters)
|
||||||
} catch (ex: SerializationException) {
|
} catch (ex: Exception) {
|
||||||
Log.e(TAG, "SerializationException in playheads().", ex)
|
|
||||||
emptyMap()
|
|
||||||
} catch (ex: Throwable) {
|
|
||||||
Log.e(TAG, "Exception in playheads().", ex.cause)
|
Log.e(TAG, "Exception in playheads().", ex.cause)
|
||||||
emptyMap()
|
NonePlayheads
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -127,9 +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 Watchlist = Collection2<WatchlistItem>
|
|
||||||
typealias HistoryList = Collection2<UpNextAccountItem>
|
|
||||||
typealias UpNextSeriesList = Collection2<UpNextSeriesItem>
|
|
||||||
typealias RecommendationsList = Collection<Item>
|
typealias RecommendationsList = Collection<Item>
|
||||||
typealias Benefits = Collection<Benefit>
|
typealias Benefits = Collection<Benefit>
|
||||||
|
|
||||||
|
@ -159,9 +156,13 @@ data class Images(val poster_tall: List<List<Poster>>, val poster_wide: List<Lis
|
||||||
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)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* continue_watching_item data classes
|
* up next & watchlist data classes
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
typealias Watchlist = Collection2<WatchlistItem>
|
||||||
|
typealias HistoryList = Collection2<UpNextAccountItem>
|
||||||
|
typealias UpNextSeriesList = Collection2<UpNextSeriesItem>
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class WatchlistItem(
|
data class WatchlistItem(
|
||||||
@SerialName("panel") val panel: EpisodePanel,
|
@SerialName("panel") val panel: EpisodePanel,
|
||||||
|
@ -352,7 +353,7 @@ val NoneVersion = Version(
|
||||||
variant = ""
|
variant = ""
|
||||||
)
|
)
|
||||||
|
|
||||||
typealias PlayheadsMap = Map<String, PlayheadObject>
|
typealias Playheads = Collection2<PlayheadObject>
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class PlayheadObject(
|
data class PlayheadObject(
|
||||||
|
@ -362,6 +363,8 @@ data class PlayheadObject(
|
||||||
@SerialName("last_modified") val lastModified: String,
|
@SerialName("last_modified") val lastModified: String,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val NonePlayheads = Playheads(0, emptyList())
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Meta data for a episode intro. All time values are in seconds.
|
* Meta data for a episode intro. All time values are in seconds.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -3,11 +3,13 @@ package org.mosad.teapod.ui.activity.main.viewmodel
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import androidx.lifecycle.AndroidViewModel
|
import androidx.lifecycle.AndroidViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import kotlinx.coroutines.async
|
||||||
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.util.DataTypes.MediaType
|
import org.mosad.teapod.util.DataTypes.MediaType
|
||||||
import org.mosad.teapod.util.tmdb.*
|
import org.mosad.teapod.util.tmdb.*
|
||||||
|
import org.mosad.teapod.util.toPlayheadsMap
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* handle media, next ep and tmdb
|
* handle media, next ep and tmdb
|
||||||
|
@ -25,7 +27,8 @@ class MediaFragmentViewModel(application: Application) : AndroidViewModel(applic
|
||||||
internal set
|
internal set
|
||||||
val currentEpisodesCrunchy = arrayListOf<Episode>() // used for EpisodeItemAdapter (easier updates)
|
val currentEpisodesCrunchy = arrayListOf<Episode>() // used for EpisodeItemAdapter (easier updates)
|
||||||
|
|
||||||
// additional media info
|
// additional media info, might change during during user interaction
|
||||||
|
// use a map to update the episode adapter values
|
||||||
val currentPlayheads: MutableMap<String, PlayheadObject> = mutableMapOf()
|
val currentPlayheads: MutableMap<String, PlayheadObject> = mutableMapOf()
|
||||||
var isWatchlist = false
|
var isWatchlist = false
|
||||||
internal set
|
internal set
|
||||||
|
@ -80,12 +83,7 @@ class MediaFragmentViewModel(application: Application) : AndroidViewModel(applic
|
||||||
|
|
||||||
// load playheads and tmdb in parallel
|
// load playheads and tmdb in parallel
|
||||||
listOf(
|
listOf(
|
||||||
viewModelScope.launch {
|
updatePlayheadsAsync(),
|
||||||
// get playheads (including fully watched state)
|
|
||||||
val episodeIDs = episodesCrunchy.data.map { it.id }
|
|
||||||
currentPlayheads.clear()
|
|
||||||
currentPlayheads.putAll(Crunchyroll.playheads(episodeIDs))
|
|
||||||
},
|
|
||||||
viewModelScope.launch { loadTmdbInfo() } // use tmdb search to get media info
|
viewModelScope.launch { loadTmdbInfo() } // use tmdb search to get media info
|
||||||
).joinAll()
|
).joinAll()
|
||||||
}
|
}
|
||||||
|
@ -117,6 +115,16 @@ class MediaFragmentViewModel(application: Application) : AndroidViewModel(applic
|
||||||
// } else NoneTMDBTVSeason
|
// } else NoneTMDBTVSeason
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get current playheads for all episodes
|
||||||
|
*/
|
||||||
|
private fun updatePlayheadsAsync() = viewModelScope.async {
|
||||||
|
currentPlayheads.clear()
|
||||||
|
currentPlayheads.putAll(
|
||||||
|
Crunchyroll.playheads(episodesCrunchy.data.map { it.id }).toPlayheadsMap()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set currentSeasonCrunchy based on the season id. Also set the new seasons episodes.
|
* Set currentSeasonCrunchy based on the season id. Also set the new seasons episodes.
|
||||||
*
|
*
|
||||||
|
@ -137,9 +145,7 @@ class MediaFragmentViewModel(application: Application) : AndroidViewModel(applic
|
||||||
currentEpisodesCrunchy.addAll(episodesCrunchy.data)
|
currentEpisodesCrunchy.addAll(episodesCrunchy.data)
|
||||||
|
|
||||||
// update playheads playheads (including fully watched state)
|
// update playheads playheads (including fully watched state)
|
||||||
val episodeIDs = episodesCrunchy.data.map { it.id }
|
updatePlayheadsAsync().await()
|
||||||
currentPlayheads.clear()
|
|
||||||
currentPlayheads.putAll(Crunchyroll.playheads(episodeIDs))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun setWatchlist() {
|
suspend fun setWatchlist() {
|
||||||
|
@ -154,11 +160,7 @@ class MediaFragmentViewModel(application: Application) : AndroidViewModel(applic
|
||||||
|
|
||||||
suspend fun updateOnResume() {
|
suspend fun updateOnResume() {
|
||||||
joinAll(
|
joinAll(
|
||||||
viewModelScope.launch {
|
updatePlayheadsAsync(),
|
||||||
val episodeIDs = episodesCrunchy.data.map { it.id }
|
|
||||||
currentPlayheads.clear()
|
|
||||||
currentPlayheads.putAll(Crunchyroll.playheads(episodeIDs))
|
|
||||||
},
|
|
||||||
viewModelScope.launch { upNextSeries = Crunchyroll.upNextSeries(seriesCrunchy.id) }
|
viewModelScope.launch { upNextSeries = Crunchyroll.upNextSeries(seriesCrunchy.id) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,6 +40,7 @@ import org.mosad.teapod.util.metadb.EpisodeMeta
|
||||||
import org.mosad.teapod.util.metadb.Meta
|
import org.mosad.teapod.util.metadb.Meta
|
||||||
import org.mosad.teapod.util.metadb.MetaDBController
|
import org.mosad.teapod.util.metadb.MetaDBController
|
||||||
import org.mosad.teapod.util.metadb.TVShowMeta
|
import org.mosad.teapod.util.metadb.TVShowMeta
|
||||||
|
import org.mosad.teapod.util.toPlayheadsMap
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.concurrent.scheduleAtFixedRate
|
import kotlin.concurrent.scheduleAtFixedRate
|
||||||
|
|
||||||
|
@ -63,7 +64,7 @@ class PlayerViewModel(application: Application) : AndroidViewModel(application)
|
||||||
internal set
|
internal set
|
||||||
var currentEpisodeMeta: EpisodeMeta? = null
|
var currentEpisodeMeta: EpisodeMeta? = null
|
||||||
internal set
|
internal set
|
||||||
var currentPlayheads: PlayheadsMap = mutableMapOf()
|
var currentPlayheads = mapOf<String, PlayheadObject>()
|
||||||
internal set
|
internal set
|
||||||
var currentIntroMetadata: DatalabIntro = NoneDatalabIntro
|
var currentIntroMetadata: DatalabIntro = NoneDatalabIntro
|
||||||
internal set
|
internal set
|
||||||
|
@ -142,7 +143,7 @@ class PlayerViewModel(application: Application) : AndroidViewModel(application)
|
||||||
viewModelScope.launch { mediaMeta = loadMediaMeta(episodes.data.first().seriesId) },
|
viewModelScope.launch { mediaMeta = loadMediaMeta(episodes.data.first().seriesId) },
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
val episodeIDs = episodes.data.map { it.id }
|
val episodeIDs = episodes.data.map { it.id }
|
||||||
currentPlayheads = Crunchyroll.playheads(episodeIDs)
|
currentPlayheads = Crunchyroll.playheads(episodeIDs).toPlayheadsMap()
|
||||||
}
|
}
|
||||||
).joinAll()
|
).joinAll()
|
||||||
Log.d(classTag, "meta: $mediaMeta")
|
Log.d(classTag, "meta: $mediaMeta")
|
||||||
|
@ -233,7 +234,9 @@ class PlayerViewModel(application: Application) : AndroidViewModel(application)
|
||||||
Log.d(classTag, currentVersion.toString())
|
Log.d(classTag, currentVersion.toString())
|
||||||
},
|
},
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
Crunchyroll.playheads(listOf(currentEpisode.id))[currentEpisode.id]?.let {
|
Crunchyroll.playheads(listOf(currentEpisode.id)).data.firstOrNull {
|
||||||
|
it.contentId == currentEpisode.id
|
||||||
|
}?.let {
|
||||||
// if the episode was fully watched, start at the beginning
|
// if the episode was fully watched, start at the beginning
|
||||||
currentPlayhead = if (it.fullyWatched) {
|
currentPlayhead = if (it.fullyWatched) {
|
||||||
0
|
0
|
||||||
|
@ -330,7 +333,7 @@ class PlayerViewModel(application: Application) : AndroidViewModel(application)
|
||||||
|
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
val episodeIDs = episodes.data.map { it.id }
|
val episodeIDs = episodes.data.map { it.id }
|
||||||
currentPlayheads = Crunchyroll.playheads(episodeIDs)
|
currentPlayheads = Crunchyroll.playheads(episodeIDs).toPlayheadsMap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,7 @@ class EpisodeListDialogFragment : DialogFragment() {
|
||||||
val adapterRecEpisodes = EpisodeItemAdapter(
|
val adapterRecEpisodes = EpisodeItemAdapter(
|
||||||
model.episodes.data,
|
model.episodes.data,
|
||||||
null,
|
null,
|
||||||
model.currentPlayheads.toMap(),
|
model.currentPlayheads,
|
||||||
EpisodeItemAdapter.OnClickListener { episode ->
|
EpisodeItemAdapter.OnClickListener { episode ->
|
||||||
dismiss()
|
dismiss()
|
||||||
// TODO make this none blocking, if necessary?
|
// TODO make this none blocking, if necessary?
|
||||||
|
|
|
@ -10,7 +10,9 @@ 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.Collection
|
||||||
|
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.ui.activity.player.PlayerActivity
|
import org.mosad.teapod.ui.activity.player.PlayerActivity
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
|
@ -57,6 +59,10 @@ fun Locale.toDisplayString(fallback: String): String {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun Collection2<PlayheadObject>.toPlayheadsMap(): Map<String, PlayheadObject> {
|
||||||
|
return this.data.associateBy { it.contentId }
|
||||||
|
}
|
||||||
|
|
||||||
fun hideBars(window: Window?, root: View) {
|
fun hideBars(window: Window?, root: View) {
|
||||||
if (window != null) {
|
if (window != null) {
|
||||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||||
|
|
|
@ -16,13 +16,12 @@ import org.mosad.teapod.databinding.ItemEpisodeBinding
|
||||||
import org.mosad.teapod.databinding.ItemEpisodePlayerBinding
|
import org.mosad.teapod.databinding.ItemEpisodePlayerBinding
|
||||||
import org.mosad.teapod.parser.crunchyroll.Episode
|
import org.mosad.teapod.parser.crunchyroll.Episode
|
||||||
import org.mosad.teapod.parser.crunchyroll.PlayheadObject
|
import org.mosad.teapod.parser.crunchyroll.PlayheadObject
|
||||||
import org.mosad.teapod.parser.crunchyroll.PlayheadsMap
|
|
||||||
import org.mosad.teapod.util.tmdb.TMDBTVEpisode
|
import org.mosad.teapod.util.tmdb.TMDBTVEpisode
|
||||||
|
|
||||||
class EpisodeItemAdapter(
|
class EpisodeItemAdapter(
|
||||||
private val episodes: List<Episode>,
|
private val episodes: List<Episode>,
|
||||||
private val tmdbEpisodes: List<TMDBTVEpisode>?,
|
private val tmdbEpisodes: List<TMDBTVEpisode>?,
|
||||||
private val playheads: PlayheadsMap,
|
private val playheads: Map<String, PlayheadObject>,
|
||||||
private val onClickListener: OnClickListener,
|
private val onClickListener: OnClickListener,
|
||||||
private val viewType: ViewType
|
private val viewType: ViewType
|
||||||
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||||
|
|
Loading…
Reference in New Issue