From 6dac929550c955a0a965f9d2e83500de3b620c9a Mon Sep 17 00:00:00 2001 From: Jannik Date: Sun, 26 Dec 2021 20:22:00 +0100 Subject: [PATCH] add support for crunchyroll media playback in player --- .../teapod/parser/crunchyroll/DataTypes.kt | 4 +- .../ui/activity/player/PlayerActivity.kt | 31 ++-- .../ui/activity/player/PlayerViewModel.kt | 150 ++++++++++-------- .../ui/components/EpisodesListPlayer.kt | 9 +- .../teapod/ui/components/FastForwardButton.kt | 26 +-- .../teapod/ui/components/RewindButton.kt | 26 +-- .../java/org/mosad/teapod/util/DataTypes.kt | 4 +- .../teapod/util/adapter/EpisodeItemAdapter.kt | 2 +- .../util/adapter/PlayerEpisodeItemAdapter.kt | 31 ++-- 9 files changed, 153 insertions(+), 130 deletions(-) diff --git a/app/src/main/java/org/mosad/teapod/parser/crunchyroll/DataTypes.kt b/app/src/main/java/org/mosad/teapod/parser/crunchyroll/DataTypes.kt index fb12c8b..6086b10 100644 --- a/app/src/main/java/org/mosad/teapod/parser/crunchyroll/DataTypes.kt +++ b/app/src/main/java/org/mosad/teapod/parser/crunchyroll/DataTypes.kt @@ -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, diff --git a/app/src/main/java/org/mosad/teapod/ui/activity/player/PlayerActivity.kt b/app/src/main/java/org/mosad/teapod/ui/activity/player/PlayerActivity.kt index 82519e3..bcc63b2 100644 --- a/app/src/main/java/org/mosad/teapod/ui/activity/player/PlayerActivity.kt +++ b/app/src/main/java/org/mosad/teapod/ui/activity/player/PlayerActivity.kt @@ -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 +// } } /** diff --git a/app/src/main/java/org/mosad/teapod/ui/activity/player/PlayerViewModel.kt b/app/src/main/java/org/mosad/teapod/ui/activity/player/PlayerViewModel.kt index c81c5d1..795707b 100644 --- a/app/src/main/java/org/mosad/teapod/ui/activity/player/PlayerViewModel.kt +++ b/app/src/main/java/org/mosad/teapod/ui/activity/player/PlayerViewModel.kt @@ -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().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 } } \ No newline at end of file diff --git a/app/src/main/java/org/mosad/teapod/ui/components/EpisodesListPlayer.kt b/app/src/main/java/org/mosad/teapod/ui/components/EpisodesListPlayer.kt index 13a6d40..cb654e9 100644 --- a/app/src/main/java/org/mosad/teapod/ui/components/EpisodesListPlayer.kt +++ b/app/src/main/java/org/mosad/teapod/ui/components/EpisodesListPlayer.kt @@ -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) diff --git a/app/src/main/java/org/mosad/teapod/ui/components/FastForwardButton.kt b/app/src/main/java/org/mosad/teapod/ui/components/FastForwardButton.kt index cabe34a..e9207d7 100644 --- a/app/src/main/java/org/mosad/teapod/ui/components/FastForwardButton.kt +++ b/app/src/main/java/org/mosad/teapod/ui/components/FastForwardButton.kt @@ -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() } diff --git a/app/src/main/java/org/mosad/teapod/ui/components/RewindButton.kt b/app/src/main/java/org/mosad/teapod/ui/components/RewindButton.kt index a72ec74..c1fa4d0 100644 --- a/app/src/main/java/org/mosad/teapod/ui/components/RewindButton.kt +++ b/app/src/main/java/org/mosad/teapod/ui/components/RewindButton.kt @@ -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() } diff --git a/app/src/main/java/org/mosad/teapod/util/DataTypes.kt b/app/src/main/java/org/mosad/teapod/util/DataTypes.kt index 83467fd..7e93be0 100644 --- a/app/src/main/java/org/mosad/teapod/util/DataTypes.kt +++ b/app/src/main/java/org/mosad/teapod/util/DataTypes.kt @@ -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() ) diff --git a/app/src/main/java/org/mosad/teapod/util/adapter/EpisodeItemAdapter.kt b/app/src/main/java/org/mosad/teapod/util/adapter/EpisodeItemAdapter.kt index 8baa776..7576d71 100644 --- a/app/src/main/java/org/mosad/teapod/util/adapter/EpisodeItemAdapter.kt +++ b/app/src/main/java/org/mosad/teapod/util/adapter/EpisodeItemAdapter.kt @@ -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))) diff --git a/app/src/main/java/org/mosad/teapod/util/adapter/PlayerEpisodeItemAdapter.kt b/app/src/main/java/org/mosad/teapod/util/adapter/PlayerEpisodeItemAdapter.kt index 6cf35a0..4efecaa 100644 --- a/app/src/main/java/org/mosad/teapod/util/adapter/PlayerEpisodeItemAdapter.kt +++ b/app/src/main/java/org/mosad/teapod/util/adapter/PlayerEpisodeItemAdapter.kt @@ -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, private val tmdbEpisodes: List?) : RecyclerView.Adapter() { +class PlayerEpisodeItemAdapter(private val episodes: Episodes, private val tmdbEpisodes: List?) : RecyclerView.Adapter() { - 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, 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, 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 + ) } } }