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
				
			
		|  | @ -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? { | ||||
|         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) | ||||
|         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,19 +305,23 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi | |||
|                                         loadIndex(index + 1) | ||||
|                                     } | ||||
|                                 }*/ | ||||
|                                 activity.runOnUiThread { | ||||
|                                     awaitLinks( | ||||
| 
 | ||||
|                                 awaitLinks( | ||||
|                                     remoteMediaClient?.queueAppendItem( | ||||
|                                         MediaQueueItem.Builder(mediaInfo).build(), | ||||
|                                         JSONObject() | ||||
|                                     ) | ||||
|                                 ) { | ||||
|                                     println("FAILED TO LOAD NEXT ITEM") | ||||
|                                     //  loadIndex(1) | ||||
|                                         remoteMediaClient?.queueAppendItem( | ||||
|                                             MediaQueueItem.Builder(mediaInfo).build(), | ||||
|                                             JSONObject() | ||||
|                                         ) | ||||
| 
 | ||||
| 
 | ||||
|                                     ) { | ||||
|                                         println("FAILED TO LOAD NEXT ITEM") | ||||
|                                         //  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.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…
	
	Add table
		Add a link
		
	
		Reference in a new issue