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