add support for crunchyroll media playback in player

This commit is contained in:
Jannik 2021-12-26 20:22:00 +01:00
parent 919bce65e9
commit 6dac929550
Signed by: Seil0
GPG Key ID: E8459F3723C52C24
9 changed files with 153 additions and 130 deletions

View File

@ -83,8 +83,8 @@ data class Episode(
@SerialName("episode") val episode: String,
@SerialName("episode_number") val episodeNumber: Int,
@SerialName("description") val description: String,
@SerialName("next_episode_id") val nextEpisodeId: String = "", // use default value since the field is optional
@SerialName("next_episode_title") val nextEpisodeTitle: String = "", // use default value since the field is optional
@SerialName("next_episode_id") val nextEpisodeId: String? = null, // default/nullable value since optional
@SerialName("next_episode_title") val nextEpisodeTitle: String? = null, // default/nullable value since optional
@SerialName("is_subbed") val isSubbed: Boolean,
@SerialName("is_dubbed") val isDubbed: Boolean,
@SerialName("images") val images: Thumbnail,

View File

@ -29,6 +29,7 @@ import kotlinx.android.synthetic.main.activity_player.*
import kotlinx.android.synthetic.main.player_controls.*
import kotlinx.coroutines.launch
import org.mosad.teapod.R
import org.mosad.teapod.parser.crunchyroll.NoneEpisode
import org.mosad.teapod.preferences.Preferences
import org.mosad.teapod.ui.components.EpisodesListPlayer
import org.mosad.teapod.ui.components.LanguageSettingsPlayer
@ -124,7 +125,7 @@ class PlayerActivity : AppCompatActivity() {
it.getStringExtra(getString(R.string.intent_season_id)) ?: "",
it.getStringExtra(getString(R.string.intent_episode_id)) ?: ""
)
model.playEpisode(model.currentEpisode.mediaId, replace = true)
model.playCurrentMedia()
}
}
@ -171,7 +172,7 @@ class PlayerActivity : AppCompatActivity() {
}
private fun initPlayer() {
if (model.media.aodId < 0) {
if (model.currentEpisode.equals(NoneEpisode)) {
Log.e(javaClass.name, "No media was set.")
this.finish()
}
@ -206,14 +207,14 @@ class PlayerActivity : AppCompatActivity() {
else -> View.VISIBLE
}
if (state == ExoPlayer.STATE_ENDED && model.nextEpisodeId != null && Preferences.autoplay) {
if (state == ExoPlayer.STATE_ENDED && model.currentEpisodeCr.nextEpisodeId != null && Preferences.autoplay) {
playNextEpisode()
}
}
})
// start playing the current episode, after all needed player components have been initialized
model.playEpisode(model.currentEpisode.mediaId, true)
model.playCurrentMedia()
}
@SuppressLint("ClickableViewAccessibility")
@ -251,9 +252,10 @@ class PlayerActivity : AppCompatActivity() {
}
private fun initGUI() {
if (model.media.type == DataTypes.MediaType.MOVIE) {
button_episodes.visibility = View.GONE
}
// TODO reimplement for cr
// if (model.media.type == DataTypes.MediaType.MOVIE) {
// button_episodes.visibility = View.GONE
// }
}
private fun initTimeUpdates() {
@ -277,7 +279,7 @@ class PlayerActivity : AppCompatActivity() {
// if remaining time < 20 sec, a next ep is set, autoplay is enabled and not in pip:
// show next ep button
if (remainingTime in 1..20000) {
if (!btnNextEpIsVisible && model.nextEpisodeId != null && Preferences.autoplay && !isInPiPMode()) {
if (!btnNextEpIsVisible && model.currentEpisodeCr.nextEpisodeId != null && Preferences.autoplay && !isInPiPMode()) {
showButtonNextEp()
}
} else if (btnNextEpIsVisible) {
@ -335,18 +337,19 @@ class PlayerActivity : AppCompatActivity() {
exo_text_title.text = model.getMediaTitle()
// hide the next ep button, if there is none
button_next_ep_c.visibility = if (model.nextEpisodeId == null) {
button_next_ep_c.visibility = if (model.currentEpisodeCr.nextEpisodeId == null) {
View.GONE
} else {
View.VISIBLE
}
// TODO reimplement for cr
// hide the episodes button, if the media type changed
button_episodes.visibility = if (model.media.type == DataTypes.MediaType.MOVIE) {
View.GONE
} else {
View.VISIBLE
}
// button_episodes.visibility = if (model.media.type == DataTypes.MediaType.MOVIE) {
// View.GONE
// } else {
// View.VISIBLE
// }
}
/**

View File

@ -5,26 +5,23 @@ import android.net.Uri
import android.support.v4.media.session.MediaSessionCompat
import android.util.Log
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import com.google.android.exoplayer2.C
import com.google.android.exoplayer2.MediaItem
import com.google.android.exoplayer2.SimpleExoPlayer
import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector
import com.google.android.exoplayer2.source.MediaSource
import com.google.android.exoplayer2.source.hls.HlsMediaSource
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory
import com.google.android.exoplayer2.util.Util
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import org.mosad.teapod.R
import org.mosad.teapod.parser.AoDParser
import org.mosad.teapod.parser.crunchyroll.Crunchyroll
import org.mosad.teapod.parser.crunchyroll.NoneEpisode
import org.mosad.teapod.parser.crunchyroll.NoneEpisodes
import org.mosad.teapod.parser.crunchyroll.NonePlayback
import org.mosad.teapod.preferences.Preferences
import org.mosad.teapod.util.*
import org.mosad.teapod.util.tmdb.TMDBApiController
import org.mosad.teapod.util.AoDEpisodeNone
import org.mosad.teapod.util.EpisodeMeta
import org.mosad.teapod.util.Meta
import org.mosad.teapod.util.TVShowMeta
import org.mosad.teapod.util.tmdb.TMDBTVSeason
import java.util.*
import kotlin.collections.ArrayList
@ -43,8 +40,8 @@ class PlayerViewModel(application: Application) : AndroidViewModel(application)
val currentEpisodeChangedListener = ArrayList<() -> Unit>()
private val preferredLanguage = if (Preferences.preferSecondary) Locale.JAPANESE else Locale.GERMAN
var media: AoDMedia = AoDMediaNone
internal set
// var media: AoDMedia = AoDMediaNone
// internal set
var mediaMeta: Meta? = null
internal set
var tmdbTVSeason: TMDBTVSeason? =null
@ -53,8 +50,8 @@ class PlayerViewModel(application: Application) : AndroidViewModel(application)
internal set
var currentEpisodeMeta: EpisodeMeta? = null
internal set
var nextEpisodeId: Int? = null
internal set
// var nextEpisodeId: Int? = null
// internal set
var currentLanguage: Locale = Locale.ROOT
internal set
@ -62,8 +59,7 @@ class PlayerViewModel(application: Application) : AndroidViewModel(application)
internal set
var currentEpisodeCr = NoneEpisode
internal set
var currentPlaybackCr = NonePlayback
internal set
private var currentPlaybackCr = NonePlayback
init {
initMediaSession()
@ -94,6 +90,7 @@ class PlayerViewModel(application: Application) : AndroidViewModel(application)
episodesCrunchy = Crunchyroll.episodes(seasonId)
//mediaMeta = loadMediaMeta(media.aodId) // can be done blocking, since it should be cached
// TODO replace this with setCurrentEpisode
currentEpisodeCr = episodesCrunchy.items.find { episode ->
episode.id == episodeId
} ?: NoneEpisode
@ -102,14 +99,15 @@ class PlayerViewModel(application: Application) : AndroidViewModel(application)
currentPlaybackCr = Crunchyroll.playback(currentEpisodeCr.playback)
}
// TODO reimplement for cr
// run async as it should be loaded by the time the episodes a
viewModelScope.launch {
// get season info, if metaDB knows the tv show
if (media.type == DataTypes.MediaType.TVSHOW && mediaMeta != null) {
val tvShowMeta = mediaMeta as TVShowMeta
tmdbTVSeason = TMDBApiController().getTVSeasonDetails(tvShowMeta.tmdbId, tvShowMeta.tmdbSeasonNumber)
}
}
// viewModelScope.launch {
// // get tmdb season info, if metaDB knows the tv show
// if (media.type == DataTypes.MediaType.TVSHOW && mediaMeta != null) {
// val tvShowMeta = mediaMeta as TVShowMeta
// tmdbTVSeason = TMDBApiController().getTVSeasonDetails(tvShowMeta.tmdbId, tvShowMeta.tmdbSeasonNumber)
// }
// }
currentEpisodeMeta = getEpisodeMetaByAoDMediaId(currentEpisode.mediaId)
currentLanguage = currentEpisode.getPreferredStream(preferredLanguage).language
@ -117,12 +115,12 @@ class PlayerViewModel(application: Application) : AndroidViewModel(application)
fun setLanguage(language: Locale) {
currentLanguage = language
playCurrentMedia(player.currentPosition)
val seekTime = player.currentPosition
val mediaSource = HlsMediaSource.Factory(dataSourceFactory).createMediaSource(
MediaItem.fromUri(Uri.parse(currentEpisode.getPreferredStream(language).url))
)
playMedia(mediaSource, true, seekTime)
// val mediaSource = HlsMediaSource.Factory(dataSourceFactory).createMediaSource(
// MediaItem.fromUri(Uri.parse(currentEpisode.getPreferredStream(language).url))
// )
// playMedia(mediaSource, seekTime)
}
// player actions
@ -138,62 +136,70 @@ class PlayerViewModel(application: Application) : AndroidViewModel(application)
/**
* play the next episode, if nextEpisode is not null
*/
fun playNextEpisode() = nextEpisodeId?.let { it ->
playEpisode(it, replace = true)
fun playNextEpisode() = currentEpisodeCr.nextEpisodeId?.let { nextEpisodeId ->
setCurrentEpisode(nextEpisodeId, startPlayback = true)
}
/**
* Set currentEpisode and start playing it.
* Update nextEpisode to reflect the change and update
* the watched state for the now playing episode.
* Set currentEpisodeCr to the episode of the given ID
* @param episodeId The ID of the episode you want to set currentEpisodeCr to
*/
fun setCurrentEpisode(episodeId: String, startPlayback: Boolean = false) {
currentEpisodeCr = episodesCrunchy.items.find { episode ->
episode.id == episodeId
} ?: NoneEpisode
// TODO don't run blocking
runBlocking {
currentPlaybackCr = Crunchyroll.playback(currentEpisodeCr.playback)
}
// TODO update metadata and language (it should not be needed to update the language here!)
if (startPlayback) {
playCurrentMedia()
}
}
/**
* Play the current media from currentPlaybackCr.
*
* @param episodeId The aod media id of the episode to play.
* @param replace (default = false)
* @param seekPosition The seek position for the episode (default = 0).
*/
fun playEpisode(episodeId: Int, replace: Boolean = false, seekPosition: Long = 0) {
currentEpisode = media.getEpisodeById(episodeId)
currentLanguage = currentEpisode.getPreferredStream(currentLanguage).language
currentEpisodeMeta = getEpisodeMetaByAoDMediaId(currentEpisode.mediaId)
nextEpisodeId = selectNextEpisode()
fun playCurrentMedia(seekPosition: Long = 0) {
// update player gui (title, next ep button) after nextEpisodeId has been set
currentEpisodeChangedListener.forEach { it() }
// get preferred stream url TODO implement
val url = currentPlaybackCr.streams.adaptive_hls["en-US"]?.url ?: ""
println("stream url: $url")
// create the media source object
val mediaSource = HlsMediaSource.Factory(dataSourceFactory).createMediaSource(
MediaItem.fromUri(Uri.parse(currentEpisode.getPreferredStream(currentLanguage).url))
MediaItem.fromUri(Uri.parse(url))
)
playMedia(mediaSource, replace, seekPosition)
// if episodes has not been watched, mark as watched
if (!currentEpisode.watched) {
viewModelScope.launch {
AoDParser.markAsWatched(media.aodId, currentEpisode.mediaId)
}
}
}
// the actual player playback code
player.setMediaSource(mediaSource)
player.prepare()
if (seekPosition > 0) player.seekTo(seekPosition)
player.playWhenReady = true
/**
* change the players media source and start playback
*/
fun playMedia(source: MediaSource, replace: Boolean = false, seekPosition: Long = 0) {
if (replace || player.contentDuration == C.TIME_UNSET) {
player.setMediaSource(source)
player.prepare()
if (seekPosition > 0) player.seekTo(seekPosition)
player.playWhenReady = true
}
// TODO reimplement mark as watched for cr, if needed
}
fun getMediaTitle(): String {
return if (media.type == DataTypes.MediaType.TVSHOW) {
// TODO add tvshow/movie diff
val isTVShow = true
return if(isTVShow) {
getApplication<Application>().getString(
R.string.component_episode_title,
currentEpisode.numberStr,
currentEpisode.description
currentEpisodeCr.episode,
currentEpisodeCr.title
)
} else {
currentEpisode.title
// TODO movie
currentEpisodeCr.title
}
}
@ -206,22 +212,28 @@ class PlayerViewModel(application: Application) : AndroidViewModel(application)
}
}
// TODO reimplement for cr
private suspend fun loadMediaMeta(aodId: Int): Meta? {
return if (media.type == DataTypes.MediaType.TVSHOW) {
MetaDBController().getTVShowMetadata(aodId)
} else {
null
}
// return if (media.type == DataTypes.MediaType.TVSHOW) {
// MetaDBController().getTVShowMetadata(aodId)
// } else {
// null
// }
return null
}
/**
* TODO reimplement for cr
* Based on the current episodes index, get the next episode.
* @return The next episode or null if there is none.
*/
private fun selectNextEpisode(): Int? {
return media.playlist.firstOrNull {
it.index > media.getEpisodeById(currentEpisode.mediaId).index
}?.mediaId
// return media.playlist.firstOrNull {
// it.index > media.getEpisodeById(currentEpisode.mediaId).index
// }?.mediaId
return null
}
}

View File

@ -28,12 +28,13 @@ class EpisodesListPlayer @JvmOverloads constructor(
}
model?.let {
adapterRecEpisodes = PlayerEpisodeItemAdapter(model.media.playlist, model.tmdbTVSeason?.episodes)
adapterRecEpisodes.onImageClick = { _, position ->
adapterRecEpisodes = PlayerEpisodeItemAdapter(model.episodesCrunchy, model.tmdbTVSeason?.episodes)
adapterRecEpisodes.onImageClick = {_, episodeId ->
(this.parent as ViewGroup).removeView(this)
model.playEpisode(model.media.playlist[position].mediaId, replace = true)
model.setCurrentEpisode(episodeId, startPlayback = true)
}
adapterRecEpisodes.currentSelected = model.currentEpisode.index
// episodeNumber starts at 1, we need the episode index -> - 1
adapterRecEpisodes.currentSelected = (model.currentEpisodeCr.episodeNumber - 1)
binding.recyclerEpisodesPlayer.adapter = adapterRecEpisodes
binding.recyclerEpisodesPlayer.scrollToPosition(model.currentEpisode.index)

View File

@ -5,13 +5,15 @@ import android.animation.AnimatorListenerAdapter
import android.animation.ObjectAnimator
import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
import android.widget.FrameLayout
import kotlinx.android.synthetic.main.button_fast_forward.view.*
import org.mosad.teapod.R
import org.mosad.teapod.databinding.ButtonFastForwardBinding
class FastForwardButton(context: Context, attrs: AttributeSet?): FrameLayout(context, attrs) {
private val binding = ButtonFastForwardBinding.inflate(LayoutInflater.from(context))
private val animationDuration: Long = 800
private val buttonAnimation: ObjectAnimator
private val labelAnimation: ObjectAnimator
@ -19,30 +21,30 @@ class FastForwardButton(context: Context, attrs: AttributeSet?): FrameLayout(con
var onAnimationEndCallback: (() -> Unit)? = null
init {
inflate(context, R.layout.button_fast_forward, this)
addView(binding.root)
buttonAnimation = ObjectAnimator.ofFloat(imageButton, View.ROTATION, 0f, 50f).apply {
buttonAnimation = ObjectAnimator.ofFloat(binding.imageButton, View.ROTATION, 0f, 50f).apply {
duration = animationDuration / 4
repeatCount = 1
repeatMode = ObjectAnimator.REVERSE
addListener(object : AnimatorListenerAdapter() {
override fun onAnimationStart(animation: Animator?) {
imageButton.isEnabled = false // disable button
imageButton.setBackgroundResource(R.drawable.ic_baseline_forward_24)
binding.imageButton.isEnabled = false // disable button
binding.imageButton.setBackgroundResource(R.drawable.ic_baseline_forward_24)
}
})
}
labelAnimation = ObjectAnimator.ofFloat(textView, View.TRANSLATION_X, 35f).apply {
labelAnimation = ObjectAnimator.ofFloat(binding.textView, View.TRANSLATION_X, 35f).apply {
duration = animationDuration
addListener(object : AnimatorListenerAdapter() {
// the label animation takes longer then the button animation, reset stuff in here
override fun onAnimationEnd(animation: Animator?) {
imageButton.isEnabled = true // enable button
imageButton.setBackgroundResource(R.drawable.ic_baseline_forward_10_24)
binding.imageButton.isEnabled = true // enable button
binding.imageButton.setBackgroundResource(R.drawable.ic_baseline_forward_10_24)
textView.visibility = View.GONE
textView.animate().translationX(0f)
binding.textView.visibility = View.GONE
binding.textView.animate().translationX(0f)
onAnimationEndCallback?.invoke()
}
@ -51,7 +53,7 @@ class FastForwardButton(context: Context, attrs: AttributeSet?): FrameLayout(con
}
fun setOnButtonClickListener(func: FastForwardButton.() -> Unit) {
imageButton.setOnClickListener {
binding.imageButton.setOnClickListener {
func()
}
}
@ -61,7 +63,7 @@ class FastForwardButton(context: Context, attrs: AttributeSet?): FrameLayout(con
buttonAnimation.start()
// run lbl animation
textView.visibility = View.VISIBLE
binding.textView.visibility = View.VISIBLE
labelAnimation.start()
}

View File

@ -5,13 +5,15 @@ import android.animation.AnimatorListenerAdapter
import android.animation.ObjectAnimator
import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
import android.widget.FrameLayout
import kotlinx.android.synthetic.main.button_rewind.view.*
import org.mosad.teapod.R
import org.mosad.teapod.databinding.ButtonRewindBinding
class RewindButton(context: Context, attrs: AttributeSet): FrameLayout(context, attrs) {
private val binding = ButtonRewindBinding.inflate(LayoutInflater.from(context))
private val animationDuration: Long = 800
private val buttonAnimation: ObjectAnimator
private val labelAnimation: ObjectAnimator
@ -19,29 +21,29 @@ class RewindButton(context: Context, attrs: AttributeSet): FrameLayout(context,
var onAnimationEndCallback: (() -> Unit)? = null
init {
inflate(context, R.layout.button_rewind, this)
addView(binding.root)
buttonAnimation = ObjectAnimator.ofFloat(imageButton, View.ROTATION, 0f, -50f).apply {
buttonAnimation = ObjectAnimator.ofFloat(binding.imageButton, View.ROTATION, 0f, -50f).apply {
duration = animationDuration / 4
repeatCount = 1
repeatMode = ObjectAnimator.REVERSE
addListener(object : AnimatorListenerAdapter() {
override fun onAnimationStart(animation: Animator?) {
imageButton.isEnabled = false // disable button
imageButton.setBackgroundResource(R.drawable.ic_baseline_rewind_24)
binding.imageButton.isEnabled = false // disable button
binding.imageButton.setBackgroundResource(R.drawable.ic_baseline_rewind_24)
}
})
}
labelAnimation = ObjectAnimator.ofFloat(textView, View.TRANSLATION_X, -35f).apply {
labelAnimation = ObjectAnimator.ofFloat(binding.textView, View.TRANSLATION_X, -35f).apply {
duration = animationDuration
addListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator?) {
imageButton.isEnabled = true // enable button
imageButton.setBackgroundResource(R.drawable.ic_baseline_rewind_10_24)
binding.imageButton.isEnabled = true // enable button
binding.imageButton.setBackgroundResource(R.drawable.ic_baseline_rewind_10_24)
textView.visibility = View.GONE
textView.animate().translationX(0f)
binding.textView.visibility = View.GONE
binding.textView.animate().translationX(0f)
onAnimationEndCallback?.invoke()
}
@ -50,7 +52,7 @@ class RewindButton(context: Context, attrs: AttributeSet): FrameLayout(context,
}
fun setOnButtonClickListener(func: RewindButton.() -> Unit) {
imageButton.setOnClickListener {
binding.imageButton.setOnClickListener {
func()
}
}
@ -60,7 +62,7 @@ class RewindButton(context: Context, attrs: AttributeSet): FrameLayout(context,
buttonAnimation.start()
// run lbl animation
textView.visibility = View.VISIBLE
binding.textView.visibility = View.VISIBLE
labelAnimation.start()
}

View File

@ -76,7 +76,7 @@ data class AoDEpisode(
* @return the preferred stream, if not present use the first stream
*/
fun getPreferredStream(language: Locale) = streams.firstOrNull { it.language == language }
?: streams.first()
?: Stream("", Locale.ROOT)
}
data class Stream(
@ -112,7 +112,7 @@ val AoDEpisodeNone = AoDEpisode(
"",
"",
-1,
false,
true,
"",
mutableListOf()
)

View File

@ -40,7 +40,7 @@ class EpisodeItemAdapter(private val episodes: Episodes, private val tmdbEpisode
""
}
// TODO is isNotEmpty() needed?
// TODO is isNotEmpty() needed? also in PlayerEpisodeItemAdapter
if (ep.images.thumbnail[0][0].source.isNotEmpty()) {
Glide.with(context).load(ep.images.thumbnail[0][0].source)
.apply(RequestOptions.placeholderOf(ColorDrawable(Color.DKGRAY)))

View File

@ -9,12 +9,12 @@ import com.bumptech.glide.request.RequestOptions
import jp.wasabeef.glide.transformations.RoundedCornersTransformation
import org.mosad.teapod.R
import org.mosad.teapod.databinding.ItemEpisodePlayerBinding
import org.mosad.teapod.util.AoDEpisode
import org.mosad.teapod.parser.crunchyroll.Episodes
import org.mosad.teapod.util.tmdb.TMDBTVEpisode
class PlayerEpisodeItemAdapter(private val episodes: List<AoDEpisode>, private val tmdbEpisodes: List<TMDBTVEpisode>?) : RecyclerView.Adapter<PlayerEpisodeItemAdapter.EpisodeViewHolder>() {
class PlayerEpisodeItemAdapter(private val episodes: Episodes, private val tmdbEpisodes: List<TMDBTVEpisode>?) : RecyclerView.Adapter<PlayerEpisodeItemAdapter.EpisodeViewHolder>() {
var onImageClick: ((String, Int) -> Unit)? = null
var onImageClick: ((seasonId: String, episodeId: String) -> Unit)? = null
var currentSelected: Int = -1 // -1, since position should never be < 0
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): EpisodeViewHolder {
@ -23,25 +23,25 @@ class PlayerEpisodeItemAdapter(private val episodes: List<AoDEpisode>, private v
override fun onBindViewHolder(holder: EpisodeViewHolder, position: Int) {
val context = holder.binding.root.context
val ep = episodes[position]
val ep = episodes.items[position]
val titleText = if (ep.hasDub()) {
context.getString(R.string.component_episode_title, ep.numberStr, ep.description)
val titleText = if (ep.isDubbed) {
context.getString(R.string.component_episode_title, ep.episode, ep.title)
} else {
context.getString(R.string.component_episode_title_sub, ep.numberStr, ep.description)
context.getString(R.string.component_episode_title_sub, ep.episode, ep.title)
}
holder.binding.textEpisodeTitle2.text = titleText
holder.binding.textEpisodeDesc2.text = if (ep.shortDesc.isNotEmpty()) {
ep.shortDesc
holder.binding.textEpisodeDesc2.text = if (ep.description.isNotEmpty()) {
ep.description
} else if (tmdbEpisodes != null && position < tmdbEpisodes.size){
tmdbEpisodes[position].overview
} else {
""
}
if (ep.imageURL.isNotEmpty()) {
Glide.with(context).load(ep.imageURL)
if (ep.images.thumbnail[0][0].source.isNotEmpty()) {
Glide.with(context).load(ep.images.thumbnail[0][0].source)
.apply(RequestOptions.bitmapTransform(RoundedCornersTransformation(10, 0)))
.into(holder.binding.imageEpisode)
}
@ -55,15 +55,18 @@ class PlayerEpisodeItemAdapter(private val episodes: List<AoDEpisode>, private v
}
override fun getItemCount(): Int {
return episodes.size
return episodes.items.size
}
inner class EpisodeViewHolder(val binding: ItemEpisodePlayerBinding) : RecyclerView.ViewHolder(binding.root) {
init {
binding.imageEpisode.setOnClickListener {
// don't execute, if it's the current episode
if (currentSelected != adapterPosition) {
onImageClick?.invoke(episodes[adapterPosition].title, adapterPosition)
if (currentSelected != bindingAdapterPosition) {
onImageClick?.invoke(
episodes.items[bindingAdapterPosition].seasonId,
episodes.items[bindingAdapterPosition].id
)
}
}
}