cloudstream/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt

1353 lines
51 KiB
Kotlin
Raw Normal View History

2022-01-07 19:27:25 +00:00
package com.lagradost.cloudstream3.ui.player
import android.animation.ValueAnimator
2022-01-07 19:27:25 +00:00
import android.annotation.SuppressLint
import android.app.Dialog
import android.content.Context
2022-01-07 19:27:25 +00:00
import android.content.Intent
import android.content.res.ColorStateList
2022-01-07 19:27:25 +00:00
import android.os.Bundle
2022-04-08 19:38:19 +00:00
import android.util.Log
2022-01-07 19:27:25 +00:00
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.EditorInfo
2022-01-07 19:27:25 +00:00
import android.widget.*
import android.widget.TextView.OnEditorActionListener
2022-01-07 19:27:25 +00:00
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AlertDialog
import androidx.core.animation.addListener
import androidx.core.content.ContextCompat
2022-01-07 19:27:25 +00:00
import androidx.core.view.isGone
import androidx.core.view.isVisible
import androidx.lifecycle.ViewModelProvider
2022-04-08 19:38:19 +00:00
import androidx.preference.PreferenceManager
import com.google.android.exoplayer2.Format.NO_VALUE
2022-02-13 20:59:41 +00:00
import com.google.android.exoplayer2.util.MimeTypes
2022-01-07 19:27:25 +00:00
import com.hippo.unifile.UniFile
import com.lagradost.cloudstream3.*
2022-02-11 09:17:04 +00:00
import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull
2022-01-07 19:27:25 +00:00
import com.lagradost.cloudstream3.CommonActivity.showToast
2023-01-17 15:57:46 +00:00
import com.lagradost.cloudstream3.mvvm.*
import com.lagradost.cloudstream3.subtitles.AbstractSubtitleEntities
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.subtitleProviders
import com.lagradost.cloudstream3.ui.player.CS3IPlayer.Companion.preferredAudioTrackLanguage
2022-05-30 13:14:41 +00:00
import com.lagradost.cloudstream3.ui.player.CustomDecoder.Companion.updateForcedEncoding
2022-01-07 19:27:25 +00:00
import com.lagradost.cloudstream3.ui.player.PlayerSubtitleHelper.Companion.toSubtitleMimeType
2023-01-17 15:57:46 +00:00
import com.lagradost.cloudstream3.ui.result.*
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
import com.lagradost.cloudstream3.ui.subtitles.SubtitlesFragment.Companion.getAutoSelectLanguageISO639_1
2022-01-08 19:39:22 +00:00
import com.lagradost.cloudstream3.utils.*
2022-02-11 09:17:04 +00:00
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
import com.lagradost.cloudstream3.utils.Coroutines.runOnMainThread
2022-05-30 13:14:41 +00:00
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showDialog
import com.lagradost.cloudstream3.utils.SubtitleHelper.fromTwoLettersToLanguage
import com.lagradost.cloudstream3.utils.SubtitleHelper.languages
import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute
2022-01-07 19:27:25 +00:00
import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe
import com.lagradost.cloudstream3.utils.UIHelper.hideSystemUI
import com.lagradost.cloudstream3.utils.UIHelper.popCurrentPage
import com.lagradost.cloudstream3.utils.UIHelper.toPx
import kotlinx.android.synthetic.main.dialog_online_subtitles.*
import kotlinx.android.synthetic.main.dialog_online_subtitles.apply_btt
import kotlinx.android.synthetic.main.dialog_online_subtitles.cancel_btt
2022-01-07 19:27:25 +00:00
import kotlinx.android.synthetic.main.fragment_player.*
import kotlinx.android.synthetic.main.player_custom_layout.*
2022-05-30 13:14:41 +00:00
import kotlinx.android.synthetic.main.player_select_source_and_subs.*
import kotlinx.android.synthetic.main.player_select_source_and_subs.subtitles_click_settings
import kotlinx.android.synthetic.main.player_select_tracks.*
2022-02-11 09:17:04 +00:00
import kotlinx.coroutines.Job
2023-01-17 15:57:46 +00:00
import java.util.*
import kotlin.collections.ArrayList
import kotlin.collections.HashMap
2022-01-07 19:27:25 +00:00
class GeneratorPlayer : FullScreenPlayer() {
companion object {
private var lastUsedGenerator: IGenerator? = null
2022-04-08 19:38:19 +00:00
fun newInstance(generator: IGenerator, syncData: HashMap<String, String>? = null): Bundle {
Log.i(TAG, "newInstance = $syncData")
2022-01-07 19:27:25 +00:00
lastUsedGenerator = generator
2022-04-08 19:38:19 +00:00
return Bundle().apply {
if (syncData != null) putSerializable("syncData", syncData)
2022-04-08 19:38:19 +00:00
}
2022-01-07 19:27:25 +00:00
}
val subsProviders
get() = subtitleProviders.filter { !it.requiresLogin || it.loginInfo() != null }
val subsProvidersIsActive
get() = subsProviders.isNotEmpty()
2022-01-07 19:27:25 +00:00
}
2022-05-15 18:38:32 +00:00
private var titleRez = 3
private var limitTitle = 0
2022-01-07 19:27:25 +00:00
private lateinit var viewModel: PlayerGeneratorViewModel //by activityViewModels()
2022-04-08 19:38:19 +00:00
private lateinit var sync: SyncViewModel
2022-01-07 19:27:25 +00:00
private var currentLinks: Set<Pair<ExtractorLink?, ExtractorUri?>> = setOf()
private var currentSubs: Set<SubtitleData> = setOf()
private var currentSelectedLink: Pair<ExtractorLink?, ExtractorUri?>? = null
private var currentSelectedSubtitles: SubtitleData? = null
private var currentMeta: Any? = null
private var nextMeta: Any? = null
private var isActive: Boolean = false
private var isNextEpisode: Boolean = false // this is used to reset the watch time
2022-01-08 19:39:22 +00:00
private var preferredAutoSelectSubtitles: String? = null // null means do nothing, "" means none
2022-01-07 19:27:25 +00:00
private fun startLoading() {
player.release()
currentSelectedSubtitles = null
isActive = false
overlay_loading_skip_button?.isVisible = false
player_loading_overlay?.isVisible = true
}
private fun setSubtitles(sub: SubtitleData?): Boolean {
currentSelectedSubtitles = sub
//Log.i(TAG, "setSubtitles = $sub")
2022-01-07 19:27:25 +00:00
return player.setPreferredSubtitles(sub)
}
2022-05-20 18:25:56 +00:00
override fun embeddedSubtitlesFetched(subtitles: List<SubtitleData>) {
viewModel.addSubtitles(subtitles.toSet())
}
override fun onTracksInfoChanged() {
val tracks = player.getVideoTracks()
player_tracks_btt?.isVisible =
tracks.allVideoTracks.size > 1 || tracks.allAudioTracks.size > 1
// Only set the preferred language if it is available.
// Otherwise it may give some users audio track init failed!
if (tracks.allAudioTracks.any { it.language == preferredAudioTrackLanguage }) {
player.setPreferredAudioTrack(preferredAudioTrackLanguage)
}
}
2022-01-07 19:27:25 +00:00
private fun noSubtitles(): Boolean {
return setSubtitles(null)
}
2022-01-30 22:02:57 +00:00
private fun getPos(): Long {
val durPos = DataStoreHelper.getViewPos(viewModel.getId()) ?: return 0L
if (durPos.duration == 0L) return 0L
if (durPos.position * 100L / durPos.duration > 95L) {
return 0L
}
return durPos.position
}
2022-02-11 09:17:04 +00:00
var currentVerifyLink: Job? = null
private fun loadExtractorJob(extractorLink: ExtractorLink?) {
currentVerifyLink?.cancel()
2022-08-03 00:04:03 +00:00
2022-08-07 23:03:54 +00:00
extractorLink?.let { link ->
2022-02-11 09:17:04 +00:00
currentVerifyLink = ioSafe {
2022-08-07 23:03:54 +00:00
if (link.extractorData != null) {
getApiFromNameNull(link.source)?.extractorVerifierJob(link.extractorData)
2022-02-11 09:17:04 +00:00
}
}
}
}
2022-01-07 19:27:25 +00:00
private fun loadLink(link: Pair<ExtractorLink?, ExtractorUri?>?, sameEpisode: Boolean) {
if (link == null) return
// manage UI
player_loading_overlay?.isVisible = false
uiReset()
currentSelectedLink = link
currentMeta = viewModel.getMeta()
nextMeta = viewModel.getNextMeta()
setEpisodes(viewModel.getAllMeta() ?: emptyList())
2022-01-07 19:27:25 +00:00
isActive = true
setPlayerDimen(null)
setTitle()
2022-11-04 23:40:31 +00:00
if (!sameEpisode)
hasRequestedStamps = false
2022-01-07 19:27:25 +00:00
2022-02-11 09:17:04 +00:00
loadExtractorJob(link.first)
2022-01-07 19:27:25 +00:00
// load player
context?.let { ctx ->
val (url, uri) = link
player.loadPlayer(
ctx,
sameEpisode,
url,
uri,
startPosition = if (sameEpisode) null else {
2022-01-30 22:02:57 +00:00
if (isNextEpisode) 0L else getPos()
2022-01-07 19:27:25 +00:00
},
currentSubs,
2022-04-08 19:38:19 +00:00
(if (sameEpisode) currentSelectedSubtitles else null) ?: getAutoSelectSubtitle(
currentSubs, settings = true, downloads = true
2022-04-08 19:38:19 +00:00
),
2022-01-07 19:27:25 +00:00
)
}
2022-11-04 23:40:31 +00:00
if (!sameEpisode)
player.addTimeStamps(listOf()) // clear stamps
2022-01-07 19:27:25 +00:00
}
private fun sortLinks(useQualitySettings: Boolean = true): List<Pair<ExtractorLink?, ExtractorUri?>> {
return currentLinks.sortedBy {
val (linkData, _) = it
var quality = linkData?.quality ?: Qualities.Unknown.value
2022-01-12 17:32:03 +00:00
// we set all qualities above current max as reverse
2022-01-07 19:27:25 +00:00
if (useQualitySettings && quality > currentPrefQuality) {
2022-01-12 17:32:03 +00:00
quality = currentPrefQuality - quality - 1
2022-01-07 19:27:25 +00:00
}
// negative because we want to sort highest quality first
-(quality)
}
}
data class TempMetaData(
var episode: Int? = null,
var season: Int? = null,
var name: String? = null,
)
private fun getMetaData(): TempMetaData {
val meta = TempMetaData()
when (val newMeta = currentMeta) {
is ResultEpisode -> {
if (!newMeta.tvType.isMovieType()) {
meta.episode = newMeta.episode
meta.season = newMeta.season
}
meta.name = newMeta.headerName
}
is ExtractorUri -> {
if (newMeta.tvType?.isMovieType() == false) {
meta.episode = newMeta.episode
meta.season = newMeta.season
}
meta.name = newMeta.headerName
}
}
return meta
}
override fun openOnlineSubPicker(
context: Context, imdbId: Long?, dismissCallback: (() -> Unit)
) {
val providers = subsProviders
val isSingleProvider = subsProviders.size == 1
val dialog = Dialog(context, R.style.AlertDialogCustomBlack)
dialog.setContentView(R.layout.dialog_online_subtitles)
var currentSubtitles: List<AbstractSubtitleEntities.SubtitleEntity> = emptyList()
var currentSubtitle: AbstractSubtitleEntities.SubtitleEntity? = null
fun getName(entry: AbstractSubtitleEntities.SubtitleEntity, withLanguage: Boolean): String {
if (entry.lang.isBlank() || !withLanguage) {
return entry.name
}
val language = fromTwoLettersToLanguage(entry.lang.trim()) ?: entry.lang
return "$language ${entry.name}"
}
val layout = R.layout.sort_bottom_single_choice_double_text
val arrayAdapter =
object : ArrayAdapter<AbstractSubtitleEntities.SubtitleEntity>(dialog.context, layout) {
fun setHearingImpairedIcon(
imageViewEnd: ImageView?, position: Int
) {
if (imageViewEnd == null) return
val isHearingImpaired =
currentSubtitles.getOrNull(position)?.isHearingImpaired ?: false
val drawableEnd = if (isHearingImpaired) {
ContextCompat.getDrawable(
context, R.drawable.ic_baseline_hearing_24
)?.apply {
setTint(
ContextCompat.getColor(
context, R.color.textColor
)
)
}
} else null
imageViewEnd.setImageDrawable(drawableEnd)
}
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val view = convertView ?: LayoutInflater.from(context).inflate(layout, null)
val item = getItem(position)
val mainTextView = view.findViewById<TextView>(R.id.main_text)
val secondaryTextView = view.findViewById<TextView>(R.id.secondary_text)
val drawableEnd = view.findViewById<ImageView>(R.id.drawable_end)
mainTextView?.text = item?.let { getName(it, false) }
val language =
item?.let { fromTwoLettersToLanguage(it.lang.trim()) ?: it.lang } ?: ""
val providerSuffix =
if (isSingleProvider || item == null) "" else " · ${item.source}"
secondaryTextView?.text = language + providerSuffix
setHearingImpairedIcon(drawableEnd, position)
return view
}
}
dialog.show()
dialog.cancel_btt.setOnClickListener {
dialog.dismissSafe()
}
dialog.subtitle_adapter.choiceMode = AbsListView.CHOICE_MODE_SINGLE
dialog.subtitle_adapter.adapter = arrayAdapter
val adapter =
dialog.subtitle_adapter.adapter as? ArrayAdapter<AbstractSubtitleEntities.SubtitleEntity>
dialog.subtitle_adapter.setOnItemClickListener { _, _, position, _ ->
currentSubtitle = currentSubtitles.getOrNull(position) ?: return@setOnItemClickListener
}
var currentLanguageTwoLetters: String = getAutoSelectLanguageISO639_1()
fun setSubtitlesList(list: List<AbstractSubtitleEntities.SubtitleEntity>) {
currentSubtitles = list
adapter?.clear()
adapter?.addAll(currentSubtitles)
}
val currentTempMeta = getMetaData()
// bruh idk why it is not correct
val color = ColorStateList.valueOf(context.colorFromAttribute(R.attr.colorAccent))
dialog.search_loading_bar.progressTintList = color
dialog.search_loading_bar.indeterminateTintList = color
2023-01-17 15:57:46 +00:00
observeNullable(viewModel.currentSubtitleYear) {
// When year is changed search again
dialog.subtitles_search.setQuery(dialog.subtitles_search.query, true)
dialog.year_btt.text = it?.toString() ?: txt(R.string.none).asString(context)
}
dialog.year_btt?.setOnClickListener {
val none = txt(R.string.none).asString(context)
val currentYear = Calendar.getInstance().get(Calendar.YEAR)
val earliestYear = 1900
val years = (currentYear downTo earliestYear).toList()
val options = listOf(none) + years.map {
it.toString()
}
2023-01-17 15:57:46 +00:00
val selectedIndex = viewModel.currentSubtitleYear.value
?.let {
// + 1 since none also takes a space
years.indexOf(it) + 1
}
?.takeIf { it >= 0 } ?: 0
activity?.showDialog(
options,
selectedIndex,
txt(R.string.year).asString(context),
true, {
}, { index ->
viewModel.setSubtitleYear(years.getOrNull(index - 1))
}
)
}
dialog.subtitles_search.setOnQueryTextListener(object :
androidx.appcompat.widget.SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String?): Boolean {
dialog.search_loading_bar?.show()
ioSafe {
val search =
2023-01-17 15:57:46 +00:00
AbstractSubtitleEntities.SubtitleSearch(
query = query ?: return@ioSafe,
imdb = imdbId,
epNumber = currentTempMeta.episode,
seasonNumber = currentTempMeta.season,
lang = currentLanguageTwoLetters.ifBlank { null },
2023-01-17 15:57:46 +00:00
year = viewModel.currentSubtitleYear.value
)
2022-10-31 00:16:15 +00:00
val results = providers.amap {
try {
it.search(search)
} catch (e: Exception) {
null
}
}.filterNotNull()
2023-01-17 15:57:46 +00:00
val max = results.maxOfOrNull { it.size } ?: return@ioSafe
// very ugly
val items = ArrayList<AbstractSubtitleEntities.SubtitleEntity>()
val arrays = results.size
for (index in 0 until max) {
for (i in 0 until arrays) {
items.add(results[i].getOrNull(index) ?: continue)
}
}
// ugly ik
activity?.runOnUiThread {
setSubtitlesList(items)
dialog.search_loading_bar?.hide()
}
}
return true
}
override fun onQueryTextChange(newText: String?): Boolean {
return true
}
})
dialog.search_filter.setOnClickListener { view ->
val lang639_1 = languages.map { it.ISO_639_1 }
activity?.showDialog(languages.map { it.languageName },
lang639_1.indexOf(currentLanguageTwoLetters),
view?.context?.getString(R.string.subs_subtitle_languages)
?: return@setOnClickListener,
true,
{ }) { index ->
currentLanguageTwoLetters = lang639_1[index]
dialog.subtitles_search.setQuery(dialog.subtitles_search.query, true)
}
}
dialog.apply_btt.setOnClickListener {
currentSubtitle?.let { currentSubtitle ->
providers.firstOrNull { it.idPrefix == currentSubtitle.idPrefix }?.let { api ->
ioSafe {
val url = api.load(currentSubtitle) ?: return@ioSafe
val subtitle = SubtitleData(
name = getName(currentSubtitle, true),
url = url,
origin = SubtitleOrigin.URL,
mimeType = url.toSubtitleMimeType(),
headers = currentSubtitle.headers
)
runOnMainThread {
addAndSelectSubtitles(subtitle)
}
}
}
}
dialog.dismissSafe()
}
dialog.setOnDismissListener {
dismissCallback.invoke()
}
dialog.show()
dialog.subtitles_search.setQuery(currentTempMeta.name, true)
//TODO: Set year text from currently loaded movie on Player
//dialog.subtitles_search_year?.setText(currentTempMeta.year)
}
2022-01-07 19:27:25 +00:00
private fun openSubPicker() {
2022-02-18 21:20:35 +00:00
try {
subsPathPicker.launch(
arrayOf(
"text/plain",
"text/str",
"application/octet-stream",
MimeTypes.TEXT_UNKNOWN,
MimeTypes.TEXT_VTT,
MimeTypes.TEXT_SSA,
MimeTypes.APPLICATION_TTML,
MimeTypes.APPLICATION_MP4VTT,
MimeTypes.APPLICATION_SUBRIP,
)
2022-01-07 19:27:25 +00:00
)
2022-03-01 21:15:18 +00:00
} catch (e: Exception) {
2022-02-18 21:20:35 +00:00
logError(e)
}
2022-01-07 19:27:25 +00:00
}
private fun addAndSelectSubtitles(subtitleData: SubtitleData) {
val ctx = context ?: return
2022-06-18 15:18:25 +00:00
val subs = currentSubs + subtitleData
// this is used instead of observe(viewModel._currentSubs), because observe is too slow
player.setActiveSubtitles(subs)
// Save current time as to not reset player to 00:00
player.saveData()
player.reloadPlayer(ctx)
setSubtitles(subtitleData)
viewModel.addSubtitles(setOf(subtitleData))
selectSourceDialog?.dismissSafe()
showToast(
activity,
String.format(ctx.getString(R.string.player_loaded_subtitles), subtitleData.name),
Toast.LENGTH_LONG
)
}
2022-01-07 19:27:25 +00:00
// Open file picker
private val subsPathPicker =
registerForActivityResult(ActivityResultContracts.OpenDocument()) { uri ->
normalSafeApiCall {
// It lies, it can be null if file manager quits.
if (uri == null) return@normalSafeApiCall
val ctx = context ?: AcraApplication.context ?: return@normalSafeApiCall
// RW perms for the path
val flags =
Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
2022-01-07 19:27:25 +00:00
ctx.contentResolver.takePersistableUriPermission(uri, flags)
val file = UniFile.fromUri(ctx, uri)
println("Loaded subtitle file. Selected URI path: $uri - Name: ${file.name}")
// DO NOT REMOVE THE FILE EXTENSION FROM NAME, IT'S NEEDED FOR MIME TYPES
val name = file.name ?: uri.toString()
val subtitleData = SubtitleData(
name,
uri.toString(),
SubtitleOrigin.DOWNLOADED_FILE,
name.toSubtitleMimeType(),
emptyMap()
2022-01-07 19:27:25 +00:00
)
addAndSelectSubtitles(subtitleData)
2022-01-07 19:27:25 +00:00
}
}
var selectSourceDialog: AlertDialog? = null
// var selectTracksDialog: AlertDialog? = null
2022-01-07 19:27:25 +00:00
override fun showMirrorsDialogue() {
2022-01-13 21:09:05 +00:00
try {
currentSelectedSubtitles = player.getCurrentPreferredSubtitle()
2022-06-09 17:25:51 +00:00
//println("CURRENT SELECTED :$currentSelectedSubtitles of $currentSubs")
2022-01-13 21:09:05 +00:00
context?.let { ctx ->
val isPlaying = player.getIsPlaying()
player.handleEvent(CSPlayerEvent.Pause)
val currentSubtitles = sortSubs(currentSubs)
val sourceBuilder = AlertDialog.Builder(ctx, R.style.AlertDialogCustomBlack)
.setView(R.layout.player_select_source_and_subs)
val sourceDialog = sourceBuilder.create()
2022-08-03 00:04:03 +00:00
2022-01-13 21:09:05 +00:00
selectSourceDialog = sourceDialog
2022-08-03 00:04:03 +00:00
2022-01-13 21:09:05 +00:00
sourceDialog.show()
2022-05-30 13:14:41 +00:00
val providerList = sourceDialog.sort_providers
val subtitleList = sourceDialog.sort_subtitles
2022-01-13 21:09:05 +00:00
val loadFromFileFooter: TextView =
2022-01-13 21:09:05 +00:00
layoutInflater.inflate(R.layout.sort_bottom_footer_add_choice, null) as TextView
loadFromFileFooter.text = ctx.getString(R.string.player_load_subtitles)
loadFromFileFooter.setOnClickListener {
2022-01-13 21:09:05 +00:00
openSubPicker()
}
subtitleList.addFooterView(loadFromFileFooter)
var shouldDismiss = true
fun dismiss() {
if (isPlaying) {
player.handleEvent(CSPlayerEvent.Play)
}
activity?.hideSystemUI()
}
if (subsProvidersIsActive) {
val loadFromOpenSubsFooter: TextView = layoutInflater.inflate(
R.layout.sort_bottom_footer_add_choice, null
) as TextView
loadFromOpenSubsFooter.text =
ctx.getString(R.string.player_load_subtitles_online)
loadFromOpenSubsFooter.setOnClickListener {
shouldDismiss = false
sourceDialog.dismissSafe(activity)
openOnlineSubPicker(it.context, null) {
dismiss()
}
}
subtitleList.addFooterView(loadFromOpenSubsFooter)
}
2022-01-07 19:27:25 +00:00
2022-01-13 21:09:05 +00:00
var sourceIndex = 0
var startSource = 0
2022-01-07 19:27:25 +00:00
2022-01-13 21:09:05 +00:00
val sortedUrls = sortLinks(useQualitySettings = false)
2022-05-20 18:25:56 +00:00
if (sortedUrls.isEmpty()) {
2022-01-13 21:09:05 +00:00
sourceDialog.findViewById<LinearLayout>(R.id.sort_sources_holder)?.isGone = true
} else {
startSource = sortedUrls.indexOf(currentSelectedLink)
sourceIndex = startSource
2022-01-07 19:27:25 +00:00
2022-01-13 21:09:05 +00:00
val sourcesArrayAdapter =
ArrayAdapter<String>(ctx, R.layout.sort_bottom_single_choice)
2022-01-07 19:27:25 +00:00
2022-05-21 18:28:58 +00:00
sourcesArrayAdapter.addAll(sortedUrls.map { (link, uri) ->
val name = link?.name ?: uri?.name ?: "NULL"
"$name ${Qualities.getStringByInt(link?.quality)}"
2022-01-13 21:09:05 +00:00
})
2022-01-07 19:27:25 +00:00
2022-01-13 21:09:05 +00:00
providerList.choiceMode = AbsListView.CHOICE_MODE_SINGLE
providerList.adapter = sourcesArrayAdapter
providerList.setSelection(sourceIndex)
providerList.setItemChecked(sourceIndex, true)
2022-01-07 19:27:25 +00:00
2022-01-13 21:09:05 +00:00
providerList.setOnItemClickListener { _, _, which, _ ->
sourceIndex = which
providerList.setItemChecked(which, true)
}
2022-01-07 19:27:25 +00:00
}
2022-06-02 19:28:09 +00:00
sourceDialog.setOnDismissListener {
if (shouldDismiss) dismiss()
2022-01-13 21:09:05 +00:00
selectSourceDialog = null
2022-01-07 19:27:25 +00:00
}
2022-01-13 21:09:05 +00:00
val subtitleIndexStart = currentSubtitles.indexOf(currentSelectedSubtitles) + 1
var subtitleIndex = subtitleIndexStart
2022-01-07 19:27:25 +00:00
val subsArrayAdapter = ArrayAdapter<String>(ctx, R.layout.sort_bottom_single_choice)
2022-06-22 01:20:10 +00:00
subsArrayAdapter.add(ctx.getString(R.string.no_subtitles))
2022-01-13 21:09:05 +00:00
subsArrayAdapter.addAll(currentSubtitles.map { it.name })
2022-01-07 19:27:25 +00:00
2022-01-13 21:09:05 +00:00
subtitleList.adapter = subsArrayAdapter
subtitleList.choiceMode = AbsListView.CHOICE_MODE_SINGLE
2022-01-07 19:27:25 +00:00
2022-01-13 21:09:05 +00:00
subtitleList.setSelection(subtitleIndex)
subtitleList.setItemChecked(subtitleIndex, true)
2022-01-07 19:27:25 +00:00
2022-01-13 21:09:05 +00:00
subtitleList.setOnItemClickListener { _, _, which, _ ->
if (which > currentSubtitles.size) {
// Since android TV is funky the setOnItemClickListener will be triggered
// instead of setOnClickListener when selecting. To override this we programmatically
// click the view when selecting an item outside the list.
// Cheeky way of getting the view at that position to click it
// to avoid keeping track of the various footers.
// getChildAt() gives null :(
val child = subtitleList.adapter.getView(which, null, subtitleList)
child?.performClick()
} else {
subtitleIndex = which
subtitleList.setItemChecked(which, true)
}
2022-01-13 21:09:05 +00:00
}
2022-01-07 19:27:25 +00:00
2022-05-30 13:14:41 +00:00
sourceDialog.cancel_btt?.setOnClickListener {
2022-01-13 21:09:05 +00:00
sourceDialog.dismissSafe(activity)
2022-01-07 19:27:25 +00:00
}
2022-01-13 21:09:05 +00:00
2022-05-30 13:14:41 +00:00
sourceDialog.subtitles_encoding_format?.apply {
val settingsManager = PreferenceManager.getDefaultSharedPreferences(ctx)
val prefNames = ctx.resources.getStringArray(R.array.subtitles_encoding_list)
val prefValues = ctx.resources.getStringArray(R.array.subtitles_encoding_values)
val value = settingsManager.getString(
ctx.getString(R.string.subtitles_encoding_key), null
2022-05-30 13:14:41 +00:00
)
val index = prefValues.indexOf(value)
2022-06-02 19:28:09 +00:00
text = prefNames[if (index == -1) 0 else index]
2022-05-30 13:14:41 +00:00
}
sourceDialog.subtitles_click_settings?.setOnClickListener {
val settingsManager = PreferenceManager.getDefaultSharedPreferences(ctx)
val prefNames = ctx.resources.getStringArray(R.array.subtitles_encoding_list)
val prefValues = ctx.resources.getStringArray(R.array.subtitles_encoding_values)
val currentPrefMedia = settingsManager.getString(
ctx.getString(R.string.subtitles_encoding_key), null
)
2022-05-30 13:14:41 +00:00
2022-06-02 19:28:09 +00:00
shouldDismiss = false
2022-05-30 13:14:41 +00:00
sourceDialog.dismissSafe(activity)
2022-06-02 19:28:09 +00:00
val index = prefValues.indexOf(currentPrefMedia)
activity?.showDialog(prefNames.toList(),
2022-06-02 19:28:09 +00:00
if (index == -1) 0 else index,
2022-05-30 13:14:41 +00:00
ctx.getString(R.string.subtitles_encoding),
true,
{}) {
settingsManager.edit().putString(
ctx.getString(R.string.subtitles_encoding_key), prefValues[it]
).apply()
2022-06-02 19:28:09 +00:00
2022-05-30 13:14:41 +00:00
updateForcedEncoding(ctx)
2022-06-02 19:28:09 +00:00
dismiss()
player.seekTime(-1) // to update subtitles, a dirty trick
2022-05-30 13:14:41 +00:00
}
}
sourceDialog.apply_btt?.setOnClickListener {
2022-01-13 21:09:05 +00:00
var init = false
if (sourceIndex != startSource) {
init = true
2022-01-07 19:27:25 +00:00
}
2022-01-13 21:09:05 +00:00
if (subtitleIndex != subtitleIndexStart) {
init = init || if (subtitleIndex <= 0) {
noSubtitles()
} else {
2022-01-30 22:02:57 +00:00
currentSubtitles.getOrNull(subtitleIndex - 1)?.let {
setSubtitles(it)
} ?: false
2022-01-13 21:09:05 +00:00
}
}
if (init) {
2022-01-30 22:02:57 +00:00
sortedUrls.getOrNull(sourceIndex)?.let {
loadLink(it, true)
}
2022-01-13 21:09:05 +00:00
}
sourceDialog.dismissSafe(activity)
2022-01-07 19:27:25 +00:00
}
}
} catch (e: Exception) {
2022-01-13 21:09:05 +00:00
logError(e)
2022-01-07 19:27:25 +00:00
}
}
override fun showTracksDialogue() {
try {
//println("CURRENT SELECTED :$currentSelectedSubtitles of $currentSubs")
context?.let { ctx ->
val tracks = player.getVideoTracks()
val isPlaying = player.getIsPlaying()
player.handleEvent(CSPlayerEvent.Pause)
val currentVideoTracks = tracks.allVideoTracks.sortedBy {
it.height?.times(-1)
}
val currentAudioTracks = tracks.allAudioTracks
val trackBuilder = AlertDialog.Builder(ctx, R.style.AlertDialogCustomBlack)
.setView(R.layout.player_select_tracks)
val tracksDialog = trackBuilder.create()
// selectTracksDialog = tracksDialog
tracksDialog.show()
val videosList = tracksDialog.video_tracks_list
val audioList = tracksDialog.auto_tracks_list
tracksDialog.video_tracks_holder.isVisible = currentVideoTracks.size > 1
tracksDialog.audio_tracks_holder.isVisible = currentAudioTracks.size > 1
fun dismiss() {
if (isPlaying) {
player.handleEvent(CSPlayerEvent.Play)
}
activity?.hideSystemUI()
}
val videosArrayAdapter =
ArrayAdapter<String>(ctx, R.layout.sort_bottom_single_choice)
videosArrayAdapter.addAll(currentVideoTracks.mapIndexed { index, format ->
format.label
?: (if (format.height == NO_VALUE || format.width == NO_VALUE) index else "${format.width}x${format.height}").toString()
})
videosList.choiceMode = AbsListView.CHOICE_MODE_SINGLE
videosList.adapter = videosArrayAdapter
// Sometimes the data is not the same because some data gets resolved at different stages i think
var videoIndex = currentVideoTracks.indexOf(tracks.currentVideoTrack).takeIf {
it != -1
} ?: currentVideoTracks.indexOfFirst {
tracks.currentVideoTrack?.id == it.id
}
videosList.setSelection(videoIndex)
videosList.setItemChecked(videoIndex, true)
videosList.setOnItemClickListener { _, _, which, _ ->
videoIndex = which
videosList.setItemChecked(which, true)
}
tracksDialog.setOnDismissListener {
dismiss()
// selectTracksDialog = null
}
var audioIndexStart = currentAudioTracks.indexOf(tracks.currentAudioTrack).takeIf {
it != -1
} ?: currentVideoTracks.indexOfFirst {
tracks.currentAudioTrack?.id == it.id
}
val audioArrayAdapter =
ArrayAdapter<String>(ctx, R.layout.sort_bottom_single_choice)
// audioArrayAdapter.add(ctx.getString(R.string.no_subtitles))
audioArrayAdapter.addAll(currentAudioTracks.mapIndexed { index, format ->
format.label ?: format.language?.let { fromTwoLettersToLanguage(it) }
?: index.toString()
})
audioList.adapter = audioArrayAdapter
audioList.choiceMode = AbsListView.CHOICE_MODE_SINGLE
audioList.setSelection(audioIndexStart)
audioList.setItemChecked(audioIndexStart, true)
audioList.setOnItemClickListener { _, _, which, _ ->
audioIndexStart = which
audioList.setItemChecked(which, true)
}
tracksDialog.cancel_btt?.setOnClickListener {
tracksDialog.dismissSafe(activity)
}
tracksDialog.apply_btt?.setOnClickListener {
val currentTrack = currentAudioTracks.getOrNull(audioIndexStart)
player.setPreferredAudioTrack(
currentTrack?.language, currentTrack?.id
)
val currentVideo = currentVideoTracks.getOrNull(videoIndex)
val width = currentVideo?.width ?: NO_VALUE
val height = currentVideo?.height ?: NO_VALUE
if (width != NO_VALUE && height != NO_VALUE) {
player.setMaxVideoSize(width, height, currentVideo?.id)
}
tracksDialog.dismissSafe(activity)
}
}
} catch (e: Exception) {
logError(e)
}
}
2022-07-27 16:27:49 +00:00
override fun playerError(exception: Exception) {
Log.i(TAG, "playerError = $currentSelectedLink")
super.playerError(exception)
}
2022-01-07 19:27:25 +00:00
private fun noLinksFound() {
showToast(activity, R.string.no_links_found_toast, Toast.LENGTH_SHORT)
activity?.popCurrentPage()
}
private fun startPlayer() {
if (isActive) return // we don't want double load when you skip loading
val links = sortLinks()
if (links.isEmpty()) {
noLinksFound()
return
}
loadLink(links.first(), false)
}
override fun nextEpisode() {
isNextEpisode = true
2022-01-30 22:02:57 +00:00
player.release()
2022-01-07 19:27:25 +00:00
viewModel.loadLinksNext()
}
override fun prevEpisode() {
isNextEpisode = true
2022-01-30 22:02:57 +00:00
player.release()
2022-01-07 19:27:25 +00:00
viewModel.loadLinksPrev()
}
override fun hasNextMirror(): Boolean {
val links = sortLinks()
return links.isNotEmpty() && links.indexOf(currentSelectedLink) + 1 < links.size
}
2022-01-07 19:27:25 +00:00
override fun nextMirror() {
val links = sortLinks()
if (links.isEmpty()) {
noLinksFound()
return
}
val newIndex = links.indexOf(currentSelectedLink) + 1
if (newIndex >= links.size) {
noLinksFound()
return
}
loadLink(links[newIndex], true)
}
2022-01-18 14:10:01 +00:00
override fun onDestroy() {
ResultFragment.updateUI()
2022-02-11 09:17:04 +00:00
currentVerifyLink?.cancel()
2022-01-18 14:10:01 +00:00
super.onDestroy()
}
2022-04-13 17:42:02 +00:00
var maxEpisodeSet: Int? = null
var hasRequestedStamps: Boolean = false
2022-01-07 19:27:25 +00:00
override fun playerPositionChanged(posDur: Pair<Long, Long>) {
// Don't save livestream data
if ((currentMeta as? ResultEpisode)?.tvType?.isLiveStream() == true) return
2022-08-22 12:57:47 +00:00
// Don't save NSFW data
if ((currentMeta as? ResultEpisode)?.tvType == TvType.NSFW) return
2022-01-07 19:27:25 +00:00
val (position, duration) = posDur
if (duration <= 0L) return // idk how you achieved this, but div by zero crash
if (!hasRequestedStamps) {
hasRequestedStamps = true
val fetchStamps = context?.let { ctx ->
val settingsManager = PreferenceManager.getDefaultSharedPreferences(ctx)
settingsManager.getBoolean(
ctx.getString(R.string.enable_skip_op_from_database),
true
)
} ?: true
if (fetchStamps)
viewModel.loadStamps(duration)
}
2022-08-18 00:54:05 +00:00
2022-01-07 19:27:25 +00:00
viewModel.getId()?.let {
DataStoreHelper.setViewPos(it, position, duration)
}
2022-01-07 19:27:25 +00:00
val percentage = position * 100L / duration
val nextEp = percentage >= NEXT_WATCH_EPISODE_PERCENTAGE
val resumeMeta = if (nextEp) nextMeta else currentMeta
if (resumeMeta == null && nextEp) {
// remove last watched as it is the last episode and you have watched too much
when (val newMeta = currentMeta) {
is ResultEpisode -> {
DataStoreHelper.removeLastWatched(newMeta.parentId)
}
is ExtractorUri -> {
DataStoreHelper.removeLastWatched(newMeta.parentId)
}
}
} else {
// save resume
when (resumeMeta) {
is ResultEpisode -> {
DataStoreHelper.setLastWatched(
resumeMeta.parentId,
resumeMeta.id,
resumeMeta.episode,
resumeMeta.season,
isFromDownload = false
)
}
is ExtractorUri -> {
DataStoreHelper.setLastWatched(
resumeMeta.parentId,
resumeMeta.id,
resumeMeta.episode,
resumeMeta.season,
isFromDownload = true
)
}
}
}
var isOpVisible = false
when (val meta = currentMeta) {
is ResultEpisode -> {
2022-04-13 17:42:02 +00:00
if (percentage >= UPDATE_SYNC_PROGRESS_PERCENTAGE && (maxEpisodeSet
?: -1) < meta.episode
) {
2022-04-08 19:38:19 +00:00
context?.let { ctx ->
val settingsManager = PreferenceManager.getDefaultSharedPreferences(ctx)
if (settingsManager.getBoolean(
ctx.getString(R.string.episode_sync_enabled_key), true
2022-04-08 19:38:19 +00:00
)
) maxEpisodeSet = meta.episode
2022-04-13 17:42:02 +00:00
sync.modifyMaxEpisode(meta.episode)
2022-04-08 19:38:19 +00:00
}
}
if (meta.tvType.isAnimeOp()) isOpVisible = percentage < SKIP_OP_VIDEO_PERCENTAGE
2022-01-07 19:27:25 +00:00
}
}
player_skip_op?.isVisible = isOpVisible
player_skip_episode?.isVisible = !isOpVisible && viewModel.hasNextEpisode() == true
2022-04-08 19:38:19 +00:00
if (percentage >= PRELOAD_NEXT_EPISODE_PERCENTAGE) {
2022-01-07 19:27:25 +00:00
viewModel.preLoadNextLinks()
}
}
private fun getAutoSelectSubtitle(
subtitles: Set<SubtitleData>, settings: Boolean, downloads: Boolean
): SubtitleData? {
val langCode = preferredAutoSelectSubtitles ?: return null
val lang = fromTwoLettersToLanguage(langCode) ?: return null
if (downloads) {
return subtitles.firstOrNull { sub ->
(sub.origin == SubtitleOrigin.DOWNLOADED_FILE && sub.name == context?.getString(
R.string.default_subtitles
))
}
}
sortSubs(subtitles).firstOrNull { sub ->
val t = sub.name.replace(Regex("[^A-Za-z]"), " ").trim()
(settings) && t == lang || t.startsWith(lang) || t == langCode
}?.let { sub ->
return sub
}
return null
}
private fun autoSelectFromSettings(): Boolean {
// auto select subtitle based of settings
val langCode = preferredAutoSelectSubtitles
val current = player.getCurrentPreferredSubtitle()
Log.i(TAG, "autoSelectFromSettings = $current")
context?.let { ctx ->
if (current != null) {
if (setSubtitles(current)) {
player.saveData()
player.reloadPlayer(ctx)
player.handleEvent(CSPlayerEvent.Play)
return true
}
} else if (!langCode.isNullOrEmpty()) {
getAutoSelectSubtitle(
currentSubs, settings = true, downloads = false
)?.let { sub ->
if (setSubtitles(sub)) {
player.saveData()
player.reloadPlayer(ctx)
player.handleEvent(CSPlayerEvent.Play)
return true
2022-01-08 19:39:22 +00:00
}
}
}
2022-01-08 19:39:22 +00:00
}
return false
2022-01-08 19:39:22 +00:00
}
private fun autoSelectFromDownloads(): Boolean {
2022-01-08 19:39:22 +00:00
if (player.getCurrentPreferredSubtitle() == null) {
getAutoSelectSubtitle(currentSubs, settings = false, downloads = true)?.let { sub ->
2022-01-08 19:39:22 +00:00
context?.let { ctx ->
if (setSubtitles(sub)) {
player.saveData()
2022-01-08 19:39:22 +00:00
player.reloadPlayer(ctx)
player.handleEvent(CSPlayerEvent.Play)
return true
2022-01-08 19:39:22 +00:00
}
}
}
}
return false
2022-01-08 19:39:22 +00:00
}
private fun autoSelectSubtitles() {
//Log.i(TAG, "autoSelectSubtitles")
2022-01-08 19:39:22 +00:00
normalSafeApiCall {
if (!autoSelectFromSettings()) {
autoSelectFromDownloads()
}
2022-01-08 19:39:22 +00:00
}
}
private fun getPlayerVideoTitle(): String {
2022-01-07 19:27:25 +00:00
var headerName: String? = null
2022-03-01 21:15:18 +00:00
var subName: String? = null
2022-01-07 19:27:25 +00:00
var episode: Int? = null
var season: Int? = null
var tvType: TvType? = null
when (val meta = currentMeta) {
is ResultEpisode -> {
headerName = meta.headerName
2022-02-27 00:13:55 +00:00
subName = meta.name
2022-01-07 19:27:25 +00:00
episode = meta.episode
season = meta.season
tvType = meta.tvType
}
is ExtractorUri -> {
headerName = meta.headerName
2022-02-27 00:13:55 +00:00
subName = meta.name
2022-01-07 19:27:25 +00:00
episode = meta.episode
season = meta.season
tvType = meta.tvType
}
}
2022-06-22 01:20:10 +00:00
context?.let { ctx ->
//Generate video title
val playerVideoTitle = if (headerName != null) {
(headerName + if (tvType.isEpisodeBased() && episode != null) if (season == null) " - ${
ctx.getString(
R.string.episode
)
} $episode"
else " \"${ctx.getString(R.string.season_short)}${season}:${
ctx.getString(
R.string.episode_short
)
}${episode}\""
else "") + if (subName.isNullOrBlank() || subName == headerName) "" else " - $subName"
2022-06-22 01:20:10 +00:00
} else {
""
}
return playerVideoTitle
}
2022-06-22 01:20:10 +00:00
return ""
}
2022-05-26 22:34:09 +00:00
@SuppressLint("SetTextI18n")
fun setTitle() {
var playerVideoTitle = getPlayerVideoTitle()
//Hide title, if set in setting
if (limitTitle < 0) {
player_video_title?.visibility = View.GONE
} else {
//Truncate video title if it exceeds limit
val differenceInLength = playerVideoTitle.length - limitTitle
val margin = 3 //If the difference is smaller than or equal to this value, ignore it
if (limitTitle > 0 && differenceInLength > margin) {
playerVideoTitle = playerVideoTitle.substring(0, limitTitle - 1) + "..."
}
}
val isFiller: Boolean? = (currentMeta as? ResultEpisode)?.isFiller
player_episode_filler_holder?.isVisible = isFiller ?: false
player_video_title?.text = playerVideoTitle
2022-01-07 19:27:25 +00:00
}
@SuppressLint("SetTextI18n")
fun setPlayerDimen(widthHeight: Pair<Int, Int>?) {
val extra = if (widthHeight != null) {
val (width, height) = widthHeight
2022-05-15 18:38:32 +00:00
"${width}x${height}"
2022-01-07 19:27:25 +00:00
} else {
""
}
2022-05-15 18:38:32 +00:00
val source = currentSelectedLink?.first?.name ?: currentSelectedLink?.second?.name ?: "NULL"
2022-05-15 18:38:32 +00:00
player_video_title_rez?.text = when (titleRez) {
0 -> ""
1 -> extra
2 -> source
3 -> "$source - $extra"
else -> ""
}
2022-01-07 19:27:25 +00:00
}
override fun playerDimensionsLoaded(widthHeight: Pair<Int, Int>) {
setPlayerDimen(widthHeight)
}
2022-04-08 19:38:19 +00:00
private fun unwrapBundle(savedInstanceState: Bundle?) {
Log.i(TAG, "unwrapBundle = $savedInstanceState")
savedInstanceState?.let { bundle ->
sync.addSyncs(bundle.getSerializable("syncData") as? HashMap<String, String>?)
}
}
2022-01-07 19:27:25 +00:00
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
2022-01-07 19:27:25 +00:00
): View? {
// this is used instead of layout-television to follow the settings and some TV devices are not classified as TV for some reason
2022-08-28 23:52:15 +00:00
isTv = isTvSettings()
layout = if (isTv) R.layout.fragment_player_tv else R.layout.fragment_player
2022-01-07 19:27:25 +00:00
viewModel = ViewModelProvider(this)[PlayerGeneratorViewModel::class.java]
2022-04-08 19:38:19 +00:00
sync = ViewModelProvider(this)[SyncViewModel::class.java]
2022-01-07 19:27:25 +00:00
viewModel.attachGenerator(lastUsedGenerator)
2022-04-08 19:38:19 +00:00
unwrapBundle(savedInstanceState)
unwrapBundle(arguments)
2022-01-07 19:27:25 +00:00
return super.onCreateView(inflater, container, savedInstanceState)
}
var timestampShowState = false
var skipAnimator: ValueAnimator? = null
var skipIndex = 0
private fun displayTimeStamp(show: Boolean) {
if (timestampShowState == show) return
skipIndex++
println("displayTimeStamp = $show")
timestampShowState = show
skip_chapter_button?.apply {
val showWidth = 170.toPx
val noShowWidth = 10.toPx
//if((show && width == showWidth) || (!show && width == noShowWidth)) {
// return
//}
val to = if (show) showWidth else noShowWidth
val from = if (!show) showWidth else noShowWidth
skipAnimator?.cancel()
isVisible = true
// just in case
val lay = layoutParams
lay.width = from
layoutParams = lay
skipAnimator = ValueAnimator.ofInt(
from, to
).apply {
addListener(onEnd = {
if (!show) skip_chapter_button?.isVisible = false
})
addUpdateListener { valueAnimator ->
val value = valueAnimator.animatedValue as Int
val layoutParams: ViewGroup.LayoutParams = layoutParams
layoutParams.width = value
setLayoutParams(layoutParams)
}
duration = 500
start()
}
}
}
override fun onTimestampSkipped(timestamp: EpisodeSkip.SkipStamp) {
displayTimeStamp(false)
}
override fun onTimestamp(timestamp: EpisodeSkip.SkipStamp?) {
if (timestamp != null) {
skip_chapter_button.setText(timestamp.uiText)
displayTimeStamp(true)
val currentIndex = skipIndex
skip_chapter_button?.handler?.postDelayed({
if (skipIndex == currentIndex)
displayTimeStamp(false)
}, 6000)
} else {
displayTimeStamp(false)
}
}
2022-01-07 19:27:25 +00:00
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
var langFilterList = listOf<String>()
var filterSubByLang = false
2022-01-07 19:27:25 +00:00
2022-05-15 18:38:32 +00:00
context?.let { ctx ->
val settingsManager = PreferenceManager.getDefaultSharedPreferences(ctx)
2022-06-22 01:20:10 +00:00
titleRez = settingsManager.getInt(ctx.getString(R.string.prefer_limit_title_rez_key), 3)
limitTitle = settingsManager.getInt(ctx.getString(R.string.prefer_limit_title_key), 0)
2022-05-30 13:14:41 +00:00
updateForcedEncoding(ctx)
filterSubByLang =
settingsManager.getBoolean(getString(R.string.filter_sub_lang_key), false)
if (filterSubByLang) {
val langFromPrefMedia = settingsManager.getStringSet(
this.getString(R.string.provider_lang_key), mutableSetOf("en")
)
langFilterList = langFromPrefMedia?.mapNotNull {
fromTwoLettersToLanguage(it)?.lowercase() ?: return@mapNotNull null
} ?: listOf()
}
2022-05-15 18:38:32 +00:00
}
2022-04-08 19:38:19 +00:00
unwrapBundle(savedInstanceState)
unwrapBundle(arguments)
sync.updateUserData()
preferredAutoSelectSubtitles = getAutoSelectLanguageISO639_1()
2022-01-08 19:39:22 +00:00
2022-01-07 19:27:25 +00:00
if (currentSelectedLink == null) {
viewModel.loadLinks()
}
overlay_loading_skip_button?.setOnClickListener {
startPlayer()
}
player_loading_go_back?.setOnClickListener {
player.release()
activity?.popCurrentPage()
}
observe(viewModel.currentStamps) { stamps ->
player.addTimeStamps(stamps)
}
2022-01-07 19:27:25 +00:00
observe(viewModel.loadingLinks) {
when (it) {
is Resource.Loading -> {
startLoading()
}
is Resource.Success -> {
// provider returned false
//if (it.value != true) {
// showToast(activity, R.string.unexpected_error, Toast.LENGTH_SHORT)
//}
startPlayer()
}
is Resource.Failure -> {
showToast(activity, it.errorString, Toast.LENGTH_LONG)
startPlayer()
}
}
}
observe(viewModel.currentLinks) {
currentLinks = it
val turnVisible = it.isNotEmpty()
2022-03-29 21:50:07 +00:00
val wasGone = overlay_loading_skip_button?.isGone == true
overlay_loading_skip_button?.isVisible = turnVisible
if (turnVisible && wasGone) {
overlay_loading_skip_button?.requestFocus()
}
2022-01-07 19:27:25 +00:00
}
2022-01-08 19:39:22 +00:00
observe(viewModel.currentSubs) { set ->
val setOfSub = mutableSetOf<SubtitleData>()
if (langFilterList.isNotEmpty() && filterSubByLang) {
Log.i("subfilter", "Filtering subtitle")
langFilterList.forEach { lang ->
Log.i("subfilter", "Lang: $lang")
setOfSub += set.filter {
it.name.contains(lang, ignoreCase = true) ||
it.origin != SubtitleOrigin.URL
}
}
currentSubs = setOfSub
} else {
currentSubs = set
}
2022-01-08 19:39:22 +00:00
player.setActiveSubtitles(set)
// If the file is downloaded then do not select auto select the subtitles
// Downloaded subtitles cannot be selected immediately after loading since
// player.getCurrentPreferredSubtitle() cannot fetch data from non-loaded subtitles
// Resulting in unselecting the downloaded subtitle
if (set.lastOrNull()?.origin != SubtitleOrigin.DOWNLOADED_FILE) {
autoSelectSubtitles()
}
2022-01-07 19:27:25 +00:00
}
}
}