This commit is contained in:
LagradOst 2021-08-04 03:50:24 +02:00
parent 6b27db036b
commit 3f8229756d
17 changed files with 450 additions and 101 deletions

View file

@ -113,6 +113,8 @@ abstract class MainAPI {
}
}
class ErrorLoadingException(message: String? = null) : Exception(message)
fun parseRating(ratingString: String?): Int? {
if (ratingString == null) return null
val floatRating = ratingString.toFloatOrNull() ?: return null
@ -149,6 +151,19 @@ fun sortSubs(urls: List<SubtitleFile>): List<SubtitleFile> {
}
}
/** https://www.imdb.com/title/tt2861424/ -> tt2861424 */
fun imdbUrlToId(url: String): String {
return url
.removePrefix("https://www.imdb.com/title/")
.removePrefix("https://imdb.com/title/tt2861424/")
.replace("/", "")
}
fun imdbUrlToIdNullable(url: String?): String? {
if(url == null) return null
return imdbUrlToId(url)
}
enum class ShowStatus {
Completed,
Ongoing,
@ -301,7 +316,7 @@ data class MovieLoadResponse(
override val year: Int?,
override val plot: String?,
val imdbUrl: String?,
val imdbId: String?,
override val rating: Int? = null,
override val tags: ArrayList<String>? = null,
override val duration: String? = null,
@ -331,7 +346,7 @@ data class TvSeriesLoadResponse(
override val plot: String?,
val showStatus: ShowStatus?,
val imdbUrl: String?,
val imdbId: String?,
override val rating: Int? = null,
override val tags: ArrayList<String>? = null,
override val duration: String? = null,

View file

@ -31,10 +31,8 @@ import com.lagradost.cloudstream3.utils.DataStore.getKey
import com.lagradost.cloudstream3.utils.DataStore.removeKey
import com.lagradost.cloudstream3.utils.DataStoreHelper.setViewPos
import com.lagradost.cloudstream3.utils.Event
import com.lagradost.cloudstream3.utils.SubtitleHelper.createISO
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.android.synthetic.main.fragment_result.*
import kotlin.concurrent.thread
const val VLC_PACKAGE = "org.videolan.vlc"
const val VLC_INTENT_ACTION_RESULT = "org.videolan.vlc.player.result"
@ -58,7 +56,7 @@ class MainActivity : AppCompatActivity() {
return appViewModelStore
}*/
companion object {
var isInPlayer: Boolean = false
var canEnterPipMode: Boolean = false
var canShowPipMode: Boolean = false
var isInPIPMode: Boolean = false
@ -67,7 +65,7 @@ class MainActivity : AppCompatActivity() {
}
private fun enterPIPMode() {
if (!shouldShowPIPMode(isInPlayer) || !canShowPipMode) return
if (!shouldShowPIPMode(canEnterPipMode) || !canShowPipMode) return
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
try {
enterPictureInPictureMode(PictureInPictureParams.Builder().build())
@ -83,7 +81,7 @@ class MainActivity : AppCompatActivity() {
override fun onUserLeaveHint() {
super.onUserLeaveHint()
if (isInPlayer && canShowPipMode) {
if (canEnterPipMode && canShowPipMode) {
enterPIPMode()
}
}

View file

@ -132,7 +132,7 @@ class MeloMovieProvider : MainAPI() {
val plot = document.selectFirst("div.col-lg-12 > p").text()
if (type == 1) { // MOVIE
val serialize = document.selectFirst("table.accordion__list")
val serialize = document.selectFirst("table.accordion__list") ?: throw ErrorLoadingException("No links found")
return MovieLoadResponse(
title,
url,
@ -142,11 +142,11 @@ class MeloMovieProvider : MainAPI() {
poster,
year,
plot,
imdbUrl
imdbUrlToIdNullable(imdbUrl)
)
} else if (type == 2) {
val episodes = ArrayList<TvSeriesEpisode>()
val seasons = document.select("div.accordion__card")
val seasons = document.select("div.accordion__card") ?: throw ErrorLoadingException("No episodes found")
for (s in seasons) {
val season =
s.selectFirst("> div.card-header > button > span").text().replace("Season: ", "").toIntOrNull()
@ -154,7 +154,7 @@ class MeloMovieProvider : MainAPI() {
for (e in localEpisodes) {
val episode =
e.selectFirst("> div.card-header > button > span").text().replace("Episode: ", "").toIntOrNull()
val links = e.selectFirst("> div.collapse > div > table.accordion__list")
val links = e.selectFirst("> div.collapse > div > table.accordion__list") ?: continue
val data = serializeData(links)
episodes.add(TvSeriesEpisode(null, season, episode, data))
}
@ -170,7 +170,7 @@ class MeloMovieProvider : MainAPI() {
year,
plot,
null,
imdbUrl
imdbUrlToIdNullable(imdbUrl)
)
}
return null

View file

@ -2,7 +2,6 @@ package com.lagradost.cloudstream3.movieproviders
import com.fasterxml.jackson.module.kotlin.readValue
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.utils.AppUtils.imdbUrlToId
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.Qualities
import com.lagradost.cloudstream3.utils.SubtitleHelper
@ -255,7 +254,7 @@ class TrailersToProvider : MainAPI() {
year,
descript,
null,
imdbUrl,
imdbUrlToIdNullable(imdbUrl),
rating,
tags,
duration,
@ -283,7 +282,7 @@ class TrailersToProvider : MainAPI() {
poster,
year,
descript,
imdbUrl,
imdbUrlToIdNullable(imdbUrl),
rating,
tags,
duration,

View file

@ -5,7 +5,7 @@ import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer
import com.bumptech.glide.load.HttpException
import com.lagradost.cloudstream3.ui.ErrorLoadingException
import com.lagradost.cloudstream3.ErrorLoadingException
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.net.SocketTimeoutException
@ -30,7 +30,8 @@ sealed class Resource<out T> {
val errorResponse: Any?, //ResponseBody
val errorString: String,
) : Resource<Nothing>()
data class Loading(val url : String? = null) : Resource<Nothing>()
data class Loading(val url: String? = null) : Resource<Nothing>()
}
fun logError(throwable: Throwable) {
@ -41,7 +42,7 @@ fun logError(throwable: Throwable) {
Log.d("ApiError", "-------------------------------------------------------------------")
}
fun<T> normalSafeApiCall(apiCall : () -> T) : T? {
fun <T> normalSafeApiCall(apiCall: () -> T): T? {
return try {
apiCall.invoke()
} catch (throwable: Throwable) {
@ -69,7 +70,7 @@ suspend fun <T> safeApiCall(
Resource.Failure(true, null, null, "Cannot connect to server, try again later.")
}
is ErrorLoadingException -> {
Resource.Failure(true, null, null, "Error loading, try again later.")
Resource.Failure(true, null, null, throwable.message ?: "Error loading, try again later.")
}
else -> {
val stackTraceMsg = throwable.localizedMessage + "\n\n" + throwable.stackTrace.joinToString(

View file

@ -6,8 +6,6 @@ import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
import com.lagradost.cloudstream3.mvvm.safeApiCall
import com.lagradost.cloudstream3.utils.ExtractorLink
class ErrorLoadingException(message: String) : Exception(message)
class APIRepository(val api: MainAPI) {
val name: String get() = api.name
val mainUrl: String get() = api.mainUrl
@ -15,25 +13,25 @@ class APIRepository(val api: MainAPI) {
suspend fun load(url: String): Resource<LoadResponse> {
return safeApiCall {
// remove suffix for some slugs to handle correctly
api.load(url.removeSuffix("/")) ?: throw ErrorLoadingException("Error Loading")
api.load(url.removeSuffix("/")) ?: throw ErrorLoadingException()
}
}
suspend fun search(query: String): Resource<ArrayList<SearchResponse>> {
return safeApiCall {
api.search(query) ?: throw ErrorLoadingException("Error Loading")
api.search(query) ?: throw ErrorLoadingException()
}
}
suspend fun quickSearch(query: String): Resource<ArrayList<SearchResponse>> {
return safeApiCall {
api.quickSearch(query) ?: throw ErrorLoadingException("Error Loading")
api.quickSearch(query) ?: throw ErrorLoadingException()
}
}
suspend fun getMainPage(): Resource<HomePageResponse> {
return safeApiCall {
api.getMainPage() ?: throw ErrorLoadingException("Error Loading")
api.getMainPage() ?: throw ErrorLoadingException()
}
}

View file

@ -23,8 +23,6 @@ import com.google.android.gms.cast.framework.media.widget.ExpandedControllerActi
import com.lagradost.cloudstream3.APIHolder.getApiFromName
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.SubtitleFile
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
@ -97,7 +95,6 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi
// lateinit var dialog: AlertDialog
val holder = getCurrentMetaData()
if (holder != null) {
val items = holder.currentLinks
if (items.isNotEmpty() && remoteMediaClient?.currentItem != null) {
@ -251,7 +248,6 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi
) VISIBLE else INVISIBLE
try {
if (meta != null && meta.episodes.size > meta.currentEpisodeIndex + 1) {
val currentIdIndex = remoteMediaClient?.getItemIndex() ?: return
val itemCount = remoteMediaClient?.mediaQueue?.itemCount
@ -264,8 +260,8 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi
val links = ArrayList<ExtractorLink>()
val subs = ArrayList<SubtitleFile>()
val res = safeApiCall {
getApiFromName(meta.apiName).loadLinks(epData.data, true, { subtitleFile ->
val isSuccessful =
APIRepository(getApiFromName(meta.apiName)).loadLinks(epData.data, true, { subtitleFile ->
if (!subs.any { it.url == subtitleFile.url }) {
subs.add(subtitleFile)
}
@ -274,9 +270,8 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi
links.add(link)
}
}
}
if (res is Resource.Success) {
if (isSuccessful) {
val sorted = sortUrls(links)
if (sorted.isNotEmpty()) {
val jsonCopy = meta.copy(

View file

@ -12,6 +12,7 @@ import android.content.pm.ActivityInfo
import android.content.res.Resources
import android.database.ContentObserver
import android.graphics.Color
import android.graphics.Typeface
import android.graphics.drawable.Icon
import android.media.AudioManager
import android.net.Uri
@ -25,8 +26,7 @@ import android.view.animation.AccelerateInterpolator
import android.view.animation.AlphaAnimation
import android.view.animation.Animation
import android.view.animation.AnimationUtils
import android.widget.ProgressBar
import android.widget.Toast
import android.widget.*
import android.widget.Toast.LENGTH_SHORT
import androidx.appcompat.app.AlertDialog
import androidx.core.content.ContextCompat
@ -45,6 +45,8 @@ import com.google.android.exoplayer2.C.TIME_UNSET
import com.google.android.exoplayer2.source.DefaultMediaSourceFactory
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector
import com.google.android.exoplayer2.ui.AspectRatioFrameLayout
import com.google.android.exoplayer2.ui.CaptionStyleCompat
import com.google.android.exoplayer2.ui.SubtitleView
import com.google.android.exoplayer2.upstream.DataSource
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory
import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory
@ -53,17 +55,11 @@ import com.google.android.exoplayer2.util.Util
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.material.button.MaterialButton
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.MainActivity.Companion.isInPIPMode
import com.lagradost.cloudstream3.MainActivity.Companion.isInPlayer
import com.lagradost.cloudstream3.MainActivity.Companion.canEnterPipMode
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.utils.UIHelper.getNavigationBarHeight
import com.lagradost.cloudstream3.utils.UIHelper.getStatusBarHeight
import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard
import com.lagradost.cloudstream3.utils.UIHelper.hideSystemUI
import com.lagradost.cloudstream3.utils.UIHelper.popCurrentPage
import com.lagradost.cloudstream3.utils.UIHelper.showSystemUI
import com.lagradost.cloudstream3.utils.UIHelper.toPx
import com.lagradost.cloudstream3.mvvm.Resource
import com.lagradost.cloudstream3.mvvm.observe
import com.lagradost.cloudstream3.mvvm.observeDirectly
@ -79,7 +75,13 @@ import com.lagradost.cloudstream3.utils.DataStore.getKey
import com.lagradost.cloudstream3.utils.DataStore.setKey
import com.lagradost.cloudstream3.utils.DataStoreHelper.setViewPos
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.UIHelper
import com.lagradost.cloudstream3.utils.UIHelper.getNavigationBarHeight
import com.lagradost.cloudstream3.utils.UIHelper.getStatusBarHeight
import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard
import com.lagradost.cloudstream3.utils.UIHelper.hideSystemUI
import com.lagradost.cloudstream3.utils.UIHelper.popCurrentPage
import com.lagradost.cloudstream3.utils.UIHelper.showSystemUI
import com.lagradost.cloudstream3.utils.UIHelper.toPx
import com.lagradost.cloudstream3.utils.VIDEO_PLAYER_BRIGHTNESS
import com.lagradost.cloudstream3.utils.getId
import kotlinx.android.synthetic.main.fragment_player.*
@ -791,6 +793,14 @@ class PlayerFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val subs = player_view.findViewById<SubtitleView>(R.id.exo_subtitles)
subs.setStyle(
CaptionStyleCompat(
Color.WHITE, Color.TRANSPARENT, Color.TRANSPARENT, CaptionStyleCompat.EDGE_TYPE_OUTLINE, Color.BLACK,
Typeface.SANS_SERIF
)
)
settingsManager = PreferenceManager.getDefaultSharedPreferences(activity)
swipeEnabled = settingsManager.getBoolean("swipe_enabled", true)
swipeVerticalEnabled = settingsManager.getBoolean("swipe_vertical_enabled", true)
@ -800,8 +810,6 @@ class PlayerFragment : Fragment() {
brightness_overlay?.alpha = context?.getKey(VIDEO_PLAYER_BRIGHTNESS, 0f) ?: 0f
isInPlayer = true // NEED REFERENCE TO MAIN ACTIVITY FOR PIP
navigationBarHeight = requireContext().getNavigationBarHeight()
statusBarHeight = requireContext().getStatusBarHeight()
@ -898,9 +906,9 @@ class PlayerFragment : Fragment() {
}
sources_btt.visibility =
if (isDownloadedFile) View.GONE else View.VISIBLE
if (isDownloadedFile) GONE else VISIBLE
player_media_route_button.visibility =
if (isDownloadedFile) View.GONE else View.VISIBLE
if (isDownloadedFile) GONE else VISIBLE
if (savedInstanceState != null) {
currentWindow = savedInstanceState.getInt(STATE_RESUME_WINDOW)
playbackPosition = savedInstanceState.getLong(STATE_RESUME_POSITION)
@ -1138,6 +1146,105 @@ class PlayerFragment : Fragment() {
lateinit var dialog: AlertDialog
getUrls()?.let { it1 ->
sortUrls(it1).let { sources ->
val isPlaying = exoPlayer.isPlaying
exoPlayer.pause()
val currentSubtitles = activeSubtitles
val sourceBuilder = AlertDialog.Builder(view.context, R.style.AlertDialogCustomBlack)
.setView(R.layout.player_select_source_and_subs)
val sourceDialog = sourceBuilder.create()
sourceDialog.show()
// bottomSheetDialog.setContentView(R.layout.sort_bottom_sheet)
val providerList = sourceDialog.findViewById<ListView>(R.id.sort_providers)!!
val subtitleList = sourceDialog.findViewById<ListView>(R.id.sort_subtitles)!!
val applyButton = sourceDialog.findViewById<MaterialButton>(R.id.pick_source_apply)!!
val cancelButton = sourceDialog.findViewById<MaterialButton>(R.id.pick_source_cancel)!!
val startSource = sources.indexOf(getCurrentUrl())
var sourceIndex = startSource
val startSubtitle = currentSubtitles.indexOf(preferredSubtitles) + 1
var subtitleIndex = startSubtitle
if (currentSubtitles.isEmpty()) {
sourceDialog.findViewById<LinearLayout>(R.id.sort_subtitles_holder)?.visibility = GONE
} else {
val subsArrayAdapter = ArrayAdapter<String>(view.context, R.layout.sort_bottom_single_choice)
subsArrayAdapter.add("No Subtitles")
subsArrayAdapter.addAll(currentSubtitles)
subtitleList.adapter = subsArrayAdapter
subtitleList.choiceMode = AbsListView.CHOICE_MODE_SINGLE
subtitleList.setSelection(subtitleIndex)
subtitleList.setItemChecked(subtitleIndex, true)
subtitleList.setOnItemClickListener { _, _, which, _ ->
subtitleIndex = which
subtitleList.setItemChecked(which, true)
}
}
val sourcesArrayAdapter = ArrayAdapter<String>(view.context, R.layout.sort_bottom_single_choice)
sourcesArrayAdapter.addAll(sources.map { it.name })
providerList.choiceMode = AbsListView.CHOICE_MODE_SINGLE
providerList.adapter = sourcesArrayAdapter
providerList.setSelection(sourceIndex)
providerList.setItemChecked(sourceIndex, true)
providerList.setOnItemClickListener { _, _, which, _ ->
sourceIndex = which
providerList.setItemChecked(which, true)
}
sourceDialog.setOnDismissListener {
activity?.hideSystemUI()
}
cancelButton.setOnClickListener {
sourceDialog.dismiss()
}
applyButton.setOnClickListener {
if (sourceIndex != startSource) {
playbackPosition = if (this::exoPlayer.isInitialized) exoPlayer.currentPosition else 0
setMirrorId(sources[sourceIndex].getId())
initPlayer(getCurrentUrl())
} else {
if (isPlaying) {
// exoPlayer.play()
}
}
if (subtitleIndex != startSubtitle) {
val textRendererIndex = getRendererIndex(C.TRACK_TYPE_TEXT) ?: return@setOnClickListener
(exoPlayer.trackSelector as DefaultTrackSelector?)?.let { trackSelector ->
if (subtitleIndex <= 0) {
preferredSubtitles = ""
trackSelector.setParameters(
trackSelector.buildUponParameters()
.setPreferredTextLanguage("")
.setRendererDisabled(textRendererIndex, true)
)
} else {
val currentPreferredSub = currentSubtitles[subtitleIndex - 1]
preferredSubtitles = currentPreferredSub
trackSelector.setParameters(
trackSelector.buildUponParameters()
.setPreferredTextLanguage(currentPreferredSub)
.setRendererDisabled(textRendererIndex, false)
)
}
}
}
sourceDialog.dismiss()
}
/*
*/
/*
val sourcesText = sources.map { it.name }
val builder = AlertDialog.Builder(requireContext(), R.style.AlertDialogCustom)
builder.setTitle("Pick source")
@ -1158,7 +1265,7 @@ class PlayerFragment : Fragment() {
activity?.hideSystemUI()
}
dialog = builder.create()
dialog.show()
dialog.show()*/
}
}
}
@ -1192,6 +1299,18 @@ class PlayerFragment : Fragment() {
// initPlayer()
}
private fun getRendererIndex(trackIndex: Int): Int? {
if (!this::exoPlayer.isInitialized) return null
for (renderIndex in 0 until exoPlayer.rendererCount) {
if (exoPlayer.getRendererType(renderIndex) == renderIndex) {
return renderIndex
}
}
return null
}
private fun getCurrentUrl(): ExtractorLink? {
val urls = getUrls() ?: return null
for (i in urls) {
@ -1201,12 +1320,6 @@ class PlayerFragment : Fragment() {
}
return null
/*ExtractorLink("",
"TEST",
"https://v6.4animu.me/Overlord/Overlord-Episode-01-1080p.mp4",
//"http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4",
"",
0)*/
}
private fun getUrls(): List<ExtractorLink>? {
@ -1306,7 +1419,7 @@ class PlayerFragment : Fragment() {
savePos()
super.onDestroy()
isInPlayer = false
canEnterPipMode = false
savePositionInPlayer()
safeReleasePlayer()
@ -1377,6 +1490,18 @@ class PlayerFragment : Fragment() {
private val updateProgressAction = Runnable { updateProgressBar() }*/
private fun String.toSubtitleMimeType(): String {
return when {
endsWith("vtt", true) -> MimeTypes.TEXT_VTT
endsWith("srt", true) -> MimeTypes.APPLICATION_SUBRIP
endsWith("xml", true) || endsWith("ttml", true) -> MimeTypes.APPLICATION_TTML
else -> MimeTypes.TEXT_VTT
}
}
var activeSubtitles: List<String> = listOf()
var preferredSubtitles: String = ""
@SuppressLint("SetTextI18n")
fun initPlayer(currentUrl: ExtractorLink?, uri: String? = null) {
if (currentUrl == null && uri == null) return
@ -1384,7 +1509,6 @@ class PlayerFragment : Fragment() {
hasUsedFirstRender = false
try {
if (!isInPlayer) return
if (this::exoPlayer.isInitialized) {
savePos()
exoPlayer.release()
@ -1446,11 +1570,35 @@ class PlayerFragment : Fragment() {
}
}
val subs = getSubs()
if (subs != null) {
val subItems = ArrayList<MediaItem.Subtitle>()
val subItemsId = ArrayList<String>()
for (sub in sortSubs(subs)) {
val langId = sub.lang //SubtitleHelper.fromLanguageToTwoLetters(it.lang) ?: it.lang
subItemsId.add(langId)
subItems.add(
MediaItem.Subtitle(
Uri.parse(sub.url),
sub.url.toSubtitleMimeType(),
langId,
C.SELECTION_FLAG_DEFAULT
)
)
}
activeSubtitles = subItemsId
mediaItemBuilder.setSubtitles(subItems)
}
//might add https://github.com/ed828a/Aihua/blob/1896f46888b5a954b367e83f40b845ce174a2328/app/src/main/java/com/dew/aihua/player/playerUI/VideoPlayer.kt#L287 toggle caps
val mediaItem = mediaItemBuilder.build()
val trackSelector = DefaultTrackSelector(requireContext())
// Disable subtitles
trackSelector.parameters = DefaultTrackSelector.ParametersBuilder(requireContext())
.setRendererDisabled(C.TRACK_TYPE_VIDEO, true)
// .setRendererDisabled(C.TRACK_TYPE_VIDEO, true)
.setRendererDisabled(C.TRACK_TYPE_TEXT, true)
.setDisabledTextTrackSelectionFlags(C.TRACK_TYPE_TEXT)
.clearSelectionOverrides()
@ -1530,6 +1678,18 @@ class PlayerFragment : Fragment() {
*/
/*exoPlayer.addTextOutput { list ->
if (list.size == 0) return@addTextOutput
val textBuilder = StringBuilder()
for (cue in list) {
textBuilder.append(cue.text).append("\n")
}
val subtitleText = if (textBuilder.isNotEmpty())
textBuilder.substring(0, textBuilder.length - 1)
else
textBuilder.toString()
}*/
//https://stackoverflow.com/questions/47731779/detect-pause-resume-in-exoplayer
exoPlayer.addListener(object : Player.Listener {
@ -1573,6 +1733,7 @@ class PlayerFragment : Fragment() {
}
override fun onPlayerStateChanged(playWhenReady: Boolean, playbackState: Int) {
canEnterPipMode = exoPlayer.isPlaying
updatePIPModeActions()
if (activity == null) return
if (playWhenReady) {

View file

@ -1,11 +1,13 @@
package com.lagradost.cloudstream3.ui.result
import android.annotation.SuppressLint
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import android.widget.Toast
import androidx.annotation.LayoutRes
import androidx.core.widget.ContentLoadingProgressBar
import androidx.recyclerview.widget.RecyclerView
@ -174,6 +176,15 @@ class EpisodeAdapter(
episodeDescript?.visibility = View.GONE
}
episodePoster?.setOnClickListener {
clickCallback.invoke(EpisodeClickEvent(ACTION_CLICK_DEFAULT, card))
}
episodePoster?.setOnLongClickListener {
Toast.makeText(it.context, R.string.play_episode_toast, Toast.LENGTH_SHORT).show()
return@setOnLongClickListener true
}
episodeHolder.setOnClickListener {
clickCallback.invoke(EpisodeClickEvent(ACTION_CLICK_DEFAULT, card))
}
@ -197,7 +208,15 @@ class EpisodeAdapter(
downloadButton.setUpButton(
downloadInfo?.fileLength, downloadInfo?.totalBytes, episodeDownloadBar, episodeDownloadImage, null,
VideoDownloadHelper.DownloadEpisodeCached(
card.name, card.poster, card.episode, card.season, card.id, 0, card.rating, card.descript, System.currentTimeMillis(),
card.name,
card.poster,
card.episode,
card.season,
card.id,
0,
card.rating,
card.descript,
System.currentTimeMillis(),
)
) {
if (it.action == DOWNLOAD_ACTION_DOWNLOAD) {

View file

@ -155,12 +155,4 @@ object AppUtils {
}
return currentAudioFocusRequest
}
/** https://www.imdb.com/title/tt2861424/ -> tt2861424 */
fun imdbUrlToId(url: String): String {
return url
.removePrefix("https://www.imdb.com/title/")
.removePrefix("https://imdb.com/title/tt2861424/")
.replace("/", "")
}
}

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_activated="true" android:color="?attr/textColor"/>
<item android:color="@color/transparent"/>
</selector>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_activated="true" android:color="?attr/textColor"/>
<item android:color="?attr/grayTextColor"/>
</selector>

View file

@ -1,4 +1,5 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
<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:layout_width="match_parent"
@ -8,7 +9,8 @@
android:screenOrientation="landscape"
tools:orientation="vertical"
>
<View android:layout_width="match_parent"
<View
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/shadow_overlay"
android:background="@color/black_overlay"
@ -383,7 +385,7 @@
android:textColor="@android:color/white"
android:textSize="14sp"
android:textStyle="normal"/>
<!--app:buffered_color="@color/videoCache"-->
<!--app:buffered_color="@color/videoCache"-->
<com.google.android.exoplayer2.ui.DefaultTimeBar
android:id="@id/exo_progress"
android:layout_width="0dp"
@ -471,7 +473,8 @@
</LinearLayout>
</androidx.cardview.widget.CardView>
<LinearLayout android:id="@+id/lock_holder" android:orientation="horizontal" android:layout_width="wrap_content" android:layout_height="match_parent">
<LinearLayout android:id="@+id/lock_holder" android:orientation="horizontal"
android:layout_width="wrap_content" android:layout_height="match_parent">
<androidx.cardview.widget.CardView
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"

View file

@ -0,0 +1,131 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:background="@null"
android:layout_height="match_parent">
<LinearLayout
android:orientation="horizontal"
android:layout_marginBottom="60dp"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:baselineAligned="false">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_weight="50">
<TextView
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
android:layout_marginTop="20dp"
android:layout_marginBottom="10dp"
android:textStyle="bold"
android:text="@string/pick_source"
android:textSize="20sp"
android:textColor="?attr/textColor"
android:layout_width="match_parent"
android:layout_rowWeight="1"
android:layout_height="wrap_content">
</TextView>
<ListView
android:layout_marginTop="-10dp"
android:paddingTop="10dp"
android:id="@+id/sort_providers"
android:background="?attr/bitDarkerGrayBackground"
tools:listitem="@layout/sort_bottom_single_choice"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_rowWeight="1"
/>
</LinearLayout>
<LinearLayout
android:id="@+id/sort_subtitles_holder"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_weight="50">
<TextView
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
android:layout_marginTop="20dp"
android:layout_marginBottom="10dp"
android:textStyle="bold"
android:text="@string/pick_subtitle"
android:textSize="20sp"
android:textColor="?attr/textColor"
android:layout_rowWeight="1"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</TextView>
<ListView
android:layout_marginTop="-10dp"
android:paddingTop="10dp"
android:id="@+id/sort_subtitles"
android:background="?attr/bitDarkerGrayBackground"
tools:listitem="@layout/sort_bottom_single_choice"
android:layout_width="match_parent"
android:layout_rowWeight="1"
android:layout_height="match_parent">
</ListView>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_gravity="bottom"
android:gravity="bottom|end"
android:layout_marginTop="-60dp"
android:layout_width="match_parent"
android:layout_height="60dp">
<com.google.android.material.button.MaterialButton
android:layout_gravity="center_vertical|end"
android:layout_height="50dp"
android:layout_margin="5dp"
android:visibility="visible"
android:textStyle="bold"
app:rippleColor="?attr/grayBackground"
android:textColor="?attr/grayBackground"
app:iconTint="?attr/grayBackground"
android:textAllCaps="false"
app:iconGravity="textStart"
app:strokeColor="?attr/grayBackground"
app:backgroundTint="?attr/textColor"
app:iconSize="20dp"
android:text="@string/sort_apply"
android:id="@+id/pick_source_apply"
android:textSize="15sp"
app:cornerRadius="5dp"
android:layout_width="wrap_content"
>
</com.google.android.material.button.MaterialButton>
<com.google.android.material.button.MaterialButton
android:layout_gravity="center_vertical|end"
android:layout_height="50dp"
android:layout_margin="5dp"
app:iconGravity="textStart"
app:strokeColor="?attr/textColor"
android:backgroundTint="?attr/grayBackground"
app:rippleColor="?attr/textColor"
android:textColor="?attr/textColor"
app:iconTint="?attr/textColor"
android:textAllCaps="false"
android:textStyle="bold"
app:iconSize="20dp"
android:text="@string/sort_cancel"
android:id="@+id/pick_source_cancel"
android:textSize="15sp"
app:cornerRadius="5dp"
android:layout_width="wrap_content"
>
</com.google.android.material.button.MaterialButton>
</LinearLayout>
</LinearLayout>

View file

@ -22,11 +22,11 @@
android:layout_height="wrap_content">
<!--app:cardCornerRadius="@dimen/roundedImageRadius"-->
<androidx.cardview.widget.CardView
android:layout_width="126dp"
android:layout_height="72dp"
>
<ImageView
android:foreground="?android:attr/selectableItemBackground"
android:id="@+id/episode_poster"
tools:src="@drawable/example_poster"
android:scaleType="centerCrop"

View file

@ -1,4 +1,4 @@
<CheckedTextView
<!--<CheckedTextView
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"
android:id="@android:id/text1"
android:layout_width="match_parent"
@ -11,3 +11,27 @@
android:checkMark="?android:attr/listChoiceIndicatorSingle"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"/>
-->
<TextView
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"
style="@style/AppTextViewStyle"
android:id="@android:id/text1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?android:attr/listPreferredItemHeightSmall"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="@color/text_selection_color"
android:textSize="16sp"
android:textStyle="bold"
android:gravity="center_vertical"
android:paddingStart="12dp"
android:paddingEnd="7dip"
tools:text="TEST"
android:checkMark="?android:attr/listChoiceIndicatorSingle"
android:ellipsize="marquee"
android:drawableStart="@drawable/ic_baseline_check_24"
android:drawableTint="@color/check_selection_color"
tools:drawableTint="?attr/textColor"
android:drawablePadding="20dp"
/>

View file

@ -66,4 +66,7 @@
<string name="filter_bookmarks">Filter Bookmarks</string>
<string name="error_bookmarks_text">Bookmarks</string>
<string name="action_remove_from_bookmarks">Remove</string>
<string name="play_episode_toast">Play Episode</string>
<string name="sort_apply">Apply</string>
<string name="sort_cancel">Cancel</string>
</resources>