diff --git a/app/build.gradle b/app/build.gradle index 3e4603f8..c8ab17e2 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -36,7 +36,7 @@ android { targetSdkVersion 30 versionCode 48 - versionName "2.10.30" + versionName "2.10.31" resValue "string", "app_version", "${defaultConfig.versionName}${versionNameSuffix ?: ""}" diff --git a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/HDMovie5.kt b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/HDMovie5.kt index 06439a00..531c0f4a 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/HDMovie5.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/HDMovie5.kt @@ -8,7 +8,7 @@ import com.lagradost.cloudstream3.utils.loadExtractor import org.jsoup.Jsoup class HDMovie5 : MainAPI() { - override var mainUrl = "https://hdmovie2.biz" + override var mainUrl = "https://Hdmovie2.biz" override var name = "HDMovie" override var lang = "hi" diff --git a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/TwoEmbedProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/TwoEmbedProvider.kt index 2b76d10d..ba4e31e4 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/TwoEmbedProvider.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/TwoEmbedProvider.kt @@ -2,8 +2,11 @@ package com.lagradost.cloudstream3.movieproviders import android.util.Log import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.cloudstream3.* 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.TmdbProvider import com.lagradost.cloudstream3.movieproviders.SflixProvider.Companion.extractRabbitStream @@ -15,7 +18,7 @@ import com.lagradost.cloudstream3.utils.loadExtractor class TwoEmbedProvider : TmdbProvider() { override val apiName = "2Embed" override var name = "2Embed" - override var mainUrl = "https://www.2embed.to" + override var mainUrl = "https://2embed.org" override val useMetaLoadResponse = true override val instantLinkLoading = false override val supportedTypes = setOf( @@ -43,10 +46,10 @@ class TwoEmbedProvider : TmdbProvider() { ) else listOf(mappedData.tmdbID.toString(), "tmdb") val isMovie = mappedData.episode == null && mappedData.season == null val embedUrl = if (isMovie) { - "$mainUrl/embed/$site/movie?id=$id" + "$mainUrl/embed/movie?$site=$id" } else { - val suffix = "$id&s=${mappedData.season ?: 1}&e=${mappedData.episode ?: 1}" - "$mainUrl/embed/$site/tv?id=$suffix" + val suffix = "$id&sea=${mappedData.season ?: 1}&epi=${mappedData.episode ?: 1}" + "$mainUrl/embed/series?$site=$suffix" } val document = app.get(embedUrl).document diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/MiniControllerFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/MiniControllerFragment.kt index eb95335b..aba6395f 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/MiniControllerFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/MiniControllerFragment.kt @@ -12,21 +12,40 @@ import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.utils.UIHelper.adjustAlpha import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute import com.lagradost.cloudstream3.utils.UIHelper.toPx +import java.lang.ref.WeakReference class MyMiniControllerFragment : MiniControllerFragment() { var currentColor: Int = 0 - // I KNOW, KINDA SPAGHETTI SOLUTION, BUT IT WORKS - override fun onInflate(context: Context, attributeSet: AttributeSet, bundle: Bundle?) { - val obtainStyledAttributes = context.obtainStyledAttributes(attributeSet, R.styleable.CustomCast, 0, 0) - if (obtainStyledAttributes.hasValue(R.styleable.CustomCast_customCastBackgroundColor)) { - currentColor = obtainStyledAttributes.getColor(R.styleable.CustomCast_customCastBackgroundColor, 0) - } - obtainStyledAttributes.recycle() - super.onInflate(context, attributeSet, bundle) + override fun onDestroy() { + currentColor = 0 + super.onDestroy() } + // 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?) { super.onViewCreated(view, savedInstanceState) @@ -38,18 +57,24 @@ class MyMiniControllerFragment : MiniControllerFragment() { val containerCurrent: RelativeLayout? = view.findViewById(R.id.container_current) context?.let { ctx -> - progressBar?.setBackgroundColor(adjustAlpha(ctx.colorFromAttribute(R.attr.colorPrimary), 0.35f)) - val params = RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, 2.toPx) + progressBar?.setBackgroundColor( + adjustAlpha( + ctx.colorFromAttribute(R.attr.colorPrimary), + 0.35f + ) + ) + val params = RelativeLayout.LayoutParams( + RelativeLayout.LayoutParams.MATCH_PARENT, + 2.toPx + ) progressBar?.layoutParams = params - - if (currentColor != 0) { - containerCurrent?.setBackgroundColor(currentColor) - } + val color = currentColor + if (color != 0) + containerCurrent?.setBackgroundColor(color) } val child = containerAll?.getChildAt(0) child?.alpha = 0f // REMOVE GRADIENT - } catch (e: Exception) { // JUST IN CASE } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadChildFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadChildFragment.kt index a5cc7b4a..e16d2d9e 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadChildFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadChildFragment.kt @@ -31,12 +31,8 @@ class DownloadChildFragment : Fragment() { override fun onDestroyView() { (download_child_list?.adapter as DownloadChildAdapter?)?.killAdapter() - super.onDestroyView() - } - - override fun onDestroy() { downloadDeleteEventListener?.let { VideoDownloadManager.downloadDeleteEvent -= it } - super.onDestroy() + super.onDestroyView() } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadFragment.kt index 25126a9b..78a5d484 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadFragment.kt @@ -65,16 +65,12 @@ class DownloadFragment : Fragment() { } override fun onDestroyView() { - (download_list?.adapter as DownloadHeaderAdapter?)?.killAdapter() - super.onDestroyView() - } - - override fun onDestroy() { if (downloadDeleteEventListener != null) { VideoDownloadManager.downloadDeleteEvent -= downloadDeleteEventListener!! downloadDeleteEventListener = null } - super.onDestroy() + (download_list?.adapter as DownloadHeaderAdapter?)?.killAdapter() + super.onDestroyView() } override fun onCreateView( @@ -83,7 +79,17 @@ class DownloadFragment : Fragment() { savedInstanceState: Bundle? ): View? { 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) { text_no_downloads.text = it } @@ -114,16 +120,8 @@ class DownloadFragment : Fragment() { getBytesAsText(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 = DownloadHeaderAdapter( diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadViewModel.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadViewModel.kt index a75b1795..baf1b363 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadViewModel.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadViewModel.kt @@ -8,6 +8,7 @@ import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope 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_HEADER_CACHE import com.lagradost.cloudstream3.utils.DataStore.getFolderName @@ -100,16 +101,20 @@ class DownloadViewModel : ViewModel() { (it.child?.episode ?: 0) + (it.child?.season?.times(10000) ?: 0) } // 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 localTotalBytes = stat.blockSizeLong * stat.blockCountLong + val localDownloadedBytes = visual.sumOf { it.totalBytes } - val localBytesAvailable = stat.availableBytes//stat.blockSizeLong * stat.blockCountLong - val localTotalBytes = stat.blockSizeLong * stat.blockCountLong - val localDownloadedBytes = visual.sumOf { it.totalBytes } - - _usedBytes.postValue(localTotalBytes - localBytesAvailable - localDownloadedBytes) - _availableBytes.postValue(localBytesAvailable) - _downloadBytes.postValue(localDownloadedBytes) + _usedBytes.postValue(localTotalBytes - localBytesAvailable - localDownloadedBytes) + _availableBytes.postValue(localBytesAvailable) + _downloadBytes.postValue(localDownloadedBytes) + } catch (e : Exception) { + _downloadBytes.postValue(0) + logError(e) + } _headerCards.postValue(visual) } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/download/EasyDownloadButton.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/download/EasyDownloadButton.kt index b0ad8e54..3cde4ba6 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/download/EasyDownloadButton.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/download/EasyDownloadButton.kt @@ -20,8 +20,13 @@ class EasyDownloadButton : IDisposable { val id: Int } + private var _clickCallback: ((DownloadClickEvent) -> Unit)? = null + private var _imageChangeCallback: ((Pair) -> Unit)? = null + override fun dispose() { try { + _clickCallback = null + _imageChangeCallback = null downloadProgressEventListener?.let { VideoDownloadManager.downloadProgressEvent -= it } downloadStatusEventListener?.let { VideoDownloadManager.downloadStatusEvent -= it } } catch (e: Exception) { @@ -119,6 +124,8 @@ class EasyDownloadButton : IDisposable { clickCallback: (DownloadClickEvent) -> Unit, isTextPercentage: Boolean = false ) { + _clickCallback = clickCallback + _imageChangeCallback = downloadImageChangeCallback var lastState: VideoDownloadManager.DownloadType? = null var currentBytes = setupCurrentBytes ?: 0 var totalBytes = setupTotalBytes ?: 0 @@ -142,7 +149,7 @@ class EasyDownloadButton : IDisposable { } else { Pair(R.drawable.netflix_download, R.string.download) } - downloadImageChangeCallback.invoke( + _imageChangeCallback?.invoke( Pair( img.first, downloadView.context.getString(img.second) @@ -223,7 +230,7 @@ class EasyDownloadButton : IDisposable { downloadView.setOnClickListener { if (currentBytes <= 0) { - clickCallback.invoke(DownloadClickEvent(DOWNLOAD_ACTION_DOWNLOAD, data)) + _clickCallback?.invoke(DownloadClickEvent(DOWNLOAD_ACTION_DOWNLOAD, data)) } else { val list = arrayListOf( Pair(DOWNLOAD_ACTION_PLAY_FILE, R.string.popup_play_file), @@ -243,7 +250,7 @@ class EasyDownloadButton : IDisposable { it.popupMenuNoIcons( list ) { - clickCallback.invoke(DownloadClickEvent(itemId, data)) + _clickCallback?.invoke(DownloadClickEvent(itemId, data)) } } } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/AbstractPlayerFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/AbstractPlayerFragment.kt index ddb3e7ab..30aed11d 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/AbstractPlayerFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/AbstractPlayerFragment.kt @@ -350,6 +350,7 @@ abstract class AbstractPlayerFragment( resizeMode = getKey(RESIZE_MODE_KEY) ?: 0 resize(resizeMode, false) + player.releaseCallbacks() player.initCallbacks( playerUpdated = ::playerUpdated, updateIsPlaying = ::updateIsPlaying, diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt index a52b2ed1..276904f7 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt @@ -108,6 +108,21 @@ class CS3IPlayer : IPlayer { private var playerUpdated: ((Any?) -> Unit)? = null private var embeddedSubtitlesFetched: ((List) -> 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( playerUpdated: (Any?) -> Unit, updateIsPlaying: ((Pair) -> Unit)?, diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/FullScreenPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/FullScreenPlayer.kt index 6c869dab..b2f834e1 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/FullScreenPlayer.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/FullScreenPlayer.kt @@ -315,6 +315,8 @@ open class FullScreenPlayer : AbstractPlayerFragment() { override fun onDestroy() { exitFullscreen() + player.release() + player.releaseCallbacks() super.onDestroy() } @@ -1104,7 +1106,6 @@ open class FullScreenPlayer : AbstractPlayerFragment() { @SuppressLint("ClickableViewAccessibility") override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - // init variables setPlayBackSpeed(getKey(PLAYBACK_SPEED_KEY) ?: 1.0f) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt index fabb2fa8..b56e6f79 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt @@ -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 } activity?.showDialog( languages.map { it.languageName }, lang639_1.indexOf(currentLanguageTwoLetters), - context.getString(R.string.subs_subtitle_languages), + view?.context?.getString(R.string.subs_subtitle_languages) ?: return@setOnClickListener, true, { } ) { index -> @@ -609,7 +609,7 @@ class GeneratorPlayer : FullScreenPlayer() { val currentPrefMedia = settingsManager.getString( - getString(R.string.subtitles_encoding_key), + ctx.getString(R.string.subtitles_encoding_key), null ) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/IPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/IPlayer.kt index 7b7553e9..0d5c1e26 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/IPlayer.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/IPlayer.kt @@ -88,6 +88,7 @@ interface IPlayer { subtitlesUpdates: (() -> Unit)? = null, // callback from player to inform that subtitles have updated in some way embeddedSubtitlesFetched: ((List) -> Unit)? = null, // callback from player to give all embedded subtitles ) + fun releaseCallbacks() fun updateSubtitleStyle(style: SaveCaptionStyle) fun saveData() diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt index 4334665a..c4e31f63 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt @@ -30,8 +30,6 @@ import androidx.core.widget.NestedScrollView import androidx.core.widget.doOnTextChanged import androidx.lifecycle.ViewModelProvider import androidx.preference.PreferenceManager -import androidx.recyclerview.widget.GridLayoutManager -import androidx.recyclerview.widget.RecyclerView import com.discord.panels.OverlappingPanelsLayout import com.discord.panels.PanelsChildGestureRegionObserver 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.syncproviders.providers.Kitsu import com.lagradost.cloudstream3.ui.WatchType -import com.lagradost.cloudstream3.ui.download.DOWNLOAD_ACTION_DOWNLOAD -import com.lagradost.cloudstream3.ui.download.DOWNLOAD_NAVIGATE_TO +import com.lagradost.cloudstream3.ui.download.* 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.GeneratorPlayer import com.lagradost.cloudstream3.ui.player.RepoLinkGenerator @@ -478,17 +474,21 @@ class ResultFragment : ResultTrailerPlayer() { } override fun onDestroyView() { + updateUIListener = null (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() } override fun onDestroy() { //requireActivity().viewModelStore.clear() // REMEMBER THE CLEAR - downloadButton?.dispose() - updateUIListener = null - result_cast_items?.let { - PanelsChildGestureRegionObserver.Provider.get().unregister(it) - } + 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) { val isSuccess = currentTrailers.getOrNull(index ?: currentTrailerIndex)?.let { trailer -> @@ -810,13 +846,412 @@ class ResultFragment : ResultTrailerPlayer() { viewModel.reloadEpisodes() } + var apiName: String = "" + private fun handleAction(episodeClick: EpisodeClickEvent): Job = main { + if (episodeClick.action == ACTION_DOWNLOAD_EPISODE) { + val isMovie = currentIsMovie ?: return@main + val headerName = currentHeaderName ?: return@main + val tvType = currentType ?: return@main + val poster = currentPoster ?: return@main + val id = currentId ?: return@main + val curl = url ?: return@main + showToast(activity, R.string.download_started, Toast.LENGTH_SHORT) + downloadEpisode( + activity, + episodeClick.data, + isMovie, + headerName, + tvType, + poster, + apiName, + id, + curl, + ) + return@main + } + + var currentLinks: Set? = null + var currentSubs: Set? = null + + //val id = episodeClick.data.id + currentLoadingCount++ + + val showTitle = + episodeClick.data.name ?: context?.getString(R.string.episode_name_format) + ?.format( + getString(R.string.episode), + episodeClick.data.episode + ) + + + fun acquireSingleExtractorLink( + links: List, + title: String, + callback: (ExtractorLink) -> Unit + ) { + val builder = AlertDialog.Builder(requireContext(), R.style.AlertDialogCustom) + + builder.setTitle(title) + builder.setItems(links.map { "${it.name} ${Qualities.getStringByInt(it.quality)}" } + .toTypedArray()) { dia, which -> + callback.invoke(links[which]) + dia?.dismiss() + } + builder.create().show() + } + + fun acquireSingleSubtitleLink( + links: List, + title: String, + callback: (SubtitleData) -> Unit + ) { + val builder = AlertDialog.Builder(requireContext(), R.style.AlertDialogCustom) + + builder.setTitle(title) + builder.setItems(links.map { it.name }.toTypedArray()) { dia, which -> + callback.invoke(links[which]) + dia?.dismiss() + } + builder.create().show() + } + + fun acquireSingeExtractorLink(title: String, callback: (ExtractorLink) -> Unit) { + acquireSingleExtractorLink(sortUrls(currentLinks ?: return), title, callback) + } + + fun startChromecast(startIndex: Int) { + val eps = currentEpisodes ?: return + activity?.getCastSession()?.startCast( + apiName, + currentIsMovie ?: return, + currentHeaderName, + currentPoster, + episodeClick.data.index, + eps, + sortUrls(currentLinks ?: return), + sortSubs(currentSubs ?: return), + startTime = episodeClick.data.getRealPosition(), + startIndex = startIndex + ) + } + + suspend fun requireLinks(isCasting: Boolean, displayLoading: Boolean = true): Boolean { + val skipLoading = getApiFromName(apiName).instantLinkLoading + + var loadingDialog: AlertDialog? = null + val currentLoad = currentLoadingCount + + if (!skipLoading && displayLoading) { + val builder = + AlertDialog.Builder(requireContext(), R.style.AlertDialogCustomTransparent) + val customLayout = layoutInflater.inflate(R.layout.dialog_loading, null) + builder.setView(customLayout) + + loadingDialog = builder.create() + + loadingDialog.show() + loadingDialog.setOnDismissListener { + currentLoadingCount++ + } + } + + val data = viewModel.loadEpisode(episodeClick.data, isCasting) + if (currentLoadingCount != currentLoad) return false + loadingDialog?.dismissSafe(activity) + + when (data) { + is Resource.Success -> { + currentLinks = data.value.first + currentSubs = data.value.second + return true + } + is Resource.Failure -> { + showToast( + activity, + R.string.error_loading_links_toast, + Toast.LENGTH_SHORT + ) + } + else -> Unit + } + return false + } + + val isLoaded = when (episodeClick.action) { + ACTION_PLAY_EPISODE_IN_PLAYER -> true + ACTION_CLICK_DEFAULT -> true + ACTION_SHOW_TOAST -> true + ACTION_DOWNLOAD_EPISODE -> { + showToast(activity, R.string.download_started, Toast.LENGTH_SHORT) + requireLinks(false, false) + } + ACTION_CHROME_CAST_EPISODE -> requireLinks(true) + ACTION_CHROME_CAST_MIRROR -> requireLinks(true) + ACTION_SHOW_DESCRIPTION -> true + else -> requireLinks(false) + } + if (!isLoaded) return@main // CANT LOAD + + when (episodeClick.action) { + ACTION_SHOW_TOAST -> { + showToast(activity, R.string.play_episode_toast, Toast.LENGTH_SHORT) + } + + ACTION_SHOW_DESCRIPTION -> { + val builder: AlertDialog.Builder = + AlertDialog.Builder(requireContext(), R.style.AlertDialogCustom) + builder.setMessage(episodeClick.data.description ?: return@main) + .setTitle(R.string.torrent_plot) + .show() + } + + ACTION_CLICK_DEFAULT -> { + context?.let { ctx -> + if (ctx.isConnectedToChromecast()) { + handleAction( + EpisodeClickEvent( + ACTION_CHROME_CAST_EPISODE, + episodeClick.data + ) + ) + } else { + handleAction( + EpisodeClickEvent( + ACTION_PLAY_EPISODE_IN_PLAYER, + episodeClick.data + ) + ) + } + } + } + + ACTION_DOWNLOAD_EPISODE_SUBTITLE -> { + acquireSingleSubtitleLink( + sortSubs( + currentSubs ?: return@main + ),//(currentLinks ?: return@main).filter { !it.isM3u8 }, + getString(R.string.episode_action_download_subtitle) + ) { link -> + downloadSubtitle( + context, + link, + getMeta( + episodeClick.data, + currentHeaderName ?: return@acquireSingleSubtitleLink, + apiName, + currentPoster ?: return@acquireSingleSubtitleLink, + currentIsMovie ?: return@acquireSingleSubtitleLink, + currentType ?: return@acquireSingleSubtitleLink + ) + ) + showToast(activity, R.string.download_started, Toast.LENGTH_SHORT) + } + } + + ACTION_SHOW_OPTIONS -> { + context?.let { ctx -> + val builder = AlertDialog.Builder(ctx, R.style.AlertDialogCustom) + var dialog: AlertDialog? = null + builder.setTitle(showTitle) + val options = + requireContext().resources.getStringArray(R.array.episode_long_click_options) + val optionsValues = + requireContext().resources.getIntArray(R.array.episode_long_click_options_values) + + val verifiedOptions = ArrayList() + val verifiedOptionsValues = ArrayList() + + val hasDownloadSupport = getApiFromName(apiName).hasDownloadSupport + + for (i in options.indices) { + val opv = optionsValues[i] + val op = options[i] + + val isConnected = ctx.isConnectedToChromecast() + val add = when (opv) { + ACTION_CHROME_CAST_EPISODE -> isConnected + ACTION_CHROME_CAST_MIRROR -> isConnected + ACTION_DOWNLOAD_EPISODE_SUBTITLE -> !currentSubs.isNullOrEmpty() + ACTION_DOWNLOAD_EPISODE -> hasDownloadSupport + ACTION_DOWNLOAD_MIRROR -> hasDownloadSupport + ACTION_PLAY_EPISODE_IN_VLC_PLAYER -> context?.isAppInstalled( + VLC_PACKAGE + ) ?: false + else -> true + } + if (add) { + verifiedOptions.add(op) + verifiedOptionsValues.add(opv) + } + } + + builder.setItems( + verifiedOptions.toTypedArray() + ) { _, which -> + handleAction( + EpisodeClickEvent( + verifiedOptionsValues[which], + episodeClick.data + ) + ) + dialog?.dismissSafe(activity) + } + + dialog = builder.create() + dialog.show() + } + } + ACTION_COPY_LINK -> { + activity?.let { act -> + try { + acquireSingeExtractorLink(act.getString(R.string.episode_action_copy_link)) { link -> + val serviceClipboard = + (act.getSystemService(CLIPBOARD_SERVICE) as? ClipboardManager?) + ?: return@acquireSingeExtractorLink + val clip = ClipData.newPlainText(link.name, link.url) + serviceClipboard.setPrimaryClip(clip) + showToast(act, R.string.copy_link_toast, Toast.LENGTH_SHORT) + } + } catch (e: Exception) { + showToast(act, e.toString(), Toast.LENGTH_LONG) + logError(e) + } + } + } + + ACTION_PLAY_EPISODE_IN_BROWSER -> { + acquireSingeExtractorLink(getString(R.string.episode_action_play_in_browser)) { link -> + try { + val i = Intent(ACTION_VIEW) + i.data = Uri.parse(link.url) + startActivity(i) + } catch (e: Exception) { + logError(e) + } + } + } + + ACTION_CHROME_CAST_MIRROR -> { + acquireSingeExtractorLink(getString(R.string.episode_action_chromecast_mirror)) { link -> + val mirrorIndex = currentLinks?.indexOf(link) ?: -1 + startChromecast(if (mirrorIndex == -1) 0 else mirrorIndex) + } + } + + ACTION_CHROME_CAST_EPISODE -> { + startChromecast(0) + } + + ACTION_PLAY_EPISODE_IN_VLC_PLAYER -> { + activity?.let { act -> + try { + if (!act.checkWrite()) { + act.requestRW() + if (act.checkWrite()) return@main + } + val data = currentLinks ?: return@main + val subs = currentSubs ?: return@main + + val outputDir = act.cacheDir + val outputFile = withContext(Dispatchers.IO) { + File.createTempFile("mirrorlist", ".m3u8", outputDir) + } + var text = "#EXTM3U" + for (sub in sortSubs(subs)) { + text += "\n#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID=\"subs\",NAME=\"${sub.name}\",DEFAULT=NO,AUTOSELECT=NO,FORCED=NO,LANGUAGE=\"${sub.name}\",URI=\"${sub.url}\"" + } + for (link in data.sortedBy { -it.quality }) { + text += "\n#EXTINF:, ${link.name}\n${link.url}" + } + outputFile.writeText(text) + + val vlcIntent = Intent(VLC_INTENT_ACTION_RESULT) + + vlcIntent.setPackage(VLC_PACKAGE) + vlcIntent.addFlags(FLAG_GRANT_PERSISTABLE_URI_PERMISSION) + vlcIntent.addFlags(FLAG_GRANT_PREFIX_URI_PERMISSION) + vlcIntent.addFlags(FLAG_GRANT_READ_URI_PERMISSION) + vlcIntent.addFlags(FLAG_GRANT_WRITE_URI_PERMISSION) + + vlcIntent.setDataAndType( + FileProvider.getUriForFile( + act, + act.applicationContext.packageName + ".provider", + outputFile + ), "video/*" + ) + + val startId = VLC_FROM_PROGRESS + + var position = startId + if (startId == VLC_FROM_START) { + position = 1 + } else if (startId == VLC_FROM_PROGRESS) { + position = 0 + } + + vlcIntent.putExtra("position", position) + + vlcIntent.component = VLC_COMPONENT + act.setKey(VLC_LAST_ID_KEY, episodeClick.data.id) + act.startActivityForResult(vlcIntent, VLC_REQUEST_CODE) + } catch (e: Exception) { + logError(e) + showToast(act, e.toString(), Toast.LENGTH_LONG) + } + } + } + + ACTION_PLAY_EPISODE_IN_PLAYER -> { + viewModel.getGenerator(episodeClick.data) + ?.let { generator -> + activity?.navigate( + R.id.global_to_navigation_player, + GeneratorPlayer.newInstance( + generator, syncdata?.let { HashMap(it) } + ) + ) + } + } + + ACTION_RELOAD_EPISODE -> { + viewModel.loadEpisode(episodeClick.data, false, clearCache = true) + } + + ACTION_DOWNLOAD_MIRROR -> { + acquireSingleExtractorLink( + sortUrls( + currentLinks ?: return@main + ),//(currentLinks ?: return@main).filter { !it.isM3u8 }, + getString(R.string.episode_action_download_mirror) + ) { link -> + startDownload( + context, + episodeClick.data, + currentIsMovie ?: return@acquireSingleExtractorLink, + currentHeaderName ?: return@acquireSingleExtractorLink, + currentType ?: return@acquireSingleExtractorLink, + currentPoster ?: return@acquireSingleExtractorLink, + apiName, + currentId ?: return@acquireSingleExtractorLink, + url ?: return@acquireSingleExtractorLink, + listOf(link), + sortSubs(currentSubs ?: return@acquireSingleExtractorLink), + ) + showToast(activity, R.string.download_started, Toast.LENGTH_SHORT) + } + } + } + } + @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() + //result_cast_items?.adapter = ActorAdaptor() fixGrid() result_recommendations?.spanCount = 3 result_overlapping_panels?.setStartPanelLockState(OverlappingPanelsLayout.LockState.CLOSE) @@ -855,7 +1290,7 @@ class ResultFragment : ResultTrailerPlayer() { // activity?.fixPaddingStatusbar(result_toolbar) url = arguments?.getString(URL_BUNDLE) - val apiName = arguments?.getString(API_NAME_BUNDLE) ?: return + 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) @@ -865,26 +1300,23 @@ class ResultFragment : ResultTrailerPlayer() { 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 - } + // this shit leaks for some reason + //castContext.addCastStateListener { state -> + // media_route_button?.isGone = state == CastState.NO_DEVICES_AVAILABLE + //} } catch (e: Exception) { logError(e) } @@ -904,7 +1336,6 @@ class ResultFragment : ResultTrailerPlayer() { player.handleEvent(CSPlayerEvent.Pause) } } - //result_poster_blur_holder?.translationY = -scrollY.toFloat() }) @@ -912,404 +1343,7 @@ class ResultFragment : ResultTrailerPlayer() { activity?.popCurrentPage() } - fun handleAction(episodeClick: EpisodeClickEvent): Job = main { - if (episodeClick.action == ACTION_DOWNLOAD_EPISODE) { - val isMovie = currentIsMovie ?: return@main - val headerName = currentHeaderName ?: return@main - val tvType = currentType ?: return@main - val poster = currentPoster ?: return@main - val id = currentId ?: return@main - val curl = url ?: return@main - showToast(activity, R.string.download_started, Toast.LENGTH_SHORT) - downloadEpisode( - activity, - episodeClick.data, - isMovie, - headerName, - tvType, - poster, - apiName, - id, - curl, - ) - return@main - } - - var currentLinks: Set? = null - var currentSubs: Set? = null - - //val id = episodeClick.data.id - currentLoadingCount++ - - val showTitle = - episodeClick.data.name ?: context?.getString(R.string.episode_name_format) - ?.format( - getString(R.string.episode), - episodeClick.data.episode - ) - - - fun acquireSingleExtractorLink( - links: List, - title: String, - callback: (ExtractorLink) -> Unit - ) { - val builder = AlertDialog.Builder(requireContext(), R.style.AlertDialogCustom) - - builder.setTitle(title) - builder.setItems(links.map { "${it.name} ${Qualities.getStringByInt(it.quality)}" } - .toTypedArray()) { dia, which -> - callback.invoke(links[which]) - dia?.dismiss() - } - builder.create().show() - } - - fun acquireSingleSubtitleLink( - links: List, - title: String, - callback: (SubtitleData) -> Unit - ) { - val builder = AlertDialog.Builder(requireContext(), R.style.AlertDialogCustom) - - builder.setTitle(title) - builder.setItems(links.map { it.name }.toTypedArray()) { dia, which -> - callback.invoke(links[which]) - dia?.dismiss() - } - builder.create().show() - } - - fun acquireSingeExtractorLink(title: String, callback: (ExtractorLink) -> Unit) { - acquireSingleExtractorLink(sortUrls(currentLinks ?: return), title, callback) - } - - fun startChromecast(startIndex: Int) { - val eps = currentEpisodes ?: return - activity?.getCastSession()?.startCast( - apiName, - currentIsMovie ?: return, - currentHeaderName, - currentPoster, - episodeClick.data.index, - eps, - sortUrls(currentLinks ?: return), - sortSubs(currentSubs ?: return), - startTime = episodeClick.data.getRealPosition(), - startIndex = startIndex - ) - } - - suspend fun requireLinks(isCasting: Boolean, displayLoading: Boolean = true): Boolean { - val skipLoading = getApiFromName(apiName).instantLinkLoading - - var loadingDialog: AlertDialog? = null - val currentLoad = currentLoadingCount - - if (!skipLoading && displayLoading) { - val builder = - AlertDialog.Builder(requireContext(), R.style.AlertDialogCustomTransparent) - val customLayout = layoutInflater.inflate(R.layout.dialog_loading, null) - builder.setView(customLayout) - - loadingDialog = builder.create() - - loadingDialog.show() - loadingDialog.setOnDismissListener { - currentLoadingCount++ - } - } - - val data = viewModel.loadEpisode(episodeClick.data, isCasting) - if (currentLoadingCount != currentLoad) return false - loadingDialog?.dismissSafe(activity) - - when (data) { - is Resource.Success -> { - currentLinks = data.value.first - currentSubs = data.value.second - return true - } - is Resource.Failure -> { - showToast( - activity, - R.string.error_loading_links_toast, - Toast.LENGTH_SHORT - ) - } - else -> Unit - } - return false - } - - val isLoaded = when (episodeClick.action) { - ACTION_PLAY_EPISODE_IN_PLAYER -> true - ACTION_CLICK_DEFAULT -> true - ACTION_SHOW_TOAST -> true - ACTION_DOWNLOAD_EPISODE -> { - showToast(activity, R.string.download_started, Toast.LENGTH_SHORT) - requireLinks(false, false) - } - ACTION_CHROME_CAST_EPISODE -> requireLinks(true) - ACTION_CHROME_CAST_MIRROR -> requireLinks(true) - ACTION_SHOW_DESCRIPTION -> true - else -> requireLinks(false) - } - if (!isLoaded) return@main // CANT LOAD - - when (episodeClick.action) { - ACTION_SHOW_TOAST -> { - showToast(activity, R.string.play_episode_toast, Toast.LENGTH_SHORT) - } - - ACTION_SHOW_DESCRIPTION -> { - val builder: AlertDialog.Builder = - AlertDialog.Builder(requireContext(), R.style.AlertDialogCustom) - builder.setMessage(episodeClick.data.description ?: return@main) - .setTitle(R.string.torrent_plot) - .show() - } - - ACTION_CLICK_DEFAULT -> { - context?.let { ctx -> - if (ctx.isConnectedToChromecast()) { - handleAction( - EpisodeClickEvent( - ACTION_CHROME_CAST_EPISODE, - episodeClick.data - ) - ) - } else { - handleAction( - EpisodeClickEvent( - ACTION_PLAY_EPISODE_IN_PLAYER, - episodeClick.data - ) - ) - } - } - } - - ACTION_DOWNLOAD_EPISODE_SUBTITLE -> { - acquireSingleSubtitleLink( - sortSubs( - currentSubs ?: return@main - ),//(currentLinks ?: return@main).filter { !it.isM3u8 }, - getString(R.string.episode_action_download_subtitle) - ) { link -> - downloadSubtitle( - context, - link, - getMeta( - episodeClick.data, - currentHeaderName ?: return@acquireSingleSubtitleLink, - apiName, - currentPoster ?: return@acquireSingleSubtitleLink, - currentIsMovie ?: return@acquireSingleSubtitleLink, - currentType ?: return@acquireSingleSubtitleLink - ) - ) - showToast(activity, R.string.download_started, Toast.LENGTH_SHORT) - } - } - - ACTION_SHOW_OPTIONS -> { - context?.let { ctx -> - val builder = AlertDialog.Builder(ctx, R.style.AlertDialogCustom) - var dialog: AlertDialog? = null - builder.setTitle(showTitle) - val options = - requireContext().resources.getStringArray(R.array.episode_long_click_options) - val optionsValues = - requireContext().resources.getIntArray(R.array.episode_long_click_options_values) - - val verifiedOptions = ArrayList() - val verifiedOptionsValues = ArrayList() - - val hasDownloadSupport = api.hasDownloadSupport - - for (i in options.indices) { - val opv = optionsValues[i] - val op = options[i] - - val isConnected = ctx.isConnectedToChromecast() - val add = when (opv) { - ACTION_CHROME_CAST_EPISODE -> isConnected - ACTION_CHROME_CAST_MIRROR -> isConnected - ACTION_DOWNLOAD_EPISODE_SUBTITLE -> !currentSubs.isNullOrEmpty() - ACTION_DOWNLOAD_EPISODE -> hasDownloadSupport - ACTION_DOWNLOAD_MIRROR -> hasDownloadSupport - ACTION_PLAY_EPISODE_IN_VLC_PLAYER -> context?.isAppInstalled( - VLC_PACKAGE - ) ?: false - else -> true - } - if (add) { - verifiedOptions.add(op) - verifiedOptionsValues.add(opv) - } - } - - builder.setItems( - verifiedOptions.toTypedArray() - ) { _, which -> - handleAction( - EpisodeClickEvent( - verifiedOptionsValues[which], - episodeClick.data - ) - ) - dialog?.dismissSafe(activity) - } - - dialog = builder.create() - dialog.show() - } - } - ACTION_COPY_LINK -> { - activity?.let { act -> - try { - acquireSingeExtractorLink(act.getString(R.string.episode_action_copy_link)) { link -> - val serviceClipboard = - (act.getSystemService(CLIPBOARD_SERVICE) as? ClipboardManager?) - ?: return@acquireSingeExtractorLink - val clip = ClipData.newPlainText(link.name, link.url) - serviceClipboard.setPrimaryClip(clip) - showToast(act, R.string.copy_link_toast, Toast.LENGTH_SHORT) - } - } catch (e: Exception) { - showToast(act, e.toString(), Toast.LENGTH_LONG) - logError(e) - } - } - } - - ACTION_PLAY_EPISODE_IN_BROWSER -> { - acquireSingeExtractorLink(getString(R.string.episode_action_play_in_browser)) { link -> - try { - val i = Intent(ACTION_VIEW) - i.data = Uri.parse(link.url) - startActivity(i) - } catch (e: Exception) { - logError(e) - } - } - } - - ACTION_CHROME_CAST_MIRROR -> { - acquireSingeExtractorLink(getString(R.string.episode_action_chromecast_mirror)) { link -> - val mirrorIndex = currentLinks?.indexOf(link) ?: -1 - startChromecast(if (mirrorIndex == -1) 0 else mirrorIndex) - } - } - - ACTION_CHROME_CAST_EPISODE -> { - startChromecast(0) - } - - ACTION_PLAY_EPISODE_IN_VLC_PLAYER -> { - activity?.let { act -> - try { - if (!act.checkWrite()) { - act.requestRW() - if (act.checkWrite()) return@main - } - val data = currentLinks ?: return@main - val subs = currentSubs ?: return@main - - val outputDir = act.cacheDir - val outputFile = withContext(Dispatchers.IO) { - File.createTempFile("mirrorlist", ".m3u8", outputDir) - } - var text = "#EXTM3U" - for (sub in sortSubs(subs)) { - text += "\n#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID=\"subs\",NAME=\"${sub.name}\",DEFAULT=NO,AUTOSELECT=NO,FORCED=NO,LANGUAGE=\"${sub.name}\",URI=\"${sub.url}\"" - } - for (link in data.sortedBy { -it.quality }) { - text += "\n#EXTINF:, ${link.name}\n${link.url}" - } - outputFile.writeText(text) - - val vlcIntent = Intent(VLC_INTENT_ACTION_RESULT) - - vlcIntent.setPackage(VLC_PACKAGE) - vlcIntent.addFlags(FLAG_GRANT_PERSISTABLE_URI_PERMISSION) - vlcIntent.addFlags(FLAG_GRANT_PREFIX_URI_PERMISSION) - vlcIntent.addFlags(FLAG_GRANT_READ_URI_PERMISSION) - vlcIntent.addFlags(FLAG_GRANT_WRITE_URI_PERMISSION) - - vlcIntent.setDataAndType( - FileProvider.getUriForFile( - act, - act.applicationContext.packageName + ".provider", - outputFile - ), "video/*" - ) - - val startId = VLC_FROM_PROGRESS - - var position = startId - if (startId == VLC_FROM_START) { - position = 1 - } else if (startId == VLC_FROM_PROGRESS) { - position = 0 - } - - vlcIntent.putExtra("position", position) - - vlcIntent.component = VLC_COMPONENT - act.setKey(VLC_LAST_ID_KEY, episodeClick.data.id) - act.startActivityForResult(vlcIntent, VLC_REQUEST_CODE) - } catch (e: Exception) { - logError(e) - showToast(act, e.toString(), Toast.LENGTH_LONG) - } - } - } - - ACTION_PLAY_EPISODE_IN_PLAYER -> { - viewModel.getGenerator(episodeClick.data) - ?.let { generator -> - activity?.navigate( - R.id.global_to_navigation_player, - GeneratorPlayer.newInstance( - generator, syncdata?.let { HashMap(it) } - ) - ) - } - } - - ACTION_RELOAD_EPISODE -> { - viewModel.loadEpisode(episodeClick.data, false, clearCache = true) - } - - ACTION_DOWNLOAD_MIRROR -> { - acquireSingleExtractorLink( - sortUrls( - currentLinks ?: return@main - ),//(currentLinks ?: return@main).filter { !it.isM3u8 }, - getString(R.string.episode_action_download_mirror) - ) { link -> - startDownload( - context, - episodeClick.data, - currentIsMovie ?: return@acquireSingleExtractorLink, - currentHeaderName ?: return@acquireSingleExtractorLink, - currentType ?: return@acquireSingleExtractorLink, - currentPoster ?: return@acquireSingleExtractorLink, - apiName, - currentId ?: return@acquireSingleExtractorLink, - url ?: return@acquireSingleExtractorLink, - listOf(link), - sortSubs(currentSubs ?: return@acquireSingleExtractorLink), - ) - showToast(activity, R.string.download_started, Toast.LENGTH_SHORT) - } - } - } - } - - val adapter: RecyclerView.Adapter = + result_episodes.adapter = EpisodeAdapter( ArrayList(), api.hasDownloadSupport, @@ -1321,9 +1355,6 @@ class ResultFragment : ResultTrailerPlayer() { } ) - result_episodes.adapter = adapter - result_episodes.layoutManager = GridLayoutManager(context, 1) - result_bookmark_button.setOnClickListener { it.popupMenuNoIcons( items = WatchType.values() @@ -1467,7 +1498,8 @@ class ResultFragment : ResultTrailerPlayer() { } } } - val imgAdapter = ImageAdapter( + + result_mini_sync?.adapter = ImageAdapter( R.layout.result_mini_image, nextFocusDown = R.id.result_sync_set_score, clickCallback = { action -> @@ -1479,7 +1511,6 @@ class ResultFragment : ResultTrailerPlayer() { } } }) - result_mini_sync?.adapter = imgAdapter observe(syncModel.synced) { list -> result_sync_names?.text = @@ -1638,7 +1669,7 @@ class ResultFragment : ResultTrailerPlayer() { ?.let { result_play_movie?.text = it } - println("startAction = $startAction") + //println("startAction = $startAction") when (startAction) { 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 if (hasFocus) result_bookmark_button?.requestFocus() } @@ -1992,6 +2023,7 @@ class ResultFragment : ResultTrailerPlayer() { result_movie_progress_downloaded_holder?.isVisible = hasDownloadSupport if (hasDownloadSupport) { val localId = d.getId() + val file = VideoDownloadManager.getDownloadFileInfoAndUpdateSettings( requireContext(), @@ -2018,42 +2050,9 @@ class ResultFragment : ResultTrailerPlayer() { d.rating, d.plot, System.currentTimeMillis(), - ) - ) { 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 - ) - } - } + ), + ::handleDownloadButton + ) result_download_movie?.setOnLongClickListener { val card = @@ -2158,16 +2157,14 @@ class ResultFragment : ResultTrailerPlayer() { } } - val recAdapter: RecyclerView.Adapter? = activity?.let { + result_recommendations?.adapter = SearchAdapter( ArrayList(), result_recommendations, ) { callback -> SearchHelper.handleSearchClickCallback(activity, callback) } - } - result_recommendations?.adapter = recAdapter context?.let { ctx -> result_bookmark_button?.isVisible = ctx.isTvSettings() diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/UIHelper.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/UIHelper.kt index d101b90e..682d9ae5 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/UIHelper.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/UIHelper.kt @@ -129,10 +129,14 @@ object UIHelper { } fun Activity?.navigate(@IdRes navigation: Int, arguments: Bundle? = null) { - if (this is FragmentActivity) { - (supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as? NavHostFragment?)?.navController?.navigate( - navigation, arguments - ) + try { + if (this is FragmentActivity) { + (supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as? NavHostFragment?)?.navController?.navigate( + navigation, arguments + ) + } + } catch (e : Exception) { + logError(e) } } diff --git a/app/src/main/res/layout/fragment_downloads.xml b/app/src/main/res/layout/fragment_downloads.xml index 96f1700e..5e12eb4c 100644 --- a/app/src/main/res/layout/fragment_downloads.xml +++ b/app/src/main/res/layout/fragment_downloads.xml @@ -20,6 +20,7 @@ --> - + + + tools:text="Episode 1022 will be released in" /> diff --git a/docs/providers.json b/docs/providers.json index 27c0ef33..b7eac519 100644 --- a/docs/providers.json +++ b/docs/providers.json @@ -497,7 +497,7 @@ "language": "en", "name": "2Embed", "status": 1, - "url": "https://www.2embed.to" + "url": "https://2embed.org" }, "UakinoProvider": { "language": "uk", diff --git a/providers.json b/providers.json index dcf36348..7573c234 100644 --- a/providers.json +++ b/providers.json @@ -241,8 +241,8 @@ }, "TwoEmbedProvider": { "name": "2Embed", - "url": "https://www.2embed.ru", - "status": 0 + "url": "https://2embed.org", + "status": 1 }, "VMoveeProvider": { "name": "VMovee",