mirror of
				https://github.com/recloudstream/cloudstream.git
				synced 2024-08-15 01:53:11 +00:00 
			
		
		
		
	episode options sff
This commit is contained in:
		
							parent
							
								
									bcc17171e5
								
							
						
					
					
						commit
						b5f913cc72
					
				
					 7 changed files with 285 additions and 134 deletions
				
			
		|  | @ -27,6 +27,7 @@ import androidx.core.content.ContextCompat | |||
| import androidx.fragment.app.FragmentActivity | ||||
| import androidx.preference.PreferenceManager | ||||
| import com.google.android.gms.cast.framework.CastContext | ||||
| import com.google.android.gms.cast.framework.CastState | ||||
| import com.google.android.gms.common.ConnectionResult | ||||
| import com.google.android.gms.common.GoogleApiAvailability | ||||
| import com.lagradost.cloudstream3.ui.result.ResultFragment | ||||
|  | @ -151,6 +152,17 @@ object UIHelper { | |||
|         return isCastApiAvailable | ||||
|     } | ||||
| 
 | ||||
|     fun Context.isConnectedToChromecast(): Boolean { | ||||
|         if (isCastApiAvailable()) { | ||||
|             val castContext = CastContext.getSharedInstance(this) | ||||
|             if (castContext.castState == CastState.CONNECTED) { | ||||
|                 return true | ||||
|             } | ||||
|         } | ||||
|         return false | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     fun adjustAlpha(@ColorInt color: Int, factor: Float): Int { | ||||
|         val alpha = (Color.alpha(color) * factor).roundToInt() | ||||
|         val red = Color.red(color) | ||||
|  |  | |||
|  | @ -2,6 +2,8 @@ package com.lagradost.cloudstream3.ui.result | |||
| 
 | ||||
| import android.annotation.SuppressLint | ||||
| import android.app.Activity | ||||
| import android.content.Context | ||||
| import android.content.DialogInterface | ||||
| import android.view.LayoutInflater | ||||
| import android.view.View | ||||
| import android.view.ViewGroup | ||||
|  | @ -9,6 +11,7 @@ import android.widget.ImageView | |||
| import android.widget.LinearLayout | ||||
| import android.widget.TextView | ||||
| import androidx.annotation.LayoutRes | ||||
| import androidx.appcompat.app.AlertDialog | ||||
| import androidx.core.widget.ContentLoadingProgressBar | ||||
| import androidx.recyclerview.widget.RecyclerView | ||||
| import com.bumptech.glide.Glide | ||||
|  | @ -16,15 +19,27 @@ import com.bumptech.glide.load.model.GlideUrl | |||
| import com.google.android.gms.cast.framework.CastContext | ||||
| import com.google.android.gms.cast.framework.CastState | ||||
| import com.lagradost.cloudstream3.* | ||||
| import com.lagradost.cloudstream3.UIHelper.hideSystemUI | ||||
| import com.lagradost.cloudstream3.UIHelper.isCastApiAvailable | ||||
| import com.lagradost.cloudstream3.utils.getId | ||||
| import kotlinx.android.synthetic.main.result_episode.view.episode_holder | ||||
| import kotlinx.android.synthetic.main.result_episode.view.episode_text | ||||
| import kotlinx.android.synthetic.main.result_episode_large.view.* | ||||
| 
 | ||||
| const val ACTION_RELOAD_EPISODE = 4 | ||||
| const val ACTION_CHROME_CAST_EPISODE = 2 | ||||
| const val ACTION_DOWNLOAD_EPISODE = 3 | ||||
| const val ACTION_PLAY_EPISODE_IN_PLAYER = 1 | ||||
| const val ACTION_PLAY_EPISODE_IN_EXTERNAL_PLAYER = 2 | ||||
| const val ACTION_PLAY_EPISODE_IN_BROWSER = 3 | ||||
| 
 | ||||
| const val ACTION_CHROME_CAST_EPISODE = 4 | ||||
| const val ACTION_CHROME_CAST_MIRROR = 5 | ||||
| 
 | ||||
| const val ACTION_DOWNLOAD_EPISODE = 6 | ||||
| const val ACTION_DOWNLOAD_MIRROR = 7 | ||||
| 
 | ||||
| const val ACTION_RELOAD_EPISODE = 8 | ||||
| const val ACTION_COPY_LINK = 9 | ||||
| 
 | ||||
| const val ACTION_SHOW_OPTIONS = 10 | ||||
| 
 | ||||
| data class EpisodeClickEvent(val action: Int, val data: ResultEpisode) | ||||
| 
 | ||||
|  | @ -69,8 +84,6 @@ class EpisodeAdapter( | |||
|         itemView: View, | ||||
|         private val clickCallback: (EpisodeClickEvent) -> Unit, | ||||
|     ) : RecyclerView.ViewHolder(itemView) { | ||||
|         //private val episodeViewPrecentage: View? = itemView.episode_view_procentage | ||||
|         // private val episodeViewPercentageOff: View? = itemView.episode_view_procentage_off | ||||
|         private val episodeText: TextView = itemView.episode_text | ||||
|         private val episodeRating: TextView? = itemView.episode_rating | ||||
|         private val episodeDescript: TextView? = itemView.episode_descript | ||||
|  | @ -78,8 +91,6 @@ class EpisodeAdapter( | |||
|         private val episodePoster: ImageView? = itemView.episode_poster | ||||
|         private val episodeDownload: ImageView? = itemView.episode_download | ||||
| 
 | ||||
|         // val episodeExtra: ImageView = itemView.episode_extra | ||||
|         // private val episodePlay: ImageView = itemView.episode_play | ||||
|         private val episodeHolder = itemView.episode_holder | ||||
| 
 | ||||
|         @SuppressLint("SetTextI18n") | ||||
|  | @ -87,20 +98,7 @@ class EpisodeAdapter( | |||
|             val name = if (card.name == null) "Episode ${card.episode}" else "${card.episode}. ${card.name}" | ||||
|             episodeText.text = name | ||||
| 
 | ||||
|             fun setWidth(v: View, procentage: Float) { | ||||
|                 val param = LinearLayout.LayoutParams( | ||||
|                     v.layoutParams.width, | ||||
|                     v.layoutParams.height, | ||||
|                     procentage | ||||
|                 ) | ||||
|                 v.layoutParams = param | ||||
|             } | ||||
| 
 | ||||
|             val watchProgress = card.getWatchProgress() | ||||
|             /*if (episodeViewPrecentage != null && episodeViewPercentageOff != null) { | ||||
|                 setWidth(episodeViewPrecentage, watchProgress) | ||||
|                 setWidth(episodeViewPercentageOff, 1 - watchProgress) | ||||
|             }*/ | ||||
| 
 | ||||
|             episodeProgress?.progress = (watchProgress * 50).toInt() | ||||
|             episodeProgress?.visibility = if (watchProgress > 0.0f) View.VISIBLE else View.GONE | ||||
|  | @ -133,21 +131,18 @@ class EpisodeAdapter( | |||
| 
 | ||||
|             episodeHolder.setOnClickListener { | ||||
|                 episodeHolder.context?.let { ctx -> | ||||
|                     if (ctx.isCastApiAvailable()) { | ||||
|                         val castContext = CastContext.getSharedInstance(ctx) | ||||
| 
 | ||||
|                         if (castContext.castState == CastState.CONNECTED) { | ||||
|                             clickCallback.invoke(EpisodeClickEvent(ACTION_CHROME_CAST_EPISODE, card)) | ||||
|                         } else { | ||||
|                             // clickCallback.invoke(EpisodeClickEvent(ACTION_PLAY_EPISODE_IN_PLAYER, card)) | ||||
|                             clickCallback.invoke(EpisodeClickEvent(ACTION_PLAY_EPISODE_IN_PLAYER, card)) | ||||
|                         } | ||||
|                     if (ctx.isConnectedToChromecast()) { | ||||
|                         clickCallback.invoke(EpisodeClickEvent(ACTION_CHROME_CAST_EPISODE, card)) | ||||
|                     } else { | ||||
|                         // clickCallback.invoke(EpisodeClickEvent(ACTION_PLAY_EPISODE_IN_PLAYER, card)) | ||||
|                         clickCallback.invoke(EpisodeClickEvent(ACTION_PLAY_EPISODE_IN_PLAYER, card)) //TODO REDO TO MAIN | ||||
|                         clickCallback.invoke(EpisodeClickEvent(ACTION_PLAY_EPISODE_IN_PLAYER, card)) | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             episodeHolder.setOnLongClickListener { | ||||
|                 clickCallback.invoke(EpisodeClickEvent(ACTION_SHOW_OPTIONS, card)) | ||||
| 
 | ||||
|                 return@setOnLongClickListener true | ||||
|             } | ||||
| 
 | ||||
|             episodeDownload?.setOnClickListener { | ||||
|  |  | |||
|  | @ -35,6 +35,7 @@ import com.lagradost.cloudstream3.UIHelper.colorFromAttribute | |||
| import com.lagradost.cloudstream3.UIHelper.fixPaddingStatusbar | ||||
| import com.lagradost.cloudstream3.UIHelper.getStatusBarHeight | ||||
| import com.lagradost.cloudstream3.UIHelper.isCastApiAvailable | ||||
| import com.lagradost.cloudstream3.UIHelper.isConnectedToChromecast | ||||
| import com.lagradost.cloudstream3.UIHelper.popCurrentPage | ||||
| import com.lagradost.cloudstream3.UIHelper.popupMenuNoIcons | ||||
| import com.lagradost.cloudstream3.UIHelper.popupMenuNoIconsAndNoStringres | ||||
|  | @ -43,14 +44,16 @@ import com.lagradost.cloudstream3.mvvm.observe | |||
| import com.lagradost.cloudstream3.ui.WatchType | ||||
| import com.lagradost.cloudstream3.ui.player.PlayerData | ||||
| import com.lagradost.cloudstream3.ui.player.PlayerFragment | ||||
| import com.lagradost.cloudstream3.utils.* | ||||
| import com.lagradost.cloudstream3.utils.CastHelper.startCast | ||||
| import com.lagradost.cloudstream3.utils.Coroutines.main | ||||
| import com.lagradost.cloudstream3.utils.DataStore.setKey | ||||
| import com.lagradost.cloudstream3.utils.DataStoreHelper.getViewPos | ||||
| 
 | ||||
| import com.lagradost.cloudstream3.utils.ExtractorLink | ||||
| import com.lagradost.cloudstream3.utils.VideoDownloadManager | ||||
| import com.lagradost.cloudstream3.utils.VideoDownloadManager.sanitizeFilename | ||||
| import jp.wasabeef.glide.transformations.BlurTransformation | ||||
| import kotlinx.android.synthetic.main.fragment_result.* | ||||
| import kotlinx.coroutines.Job | ||||
| 
 | ||||
| 
 | ||||
| const val MAX_SYNO_LENGH = 300 | ||||
|  | @ -134,6 +137,7 @@ class ResultFragment : Fragment() { | |||
|     private var currentLoadingCount = 0 // THIS IS USED TO PREVENT LATE EVENTS, AFTER DISMISS WAS CLICKED | ||||
|     private lateinit var viewModel: ResultViewModel | ||||
|     private var allEpisodes: HashMap<Int, ArrayList<ExtractorLink>> = HashMap() | ||||
|     private var allEpisodesSubs: HashMap<Int, ArrayList<SubtitleFile>> = HashMap() | ||||
|     private var currentHeaderName: String? = null | ||||
|     private var currentType: TvType? = null | ||||
|     private var currentEpisodes: List<ResultEpisode>? = null | ||||
|  | @ -188,6 +192,7 @@ class ResultFragment : Fragment() { | |||
|     } | ||||
| 
 | ||||
|     private var currentPoster: String? = null | ||||
|     private var currentId: Int? = null | ||||
|     private var currentIsMovie: Boolean? = null | ||||
| 
 | ||||
|     var url: String? = null | ||||
|  | @ -255,59 +260,126 @@ class ResultFragment : Fragment() { | |||
|             requireActivity().popCurrentPage() | ||||
|         } | ||||
| 
 | ||||
|         fun handleAction(episodeClick: EpisodeClickEvent) { | ||||
|         fun handleAction(episodeClick: EpisodeClickEvent): Job = main { | ||||
|             //val id = episodeClick.data.id | ||||
|             val index = episodeClick.data.index | ||||
|             val buildInPlayer = true | ||||
|             currentLoadingCount++ | ||||
|             var currentLinks: ArrayList<ExtractorLink>? = null | ||||
|             var currentSubs: ArrayList<SubtitleFile>? = null | ||||
| 
 | ||||
|             suspend fun requireLinks(isCasting: Boolean = false): Boolean { | ||||
|                 val currentLinksTemp = | ||||
|                     if (allEpisodes.containsKey(episodeClick.data.id)) allEpisodes[episodeClick.data.id] else null | ||||
|                 val currentSubsTemp = | ||||
|                     if (allEpisodes.containsKey(episodeClick.data.id)) allEpisodes[episodeClick.data.id] else null | ||||
|                 if (currentLinksTemp != null && currentLinksTemp.size > 0) { | ||||
|                     currentLinks = currentLinksTemp | ||||
|                     return true | ||||
|                 } | ||||
| 
 | ||||
|                 val skipLoading = if (apiName != null) { | ||||
|                     getApiFromName(apiName).instantLinkLoading | ||||
|                 } else false | ||||
| 
 | ||||
|                 var loadingDialog: AlertDialog? = null | ||||
|                 val currentLoad = currentLoadingCount | ||||
| 
 | ||||
|                 if (!skipLoading) { | ||||
|                     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?.dismiss() | ||||
| 
 | ||||
|                 when (data) { | ||||
|                     is Resource.Success -> { | ||||
|                         currentLinks = data.value.links | ||||
|                         currentSubs = data.value.subs | ||||
|                         return true | ||||
|                     } | ||||
|                     is Resource.Failure -> { | ||||
|                         Toast.makeText(requireContext(), R.string.error_loading_links, Toast.LENGTH_SHORT).show() | ||||
|                     } | ||||
|                     else -> { | ||||
| 
 | ||||
|                     } | ||||
|                 } | ||||
|                 return false | ||||
|             } | ||||
| 
 | ||||
|             val isLoaded = when (episodeClick.action) { | ||||
|                 ACTION_PLAY_EPISODE_IN_PLAYER -> true | ||||
|                 ACTION_CHROME_CAST_EPISODE -> requireLinks(true) | ||||
|                 ACTION_CHROME_CAST_MIRROR -> requireLinks(true) | ||||
|                 else -> requireLinks() | ||||
|             } | ||||
|             if (!isLoaded) return@main // CANT LOAD | ||||
| 
 | ||||
|             when (episodeClick.action) { | ||||
|                 ACTION_CHROME_CAST_EPISODE -> { | ||||
|                     val skipLoading = if (apiName != null) { | ||||
|                         getApiFromName(apiName).instantLinkLoading | ||||
|                     } else false | ||||
| 
 | ||||
|                 ACTION_SHOW_OPTIONS -> { | ||||
|                     val builder = AlertDialog.Builder(requireContext(), R.style.AlertDialogCustom) | ||||
|                     var dialog: AlertDialog? = null | ||||
|                     val currentLoad = currentLoadingCount | ||||
|                     builder.setTitle(episodeClick.data.name) | ||||
|                     val options = requireContext().resources.getStringArray(R.array.episode_long_click_options) | ||||
|                     val optionsValues = | ||||
|                         requireContext().resources.getIntArray(R.array.episode_long_click_options_values) | ||||
| 
 | ||||
|                     if (!skipLoading) { | ||||
|                         val builder = AlertDialog.Builder(requireContext(), R.style.AlertDialogCustomTransparent) | ||||
|                         val customLayout = layoutInflater.inflate(R.layout.dialog_loading, null) | ||||
|                         builder.setView(customLayout) | ||||
|                     val verifiedOptions = ArrayList<String>() | ||||
|                     val verifiedOptionsValues = ArrayList<Int>() | ||||
| 
 | ||||
|                         dialog = builder.create() | ||||
|                     for (i in options.indices) { | ||||
|                         val opv = optionsValues[i] | ||||
|                         val op = options[i] | ||||
| 
 | ||||
|                         dialog.show() | ||||
|                         dialog.setOnDismissListener { | ||||
|                             currentLoadingCount++ | ||||
|                         val isConnected = requireContext().isConnectedToChromecast() | ||||
|                         val add = when (opv) { | ||||
|                             ACTION_CHROME_CAST_EPISODE -> isConnected | ||||
|                             ACTION_CHROME_CAST_MIRROR -> isConnected | ||||
|                             else -> true | ||||
|                         } | ||||
|                         if (add) { | ||||
|                             verifiedOptions.add(op) | ||||
|                             verifiedOptionsValues.add(opv) | ||||
|                         } | ||||
|                     } | ||||
| 
 | ||||
|                     // Toast.makeText(activity, "Loading links", Toast.LENGTH_SHORT).show() | ||||
| 
 | ||||
|                     viewModel.loadEpisode(episodeClick.data, true) { data -> | ||||
|                         if (currentLoadingCount != currentLoad) return@loadEpisode | ||||
|                     builder.setItems( | ||||
|                         verifiedOptions.toTypedArray() | ||||
|                     ) { _, which -> | ||||
|                         handleAction(EpisodeClickEvent(verifiedOptionsValues[which], episodeClick.data)) | ||||
|                         dialog?.dismiss() | ||||
| 
 | ||||
|                         when (data) { | ||||
|                             is Resource.Failure -> { | ||||
|                                 Toast.makeText(activity, "Failed to load links", Toast.LENGTH_SHORT).show() | ||||
|                             } | ||||
|                             is Resource.Success -> { | ||||
|                                 val eps = currentEpisodes ?: return@loadEpisode | ||||
|                                 context?.startCast( | ||||
|                                     apiName ?: return@loadEpisode, | ||||
|                                     currentIsMovie ?: return@loadEpisode, | ||||
|                                     currentHeaderName, | ||||
|                                     currentPoster, | ||||
|                                     episodeClick.data.index, | ||||
|                                     eps, | ||||
|                                     sortUrls(data.value.links), | ||||
|                                     data.value.subs, | ||||
|                                     startTime = episodeClick.data.getRealPosition(), | ||||
|                                 ) | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
| 
 | ||||
|                     dialog = builder.create() | ||||
|                     dialog.show() | ||||
|                 } | ||||
| 
 | ||||
| 
 | ||||
|                 ACTION_CHROME_CAST_EPISODE -> { | ||||
| 
 | ||||
|                     val eps = currentEpisodes ?: return@main | ||||
|                     context?.startCast( | ||||
|                         apiName ?: return@main, | ||||
|                         currentIsMovie ?: return@main, | ||||
|                         currentHeaderName, | ||||
|                         currentPoster, | ||||
|                         episodeClick.data.index, | ||||
|                         eps, | ||||
|                         sortUrls(currentLinks ?: return@main), | ||||
|                         currentSubs ?: return@main, | ||||
|                         startTime = episodeClick.data.getRealPosition(), | ||||
|                     ) | ||||
|                 } | ||||
| 
 | ||||
|                 ACTION_PLAY_EPISODE_IN_PLAYER -> { | ||||
|  | @ -330,60 +402,80 @@ class ResultFragment : Fragment() { | |||
|                     } | ||||
|                 } | ||||
|                 ACTION_RELOAD_EPISODE -> { | ||||
|                     /*viewModel.load(episodeClick.data) { res -> | ||||
|                         if (res is Resource.Success) { | ||||
|                             playEpisode(allEpisodes[id], index) | ||||
|                         } | ||||
|                     }*/ | ||||
|                     viewModel.loadEpisode(episodeClick.data, false) | ||||
|                 } | ||||
|                 ACTION_DOWNLOAD_EPISODE -> { | ||||
|                     val tempUrl = url | ||||
|                     if (tempUrl != null) { | ||||
|                         viewModel.loadEpisode(episodeClick.data, true) { data -> | ||||
|                             if (data is Resource.Success) { | ||||
|                                 val isMovie = currentIsMovie ?: return@loadEpisode | ||||
|                                 val titleName = sanitizeFilename(currentHeaderName ?: return@loadEpisode) | ||||
|                                 val meta = VideoDownloadManager.DownloadEpisodeMetadata( | ||||
|                                     episodeClick.data.id, | ||||
|                                     titleName, | ||||
|                                     apiName ?: return@loadEpisode, | ||||
|                                     episodeClick.data.poster ?: currentPoster, | ||||
|                                     episodeClick.data.name, | ||||
|                                     if (isMovie) null else episodeClick.data.season, | ||||
|                                     if (isMovie) null else episodeClick.data.episode | ||||
|                                 ) | ||||
|                     val isMovie = currentIsMovie ?: return@main | ||||
|                     val titleName = sanitizeFilename(currentHeaderName ?: return@main) | ||||
| 
 | ||||
|                                 val folder = when (currentType) { | ||||
|                                     TvType.Anime -> "Anime/$titleName" | ||||
|                                     TvType.Movie -> "Movies" | ||||
|                                     TvType.TvSeries -> "TVSeries/$titleName" | ||||
|                                     TvType.ONA -> "ONA" | ||||
|                                     else -> null | ||||
|                                 } | ||||
|                     val meta = VideoDownloadManager.DownloadEpisodeMetadata( | ||||
|                         episodeClick.data.id, | ||||
|                         titleName, | ||||
|                         apiName ?: return@main, | ||||
|                         episodeClick.data.poster ?: currentPoster, | ||||
|                         episodeClick.data.name, | ||||
|                         if (isMovie) null else episodeClick.data.season, | ||||
|                         if (isMovie) null else episodeClick.data.episode | ||||
|                     ) | ||||
| 
 | ||||
|                                 VideoDownloadManager.downloadEpisode( | ||||
|                                     requireContext(), | ||||
|                                     tempUrl, | ||||
|                                     folder, | ||||
|                                     meta, | ||||
|                                     data.value.links | ||||
|                                 ) | ||||
|                             } | ||||
|                         } | ||||
|                     val folder = when (currentType) { | ||||
|                         TvType.Anime -> "Anime/$titleName" | ||||
|                         TvType.Movie -> "Movies" | ||||
|                         TvType.TvSeries -> "TVSeries/$titleName" | ||||
|                         TvType.ONA -> "ONA" | ||||
|                         else -> null | ||||
|                     } | ||||
| 
 | ||||
|                     context?.let { ctx -> | ||||
|                         // SET VISUAL KEYS | ||||
|                         ctx.setKey( | ||||
|                             DOWNLOAD_HEADER_CACHE, (currentId ?: return@let).toString(), | ||||
|                             VideoDownloadHelper.DownloadHeaderCached( | ||||
|                                 apiName, | ||||
|                                 url ?: return@let, | ||||
|                                 currentType ?: return@let, | ||||
|                                 currentHeaderName ?: return@let, | ||||
|                                 currentPoster ?: return@let, | ||||
|                                 currentId ?: return@let | ||||
|                             ) | ||||
|                         ) | ||||
| 
 | ||||
|                         val epData = episodeClick.data | ||||
|                         ctx.setKey( | ||||
|                             DOWNLOAD_EPISODE_CACHE, | ||||
|                             epData.id.toString(), | ||||
|                             VideoDownloadHelper.DownloadEpisodeCached( | ||||
|                                 epData.name, | ||||
|                                 epData.poster, | ||||
|                                 epData.episode, | ||||
|                                 epData.season, | ||||
|                                 epData.id, | ||||
|                                 currentId ?: return@let, | ||||
|                                 epData.rating, | ||||
|                                 epData.descript | ||||
|                             ) | ||||
|                         ) | ||||
| 
 | ||||
|                         // DOWNLOAD VIDEO | ||||
|                         VideoDownloadManager.downloadEpisode( | ||||
|                             ctx, | ||||
|                             url ?: return@main, | ||||
|                             folder, | ||||
|                             meta, | ||||
|                             currentLinks ?: return@main | ||||
|                         ) | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         val adapter: RecyclerView.Adapter<RecyclerView.ViewHolder>? = activity?.let { it -> | ||||
|         val adapter: RecyclerView.Adapter<RecyclerView.ViewHolder> = | ||||
|             EpisodeAdapter( | ||||
|                 it, | ||||
|                 ArrayList(), | ||||
|                 result_episodes, | ||||
|             ) { episodeClick -> | ||||
|                 handleAction(episodeClick) | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         result_episodes.adapter = adapter | ||||
|         result_episodes.layoutManager = GridLayoutManager(context, 1) | ||||
|  | @ -409,6 +501,12 @@ class ResultFragment : Fragment() { | |||
|             allEpisodes = it | ||||
|         } | ||||
| 
 | ||||
|         observe(viewModel.allEpisodesSubs) { | ||||
|             allEpisodesSubs = it | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|         observe(viewModel.selectedSeason) { season -> | ||||
|             result_season_button?.text = fromIndexToSeasonText(season) | ||||
|         } | ||||
|  | @ -437,6 +535,10 @@ class ResultFragment : Fragment() { | |||
|             (result_episodes.adapter as EpisodeAdapter).notifyDataSetChanged() | ||||
|         } | ||||
| 
 | ||||
|         observe(viewModel.id) { | ||||
|             currentId = it | ||||
|         } | ||||
| 
 | ||||
|         observe(viewModel.resultResponse) { data -> | ||||
|             when (data) { | ||||
|                 is Resource.Success -> { | ||||
|  |  | |||
|  | @ -26,7 +26,7 @@ class ResultViewModel : ViewModel() { | |||
|     private val dubStatus: MutableLiveData<DubStatus> = MutableLiveData() | ||||
| 
 | ||||
|     private val page: MutableLiveData<LoadResponse> = MutableLiveData() | ||||
|     private val id: MutableLiveData<Int> = MutableLiveData() | ||||
|     val id: MutableLiveData<Int> = MutableLiveData() | ||||
|     val selectedSeason: MutableLiveData<Int> = MutableLiveData(-2) | ||||
|     val seasonSelections: MutableLiveData<List<Int?>> = MutableLiveData() | ||||
| 
 | ||||
|  | @ -208,6 +208,41 @@ class ResultViewModel : ViewModel() { | |||
|         loadEpisode(episode.id, episode.data, isCasting, callback) | ||||
|     } | ||||
| 
 | ||||
|     suspend fun loadEpisode( | ||||
|         episode: ResultEpisode, | ||||
|         isCasting: Boolean, | ||||
|     ) : Resource<ResultViewModel.EpisodeData> { | ||||
|         return loadEpisode(episode.id, episode.data, isCasting) | ||||
|     } | ||||
| 
 | ||||
|     private suspend fun loadEpisode( | ||||
|         id: Int, | ||||
|         data: String, | ||||
|         isCasting: Boolean, | ||||
|     ): Resource<ResultViewModel.EpisodeData> { | ||||
|         if (_allEpisodes.value?.contains(id) == true) { | ||||
|             _allEpisodes.value?.remove(id) | ||||
|         } | ||||
|         val links = ArrayList<ExtractorLink>() | ||||
|         val subs = ArrayList<SubtitleFile>() | ||||
|         return safeApiCall { | ||||
|             getApiFromName(_apiName.value).loadLinks(data, isCasting, { subtitleFile -> | ||||
|                 if (!subs.any { it.url == subtitleFile.url }) { | ||||
|                     subs.add(subtitleFile) | ||||
|                     _allEpisodesSubs.value?.set(id, subs) | ||||
|                     _allEpisodesSubs.postValue(_allEpisodesSubs.value) | ||||
|                 } | ||||
|             }) { link -> | ||||
|                 if (!links.any { it.url == link.url }) { | ||||
|                     links.add(link) | ||||
|                     _allEpisodes.value?.set(id, links) | ||||
|                     _allEpisodes.postValue(_allEpisodes.value) | ||||
|                 } | ||||
|             } | ||||
|             EpisodeData(links, subs) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private fun loadEpisode( | ||||
|         id: Int, | ||||
|         data: String, | ||||
|  | @ -215,27 +250,7 @@ class ResultViewModel : ViewModel() { | |||
|         callback: (Resource<EpisodeData>) -> Unit, | ||||
|     ) = | ||||
|         viewModelScope.launch { | ||||
|             if (_allEpisodes.value?.contains(id) == true) { | ||||
|                 _allEpisodes.value?.remove(id) | ||||
|             } | ||||
|             val links = ArrayList<ExtractorLink>() | ||||
|             val subs = ArrayList<SubtitleFile>() | ||||
|             val localData = safeApiCall { | ||||
|                 getApiFromName(_apiName.value).loadLinks(data, isCasting, { subtitleFile -> | ||||
|                     if (!subs.any { it.url == subtitleFile.url }) { | ||||
|                         subs.add(subtitleFile) | ||||
|                         _allEpisodesSubs.value?.set(id, subs) | ||||
|                         _allEpisodesSubs.postValue(_allEpisodesSubs.value) | ||||
|                     } | ||||
|                 }) { link -> | ||||
|                     if (!links.any { it.url == link.url }) { | ||||
|                         links.add(link) | ||||
|                         _allEpisodes.value?.set(id, links) | ||||
|                         _allEpisodes.postValue(_allEpisodes.value) | ||||
|                     } | ||||
|                 } | ||||
|                 EpisodeData(links, subs) | ||||
|             } | ||||
|             val localData = loadEpisode(id, data, isCasting) | ||||
|             callback.invoke(localData) | ||||
|         } | ||||
| 
 | ||||
|  |  | |||
|  | @ -6,6 +6,9 @@ import com.fasterxml.jackson.databind.DeserializationFeature | |||
| import com.fasterxml.jackson.databind.json.JsonMapper | ||||
| import com.fasterxml.jackson.module.kotlin.KotlinModule | ||||
| 
 | ||||
| const val DOWNLOAD_HEADER_CACHE = "download_header_cache" | ||||
| const val DOWNLOAD_EPISODE_CACHE = "download_episode_cache" | ||||
| 
 | ||||
| const val PREFERENCES_NAME: String = "rebuild_preference" | ||||
| 
 | ||||
| object DataStore { | ||||
|  |  | |||
|  | @ -13,4 +13,27 @@ | |||
|         <!-- Actually fake to make the skip op button the same style --> | ||||
|         <item>@id/cast_button_type_forward_30_seconds</item> | ||||
|     </array> | ||||
| 
 | ||||
|     <array name="episode_long_click_options"> | ||||
|         <item>Chromecast Episode</item> | ||||
|         <item>Chromecast Mirror</item> | ||||
|         <item>Play In App</item> | ||||
|         <item>Play In External App</item> | ||||
|         <item>Play In Browser</item> | ||||
|         <item>Copy Link</item> | ||||
|         <item>Auto Download</item> | ||||
|         <item>Download Mirror</item> | ||||
|         <item>Reload Links</item> | ||||
|     </array> | ||||
|     <array name="episode_long_click_options_values"> | ||||
|         <item>4</item> | ||||
|         <item>5</item> | ||||
|         <item>1</item> | ||||
|         <item>2</item> | ||||
|         <item>3</item> | ||||
|         <item>9</item> | ||||
|         <item>6</item> | ||||
|         <item>7</item> | ||||
|         <item>8</item> | ||||
|     </array> | ||||
| </resources> | ||||
|  | @ -39,4 +39,5 @@ | |||
|     <string name="play_episode">Play Episode</string> | ||||
|     <string name="need_storage">Allow to download episodes</string> | ||||
|     <string name="download_descript">Download</string> | ||||
|     <string name="error_loading_links">Error Loading Links</string> | ||||
| </resources> | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue