fixed chromecast, fixed tenshi, fixed lang providers

This commit is contained in:
LagradOst 2021-09-23 20:08:57 +02:00
parent d65ccffe4e
commit f394469787
13 changed files with 266 additions and 95 deletions

View file

@ -31,8 +31,8 @@ android {
applicationId "com.lagradost.cloudstream3" applicationId "com.lagradost.cloudstream3"
minSdkVersion 21 minSdkVersion 21
targetSdkVersion 31 targetSdkVersion 31
versionCode 26 versionCode 27
versionName "1.9.10" versionName "1.9.11"
resValue "string", "app_version", resValue "string", "app_version",
"${defaultConfig.versionName}${versionNameSuffix ?: ""}" "${defaultConfig.versionName}${versionNameSuffix ?: ""}"
@ -72,9 +72,6 @@ android {
} }
repositories { repositories {
maven {
url 'https://github.com/psiegman/mvn-repo/raw/master/releases'
}
maven { url 'https://jitpack.io' } maven { url 'https://jitpack.io' }
} }
@ -108,9 +105,9 @@ dependencies {
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0' implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
// Exoplayer // Exoplayer
implementation 'com.google.android.exoplayer:exoplayer:2.14.2' implementation 'com.google.android.exoplayer:exoplayer:2.15.0'
implementation 'com.google.android.exoplayer:extension-cast:2.14.2' implementation 'com.google.android.exoplayer:extension-cast:2.15.0'
implementation "com.google.android.exoplayer:extension-mediasession:2.14.2" implementation "com.google.android.exoplayer:extension-mediasession:2.15.0"
//implementation "com.google.android.exoplayer:extension-leanback:2.14.0" //implementation "com.google.android.exoplayer:extension-leanback:2.14.0"
// Bug reports // Bug reports

View file

@ -1,6 +1,7 @@
package com.lagradost.cloudstream3 package com.lagradost.cloudstream3
import android.app.Activity import android.app.Activity
import android.content.Context
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.fasterxml.jackson.databind.DeserializationFeature import com.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.databind.json.JsonMapper 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.movieproviders.*
import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.ExtractorLink
import java.util.* import java.util.*
import kotlin.collections.ArrayList
import kotlin.collections.HashSet
const val USER_AGENT = 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" "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() return url.replace(getApiFromName(apiName).mainUrl, "").replace("/", "").hashCode()
} }
fun Activity.getApiSettings(): HashSet<String> { fun Context.getApiSettings(): HashSet<String> {
val settingsManager = PreferenceManager.getDefaultSharedPreferences(this) val settingsManager = PreferenceManager.getDefaultSharedPreferences(this)
val hashSet = HashSet<String>() val hashSet = HashSet<String>()
hashSet.addAll(apis.map { it.name }) hashSet.addAll(apis.map { it.name })
return settingsManager.getStringSet( val set = settingsManager.getStringSet(
this.getString(R.string.search_providers_list_key), this.getString(R.string.search_providers_list_key),
hashSet hashSet
)?.toHashSet() ?: 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 settingsManager = PreferenceManager.getDefaultSharedPreferences(this)
val hashSet = HashSet<DubStatus>() val hashSet = HashSet<DubStatus>()
hashSet.addAll(DubStatus.values()) hashSet.addAll(DubStatus.values())
@ -96,7 +110,20 @@ object APIHolder {
return list.filter { names.contains(it) }.map { DubStatus.valueOf(it) }.toHashSet() 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 settingsManager = PreferenceManager.getDefaultSharedPreferences(this)
val hashSet = HashSet<TvType>() val hashSet = HashSet<TvType>()
hashSet.addAll(TvType.values()) hashSet.addAll(TvType.values())
@ -120,6 +147,8 @@ abstract class MainAPI {
open val name = "NONE" open val name = "NONE"
open val mainUrl = "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*/ /**If link is stored in the "data" string, so links can be instantly loaded*/
open val instantLinkLoading = false open val instantLinkLoading = false

View file

@ -26,7 +26,7 @@ import androidx.navigation.ui.setupWithNavController
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import androidx.transition.ChangeBounds import androidx.transition.ChangeBounds
import androidx.transition.TransitionManager 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.jaredrummler.android.colorpicker.ColorPickerDialogListener
import com.lagradost.cloudstream3.APIHolder.apis import com.lagradost.cloudstream3.APIHolder.apis
import com.lagradost.cloudstream3.APIHolder.getApiDubstatusSettings 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 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 { companion object {
var canEnterPipMode: Boolean = false var canEnterPipMode: Boolean = false
var canShowPipMode: 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 ) // THEME IS SET BEFORE VIEW IS CREATED TO APPLY THE THEME TO THE MAIN VIEW
updateLocale() updateLocale()
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
mSessionManager = CastContext.getSharedInstance(this).sessionManager
window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN) window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN)

View file

@ -140,12 +140,16 @@ class TenshiProvider : MainAPI() {
@SuppressLint("SimpleDateFormat") @SuppressLint("SimpleDateFormat")
private fun dateParser(dateString: String): String? { private fun dateParser(dateString: String): String? {
try {
val format = SimpleDateFormat("dd 'of' MMM',' yyyy") val format = SimpleDateFormat("dd 'of' MMM',' yyyy")
val newFormat = SimpleDateFormat("dd-MM-yyyy") val newFormat = SimpleDateFormat("dd-MM-yyyy")
val data = format.parse( val data = format.parse(
dateString.replace("th ", " ").replace("st ", " ").replace("nd ", " ").replace("rd ", " ") dateString.replace("th ", " ").replace("st ", " ").replace("nd ", " ").replace("rd ", " ")
) ?: return null ) ?: return null
return newFormat.format(data) return newFormat.format(data)
} catch (e : Exception) {
return null
}
} }
// data class TenshiSearchResponse( // data class TenshiSearchResponse(

View file

@ -11,6 +11,7 @@ import com.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.databind.json.JsonMapper import com.fasterxml.jackson.databind.json.JsonMapper
import com.fasterxml.jackson.module.kotlin.KotlinModule import com.fasterxml.jackson.module.kotlin.KotlinModule
import com.google.android.gms.cast.MediaQueueItem 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.MediaStatus.REPEAT_MODE_REPEAT_OFF
import com.google.android.gms.cast.MediaTrack import com.google.android.gms.cast.MediaTrack
import com.google.android.gms.cast.TextTrackStyle import com.google.android.gms.cast.TextTrackStyle
@ -33,12 +34,17 @@ import com.lagradost.cloudstream3.utils.ExtractorLink
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.json.JSONObject import org.json.JSONObject
import kotlin.concurrent.thread
class SkipOpController(val view: ImageView) : UIController() { class SkipOpController(val view: ImageView) : UIController() {
init { init {
view.setImageResource(R.drawable.exo_controls_fastforward) view.setImageResource(R.drawable.exo_controls_fastforward)
view.setOnClickListener { 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 try { // THIS IS VERY IMPORTANT BECAUSE WE NEVER WANT TO AUTOLOAD THE NEXT EPISODE
val currentIdIndex = remoteMediaClient?.getItemIndex() 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) { if (currentIdIndex == null && nextId != null) {
awaitLinks( awaitLinks(
remoteMediaClient?.queueInsertAndPlayItem( remoteMediaClient?.queueInsertAndPlayItem(
MediaQueueItem.Builder( MediaQueueItem.Builder(mediaItem).build(),
mediaItem
)
.build(),
nextId, nextId,
startAt, startAt,
JSONObject() JSONObject()
@ -223,6 +225,7 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi
bottomSheetDialog.dismiss() bottomSheetDialog.dismiss()
} }
} }
} }
} }
@ -253,8 +256,7 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi
if (itemCount != null && itemCount - currentIdIndex == 1 && !isLoadingMore) { if (itemCount != null && itemCount - currentIdIndex == 1 && !isLoadingMore) {
isLoadingMore = true isLoadingMore = true
thread {
main {
val index = meta.currentEpisodeIndex + 1 val index = meta.currentEpisodeIndex + 1
val epData = meta.episodes[index] val epData = meta.episodes[index]
val links = ArrayList<ExtractorLink>() val links = ArrayList<ExtractorLink>()
@ -280,9 +282,8 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi
currentEpisodeIndex = index currentEpisodeIndex = index
) )
val done = withContext(Dispatchers.IO) { val done =
JSONObject(mapper.writeValueAsString(jsonCopy)) JSONObject(mapper.writeValueAsString(jsonCopy))
}
val mediaInfo = getMediaInfo( val mediaInfo = getMediaInfo(
epData, epData,
@ -304,20 +305,24 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi
loadIndex(index + 1) loadIndex(index + 1)
} }
}*/ }*/
activity.runOnUiThread {
awaitLinks( awaitLinks(
remoteMediaClient?.queueAppendItem( remoteMediaClient?.queueAppendItem(
MediaQueueItem.Builder(mediaInfo).build(), MediaQueueItem.Builder(mediaInfo).build(),
JSONObject() JSONObject()
) )
) { ) {
println("FAILED TO LOAD NEXT ITEM") println("FAILED TO LOAD NEXT ITEM")
// loadIndex(1) // loadIndex(1)
} }
isLoadingMore = false 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.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.setImageResource(if (forwards) R.drawable.go_forward_30 else R.drawable.go_back_30)
view.setOnClickListener { 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())
}
} }
} }
} }

View file

@ -14,6 +14,7 @@ import android.database.ContentObserver
import android.graphics.Color import android.graphics.Color
import android.graphics.drawable.Icon import android.graphics.drawable.Icon
import android.media.AudioManager import android.media.AudioManager
import android.media.metrics.PlaybackErrorEvent
import android.net.Uri import android.net.Uri
import android.os.* import android.os.*
import android.provider.Settings import android.provider.Settings
@ -46,6 +47,7 @@ import com.fasterxml.jackson.module.kotlin.KotlinModule
import com.fasterxml.jackson.module.kotlin.readValue import com.fasterxml.jackson.module.kotlin.readValue
import com.google.android.exoplayer2.* import com.google.android.exoplayer2.*
import com.google.android.exoplayer2.C.TIME_UNSET 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.database.ExoDatabaseProvider
import com.google.android.exoplayer2.source.DefaultMediaSourceFactory import com.google.android.exoplayer2.source.DefaultMediaSourceFactory
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector import com.google.android.exoplayer2.trackselection.DefaultTrackSelector
@ -165,7 +167,6 @@ data class UriData(
// YE, I KNOW, THIS COULD BE HANDLED A LOT BETTER // YE, I KNOW, THIS COULD BE HANDLED A LOT BETTER
class PlayerFragment : Fragment() { class PlayerFragment : Fragment() {
// ============ TORRENT ============ // ============ TORRENT ============
//private var torrentStream: TorrentStream? = null //private var torrentStream: TorrentStream? = null
private var lastTorrentUrl = "" private var lastTorrentUrl = ""
@ -752,6 +753,15 @@ class PlayerFragment : Fragment() {
private var volumeObserver: SettingsContentObserver? = null private var volumeObserver: SettingsContentObserver? = null
companion object { 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 { fun newInstance(data: PlayerData, startPos: Long? = null): Bundle {
return Bundle().apply { return Bundle().apply {
//println(data) //println(data)
@ -1050,7 +1060,7 @@ class PlayerFragment : Fragment() {
val epData = getEpisode() ?: return@addCastStateListener val epData = getEpisode() ?: return@addCastStateListener
val index = links.indexOf(getCurrentUrl()) val index = links.indexOf(getCurrentUrl())
context?.startCast( (activity as MainActivity?)?.mCastSession?.startCast(
apiName, apiName,
currentIsMovie ?: return@addCastStateListener, currentIsMovie ?: return@addCastStateListener,
currentHeaderName, currentHeaderName,
@ -1737,15 +1747,6 @@ class PlayerFragment : Fragment() {
private val updateProgressAction = Runnable { updateProgressBar() }*/ 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 activeSubtitles: List<String> = listOf()
var preferredSubtitles: String = "" 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) println("CURRENT URL: " + currentUrl?.url)
// Lets pray this doesn't spam Toasts :) // Lets pray this doesn't spam Toasts :)
when (error.type) { when (error.type) {
@ -2079,7 +2117,7 @@ class PlayerFragment : Fragment() {
) )
} }
} }
} }*/
}) })
} catch (e: java.lang.IllegalStateException) { } catch (e: java.lang.IllegalStateException) {
println("Warning: Illegal state exception in PlayerFragment") println("Warning: Illegal state exception in PlayerFragment")

View file

@ -427,7 +427,7 @@ class ResultFragment : Fragment() {
fun startChromecast(startIndex: Int) { fun startChromecast(startIndex: Int) {
val eps = currentEpisodes ?: return val eps = currentEpisodes ?: return
context?.startCast( (activity as MainActivity?)?.mCastSession?.startCast(
apiName, apiName,
currentIsMovie ?: return, currentIsMovie ?: return,
currentHeaderName, currentHeaderName,

View file

@ -6,7 +6,10 @@ import androidx.appcompat.app.AlertDialog
import androidx.preference.Preference import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceFragmentCompat
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.lagradost.cloudstream3.APIHolder.apis
import com.lagradost.cloudstream3.APIHolder.getApiDubstatusSettings 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.DubStatus
import com.lagradost.cloudstream3.MainActivity.Companion.setLocale import com.lagradost.cloudstream3.MainActivity.Companion.setLocale
import com.lagradost.cloudstream3.MainActivity.Companion.showToast 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.showBottomDialog
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showDialog import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showDialog
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showMultiDialog import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showMultiDialog
import com.lagradost.cloudstream3.utils.SubtitleHelper
import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard
import kotlin.concurrent.thread import kotlin.concurrent.thread
@ -50,6 +54,7 @@ class SettingsFragment : PreferenceFragmentCompat() {
val watchQualityPreference = findPreference<Preference>(getString(R.string.quality_pref_key))!! val watchQualityPreference = findPreference<Preference>(getString(R.string.quality_pref_key))!!
val legalPreference = findPreference<Preference>(getString(R.string.legal_notice_key))!! val legalPreference = findPreference<Preference>(getString(R.string.legal_notice_key))!!
val subdubPreference = findPreference<Preference>(getString(R.string.display_sub_key))!! val subdubPreference = findPreference<Preference>(getString(R.string.display_sub_key))!!
val providerLangPreference = findPreference<Preference>(getString(R.string.provider_lang_key))!!
legalPreference.setOnPreferenceClickListener { legalPreference.setOnPreferenceClickListener {
val builder: AlertDialog.Builder = AlertDialog.Builder(it.context) 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 return@setOnPreferenceClickListener true
} }

View file

@ -1,15 +1,12 @@
package com.lagradost.cloudstream3.utils package com.lagradost.cloudstream3.utils
import android.content.Context
import android.net.Uri import android.net.Uri
import com.fasterxml.jackson.databind.DeserializationFeature import com.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.databind.json.JsonMapper import com.fasterxml.jackson.databind.json.JsonMapper
import com.fasterxml.jackson.module.kotlin.KotlinModule 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.exoplayer2.util.MimeTypes
import com.google.android.gms.cast.* import com.google.android.gms.cast.*
import com.google.android.gms.cast.MediaStatus.REPEAT_MODE_REPEAT_OFF import com.google.android.gms.cast.framework.CastSession
import com.google.android.gms.cast.framework.CastContext
import com.google.android.gms.cast.framework.media.RemoteMediaClient import com.google.android.gms.cast.framework.media.RemoteMediaClient
import com.google.android.gms.common.api.PendingResult import com.google.android.gms.common.api.PendingResult
import com.google.android.gms.common.images.WebImage import com.google.android.gms.common.images.WebImage
@ -59,13 +56,16 @@ object CastHelper {
.build() .build()
} }
return MediaInfo.Builder(link.url) val builder = MediaInfo.Builder(link.url)
.setStreamType(MediaInfo.STREAM_TYPE_BUFFERED) .setStreamType(MediaInfo.STREAM_TYPE_BUFFERED)
.setContentType(MimeTypes.VIDEO_UNKNOWN) .setContentType(MimeTypes.VIDEO_UNKNOWN)
.setCustomData(data)
.setMetadata(movieMetadata) .setMetadata(movieMetadata)
.setMediaTracks(tracks) .setMediaTracks(tracks)
.build() data?.let {
builder.setCustomData(data)
}
return builder.build()
} }
fun awaitLinks(pending: PendingResult<RemoteMediaClient.MediaChannelResult>?, callback: (Boolean) -> Unit) { fun awaitLinks(pending: PendingResult<RemoteMediaClient.MediaChannelResult>?, callback: (Boolean) -> Unit) {
@ -84,8 +84,7 @@ object CastHelper {
} }
} }
fun CastSession?.startCast(
fun Context.startCast(
apiName: String, apiName: String,
isMovie: Boolean, isMovie: Boolean,
title: String?, title: String?,
@ -97,11 +96,10 @@ object CastHelper {
startIndex: Int? = null, startIndex: Int? = null,
startTime: Long? = null, startTime: Long? = null,
) : Boolean { ) : Boolean {
if(this == null) return false
if (episodes.isEmpty()) return false if (episodes.isEmpty()) return false
if (currentLinks.size <= currentEpisodeIndex) return false if (currentLinks.size <= currentEpisodeIndex) return false
val castContext = CastContext.getSharedInstance(this)
val epData = episodes[currentEpisodeIndex] val epData = episodes[currentEpisodeIndex]
val holder = val holder =
@ -112,15 +110,8 @@ object CastHelper {
val mediaItem = val mediaItem =
getMediaInfo(epData, holder, index, JSONObject(mapper.writeValueAsString(holder)), subtitles) getMediaInfo(epData, holder, index, JSONObject(mapper.writeValueAsString(holder)), subtitles)
val castPlayer = CastPlayer(castContext)
castPlayer.repeatMode = REPEAT_MODE_REPEAT_OFF
awaitLinks( awaitLinks(
castPlayer.loadItem( this.remoteMediaClient?.load(MediaLoadRequestData.Builder().setMediaInfo(mediaItem).setCurrentTime(startTime ?: 0L).build() )
MediaQueueItem.Builder(mediaItem).build(),
startTime ?: 0,
)
) { ) {
if (currentLinks.size > index + 1) if (currentLinks.size > index + 1)
startCast( startCast(

View file

@ -202,5 +202,5 @@
<string name="resume">Återuppta</string> <string name="resume">Återuppta</string>
<string name="storage_error">Ett nerladdningsfel uppstod, kolla om appen har lagringsbehörigheter</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="watch_quality_pref">Föredragen videokvalitet</string>
<string name="general">Allmänna Inställningar</string>
</resources> </resources>

View file

@ -22,6 +22,8 @@
<string name="swipe_vertical_enabled_key" translatable="false">swipe_vertical_enabled_key</string> <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="display_sub_key" translatable="false">display_sub_key</string>
<string name="show_fillers_key" translatable="false">show_fillers_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 --> <!-- FORMAT MIGHT TRANSLATE, WILL CAUSE CRASH IF APPLIED WRONG -->
<string name="extra_info_format" translatable="false" formatted="true">%d %s | %sMB</string> <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. 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>
<string name="general">General</string>
<string name="provider_lang_settings">Provider Languages</string>
</resources> </resources>

View file

@ -177,16 +177,19 @@
<item name="android:textColor">?attr/textColor</item> <item name="android:textColor">?attr/textColor</item>
<item name="rippleColor">?attr/textColor</item> <item name="rippleColor">?attr/textColor</item>
</style> </style>
<!--@color/white ?attr/colorPrimary-->
<!--CHECK ?attr/darkBackground ?attr/colorPrimary-->
<!-- CHROMECAST --> <!-- CHROMECAST -->
<style name="CustomCastExpandedController" parent="CastExpandedController"> <style name="CustomCastExpandedController" parent="CastExpandedController">
<item name="castControlButtons"> <item name="castControlButtons">
@array/cast_expanded_controller_control_buttons @array/cast_expanded_controller_control_buttons
</item> </item>
<item name="castButtonColor">@null</item> <!-- <item name="castButtonColor">@null</item>
<item name="castSeekBarProgressAndThumbColor">?attr/white</item> <!--@color/white ?attr/colorPrimary-->
<item name="castSeekBarSecondaryProgressColor">?attr/darkBackground <item name="castSeekBarSecondaryProgressColor">@color/darkBar
</item> <!--CHECK ?attr/darkBackground ?attr/colorPrimary--> </item>
-->
<item name="castSeekBarProgressAndThumbColor">?attr/colorPrimary</item>
<item name="castBackground">?attr/colorPrimary</item> <item name="castBackground">?attr/colorPrimary</item>
<item name="castProgressBarColor">?attr/colorPrimary</item> <item name="castProgressBarColor">?attr/colorPrimary</item>
<item name="castPlayButtonDrawable">@drawable/ic_baseline_play_arrow_24</item> <item name="castPlayButtonDrawable">@drawable/ic_baseline_play_arrow_24</item>

View file

@ -68,6 +68,32 @@
app:defaultValue="false" app:defaultValue="false"
/> />
</PreferenceCategory> </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 <PreferenceCategory
android:key="search" android:key="search"
android:title="@string/search" android:title="@string/search"
@ -80,11 +106,6 @@
android:summary="@string/advanced_search_des" android:summary="@string/advanced_search_des"
app:defaultValue="true" 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>
<PreferenceCategory <PreferenceCategory
@ -98,16 +119,6 @@
android:icon="@drawable/ic_baseline_warning_24"> android:icon="@drawable/ic_baseline_warning_24">
</Preference> </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 <SwitchPreference
android:key="acra.disable" android:key="acra.disable"
android:icon="@drawable/ic_baseline_bug_report_24" android:icon="@drawable/ic_baseline_bug_report_24"