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