play any stream & a bit of work on invideoplayer episodes & no links found last error

This commit is contained in:
LagradOst 2022-07-16 03:43:10 +02:00
parent 6a3df3b412
commit 524d0717e2
21 changed files with 637 additions and 45 deletions

View File

@ -23,7 +23,7 @@ class GMPlayer : ExtractorApi() {
"origin" to mainUrl
),
data = mapOf("hash" to id, "r" to ref)
).also { println("shiv " + it.text) }.parsed<GmResponse>().videoSource ?: return null
).parsed<GmResponse>().videoSource ?: return null
return M3u8Helper.generateM3u8(
name,

View File

@ -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)

View File

@ -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
)
}
}
}
}

View File

@ -46,6 +46,10 @@ class DownloadFileGenerator(
return episodes.getOrNull(currentIndex + offset)
}
override fun getAll(): List<Any>? {
return null
}
override suspend fun generateLinks(
clearCache: Boolean,
isCasting: Boolean,

View File

@ -94,6 +94,17 @@ open class FullScreenPlayer : AbstractPlayerFragment() {
protected var isShowing = false
protected var isLocked = false
//private var episodes: List<Any> = listOf()
protected fun setEpisodes(ep: List<Any>) {
//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)

View File

@ -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()) {

View File

@ -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<Any>? // thus us used to get the metadata about all entries, not needed
/* not safe, must use try catch */
suspend fun generateLinks(

View File

@ -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<String>,
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<Any>? {
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<ExtractorLink?, ExtractorUri?>) -> 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
}
}

View File

@ -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<Any> = mutableListOf(),
private val clickCallback: (PlayerEpisodeClickEvent) -> Unit,
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
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<Any>) {
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<Any>,
private val newList: List<Any>
) :
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]
}

View File

@ -80,6 +80,10 @@ class PlayerGeneratorViewModel : ViewModel() {
return normalSafeApiCall { generator?.getCurrent() }
}
fun getAllMeta(): List<Any>? {
return normalSafeApiCall { generator?.getAll() }
}
fun getNextMeta(): Any? {
return normalSafeApiCall {
if (generator?.hasNext() == false) return@normalSafeApiCall null

View File

@ -15,7 +15,8 @@ class RepoLinkGenerator(
) : IGenerator {
companion object {
const val TAG = "RepoLink"
val cache: HashMap<Pair<String, Int>, Pair<MutableSet<ExtractorLink>, MutableSet<SubtitleData>>> = hashMapOf()
val cache: HashMap<Pair<String, Int>, Pair<MutableSet<ExtractorLink>, MutableSet<SubtitleData>>> =
hashMapOf()
}
override val hasCache = true
@ -54,6 +55,10 @@ class RepoLinkGenerator(
return episodes.getOrNull(currentIndex + offset)
}
override fun getAll(): List<Any> {
return episodes
}
// this is a simple array that is used to instantly load links if they are already loaded
//var linkCache = Array<Set<ExtractorLink>>(size = episodes.size, init = { setOf() })
//var subsCache = Array<Set<SubtitleData>>(size = episodes.size, init = { setOf() })

View File

@ -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)

View File

@ -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)

View File

@ -0,0 +1,5 @@
<vector android:autoMirrored="true" android:height="24dp"
android:tint="#FFFFFF" android:viewportHeight="24"
android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M11.67,3.87L9.9,2.1 0,12l9.9,9.9 1.77,-1.77L3.54,12z"/>
</vector>

View File

@ -189,7 +189,6 @@
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
android:text="@string/stream"
android:visibility="gone"
android:id="@+id/download_stream_button"
app:icon="@drawable/netflix_play"
style="@style/ExtendedFloatingActionButton"

View File

@ -607,14 +607,55 @@
style="@android:style/Widget.Material.ProgressBar.Horizontal"
android:layout_width="4dp"
android:layout_height="150dp"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:layout_gravity="end|center_vertical"
android:layout_marginEnd="40dp"
android:indeterminate="false"
android:max="100"
android:progress="100"
android:progressDrawable="@drawable/progress_drawable_vertical" />
android:progressDrawable="@drawable/progress_drawable_vertical"
android:layout_centerInParent="true" />
</RelativeLayout>
</LinearLayout>
<!--
<ImageView
android:padding="15dp"
android:id="@+id/player_episodes_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|center_vertical"
android:src="@drawable/ic_baseline_arrow_back_ios_24"
android:background="@drawable/video_tap_button_always_white"
app:tint="@android:color/white"
tools:ignore="ContentDescription"></ImageView>
<LinearLayout
android:visibility="gone"
android:background="?attr/primaryBlackBackground"
android:orientation="vertical"
android:layout_gravity="end"
android:layout_width="wrap_content"
android:layout_height="match_parent">
<TextView
android:padding="10dp"
style="@style/WatchHeaderText"
android:textSize="15sp"
android:layout_marginEnd="0dp"
android:text="@string/episodes"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<androidx.recyclerview.widget.RecyclerView
android:requiresFadingEdge="vertical"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
android:id="@+id/player_episode_list"
tools:listitem="@layout/player_episodes"
android:layout_width="200dp"
android:layout_height="match_parent">
</androidx.recyclerview.widget.RecyclerView>
</LinearLayout>
-->
</FrameLayout>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<include android:visibility="gone" layout="@layout/player_episodes_small" />
<include android:visibility="gone" layout="@layout/player_episodes_large" />
</FrameLayout>

View File

@ -0,0 +1,106 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/episode_holder_large"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardCornerRadius="@dimen/rounded_image_radius"
app:cardBackgroundColor="@color/transparent"
app:cardElevation="0dp"
android:foreground="@drawable/outline_drawable"
android:layout_marginBottom="5dp">
<!-- IDK BUT THIS DOES NOT SEAM LIKE A GOOD WAY OF DOING IT -->
<!--<LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="horizontal">
<View
android:layout_weight="0.5"
android:id="@+id/episode_view_procentage"
android:alpha="0.2"
android:background="?attr/colorPrimary"
android:layout_width="0dp"
android:layout_height="match_parent">
</View>
<View
android:id="@+id/episode_view_procentage_off"
android:layout_weight="0.10"
android:alpha="0"
android:background="@color/transparent"
android:layout_width="0dp"
android:layout_height="match_parent">
</View>
</LinearLayout>-->
<androidx.cardview.widget.CardView
android:layout_width="200dp"
android:layout_height="114dp"
android:foreground="@drawable/outline_drawable">
<ImageView
android:nextFocusLeft="@id/result_episode_download"
android:nextFocusRight="@id/episode_holder"
android:id="@+id/episode_poster"
tools:src="@drawable/example_poster"
android:foreground="?android:attr/selectableItemBackgroundBorderless"
android:scaleType="centerCrop"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:contentDescription="@string/episode_poster_img_des" />
<ImageView
android:src="@drawable/play_button"
android:layout_gravity="center"
android:layout_width="57dp"
android:layout_height="57dp"
android:contentDescription="@string/play_episode" />
<androidx.core.widget.ContentLoadingProgressBar
android:layout_marginBottom="-1.5dp"
android:id="@+id/episode_progress"
android:progressTint="?attr/colorPrimary"
android:progressBackgroundTint="?attr/colorPrimary"
style="@android:style/Widget.Material.ProgressBar.Horizontal"
android:layout_width="match_parent"
tools:progress="50"
android:layout_gravity="bottom"
android:layout_height="5dp" />
</androidx.cardview.widget.CardView>
<!-- <LinearLayout
android:foreground="?android:attr/selectableItemBackgroundBorderless"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.button.MaterialButton
android:layout_marginStart="10dp"
android:layout_gravity="center"
style="@style/SmallBlackButton"
android:text="@string/filler"
android:id="@+id/episode_filler" />
<TextView
android:id="@+id/episode_text"
android:layout_marginStart="10dp"
android:layout_marginEnd="50dp"
android:layout_gravity="center_vertical"
android:gravity="center_vertical"
tools:text="Episode 1"
android:textColor="?attr/textColor"
android:scrollHorizontally="true"
android:ellipsize="marquee"
android:marqueeRepeatLimit="0"
android:singleLine="true"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>-->
</LinearLayout>

View File

@ -0,0 +1,56 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/episode_holder"
android:layout_width="match_parent"
android:layout_height="50dp"
app:cardBackgroundColor="@color/transparent"
app:cardElevation="0dp"
android:foreground="@drawable/outline_drawable">
<androidx.core.widget.ContentLoadingProgressBar
android:layout_marginBottom="-1.5dp"
android:id="@+id/episode_progress"
android:progressTint="?attr/colorPrimary"
android:progressBackgroundTint="?attr/colorPrimary"
style="@android:style/Widget.Material.ProgressBar.Horizontal"
android:layout_width="match_parent"
tools:progress="50"
android:layout_gravity="bottom"
android:layout_height="5dp" />
<LinearLayout
android:foreground="?android:attr/selectableItemBackgroundBorderless"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!--marquee_forever-->
<com.google.android.material.button.MaterialButton
android:layout_marginStart="10dp"
android:layout_gravity="center"
style="@style/SmallBlackButton"
android:text="@string/filler"
android:id="@+id/episode_filler" />
<TextView
android:id="@+id/episode_text"
android:layout_marginStart="10dp"
android:layout_gravity="center_vertical"
android:gravity="center_vertical"
tools:text="Episode 1"
android:textColor="?attr/textColor"
android:scrollHorizontally="true"
android:ellipsize="marquee"
android:marqueeRepeatLimit="0"
android:singleLine="true"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
</FrameLayout>

View File

@ -5,14 +5,46 @@
android:layout_width="match_parent"
android:layout_height="wrap_content">
<EditText
android:layout_weight="20"
android:id="@+id/subtitle_offset_input"
android:inputType="textUri"
<LinearLayout
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/subtitle_offset_hint"
tools:ignore="LabelFor" />
android:layout_height="wrap_content">
<TextView
android:id="@+id/text1"
android:layout_marginTop="20dp"
android:layout_marginBottom="10dp"
android:textStyle="bold"
android:textSize="20sp"
android:textColor="?attr/textColor"
android:layout_width="match_parent"
android:layout_rowWeight="1"
android:text="@string/stream"
android:layout_height="wrap_content" />
<EditText
android:layout_weight="20"
android:id="@+id/stream_url"
android:inputType="textUri"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/network_adress_example"
tools:ignore="LabelFor" />
<EditText
android:layout_weight="20"
android:id="@+id/stream_referer"
android:inputType="textUri"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/referer"
tools:ignore="LabelFor" />
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_gravity="bottom"
@ -24,7 +56,7 @@
style="@style/WhiteButton"
android:layout_gravity="center_vertical|end"
android:visibility="visible"
android:text="@string/sort_apply"
android:text="@string/home_play"
android:id="@+id/apply_btt"
android:layout_width="wrap_content">

View File

@ -546,9 +546,12 @@
<string name="resolution">Resolution</string>
<string name="error_invalid_id">Invalid id</string>
<string name="error_invalid_data">Invalid data</string>
<string name="error_invalid_url">Invalid url</string>
<string name="error">Error</string>
<string name="subtitles_remove_captions">Remove closed captions from subtitles</string>
<string name="subtitles_remove_bloat">Remove bloat from subtitles</string>
<string name="extras">Extras</string>
<string name="trailer">Trailer</string>
<string name="network_adress_example">Link to stream</string>
<string name="referer">Referer</string>
</resources>