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

417 lines
18 KiB
Kotlin
Raw Normal View History

2021-06-06 18:06:01 +00:00
package com.lagradost.cloudstream3.ui
import android.os.Bundle
2021-07-02 18:46:18 +00:00
import android.util.Log
2021-06-06 18:06:01 +00:00
import android.view.Menu
2021-06-14 19:24:07 +00:00
import android.view.View.*
2021-07-02 18:46:18 +00:00
import android.widget.*
import androidx.appcompat.app.AlertDialog
2021-06-14 00:00:29 +00:00
import com.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.databind.json.JsonMapper
import com.fasterxml.jackson.module.kotlin.KotlinModule
2021-06-14 16:58:43 +00:00
import com.google.android.gms.cast.MediaQueueItem
import com.google.android.gms.cast.MediaSeekOptions
2021-06-14 16:58:43 +00:00
import com.google.android.gms.cast.MediaStatus.REPEAT_MODE_REPEAT_OFF
2021-07-02 18:46:18 +00:00
import com.google.android.gms.cast.MediaTrack
import com.google.android.gms.cast.TextTrackStyle
2021-06-06 18:06:01 +00:00
import com.google.android.gms.cast.framework.CastButtonFactory
import com.google.android.gms.cast.framework.CastSession
2021-06-14 19:24:07 +00:00
import com.google.android.gms.cast.framework.media.RemoteMediaClient
2021-06-06 18:06:01 +00:00
import com.google.android.gms.cast.framework.media.uicontroller.UIController
import com.google.android.gms.cast.framework.media.widget.ExpandedControllerActivity
import com.lagradost.cloudstream3.R
2022-01-16 22:31:42 +00:00
import com.lagradost.cloudstream3.mvvm.Resource
2022-04-22 19:01:29 +00:00
import com.lagradost.cloudstream3.mvvm.logError
2022-01-16 22:31:42 +00:00
import com.lagradost.cloudstream3.mvvm.safeApiCall
2022-01-07 19:27:25 +00:00
import com.lagradost.cloudstream3.sortSubs
2021-06-14 16:58:43 +00:00
import com.lagradost.cloudstream3.sortUrls
2022-01-07 19:27:25 +00:00
import com.lagradost.cloudstream3.ui.player.RepoLinkGenerator
import com.lagradost.cloudstream3.ui.player.SubtitleData
2021-06-14 16:58:43 +00:00
import com.lagradost.cloudstream3.ui.result.ResultEpisode
2022-02-12 20:20:56 +00:00
import com.lagradost.cloudstream3.ui.subtitles.ChromecastSubtitlesFragment
2022-01-14 18:14:24 +00:00
import com.lagradost.cloudstream3.utils.AppUtils.toJson
2021-06-14 19:24:07 +00:00
import com.lagradost.cloudstream3.utils.CastHelper.awaitLinks
2021-06-14 16:58:43 +00:00
import com.lagradost.cloudstream3.utils.CastHelper.getMediaInfo
2022-01-16 22:31:42 +00:00
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
2021-06-14 16:58:43 +00:00
import com.lagradost.cloudstream3.utils.DataStore.toKotlinObject
2022-04-22 19:01:29 +00:00
import com.lagradost.cloudstream3.utils.DataStoreHelper
2021-06-14 16:58:43 +00:00
import com.lagradost.cloudstream3.utils.ExtractorLink
2022-05-06 11:55:56 +00:00
import com.lagradost.cloudstream3.utils.Qualities
2021-12-12 02:33:17 +00:00
import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe
2021-06-06 18:06:01 +00:00
import org.json.JSONObject
2021-10-03 00:09:13 +00:00
/*class SkipOpController(val view: ImageView) : UIController() {
2021-06-06 18:06:01 +00:00
init {
view.setImageResource(R.drawable.exo_controls_fastforward)
view.setOnClickListener {
remoteMediaClient?.let {
val options = MediaSeekOptions.Builder()
.setPosition(it.approximateStreamPosition + 85000)
it.seek(options.build())
}
2021-06-06 18:06:01 +00:00
}
}
2021-10-03 00:09:13 +00:00
}*/
2021-06-06 18:06:01 +00:00
2021-06-14 19:24:07 +00:00
private fun RemoteMediaClient.getItemIndex(): Int? {
return try {
2021-10-03 00:09:13 +00:00
val index = this.mediaQueue.itemIds.indexOf(this.currentItem?.itemId ?: 0)
if (index < 0) null else index
2021-06-14 19:24:07 +00:00
} catch (e: Exception) {
null
}
}
class SkipNextEpisodeController(val view: ImageView) : UIController() {
init {
2021-06-15 16:07:20 +00:00
view.setImageResource(R.drawable.ic_baseline_skip_next_24)
2021-06-14 19:24:07 +00:00
view.setOnClickListener {
remoteMediaClient?.let {
it.queueNext(JSONObject())
view.visibility = GONE // TO PREVENT MULTI CLICK
}
}
}
override fun onMediaStatusUpdated() {
super.onMediaStatusUpdated()
view.visibility = GONE
val currentIdIndex = remoteMediaClient?.getItemIndex() ?: return
val itemCount = remoteMediaClient?.mediaQueue?.itemCount ?: return
if (itemCount - currentIdIndex > 1 && remoteMediaClient?.isLoadingNextItem == false) {
view.visibility = VISIBLE
}
}
}
2021-06-14 16:58:43 +00:00
data class MetadataHolder(
val apiName: String,
val isMovie: Boolean,
2021-06-14 16:58:43 +00:00
val title: String?,
val poster: String?,
val currentEpisodeIndex: Int,
val episodes: List<ResultEpisode>,
val currentLinks: List<ExtractorLink>,
2022-01-07 19:27:25 +00:00
val currentSubtitles: List<SubtitleData>
2021-06-14 16:58:43 +00:00
)
2021-06-14 00:00:29 +00:00
2022-01-07 19:27:25 +00:00
class SelectSourceController(val view: ImageView, val activity: ControllerActivity) :
UIController() {
2021-06-14 00:00:29 +00:00
private val mapper: JsonMapper = JsonMapper.builder().addModule(KotlinModule())
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false).build()
2021-06-06 18:06:01 +00:00
init {
view.setImageResource(R.drawable.ic_baseline_playlist_play_24)
view.setOnClickListener {
// lateinit var dialog: AlertDialog
2021-06-14 00:00:29 +00:00
val holder = getCurrentMetaData()
if (holder != null) {
2021-06-14 16:58:43 +00:00
val items = holder.currentLinks
2021-06-14 19:24:07 +00:00
if (items.isNotEmpty() && remoteMediaClient?.currentItem != null) {
2021-07-02 18:46:18 +00:00
val subTracks =
remoteMediaClient?.mediaInfo?.mediaTracks?.filter { it.type == MediaTrack.TYPE_TEXT }
?: ArrayList()
2022-01-07 19:27:25 +00:00
val bottomSheetDialogBuilder =
AlertDialog.Builder(view.context, R.style.AlertDialogCustomBlack)
2021-07-02 18:46:18 +00:00
bottomSheetDialogBuilder.setView(R.layout.sort_bottom_sheet)
val bottomSheetDialog = bottomSheetDialogBuilder.create()
bottomSheetDialog.show()
// bottomSheetDialog.setContentView(R.layout.sort_bottom_sheet)
2022-01-07 19:27:25 +00:00
val providerList =
bottomSheetDialog.findViewById<ListView>(R.id.sort_providers)!!
val subtitleList =
bottomSheetDialog.findViewById<ListView>(R.id.sort_subtitles)!!
2021-07-02 18:46:18 +00:00
if (subTracks.isEmpty()) {
2022-01-07 19:27:25 +00:00
bottomSheetDialog.findViewById<LinearLayout>(R.id.sort_subtitles_holder)?.visibility =
GONE
2021-07-02 18:46:18 +00:00
} else {
2022-01-07 19:27:25 +00:00
val arrayAdapter =
ArrayAdapter<String>(view.context, R.layout.sort_bottom_single_choice)
arrayAdapter.add(view.context.getString(R.string.no_subtitles))
2021-07-02 19:43:15 +00:00
arrayAdapter.addAll(subTracks.mapNotNull { it.name })
2021-07-02 18:46:18 +00:00
subtitleList.choiceMode = AbsListView.CHOICE_MODE_SINGLE
subtitleList.adapter = arrayAdapter
2021-07-02 19:43:15 +00:00
val currentTracks = remoteMediaClient?.mediaStatus?.activeTrackIds
val subtitleIndex =
if (currentTracks == null) 0 else subTracks.map { it.id }
.indexOfFirst { currentTracks.contains(it) } + 1
subtitleList.setSelection(subtitleIndex)
subtitleList.setItemChecked(subtitleIndex, true)
2021-07-02 18:46:18 +00:00
subtitleList.setOnItemClickListener { _, _, which, _ ->
if (which == 0) {
2021-10-03 00:09:13 +00:00
remoteMediaClient?.setActiveMediaTracks(longArrayOf()) // NO SUBS
2021-07-02 18:46:18 +00:00
} else {
2022-02-12 20:20:56 +00:00
ChromecastSubtitlesFragment.getCurrentSavedStyle().apply {
val font = TextTrackStyle()
font.fontFamily = fontFamily ?: "Google Sans"
fontGenericFamily?.let {
font.fontGenericFamily = it
}
font.windowColor = windowColor
font.backgroundColor = backgroundColor
2021-07-02 18:46:18 +00:00
2022-02-12 20:20:56 +00:00
font.edgeColor = edgeColor
font.edgeType = edgeType
font.foregroundColor = foregroundColor
font.fontScale = fontScale
2021-07-02 18:46:18 +00:00
2022-02-12 20:20:56 +00:00
remoteMediaClient?.setTextTrackStyle(font)
}
2021-07-02 18:46:18 +00:00
2021-10-03 00:09:13 +00:00
remoteMediaClient?.setActiveMediaTracks(longArrayOf(subTracks[which - 1].id))
?.setResultCallback {
2021-07-02 18:46:18 +00:00
if (!it.status.isSuccess) {
Log.e(
"CHROMECAST", "Failed with status code:" +
it.status.statusCode + " > " + it.status.statusMessage
)
}
}
}
2021-12-12 02:33:17 +00:00
bottomSheetDialog.dismissSafe(activity)
2021-07-02 18:46:18 +00:00
}
}
2021-06-14 00:00:29 +00:00
2021-06-14 16:58:43 +00:00
//https://developers.google.com/cast/docs/reference/web_receiver/cast.framework.messages.MediaInformation
2021-06-14 19:24:07 +00:00
val contentUrl = (remoteMediaClient?.currentItem?.media?.contentUrl
?: remoteMediaClient?.currentItem?.media?.contentId)
2021-06-14 16:58:43 +00:00
2022-05-06 11:55:56 +00:00
val sortingMethods = items.map { "${it.name} ${Qualities.getStringByInt(it.quality)}" }.toTypedArray()
2021-06-16 16:54:07 +00:00
val sotringIndex = items.indexOfFirst { it.url == contentUrl }
2022-01-07 19:27:25 +00:00
val arrayAdapter =
ArrayAdapter<String>(view.context, R.layout.sort_bottom_single_choice)
2021-06-16 16:54:07 +00:00
arrayAdapter.addAll(sortingMethods.toMutableList())
2021-07-02 18:46:18 +00:00
providerList.choiceMode = AbsListView.CHOICE_MODE_SINGLE
providerList.adapter = arrayAdapter
2021-07-02 19:43:15 +00:00
providerList.setSelection(sotringIndex)
2021-07-02 18:46:18 +00:00
providerList.setItemChecked(sotringIndex, true)
2021-06-16 16:54:07 +00:00
2021-07-02 18:46:18 +00:00
providerList.setOnItemClickListener { _, _, which, _ ->
2021-06-14 16:58:43 +00:00
val epData = holder.episodes[holder.currentEpisodeIndex]
2021-06-14 19:24:07 +00:00
fun loadMirror(index: Int) {
if (holder.currentLinks.size <= index) return
2021-06-14 16:58:43 +00:00
2021-06-14 19:24:07 +00:00
val mediaItem = getMediaInfo(
epData,
holder,
index,
2021-07-02 18:46:18 +00:00
remoteMediaClient?.mediaInfo?.customData,
holder.currentSubtitles,
2021-07-01 20:11:33 +00:00
)
2021-06-14 16:58:43 +00:00
2021-06-14 19:24:07 +00:00
val startAt = remoteMediaClient?.approximateStreamPosition ?: 0
2021-06-14 16:58:43 +00:00
2021-06-14 19:24:07 +00:00
//remoteMediaClient.load(mediaItem, true, startAt)
try { // THIS IS VERY IMPORTANT BECAUSE WE NEVER WANT TO AUTOLOAD THE NEXT EPISODE
val currentIdIndex = remoteMediaClient?.getItemIndex()
2021-06-14 16:58:43 +00:00
2022-01-07 19:27:25 +00:00
val nextId = remoteMediaClient?.mediaQueue?.itemIds?.get(
currentIdIndex?.plus(1) ?: 0
)
2021-06-14 19:24:07 +00:00
if (currentIdIndex == null && nextId != null) {
2021-07-01 20:11:33 +00:00
awaitLinks(
remoteMediaClient?.queueInsertAndPlayItem(
MediaQueueItem.Builder(mediaItem).build(),
2021-07-01 20:11:33 +00:00
nextId,
startAt,
JSONObject()
)
) {
2021-06-14 19:24:07 +00:00
loadMirror(index + 1)
}
} else {
awaitLinks(remoteMediaClient?.load(mediaItem, true, startAt)) {
loadMirror(index + 1)
}
}
} catch (e: Exception) {
awaitLinks(remoteMediaClient?.load(mediaItem, true, startAt)) {
loadMirror(index + 1)
}
2021-06-14 16:58:43 +00:00
}
2021-06-14 00:00:29 +00:00
}
2021-06-14 19:24:07 +00:00
loadMirror(which)
2021-06-14 00:00:29 +00:00
2021-12-12 02:33:17 +00:00
bottomSheetDialog.dismissSafe(activity)
2021-06-14 00:00:29 +00:00
}
2021-06-06 18:06:01 +00:00
}
}
}
}
2021-06-14 00:00:29 +00:00
private fun getCurrentMetaData(): MetadataHolder? {
return try {
2021-06-14 16:58:43 +00:00
val data = remoteMediaClient?.mediaInfo?.customData?.toString()
data?.toKotlinObject()
2021-06-14 00:00:29 +00:00
} catch (e: Exception) {
null
}
}
2021-06-14 16:58:43 +00:00
var isLoadingMore = false
2021-06-06 18:06:01 +00:00
override fun onMediaStatusUpdated() {
super.onMediaStatusUpdated()
2021-06-14 16:58:43 +00:00
val meta = getCurrentMetaData()
view.visibility = if ((meta?.currentLinks?.size
?: 0) > 1
) VISIBLE else INVISIBLE
try {
if (meta != null && meta.episodes.size > meta.currentEpisodeIndex + 1) {
2021-06-14 19:24:07 +00:00
val currentIdIndex = remoteMediaClient?.getItemIndex() ?: return
2021-06-14 16:58:43 +00:00
val itemCount = remoteMediaClient?.mediaQueue?.itemCount
2022-04-22 19:01:29 +00:00
val index = meta.currentEpisodeIndex + 1
val epData = meta.episodes[index]
try {
val currentDuration = remoteMediaClient?.streamDuration
val currentPosition = remoteMediaClient?.approximateStreamPosition
if (currentDuration != null && currentPosition != null)
DataStoreHelper.setViewPos(epData.id, currentPosition, currentDuration)
2022-08-25 01:59:20 +00:00
} catch (t : Throwable) {
logError(t)
2022-04-22 19:01:29 +00:00
}
2021-06-14 16:58:43 +00:00
if (itemCount != null && itemCount - currentIdIndex == 1 && !isLoadingMore) {
isLoadingMore = true
2022-01-16 22:31:42 +00:00
ioSafe {
2022-01-07 19:27:25 +00:00
val currentLinks = mutableSetOf<ExtractorLink>()
val currentSubs = mutableSetOf<SubtitleData>()
2021-06-14 16:58:43 +00:00
2022-01-07 19:27:25 +00:00
val generator = RepoLinkGenerator(listOf(epData))
2022-01-16 22:31:42 +00:00
val isSuccessful = safeApiCall {
2022-04-22 19:01:29 +00:00
generator.generateLinks(clearCache = false, isCasting = true,
callback = {
2022-01-07 19:27:25 +00:00
it.first?.let { link ->
currentLinks.add(link)
}
2022-04-22 19:01:29 +00:00
}, subtitleCallback = {
2022-01-07 19:27:25 +00:00
currentSubs.add(it)
})
}
2022-01-07 19:27:25 +00:00
val sortedLinks = sortUrls(currentLinks)
val sortedSubs = sortSubs(currentSubs)
2022-01-16 22:31:42 +00:00
if (isSuccessful == Resource.Success(true)) {
2022-01-07 19:27:25 +00:00
if (currentLinks.isNotEmpty()) {
2021-07-02 18:46:18 +00:00
val jsonCopy = meta.copy(
2022-02-12 20:20:56 +00:00
currentLinks = sortedLinks,
2022-01-07 19:27:25 +00:00
currentSubtitles = sortedSubs,
2021-07-02 18:46:18 +00:00
currentEpisodeIndex = index
)
2021-06-14 16:58:43 +00:00
val done =
2022-01-14 18:14:24 +00:00
JSONObject(jsonCopy.toJson())
2021-06-14 19:24:07 +00:00
val mediaInfo = getMediaInfo(
epData,
jsonCopy,
0,
2021-07-02 18:46:18 +00:00
done,
2022-01-07 19:27:25 +00:00
sortedSubs
2021-07-01 20:11:33 +00:00
)
2021-06-14 19:24:07 +00:00
/*fun loadIndex(index: Int) {
println("LOAD INDEX::::: $index")
if (meta.currentLinks.size <= index) return
val info = getMediaInfo(
epData,
2021-06-14 16:58:43 +00:00
meta,
2021-06-14 19:24:07 +00:00
index,
done)
awaitLinks(remoteMediaClient?.load(info, true, 0)) {
loadIndex(index + 1)
}
}*/
activity.runOnUiThread {
awaitLinks(
remoteMediaClient?.queueAppendItem(
MediaQueueItem.Builder(mediaInfo).build(),
JSONObject()
)
) {
println("FAILED TO LOAD NEXT ITEM")
// loadIndex(1)
}
isLoadingMore = false
}
2021-06-14 16:58:43 +00:00
}
2021-06-14 16:58:43 +00:00
}
}
}
}
} catch (e: Exception) {
println(e)
}
2021-06-06 18:06:01 +00:00
}
override fun onSessionConnected(castSession: CastSession?) {
2021-10-03 00:09:13 +00:00
castSession?.let {
super.onSessionConnected(it)
}
2021-06-14 16:58:43 +00:00
remoteMediaClient?.queueSetRepeatMode(REPEAT_MODE_REPEAT_OFF, JSONObject())
2021-06-06 18:06:01 +00:00
}
}
class SkipTimeController(val view: ImageView, forwards: Boolean) : UIController() {
init {
//val settingsManager = PreferenceManager.getDefaultSharedPreferences()
//val time = settingsManager?.getInt("chromecast_tap_time", 30) ?: 30
val time = 30
2021-06-10 18:21:42 +00:00
//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)
2021-06-06 18:06:01 +00:00
view.setOnClickListener {
remoteMediaClient?.let {
val options = MediaSeekOptions.Builder()
.setPosition(it.approximateStreamPosition + time * 1000 * if (forwards) 1 else -1)
it.seek(options.build())
}
2021-06-06 18:06:01 +00:00
}
}
}
class ControllerActivity : ExpandedControllerActivity() {
override fun onCreateOptionsMenu(menu: Menu): Boolean {
super.onCreateOptionsMenu(menu)
menuInflater.inflate(R.menu.cast_expanded_controller_menu, menu)
CastButtonFactory.setUpMediaRouteButton(this, menu, R.id.media_route_menu_item)
return true
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val sourcesButton: ImageView = getButtonImageViewAt(0)
val skipBackButton: ImageView = getButtonImageViewAt(1)
val skipForwardButton: ImageView = getButtonImageViewAt(2)
val skipOpButton: ImageView = getButtonImageViewAt(3)
2022-01-07 19:27:25 +00:00
uiMediaController.bindViewToUIController(
sourcesButton,
SelectSourceController(sourcesButton, this)
)
uiMediaController.bindViewToUIController(
skipBackButton,
SkipTimeController(skipBackButton, false)
)
uiMediaController.bindViewToUIController(
skipForwardButton,
SkipTimeController(skipForwardButton, true)
)
uiMediaController.bindViewToUIController(
skipOpButton,
SkipNextEpisodeController(skipOpButton)
)
2021-06-06 18:06:01 +00:00
}
}