implement preferred season/languag choosing in MediaFragment
This commit is contained in:
		| @ -8,9 +8,9 @@ import com.github.kittinunf.fuel.json.FuelJson | ||||
| import com.github.kittinunf.fuel.json.responseJson | ||||
| import com.github.kittinunf.result.Result | ||||
| import kotlinx.coroutines.* | ||||
| import kotlinx.serialization.Serializable | ||||
| import kotlinx.serialization.decodeFromString | ||||
| import kotlinx.serialization.json.Json | ||||
| import org.mosad.teapod.preferences.Preferences | ||||
| import java.util.* | ||||
|  | ||||
| private val json = Json { ignoreUnknownKeys = true } | ||||
| @ -27,10 +27,10 @@ object Crunchyroll { | ||||
|     private var keyPairID = "" | ||||
|  | ||||
|     // TODO temp helper vary | ||||
|     var locale = "${Locale.GERMANY.language}-${Locale.GERMANY.country}" | ||||
|     var country = Locale.GERMANY.country | ||||
|     private var locale: String = Preferences.preferredLocal.toLanguageTag() | ||||
|     private var country: String = Preferences.preferredLocal.country | ||||
|  | ||||
|     val browsingCache = arrayListOf<Item>() | ||||
|     private val browsingCache = arrayListOf<Item>() | ||||
|  | ||||
|     fun login(username: String, password: String): Boolean = runBlocking { | ||||
|         val tokenEndpoint = "/auth/v1/token" | ||||
|  | ||||
| @ -2,6 +2,7 @@ package org.mosad.teapod.parser.crunchyroll | ||||
|  | ||||
| import kotlinx.serialization.SerialName | ||||
| import kotlinx.serialization.Serializable | ||||
| import java.util.* | ||||
|  | ||||
| /** | ||||
|  * data classes for browse | ||||
| @ -36,6 +37,7 @@ val NoneSearchResult = SearchResult(0, emptyList()) | ||||
| data class BrowseResult(val total: Int, val items: List<Item>) | ||||
|  | ||||
| // the data class Item is used in browse and search | ||||
| // TODO rename to MediaPanel | ||||
| @Serializable | ||||
| data class Item( | ||||
|     val id: String, | ||||
| @ -74,14 +76,38 @@ val NoneSeries = Series("", "", "", Images(listOf(), listOf())) | ||||
|  * Seasons data type | ||||
|  */ | ||||
| @Serializable | ||||
| data class Seasons(val total: Int, val items: List<Season>) | ||||
| data class Seasons( | ||||
|     val total: Int, | ||||
|     val items: List<Season> | ||||
| ) { | ||||
|     fun getPreferredSeasonId(local: Locale): String { | ||||
|         // try to get the the first seasons which matches the preferred local | ||||
|         items.forEach { season -> | ||||
|             if (season.title.startsWith("(${local.language})", true)) { | ||||
|                 return season.id | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // if there is no season with the preferred local, try to find a subbed season | ||||
|         items.forEach { season -> | ||||
|             if (season.isSubbed) { | ||||
|                 return season.id | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // if there is no preferred language season and no sub, use the first season | ||||
|         return items.first().id | ||||
|     } | ||||
| } | ||||
|  | ||||
| @Serializable | ||||
| data class Season( | ||||
|     val id: String, | ||||
|     val title: String, | ||||
|     val series_id: String, | ||||
|     val season_number: Int | ||||
|     @SerialName("id") val id: String, | ||||
|     @SerialName("title") val title: String, | ||||
|     @SerialName("series_id") val seriesId: String, | ||||
|     @SerialName("season_number") val seasonNumber: Int, | ||||
|     @SerialName("is_subbed") val isSubbed: Boolean, | ||||
|     @SerialName("is_dubbed") val isDubbed: Boolean, | ||||
| ) | ||||
|  | ||||
| val NoneSeasons = Seasons(0, listOf()) | ||||
| @ -101,7 +127,7 @@ data class Episode( | ||||
|     @SerialName("season_id") val seasonId: String, | ||||
|     @SerialName("season_number") val seasonNumber: Int, | ||||
|     @SerialName("episode") val episode: String, | ||||
|     @SerialName("episode_number") val episodeNumber: Int, | ||||
|     @SerialName("episode_number") val episodeNumber: Int? = null, | ||||
|     @SerialName("description") val description: String, | ||||
|     @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 | ||||
|  | ||||
| @ -4,11 +4,14 @@ import android.content.Context | ||||
| import android.content.SharedPreferences | ||||
| import org.mosad.teapod.R | ||||
| import org.mosad.teapod.util.DataTypes | ||||
| import java.util.* | ||||
|  | ||||
| object Preferences { | ||||
|  | ||||
|     var preferSecondary = false | ||||
|         internal set | ||||
|     var preferredLocal = Locale.GERMANY | ||||
|         internal set | ||||
|     var autoplay = true | ||||
|         internal set | ||||
|     var devSettings = false | ||||
|  | ||||
| @ -15,6 +15,7 @@ import androidx.viewpager2.adapter.FragmentStateAdapter | ||||
| import com.bumptech.glide.Glide | ||||
| import com.bumptech.glide.request.RequestOptions | ||||
| import com.google.android.material.appbar.AppBarLayout | ||||
| import com.google.android.material.tabs.TabLayoutMediator | ||||
| import jp.wasabeef.glide.transformations.BlurTransformation | ||||
| import kotlinx.coroutines.launch | ||||
| import org.mosad.teapod.R | ||||
| @ -56,14 +57,14 @@ class MediaFragment(private val mediaIdStr: String, mediaCr: Item = NoneItem) : | ||||
|         // fix material components issue #1878, if more tabs are added increase | ||||
|         binding.pagerEpisodesSimilar.offscreenPageLimit = 2 | ||||
|         binding.pagerEpisodesSimilar.adapter = pagerAdapter | ||||
|         // TODO implement for cr media items | ||||
| //        TabLayoutMediator(binding.tabEpisodesSimilar, binding.pagerEpisodesSimilar) { tab, position -> | ||||
| //            tab.text = if (model.media.type == MediaType.TVSHOW && position == 0) { | ||||
| //                getString(R.string.episodes) | ||||
| //            } else { | ||||
| //                getString(R.string.similar_titles) | ||||
| //            } | ||||
| //        }.attach() | ||||
|         // TODO is position 0 always episodes? (and 1 always similar titles) | ||||
|         TabLayoutMediator(binding.tabEpisodesSimilar, binding.pagerEpisodesSimilar) { tab, position -> | ||||
|             tab.text = when(position) { | ||||
|                 0 -> getString(R.string.episodes) | ||||
|                 1 -> getString(R.string.similar_titles) | ||||
|                 else -> "" | ||||
|             } | ||||
|         }.attach() | ||||
|  | ||||
|         lifecycleScope.launch { | ||||
|             model.loadCrunchy(mediaIdStr) | ||||
| @ -77,9 +78,10 @@ class MediaFragment(private val mediaIdStr: String, mediaCr: Item = NoneItem) : | ||||
|         super.onResume() | ||||
|  | ||||
|         // update the next ep text if there is one, since it may have changed | ||||
|         if (model.media.getEpisodeById(model.nextEpisodeId).title.isNotEmpty()) { | ||||
|             binding.textTitle.text = model.media.getEpisodeById(model.nextEpisodeId).title | ||||
|         } | ||||
|         // TODO reimplement | ||||
| //        if (model.media.getEpisodeById(model.nextEpisodeId).title.isNotEmpty()) { | ||||
| //            binding.textTitle.text = model.media.getEpisodeById(model.nextEpisodeId).title | ||||
| //        } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @ -88,9 +90,9 @@ class MediaFragment(private val mediaIdStr: String, mediaCr: Item = NoneItem) : | ||||
|     private fun updateGUI() = with(model) { | ||||
|         // generic gui | ||||
|         val backdropUrl = tmdbResult?.backdropPath?.let { TMDBApiController.imageUrl + it } | ||||
|             ?: mediaCrunchy.images.poster_wide[0][2].source | ||||
|             ?: seriesCrunchy.images.poster_wide[0][2].source | ||||
|         val posterUrl = tmdbResult?.posterPath?.let { TMDBApiController.imageUrl + it } | ||||
|             ?: mediaCrunchy.images.poster_tall[0][2].source | ||||
|             ?: seriesCrunchy.images.poster_tall[0][2].source | ||||
|  | ||||
|         // load poster and backdrop | ||||
|         Glide.with(requireContext()).load(posterUrl) | ||||
| @ -100,65 +102,74 @@ class MediaFragment(private val mediaIdStr: String, mediaCr: Item = NoneItem) : | ||||
|             .apply(RequestOptions.bitmapTransform(BlurTransformation(20, 3))) | ||||
|             .into(binding.imageBackdrop) | ||||
|  | ||||
|         binding.textTitle.text = mediaCrunchy.title | ||||
|         //binding.textYear.text = media.year.toString() // TODO | ||||
|         //binding.textAge.text = media.age.toString() // TODO | ||||
|         binding.textOverview.text = mediaCrunchy.description | ||||
|         binding.textTitle.text = seriesCrunchy.title | ||||
|         //binding.textYear.text = media.year.toString() // TODO get from tmdb | ||||
|         //binding.textAge.text = media.age.toString() // TODO get from tmdb | ||||
|         binding.textOverview.text = seriesCrunchy.description | ||||
|  | ||||
|         // TODO set "my list" indicator | ||||
|         if (StorageController.myList.contains(media.aodId)) { | ||||
|             Glide.with(requireContext()).load(R.drawable.ic_baseline_check_24).into(binding.imageMyListAction) | ||||
|         } else { | ||||
|             Glide.with(requireContext()).load(R.drawable.ic_baseline_add_24).into(binding.imageMyListAction) | ||||
|         } | ||||
| //        if (StorageController.myList.contains(media.aodId)) { | ||||
| //            Glide.with(requireContext()).load(R.drawable.ic_baseline_check_24).into(binding.imageMyListAction) | ||||
| //        } else { | ||||
| //            Glide.with(requireContext()).load(R.drawable.ic_baseline_add_24).into(binding.imageMyListAction) | ||||
| //        } | ||||
|  | ||||
|         // clear fragments, since it lives in onCreate scope (don't do this in onPause/onStop -> FragmentManager transaction) | ||||
|         val fragmentsSize = if (fragments.lastIndex < 0) 0 else fragments.lastIndex | ||||
|         fragments.clear() | ||||
|         pagerAdapter.notifyItemRangeRemoved(0, fragmentsSize) | ||||
|  | ||||
|         // specific gui | ||||
|         if (mediaCrunchy.type == MediaType.TVSHOW.str) { | ||||
|             // TODO get next episode | ||||
| //            nextEpisodeId = media.playlist.firstOrNull{ !it.watched }?.mediaId | ||||
| //                ?: media.playlist.first().mediaId | ||||
|  | ||||
|             // TODO title is the next episodes title | ||||
| //            binding.textTitle.text = media.getEpisodeById(nextEpisodeId).title | ||||
|  | ||||
|             // episodes count | ||||
|             binding.textEpisodesOrRuntime.text = resources.getQuantityString( | ||||
|                 R.plurals.text_episodes_count, | ||||
|                 episodesCrunchy.total, | ||||
|                 episodesCrunchy.total | ||||
|             ) | ||||
|  | ||||
|             // episodes | ||||
|             MediaFragmentEpisodes().also { | ||||
|                 fragments.add(it) | ||||
|                 pagerAdapter.notifyItemInserted(fragments.indexOf(it)) | ||||
|             } | ||||
|         } else if (media.type == MediaType.MOVIE) { | ||||
|             val tmdbMovie = (tmdbResult as TMDBMovie?) | ||||
|  | ||||
|             if (tmdbMovie?.runtime != null) { | ||||
|                 binding.textEpisodesOrRuntime.text = resources.getQuantityString( | ||||
|                     R.plurals.text_runtime, | ||||
|                     tmdbMovie.runtime, | ||||
|                     tmdbMovie.runtime | ||||
|                 ) | ||||
|             } else { | ||||
|                 binding.textEpisodesOrRuntime.visibility = View.GONE | ||||
|             } | ||||
|         // add the episodes fragment (as tab) | ||||
|         MediaFragmentEpisodes().also { | ||||
|             fragments.add(it) | ||||
|             pagerAdapter.notifyItemInserted(fragments.indexOf(it)) | ||||
|         } | ||||
|  | ||||
|         // TODO reimplement via tmdb/metaDB | ||||
|         // specific gui | ||||
| //        if (mediaCrunchy.type == MediaType.TVSHOW.str) { | ||||
| //            // TODO get next episode | ||||
| ////            nextEpisodeId = media.playlist.firstOrNull{ !it.watched }?.mediaId | ||||
| ////                ?: media.playlist.first().mediaId | ||||
| // | ||||
| //            // TODO title is the next episodes title | ||||
| ////            binding.textTitle.text = media.getEpisodeById(nextEpisodeId).title | ||||
| // | ||||
| //            // episodes count | ||||
| //            binding.textEpisodesOrRuntime.text = resources.getQuantityString( | ||||
| //                R.plurals.text_episodes_count, | ||||
| //                episodesCrunchy.total, | ||||
| //                episodesCrunchy.total | ||||
| //            ) | ||||
| // | ||||
| //            // episodes | ||||
| //            MediaFragmentEpisodes().also { | ||||
| //                fragments.add(it) | ||||
| //                pagerAdapter.notifyItemInserted(fragments.indexOf(it)) | ||||
| //            } | ||||
| //        } else if (media.type == MediaType.MOVIE) { | ||||
| //            val tmdbMovie = (tmdbResult as TMDBMovie?) | ||||
| // | ||||
| //            if (tmdbMovie?.runtime != null) { | ||||
| //                binding.textEpisodesOrRuntime.text = resources.getQuantityString( | ||||
| //                    R.plurals.text_runtime, | ||||
| //                    tmdbMovie.runtime, | ||||
| //                    tmdbMovie.runtime | ||||
| //                ) | ||||
| //            } else { | ||||
| //                binding.textEpisodesOrRuntime.visibility = View.GONE | ||||
| //            } | ||||
| //        } | ||||
|  | ||||
|         // if has similar titles | ||||
|         if (media.similar.isNotEmpty()) { | ||||
|             MediaFragmentSimilar().also { | ||||
|                 fragments.add(it) | ||||
|                 pagerAdapter.notifyItemInserted(fragments.indexOf(it)) | ||||
|             } | ||||
|         } | ||||
|         // TODO reimplement | ||||
| //        if (media.similar.isNotEmpty()) { | ||||
| //            MediaFragmentSimilar().also { | ||||
| //                fragments.add(it) | ||||
| //                pagerAdapter.notifyItemInserted(fragments.indexOf(it)) | ||||
| //            } | ||||
| //        } | ||||
|  | ||||
|         // disable scrolling on appbar, if no tabs where added | ||||
|         if(fragments.isEmpty()) { | ||||
| @ -171,28 +182,30 @@ class MediaFragment(private val mediaIdStr: String, mediaCr: Item = NoneItem) : | ||||
|  | ||||
|     private fun initActions() = with(model) { | ||||
|         binding.buttonPlay.setOnClickListener { | ||||
|             when (media.type) { | ||||
|                 //MediaType.MOVIE -> playEpisode(media.playlist.first().mediaId) // TODO | ||||
|                 //MediaType.TVSHOW -> playEpisode(nextEpisodeId) // TODO | ||||
|                 else -> Log.e(javaClass.name, "Wrong Type: ${media.type}") | ||||
|             } | ||||
|             // TODO reimplement | ||||
| //            when (media.type) { | ||||
| //                MediaType.MOVIE -> playEpisode(media.playlist.first().mediaId) | ||||
| //                MediaType.TVSHOW -> playEpisode(nextEpisodeId) | ||||
| //                else -> Log.e(javaClass.name, "Wrong Type: ${media.type}") | ||||
| //            } | ||||
|         } | ||||
|  | ||||
|         // add or remove media from myList | ||||
|         binding.linearMyListAction.setOnClickListener { | ||||
|             if (StorageController.myList.contains(media.aodId)) { | ||||
|                 StorageController.myList.remove(media.aodId) | ||||
|                 Glide.with(requireContext()).load(R.drawable.ic_baseline_add_24).into(binding.imageMyListAction) | ||||
|             } else { | ||||
|                 StorageController.myList.add(media.aodId) | ||||
|                 Glide.with(requireContext()).load(R.drawable.ic_baseline_check_24).into(binding.imageMyListAction) | ||||
|             } | ||||
|             StorageController.saveMyList(requireContext()) | ||||
|  | ||||
|             // notify home fragment on change | ||||
|             parentFragmentManager.findFragmentByTag("HomeFragment")?.let { | ||||
|                 (it as HomeFragment).updateMyListMedia() | ||||
|             } | ||||
|             // TODO reimplement | ||||
| //            if (StorageController.myList.contains(media.aodId)) { | ||||
| //                StorageController.myList.remove(media.aodId) | ||||
| //                Glide.with(requireContext()).load(R.drawable.ic_baseline_add_24).into(binding.imageMyListAction) | ||||
| //            } else { | ||||
| //                StorageController.myList.add(media.aodId) | ||||
| //                Glide.with(requireContext()).load(R.drawable.ic_baseline_check_24).into(binding.imageMyListAction) | ||||
| //            } | ||||
| //            StorageController.saveMyList(requireContext()) | ||||
| // | ||||
| //            // notify home fragment on change | ||||
| //            parentFragmentManager.findFragmentByTag("HomeFragment")?.let { | ||||
| //                (it as HomeFragment).updateMyListMedia() | ||||
| //            } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
| @ -44,10 +44,11 @@ class MediaFragmentEpisodes : Fragment() { | ||||
|  | ||||
|         // if adapterRecEpisodes is initialized, update the watched state for the episodes | ||||
|         if (this::adapterRecEpisodes.isInitialized) { | ||||
|             model.media.playlist.forEachIndexed { index, episodeInfo -> | ||||
|                 adapterRecEpisodes.updateWatchedState(episodeInfo.watched, index) | ||||
|             } | ||||
|             adapterRecEpisodes.notifyDataSetChanged() | ||||
|             // TODO reimplement, if needed | ||||
| //            model.media.playlist.forEachIndexed { index, episodeInfo -> | ||||
| //                adapterRecEpisodes.updateWatchedState(episodeInfo.watched, index) | ||||
| //            } | ||||
| //            adapterRecEpisodes.notifyDataSetChanged() | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
| @ -27,7 +27,7 @@ class MediaFragmentSimilar : Fragment()  { | ||||
|     override fun onViewCreated(view: View, savedInstanceState: Bundle?) { | ||||
|         super.onViewCreated(view, savedInstanceState) | ||||
|  | ||||
|         adapterSimilar = MediaItemAdapter(model.media.similar) | ||||
|         adapterSimilar = MediaItemAdapter(emptyList()) //(model.media.similar) | ||||
|         binding.recyclerMediaSimilar.adapter = adapterSimilar | ||||
|         binding.recyclerMediaSimilar.addItemDecoration(MediaItemDecoration(9)) | ||||
|  | ||||
|  | ||||
| @ -3,9 +3,16 @@ package org.mosad.teapod.ui.activity.main.viewmodel | ||||
| import android.app.Application | ||||
| import android.util.Log | ||||
| import androidx.lifecycle.AndroidViewModel | ||||
| import org.mosad.teapod.parser.crunchyroll.* | ||||
| import org.mosad.teapod.util.* | ||||
| import androidx.lifecycle.viewModelScope | ||||
| import kotlinx.coroutines.joinAll | ||||
| import kotlinx.coroutines.launch | ||||
| import org.mosad.teapod.parser.crunchyroll.Crunchyroll | ||||
| import org.mosad.teapod.parser.crunchyroll.NoneEpisodes | ||||
| import org.mosad.teapod.parser.crunchyroll.NoneSeasons | ||||
| import org.mosad.teapod.parser.crunchyroll.NoneSeries | ||||
| import org.mosad.teapod.preferences.Preferences | ||||
| import org.mosad.teapod.util.DataTypes.MediaType | ||||
| import org.mosad.teapod.util.Meta | ||||
| import org.mosad.teapod.util.tmdb.TMDBApiController | ||||
| import org.mosad.teapod.util.tmdb.TMDBResult | ||||
| import org.mosad.teapod.util.tmdb.TMDBTVSeason | ||||
| @ -16,12 +23,9 @@ import org.mosad.teapod.util.tmdb.TMDBTVSeason | ||||
|  */ | ||||
| class MediaFragmentViewModel(application: Application) : AndroidViewModel(application) { | ||||
|  | ||||
|     var media = AoDMediaNone | ||||
|         internal set | ||||
|     var nextEpisodeId = -1 | ||||
|         internal set | ||||
|  | ||||
|     var mediaCrunchy = NoneItem | ||||
| //    var mediaCrunchy = NoneItem | ||||
| //        internal set | ||||
|     var seriesCrunchy = NoneSeries // TODO it seems movies also series? | ||||
|         internal set | ||||
|     var seasonsCrunchy = NoneSeasons | ||||
|         internal set | ||||
| @ -35,34 +39,31 @@ class MediaFragmentViewModel(application: Application) : AndroidViewModel(applic | ||||
|     var mediaMeta: Meta? = null | ||||
|         internal set | ||||
|  | ||||
|     /** | ||||
|      * @param crunchyId the crunchyroll series id | ||||
|      */ | ||||
|     suspend fun loadCrunchy(crunchyId: String) { | ||||
|         val tmdbApiController = TMDBApiController() | ||||
|  | ||||
|         println("loading crunchyroll media $crunchyId") | ||||
|         // load series and seasons info in parallel | ||||
|         listOf( | ||||
|             viewModelScope.launch { seriesCrunchy = Crunchyroll.series(crunchyId) }, | ||||
|             viewModelScope.launch { seasonsCrunchy = Crunchyroll.seasons(crunchyId) } | ||||
|         ).joinAll() | ||||
|  | ||||
|         // TODO info also in browse result item | ||||
|         // TODO doesn't support search | ||||
|         mediaCrunchy = Crunchyroll.browsingCache.find { it -> | ||||
|             it.id == crunchyId | ||||
|         } ?: NoneItem | ||||
|         println("media: $mediaCrunchy") | ||||
|  | ||||
|         // load seasons | ||||
|         seasonsCrunchy = Crunchyroll.seasons(crunchyId) | ||||
|         println("series: $seriesCrunchy") | ||||
|         println("seasons: $seasonsCrunchy") | ||||
|  | ||||
|         // load first season | ||||
|         // TODO make sure to load the preferred season (language), language is set per season, not per stream | ||||
|         episodesCrunchy = Crunchyroll.episodes(seasonsCrunchy.items.first().id) | ||||
|         // load the preferred season (preferred language, language per season, not per stream) | ||||
|         val preferredSeasonId = seasonsCrunchy.getPreferredSeasonId(Preferences.preferredLocal) | ||||
|         episodesCrunchy = Crunchyroll.episodes(preferredSeasonId) | ||||
|         println("episodes: $episodesCrunchy") | ||||
|  | ||||
|  | ||||
|  | ||||
|         // TODO check if metaDB knows the title | ||||
|  | ||||
|         // use tmdb search to get media info TODO media type is hardcoded, use type info from browse result once implemented | ||||
|         // use tmdb search to get media info TODO media type is hardcoded, use episodeNumber? (if null it should be a movie) | ||||
|         mediaMeta = null // set mediaMeta to null, if metaDB doesn't know the media | ||||
|         val tmdbId = tmdbApiController.search(mediaCrunchy.title, MediaType.TVSHOW) | ||||
|         val tmdbId = tmdbApiController.search(seriesCrunchy.title, MediaType.TVSHOW) | ||||
|  | ||||
|         tmdbResult = when (MediaType.TVSHOW) { | ||||
|             MediaType.MOVIE -> tmdbApiController.getMovieDetails(tmdbId) | ||||
| @ -122,10 +123,11 @@ class MediaFragmentViewModel(application: Application) : AndroidViewModel(applic | ||||
|      * if no matching is found, use first episode | ||||
|      */ | ||||
|     fun updateNextEpisode(episodeId: Int) { | ||||
|         if (media.type == MediaType.MOVIE) return // return if movie | ||||
|  | ||||
|         nextEpisodeId = media.playlist.firstOrNull { it.index > media.getEpisodeById(episodeId).index }?.mediaId | ||||
|             ?: media.playlist.first().mediaId | ||||
|         // TODO reimplement if needed | ||||
| //        if (media.type == MediaType.MOVIE) return // return if movie | ||||
| // | ||||
| //        nextEpisodeId = media.playlist.firstOrNull { it.index > media.getEpisodeById(episodeId).index }?.mediaId | ||||
| //            ?: media.playlist.first().mediaId | ||||
|     } | ||||
|  | ||||
|     // remove unneeded info from the media title before searching | ||||
|  | ||||
| @ -168,7 +168,9 @@ class PlayerViewModel(application: Application) : AndroidViewModel(application) | ||||
|         currentEpisodeChangedListener.forEach { it() } | ||||
|  | ||||
|         // get preferred stream url TODO implement | ||||
|         val url = currentPlayback.streams.adaptive_hls["en-US"]?.url ?: "" | ||||
|         val localeKey = Preferences.preferredLocal.toLanguageTag() | ||||
|         val url = currentPlayback.streams.adaptive_hls[localeKey]?.url | ||||
|             ?: currentPlayback.streams.adaptive_hls[""]?.url ?: "" | ||||
|         println("stream url: $url") | ||||
|  | ||||
|         // create the media source object | ||||
|  | ||||
| @ -34,7 +34,7 @@ class EpisodesListPlayer @JvmOverloads constructor( | ||||
|                 model.setCurrentEpisode(episodeId, startPlayback = true) | ||||
|             } | ||||
|             // episodeNumber starts at 1, we need the episode index -> - 1 | ||||
|             adapterRecEpisodes.currentSelected = (model.currentEpisode.episodeNumber - 1) | ||||
|             adapterRecEpisodes.currentSelected = model.currentEpisode.episodeNumber?.minus(1) ?: 0 | ||||
|  | ||||
|             binding.recyclerEpisodesPlayer.adapter = adapterRecEpisodes | ||||
|             binding.recyclerEpisodesPlayer.scrollToPosition(adapterRecEpisodes.currentSelected) | ||||
|  | ||||
		Reference in New Issue
	
	Block a user