mirror of
				https://github.com/recloudstream/cloudstream.git
				synced 2024-08-15 01:53:11 +00:00 
			
		
		
		
	range and download fix
This commit is contained in:
		
							parent
							
								
									de149172bc
								
							
						
					
					
						commit
						3e2836f462
					
				
					 6 changed files with 153 additions and 60 deletions
				
			
		|  | @ -419,7 +419,7 @@ object UIHelper { | |||
| 
 | ||||
|     /**id, string */ | ||||
|     @SuppressLint("RestrictedApi") | ||||
|     fun View.popupMenuNoIconsAndNoStringres( | ||||
|     fun View.popupMenuNoIconsAndNoStringRes( | ||||
|         items: List<Pair<Int, String>>, | ||||
|         onMenuItemClick: MenuItem.() -> Unit, | ||||
|     ): PopupMenu { | ||||
|  |  | |||
|  | @ -211,8 +211,11 @@ class ShiroProvider : MainAPI() { | |||
|         val mapped = response.let { mapper.readValue<AnimePage>(it.text) } | ||||
|         val data = mapped.data | ||||
|         val isDubbed = data.language == "dubbed" | ||||
|         val episodes = ArrayList<AnimeEpisode>(data.episodes?.map { AnimeEpisode(it.videos[0].video_id) } | ||||
|             ?: ArrayList<AnimeEpisode>()) | ||||
|         val episodes = | ||||
|             ArrayList<AnimeEpisode>( | ||||
|                 data.episodes?.distinctBy { it.episode_number }?.sortedBy { it.episode_number } | ||||
|                 ?.map { AnimeEpisode(it.videos[0].video_id) } | ||||
|                 ?: ArrayList<AnimeEpisode>()) | ||||
|         val status = when (data.status) { | ||||
|             "current" -> ShowStatus.Ongoing | ||||
|             "finished" -> ShowStatus.Completed | ||||
|  |  | |||
|  | @ -51,7 +51,7 @@ class EpisodeAdapter( | |||
|     @LayoutRes | ||||
|     private var layout: Int = 0 | ||||
|     fun updateLayout() { | ||||
|         layout = if (cardList.filter { it.poster != null }.size >= cardList.size / 2) | ||||
|         layout = if (cardList.filter { it.poster != null }.size >= cardList.size / 2f) // If over half has posters then use the large layout | ||||
|             R.layout.result_episode_large | ||||
|         else R.layout.result_episode | ||||
|     } | ||||
|  |  | |||
|  | @ -43,14 +43,13 @@ 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 | ||||
| import com.lagradost.cloudstream3.UIHelper.popupMenuNoIconsAndNoStringRes | ||||
| import com.lagradost.cloudstream3.UIHelper.requestRW | ||||
| import com.lagradost.cloudstream3.mvvm.Resource | ||||
| import com.lagradost.cloudstream3.mvvm.observe | ||||
| import com.lagradost.cloudstream3.ui.WatchType | ||||
| import com.lagradost.cloudstream3.ui.download.DOWNLOAD_ACTION_DOWNLOAD | ||||
| import com.lagradost.cloudstream3.ui.download.DownloadButtonSetup.handleDownloadClick | ||||
| import com.lagradost.cloudstream3.ui.download.DownloadButtonSetup.setUpButton | ||||
| import com.lagradost.cloudstream3.ui.download.DownloadButtonSetup.setUpMaterialButton | ||||
| import com.lagradost.cloudstream3.ui.player.PlayerData | ||||
| import com.lagradost.cloudstream3.ui.player.PlayerFragment | ||||
|  | @ -206,6 +205,7 @@ class ResultFragment : Fragment() { | |||
|     private var currentPoster: String? = null | ||||
|     private var currentId: Int? = null | ||||
|     private var currentIsMovie: Boolean? = null | ||||
|     private var episodeRanges: List<String>? = null | ||||
| 
 | ||||
|     var url: String? = null | ||||
| 
 | ||||
|  | @ -666,8 +666,6 @@ class ResultFragment : Fragment() { | |||
|             allEpisodesSubs = it | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|         observe(viewModel.selectedSeason) { season -> | ||||
|             result_season_button?.text = fromIndexToSeasonText(season) | ||||
|         } | ||||
|  | @ -675,7 +673,7 @@ class ResultFragment : Fragment() { | |||
|         observe(viewModel.seasonSelections) { seasonList -> | ||||
|             result_season_button?.visibility = if (seasonList.size <= 1) GONE else VISIBLE | ||||
|             result_season_button?.setOnClickListener { | ||||
|                 result_season_button?.popupMenuNoIconsAndNoStringres( | ||||
|                 result_season_button?.popupMenuNoIconsAndNoStringRes( | ||||
|                     items = seasonList | ||||
|                         .map { Pair(it ?: -2, fromIndexToSeasonText(it)) }, | ||||
|                 ) { | ||||
|  | @ -689,7 +687,6 @@ class ResultFragment : Fragment() { | |||
| 
 | ||||
|         observe(viewModel.publicEpisodes) { episodes -> | ||||
|             if (result_episodes == null || result_episodes.adapter == null) return@observe | ||||
|             result_episodes_text.text = "${episodes.size} Episode${if (episodes.size == 1) "" else "s"}" | ||||
|             currentEpisodes = episodes | ||||
|             activity?.runOnUiThread { | ||||
|                 (result_episodes.adapter as EpisodeAdapter).cardList = episodes | ||||
|  | @ -698,6 +695,28 @@ class ResultFragment : Fragment() { | |||
|             } | ||||
|         } | ||||
| 
 | ||||
|         observe(viewModel.selectedRange) { range -> | ||||
|             result_episode_select?.text = range | ||||
|         } | ||||
| 
 | ||||
|         observe(viewModel.rangeOptions) { range -> | ||||
|             episodeRanges = range | ||||
|             result_episode_select?.visibility = if (range.size <= 1) GONE else VISIBLE | ||||
|         } | ||||
| 
 | ||||
|         result_episode_select.setOnClickListener { | ||||
|             val ranges = episodeRanges | ||||
|             if (ranges != null) { | ||||
|                 it.popupMenuNoIconsAndNoStringRes(ranges.mapIndexed { index, s -> Pair(index, s) }.toList()) { | ||||
|                     viewModel.changeRange(requireContext(), itemId) | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         observe(viewModel.publicEpisodesCount) { count -> | ||||
|             result_episodes_text.text = "$count Episode${if (count == 1) "" else "s"}" | ||||
|         } | ||||
| 
 | ||||
|         observe(viewModel.id) { | ||||
|             currentId = it | ||||
|         } | ||||
|  | @ -820,6 +839,7 @@ class ResultFragment : Fragment() { | |||
|                         } | ||||
| 
 | ||||
|                         if (d.type == TvType.Movie && d is MovieLoadResponse) { | ||||
|                             val hasDownloadSupport = api.hasDownloadSupport | ||||
|                             result_movie_parent.visibility = VISIBLE | ||||
|                             result_episodes_text.visibility = GONE | ||||
|                             result_episodes.visibility = GONE | ||||
|  | @ -835,54 +855,58 @@ class ResultFragment : Fragment() { | |||
|                                 return@setOnLongClickListener true | ||||
|                             } | ||||
| 
 | ||||
| 
 | ||||
| //                            result_options.setOnClickListener { | ||||
| //                                val card = currentEpisodes?.first() ?: return@setOnClickListener | ||||
| //                                handleAction(EpisodeClickEvent(ACTION_SHOW_OPTIONS, card)) | ||||
| //                            } | ||||
| 
 | ||||
|                             val localId = d.getId() | ||||
|                             val file = | ||||
|                                 VideoDownloadManager.getDownloadFileInfoAndUpdateSettings(requireContext(), localId) | ||||
|                             result_download_movie.visibility = if (hasDownloadSupport) VISIBLE else GONE | ||||
|                             if (hasDownloadSupport) { | ||||
|                                 val localId = d.getId() | ||||
|                                 val file = | ||||
|                                     VideoDownloadManager.getDownloadFileInfoAndUpdateSettings(requireContext(), localId) | ||||
| 
 | ||||
|                             setUpMaterialButton( | ||||
|                                 file?.fileLength, | ||||
|                                 file?.totalBytes, | ||||
|                                 result_movie_progress_downloaded, | ||||
|                                 result_download_movie, | ||||
|                                 result_movie_text_progress, | ||||
|                                 VideoDownloadHelper.DownloadEpisodeCached( | ||||
|                                     d.name, | ||||
|                                     d.posterUrl, | ||||
|                                     0, | ||||
|                                     null, | ||||
|                                     localId, | ||||
|                                     localId, | ||||
|                                     d.rating, | ||||
|                                     d.plot | ||||
|                                 ) | ||||
|                             ) { downloadClickEvent -> | ||||
|                                 if (downloadClickEvent.action == DOWNLOAD_ACTION_DOWNLOAD) { | ||||
|                                     handleAction( | ||||
|                                         EpisodeClickEvent( | ||||
|                                             ACTION_DOWNLOAD_EPISODE, | ||||
|                                             ResultEpisode( | ||||
|                                                 d.name, | ||||
|                                                 null, | ||||
|                                                 0, | ||||
|                                                 null, | ||||
|                                                 d.dataUrl, | ||||
|                                                 d.apiName, | ||||
|                                                 localId, | ||||
|                                                 0, | ||||
|                                                 0L, | ||||
|                                                 0L, | ||||
|                                                 null, | ||||
|                                                 null | ||||
|                                 setUpMaterialButton( | ||||
|                                     file?.fileLength, | ||||
|                                     file?.totalBytes, | ||||
|                                     result_movie_progress_downloaded, | ||||
|                                     result_download_movie, | ||||
|                                     result_movie_text_progress, | ||||
|                                     VideoDownloadHelper.DownloadEpisodeCached( | ||||
|                                         d.name, | ||||
|                                         d.posterUrl, | ||||
|                                         0, | ||||
|                                         null, | ||||
|                                         localId, | ||||
|                                         localId, | ||||
|                                         d.rating, | ||||
|                                         d.plot | ||||
|                                     ) | ||||
|                                 ) { downloadClickEvent -> | ||||
|                                     if (downloadClickEvent.action == DOWNLOAD_ACTION_DOWNLOAD) { | ||||
|                                         handleAction( | ||||
|                                             EpisodeClickEvent( | ||||
|                                                 ACTION_DOWNLOAD_EPISODE, | ||||
|                                                 ResultEpisode( | ||||
|                                                     d.name, | ||||
|                                                     null, | ||||
|                                                     0, | ||||
|                                                     null, | ||||
|                                                     d.dataUrl, | ||||
|                                                     d.apiName, | ||||
|                                                     localId, | ||||
|                                                     0, | ||||
|                                                     0L, | ||||
|                                                     0L, | ||||
|                                                     null, | ||||
|                                                     null | ||||
|                                                 ) | ||||
|                                             ) | ||||
|                                         ) | ||||
|                                     ) | ||||
|                                 } else { | ||||
|                                     handleDownloadClick(activity, currentHeaderName, downloadClickEvent) | ||||
|                                     } else { | ||||
|                                         handleDownloadClick(activity, currentHeaderName, downloadClickEvent) | ||||
|                                     } | ||||
|                                 } | ||||
|                             } | ||||
|                         } else { | ||||
|  |  | |||
|  | @ -16,13 +16,23 @@ import com.lagradost.cloudstream3.utils.DataStoreHelper.setResultWatchState | |||
| import com.lagradost.cloudstream3.utils.ExtractorLink | ||||
| import kotlinx.coroutines.launch | ||||
| 
 | ||||
| const val EPISODE_RANGE_SIZE = 50 | ||||
| const val EPISODE_RANGE_OVERLOAD = 60 | ||||
| 
 | ||||
| class ResultViewModel : ViewModel() { | ||||
|     private val _resultResponse: MutableLiveData<Resource<Any?>> = MutableLiveData() | ||||
|     private val _episodes: MutableLiveData<List<ResultEpisode>> = MutableLiveData() | ||||
|     private val _publicEpisodes: MutableLiveData<List<ResultEpisode>> = MutableLiveData() | ||||
|     private val _publicEpisodesCount: MutableLiveData<Int> = MutableLiveData() // before the sorting | ||||
|     private val _rangeOptions: MutableLiveData<List<String>> = MutableLiveData() | ||||
|     val selectedRange: MutableLiveData<String> = MutableLiveData() | ||||
|     private val selectedRangeInt: MutableLiveData<Int> = MutableLiveData() | ||||
|     val rangeOptions: LiveData<List<String>> = _rangeOptions | ||||
| 
 | ||||
|     val resultResponse: LiveData<Resource<Any?>> get() = _resultResponse | ||||
|     val episodes: LiveData<List<ResultEpisode>> get() = _episodes | ||||
|     val publicEpisodes: LiveData<List<ResultEpisode>> get() = _publicEpisodes | ||||
|     val publicEpisodesCount: LiveData<Int> get() = _publicEpisodesCount | ||||
| 
 | ||||
|     private val dubStatus: MutableLiveData<DubStatus> = MutableLiveData() | ||||
| 
 | ||||
|  | @ -46,7 +56,7 @@ class ResultViewModel : ViewModel() { | |||
|         _watchStatus.postValue(currentWatch) | ||||
|     } | ||||
| 
 | ||||
|     private fun filterEpisodes(context: Context, list: List<ResultEpisode>?, selection: Int?) { | ||||
|     private fun filterEpisodes(context: Context, list: List<ResultEpisode>?, selection: Int?, range: Int?) { | ||||
|         if (list == null) return | ||||
|         val seasonTypes = HashMap<Int?, Boolean>() | ||||
|         for (i in list) { | ||||
|  | @ -62,11 +72,51 @@ class ResultViewModel : ViewModel() { | |||
|         if (internalId != null) context.setResultSeason(internalId, realSelection) | ||||
| 
 | ||||
|         selectedSeason.postValue(realSelection ?: -2) | ||||
|         _publicEpisodes.postValue(list.filter { it.season == realSelection }) | ||||
| 
 | ||||
|         var currentList = list.filter { it.season == realSelection } | ||||
|         _publicEpisodesCount.postValue(currentList.size) | ||||
| 
 | ||||
|         val rangeList = ArrayList<String>() | ||||
|         for (i in currentList.indices step EPISODE_RANGE_SIZE) { | ||||
|             if (i + EPISODE_RANGE_SIZE < currentList.size) { | ||||
|                 rangeList.add("${i + 1}-${i + EPISODE_RANGE_SIZE}") | ||||
|             } else { | ||||
|                 rangeList.add("${i + 1}-${currentList.size}") | ||||
|             } | ||||
|         } | ||||
|         _rangeOptions.postValue(rangeList) | ||||
| 
 | ||||
|         val cRange = range ?: if (selection != null) { | ||||
|             0 | ||||
|         } else { | ||||
|             selectedRangeInt.value ?: 0 | ||||
|         } | ||||
| 
 | ||||
|         val realRange = if (cRange * EPISODE_RANGE_SIZE > currentList.size) { | ||||
|             currentList.size / EPISODE_RANGE_SIZE | ||||
|         } else { | ||||
|             cRange | ||||
|         } | ||||
| 
 | ||||
|         selectedRangeInt.postValue(realRange) | ||||
|         selectedRange.postValue(rangeList[realRange]) | ||||
| 
 | ||||
|         if (currentList.size > EPISODE_RANGE_OVERLOAD) { | ||||
|             currentList = currentList.subList( | ||||
|                 realRange * EPISODE_RANGE_SIZE, | ||||
|                 minOf(currentList.size, (realRange + 1) * EPISODE_RANGE_SIZE) | ||||
|             ) | ||||
|         } | ||||
| 
 | ||||
|         _publicEpisodes.postValue(currentList) | ||||
|     } | ||||
| 
 | ||||
|     fun changeSeason(context: Context, selection: Int?) { | ||||
|         filterEpisodes(context, _episodes.value, selection) | ||||
|         filterEpisodes(context, _episodes.value, selection, null) | ||||
|     } | ||||
| 
 | ||||
|     fun changeRange(context: Context, range: Int?) { | ||||
|         filterEpisodes(context, _episodes.value, null, range) | ||||
|     } | ||||
| 
 | ||||
|     private fun updateEpisodes(context: Context, localId: Int?, list: List<ResultEpisode>, selection: Int?) { | ||||
|  | @ -74,7 +124,7 @@ class ResultViewModel : ViewModel() { | |||
|         filterEpisodes( | ||||
|             context, | ||||
|             list, | ||||
|             if (selection == -1) context.getResultSeason(localId ?: id.value ?: return) else selection | ||||
|             if (selection == -1) context.getResultSeason(localId ?: id.value ?: return) else selection, null | ||||
|         ) | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -191,7 +191,7 @@ | |||
|                         android:layout_width="match_parent" | ||||
|                         android:layout_height="50dp"> | ||||
|                     <com.google.android.material.button.MaterialButton | ||||
|                             android:visibility="visible" | ||||
|                             android:visibility="gone" | ||||
|                             android:layout_gravity="center_vertical" | ||||
|                             app:cornerRadius="4dp" | ||||
|                             android:id="@+id/result_bookmark_button" | ||||
|  | @ -202,7 +202,7 @@ | |||
|                             app:iconTint="?attr/textColor" | ||||
|                             android:textAllCaps="false" | ||||
|                             app:icon="@drawable/ic_outline_remove_red_eye_24" | ||||
|                             android:backgroundTint="@color/itemBackground" | ||||
|                             android:backgroundTint="?attr/grayBackground" | ||||
|                             style="@style/Widget.MaterialComponents.Button" | ||||
|                             android:layout_width="wrap_content" | ||||
|                             android:layout_height="50dp"> | ||||
|  | @ -345,7 +345,7 @@ | |||
|                             android:max="100" | ||||
|                             android:layout_gravity="end|center_vertical" | ||||
|                             android:progress="0" | ||||
|                             android:visibility="visible" | ||||
|                             android:visibility="gone" | ||||
|                             tools:visibility="visible"/> | ||||
|                     <!-- | ||||
|                     <androidx.constraintlayout.widget.ConstraintLayout | ||||
|  | @ -401,11 +401,27 @@ | |||
|                     <com.google.android.material.button.MaterialButton | ||||
|                             android:visibility="visible" | ||||
|                             android:layout_gravity="center_vertical" | ||||
|                             app:cornerRadius="4dp" | ||||
|                             android:id="@+id/result_season_button" | ||||
|                             tools:text="Subbed" | ||||
|                             tools:text="Season 1" | ||||
| 
 | ||||
|                             app:rippleColor="?attr/bitDarkerGrayBackground" | ||||
|                             app:rippleColor="?attr/grayBackground" | ||||
|                             android:textColor="?attr/textColor" | ||||
|                             app:iconTint="?attr/textColor" | ||||
|                             android:textAllCaps="false" | ||||
| 
 | ||||
|                             android:backgroundTint="@color/itemBackground" | ||||
|                             style="@style/Widget.MaterialComponents.Button" | ||||
|                             android:layout_width="wrap_content" | ||||
|                             android:layout_marginEnd="10dp" | ||||
|                             android:layout_height="50dp"> | ||||
|                     </com.google.android.material.button.MaterialButton> | ||||
|                     <com.google.android.material.button.MaterialButton | ||||
|                             android:visibility="visible" | ||||
|                             android:layout_gravity="center_vertical" | ||||
|                             android:id="@+id/result_episode_select" | ||||
|                             tools:text="50-100" | ||||
| 
 | ||||
|                             app:rippleColor="?attr/grayBackground" | ||||
|                             android:textColor="?attr/textColor" | ||||
|                             app:iconTint="?attr/textColor" | ||||
|                             android:textAllCaps="false" | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue