From 524d0717e2b39c706e2ec82b5a988798996c7192 Mon Sep 17 00:00:00 2001 From: LagradOst <11805592+LagradOst@users.noreply.github.com> Date: Sat, 16 Jul 2022 03:43:10 +0200 Subject: [PATCH] play any stream & a bit of work on invideoplayer episodes & no links found last error --- .../cloudstream3/extractors/GMPlayer.kt | 2 +- .../ui/download/DownloadFragment.kt | 52 +++++- .../ui/player/AbstractPlayerFragment.kt | 48 ++++-- .../ui/player/DownloadFileGenerator.kt | 4 + .../ui/player/FullScreenPlayer.kt | 28 +++- .../cloudstream3/ui/player/GeneratorPlayer.kt | 12 +- .../cloudstream3/ui/player/IGenerator.kt | 1 + .../cloudstream3/ui/player/LinkGenerator.kt | 68 ++++++++ .../ui/player/PlayerEpisodeAdapter.kt | 158 ++++++++++++++++++ .../ui/player/PlayerGeneratorViewModel.kt | 4 + .../ui/player/RepoLinkGenerator.kt | 7 +- .../cloudstream3/ui/result/EpisodeAdapter.kt | 10 +- .../cloudstream3/ui/result/ResultFragment.kt | 14 +- .../ic_baseline_arrow_back_ios_24.xml | 5 + .../main/res/layout/fragment_downloads.xml | 1 - .../main/res/layout/player_custom_layout.xml | 47 +++++- app/src/main/res/layout/player_episodes.xml | 8 + .../main/res/layout/player_episodes_large.xml | 106 ++++++++++++ .../main/res/layout/player_episodes_small.xml | 56 +++++++ app/src/main/res/layout/stream_input.xml | 48 +++++- app/src/main/res/values/strings.xml | 3 + 21 files changed, 637 insertions(+), 45 deletions(-) create mode 100644 app/src/main/java/com/lagradost/cloudstream3/ui/player/LinkGenerator.kt create mode 100644 app/src/main/java/com/lagradost/cloudstream3/ui/player/PlayerEpisodeAdapter.kt create mode 100644 app/src/main/res/drawable/ic_baseline_arrow_back_ios_24.xml create mode 100644 app/src/main/res/layout/player_episodes.xml create mode 100644 app/src/main/res/layout/player_episodes_large.xml create mode 100644 app/src/main/res/layout/player_episodes_small.xml diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/GMPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/GMPlayer.kt index 4bbff386..e36a03d3 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/GMPlayer.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/GMPlayer.kt @@ -23,7 +23,7 @@ class GMPlayer : ExtractorApi() { "origin" to mainUrl ), data = mapOf("hash" to id, "r" to ref) - ).also { println("shiv " + it.text) }.parsed().videoSource ?: return null + ).parsed().videoSource ?: return null return M3u8Helper.generateM3u8( name, diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadFragment.kt index d6996b92..25126a9b 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadFragment.kt @@ -1,30 +1,42 @@ package com.lagradost.cloudstream3.ui.download +import android.app.Dialog +import android.content.ClipboardManager +import android.content.Context +import android.os.Build import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.LinearLayout +import android.widget.Toast import androidx.appcompat.app.AppCompatActivity +import androidx.core.view.isGone import androidx.core.view.isVisible import androidx.fragment.app.Fragment import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.RecyclerView +import com.lagradost.cloudstream3.CommonActivity.showToast import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.isMovieType import com.lagradost.cloudstream3.mvvm.observe import com.lagradost.cloudstream3.ui.download.DownloadButtonSetup.handleDownloadClick +import com.lagradost.cloudstream3.ui.player.GeneratorPlayer +import com.lagradost.cloudstream3.ui.player.LinkGenerator +import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings import com.lagradost.cloudstream3.utils.AppUtils.loadResult import com.lagradost.cloudstream3.utils.Coroutines.main import com.lagradost.cloudstream3.utils.DOWNLOAD_EPISODE_CACHE import com.lagradost.cloudstream3.utils.DataStore +import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard import com.lagradost.cloudstream3.utils.UIHelper.navigate import com.lagradost.cloudstream3.utils.VideoDownloadHelper import com.lagradost.cloudstream3.utils.VideoDownloadManager import kotlinx.android.synthetic.main.fragment_downloads.* +import kotlinx.android.synthetic.main.stream_input.* const val DOWNLOAD_NAVIGATE_TO = "downloadpage" @@ -168,9 +180,45 @@ class DownloadFragment : Fragment() { download_list?.adapter = adapter download_list?.layoutManager = GridLayoutManager(context, 1) - /*download_stream_button?.isGone = context?.isTvSettings() == true + download_stream_button?.isGone = context?.isTvSettings() == true download_stream_button?.setOnClickListener { + val dialog = + Dialog(it.context ?: return@setOnClickListener, R.style.AlertDialogCustom) + dialog.setContentView(R.layout.stream_input) + dialog.show() + + (activity?.getSystemService(Context.CLIPBOARD_SERVICE) as? ClipboardManager?)?.primaryClip?.getItemAt( + 0 + )?.text?.toString()?.let { copy -> + dialog.stream_url?.setText(copy) + } + + dialog.apply_btt?.setOnClickListener { + val url = dialog.stream_url.text?.toString() + if (url.isNullOrEmpty()) { + showToast(activity, R.string.error_invalid_url, Toast.LENGTH_SHORT) + } else { + val referer = dialog.stream_referer.text?.toString() + + activity?.navigate( + R.id.global_to_navigation_player, + GeneratorPlayer.newInstance( + LinkGenerator( + listOf(url), + extract = true, + referer = referer + ) + ) + ) + + dialog.dismissSafe(activity) + } + } + + dialog.cancel_btt?.setOnClickListener { + dialog.dismissSafe(activity) + } } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { download_list?.setOnScrollChangeListener { _, _, scrollY, _, oldScrollY -> @@ -181,7 +229,7 @@ class DownloadFragment : Fragment() { download_stream_button?.extend() // show } } - }*/ + } downloadsViewModel.updateList(requireContext()) context?.fixPaddingStatusbar(download_root) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/AbstractPlayerFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/AbstractPlayerFragment.kt index 072bd501..ddb3e7ab 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/AbstractPlayerFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/AbstractPlayerFragment.kt @@ -40,6 +40,7 @@ import com.lagradost.cloudstream3.utils.AppUtils import com.lagradost.cloudstream3.utils.AppUtils.requestLocalAudioFocus import com.lagradost.cloudstream3.utils.UIHelper import com.lagradost.cloudstream3.utils.UIHelper.hideSystemUI +import com.lagradost.cloudstream3.utils.UIHelper.popCurrentPage import kotlinx.android.synthetic.main.fragment_player.* import kotlinx.android.synthetic.main.player_custom_layout.* @@ -211,6 +212,10 @@ abstract class AbstractPlayerFragment( } } + open fun hasNextMirror(): Boolean { + throw NotImplementedError() + } + open fun nextMirror() { throw NotImplementedError() } @@ -222,6 +227,23 @@ abstract class AbstractPlayerFragment( } open fun playerError(exception: Exception) { + fun showToast(message: String, gotoNext: Boolean = false) { + if (!gotoNext || hasNextMirror()) { + showToast( + activity, + message, + Toast.LENGTH_SHORT + ) + } else { + showToast( + activity, + context?.getString(R.string.no_links_found_toast) + "\n" + message, + Toast.LENGTH_LONG + ) + activity?.popCurrentPage() + } + } + val ctx = context ?: return when (exception) { is PlaybackException -> { @@ -230,47 +252,43 @@ abstract class AbstractPlayerFragment( when (val code = exception.errorCode) { PlaybackException.ERROR_CODE_IO_FILE_NOT_FOUND, PlaybackException.ERROR_CODE_PARSING_CONTAINER_UNSUPPORTED, PlaybackException.ERROR_CODE_IO_NO_PERMISSION, PlaybackException.ERROR_CODE_IO_UNSPECIFIED -> { showToast( - activity, "${ctx.getString(R.string.source_error)}\n$errorName ($code)\n$msg", - Toast.LENGTH_SHORT + gotoNext = true ) - nextMirror() } 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, "${ctx.getString(R.string.remote_error)}\n$errorName ($code)\n$msg", - Toast.LENGTH_SHORT + gotoNext = true ) - nextMirror() } 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, "${ctx.getString(R.string.render_error)}\n$errorName ($code)\n$msg", - Toast.LENGTH_SHORT + gotoNext = true ) - nextMirror() } else -> { showToast( - activity, "${ctx.getString(R.string.unexpected_error)}\n$errorName ($code)\n$msg", - Toast.LENGTH_SHORT + gotoNext = false ) } } } is InvalidFileException -> { showToast( - activity, "${ctx.getString(R.string.source_error)}\n${exception.message}", - Toast.LENGTH_SHORT + gotoNext = true ) - nextMirror() } else -> { - showToast(activity, exception.message, Toast.LENGTH_SHORT) + exception.message?.let { + showToast( + it, + gotoNext = false + ) + } } } } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/DownloadFileGenerator.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/DownloadFileGenerator.kt index 1f7bb103..63181d30 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/DownloadFileGenerator.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/DownloadFileGenerator.kt @@ -46,6 +46,10 @@ class DownloadFileGenerator( return episodes.getOrNull(currentIndex + offset) } + override fun getAll(): List? { + return null + } + override suspend fun generateLinks( clearCache: Boolean, isCasting: Boolean, diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/FullScreenPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/FullScreenPlayer.kt index c69f19d1..6c869dab 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/FullScreenPlayer.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/FullScreenPlayer.kt @@ -94,6 +94,17 @@ open class FullScreenPlayer : AbstractPlayerFragment() { protected var isShowing = false protected var isLocked = false + //private var episodes: List = listOf() + protected fun setEpisodes(ep: List) { + //hasEpisodes = ep.size > 1 // if has 2 episodes or more because you dont want to switch to your current episode + //(player_episode_list?.adapter as? PlayerEpisodeAdapter?)?.updateList(ep) + } + + protected var hasEpisodes = false + private set + //protected val hasEpisodes + // get() = episodes.isNotEmpty() + // options for player protected var currentPrefQuality = Qualities.P2160.value // preferred maximum quality, used for ppl w bad internet or on cell @@ -541,6 +552,9 @@ open class FullScreenPlayer : AbstractPlayerFragment() { player_pause_play?.startAnimation(fadeAnimation) player_ffwd_holder?.startAnimation(fadeAnimation) player_rew_holder?.startAnimation(fadeAnimation) + + //if (hasEpisodes) + // player_episodes_button?.startAnimation(fadeAnimation) //player_media_route_button?.startAnimation(fadeAnimation) //video_bar.startAnimation(fadeAnimation) @@ -575,6 +589,7 @@ open class FullScreenPlayer : AbstractPlayerFragment() { player_pause_play?.isGone = isGone //player_buffering?.isGone = isGone player_top_holder?.isGone = isGone + //player_episodes_button?.isVisible = !isGone && hasEpisodes player_video_title?.isGone = togglePlayerTitleGone player_video_title_rez?.isGone = isGone player_episode_filler?.isGone = isGone @@ -750,7 +765,9 @@ open class FullScreenPlayer : AbstractPlayerFragment() { MotionEvent.ACTION_DOWN -> { // validates if the touch is inside of the player area isCurrentTouchValid = isValidTouch(currentTouch.x, currentTouch.y) - if (isCurrentTouchValid) { + /*if (isCurrentTouchValid && player_episode_list?.isVisible == true) { + player_episode_list?.isVisible = false + } else*/ if (isCurrentTouchValid) { currentTouchStartTime = System.currentTimeMillis() currentTouchStart = currentTouch currentTouchLast = currentTouch @@ -1152,6 +1169,15 @@ open class FullScreenPlayer : AbstractPlayerFragment() { } else false } + //player_episodes_button?.setOnClickListener { + // player_episodes_button?.isGone = true + // player_episode_list?.isVisible = true + //} +// + //player_episode_list?.adapter = PlayerEpisodeAdapter { click -> +// + //} + try { context?.let { ctx -> val settingsManager = PreferenceManager.getDefaultSharedPreferences(ctx) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt index d2502269..fabb2fa8 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt @@ -143,6 +143,7 @@ class GeneratorPlayer : FullScreenPlayer() { currentSelectedLink = link currentMeta = viewModel.getMeta() nextMeta = viewModel.getNextMeta() + setEpisodes(viewModel.getAllMeta() ?: emptyList()) isActive = true setPlayerDimen(null) setTitle() @@ -273,8 +274,10 @@ class GeneratorPlayer : FullScreenPlayer() { mainTextView?.text = item?.let { getName(it, false) } - val language = item?.let { fromTwoLettersToLanguage(it.lang.trim()) ?: it.lang } ?: "" - val providerSuffix = if (isSingleProvider || item == null) "" else " · ${item.source}" + val language = + item?.let { fromTwoLettersToLanguage(it.lang.trim()) ?: it.lang } ?: "" + val providerSuffix = + if (isSingleProvider || item == null) "" else " · ${item.source}" secondaryTextView?.text = language + providerSuffix setHearingImpairedIcon(drawableEnd, position) @@ -688,6 +691,11 @@ class GeneratorPlayer : FullScreenPlayer() { viewModel.loadLinksPrev() } + override fun hasNextMirror(): Boolean { + val links = sortLinks() + return links.isNotEmpty() && links.indexOf(currentSelectedLink) + 1 < links.size + } + override fun nextMirror() { val links = sortLinks() if (links.isEmpty()) { diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/IGenerator.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/IGenerator.kt index b575886b..219f5502 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/IGenerator.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/IGenerator.kt @@ -14,6 +14,7 @@ interface IGenerator { fun getCurrentId(): Int? // this is used to save data or read data about this id fun getCurrent(offset : Int = 0): Any? // this is used to get metadata about the current playing, can return null + fun getAll() : List? // thus us used to get the metadata about all entries, not needed /* not safe, must use try catch */ suspend fun generateLinks( diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/LinkGenerator.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/LinkGenerator.kt new file mode 100644 index 00000000..38225dae --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/LinkGenerator.kt @@ -0,0 +1,68 @@ +package com.lagradost.cloudstream3.ui.player + +import com.lagradost.cloudstream3.apmap +import com.lagradost.cloudstream3.utils.ExtractorLink +import com.lagradost.cloudstream3.utils.ExtractorUri +import com.lagradost.cloudstream3.utils.Qualities +import com.lagradost.cloudstream3.utils.loadExtractor + +class LinkGenerator( + private val links: List, + private val extract: Boolean = true, + private val referer: String? = null, +) : IGenerator { + override val hasCache = false + + override fun getCurrentId(): Int? { + return null + } + + override fun hasNext(): Boolean { + return false + } + + override fun getAll(): List? { + return null + } + + override fun hasPrev(): Boolean { + return false + } + + override fun getCurrent(offset: Int): Any? { + return null + } + + override fun goto(index: Int) {} + + override fun next() {} + + override fun prev() {} + + override suspend fun generateLinks( + clearCache: Boolean, + isCasting: Boolean, + callback: (Pair) -> Unit, + subtitleCallback: (SubtitleData) -> Unit, + offset: Int + ): Boolean { + links.apmap { link -> + if (!extract || !loadExtractor(link, referer) { + callback(it to null) + }) { + // if don't extract or if no extractor found simply return the link + callback( + ExtractorLink( + "", + link, + link, + referer ?: "", + Qualities.Unknown.value, link.contains(".m3u8") // TODO USE REAL PARSER + ) to null + ) + } + } + + return true + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/PlayerEpisodeAdapter.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/PlayerEpisodeAdapter.kt new file mode 100644 index 00000000..2ba47978 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/PlayerEpisodeAdapter.kt @@ -0,0 +1,158 @@ +package com.lagradost.cloudstream3.ui.player + +import android.annotation.SuppressLint +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ImageView +import android.widget.TextView +import androidx.core.view.isGone +import androidx.core.view.isVisible +import androidx.core.widget.ContentLoadingProgressBar +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.RecyclerView +import com.google.android.material.button.MaterialButton +import com.lagradost.cloudstream3.R +import com.lagradost.cloudstream3.ui.result.ResultEpisode +import com.lagradost.cloudstream3.ui.result.getDisplayPosition +import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings +import com.lagradost.cloudstream3.utils.AppUtils.html +import com.lagradost.cloudstream3.utils.UIHelper.setImage +import kotlinx.android.synthetic.main.player_episodes_large.view.episode_holder_large +import kotlinx.android.synthetic.main.player_episodes_large.view.episode_progress +import kotlinx.android.synthetic.main.player_episodes_small.view.episode_holder +import kotlinx.android.synthetic.main.result_episode_large.view.* + + +data class PlayerEpisodeClickEvent(val action: Int, val data: Any) + +class PlayerEpisodeAdapter( + private val items: MutableList = mutableListOf(), + private val clickCallback: (PlayerEpisodeClickEvent) -> Unit, +) : RecyclerView.Adapter() { + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + return PlayerEpisodeCardViewHolder( + LayoutInflater.from(parent.context) + .inflate(R.layout.player_episodes, parent, false), + clickCallback, + ) + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + println("HOLDER $holder $position") + + when (holder) { + is PlayerEpisodeCardViewHolder -> { + holder.bind(items[position]) + } + } + } + + override fun getItemCount(): Int { + return items.size + } + + fun updateList(newList: List) { + println("Updated list $newList") + val diffResult = DiffUtil.calculateDiff(EpisodeDiffCallback(this.items, newList)) + items.clear() + items.addAll(newList) + + diffResult.dispatchUpdatesTo(this) + } + + class PlayerEpisodeCardViewHolder + constructor( + itemView: View, + private val clickCallback: (PlayerEpisodeClickEvent) -> Unit, + ) : RecyclerView.ViewHolder(itemView) { + @SuppressLint("SetTextI18n") + fun bind(card: Any) { + if (card is ResultEpisode) { + val (parentView, otherView) = if (card.poster == null) { + itemView.episode_holder to itemView.episode_holder_large + } else { + itemView.episode_holder_large to itemView.episode_holder + } + + val episodeText: TextView? = parentView.episode_text + val episodeFiller: MaterialButton? = parentView.episode_filler + val episodeRating: TextView? = parentView.episode_rating + val episodeDescript: TextView? = parentView.episode_descript + val episodeProgress: ContentLoadingProgressBar? = parentView.episode_progress + val episodePoster: ImageView? = parentView.episode_poster + + parentView.isVisible = true + otherView.isVisible = false + + + episodeText?.apply { + val name = + if (card.name == null) "${context.getString(R.string.episode)} ${card.episode}" else "${card.episode}. ${card.name}" + + text = name + isSelected = true + } + + episodeFiller?.isVisible = card.isFiller == true + + val displayPos = card.getDisplayPosition() + episodeProgress?.max = (card.duration / 1000).toInt() + episodeProgress?.progress = (displayPos / 1000).toInt() + episodeProgress?.isVisible = displayPos > 0L + episodePoster?.isVisible = episodePoster?.setImage(card.poster) == true + + if (card.rating != null) { + episodeRating?.text = episodeRating?.context?.getString(R.string.rated_format) + ?.format(card.rating.toFloat() / 10f) + } else { + episodeRating?.text = "" + } + + episodeRating?.isGone = episodeRating?.text.isNullOrBlank() + + episodeDescript?.apply { + text = card.description.html() + isGone = text.isNullOrBlank() + //setOnClickListener { + // clickCallback.invoke(PlayerEpisodeClickEvent(ACTION_SHOW_DESCRIPTION, card)) + //} + } + + parentView.setOnClickListener { + clickCallback.invoke(PlayerEpisodeClickEvent(0, card)) + } + + if (parentView.context.isTrueTvSettings()) { + parentView.isFocusable = true + parentView.isFocusableInTouchMode = true + parentView.touchscreenBlocksFocus = false + } + } + } + } +} + +class EpisodeDiffCallback( + private val oldList: List, + private val newList: List +) : + DiffUtil.Callback() { + override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { + val a = oldList[oldItemPosition] + val b = newList[newItemPosition] + return if (a is ResultEpisode && b is ResultEpisode) { + a.id == b.id + } else { + a == b + } + } + + override fun getOldListSize() = oldList.size + + override fun getNewListSize() = newList.size + + override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int) = + oldList[oldItemPosition] == newList[newItemPosition] +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/PlayerGeneratorViewModel.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/PlayerGeneratorViewModel.kt index b3537333..3f37fa15 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/PlayerGeneratorViewModel.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/PlayerGeneratorViewModel.kt @@ -80,6 +80,10 @@ class PlayerGeneratorViewModel : ViewModel() { return normalSafeApiCall { generator?.getCurrent() } } + fun getAllMeta(): List? { + return normalSafeApiCall { generator?.getAll() } + } + fun getNextMeta(): Any? { return normalSafeApiCall { if (generator?.hasNext() == false) return@normalSafeApiCall null diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/RepoLinkGenerator.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/RepoLinkGenerator.kt index 9ac1e2c6..d0ab245d 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/RepoLinkGenerator.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/RepoLinkGenerator.kt @@ -15,7 +15,8 @@ class RepoLinkGenerator( ) : IGenerator { companion object { const val TAG = "RepoLink" - val cache: HashMap, Pair, MutableSet>> = hashMapOf() + val cache: HashMap, Pair, MutableSet>> = + hashMapOf() } override val hasCache = true @@ -54,6 +55,10 @@ class RepoLinkGenerator( return episodes.getOrNull(currentIndex + offset) } + override fun getAll(): List { + return episodes + } + // this is a simple array that is used to instantly load links if they are already loaded //var linkCache = Array>(size = episodes.size, init = { setOf() }) //var subsCache = Array>(size = episodes.size, init = { setOf() }) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/EpisodeAdapter.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/EpisodeAdapter.kt index 6a62a48b..69d2e07a 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/EpisodeAdapter.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/EpisodeAdapter.kt @@ -175,15 +175,9 @@ class EpisodeAdapter( val displayPos = card.getDisplayPosition() episodeProgress?.max = (card.duration / 1000).toInt() episodeProgress?.progress = (displayPos / 1000).toInt() + episodeProgress?.isVisible = displayPos > 0L - episodeProgress?.visibility = if (displayPos > 0L) View.VISIBLE else View.GONE - - if (card.poster != null) { - episodePoster?.visibility = View.VISIBLE - episodePoster?.setImage(card.poster) - } else { - episodePoster?.visibility = View.GONE - } + episodePoster?.isVisible = episodePoster?.setImage(card.poster) == true if (card.rating != null) { episodeRating?.text = episodeRating?.context?.getString(R.string.rated_format) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt index 91f8ea4d..4334665a 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt @@ -97,7 +97,10 @@ import kotlinx.android.synthetic.main.fragment_trailer.* import kotlinx.android.synthetic.main.result_recommendations.* import kotlinx.android.synthetic.main.result_sync.* import kotlinx.android.synthetic.main.trailer_custom_layout.* -import kotlinx.coroutines.* +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.withContext import java.io.File import java.util.concurrent.TimeUnit @@ -610,6 +613,10 @@ class ResultFragment : ResultTrailerPlayer() { loadTrailer() } + override fun hasNextMirror(): Boolean { + return currentTrailerIndex + 1 < currentTrailers.size + } + override fun playerError(exception: Exception) { if (player.getIsPlaying()) { // because we dont want random toasts in player super.playerError(exception) @@ -697,7 +704,8 @@ class ResultFragment : ResultTrailerPlayer() { }?.also { text -> result_next_airing_time?.text = text result_next_airing?.text = - ctx.getString(R.string.next_episode_format).format(nextAiring.episode) + ctx.getString(R.string.next_episode_format) + .format(nextAiring.episode) } != null } } catch (e: Exception) { // mistranslation @@ -1163,7 +1171,7 @@ class ResultFragment : ResultTrailerPlayer() { try { acquireSingeExtractorLink(act.getString(R.string.episode_action_copy_link)) { link -> val serviceClipboard = - (act.getSystemService(CLIPBOARD_SERVICE) as ClipboardManager?) + (act.getSystemService(CLIPBOARD_SERVICE) as? ClipboardManager?) ?: return@acquireSingeExtractorLink val clip = ClipData.newPlainText(link.name, link.url) serviceClipboard.setPrimaryClip(clip) diff --git a/app/src/main/res/drawable/ic_baseline_arrow_back_ios_24.xml b/app/src/main/res/drawable/ic_baseline_arrow_back_ios_24.xml new file mode 100644 index 00000000..6c3197a6 --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_arrow_back_ios_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/layout/fragment_downloads.xml b/app/src/main/res/layout/fragment_downloads.xml index 1c1f30ec..96f1700e 100644 --- a/app/src/main/res/layout/fragment_downloads.xml +++ b/app/src/main/res/layout/fragment_downloads.xml @@ -189,7 +189,6 @@ + android:progressDrawable="@drawable/progress_drawable_vertical" + android:layout_centerInParent="true" /> + + diff --git a/app/src/main/res/layout/player_episodes.xml b/app/src/main/res/layout/player_episodes.xml new file mode 100644 index 00000000..a491bc09 --- /dev/null +++ b/app/src/main/res/layout/player_episodes.xml @@ -0,0 +1,8 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/player_episodes_large.xml b/app/src/main/res/layout/player_episodes_large.xml new file mode 100644 index 00000000..b1f815e4 --- /dev/null +++ b/app/src/main/res/layout/player_episodes_large.xml @@ -0,0 +1,106 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/player_episodes_small.xml b/app/src/main/res/layout/player_episodes_small.xml new file mode 100644 index 00000000..62dd4bca --- /dev/null +++ b/app/src/main/res/layout/player_episodes_small.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/stream_input.xml b/app/src/main/res/layout/stream_input.xml index 384cf5b5..ef6a35b8 100644 --- a/app/src/main/res/layout/stream_input.xml +++ b/app/src/main/res/layout/stream_input.xml @@ -5,14 +5,46 @@ android:layout_width="match_parent" android:layout_height="wrap_content"> - + android:layout_height="wrap_content"> + + + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 70e3c4d9..5e10bb4d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -546,9 +546,12 @@ Resolution Invalid id Invalid data + Invalid url Error Remove closed captions from subtitles Remove bloat from subtitles Extras Trailer + Link to stream + Referer