Merge remote-tracking branch 'origin/master'

This commit is contained in:
Blatzar 2022-07-17 22:08:54 +02:00
commit e78e068214
19 changed files with 591 additions and 534 deletions

View file

@ -36,7 +36,7 @@ android {
targetSdkVersion 30 targetSdkVersion 30
versionCode 48 versionCode 48
versionName "2.10.30" versionName "2.10.31"
resValue "string", "app_version", resValue "string", "app_version",
"${defaultConfig.versionName}${versionNameSuffix ?: ""}" "${defaultConfig.versionName}${versionNameSuffix ?: ""}"

View file

@ -8,7 +8,7 @@ import com.lagradost.cloudstream3.utils.loadExtractor
import org.jsoup.Jsoup import org.jsoup.Jsoup
class HDMovie5 : MainAPI() { class HDMovie5 : MainAPI() {
override var mainUrl = "https://hdmovie2.biz" override var mainUrl = "https://Hdmovie2.biz"
override var name = "HDMovie" override var name = "HDMovie"
override var lang = "hi" override var lang = "hi"

View file

@ -2,8 +2,11 @@ package com.lagradost.cloudstream3.movieproviders
import android.util.Log import android.util.Log
import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.APIHolder.getCaptchaToken import com.lagradost.cloudstream3.APIHolder.getCaptchaToken
import com.lagradost.cloudstream3.SubtitleFile
import com.lagradost.cloudstream3.TvType
import com.lagradost.cloudstream3.apmap
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.metaproviders.TmdbLink import com.lagradost.cloudstream3.metaproviders.TmdbLink
import com.lagradost.cloudstream3.metaproviders.TmdbProvider import com.lagradost.cloudstream3.metaproviders.TmdbProvider
import com.lagradost.cloudstream3.movieproviders.SflixProvider.Companion.extractRabbitStream import com.lagradost.cloudstream3.movieproviders.SflixProvider.Companion.extractRabbitStream
@ -15,7 +18,7 @@ import com.lagradost.cloudstream3.utils.loadExtractor
class TwoEmbedProvider : TmdbProvider() { class TwoEmbedProvider : TmdbProvider() {
override val apiName = "2Embed" override val apiName = "2Embed"
override var name = "2Embed" override var name = "2Embed"
override var mainUrl = "https://www.2embed.to" override var mainUrl = "https://2embed.org"
override val useMetaLoadResponse = true override val useMetaLoadResponse = true
override val instantLinkLoading = false override val instantLinkLoading = false
override val supportedTypes = setOf( override val supportedTypes = setOf(
@ -43,10 +46,10 @@ class TwoEmbedProvider : TmdbProvider() {
) else listOf(mappedData.tmdbID.toString(), "tmdb") ) else listOf(mappedData.tmdbID.toString(), "tmdb")
val isMovie = mappedData.episode == null && mappedData.season == null val isMovie = mappedData.episode == null && mappedData.season == null
val embedUrl = if (isMovie) { val embedUrl = if (isMovie) {
"$mainUrl/embed/$site/movie?id=$id" "$mainUrl/embed/movie?$site=$id"
} else { } else {
val suffix = "$id&s=${mappedData.season ?: 1}&e=${mappedData.episode ?: 1}" val suffix = "$id&sea=${mappedData.season ?: 1}&epi=${mappedData.episode ?: 1}"
"$mainUrl/embed/$site/tv?id=$suffix" "$mainUrl/embed/series?$site=$suffix"
} }
val document = app.get(embedUrl).document val document = app.get(embedUrl).document

View file

@ -12,21 +12,40 @@ import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.utils.UIHelper.adjustAlpha import com.lagradost.cloudstream3.utils.UIHelper.adjustAlpha
import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute
import com.lagradost.cloudstream3.utils.UIHelper.toPx import com.lagradost.cloudstream3.utils.UIHelper.toPx
import java.lang.ref.WeakReference
class MyMiniControllerFragment : MiniControllerFragment() { class MyMiniControllerFragment : MiniControllerFragment() {
var currentColor: Int = 0 var currentColor: Int = 0
// I KNOW, KINDA SPAGHETTI SOLUTION, BUT IT WORKS override fun onDestroy() {
override fun onInflate(context: Context, attributeSet: AttributeSet, bundle: Bundle?) { currentColor = 0
val obtainStyledAttributes = context.obtainStyledAttributes(attributeSet, R.styleable.CustomCast, 0, 0) super.onDestroy()
if (obtainStyledAttributes.hasValue(R.styleable.CustomCast_customCastBackgroundColor)) {
currentColor = obtainStyledAttributes.getColor(R.styleable.CustomCast_customCastBackgroundColor, 0)
}
obtainStyledAttributes.recycle()
super.onInflate(context, attributeSet, bundle)
} }
// I KNOW, KINDA SPAGHETTI SOLUTION, BUT IT WORKS
override fun onInflate(context: Context, attributeSet: AttributeSet, bundle: Bundle?) {
super.onInflate(context, attributeSet, bundle)
// somehow this leaks and I really dont know why, it seams like if you go back to a fragment with this, it leaks????
if (currentColor == 0) {
WeakReference(
context.obtainStyledAttributes(
attributeSet,
R.styleable.CustomCast
)
).apply {
if (get()
?.hasValue(R.styleable.CustomCast_customCastBackgroundColor) == true
) {
currentColor =
get()
?.getColor(R.styleable.CustomCast_customCastBackgroundColor, 0) ?: 0
}
get()?.recycle()
}.clear()
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
@ -38,18 +57,24 @@ class MyMiniControllerFragment : MiniControllerFragment() {
val containerCurrent: RelativeLayout? = view.findViewById(R.id.container_current) val containerCurrent: RelativeLayout? = view.findViewById(R.id.container_current)
context?.let { ctx -> context?.let { ctx ->
progressBar?.setBackgroundColor(adjustAlpha(ctx.colorFromAttribute(R.attr.colorPrimary), 0.35f)) progressBar?.setBackgroundColor(
val params = RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, 2.toPx) adjustAlpha(
ctx.colorFromAttribute(R.attr.colorPrimary),
0.35f
)
)
val params = RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.MATCH_PARENT,
2.toPx
)
progressBar?.layoutParams = params progressBar?.layoutParams = params
val color = currentColor
if (currentColor != 0) { if (color != 0)
containerCurrent?.setBackgroundColor(currentColor) containerCurrent?.setBackgroundColor(color)
}
} }
val child = containerAll?.getChildAt(0) val child = containerAll?.getChildAt(0)
child?.alpha = 0f // REMOVE GRADIENT child?.alpha = 0f // REMOVE GRADIENT
} catch (e: Exception) { } catch (e: Exception) {
// JUST IN CASE // JUST IN CASE
} }

View file

@ -31,12 +31,8 @@ class DownloadChildFragment : Fragment() {
override fun onDestroyView() { override fun onDestroyView() {
(download_child_list?.adapter as DownloadChildAdapter?)?.killAdapter() (download_child_list?.adapter as DownloadChildAdapter?)?.killAdapter()
super.onDestroyView()
}
override fun onDestroy() {
downloadDeleteEventListener?.let { VideoDownloadManager.downloadDeleteEvent -= it } downloadDeleteEventListener?.let { VideoDownloadManager.downloadDeleteEvent -= it }
super.onDestroy() super.onDestroyView()
} }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {

View file

@ -65,16 +65,12 @@ class DownloadFragment : Fragment() {
} }
override fun onDestroyView() { override fun onDestroyView() {
(download_list?.adapter as DownloadHeaderAdapter?)?.killAdapter()
super.onDestroyView()
}
override fun onDestroy() {
if (downloadDeleteEventListener != null) { if (downloadDeleteEventListener != null) {
VideoDownloadManager.downloadDeleteEvent -= downloadDeleteEventListener!! VideoDownloadManager.downloadDeleteEvent -= downloadDeleteEventListener!!
downloadDeleteEventListener = null downloadDeleteEventListener = null
} }
super.onDestroy() (download_list?.adapter as DownloadHeaderAdapter?)?.killAdapter()
super.onDestroyView()
} }
override fun onCreateView( override fun onCreateView(
@ -83,7 +79,17 @@ class DownloadFragment : Fragment() {
savedInstanceState: Bundle? savedInstanceState: Bundle?
): View? { ): View? {
downloadsViewModel = downloadsViewModel =
ViewModelProvider(this).get(DownloadViewModel::class.java) ViewModelProvider(this)[DownloadViewModel::class.java]
return inflater.inflate(R.layout.fragment_downloads, container, false)
}
private var downloadDeleteEventListener: ((Int) -> Unit)? = null
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
hideKeyboard()
observe(downloadsViewModel.noDownloadsText) { observe(downloadsViewModel.noDownloadsText) {
text_no_downloads.text = it text_no_downloads.text = it
} }
@ -114,16 +120,8 @@ class DownloadFragment : Fragment() {
getBytesAsText(it) getBytesAsText(it)
) )
download_app?.setLayoutWidth(it) download_app?.setLayoutWidth(it)
download_storage_appbar?.visibility = View.VISIBLE download_storage_appbar?.isVisible = it > 0
} }
return inflater.inflate(R.layout.fragment_downloads, container, false)
}
private var downloadDeleteEventListener: ((Int) -> Unit)? = null
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
hideKeyboard()
val adapter: RecyclerView.Adapter<RecyclerView.ViewHolder> = val adapter: RecyclerView.Adapter<RecyclerView.ViewHolder> =
DownloadHeaderAdapter( DownloadHeaderAdapter(

View file

@ -8,6 +8,7 @@ import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.lagradost.cloudstream3.isMovieType import com.lagradost.cloudstream3.isMovieType
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.utils.DOWNLOAD_EPISODE_CACHE import com.lagradost.cloudstream3.utils.DOWNLOAD_EPISODE_CACHE
import com.lagradost.cloudstream3.utils.DOWNLOAD_HEADER_CACHE import com.lagradost.cloudstream3.utils.DOWNLOAD_HEADER_CACHE
import com.lagradost.cloudstream3.utils.DataStore.getFolderName import com.lagradost.cloudstream3.utils.DataStore.getFolderName
@ -100,7 +101,7 @@ class DownloadViewModel : ViewModel() {
(it.child?.episode ?: 0) + (it.child?.season?.times(10000) ?: 0) (it.child?.episode ?: 0) + (it.child?.season?.times(10000) ?: 0)
} // episode sorting by episode, lowest to highest } // episode sorting by episode, lowest to highest
} }
try {
val stat = StatFs(Environment.getExternalStorageDirectory().path) val stat = StatFs(Environment.getExternalStorageDirectory().path)
val localBytesAvailable = stat.availableBytes//stat.blockSizeLong * stat.blockCountLong val localBytesAvailable = stat.availableBytes//stat.blockSizeLong * stat.blockCountLong
@ -110,6 +111,10 @@ class DownloadViewModel : ViewModel() {
_usedBytes.postValue(localTotalBytes - localBytesAvailable - localDownloadedBytes) _usedBytes.postValue(localTotalBytes - localBytesAvailable - localDownloadedBytes)
_availableBytes.postValue(localBytesAvailable) _availableBytes.postValue(localBytesAvailable)
_downloadBytes.postValue(localDownloadedBytes) _downloadBytes.postValue(localDownloadedBytes)
} catch (e : Exception) {
_downloadBytes.postValue(0)
logError(e)
}
_headerCards.postValue(visual) _headerCards.postValue(visual)
} }

View file

@ -20,8 +20,13 @@ class EasyDownloadButton : IDisposable {
val id: Int val id: Int
} }
private var _clickCallback: ((DownloadClickEvent) -> Unit)? = null
private var _imageChangeCallback: ((Pair<Int, String>) -> Unit)? = null
override fun dispose() { override fun dispose() {
try { try {
_clickCallback = null
_imageChangeCallback = null
downloadProgressEventListener?.let { VideoDownloadManager.downloadProgressEvent -= it } downloadProgressEventListener?.let { VideoDownloadManager.downloadProgressEvent -= it }
downloadStatusEventListener?.let { VideoDownloadManager.downloadStatusEvent -= it } downloadStatusEventListener?.let { VideoDownloadManager.downloadStatusEvent -= it }
} catch (e: Exception) { } catch (e: Exception) {
@ -119,6 +124,8 @@ class EasyDownloadButton : IDisposable {
clickCallback: (DownloadClickEvent) -> Unit, clickCallback: (DownloadClickEvent) -> Unit,
isTextPercentage: Boolean = false isTextPercentage: Boolean = false
) { ) {
_clickCallback = clickCallback
_imageChangeCallback = downloadImageChangeCallback
var lastState: VideoDownloadManager.DownloadType? = null var lastState: VideoDownloadManager.DownloadType? = null
var currentBytes = setupCurrentBytes ?: 0 var currentBytes = setupCurrentBytes ?: 0
var totalBytes = setupTotalBytes ?: 0 var totalBytes = setupTotalBytes ?: 0
@ -142,7 +149,7 @@ class EasyDownloadButton : IDisposable {
} else { } else {
Pair(R.drawable.netflix_download, R.string.download) Pair(R.drawable.netflix_download, R.string.download)
} }
downloadImageChangeCallback.invoke( _imageChangeCallback?.invoke(
Pair( Pair(
img.first, img.first,
downloadView.context.getString(img.second) downloadView.context.getString(img.second)
@ -223,7 +230,7 @@ class EasyDownloadButton : IDisposable {
downloadView.setOnClickListener { downloadView.setOnClickListener {
if (currentBytes <= 0) { if (currentBytes <= 0) {
clickCallback.invoke(DownloadClickEvent(DOWNLOAD_ACTION_DOWNLOAD, data)) _clickCallback?.invoke(DownloadClickEvent(DOWNLOAD_ACTION_DOWNLOAD, data))
} else { } else {
val list = arrayListOf( val list = arrayListOf(
Pair(DOWNLOAD_ACTION_PLAY_FILE, R.string.popup_play_file), Pair(DOWNLOAD_ACTION_PLAY_FILE, R.string.popup_play_file),
@ -243,7 +250,7 @@ class EasyDownloadButton : IDisposable {
it.popupMenuNoIcons( it.popupMenuNoIcons(
list list
) { ) {
clickCallback.invoke(DownloadClickEvent(itemId, data)) _clickCallback?.invoke(DownloadClickEvent(itemId, data))
} }
} }
} }

View file

@ -350,6 +350,7 @@ abstract class AbstractPlayerFragment(
resizeMode = getKey(RESIZE_MODE_KEY) ?: 0 resizeMode = getKey(RESIZE_MODE_KEY) ?: 0
resize(resizeMode, false) resize(resizeMode, false)
player.releaseCallbacks()
player.initCallbacks( player.initCallbacks(
playerUpdated = ::playerUpdated, playerUpdated = ::playerUpdated,
updateIsPlaying = ::updateIsPlaying, updateIsPlaying = ::updateIsPlaying,

View file

@ -108,6 +108,21 @@ class CS3IPlayer : IPlayer {
private var playerUpdated: ((Any?) -> Unit)? = null private var playerUpdated: ((Any?) -> Unit)? = null
private var embeddedSubtitlesFetched: ((List<SubtitleData>) -> Unit)? = null private var embeddedSubtitlesFetched: ((List<SubtitleData>) -> Unit)? = null
override fun releaseCallbacks() {
playerUpdated = null
updateIsPlaying = null
requestAutoFocus = null
playerError = null
playerDimensionsLoaded = null
requestedListeningPercentages = null
playerPositionChanged = null
nextEpisode = null
prevEpisode = null
subtitlesUpdates = null
embeddedSubtitlesFetched = null
requestSubtitleUpdate = null
}
override fun initCallbacks( override fun initCallbacks(
playerUpdated: (Any?) -> Unit, playerUpdated: (Any?) -> Unit,
updateIsPlaying: ((Pair<CSPlayerLoading, CSPlayerLoading>) -> Unit)?, updateIsPlaying: ((Pair<CSPlayerLoading, CSPlayerLoading>) -> Unit)?,

View file

@ -315,6 +315,8 @@ open class FullScreenPlayer : AbstractPlayerFragment() {
override fun onDestroy() { override fun onDestroy() {
exitFullscreen() exitFullscreen()
player.release()
player.releaseCallbacks()
super.onDestroy() super.onDestroy()
} }
@ -1104,7 +1106,6 @@ open class FullScreenPlayer : AbstractPlayerFragment() {
@SuppressLint("ClickableViewAccessibility") @SuppressLint("ClickableViewAccessibility")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
// init variables // init variables
setPlayBackSpeed(getKey(PLAYBACK_SPEED_KEY) ?: 1.0f) setPlayBackSpeed(getKey(PLAYBACK_SPEED_KEY) ?: 1.0f)

View file

@ -359,12 +359,12 @@ class GeneratorPlayer : FullScreenPlayer() {
} }
}) })
dialog.search_filter.setOnClickListener { dialog.search_filter.setOnClickListener { view ->
val lang639_1 = languages.map { it.ISO_639_1 } val lang639_1 = languages.map { it.ISO_639_1 }
activity?.showDialog( activity?.showDialog(
languages.map { it.languageName }, languages.map { it.languageName },
lang639_1.indexOf(currentLanguageTwoLetters), lang639_1.indexOf(currentLanguageTwoLetters),
context.getString(R.string.subs_subtitle_languages), view?.context?.getString(R.string.subs_subtitle_languages) ?: return@setOnClickListener,
true, true,
{ } { }
) { index -> ) { index ->
@ -609,7 +609,7 @@ class GeneratorPlayer : FullScreenPlayer() {
val currentPrefMedia = val currentPrefMedia =
settingsManager.getString( settingsManager.getString(
getString(R.string.subtitles_encoding_key), ctx.getString(R.string.subtitles_encoding_key),
null null
) )

View file

@ -88,6 +88,7 @@ interface IPlayer {
subtitlesUpdates: (() -> Unit)? = null, // callback from player to inform that subtitles have updated in some way subtitlesUpdates: (() -> Unit)? = null, // callback from player to inform that subtitles have updated in some way
embeddedSubtitlesFetched: ((List<SubtitleData>) -> Unit)? = null, // callback from player to give all embedded subtitles embeddedSubtitlesFetched: ((List<SubtitleData>) -> Unit)? = null, // callback from player to give all embedded subtitles
) )
fun releaseCallbacks()
fun updateSubtitleStyle(style: SaveCaptionStyle) fun updateSubtitleStyle(style: SaveCaptionStyle)
fun saveData() fun saveData()

View file

@ -30,8 +30,6 @@ import androidx.core.widget.NestedScrollView
import androidx.core.widget.doOnTextChanged import androidx.core.widget.doOnTextChanged
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.discord.panels.OverlappingPanelsLayout import com.discord.panels.OverlappingPanelsLayout
import com.discord.panels.PanelsChildGestureRegionObserver import com.discord.panels.PanelsChildGestureRegionObserver
import com.google.android.gms.cast.framework.CastButtonFactory import com.google.android.gms.cast.framework.CastButtonFactory
@ -49,10 +47,8 @@ import com.lagradost.cloudstream3.CommonActivity.showToast
import com.lagradost.cloudstream3.mvvm.* import com.lagradost.cloudstream3.mvvm.*
import com.lagradost.cloudstream3.syncproviders.providers.Kitsu import com.lagradost.cloudstream3.syncproviders.providers.Kitsu
import com.lagradost.cloudstream3.ui.WatchType import com.lagradost.cloudstream3.ui.WatchType
import com.lagradost.cloudstream3.ui.download.DOWNLOAD_ACTION_DOWNLOAD import com.lagradost.cloudstream3.ui.download.*
import com.lagradost.cloudstream3.ui.download.DOWNLOAD_NAVIGATE_TO
import com.lagradost.cloudstream3.ui.download.DownloadButtonSetup.handleDownloadClick import com.lagradost.cloudstream3.ui.download.DownloadButtonSetup.handleDownloadClick
import com.lagradost.cloudstream3.ui.download.EasyDownloadButton
import com.lagradost.cloudstream3.ui.player.CSPlayerEvent import com.lagradost.cloudstream3.ui.player.CSPlayerEvent
import com.lagradost.cloudstream3.ui.player.GeneratorPlayer import com.lagradost.cloudstream3.ui.player.GeneratorPlayer
import com.lagradost.cloudstream3.ui.player.RepoLinkGenerator import com.lagradost.cloudstream3.ui.player.RepoLinkGenerator
@ -478,17 +474,21 @@ class ResultFragment : ResultTrailerPlayer() {
} }
override fun onDestroyView() { override fun onDestroyView() {
updateUIListener = null
(result_episodes?.adapter as EpisodeAdapter?)?.killAdapter() (result_episodes?.adapter as EpisodeAdapter?)?.killAdapter()
downloadButton?.dispose()
//somehow this still leaks and I dont know why????
// todo look at https://github.com/discord/OverlappingPanels/blob/70b4a7cf43c6771873b1e091029d332896d41a1a/sample_app/src/main/java/com/discord/sampleapp/MainActivity.kt
PanelsChildGestureRegionObserver.Provider.get().removeGestureRegionsUpdateListener(this)
result_cast_items?.let {
PanelsChildGestureRegionObserver.Provider.get().unregister(it)
}
super.onDestroyView() super.onDestroyView()
} }
override fun onDestroy() { override fun onDestroy() {
//requireActivity().viewModelStore.clear() // REMEMBER THE CLEAR //requireActivity().viewModelStore.clear() // REMEMBER THE CLEAR
downloadButton?.dispose()
updateUIListener = null
result_cast_items?.let {
PanelsChildGestureRegionObserver.Provider.get().unregister(it)
}
super.onDestroy() super.onDestroy()
} }
@ -625,6 +625,42 @@ class ResultFragment : ResultTrailerPlayer() {
} }
} }
private fun handleDownloadButton(downloadClickEvent: DownloadClickEvent) {
if (downloadClickEvent.action == DOWNLOAD_ACTION_DOWNLOAD) {
currentEpisodes?.firstOrNull()?.let { episode ->
handleAction(
EpisodeClickEvent(
ACTION_DOWNLOAD_EPISODE,
ResultEpisode(
currentHeaderName ?: return@let,
currentHeaderName,
null,
0,
null,
episode.data,
apiName,
currentId ?: return@let,
0,
0L,
0L,
null,
null,
null,
currentType ?: return@let,
currentId ?: return@let,
)
)
)
}
} else {
DownloadButtonSetup.handleDownloadClick(
activity,
currentHeaderName,
downloadClickEvent
)
}
}
private fun loadTrailer(index: Int? = null) { private fun loadTrailer(index: Int? = null) {
val isSuccess = val isSuccess =
currentTrailers.getOrNull(index ?: currentTrailerIndex)?.let { trailer -> currentTrailers.getOrNull(index ?: currentTrailerIndex)?.let { trailer ->
@ -810,109 +846,8 @@ class ResultFragment : ResultTrailerPlayer() {
viewModel.reloadEpisodes() viewModel.reloadEpisodes()
} }
@SuppressLint("SetTextI18n") var apiName: String = ""
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { private fun handleAction(episodeClick: EpisodeClickEvent): Job = main {
super.onViewCreated(view, savedInstanceState)
result_cast_items?.let {
PanelsChildGestureRegionObserver.Provider.get().register(it)
}
result_cast_items?.adapter = ActorAdaptor()
fixGrid()
result_recommendations?.spanCount = 3
result_overlapping_panels?.setStartPanelLockState(OverlappingPanelsLayout.LockState.CLOSE)
result_overlapping_panels?.setEndPanelLockState(OverlappingPanelsLayout.LockState.CLOSE)
player_open_source?.setOnClickListener {
currentTrailers.getOrNull(currentTrailerIndex)?.let {
context?.openBrowser(it.url)
}
}
updateUIListener = ::updateUI
val restart = arguments?.getBoolean(RESTART_BUNDLE) ?: false
if (restart) {
arguments?.putBoolean(RESTART_BUNDLE, false)
}
activity?.window?.decorView?.clearFocus()
hideKeyboard()
context?.updateHasTrailers()
activity?.loadCache()
activity?.fixPaddingStatusbar(result_top_bar)
//activity?.fixPaddingStatusbar(result_barstatus)
/* val backParameter = result_back.layoutParams as FrameLayout.LayoutParams
backParameter.setMargins(
backParameter.leftMargin,
backParameter.topMargin + requireContext().getStatusBarHeight(),
backParameter.rightMargin,
backParameter.bottomMargin
)
result_back.layoutParams = backParameter*/
// activity?.fixPaddingStatusbar(result_toolbar)
url = arguments?.getString(URL_BUNDLE)
val apiName = arguments?.getString(API_NAME_BUNDLE) ?: return
startAction = arguments?.getInt(START_ACTION_BUNDLE) ?: START_ACTION_NORMAL
startValue = arguments?.getInt(START_VALUE_BUNDLE)
val resumeEpisode = arguments?.getInt(EPISODE_BUNDLE)
val resumeSeason = arguments?.getInt(SEASON_BUNDLE)
syncModel.addFromUrl(url)
val api = getApiFromName(apiName)
if (media_route_button != null) {
val chromecastSupport = api.hasChromecastSupport
media_route_button?.alpha = if (chromecastSupport) 1f else 0.3f
if (!chromecastSupport) {
media_route_button?.setOnClickListener {
showToast(activity, R.string.no_chromecast_support_toast, Toast.LENGTH_LONG)
}
}
activity?.let { act ->
if (act.isCastApiAvailable()) {
try {
CastButtonFactory.setUpMediaRouteButton(act, media_route_button)
val castContext = CastContext.getSharedInstance(act.applicationContext)
media_route_button?.isGone =
castContext.castState == CastState.NO_DEVICES_AVAILABLE
castContext.addCastStateListener { state ->
media_route_button?.isGone = state == CastState.NO_DEVICES_AVAILABLE
}
} catch (e: Exception) {
logError(e)
}
}
}
}
result_scroll.setOnScrollChangeListener(NestedScrollView.OnScrollChangeListener { _, _, scrollY, _, oldScrollY ->
val dy = scrollY - oldScrollY
if (dy > 0) { //check for scroll down
result_bookmark_fab?.shrink()
} else if (dy < -5) {
result_bookmark_fab?.extend()
}
if (!isFullScreenPlayer && player.getIsPlaying()) {
if (scrollY > (player_background?.height ?: scrollY)) {
player.handleEvent(CSPlayerEvent.Pause)
}
}
//result_poster_blur_holder?.translationY = -scrollY.toFloat()
})
result_back.setOnClickListener {
activity?.popCurrentPage()
}
fun handleAction(episodeClick: EpisodeClickEvent): Job = main {
if (episodeClick.action == ACTION_DOWNLOAD_EPISODE) { if (episodeClick.action == ACTION_DOWNLOAD_EPISODE) {
val isMovie = currentIsMovie ?: return@main val isMovie = currentIsMovie ?: return@main
val headerName = currentHeaderName ?: return@main val headerName = currentHeaderName ?: return@main
@ -1126,7 +1061,7 @@ class ResultFragment : ResultTrailerPlayer() {
val verifiedOptions = ArrayList<String>() val verifiedOptions = ArrayList<String>()
val verifiedOptionsValues = ArrayList<Int>() val verifiedOptionsValues = ArrayList<Int>()
val hasDownloadSupport = api.hasDownloadSupport val hasDownloadSupport = getApiFromName(apiName).hasDownloadSupport
for (i in options.indices) { for (i in options.indices) {
val opv = optionsValues[i] val opv = optionsValues[i]
@ -1309,7 +1244,106 @@ class ResultFragment : ResultTrailerPlayer() {
} }
} }
val adapter: RecyclerView.Adapter<RecyclerView.ViewHolder> = @SuppressLint("SetTextI18n")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
result_cast_items?.let {
PanelsChildGestureRegionObserver.Provider.get().register(it)
}
//result_cast_items?.adapter = ActorAdaptor()
fixGrid()
result_recommendations?.spanCount = 3
result_overlapping_panels?.setStartPanelLockState(OverlappingPanelsLayout.LockState.CLOSE)
result_overlapping_panels?.setEndPanelLockState(OverlappingPanelsLayout.LockState.CLOSE)
player_open_source?.setOnClickListener {
currentTrailers.getOrNull(currentTrailerIndex)?.let {
context?.openBrowser(it.url)
}
}
updateUIListener = ::updateUI
val restart = arguments?.getBoolean(RESTART_BUNDLE) ?: false
if (restart) {
arguments?.putBoolean(RESTART_BUNDLE, false)
}
activity?.window?.decorView?.clearFocus()
hideKeyboard()
context?.updateHasTrailers()
activity?.loadCache()
activity?.fixPaddingStatusbar(result_top_bar)
//activity?.fixPaddingStatusbar(result_barstatus)
/* val backParameter = result_back.layoutParams as FrameLayout.LayoutParams
backParameter.setMargins(
backParameter.leftMargin,
backParameter.topMargin + requireContext().getStatusBarHeight(),
backParameter.rightMargin,
backParameter.bottomMargin
)
result_back.layoutParams = backParameter*/
// activity?.fixPaddingStatusbar(result_toolbar)
url = arguments?.getString(URL_BUNDLE)
apiName = arguments?.getString(API_NAME_BUNDLE) ?: return
startAction = arguments?.getInt(START_ACTION_BUNDLE) ?: START_ACTION_NORMAL
startValue = arguments?.getInt(START_VALUE_BUNDLE)
val resumeEpisode = arguments?.getInt(EPISODE_BUNDLE)
val resumeSeason = arguments?.getInt(SEASON_BUNDLE)
syncModel.addFromUrl(url)
val api = getApiFromName(apiName)
if (media_route_button != null) {
val chromecastSupport = api.hasChromecastSupport
media_route_button?.alpha = if (chromecastSupport) 1f else 0.3f
if (!chromecastSupport) {
media_route_button?.setOnClickListener {
showToast(activity, R.string.no_chromecast_support_toast, Toast.LENGTH_LONG)
}
}
activity?.let { act ->
if (act.isCastApiAvailable()) {
try {
CastButtonFactory.setUpMediaRouteButton(act, media_route_button)
val castContext = CastContext.getSharedInstance(act.applicationContext)
media_route_button?.isGone =
castContext.castState == CastState.NO_DEVICES_AVAILABLE
// this shit leaks for some reason
//castContext.addCastStateListener { state ->
// media_route_button?.isGone = state == CastState.NO_DEVICES_AVAILABLE
//}
} catch (e: Exception) {
logError(e)
}
}
}
}
result_scroll.setOnScrollChangeListener(NestedScrollView.OnScrollChangeListener { _, _, scrollY, _, oldScrollY ->
val dy = scrollY - oldScrollY
if (dy > 0) { //check for scroll down
result_bookmark_fab?.shrink()
} else if (dy < -5) {
result_bookmark_fab?.extend()
}
if (!isFullScreenPlayer && player.getIsPlaying()) {
if (scrollY > (player_background?.height ?: scrollY)) {
player.handleEvent(CSPlayerEvent.Pause)
}
}
//result_poster_blur_holder?.translationY = -scrollY.toFloat()
})
result_back.setOnClickListener {
activity?.popCurrentPage()
}
result_episodes.adapter =
EpisodeAdapter( EpisodeAdapter(
ArrayList(), ArrayList(),
api.hasDownloadSupport, api.hasDownloadSupport,
@ -1321,9 +1355,6 @@ class ResultFragment : ResultTrailerPlayer() {
} }
) )
result_episodes.adapter = adapter
result_episodes.layoutManager = GridLayoutManager(context, 1)
result_bookmark_button.setOnClickListener { result_bookmark_button.setOnClickListener {
it.popupMenuNoIcons( it.popupMenuNoIcons(
items = WatchType.values() items = WatchType.values()
@ -1467,7 +1498,8 @@ class ResultFragment : ResultTrailerPlayer() {
} }
} }
} }
val imgAdapter = ImageAdapter(
result_mini_sync?.adapter = ImageAdapter(
R.layout.result_mini_image, R.layout.result_mini_image,
nextFocusDown = R.id.result_sync_set_score, nextFocusDown = R.id.result_sync_set_score,
clickCallback = { action -> clickCallback = { action ->
@ -1479,7 +1511,6 @@ class ResultFragment : ResultTrailerPlayer() {
} }
} }
}) })
result_mini_sync?.adapter = imgAdapter
observe(syncModel.synced) { list -> observe(syncModel.synced) { list ->
result_sync_names?.text = result_sync_names?.text =
@ -1638,7 +1669,7 @@ class ResultFragment : ResultTrailerPlayer() {
?.let { ?.let {
result_play_movie?.text = it result_play_movie?.text = it
} }
println("startAction = $startAction") //println("startAction = $startAction")
when (startAction) { when (startAction) {
START_ACTION_RESUME_LATEST -> { START_ACTION_RESUME_LATEST -> {
@ -1738,7 +1769,7 @@ class ResultFragment : ResultTrailerPlayer() {
} }
} }
result_cast_items?.setOnFocusChangeListener { v, hasFocus -> result_cast_items?.setOnFocusChangeListener { _, hasFocus ->
// Always escape focus // Always escape focus
if (hasFocus) result_bookmark_button?.requestFocus() if (hasFocus) result_bookmark_button?.requestFocus()
} }
@ -1992,6 +2023,7 @@ class ResultFragment : ResultTrailerPlayer() {
result_movie_progress_downloaded_holder?.isVisible = hasDownloadSupport result_movie_progress_downloaded_holder?.isVisible = hasDownloadSupport
if (hasDownloadSupport) { if (hasDownloadSupport) {
val localId = d.getId() val localId = d.getId()
val file = val file =
VideoDownloadManager.getDownloadFileInfoAndUpdateSettings( VideoDownloadManager.getDownloadFileInfoAndUpdateSettings(
requireContext(), requireContext(),
@ -2018,42 +2050,9 @@ class ResultFragment : ResultTrailerPlayer() {
d.rating, d.rating,
d.plot, d.plot,
System.currentTimeMillis(), System.currentTimeMillis(),
),
::handleDownloadButton
) )
) { downloadClickEvent ->
if (downloadClickEvent.action == DOWNLOAD_ACTION_DOWNLOAD) {
currentEpisodes?.firstOrNull()?.let { episode ->
handleAction(
EpisodeClickEvent(
ACTION_DOWNLOAD_EPISODE,
ResultEpisode(
d.name,
d.name,
null,
0,
null,
episode.data,
d.apiName,
localId,
0,
0L,
0L,
null,
null,
null,
d.type,
localId,
)
)
)
}
} else {
handleDownloadClick(
activity,
currentHeaderName,
downloadClickEvent
)
}
}
result_download_movie?.setOnLongClickListener { result_download_movie?.setOnLongClickListener {
val card = val card =
@ -2158,16 +2157,14 @@ class ResultFragment : ResultTrailerPlayer() {
} }
} }
val recAdapter: RecyclerView.Adapter<RecyclerView.ViewHolder>? = activity?.let { result_recommendations?.adapter =
SearchAdapter( SearchAdapter(
ArrayList(), ArrayList(),
result_recommendations, result_recommendations,
) { callback -> ) { callback ->
SearchHelper.handleSearchClickCallback(activity, callback) SearchHelper.handleSearchClickCallback(activity, callback)
} }
}
result_recommendations?.adapter = recAdapter
context?.let { ctx -> context?.let { ctx ->
result_bookmark_button?.isVisible = ctx.isTvSettings() result_bookmark_button?.isVisible = ctx.isTvSettings()

View file

@ -129,11 +129,15 @@ object UIHelper {
} }
fun Activity?.navigate(@IdRes navigation: Int, arguments: Bundle? = null) { fun Activity?.navigate(@IdRes navigation: Int, arguments: Bundle? = null) {
try {
if (this is FragmentActivity) { if (this is FragmentActivity) {
(supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as? NavHostFragment?)?.navController?.navigate( (supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as? NavHostFragment?)?.navController?.navigate(
navigation, arguments navigation, arguments
) )
} }
} catch (e : Exception) {
logError(e)
}
} }
@ColorInt @ColorInt

View file

@ -20,6 +20,7 @@
--> -->
<LinearLayout <LinearLayout
android:id="@+id/download_storage_appbar" android:id="@+id/download_storage_appbar"
tools:visibility="visible"
android:visibility="gone" android:visibility="gone"
android:layout_margin="10dp" android:layout_margin="10dp"
android:orientation="vertical" android:orientation="vertical"

View file

@ -360,14 +360,15 @@
tools:text="121min" /> tools:text="121min" />
</com.lagradost.cloudstream3.widget.FlowLayout> </com.lagradost.cloudstream3.widget.FlowLayout>
<!-- <!--
This has half margin and half padding to make TV focus on description look better. This has half margin and half padding to make TV focus on description look better.
The focus outline now settles between the poster and text. The focus outline now settles between the poster and text.
--> -->
<FrameLayout <FrameLayout
android:layout_marginHorizontal="5dp" android:layout_marginHorizontal="5dp"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<TextView <TextView
android:padding="5dp" android:padding="5dp"
android:maxLength="1000" android:maxLength="1000"
@ -848,9 +849,10 @@ The focus outline now settles between the poster and text.
android:textColor="?attr/grayTextColor" android:textColor="?attr/grayTextColor"
android:textSize="17sp" android:textSize="17sp"
android:textStyle="normal" android:textStyle="normal"
android:text="Episode 1022 will be released in" /> tools:text="Episode 1022 will be released in" />
<TextView <TextView
android:paddingEnd="5dp"
android:paddingStart="5dp" android:paddingStart="5dp"
android:gravity="center" android:gravity="center"
android:id="@+id/result_next_airing_time" android:id="@+id/result_next_airing_time"
@ -903,6 +905,7 @@ The focus outline now settles between the poster and text.
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="0dp" android:layout_marginTop="0dp"
android:clipToPadding="false" android:clipToPadding="false"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
android:descendantFocusability="afterDescendants" android:descendantFocusability="afterDescendants"
android:paddingBottom="100dp" android:paddingBottom="100dp"
tools:listitem="@layout/result_episode" /> tools:listitem="@layout/result_episode" />

View file

@ -497,7 +497,7 @@
"language": "en", "language": "en",
"name": "2Embed", "name": "2Embed",
"status": 1, "status": 1,
"url": "https://www.2embed.to" "url": "https://2embed.org"
}, },
"UakinoProvider": { "UakinoProvider": {
"language": "uk", "language": "uk",

View file

@ -241,8 +241,8 @@
}, },
"TwoEmbedProvider": { "TwoEmbedProvider": {
"name": "2Embed", "name": "2Embed",
"url": "https://www.2embed.ru", "url": "https://2embed.org",
"status": 0 "status": 1
}, },
"VMoveeProvider": { "VMoveeProvider": {
"name": "VMovee", "name": "VMovee",