This commit is contained in:
LagradOst 2021-06-15 18:07:20 +02:00
parent efeb42fd2e
commit 1e01abf929
10 changed files with 140 additions and 101 deletions

View file

@ -50,7 +50,7 @@ private fun RemoteMediaClient.getItemIndex(): Int? {
class SkipNextEpisodeController(val view: ImageView) : UIController() {
init {
view.setImageResource(R.drawable.exo_controls_fastforward)
view.setImageResource(R.drawable.ic_baseline_skip_next_24)
view.setOnClickListener {
remoteMediaClient?.let {
it.queueNext(JSONObject())

View file

@ -81,6 +81,8 @@ import com.lagradost.cloudstream3.ui.result.ResultViewModel
import com.lagradost.cloudstream3.utils.CastHelper.startCast
import com.lagradost.cloudstream3.utils.DataStore.getKey
import com.lagradost.cloudstream3.utils.DataStore.setKey
import com.lagradost.cloudstream3.utils.DataStoreHelper
import com.lagradost.cloudstream3.utils.DataStoreHelper.saveViewPos
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.getId
import kotlinx.android.synthetic.main.fragment_player.*
@ -544,11 +546,15 @@ class PlayerFragment : Fragment() {
private lateinit var volumeObserver: SettingsContentObserver
companion object {
fun newInstance(data: PlayerData) =
fun newInstance(data: PlayerData, startPos: Long? = null) =
PlayerFragment().apply {
arguments = Bundle().apply {
//println(data)
putString("data", mapper.writeValueAsString(data))
println("PUT START: " + startPos)
if (startPos != null) {
putLong(STATE_RESUME_POSITION, startPos)
}
}
}
}
@ -556,8 +562,7 @@ class PlayerFragment : Fragment() {
private fun savePos() {
if (this::exoPlayer.isInitialized) {
if (exoPlayer.duration > 0 && exoPlayer.currentPosition > 0) {
//TODO FIX SAVE POS
// setViewPosDur(data!!, exoPlayer.currentPosition, exoPlayer.duration)
context?.saveViewPos(getEpisode()?.id, exoPlayer.currentPosition, exoPlayer.duration)
}
}
}
@ -610,7 +615,7 @@ class PlayerFragment : Fragment() {
private var resizeMode = 0
private var playbackSpeed = 0f
private var allEpisodes: HashMap<Int, ArrayList<ExtractorLink>> = HashMap()
private var episodes: ArrayList<ResultEpisode> = ArrayList()
private var episodes: List<ResultEpisode> = ArrayList()
var currentPoster: String? = null
var currentHeaderName: String? = null
@ -795,6 +800,14 @@ class PlayerFragment : Fragment() {
}
}
arguments?.getString("data")?.let {
playerData = mapper.readValue(it, PlayerData::class.java)
}
arguments?.getLong(STATE_RESUME_POSITION)?.let {
playbackPosition = it
}
if (savedInstanceState != null) {
currentWindow = savedInstanceState.getInt(STATE_RESUME_WINDOW)
playbackPosition = savedInstanceState.getLong(STATE_RESUME_POSITION)
@ -819,9 +832,7 @@ class PlayerFragment : Fragment() {
)
viewModel = ViewModelProvider(requireActivity()).get(ResultViewModel::class.java)
arguments?.getString("data")?.let {
playerData = mapper.readValue(it, PlayerData::class.java)
}
observeDirectly(viewModel.episodes) { _episodes ->
episodes = _episodes
@ -1175,6 +1186,8 @@ class PlayerFragment : Fragment() {
}
override fun onDestroy() {
savePos()
super.onDestroy()
isInPlayer = false
releasePlayer()
@ -1184,6 +1197,7 @@ class PlayerFragment : Fragment() {
}
override fun onPause() {
savePos()
super.onPause()
if (Util.SDK_INT <= 23) {
if (player_view != null) player_view.onPause()
@ -1192,6 +1206,7 @@ class PlayerFragment : Fragment() {
}
override fun onStop() {
savePos()
super.onStop()
if (Util.SDK_INT > 23) {
if (player_view != null) player_view.onPause()
@ -1200,6 +1215,8 @@ class PlayerFragment : Fragment() {
}
override fun onSaveInstanceState(outState: Bundle) {
savePos()
if (this::exoPlayer.isInitialized) {
outState.putInt(STATE_RESUME_WINDOW, exoPlayer.currentWindowIndex)
outState.putLong(STATE_RESUME_POSITION, exoPlayer.currentPosition)
@ -1209,7 +1226,6 @@ class PlayerFragment : Fragment() {
outState.putInt(RESIZE_MODE_KEY, resizeMode)
outState.putFloat(PLAYBACK_SPEED, playbackSpeed)
outState.putString("data", mapper.writeValueAsString(playerData))
savePos()
super.onSaveInstanceState(outState)
}
@ -1305,6 +1321,7 @@ class PlayerFragment : Fragment() {
.setTrackSelector(trackSelector)
_exoPlayer.setMediaSourceFactory(DefaultMediaSourceFactory(CustomFactory()))
println("START POS: " + playbackPosition)
exoPlayer = _exoPlayer.build().apply {
playWhenReady = isPlayerPlaying
seekTo(currentWindow, playbackPosition)

View file

@ -22,7 +22,7 @@ data class EpisodeClickEvent(val action: Int, val data: ResultEpisode)
class EpisodeAdapter(
private var activity: Activity,
var cardList: ArrayList<ResultEpisode>,
var cardList: List<ResultEpisode>,
private val resView: RecyclerView,
private val clickCallback: (EpisodeClickEvent) -> Unit,
) :

View file

@ -1,6 +1,7 @@
package com.lagradost.cloudstream3.ui.result
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Bundle
@ -20,18 +21,9 @@ import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.bumptech.glide.load.model.GlideUrl
import com.bumptech.glide.request.RequestOptions.bitmapTransform
import com.fasterxml.jackson.annotation.JsonProperty
import com.google.android.exoplayer2.ext.cast.CastPlayer
import com.google.android.exoplayer2.util.MimeTypes
import com.google.android.gms.cast.MediaInfo
import com.google.android.gms.cast.MediaMetadata
import com.google.android.gms.cast.MediaQueueItem
import com.google.android.gms.cast.MediaStatus.REPEAT_MODE_REPEAT_OFF
import com.google.android.gms.cast.MediaStatus.REPEAT_MODE_REPEAT_SINGLE
import com.google.android.gms.cast.framework.CastButtonFactory
import com.google.android.gms.cast.framework.CastContext
import com.google.android.gms.cast.framework.CastState
import com.google.android.gms.common.images.WebImage
import com.google.android.material.button.MaterialButton
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.UIHelper.fixPaddingStatusbar
@ -41,11 +33,10 @@ import com.lagradost.cloudstream3.mvvm.observe
import com.lagradost.cloudstream3.ui.player.PlayerData
import com.lagradost.cloudstream3.ui.player.PlayerFragment
import com.lagradost.cloudstream3.utils.CastHelper.startCast
import com.lagradost.cloudstream3.utils.DataStoreHelper.getViewPos
import com.lagradost.cloudstream3.utils.ExtractorLink
import jp.wasabeef.glide.transformations.BlurTransformation
import kotlinx.android.synthetic.main.fragment_result.*
import org.json.JSONArray
import org.json.JSONObject
const val MAX_SYNO_LENGH = 300
@ -59,12 +50,42 @@ data class ResultEpisode(
val apiName: String,
val id: Int,
val index: Int,
val progress: Long, // time in MS
val position: Long, // time in MS
val duration: Long, // duration in MS
)
fun ResultEpisode.getRealPosition(): Long {
if (duration <= 0) return 0
val percentage = position * 100 / duration
if (percentage <= 5 || percentage >= 95) return 0
return position
}
fun Context.buildResultEpisode(
name: String?,
poster: String?,
episode: Int,
season: Int?,
data: String,
apiName: String,
id: Int,
index: Int,
): ResultEpisode {
val posDur = getViewPos(id)
return ResultEpisode(name,
poster,
episode,
season,
data,
apiName,
id,
index,
posDur?.position ?: 0,
posDur?.duration ?: 0)
}
fun ResultEpisode.getWatchProgress(): Float {
return progress.toFloat() / duration
return position.toFloat() / duration
}
class ResultFragment : Fragment() {
@ -79,11 +100,11 @@ class ResultFragment : Fragment() {
}
}
private var currentLoadingCount = 0 // THIS IS USED TO PREVENT LATE EVENTS, AFTER DISMISS WAS CLICKED
private lateinit var viewModel: ResultViewModel
private var allEpisodes: HashMap<Int, ArrayList<ExtractorLink>> = HashMap()
var currentHeaderName: String? = null
var currentEpisodes: ArrayList<ResultEpisode>? = null
private var currentHeaderName: String? = null
private var currentEpisodes: List<ResultEpisode>? = null
override fun onCreateView(
inflater: LayoutInflater,
@ -100,7 +121,7 @@ class ResultFragment : Fragment() {
super.onDestroy()
}
var currentPoster: String? = null
private var currentPoster: String? = null
@SuppressLint("SetTextI18n")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
@ -149,17 +170,24 @@ class ResultFragment : Fragment() {
//val id = episodeClick.data.id
val index = episodeClick.data.index
val buildInPlayer = true
currentLoadingCount++
when (episodeClick.action) {
ACTION_CHROME_CAST_EPISODE -> {
val builder = AlertDialog.Builder(requireContext(), R.style.AlertDialogCustom)
val customLayout = layoutInflater.inflate(R.layout.dialog_loading, null);
val currentLoad = currentLoadingCount
val builder = AlertDialog.Builder(requireContext(), R.style.AlertDialogCustomTransparent)
val customLayout = layoutInflater.inflate(R.layout.dialog_loading, null)
builder.setView(customLayout)
val dialog = builder.create()
dialog.show()
Toast.makeText(activity, "Loading links", Toast.LENGTH_SHORT).show()
dialog.setOnDismissListener {
currentLoadingCount++
}
// Toast.makeText(activity, "Loading links", Toast.LENGTH_SHORT).show()
viewModel.loadEpisode(episodeClick.data, true) { data ->
if(currentLoadingCount != currentLoad) return@loadEpisode
dialog.dismiss()
when (data) {
is Resource.Failure -> {
@ -173,52 +201,9 @@ class ResultFragment : Fragment() {
currentPoster,
episodeClick.data.index,
eps,
sortUrls(data.value))
/*
val epData = episodeClick.data
val links = sortUrls(data.value)
val castContext = CastContext.getSharedInstance(requireContext())
val customData =
links.map { JSONObject().put("name", it.name) }
val jsonArray = JSONArray()
for (item in customData) {
jsonArray.put(item)
}
val mediaItems = links.map {
val movieMetadata = MediaMetadata(MediaMetadata.MEDIA_TYPE_MOVIE)
movieMetadata.putString(MediaMetadata.KEY_SUBTITLE,
epData.name ?: "Episode ${epData.episode}")
if (currentHeaderName != null)
movieMetadata.putString(MediaMetadata.KEY_TITLE, currentHeaderName)
val srcPoster = epData.poster ?: currentPoster
if (srcPoster != null) {
movieMetadata.addImage(WebImage(Uri.parse(srcPoster)))
}
MediaQueueItem.Builder(
MediaInfo.Builder(it.url)
.setStreamType(MediaInfo.STREAM_TYPE_BUFFERED)
.setContentType(MimeTypes.VIDEO_UNKNOWN)
.setCustomData(JSONObject().put("data", jsonArray))
.setMetadata(movieMetadata)
.build()
)
.build()
}.toTypedArray()
val castPlayer = CastPlayer(castContext)
castPlayer.loadItems(
mediaItems,
0,
epData.progress,
REPEAT_MODE_REPEAT_SINGLE
//REPEAT_MODE_REPEAT_SINGLE
)*/
sortUrls(data.value),
startTime = episodeClick.data.getRealPosition()
)
}
}
}
@ -231,7 +216,10 @@ class ResultFragment : Fragment() {
R.anim.exit_anim,
R.anim.pop_enter,
R.anim.pop_exit)
.add(R.id.homeRoot, PlayerFragment.newInstance(PlayerData(index, null, 0)))
.add(R.id.homeRoot,
PlayerFragment.newInstance(PlayerData(index, null, 0),
episodeClick.data.getRealPosition())
)
.commit()
}
}
@ -288,10 +276,10 @@ class ResultFragment : Fragment() {
}
if (d.year != null) {
result_year.visibility = View.VISIBLE
result_year.visibility = VISIBLE
result_year.text = d.year.toString()
} else {
result_year.visibility = View.GONE
result_year.visibility = GONE
}
if (d.posterUrl != null) {
@ -383,12 +371,12 @@ activity?.startActivityForResult(vlcIntent, REQUEST_CODE)
}
result_tag.removeAllViews()
result_tag_holder.visibility = View.GONE
result_status.visibility = View.GONE
result_tag_holder.visibility = GONE
result_status.visibility = GONE
when (d) {
is AnimeLoadResponse -> {
result_status.visibility = View.VISIBLE
result_status.visibility = VISIBLE
result_status.text = when (d.showStatus) {
null -> ""
ShowStatus.Ongoing -> "Ongoing"
@ -402,9 +390,9 @@ activity?.startActivityForResult(vlcIntent, REQUEST_CODE)
result_toolbar.title = titleName
if (d.tags == null) {
result_tag_holder.visibility = View.GONE
result_tag_holder.visibility = GONE
} else {
result_tag_holder.visibility = View.VISIBLE
result_tag_holder.visibility = VISIBLE
for ((index, tag) in d.tags.withIndex()) {
val viewBtt = layoutInflater.inflate(R.layout.result_tag, null)
@ -426,6 +414,6 @@ activity?.startActivityForResult(vlcIntent, REQUEST_CODE)
}
if (viewModel.resultResponse.value == null && apiName != null && slug != null)
viewModel.load(slug, apiName)
viewModel.load(requireContext(), slug, apiName)
}
}

View file

@ -1,5 +1,6 @@
package com.lagradost.cloudstream3.ui.result
import android.content.Context
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
@ -8,17 +9,27 @@ import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.APIHolder.getApiFromName
import com.lagradost.cloudstream3.mvvm.Resource
import com.lagradost.cloudstream3.mvvm.safeApiCall
import com.lagradost.cloudstream3.utils.DataStoreHelper.getViewPos
import com.lagradost.cloudstream3.utils.ExtractorLink
import kotlinx.coroutines.launch
class ResultViewModel : ViewModel() {
private val _resultResponse: MutableLiveData<Resource<Any?>> = MutableLiveData()
private val _episodes: MutableLiveData<ArrayList<ResultEpisode>> = MutableLiveData()
private val _episodes: MutableLiveData<List<ResultEpisode>> = MutableLiveData()
val resultResponse: LiveData<Resource<Any?>> get() = _resultResponse
val episodes: LiveData<ArrayList<ResultEpisode>> get() = _episodes
val episodes: LiveData<List<ResultEpisode>> get() = _episodes
private val dubStatus: MutableLiveData<DubStatus> = MutableLiveData()
fun load(url: String, apiName: String) = viewModelScope.launch {
fun reloadEpisodes(context: Context) {
val current = _episodes.value ?: return
val copy = current.map {
val posDur = context.getViewPos(it.id)
it.copy(position = posDur?.position ?: 0, duration = posDur?.duration ?: 0)
}
_episodes.postValue(copy)
}
fun load(context: Context, url: String, apiName: String) = viewModelScope.launch {
_apiName.postValue(apiName)
val data = safeApiCall {
getApiFromName(apiName).load(url)
@ -39,7 +50,7 @@ class ResultViewModel : ViewModel() {
if (dataList != null) {
val episodes = ArrayList<ResultEpisode>()
for ((index, i) in dataList.withIndex()) {
episodes.add(ResultEpisode(
episodes.add(context.buildResultEpisode(
null, // TODO ADD NAMES
null,
index + 1, //TODO MAKE ABLE TO NOT HAVE SOME EPISODE
@ -48,8 +59,6 @@ class ResultViewModel : ViewModel() {
apiName,
(d.url + index).hashCode(),
index,
0,//(index * 0.1f),//TODO TEST; REMOVE
0,
))
}
_episodes.postValue(episodes)
@ -59,7 +68,7 @@ class ResultViewModel : ViewModel() {
is TvSeriesLoadResponse -> {
val episodes = ArrayList<ResultEpisode>()
for ((index, i) in d.episodes.withIndex()) {
episodes.add(ResultEpisode(
episodes.add(context.buildResultEpisode(
null, // TODO ADD NAMES
null,
index + 1, //TODO MAKE ABLE TO NOT HAVE SOME EPISODE
@ -68,21 +77,19 @@ class ResultViewModel : ViewModel() {
apiName,
(d.url + index).hashCode(),
index,
0,//(index * 0.1f),//TODO TEST; REMOVE
0,
))
}
_episodes.postValue(episodes)
}
is MovieLoadResponse -> {
_episodes.postValue(arrayListOf(ResultEpisode(null,
_episodes.postValue(arrayListOf(context.buildResultEpisode(
null,
null,
0, null,
d.movieUrl,
d.apiName,
(d.url).hashCode(),
0,
0, 0,
)))
}
}
@ -141,5 +148,4 @@ class ResultViewModel : ViewModel() {
fun loadIndex(index: Int): ResultEpisode? {
return episodes.value?.get(index)
}
}

View file

@ -1,6 +1,5 @@
package com.lagradost.cloudstream3.utils
import android.app.Activity
import android.content.Context
import android.content.SharedPreferences
import com.fasterxml.jackson.databind.DeserializationFeature

View file

@ -0,0 +1,22 @@
package com.lagradost.cloudstream3.utils
import android.content.Context
import com.lagradost.cloudstream3.utils.DataStore.getKey
import com.lagradost.cloudstream3.utils.DataStore.setKey
const val VIDEO_POS_DUR = "video_pos_dur"
data class PosDur(val position: Long, val duration: Long)
object DataStoreHelper {
var currentAccount: String = "0" //TODO ACCOUNT IMPLEMENTATION
fun Context.saveViewPos(id: Int?, pos: Long, dur: Long) {
if(id == null) return
setKey("$currentAccount/$VIDEO_POS_DUR", id.toString(), PosDur(pos, dur))
}
fun Context.getViewPos(id: Int): PosDur? {
return getKey<PosDur>("$currentAccount/$VIDEO_POS_DUR", id.toString(), null)
}
}

View file

@ -1,15 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:orientation="vertical" xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="match_parent" >
<!--
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content"
android:text="Loading..." android:textColor="?attr/textColor" android:textSize="20sp"
android:textStyle="bold" android:layout_margin="10dp"/>-->
<!-- style="@android:style/Widget.Material.ProgressBar.Horizontal"
-->
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content"
android:text="@string/loading_chromecast" android:layout_gravity="center" android:textColor="?attr/textColor" android:textSize="20sp"
android:textStyle="bold" android:layout_margin="10dp" />
<ProgressBar
android:layout_margin="10dp"
android:layout_margin="20dp"
android:layout_gravity="center"
android:layout_width="60dp"

View file

@ -22,4 +22,5 @@
<string name="result_share">Share</string>
<string name="result_open_in_browser">Open In Browser</string>
<string name="skip_loading">Skip Loading</string>
<string name="loading_chromecast">Loading...</string>
</resources>

View file

@ -114,7 +114,9 @@
<item name="android:windowMinWidthMinor">@android:dimen/dialog_min_width_minor</item>
<item name="android:windowBackground">@drawable/dialog__window_background</item>
</style>
<style name="AlertDialogCustomTransparent" parent="Theme.AppCompat.Dialog.Alert">
<item name="android:windowBackground">@color/transparent</item>
</style>
<!-- CHROMECAST -->