mirror of
				https://github.com/recloudstream/cloudstream.git
				synced 2024-08-15 01:53:11 +00:00 
			
		
		
		
	Merge remote-tracking branch 'origin/master'
This commit is contained in:
		
						commit
						20eafc2e89
					
				
					 8 changed files with 157 additions and 126 deletions
				
			
		|  | @ -35,6 +35,7 @@ import com.lagradost.cloudstream3.mvvm.logError | |||
| import com.lagradost.cloudstream3.receivers.VideoDownloadRestartReceiver | ||||
| import com.lagradost.cloudstream3.ui.APIRepository | ||||
| import com.lagradost.cloudstream3.ui.download.DOWNLOAD_NAVIGATE_TO | ||||
| import com.lagradost.cloudstream3.utils.AppUtils.isCastApiAvailable | ||||
| import com.lagradost.cloudstream3.utils.AppUtils.loadResult | ||||
| import com.lagradost.cloudstream3.utils.DataStore.getKey | ||||
| import com.lagradost.cloudstream3.utils.DataStore.removeKey | ||||
|  | @ -117,14 +118,26 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { | |||
| 
 | ||||
|     override fun onResume() { | ||||
|         super.onResume() | ||||
|         mCastSession = mSessionManager.currentCastSession | ||||
|         mSessionManager.addSessionManagerListener(mSessionManagerListener) | ||||
|         try { | ||||
|             if(isCastApiAvailable()) { | ||||
|                 mCastSession = mSessionManager.currentCastSession | ||||
|                 mSessionManager.addSessionManagerListener(mSessionManagerListener) | ||||
|             } | ||||
|         } catch (e : Exception) { | ||||
|             logError(e) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     override fun onPause() { | ||||
|         super.onPause() | ||||
|         mSessionManager.removeSessionManagerListener(mSessionManagerListener) | ||||
|         mCastSession = null | ||||
|         try { | ||||
|             if(isCastApiAvailable()) { | ||||
|                 mSessionManager.removeSessionManagerListener(mSessionManagerListener) | ||||
|                 mCastSession = null | ||||
|             } | ||||
|         } catch (e : Exception) { | ||||
|             logError(e) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     companion object { | ||||
|  | @ -188,7 +201,6 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { | |||
|         fun Context.updateLocale() { | ||||
|             val settingsManager = PreferenceManager.getDefaultSharedPreferences(this) | ||||
|             val localeCode = settingsManager.getString(getString(R.string.locale_key), null) | ||||
|             println("LOCALE: " + localeCode) | ||||
|             setLocale(this, localeCode) | ||||
|         } | ||||
|     } | ||||
|  | @ -219,43 +231,9 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private fun AppCompatActivity.backPressed(): Boolean { | ||||
|         /*val currentFragment = supportFragmentManager.fragments.last { | ||||
|             it.isVisible | ||||
|         } | ||||
| 
 | ||||
|         if (currentFragment is NavHostFragment) { | ||||
|             val child = currentFragment.childFragmentManager.fragments.last { | ||||
|                 it.isVisible | ||||
|             } | ||||
|             if (child is DownloadChildFragment) { | ||||
|                 val navController = findNavController(R.id.nav_host_fragment) | ||||
|                 navController.navigate(R.id.navigation_downloads, Bundle(), navOptions) | ||||
|                 return true | ||||
|             } | ||||
|             if (child is SearchFragment || child is HomeFragment || child is DownloadFragment || child is SettingsFragment) { | ||||
|                 this.finish() | ||||
|                 return true | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if (currentFragment != null && supportFragmentManager.fragments.size > 2) { | ||||
|             //MainActivity.showNavbar() | ||||
|             supportFragmentManager.beginTransaction() | ||||
|                 .setCustomAnimations(R.anim.enter_anim, R.anim.exit_anim, R.anim.pop_enter, R.anim.pop_exit) | ||||
|                 .remove(currentFragment) | ||||
|                 .commitAllowingStateLoss() | ||||
|             backEvent.invoke(true) | ||||
|             return true | ||||
|         } | ||||
|         */ | ||||
|         backEvent.invoke(false) | ||||
|         return false | ||||
|     } | ||||
| 
 | ||||
|     override fun onBackPressed() { | ||||
|         this.updateLocale() | ||||
|         if (backPressed()) return | ||||
|         backEvent.invoke(true) | ||||
|         super.onBackPressed() | ||||
|         this.updateLocale() | ||||
|     } | ||||
|  | @ -315,7 +293,13 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { | |||
|         ) // THEME IS SET BEFORE VIEW IS CREATED TO APPLY THE THEME TO THE MAIN VIEW | ||||
|         updateLocale() | ||||
|         super.onCreate(savedInstanceState) | ||||
|         mSessionManager = CastContext.getSharedInstance(this).sessionManager | ||||
|         try { | ||||
|             if(isCastApiAvailable()) { | ||||
|                 mSessionManager = CastContext.getSharedInstance(this).sessionManager | ||||
|             } | ||||
|         } catch (e : Exception) { | ||||
|             logError(e) | ||||
|         } | ||||
| 
 | ||||
|         window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN) | ||||
| 
 | ||||
|  |  | |||
|  | @ -7,6 +7,8 @@ import com.lagradost.cloudstream3.utils.Qualities | |||
| import com.lagradost.cloudstream3.utils.loadExtractor | ||||
| import org.jsoup.Jsoup | ||||
| import org.jsoup.nodes.Document | ||||
| import java.lang.Thread.sleep | ||||
| import java.net.URLDecoder | ||||
| 
 | ||||
| class AllMoviesForYouProvider : MainAPI() { | ||||
|     companion object { | ||||
|  | @ -79,7 +81,7 @@ class AllMoviesForYouProvider : MainAPI() { | |||
|             document.selectFirst("div.Vote > div.post-ratings > span")?.text()?.toFloatOrNull()?.times(10)?.toInt() | ||||
|         val year = document.selectFirst("span.Date")?.text() | ||||
|         val duration = document.selectFirst("span.Time").text() | ||||
|         val backgroundPoster = fixUrl(document.selectFirst("div.Image > figure > img").attr("src")) | ||||
|         val backgroundPoster = fixUrl(document.selectFirst("div.Image > figure > img").attr("data-src")) | ||||
| 
 | ||||
|         if (type == TvType.TvSeries) { | ||||
|             val list = ArrayList<Pair<Int, String>>() | ||||
|  | @ -142,7 +144,7 @@ class AllMoviesForYouProvider : MainAPI() { | |||
|                 url, | ||||
|                 this.name, | ||||
|                 type, | ||||
|                 mapper.writeValueAsString(data), | ||||
|                 mapper.writeValueAsString(data.filter { it != "about:blank" }), | ||||
|                 backgroundPoster, | ||||
|                 year?.toIntOrNull(), | ||||
|                 descipt, | ||||
|  | @ -170,9 +172,43 @@ class AllMoviesForYouProvider : MainAPI() { | |||
|             } | ||||
|             return false | ||||
|         } else if (data.startsWith(mainUrl) && data != mainUrl) { | ||||
|             val realDataUrl = data.replace("&", "&").replace("&", "&") | ||||
|             val realDataUrl = URLDecoder.decode(data, "application/x-www-form-urlencoded") | ||||
|             if (data.contains("trdownload")) { | ||||
|                 callback(ExtractorLink(this.name, this.name, realDataUrl, mainUrl, Qualities.Unknown.value)) | ||||
|                 val request = khttp.get(data, stream = true) | ||||
|                 if (request.url.startsWith("https://streamhub.to/d/")) { | ||||
|                     val document = Jsoup.parse(request.text) | ||||
|                     val inputs = document.select("Form > input") | ||||
|                     if (inputs.size < 4) return false | ||||
|                     var op: String? = null | ||||
|                     var id: String? = null | ||||
|                     var mode: String? = null | ||||
|                     var hash: String? = null | ||||
| 
 | ||||
|                     for (input in inputs) { | ||||
|                         val value = input.attr("value") ?: continue | ||||
|                         when (input.attr("name")) { | ||||
|                             "op" -> op = value | ||||
|                             "id" -> id = value | ||||
|                             "mode" -> mode = value | ||||
|                             "hash" -> hash = value | ||||
|                             else -> { | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                     if(op == null || id == null || mode == null || hash == null) { | ||||
|                         return false | ||||
|                     } | ||||
|                     sleep(5000) // ye this is needed, wont work with 0 delay | ||||
| 
 | ||||
|                     val postResponse = khttp.post(request.url, headers = mapOf("content-type" to "application/x-www-form-urlencoded", "referer" to request.url, "user-agent" to USER_AGENT, "accept" to "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"), data = mapOf("op" to op, "id" to id, "mode" to mode, "hash" to hash)) | ||||
|                     val postDocument = Jsoup.parse(postResponse.text) | ||||
| 
 | ||||
|                     val url = postDocument.selectFirst("a.downloadbtn").attr("href") | ||||
|                     if(url.isNullOrEmpty()) return false | ||||
|                     callback(ExtractorLink(this.name, this.name, url, mainUrl, Qualities.Unknown.value)) | ||||
|                 } else { | ||||
|                     callback(ExtractorLink(this.name, this.name, realDataUrl, mainUrl, Qualities.Unknown.value)) | ||||
|                 } | ||||
|                 return true | ||||
|             } | ||||
|             val response = khttp.get(realDataUrl) | ||||
|  | @ -188,4 +224,4 @@ class AllMoviesForYouProvider : MainAPI() { | |||
|             return true | ||||
|         } | ||||
|     } | ||||
| } | ||||
| } | ||||
|  |  | |||
|  | @ -14,7 +14,6 @@ import android.widget.TextView | |||
| import androidx.core.view.isVisible | ||||
| import androidx.fragment.app.Fragment | ||||
| import androidx.fragment.app.activityViewModels | ||||
| import androidx.lifecycle.ViewModelProvider | ||||
| import androidx.recyclerview.widget.GridLayoutManager | ||||
| import androidx.recyclerview.widget.RecyclerView | ||||
| import com.google.android.material.bottomsheet.BottomSheetDialog | ||||
|  |  | |||
|  | @ -53,6 +53,7 @@ class HomeViewModel : ViewModel() { | |||
|     val resumeWatching: LiveData<List<SearchResponse>> = _resumeWatching | ||||
| 
 | ||||
|     fun loadResumeWatching(context: Context) = viewModelScope.launch { | ||||
|         println("Resume::") | ||||
|         val resumeWatching = withContext(Dispatchers.IO) { | ||||
|             context.getAllResumeStateIds().mapNotNull { id -> | ||||
|                 context.getLastWatched(id) | ||||
|  |  | |||
|  | @ -70,10 +70,7 @@ import com.lagradost.cloudstream3.MainActivity.Companion.canEnterPipMode | |||
| import com.lagradost.cloudstream3.MainActivity.Companion.isInPIPMode | ||||
| import com.lagradost.cloudstream3.MainActivity.Companion.showToast | ||||
| import com.lagradost.cloudstream3.R | ||||
| import com.lagradost.cloudstream3.mvvm.Resource | ||||
| import com.lagradost.cloudstream3.mvvm.normalSafeApiCall | ||||
| import com.lagradost.cloudstream3.mvvm.observe | ||||
| import com.lagradost.cloudstream3.mvvm.observeDirectly | ||||
| import com.lagradost.cloudstream3.mvvm.* | ||||
| import com.lagradost.cloudstream3.ui.result.ResultEpisode | ||||
| import com.lagradost.cloudstream3.ui.result.ResultViewModel | ||||
| import com.lagradost.cloudstream3.ui.subtitles.SaveCaptionStyle | ||||
|  | @ -1046,79 +1043,83 @@ class PlayerFragment : Fragment() { | |||
|         }*/ | ||||
| 
 | ||||
|         if (activity?.isCastApiAvailable() == true && !isDownloadedFile) { | ||||
|             CastButtonFactory.setUpMediaRouteButton(activity, player_media_route_button) | ||||
|             val castContext = CastContext.getSharedInstance(requireContext()) | ||||
|             try { | ||||
|                 CastButtonFactory.setUpMediaRouteButton(activity, player_media_route_button) | ||||
|                 val castContext = CastContext.getSharedInstance(requireContext()) | ||||
| 
 | ||||
|             if (castContext.castState != CastState.NO_DEVICES_AVAILABLE) player_media_route_button.visibility = VISIBLE | ||||
|             castContext.addCastStateListener { state -> | ||||
|                 if (player_media_route_button != null) { | ||||
|                     player_media_route_button.isVisible = state != CastState.NO_DEVICES_AVAILABLE | ||||
|                 if (castContext.castState != CastState.NO_DEVICES_AVAILABLE) player_media_route_button.visibility = VISIBLE | ||||
|                 castContext.addCastStateListener { state -> | ||||
|                     if (player_media_route_button != null) { | ||||
|                         player_media_route_button.isVisible = state != CastState.NO_DEVICES_AVAILABLE | ||||
| 
 | ||||
|                     if (state == CastState.CONNECTED) { | ||||
|                         if (!this::exoPlayer.isInitialized) return@addCastStateListener | ||||
|                         val links = sortUrls(getUrls() ?: return@addCastStateListener) | ||||
|                         val epData = getEpisode() ?: return@addCastStateListener | ||||
|                         if (state == CastState.CONNECTED) { | ||||
|                             if (!this::exoPlayer.isInitialized) return@addCastStateListener | ||||
|                             val links = sortUrls(getUrls() ?: return@addCastStateListener) | ||||
|                             val epData = getEpisode() ?: return@addCastStateListener | ||||
| 
 | ||||
|                         val index = links.indexOf(getCurrentUrl()) | ||||
|                         (activity as MainActivity?)?.mCastSession?.startCast( | ||||
|                             apiName, | ||||
|                             currentIsMovie ?: return@addCastStateListener, | ||||
|                             currentHeaderName, | ||||
|                             currentPoster, | ||||
|                             epData.index, | ||||
|                             episodes, | ||||
|                             links, | ||||
|                             context?.getSubs(supportsDownloadedFiles = false) ?: emptyList(), | ||||
|                             index, | ||||
|                             exoPlayer.currentPosition | ||||
|                         ) | ||||
|                             val index = links.indexOf(getCurrentUrl()) | ||||
|                             (activity as MainActivity?)?.mCastSession?.startCast( | ||||
|                                 apiName, | ||||
|                                 currentIsMovie ?: return@addCastStateListener, | ||||
|                                 currentHeaderName, | ||||
|                                 currentPoster, | ||||
|                                 epData.index, | ||||
|                                 episodes, | ||||
|                                 links, | ||||
|                                 context?.getSubs(supportsDownloadedFiles = false) ?: emptyList(), | ||||
|                                 index, | ||||
|                                 exoPlayer.currentPosition | ||||
|                             ) | ||||
| 
 | ||||
|                         /* | ||||
|                         val customData = | ||||
|                             links.map { JSONObject().put("name", it.name) } | ||||
|                         val jsonArray = JSONArray() | ||||
|                         for (item in customData) { | ||||
|                             jsonArray.put(item) | ||||
|                         } | ||||
| 
 | ||||
|                         val mediaItems = links.map { | ||||
|                             val movieMetadata = MediaMetadata(MediaMetadata.MEDIA_TYPE_MOVIE) | ||||
| 
 | ||||
|                             movieMetadata.putString(MediaMetadata.KEY_SUBTITLE, | ||||
|                                 epData.name ?: "Episode ${epData.episode}") | ||||
| 
 | ||||
|                             if (currentHeaderName != null) | ||||
|                                 movieMetadata.putString(MediaMetadata.KEY_TITLE, currentHeaderName) | ||||
| 
 | ||||
|                             val srcPoster = epData.poster ?: currentPoster | ||||
|                             if (srcPoster != null) { | ||||
|                                 movieMetadata.addImage(WebImage(Uri.parse(srcPoster))) | ||||
|                             /* | ||||
|                             val customData = | ||||
|                                 links.map { JSONObject().put("name", it.name) } | ||||
|                             val jsonArray = JSONArray() | ||||
|                             for (item in customData) { | ||||
|                                 jsonArray.put(item) | ||||
|                             } | ||||
| 
 | ||||
|                             MediaQueueItem.Builder( | ||||
|                                 MediaInfo.Builder(it.url) | ||||
|                                     .setStreamType(MediaInfo.STREAM_TYPE_BUFFERED) | ||||
|                                     .setContentType(MimeTypes.VIDEO_UNKNOWN) | ||||
|                             val mediaItems = links.map { | ||||
|                                 val movieMetadata = MediaMetadata(MediaMetadata.MEDIA_TYPE_MOVIE) | ||||
| 
 | ||||
|                                     .setCustomData(JSONObject().put("data", jsonArray)) | ||||
|                                     .setMetadata(movieMetadata) | ||||
|                                 movieMetadata.putString(MediaMetadata.KEY_SUBTITLE, | ||||
|                                     epData.name ?: "Episode ${epData.episode}") | ||||
| 
 | ||||
|                                 if (currentHeaderName != null) | ||||
|                                     movieMetadata.putString(MediaMetadata.KEY_TITLE, currentHeaderName) | ||||
| 
 | ||||
|                                 val srcPoster = epData.poster ?: currentPoster | ||||
|                                 if (srcPoster != null) { | ||||
|                                     movieMetadata.addImage(WebImage(Uri.parse(srcPoster))) | ||||
|                                 } | ||||
| 
 | ||||
|                                 MediaQueueItem.Builder( | ||||
|                                     MediaInfo.Builder(it.url) | ||||
|                                         .setStreamType(MediaInfo.STREAM_TYPE_BUFFERED) | ||||
|                                         .setContentType(MimeTypes.VIDEO_UNKNOWN) | ||||
| 
 | ||||
|                                         .setCustomData(JSONObject().put("data", jsonArray)) | ||||
|                                         .setMetadata(movieMetadata) | ||||
|                                         .build() | ||||
|                                 ) | ||||
|                                     .build() | ||||
|                             ) | ||||
|                                 .build() | ||||
|                         }.toTypedArray() | ||||
|                             }.toTypedArray() | ||||
| 
 | ||||
|                         val castPlayer = CastPlayer(castContext) | ||||
|                         castPlayer.loadItems( | ||||
|                             mediaItems, | ||||
|                             if (index > 0) index else 0, | ||||
|                             exoPlayer.currentPosition, | ||||
|                             MediaStatus.REPEAT_MODE_REPEAT_SINGLE | ||||
|                         )*/ | ||||
|                         //  activity?.popCurrentPage(isInPlayer = true, isInExpandedView = false, isInResults = false) | ||||
|                         safeReleasePlayer() | ||||
|                         activity?.popCurrentPage() | ||||
|                             val castPlayer = CastPlayer(castContext) | ||||
|                             castPlayer.loadItems( | ||||
|                                 mediaItems, | ||||
|                                 if (index > 0) index else 0, | ||||
|                                 exoPlayer.currentPosition, | ||||
|                                 MediaStatus.REPEAT_MODE_REPEAT_SINGLE | ||||
|                             )*/ | ||||
|                             //  activity?.popCurrentPage(isInPlayer = true, isInExpandedView = false, isInResults = false) | ||||
|                             safeReleasePlayer() | ||||
|                             activity?.popCurrentPage() | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } catch (e : Exception) { | ||||
|                 logError(e) | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|  |  | |||
|  | @ -17,7 +17,6 @@ import android.view.View.VISIBLE | |||
| import android.view.ViewGroup | ||||
| import android.widget.Toast | ||||
| import androidx.appcompat.app.AlertDialog | ||||
| import androidx.appcompat.app.AppCompatActivity | ||||
| import androidx.coordinatorlayout.widget.CoordinatorLayout | ||||
| import androidx.core.content.ContextCompat | ||||
| import androidx.core.content.FileProvider | ||||
|  | @ -37,8 +36,8 @@ import com.lagradost.cloudstream3.* | |||
| import com.lagradost.cloudstream3.APIHolder.getApiFromName | ||||
| import com.lagradost.cloudstream3.APIHolder.getId | ||||
| import com.lagradost.cloudstream3.MainActivity.Companion.showToast | ||||
| import com.lagradost.cloudstream3.MainActivity.Companion.updateLocale | ||||
| import com.lagradost.cloudstream3.mvvm.Resource | ||||
| import com.lagradost.cloudstream3.mvvm.logError | ||||
| import com.lagradost.cloudstream3.mvvm.observe | ||||
| import com.lagradost.cloudstream3.ui.WatchType | ||||
| import com.lagradost.cloudstream3.ui.download.DOWNLOAD_ACTION_DOWNLOAD | ||||
|  | @ -304,16 +303,20 @@ class ResultFragment : Fragment() { | |||
| 
 | ||||
|             activity?.let { | ||||
|                 if (it.isCastApiAvailable()) { | ||||
|                     CastButtonFactory.setUpMediaRouteButton(it, media_route_button) | ||||
|                     val castContext = CastContext.getSharedInstance(it.applicationContext) | ||||
|                     try { | ||||
|                         CastButtonFactory.setUpMediaRouteButton(it, media_route_button) | ||||
|                         val castContext = CastContext.getSharedInstance(it.applicationContext) | ||||
| 
 | ||||
|                     if (castContext.castState != CastState.NO_DEVICES_AVAILABLE) media_route_button.visibility = VISIBLE | ||||
|                     castContext.addCastStateListener { state -> | ||||
|                         if (media_route_button != null) { | ||||
|                             if (state == CastState.NO_DEVICES_AVAILABLE) media_route_button.visibility = GONE else { | ||||
|                                 if (media_route_button.visibility == GONE) media_route_button.visibility = VISIBLE | ||||
|                         if (castContext.castState != CastState.NO_DEVICES_AVAILABLE) media_route_button.visibility = VISIBLE | ||||
|                         castContext.addCastStateListener { state -> | ||||
|                             if (media_route_button != null) { | ||||
|                                 if (state == CastState.NO_DEVICES_AVAILABLE) media_route_button.visibility = GONE else { | ||||
|                                     if (media_route_button.visibility == GONE) media_route_button.visibility = VISIBLE | ||||
|                                 } | ||||
|                             } | ||||
|                         } | ||||
|                     } catch (e : Exception) { | ||||
|                         logError(e) | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|  |  | |||
|  | @ -110,16 +110,23 @@ class SettingsFragment : PreferenceFragmentCompat() { | |||
|                     currentList.add(allLangs.indexOf(i)) | ||||
|                 } | ||||
| 
 | ||||
|                 val names = allLangs.mapNotNull { SubtitleHelper.fromTwoLettersToLanguage(it) } | ||||
|                 val names = allLangs.mapNotNull { | ||||
|                     val fullName = SubtitleHelper.fromTwoLettersToLanguage(it) | ||||
|                     if (fullName.isNullOrEmpty()) { | ||||
|                         return@mapNotNull null | ||||
|                     } | ||||
| 
 | ||||
|                     Pair(it, fullName) | ||||
|                 } | ||||
| 
 | ||||
|                 context?.showMultiDialog( | ||||
|                     names, | ||||
|                     names.map { it.second }, | ||||
|                     currentList, | ||||
|                     getString(R.string.provider_lang_settings), | ||||
|                     {}) { selectedList -> | ||||
|                     settingsManager.edit().putStringSet( | ||||
|                         this.getString(R.string.provider_lang_key), | ||||
|                         selectedList.map { names[it] }.toMutableSet() | ||||
|                         selectedList.map { names[it].first }.toMutableSet() | ||||
|                     ).apply() | ||||
|                 } | ||||
|             } | ||||
|  |  | |||
|  | @ -260,7 +260,7 @@ object VideoDownloadManager { | |||
|         } | ||||
| 
 | ||||
|         if (state == DownloadType.IsDownloading || state == DownloadType.IsPaused) { | ||||
|             builder.setProgress(total.toInt(), progress.toInt(), false) | ||||
|             builder.setProgress((total / 1000).toInt(), (progress / 1000).toInt(), false) | ||||
|         } | ||||
| 
 | ||||
|         val rowTwoExtra = if (ep.name != null) " - ${ep.name}\n" else "" | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue