mirror of
https://github.com/recloudstream/cloudstream.git
synced 2024-08-15 01:53:11 +00:00
fixed chromecast, fixed tenshi, fixed lang providers
This commit is contained in:
parent
d65ccffe4e
commit
f394469787
13 changed files with 266 additions and 95 deletions
|
@ -31,8 +31,8 @@ android {
|
|||
applicationId "com.lagradost.cloudstream3"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 31
|
||||
versionCode 26
|
||||
versionName "1.9.10"
|
||||
versionCode 27
|
||||
versionName "1.9.11"
|
||||
|
||||
resValue "string", "app_version",
|
||||
"${defaultConfig.versionName}${versionNameSuffix ?: ""}"
|
||||
|
@ -72,9 +72,6 @@ android {
|
|||
}
|
||||
|
||||
repositories {
|
||||
maven {
|
||||
url 'https://github.com/psiegman/mvn-repo/raw/master/releases'
|
||||
}
|
||||
maven { url 'https://jitpack.io' }
|
||||
}
|
||||
|
||||
|
@ -108,9 +105,9 @@ dependencies {
|
|||
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
|
||||
|
||||
// Exoplayer
|
||||
implementation 'com.google.android.exoplayer:exoplayer:2.14.2'
|
||||
implementation 'com.google.android.exoplayer:extension-cast:2.14.2'
|
||||
implementation "com.google.android.exoplayer:extension-mediasession:2.14.2"
|
||||
implementation 'com.google.android.exoplayer:exoplayer:2.15.0'
|
||||
implementation 'com.google.android.exoplayer:extension-cast:2.15.0'
|
||||
implementation "com.google.android.exoplayer:extension-mediasession:2.15.0"
|
||||
//implementation "com.google.android.exoplayer:extension-leanback:2.14.0"
|
||||
|
||||
// Bug reports
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package com.lagradost.cloudstream3
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature
|
||||
import com.fasterxml.jackson.databind.json.JsonMapper
|
||||
|
@ -9,6 +10,8 @@ import com.lagradost.cloudstream3.animeproviders.*
|
|||
import com.lagradost.cloudstream3.movieproviders.*
|
||||
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||
import java.util.*
|
||||
import kotlin.collections.ArrayList
|
||||
import kotlin.collections.HashSet
|
||||
|
||||
const val USER_AGENT =
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
|
||||
|
@ -69,19 +72,30 @@ object APIHolder {
|
|||
return url.replace(getApiFromName(apiName).mainUrl, "").replace("/", "").hashCode()
|
||||
}
|
||||
|
||||
fun Activity.getApiSettings(): HashSet<String> {
|
||||
fun Context.getApiSettings(): HashSet<String> {
|
||||
val settingsManager = PreferenceManager.getDefaultSharedPreferences(this)
|
||||
|
||||
val hashSet = HashSet<String>()
|
||||
hashSet.addAll(apis.map { it.name })
|
||||
|
||||
return settingsManager.getStringSet(
|
||||
val set = settingsManager.getStringSet(
|
||||
this.getString(R.string.search_providers_list_key),
|
||||
hashSet
|
||||
)?.toHashSet() ?: hashSet
|
||||
|
||||
val activeLangs = getApiProviderLangSettings()
|
||||
val list = HashSet<String>()
|
||||
for (name in set) {
|
||||
val api = getApiFromNameNull(name) ?: continue
|
||||
if(activeLangs.contains(api.lang) ) {
|
||||
list.add(name)
|
||||
}
|
||||
}
|
||||
if(list.isEmpty()) return hashSet
|
||||
return list
|
||||
}
|
||||
|
||||
fun Activity.getApiDubstatusSettings(): HashSet<DubStatus> {
|
||||
fun Context.getApiDubstatusSettings(): HashSet<DubStatus> {
|
||||
val settingsManager = PreferenceManager.getDefaultSharedPreferences(this)
|
||||
val hashSet = HashSet<DubStatus>()
|
||||
hashSet.addAll(DubStatus.values())
|
||||
|
@ -96,7 +110,20 @@ object APIHolder {
|
|||
return list.filter { names.contains(it) }.map { DubStatus.valueOf(it) }.toHashSet()
|
||||
}
|
||||
|
||||
fun Activity.getApiTypeSettings(): HashSet<TvType> {
|
||||
fun Context.getApiProviderLangSettings(): HashSet<String> {
|
||||
val settingsManager = PreferenceManager.getDefaultSharedPreferences(this)
|
||||
val hashSet = HashSet<String>()
|
||||
hashSet.add("en") // def is only en
|
||||
val list = settingsManager.getStringSet(
|
||||
this.getString(R.string.provider_lang_key),
|
||||
hashSet.toMutableSet()
|
||||
)
|
||||
|
||||
if(list.isNullOrEmpty()) return hashSet
|
||||
return list.toHashSet()
|
||||
}
|
||||
|
||||
fun Context.getApiTypeSettings(): HashSet<TvType> {
|
||||
val settingsManager = PreferenceManager.getDefaultSharedPreferences(this)
|
||||
val hashSet = HashSet<TvType>()
|
||||
hashSet.addAll(TvType.values())
|
||||
|
@ -120,6 +147,8 @@ abstract class MainAPI {
|
|||
open val name = "NONE"
|
||||
open val mainUrl = "NONE"
|
||||
|
||||
open val lang = "en" // ISO_639_1 check SubtitleHelper
|
||||
|
||||
/**If link is stored in the "data" string, so links can be instantly loaded*/
|
||||
open val instantLinkLoading = false
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ import androidx.navigation.ui.setupWithNavController
|
|||
import androidx.preference.PreferenceManager
|
||||
import androidx.transition.ChangeBounds
|
||||
import androidx.transition.TransitionManager
|
||||
import com.google.android.gms.cast.framework.CastButtonFactory
|
||||
import com.google.android.gms.cast.framework.*
|
||||
import com.jaredrummler.android.colorpicker.ColorPickerDialogListener
|
||||
import com.lagradost.cloudstream3.APIHolder.apis
|
||||
import com.lagradost.cloudstream3.APIHolder.getApiDubstatusSettings
|
||||
|
@ -80,6 +80,53 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
|
|||
updateLocale() // android fucks me by chaining lang when rotating the phone
|
||||
}
|
||||
|
||||
var mCastSession: CastSession? = null
|
||||
lateinit var mSessionManager: SessionManager
|
||||
val mSessionManagerListener: SessionManagerListener<Session> by lazy { SessionManagerListenerImpl() }
|
||||
|
||||
private inner class SessionManagerListenerImpl : SessionManagerListener<Session> {
|
||||
override fun onSessionStarting(session: Session) {
|
||||
}
|
||||
|
||||
override fun onSessionStarted(session: Session, sessionId: String) {
|
||||
invalidateOptionsMenu()
|
||||
}
|
||||
|
||||
override fun onSessionStartFailed(session: Session, i: Int) {
|
||||
}
|
||||
|
||||
override fun onSessionEnding(session: Session) {
|
||||
}
|
||||
|
||||
override fun onSessionResumed(session: Session, wasSuspended: Boolean) {
|
||||
invalidateOptionsMenu()
|
||||
}
|
||||
|
||||
override fun onSessionResumeFailed(session: Session, i: Int) {
|
||||
}
|
||||
|
||||
override fun onSessionSuspended(session: Session, i: Int) {
|
||||
}
|
||||
|
||||
override fun onSessionEnded(session: Session, error: Int) {
|
||||
}
|
||||
|
||||
override fun onSessionResuming(session: Session, s: String) {
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
mCastSession = mSessionManager.currentCastSession
|
||||
mSessionManager.addSessionManagerListener(mSessionManagerListener)
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
mSessionManager.removeSessionManagerListener(mSessionManagerListener)
|
||||
mCastSession = null
|
||||
}
|
||||
|
||||
companion object {
|
||||
var canEnterPipMode: Boolean = false
|
||||
var canShowPipMode: Boolean = false
|
||||
|
@ -268,6 +315,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
|
|||
) // THEME IS SET BEFORE VIEW IS CREATED TO APPLY THE THEME TO THE MAIN VIEW
|
||||
updateLocale()
|
||||
super.onCreate(savedInstanceState)
|
||||
mSessionManager = CastContext.getSharedInstance(this).sessionManager
|
||||
|
||||
window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN)
|
||||
|
||||
|
|
|
@ -140,12 +140,16 @@ class TenshiProvider : MainAPI() {
|
|||
|
||||
@SuppressLint("SimpleDateFormat")
|
||||
private fun dateParser(dateString: String): String? {
|
||||
try {
|
||||
val format = SimpleDateFormat("dd 'of' MMM',' yyyy")
|
||||
val newFormat = SimpleDateFormat("dd-MM-yyyy")
|
||||
val data = format.parse(
|
||||
dateString.replace("th ", " ").replace("st ", " ").replace("nd ", " ").replace("rd ", " ")
|
||||
) ?: return null
|
||||
return newFormat.format(data)
|
||||
} catch (e : Exception) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
// data class TenshiSearchResponse(
|
||||
|
|
|
@ -11,6 +11,7 @@ import com.fasterxml.jackson.databind.DeserializationFeature
|
|||
import com.fasterxml.jackson.databind.json.JsonMapper
|
||||
import com.fasterxml.jackson.module.kotlin.KotlinModule
|
||||
import com.google.android.gms.cast.MediaQueueItem
|
||||
import com.google.android.gms.cast.MediaSeekOptions
|
||||
import com.google.android.gms.cast.MediaStatus.REPEAT_MODE_REPEAT_OFF
|
||||
import com.google.android.gms.cast.MediaTrack
|
||||
import com.google.android.gms.cast.TextTrackStyle
|
||||
|
@ -33,12 +34,17 @@ import com.lagradost.cloudstream3.utils.ExtractorLink
|
|||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.json.JSONObject
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
class SkipOpController(val view: ImageView) : UIController() {
|
||||
init {
|
||||
view.setImageResource(R.drawable.exo_controls_fastforward)
|
||||
view.setOnClickListener {
|
||||
remoteMediaClient.seek(remoteMediaClient.approximateStreamPosition + 85000)
|
||||
remoteMediaClient?.let {
|
||||
val options = MediaSeekOptions.Builder()
|
||||
.setPosition(it.approximateStreamPosition + 85000)
|
||||
it.seek(options.build())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -192,15 +198,11 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi
|
|||
try { // THIS IS VERY IMPORTANT BECAUSE WE NEVER WANT TO AUTOLOAD THE NEXT EPISODE
|
||||
val currentIdIndex = remoteMediaClient?.getItemIndex()
|
||||
|
||||
val nextId = remoteMediaClient.mediaQueue.itemIds?.get(currentIdIndex?.plus(1) ?: 0)
|
||||
|
||||
val nextId = remoteMediaClient?.mediaQueue?.itemIds?.get(currentIdIndex?.plus(1) ?: 0)
|
||||
if (currentIdIndex == null && nextId != null) {
|
||||
awaitLinks(
|
||||
remoteMediaClient?.queueInsertAndPlayItem(
|
||||
MediaQueueItem.Builder(
|
||||
mediaItem
|
||||
)
|
||||
.build(),
|
||||
MediaQueueItem.Builder(mediaItem).build(),
|
||||
nextId,
|
||||
startAt,
|
||||
JSONObject()
|
||||
|
@ -223,6 +225,7 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi
|
|||
|
||||
bottomSheetDialog.dismiss()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -253,8 +256,7 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi
|
|||
|
||||
if (itemCount != null && itemCount - currentIdIndex == 1 && !isLoadingMore) {
|
||||
isLoadingMore = true
|
||||
|
||||
main {
|
||||
thread {
|
||||
val index = meta.currentEpisodeIndex + 1
|
||||
val epData = meta.episodes[index]
|
||||
val links = ArrayList<ExtractorLink>()
|
||||
|
@ -280,9 +282,8 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi
|
|||
currentEpisodeIndex = index
|
||||
)
|
||||
|
||||
val done = withContext(Dispatchers.IO) {
|
||||
val done =
|
||||
JSONObject(mapper.writeValueAsString(jsonCopy))
|
||||
}
|
||||
|
||||
val mediaInfo = getMediaInfo(
|
||||
epData,
|
||||
|
@ -304,20 +305,24 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi
|
|||
loadIndex(index + 1)
|
||||
}
|
||||
}*/
|
||||
|
||||
activity.runOnUiThread {
|
||||
awaitLinks(
|
||||
|
||||
remoteMediaClient?.queueAppendItem(
|
||||
MediaQueueItem.Builder(mediaInfo).build(),
|
||||
JSONObject()
|
||||
)
|
||||
|
||||
|
||||
) {
|
||||
println("FAILED TO LOAD NEXT ITEM")
|
||||
// loadIndex(1)
|
||||
}
|
||||
|
||||
isLoadingMore = false
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -340,7 +345,11 @@ class SkipTimeController(val view: ImageView, forwards: Boolean) : UIController(
|
|||
//view.setImageResource(if (forwards) R.drawable.netflix_skip_forward else R.drawable.netflix_skip_back)
|
||||
view.setImageResource(if (forwards) R.drawable.go_forward_30 else R.drawable.go_back_30)
|
||||
view.setOnClickListener {
|
||||
remoteMediaClient.seek(remoteMediaClient.approximateStreamPosition + time * 1000 * if (forwards) 1 else -1)
|
||||
remoteMediaClient?.let {
|
||||
val options = MediaSeekOptions.Builder()
|
||||
.setPosition(it.approximateStreamPosition + time * 1000 * if (forwards) 1 else -1)
|
||||
it.seek(options.build())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ import android.database.ContentObserver
|
|||
import android.graphics.Color
|
||||
import android.graphics.drawable.Icon
|
||||
import android.media.AudioManager
|
||||
import android.media.metrics.PlaybackErrorEvent
|
||||
import android.net.Uri
|
||||
import android.os.*
|
||||
import android.provider.Settings
|
||||
|
@ -46,6 +47,7 @@ import com.fasterxml.jackson.module.kotlin.KotlinModule
|
|||
import com.fasterxml.jackson.module.kotlin.readValue
|
||||
import com.google.android.exoplayer2.*
|
||||
import com.google.android.exoplayer2.C.TIME_UNSET
|
||||
import com.google.android.exoplayer2.PlaybackException.ERROR_CODE_AUDIO_TRACK_INIT_FAILED
|
||||
import com.google.android.exoplayer2.database.ExoDatabaseProvider
|
||||
import com.google.android.exoplayer2.source.DefaultMediaSourceFactory
|
||||
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector
|
||||
|
@ -165,7 +167,6 @@ data class UriData(
|
|||
|
||||
// YE, I KNOW, THIS COULD BE HANDLED A LOT BETTER
|
||||
class PlayerFragment : Fragment() {
|
||||
|
||||
// ============ TORRENT ============
|
||||
//private var torrentStream: TorrentStream? = null
|
||||
private var lastTorrentUrl = ""
|
||||
|
@ -752,6 +753,15 @@ class PlayerFragment : Fragment() {
|
|||
private var volumeObserver: SettingsContentObserver? = null
|
||||
|
||||
companion object {
|
||||
fun String.toSubtitleMimeType(): String {
|
||||
return when {
|
||||
endsWith("vtt", true) -> MimeTypes.TEXT_VTT
|
||||
endsWith("srt", true) -> MimeTypes.APPLICATION_SUBRIP
|
||||
endsWith("xml", true) || endsWith("ttml", true) -> MimeTypes.APPLICATION_TTML
|
||||
else -> MimeTypes.APPLICATION_SUBRIP // TODO get request to see
|
||||
}
|
||||
}
|
||||
|
||||
fun newInstance(data: PlayerData, startPos: Long? = null): Bundle {
|
||||
return Bundle().apply {
|
||||
//println(data)
|
||||
|
@ -1050,7 +1060,7 @@ class PlayerFragment : Fragment() {
|
|||
val epData = getEpisode() ?: return@addCastStateListener
|
||||
|
||||
val index = links.indexOf(getCurrentUrl())
|
||||
context?.startCast(
|
||||
(activity as MainActivity?)?.mCastSession?.startCast(
|
||||
apiName,
|
||||
currentIsMovie ?: return@addCastStateListener,
|
||||
currentHeaderName,
|
||||
|
@ -1737,15 +1747,6 @@ class PlayerFragment : Fragment() {
|
|||
|
||||
private val updateProgressAction = Runnable { updateProgressBar() }*/
|
||||
|
||||
private fun String.toSubtitleMimeType(): String {
|
||||
return when {
|
||||
endsWith("vtt", true) -> MimeTypes.TEXT_VTT
|
||||
endsWith("srt", true) -> MimeTypes.APPLICATION_SUBRIP
|
||||
endsWith("xml", true) || endsWith("ttml", true) -> MimeTypes.APPLICATION_TTML
|
||||
else -> MimeTypes.APPLICATION_SUBRIP // TODO get request to see
|
||||
}
|
||||
}
|
||||
|
||||
var activeSubtitles: List<String> = listOf()
|
||||
var preferredSubtitles: String = ""
|
||||
|
||||
|
@ -2047,7 +2048,44 @@ class PlayerFragment : Fragment() {
|
|||
}
|
||||
}
|
||||
|
||||
override fun onPlayerError(error: ExoPlaybackException) {
|
||||
override fun onPlayerError(error: PlaybackException) {
|
||||
println("CURRENT URL: " + currentUrl?.url)
|
||||
// Lets pray this doesn't spam Toasts :)
|
||||
val msg = error.message ?: ""
|
||||
when (val code = error.errorCode) {
|
||||
PlaybackException.ERROR_CODE_IO_FILE_NOT_FOUND, PlaybackException.ERROR_CODE_IO_NO_PERMISSION, PlaybackException.ERROR_CODE_IO_UNSPECIFIED -> {
|
||||
if (currentUrl?.url != "") {
|
||||
showToast(
|
||||
activity,
|
||||
"${getString(R.string.source_error)}\n$code\n$msg",
|
||||
LENGTH_SHORT
|
||||
)
|
||||
tryNextMirror()
|
||||
}
|
||||
}
|
||||
PlaybackException.ERROR_CODE_REMOTE_ERROR, PlaybackException.ERROR_CODE_IO_BAD_HTTP_STATUS, PlaybackException.ERROR_CODE_TIMEOUT, PlaybackException.ERROR_CODE_IO_NETWORK_CONNECTION_FAILED, PlaybackException.ERROR_CODE_IO_INVALID_HTTP_CONTENT_TYPE -> {
|
||||
showToast(activity, "${getString(R.string.remote_error)}\n$code\n$msg", LENGTH_SHORT)
|
||||
}
|
||||
PlaybackException.ERROR_CODE_DECODING_FAILED, PlaybackErrorEvent.ERROR_AUDIO_TRACK_INIT_FAILED, PlaybackErrorEvent.ERROR_AUDIO_TRACK_OTHER, PlaybackException.ERROR_CODE_AUDIO_TRACK_WRITE_FAILED, PlaybackException.ERROR_CODE_DECODER_INIT_FAILED, PlaybackException.ERROR_CODE_DECODER_QUERY_FAILED -> {
|
||||
showToast(
|
||||
activity,
|
||||
"${getString(R.string.render_error)}\n$code\n$msg",
|
||||
LENGTH_SHORT
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
showToast(
|
||||
activity,
|
||||
"${getString(R.string.unexpected_error)}\n$code\n$msg",
|
||||
LENGTH_SHORT
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
super.onPlayerError(error)
|
||||
}
|
||||
|
||||
/*override fun onPlayerError(error: ExoPlaybackException) {
|
||||
println("CURRENT URL: " + currentUrl?.url)
|
||||
// Lets pray this doesn't spam Toasts :)
|
||||
when (error.type) {
|
||||
|
@ -2079,7 +2117,7 @@ class PlayerFragment : Fragment() {
|
|||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
})
|
||||
} catch (e: java.lang.IllegalStateException) {
|
||||
println("Warning: Illegal state exception in PlayerFragment")
|
||||
|
|
|
@ -427,7 +427,7 @@ class ResultFragment : Fragment() {
|
|||
|
||||
fun startChromecast(startIndex: Int) {
|
||||
val eps = currentEpisodes ?: return
|
||||
context?.startCast(
|
||||
(activity as MainActivity?)?.mCastSession?.startCast(
|
||||
apiName,
|
||||
currentIsMovie ?: return,
|
||||
currentHeaderName,
|
||||
|
|
|
@ -6,7 +6,10 @@ import androidx.appcompat.app.AlertDialog
|
|||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceFragmentCompat
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.lagradost.cloudstream3.APIHolder.apis
|
||||
import com.lagradost.cloudstream3.APIHolder.getApiDubstatusSettings
|
||||
import com.lagradost.cloudstream3.APIHolder.getApiProviderLangSettings
|
||||
import com.lagradost.cloudstream3.APIHolder.restrictedApis
|
||||
import com.lagradost.cloudstream3.DubStatus
|
||||
import com.lagradost.cloudstream3.MainActivity.Companion.setLocale
|
||||
import com.lagradost.cloudstream3.MainActivity.Companion.showToast
|
||||
|
@ -19,6 +22,7 @@ import com.lagradost.cloudstream3.utils.Qualities
|
|||
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog
|
||||
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showDialog
|
||||
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showMultiDialog
|
||||
import com.lagradost.cloudstream3.utils.SubtitleHelper
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
|
@ -50,6 +54,7 @@ class SettingsFragment : PreferenceFragmentCompat() {
|
|||
val watchQualityPreference = findPreference<Preference>(getString(R.string.quality_pref_key))!!
|
||||
val legalPreference = findPreference<Preference>(getString(R.string.legal_notice_key))!!
|
||||
val subdubPreference = findPreference<Preference>(getString(R.string.display_sub_key))!!
|
||||
val providerLangPreference = findPreference<Preference>(getString(R.string.provider_lang_key))!!
|
||||
|
||||
legalPreference.setOnPreferenceClickListener {
|
||||
val builder: AlertDialog.Builder = AlertDialog.Builder(it.context)
|
||||
|
@ -85,7 +90,39 @@ class SettingsFragment : PreferenceFragmentCompat() {
|
|||
}
|
||||
}
|
||||
|
||||
return@setOnPreferenceClickListener true
|
||||
}
|
||||
|
||||
providerLangPreference.setOnPreferenceClickListener {
|
||||
val settingsManager = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
|
||||
activity?.getApiProviderLangSettings()?.let { current ->
|
||||
val allLangs = HashSet<String>()
|
||||
for (api in apis) {
|
||||
allLangs.add(api.lang)
|
||||
}
|
||||
for (api in restrictedApis) {
|
||||
allLangs.add(api.lang)
|
||||
}
|
||||
|
||||
val currentList = ArrayList<Int>()
|
||||
for (i in current) {
|
||||
currentList.add(allLangs.indexOf(i))
|
||||
}
|
||||
|
||||
val names = allLangs.mapNotNull { SubtitleHelper.fromTwoLettersToLanguage(it) }
|
||||
|
||||
context?.showMultiDialog(
|
||||
names,
|
||||
currentList,
|
||||
getString(R.string.provider_lang_settings),
|
||||
{}) { selectedList ->
|
||||
settingsManager.edit().putStringSet(
|
||||
this.getString(R.string.provider_lang_key),
|
||||
selectedList.map { names[it] }.toMutableSet()
|
||||
).apply()
|
||||
}
|
||||
}
|
||||
|
||||
return@setOnPreferenceClickListener true
|
||||
}
|
||||
|
|
|
@ -1,15 +1,12 @@
|
|||
package com.lagradost.cloudstream3.utils
|
||||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature
|
||||
import com.fasterxml.jackson.databind.json.JsonMapper
|
||||
import com.fasterxml.jackson.module.kotlin.KotlinModule
|
||||
import com.google.android.exoplayer2.ext.cast.CastPlayer
|
||||
import com.google.android.exoplayer2.util.MimeTypes
|
||||
import com.google.android.gms.cast.*
|
||||
import com.google.android.gms.cast.MediaStatus.REPEAT_MODE_REPEAT_OFF
|
||||
import com.google.android.gms.cast.framework.CastContext
|
||||
import com.google.android.gms.cast.framework.CastSession
|
||||
import com.google.android.gms.cast.framework.media.RemoteMediaClient
|
||||
import com.google.android.gms.common.api.PendingResult
|
||||
import com.google.android.gms.common.images.WebImage
|
||||
|
@ -59,13 +56,16 @@ object CastHelper {
|
|||
.build()
|
||||
}
|
||||
|
||||
return MediaInfo.Builder(link.url)
|
||||
val builder = MediaInfo.Builder(link.url)
|
||||
.setStreamType(MediaInfo.STREAM_TYPE_BUFFERED)
|
||||
.setContentType(MimeTypes.VIDEO_UNKNOWN)
|
||||
.setCustomData(data)
|
||||
.setMetadata(movieMetadata)
|
||||
.setMediaTracks(tracks)
|
||||
.build()
|
||||
data?.let {
|
||||
builder.setCustomData(data)
|
||||
}
|
||||
|
||||
return builder.build()
|
||||
}
|
||||
|
||||
fun awaitLinks(pending: PendingResult<RemoteMediaClient.MediaChannelResult>?, callback: (Boolean) -> Unit) {
|
||||
|
@ -84,8 +84,7 @@ object CastHelper {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
fun Context.startCast(
|
||||
fun CastSession?.startCast(
|
||||
apiName: String,
|
||||
isMovie: Boolean,
|
||||
title: String?,
|
||||
|
@ -97,11 +96,10 @@ object CastHelper {
|
|||
startIndex: Int? = null,
|
||||
startTime: Long? = null,
|
||||
) : Boolean {
|
||||
if(this == null) return false
|
||||
if (episodes.isEmpty()) return false
|
||||
if (currentLinks.size <= currentEpisodeIndex) return false
|
||||
|
||||
val castContext = CastContext.getSharedInstance(this)
|
||||
|
||||
val epData = episodes[currentEpisodeIndex]
|
||||
|
||||
val holder =
|
||||
|
@ -112,15 +110,8 @@ object CastHelper {
|
|||
val mediaItem =
|
||||
getMediaInfo(epData, holder, index, JSONObject(mapper.writeValueAsString(holder)), subtitles)
|
||||
|
||||
val castPlayer = CastPlayer(castContext)
|
||||
|
||||
castPlayer.repeatMode = REPEAT_MODE_REPEAT_OFF
|
||||
|
||||
awaitLinks(
|
||||
castPlayer.loadItem(
|
||||
MediaQueueItem.Builder(mediaItem).build(),
|
||||
startTime ?: 0,
|
||||
)
|
||||
this.remoteMediaClient?.load(MediaLoadRequestData.Builder().setMediaInfo(mediaItem).setCurrentTime(startTime ?: 0L).build() )
|
||||
) {
|
||||
if (currentLinks.size > index + 1)
|
||||
startCast(
|
||||
|
|
|
@ -202,5 +202,5 @@
|
|||
<string name="resume">Återuppta</string>
|
||||
<string name="storage_error">Ett nerladdningsfel uppstod, kolla om appen har lagringsbehörigheter</string>
|
||||
<string name="watch_quality_pref">Föredragen videokvalitet</string>
|
||||
|
||||
<string name="general">Allmänna Inställningar</string>
|
||||
</resources>
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
<string name="swipe_vertical_enabled_key" translatable="false">swipe_vertical_enabled_key</string>
|
||||
<string name="display_sub_key" translatable="false">display_sub_key</string>
|
||||
<string name="show_fillers_key" translatable="false">show_fillers_key</string>
|
||||
<string name="provider_lang_key" translatable="false">provider_lang_key</string>
|
||||
|
||||
|
||||
<!-- FORMAT MIGHT TRANSLATE, WILL CAUSE CRASH IF APPLIED WRONG -->
|
||||
<string name="extra_info_format" translatable="false" formatted="true">%d %s | %sMB</string>
|
||||
|
@ -276,4 +278,6 @@
|
|||
|
||||
It merely scrapes 3rd-party websites that are publicly accessable via any regular web browser. It is the responsibility of user to avoid any actions that might violate the laws governing his/her locality. Use CloudStream 3 at your own risk.
|
||||
</string>
|
||||
<string name="general">General</string>
|
||||
<string name="provider_lang_settings">Provider Languages</string>
|
||||
</resources>
|
||||
|
|
|
@ -177,16 +177,19 @@
|
|||
<item name="android:textColor">?attr/textColor</item>
|
||||
<item name="rippleColor">?attr/textColor</item>
|
||||
</style>
|
||||
|
||||
<!--@color/white ?attr/colorPrimary-->
|
||||
<!--CHECK ?attr/darkBackground ?attr/colorPrimary-->
|
||||
<!-- CHROMECAST -->
|
||||
<style name="CustomCastExpandedController" parent="CastExpandedController">
|
||||
<item name="castControlButtons">
|
||||
@array/cast_expanded_controller_control_buttons
|
||||
</item>
|
||||
<item name="castButtonColor">@null</item>
|
||||
<item name="castSeekBarProgressAndThumbColor">?attr/white</item> <!--@color/white ?attr/colorPrimary-->
|
||||
<item name="castSeekBarSecondaryProgressColor">?attr/darkBackground
|
||||
</item> <!--CHECK ?attr/darkBackground ?attr/colorPrimary-->
|
||||
<!-- <item name="castButtonColor">@null</item>
|
||||
|
||||
<item name="castSeekBarSecondaryProgressColor">@color/darkBar
|
||||
</item>
|
||||
-->
|
||||
<item name="castSeekBarProgressAndThumbColor">?attr/colorPrimary</item>
|
||||
<item name="castBackground">?attr/colorPrimary</item>
|
||||
<item name="castProgressBarColor">?attr/colorPrimary</item>
|
||||
<item name="castPlayButtonDrawable">@drawable/ic_baseline_play_arrow_24</item>
|
||||
|
|
|
@ -68,6 +68,32 @@
|
|||
app:defaultValue="false"
|
||||
/>
|
||||
</PreferenceCategory>
|
||||
<PreferenceCategory
|
||||
android:key="general"
|
||||
android:title="@string/general"
|
||||
app:isPreferenceVisible="true"
|
||||
>
|
||||
<Preference
|
||||
android:icon="@drawable/ic_baseline_language_24"
|
||||
android:key="@string/provider_lang_key"
|
||||
android:title="@string/provider_lang_settings">
|
||||
</Preference>
|
||||
<Preference
|
||||
android:key="@string/locale_key"
|
||||
android:title="@string/app_language"
|
||||
android:icon="@drawable/ic_baseline_language_24">
|
||||
</Preference>
|
||||
<Preference
|
||||
android:key="@string/display_sub_key"
|
||||
android:title="@string/display_subbed_dubbed_settings"
|
||||
android:icon="@drawable/ic_outline_voice_over_off_24">
|
||||
</Preference>
|
||||
<SwitchPreference
|
||||
android:key="@string/show_fillers_key"
|
||||
android:icon="@drawable/ic_baseline_skip_next_24"
|
||||
android:title="@string/show_fillers_settings"
|
||||
android:defaultValue="true"/>
|
||||
</PreferenceCategory>
|
||||
<PreferenceCategory
|
||||
android:key="search"
|
||||
android:title="@string/search"
|
||||
|
@ -80,11 +106,6 @@
|
|||
android:summary="@string/advanced_search_des"
|
||||
app:defaultValue="true"
|
||||
/>
|
||||
<Preference
|
||||
android:key="@string/display_sub_key"
|
||||
android:title="@string/display_subbed_dubbed_settings"
|
||||
android:icon="@drawable/ic_outline_voice_over_off_24">
|
||||
</Preference>
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory
|
||||
|
@ -98,16 +119,6 @@
|
|||
android:icon="@drawable/ic_baseline_warning_24">
|
||||
</Preference>
|
||||
|
||||
<Preference
|
||||
android:key="@string/locale_key"
|
||||
android:title="@string/app_language"
|
||||
android:icon="@drawable/ic_baseline_language_24">
|
||||
</Preference>
|
||||
<SwitchPreference
|
||||
android:key="@string/show_fillers_key"
|
||||
android:icon="@drawable/ic_baseline_skip_next_24"
|
||||
android:title="@string/show_fillers_settings"
|
||||
android:defaultValue="true"/>
|
||||
<SwitchPreference
|
||||
android:key="acra.disable"
|
||||
android:icon="@drawable/ic_baseline_bug_report_24"
|
||||
|
|
Loading…
Reference in a new issue