crunchyroll support #49
|
@ -39,6 +39,7 @@ class MediaFragmentEpisodes : Fragment() {
|
|||
playEpisode(seasonId, episodeId)
|
||||
}
|
||||
|
||||
// TODO don't show selection if only one season is present
|
||||
binding.buttonSeasonSelection.text = model.currentSeasonCrunchy.title
|
||||
binding.buttonSeasonSelection.setOnClickListener { v ->
|
||||
showSeasonSelection(v)
|
||||
|
|
|
@ -30,7 +30,7 @@ class MediaFragmentViewModel(application: Application) : AndroidViewModel(applic
|
|||
internal set
|
||||
var episodesCrunchy = NoneEpisodes
|
||||
internal set
|
||||
val currentEpisodesCrunchy = arrayListOf<Episode>()
|
||||
val currentEpisodesCrunchy = arrayListOf<Episode>() // used for EpisodeItemAdapter (easier updates)
|
||||
|
||||
var tmdbResult: TMDBResult? = null // TODO rename
|
||||
internal set
|
||||
|
@ -57,6 +57,7 @@ class MediaFragmentViewModel(application: Application) : AndroidViewModel(applic
|
|||
// load the preferred season (preferred language, language per season, not per stream)
|
||||
currentSeasonCrunchy = seasonsCrunchy.getPreferredSeason(Preferences.preferredLocal)
|
||||
episodesCrunchy = Crunchyroll.episodes(currentSeasonCrunchy.id)
|
||||
currentEpisodesCrunchy.clear()
|
||||
currentEpisodesCrunchy.addAll(episodesCrunchy.items)
|
||||
println("episodes: $episodesCrunchy")
|
||||
|
||||
|
@ -90,7 +91,6 @@ class MediaFragmentViewModel(application: Application) : AndroidViewModel(applic
|
|||
|
||||
/**
|
||||
* set media, tmdb and nextEpisode
|
||||
* TODO run aod and tmdb load parallel
|
||||
*/
|
||||
// suspend fun loadAoD(aodId: Int) {
|
||||
// val tmdbApiController = TMDBApiController()
|
||||
|
@ -146,30 +146,4 @@ class MediaFragmentViewModel(application: Application) : AndroidViewModel(applic
|
|||
// ?: media.playlist.first().mediaId
|
||||
}
|
||||
|
||||
// remove unneeded info from the media title before searching
|
||||
private fun stripTitleInfo(title: String): String {
|
||||
return title.replace("(Sub)", "")
|
||||
.replace(Regex("-?\\s?[0-9]+.\\s?(Staffel|Season)"), "")
|
||||
.replace(Regex("(Staffel|Season)\\s?[0-9]+"), "")
|
||||
.trim()
|
||||
}
|
||||
|
||||
/** guess Season from title
|
||||
* if the title ends with a number, that could be the season
|
||||
* if the title ends with Regex("-?\\s?[0-9]+.\\s?(Staffel|Season)") or
|
||||
* Regex("(Staffel|Season)\\s?[0-9]+"), that is the season information
|
||||
*/
|
||||
private fun guessSeasonFromTitle(title: String): Int {
|
||||
val helpTitle = title.replace("(Sub)", "").trim()
|
||||
Log.d("test", "helpTitle: $helpTitle")
|
||||
|
||||
return if (helpTitle.last().isDigit()) {
|
||||
helpTitle.last().digitToInt()
|
||||
} else {
|
||||
Regex("([0-9]+.\\s?(Staffel|Season))|((Staffel|Season)\\s?[0-9]+)")
|
||||
.find(helpTitle)
|
||||
?.value?.filter { it.isDigit() }?.toInt() ?: 1
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -52,10 +52,10 @@ class PlayerViewModel(application: Application) : AndroidViewModel(application)
|
|||
internal set
|
||||
var currentEpisode = NoneEpisode
|
||||
internal set
|
||||
private var currentPlayback = NonePlayback
|
||||
var currentPlayback = NonePlayback
|
||||
|
||||
// current playback settings
|
||||
var currentLanguage: Locale = Locale.ROOT
|
||||
var currentLanguage: Locale = Preferences.preferredLocal
|
||||
internal set
|
||||
|
||||
init {
|
||||
|
@ -113,11 +113,6 @@ class PlayerViewModel(application: Application) : AndroidViewModel(application)
|
|||
fun setLanguage(language: Locale) {
|
||||
currentLanguage = language
|
||||
playCurrentMedia(player.currentPosition)
|
||||
|
||||
// val mediaSource = HlsMediaSource.Factory(dataSourceFactory).createMediaSource(
|
||||
// MediaItem.fromUri(Uri.parse(currentEpisodeAoD.getPreferredStream(language).url))
|
||||
// )
|
||||
// playMedia(mediaSource, seekTime)
|
||||
}
|
||||
|
||||
// player actions
|
||||
|
@ -167,10 +162,24 @@ class PlayerViewModel(application: Application) : AndroidViewModel(application)
|
|||
// update player gui (title, next ep button) after nextEpisodeId has been set
|
||||
currentEpisodeChangedListener.forEach { it() }
|
||||
|
||||
// get preferred stream url TODO implement
|
||||
val localeKey = Preferences.preferredLocal.toLanguageTag()
|
||||
val url = currentPlayback.streams.adaptive_hls[localeKey]?.url
|
||||
?: currentPlayback.streams.adaptive_hls[""]?.url ?: ""
|
||||
// get preferred stream url, set current language if it differs from the preferred one
|
||||
val preferredLocale = currentLanguage
|
||||
val fallbackLocal = Locale.US
|
||||
val url = when {
|
||||
currentPlayback.streams.adaptive_hls.containsKey(preferredLocale.toLanguageTag()) -> {
|
||||
currentPlayback.streams.adaptive_hls[preferredLocale.toLanguageTag()]?.url
|
||||
}
|
||||
currentPlayback.streams.adaptive_hls.containsKey(fallbackLocal.toLanguageTag()) -> {
|
||||
currentLanguage = fallbackLocal
|
||||
currentPlayback.streams.adaptive_hls[fallbackLocal.toLanguageTag()]?.url
|
||||
}
|
||||
else -> {
|
||||
currentLanguage = Locale.ROOT
|
||||
currentPlayback.streams.adaptive_hls[Locale.ROOT.toLanguageTag()]?.url ?: ""
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
println("stream url: $url")
|
||||
|
||||
// create the media source object
|
||||
|
@ -187,17 +196,18 @@ class PlayerViewModel(application: Application) : AndroidViewModel(application)
|
|||
// TODO reimplement mark as watched for cr, if needed
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current episode title (with episode number, if it's a tv show)
|
||||
*/
|
||||
fun getMediaTitle(): String {
|
||||
// TODO add tvshow/movie diff
|
||||
val isTVShow = true
|
||||
return if(isTVShow) {
|
||||
// currentEpisode.episodeNumber defines the media type (tv show = none null, movie = null)
|
||||
return if (currentEpisode.episodeNumber != null) {
|
||||
getApplication<Application>().getString(
|
||||
R.string.component_episode_title,
|
||||
currentEpisode.episode,
|
||||
currentEpisode.title
|
||||
)
|
||||
} else {
|
||||
// TODO movie
|
||||
currentEpisode.title
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ import org.mosad.teapod.databinding.PlayerLanguageSettingsBinding
|
|||
import org.mosad.teapod.ui.activity.player.PlayerViewModel
|
||||
import java.util.*
|
||||
|
||||
// TODO port to DialogFragment
|
||||
class LanguageSettingsPlayer @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
|
@ -24,34 +25,34 @@ class LanguageSettingsPlayer @JvmOverloads constructor(
|
|||
) : LinearLayout(context, attrs, defStyleAttr) {
|
||||
|
||||
private val binding = PlayerLanguageSettingsBinding.inflate(LayoutInflater.from(context), this, true)
|
||||
var onViewRemovedAction: (() -> Unit)? = null // TODO find a better solution for this
|
||||
var onViewRemovedAction: (() -> Unit)? = null
|
||||
|
||||
private var currentLanguage = model?.currentLanguage ?: Locale.ROOT
|
||||
private var selectedLocale = model?.currentLanguage ?: Locale.ROOT
|
||||
|
||||
init {
|
||||
model?.let {
|
||||
// TODO reimplement for cr
|
||||
// it.currentEpisode.streams.forEach { stream ->
|
||||
// addLanguage(stream.language.displayName, stream.language == currentLanguage) {
|
||||
// currentLanguage = stream.language
|
||||
// updateSelectedLanguage(it as TextView)
|
||||
// }
|
||||
// }
|
||||
model?.let { m ->
|
||||
m.currentPlayback.streams.adaptive_hls.keys.forEach { languageTag ->
|
||||
val locale = Locale.forLanguageTag(languageTag)
|
||||
addLanguage(locale, locale == m.currentLanguage) { v ->
|
||||
selectedLocale = locale
|
||||
updateSelectedLanguage(v as TextView)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
binding.buttonCloseLanguageSettings.setOnClickListener { close() }
|
||||
binding.buttonCancel.setOnClickListener { close() }
|
||||
binding.buttonSelect.setOnClickListener {
|
||||
model?.setLanguage(currentLanguage)
|
||||
model?.setLanguage(selectedLocale)
|
||||
close()
|
||||
}
|
||||
}
|
||||
|
||||
private fun addLanguage(str: String, isSelected: Boolean, onClick: OnClickListener) {
|
||||
private fun addLanguage(locale: Locale, isSelected: Boolean, onClick: OnClickListener) {
|
||||
val text = TextView(context).apply {
|
||||
height = 96
|
||||
gravity = Gravity.CENTER_VERTICAL
|
||||
text = str
|
||||
text = if (locale == Locale.ROOT) context.getString(R.string.no_subtitles) else locale.displayLanguage
|
||||
setTextSize(TypedValue.COMPLEX_UNIT_SP, 16f)
|
||||
|
||||
if (isSelected) {
|
||||
|
|
|
@ -25,10 +25,15 @@ class EpisodeItemAdapter(private val episodes: List<Episode>, private val tmdbEp
|
|||
val context = holder.binding.root.context
|
||||
val ep = episodes[position]
|
||||
|
||||
val titleText = if (ep.isDubbed) {
|
||||
context.getString(R.string.component_episode_title, ep.episode, ep.title)
|
||||
val titleText = if (ep.episodeNumber != null) {
|
||||
// for tv shows add ep prefix and episode number
|
||||
if (ep.isDubbed) {
|
||||
context.getString(R.string.component_episode_title, ep.episode, ep.title)
|
||||
} else {
|
||||
context.getString(R.string.component_episode_title_sub, ep.episode, ep.title)
|
||||
}
|
||||
} else {
|
||||
context.getString(R.string.component_episode_title_sub, ep.episode, ep.title)
|
||||
ep.title
|
||||
}
|
||||
|
||||
holder.binding.textEpisodeTitle.text = titleText
|
||||
|
|
|
@ -25,10 +25,15 @@ class PlayerEpisodeItemAdapter(private val episodes: Episodes, private val tmdbE
|
|||
val context = holder.binding.root.context
|
||||
val ep = episodes.items[position]
|
||||
|
||||
val titleText = if (ep.isDubbed) {
|
||||
context.getString(R.string.component_episode_title, ep.episode, ep.title)
|
||||
val titleText = if (ep.episodeNumber != null) {
|
||||
// for tv shows add ep prefix and episode number
|
||||
if (ep.isDubbed) {
|
||||
context.getString(R.string.component_episode_title, ep.episode, ep.title)
|
||||
} else {
|
||||
context.getString(R.string.component_episode_title_sub, ep.episode, ep.title)
|
||||
}
|
||||
} else {
|
||||
context.getString(R.string.component_episode_title_sub, ep.episode, ep.title)
|
||||
ep.title
|
||||
}
|
||||
|
||||
holder.binding.textEpisodeTitle2.text = titleText
|
||||
|
|
|
@ -125,7 +125,7 @@
|
|||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="7dp"
|
||||
android:text="@string/language"
|
||||
android:text="@string/subtitles"
|
||||
android:textAllCaps="false"
|
||||
app:icon="@drawable/ic_baseline_subtitles_24"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="44dp"
|
||||
android:text="@string/language"
|
||||
android:text="@string/subtitles"
|
||||
android:textAlignment="center"
|
||||
android:textColor="@color/exo_white"
|
||||
android:textSize="16sp"
|
||||
|
|
|
@ -71,8 +71,10 @@
|
|||
<string name="next_episode">Nächste Folge</string>
|
||||
<string name="skip_opening">Intro überspringen</string>
|
||||
<string name="language">Sprache</string>
|
||||
<string name="subtitles">Untertitel</string>
|
||||
<string name="episodes">Folgen</string>
|
||||
<string name="episode">Folge</string>
|
||||
<string name="no_subtitles">Aus</string>
|
||||
|
||||
<!-- Onboarding -->
|
||||
<string name="skip">Überspringen</string>
|
||||
|
|
|
@ -92,8 +92,10 @@
|
|||
<string name="time_min_sec" translatable="false">%1$02d:%2$02d</string>
|
||||
<string name="time_hour_min_sec" translatable="false">%1$d:%2$02d:%3$02d</string>
|
||||
<string name="language">Language</string>
|
||||
<string name="subtitles">Subtitles</string>
|
||||
<string name="episodes">Episodes</string>
|
||||
<string name="episode">Episode</string>
|
||||
<string name="no_subtitles">None</string>
|
||||
|
||||
<!-- Onboarding -->
|
||||
<string name="skip">Skip</string>
|
||||
|
|
Loading…
Reference in New Issue