forked from recloudstream/cloudstream
chromecast bug fix
This commit is contained in:
parent
50abf2c74c
commit
efeb42fd2e
3 changed files with 125 additions and 53 deletions
|
@ -2,8 +2,7 @@ package com.lagradost.cloudstream3.ui
|
|||
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.View.INVISIBLE
|
||||
import android.view.View.VISIBLE
|
||||
import android.view.View.*
|
||||
import android.widget.ImageView
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature
|
||||
|
@ -13,6 +12,7 @@ import com.google.android.gms.cast.MediaQueueItem
|
|||
import com.google.android.gms.cast.MediaStatus.REPEAT_MODE_REPEAT_OFF
|
||||
import com.google.android.gms.cast.framework.CastButtonFactory
|
||||
import com.google.android.gms.cast.framework.CastSession
|
||||
import com.google.android.gms.cast.framework.media.RemoteMediaClient
|
||||
import com.google.android.gms.cast.framework.media.uicontroller.UIController
|
||||
import com.google.android.gms.cast.framework.media.widget.ExpandedControllerActivity
|
||||
import com.lagradost.cloudstream3.APIHolder.getApiFromName
|
||||
|
@ -21,6 +21,7 @@ import com.lagradost.cloudstream3.mvvm.Resource
|
|||
import com.lagradost.cloudstream3.mvvm.safeApiCall
|
||||
import com.lagradost.cloudstream3.sortUrls
|
||||
import com.lagradost.cloudstream3.ui.result.ResultEpisode
|
||||
import com.lagradost.cloudstream3.utils.CastHelper.awaitLinks
|
||||
import com.lagradost.cloudstream3.utils.CastHelper.getMediaInfo
|
||||
import com.lagradost.cloudstream3.utils.Coroutines.main
|
||||
import com.lagradost.cloudstream3.utils.DataStore.toKotlinObject
|
||||
|
@ -38,6 +39,37 @@ class SkipOpController(val view: ImageView) : UIController() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun RemoteMediaClient.getItemIndex(): Int? {
|
||||
return try {
|
||||
val index = this.mediaQueue?.itemIds?.indexOf(this.currentItem?.itemId ?: 0)
|
||||
if (index == null || index < 0) null else index
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
class SkipNextEpisodeController(val view: ImageView) : UIController() {
|
||||
init {
|
||||
view.setImageResource(R.drawable.exo_controls_fastforward)
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class MetadataHolder(
|
||||
val apiName: String,
|
||||
val title: String?,
|
||||
|
@ -59,13 +91,13 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi
|
|||
|
||||
if (holder != null) {
|
||||
val items = holder.currentLinks
|
||||
if (items.isNotEmpty() && remoteMediaClient.currentItem != null) {
|
||||
if (items.isNotEmpty() && remoteMediaClient?.currentItem != null) {
|
||||
val builder = AlertDialog.Builder(view.context, R.style.AlertDialogCustom)
|
||||
builder.setTitle("Pick source")
|
||||
|
||||
//https://developers.google.com/cast/docs/reference/web_receiver/cast.framework.messages.MediaInformation
|
||||
val contentUrl = (remoteMediaClient.currentItem.media.contentUrl
|
||||
?: remoteMediaClient.currentItem.media.contentId)
|
||||
val contentUrl = (remoteMediaClient?.currentItem?.media?.contentUrl
|
||||
?: remoteMediaClient?.currentItem?.media?.contentId)
|
||||
|
||||
builder.setSingleChoiceItems(
|
||||
items.map { it.name }.toTypedArray(),
|
||||
|
@ -73,30 +105,44 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi
|
|||
) { _, which ->
|
||||
val epData = holder.episodes[holder.currentEpisodeIndex]
|
||||
|
||||
val mediaItem = getMediaInfo(epData,
|
||||
fun loadMirror(index: Int) {
|
||||
if (holder.currentLinks.size <= index) return
|
||||
|
||||
val mediaItem = getMediaInfo(
|
||||
epData,
|
||||
holder,
|
||||
holder.currentLinks[which],
|
||||
remoteMediaClient.mediaInfo.customData)
|
||||
index,
|
||||
remoteMediaClient?.mediaInfo?.customData)
|
||||
|
||||
val startAt = remoteMediaClient.approximateStreamPosition
|
||||
val startAt = remoteMediaClient?.approximateStreamPosition ?: 0
|
||||
|
||||
try {
|
||||
//remoteMediaClient.load(mediaItem, true, startAt)
|
||||
try { // THIS IS VERY IMPORTANT BECAUSE WE NEVER WANT TO AUTOLOAD THE NEXT EPISODE
|
||||
val currentIdIndex = remoteMediaClient?.getItemIndex()
|
||||
|
||||
val currentIdIndex = getItemIndex() ?: return@setSingleChoiceItems
|
||||
val nextId = remoteMediaClient.mediaQueue.itemIds?.get(currentIdIndex?.plus(1) ?: 0)
|
||||
|
||||
val nextId = remoteMediaClient.mediaQueue.itemIds?.get(currentIdIndex + 1)
|
||||
|
||||
if (nextId != null) {
|
||||
remoteMediaClient.queueInsertAndPlayItem(MediaQueueItem.Builder(mediaItem).build(),
|
||||
if (currentIdIndex == null && nextId != null) {
|
||||
awaitLinks(remoteMediaClient?.queueInsertAndPlayItem(MediaQueueItem.Builder(
|
||||
mediaItem)
|
||||
.build(),
|
||||
nextId,
|
||||
startAt,
|
||||
JSONObject())
|
||||
JSONObject())) {
|
||||
loadMirror(index + 1)
|
||||
}
|
||||
} else {
|
||||
remoteMediaClient.load(mediaItem, true, startAt)
|
||||
awaitLinks(remoteMediaClient?.load(mediaItem, true, startAt)) {
|
||||
loadMirror(index + 1)
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
remoteMediaClient.load(mediaItem, true, startAt)
|
||||
awaitLinks(remoteMediaClient?.load(mediaItem, true, startAt)) {
|
||||
loadMirror(index + 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
loadMirror(which)
|
||||
|
||||
dialog.dismiss()
|
||||
}
|
||||
|
@ -107,11 +153,6 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi
|
|||
}
|
||||
}
|
||||
|
||||
private fun getItemIndex(): Int? {
|
||||
val index = remoteMediaClient?.mediaQueue?.itemIds?.indexOf(remoteMediaClient.currentItem.itemId)
|
||||
return if (index == null || index < 0) null else index
|
||||
}
|
||||
|
||||
private fun getCurrentMetaData(): MetadataHolder? {
|
||||
return try {
|
||||
val data = remoteMediaClient?.mediaInfo?.customData?.toString()
|
||||
|
@ -133,7 +174,7 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi
|
|||
try {
|
||||
if (meta != null && meta.episodes.size > meta.currentEpisodeIndex + 1) {
|
||||
|
||||
val currentIdIndex = getItemIndex() ?: return
|
||||
val currentIdIndex = remoteMediaClient?.getItemIndex() ?: return
|
||||
val itemCount = remoteMediaClient?.mediaQueue?.itemCount
|
||||
|
||||
if (itemCount != null && itemCount - currentIdIndex == 1 && !isLoadingMore) {
|
||||
|
@ -152,19 +193,41 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi
|
|||
links.add(it)
|
||||
}
|
||||
}
|
||||
|
||||
if (res is Resource.Success) {
|
||||
val sorted = sortUrls(links)
|
||||
if (sorted.isNotEmpty()) {
|
||||
val jsonCopy = meta.copy(currentLinks = sorted, currentEpisodeIndex = index)
|
||||
|
||||
val done = withContext(Dispatchers.IO) {
|
||||
getMediaInfo(epData,
|
||||
meta,
|
||||
sorted.first(),
|
||||
JSONObject(mapper.writeValueAsString(jsonCopy)))
|
||||
JSONObject(mapper.writeValueAsString(jsonCopy))
|
||||
}
|
||||
|
||||
val mediaInfo = getMediaInfo(
|
||||
epData,
|
||||
jsonCopy,
|
||||
0,
|
||||
done)
|
||||
|
||||
/*fun loadIndex(index: Int) {
|
||||
println("LOAD INDEX::::: $index")
|
||||
if (meta.currentLinks.size <= index) return
|
||||
val info = getMediaInfo(
|
||||
epData,
|
||||
meta,
|
||||
index,
|
||||
done)
|
||||
awaitLinks(remoteMediaClient?.load(info, true, 0)) {
|
||||
loadIndex(index + 1)
|
||||
}
|
||||
}*/
|
||||
|
||||
awaitLinks(remoteMediaClient?.queueAppendItem(MediaQueueItem.Builder(mediaInfo).build(),
|
||||
JSONObject())) {
|
||||
println("FAILED TO LOAD NEXT ITEM")
|
||||
// loadIndex(1)
|
||||
}
|
||||
|
||||
remoteMediaClient?.queueAppendItem(MediaQueueItem.Builder(done).build(), JSONObject())
|
||||
isLoadingMore = false
|
||||
}
|
||||
}
|
||||
|
@ -212,6 +275,6 @@ class ControllerActivity : ExpandedControllerActivity() {
|
|||
uiMediaController.bindViewToUIController(sourcesButton, SelectSourceController(sourcesButton, this))
|
||||
uiMediaController.bindViewToUIController(skipBackButton, SkipTimeController(skipBackButton, false))
|
||||
uiMediaController.bindViewToUIController(skipForwardButton, SkipTimeController(skipForwardButton, true))
|
||||
uiMediaController.bindViewToUIController(skipOpButton, SkipOpController(skipOpButton))
|
||||
uiMediaController.bindViewToUIController(skipOpButton, SkipNextEpisodeController(skipOpButton))
|
||||
}
|
||||
}
|
|
@ -517,10 +517,10 @@ class PlayerFragment : Fragment() {
|
|||
val alphaAnimation = AlphaAnimation(0f, 1f)
|
||||
alphaAnimation.duration = 100
|
||||
alphaAnimation.fillAfter = true
|
||||
video_go_back_holder.visibility = VISIBLE
|
||||
video_go_back_holder?.visibility = VISIBLE
|
||||
|
||||
overlay_loading_skip_button.visibility = VISIBLE
|
||||
loading_overlay.startAnimation(alphaAnimation)
|
||||
overlay_loading_skip_button?.visibility = VISIBLE
|
||||
loading_overlay?.startAnimation(alphaAnimation)
|
||||
if (this::exoPlayer.isInitialized) {
|
||||
isPlayerPlaying = exoPlayer.playWhenReady
|
||||
playbackPosition = exoPlayer.currentPosition
|
||||
|
@ -555,11 +555,10 @@ class PlayerFragment : Fragment() {
|
|||
|
||||
private fun savePos() {
|
||||
if (this::exoPlayer.isInitialized) {
|
||||
/*if (
|
||||
&& exoPlayer.duration > 0 && exoPlayer.currentPosition > 0
|
||||
) {
|
||||
setViewPosDur(data!!, exoPlayer.currentPosition, exoPlayer.duration)
|
||||
}*/
|
||||
if (exoPlayer.duration > 0 && exoPlayer.currentPosition > 0) {
|
||||
//TODO FIX SAVE POS
|
||||
// setViewPosDur(data!!, exoPlayer.currentPosition, exoPlayer.duration)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1175,11 +1174,10 @@ class PlayerFragment : Fragment() {
|
|||
}
|
||||
}
|
||||
|
||||
//TODO FIX NON PIP MODE BUG
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
isInPlayer = false
|
||||
// releasePlayer()
|
||||
releasePlayer()
|
||||
|
||||
activity?.showSystemUI()
|
||||
activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_USER
|
||||
|
@ -1247,6 +1245,7 @@ class PlayerFragment : Fragment() {
|
|||
hasUsedFirstRender = false
|
||||
|
||||
try {
|
||||
if (!isInPlayer) return
|
||||
if (this::exoPlayer.isInitialized) {
|
||||
savePos()
|
||||
exoPlayer.release()
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package com.lagradost.cloudstream3.utils
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature
|
||||
|
@ -19,7 +20,9 @@ import com.google.android.gms.common.images.WebImage
|
|||
import com.lagradost.cloudstream3.ui.MetadataHolder
|
||||
import com.lagradost.cloudstream3.ui.result.ResultEpisode
|
||||
import com.lagradost.cloudstream3.utils.Coroutines.main
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.awaitAll
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.json.JSONObject
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
|
@ -27,7 +30,8 @@ object CastHelper {
|
|||
private val mapper: JsonMapper = JsonMapper.builder().addModule(KotlinModule())
|
||||
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false).build()
|
||||
|
||||
fun getMediaInfo(epData: ResultEpisode, holder: MetadataHolder, link: ExtractorLink, data: JSONObject?): MediaInfo {
|
||||
fun getMediaInfo(epData: ResultEpisode, holder: MetadataHolder, index: Int, data: JSONObject?): MediaInfo {
|
||||
val link = holder.currentLinks[index]
|
||||
val movieMetadata = MediaMetadata(MediaMetadata.MEDIA_TYPE_MOVIE)
|
||||
movieMetadata.putString(MediaMetadata.KEY_SUBTITLE,
|
||||
(epData.name ?: "Episode ${epData.episode}") + " - ${link.name}")
|
||||
|
@ -47,20 +51,22 @@ object CastHelper {
|
|||
.build()
|
||||
}
|
||||
|
||||
fun awaitLinks(pending: PendingResult<RemoteMediaClient.MediaChannelResult>?) {
|
||||
fun awaitLinks(pending: PendingResult<RemoteMediaClient.MediaChannelResult>?, callback: (Boolean) -> Unit) {
|
||||
if (pending == null) return
|
||||
thread {
|
||||
val res = pending.await()
|
||||
main {
|
||||
val res = withContext(Dispatchers.IO) { pending.await() }
|
||||
when (res.status?.statusCode) {
|
||||
CastStatusCodes.FAILED -> {
|
||||
println("FAILED WITH DATA: " + res.customData)
|
||||
callback.invoke(true)
|
||||
println("FAILED AND LOAD NEXT")
|
||||
}
|
||||
else -> {
|
||||
println("FAILED::: " + res.status)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Context.startCast(
|
||||
apiName: String,
|
||||
|
@ -80,16 +86,20 @@ object CastHelper {
|
|||
|
||||
val holder = MetadataHolder(apiName, title, poster, currentEpisodeIndex, episodes, currentLinks)
|
||||
|
||||
val index = startIndex ?: 0
|
||||
val mediaItem =
|
||||
getMediaInfo(epData, holder, currentLinks[startIndex ?: 0], JSONObject(mapper.writeValueAsString(holder)))
|
||||
getMediaInfo(epData, holder, index, JSONObject(mapper.writeValueAsString(holder)))
|
||||
|
||||
val castPlayer = CastPlayer(castContext)
|
||||
|
||||
castPlayer.repeatMode = REPEAT_MODE_REPEAT_OFF
|
||||
castPlayer.stop()
|
||||
|
||||
awaitLinks(castPlayer.loadItem(
|
||||
MediaQueueItem.Builder(mediaItem).build(),
|
||||
startTime ?: 0,
|
||||
))
|
||||
)) {
|
||||
if (currentLinks.size > index + 1)
|
||||
startCast(apiName, title, poster, currentEpisodeIndex, episodes, currentLinks, index + 1, startTime)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue