forked from recloudstream/cloudstream
		
	reverted sflix to old system because of complaints, make cache for all links, made downloads more reliable
This commit is contained in:
		
							parent
							
								
									c35364fb82
								
							
						
					
					
						commit
						1804484860
					
				
					 4 changed files with 226 additions and 136 deletions
				
			
		|  | @ -294,7 +294,7 @@ class SflixProvider(providerUrl: String, providerName: String) : MainAPI() { | |||
|                     ignoreCase = true | ||||
|                 ) || this.equals("RapidStream", ignoreCase = true) | ||||
|             ) return true | ||||
|             return true | ||||
|             return false | ||||
|         } | ||||
| 
 | ||||
|         // For re-use in Zoro | ||||
|  |  | |||
|  | @ -556,7 +556,11 @@ class GeneratorPlayer : FullScreenPlayer() { | |||
| 
 | ||||
|         observe(viewModel.currentLinks) { | ||||
|             currentLinks = it | ||||
|             overlay_loading_skip_button?.isVisible = it.isNotEmpty() | ||||
|             val turnVisible = it.isNotEmpty() | ||||
|             if (turnVisible && overlay_loading_skip_button?.isGone == true) { | ||||
|                 overlay_loading_skip_button?.requestFocus() | ||||
|             } | ||||
|             overlay_loading_skip_button?.isVisible = turnVisible | ||||
|         } | ||||
| 
 | ||||
|         observe(viewModel.currentSubs) { set -> | ||||
|  |  | |||
|  | @ -14,7 +14,8 @@ class RepoLinkGenerator( | |||
|     private var currentIndex: Int = 0 | ||||
| ) : IGenerator { | ||||
|     companion object { | ||||
|         val TAG = "RepoLink" | ||||
|         const val TAG = "RepoLink" | ||||
|         val cache: HashMap<Int, Pair<MutableSet<ExtractorLink>, MutableSet<SubtitleData>>> = hashMapOf() | ||||
|     } | ||||
| 
 | ||||
|     override val hasCache = true | ||||
|  | @ -54,8 +55,8 @@ class RepoLinkGenerator( | |||
|     } | ||||
| 
 | ||||
|     // this is a simple array that is used to instantly load links if they are already loaded | ||||
|     var linkCache = Array<Set<ExtractorLink>>(size = episodes.size, init = { setOf() }) | ||||
|     var subsCache = Array<Set<SubtitleData>>(size = episodes.size, init = { setOf() }) | ||||
|     //var linkCache = Array<Set<ExtractorLink>>(size = episodes.size, init = { setOf() }) | ||||
|     //var subsCache = Array<Set<SubtitleData>>(size = episodes.size, init = { setOf() }) | ||||
| 
 | ||||
|     override suspend fun generateLinks( | ||||
|         clearCache: Boolean, | ||||
|  | @ -67,8 +68,14 @@ class RepoLinkGenerator( | |||
|         val index = currentIndex | ||||
|         val current = episodes.getOrNull(index + offset) ?: return false | ||||
| 
 | ||||
|         val currentLinkCache = if (clearCache) mutableSetOf() else linkCache[index].toMutableSet() | ||||
|         val currentSubsCache = if (clearCache) mutableSetOf() else subsCache[index].toMutableSet() | ||||
|         val (currentLinkCache, currentSubsCache) = if (clearCache) { | ||||
|             Pair(mutableSetOf(), mutableSetOf()) | ||||
|         } else { | ||||
|             cache[current.id] ?: Pair(mutableSetOf(), mutableSetOf()) | ||||
|         } | ||||
| 
 | ||||
|         //val currentLinkCache = if (clearCache) mutableSetOf() else linkCache[index].toMutableSet() | ||||
|         //val currentSubsCache = if (clearCache) mutableSetOf() else subsCache[index].toMutableSet() | ||||
| 
 | ||||
|         val currentLinks = mutableSetOf<String>()       // makes all urls unique | ||||
|         val currentSubsUrls = mutableSetOf<String>()    // makes all subs urls unique | ||||
|  | @ -91,7 +98,7 @@ class RepoLinkGenerator( | |||
|             return true | ||||
|         } | ||||
| 
 | ||||
|         return APIRepository( | ||||
|         val result = APIRepository( | ||||
|             getApiFromNameNull(current.apiName) ?: throw Exception("This provider does not exist") | ||||
|         ).loadLinks(current.data, | ||||
|             isCasting, | ||||
|  | @ -114,7 +121,7 @@ class RepoLinkGenerator( | |||
|                     if (!currentSubsCache.contains(updatedFile)) { | ||||
|                         subtitleCallback(updatedFile) | ||||
|                         currentSubsCache.add(updatedFile) | ||||
|                         subsCache[index] = currentSubsCache | ||||
|                         //subsCache[index] = currentSubsCache | ||||
|                     } | ||||
|                 } | ||||
|             }, | ||||
|  | @ -124,10 +131,13 @@ class RepoLinkGenerator( | |||
|                         currentLinks.add(link.url) | ||||
|                         callback(Pair(link, null)) | ||||
|                         currentLinkCache.add(link) | ||||
|                         linkCache[index] = currentLinkCache | ||||
|                         //linkCache[index] = currentLinkCache | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         ) | ||||
|         cache[current.id] = Pair(currentLinkCache, currentSubsCache) | ||||
| 
 | ||||
|         return result | ||||
|     } | ||||
| } | ||||
|  | @ -1,6 +1,7 @@ | |||
| package com.lagradost.cloudstream3.ui.result | ||||
| 
 | ||||
| import android.annotation.SuppressLint | ||||
| import android.app.Activity | ||||
| import android.content.ClipData | ||||
| import android.content.ClipboardManager | ||||
| import android.content.Context.CLIPBOARD_SERVICE | ||||
|  | @ -35,6 +36,8 @@ import com.google.android.material.button.MaterialButton | |||
| import com.lagradost.cloudstream3.* | ||||
| import com.lagradost.cloudstream3.APIHolder.getApiFromName | ||||
| import com.lagradost.cloudstream3.APIHolder.getId | ||||
| import com.lagradost.cloudstream3.AcraApplication.Companion.context | ||||
| import com.lagradost.cloudstream3.AcraApplication.Companion.setKey | ||||
| import com.lagradost.cloudstream3.CommonActivity.getCastSession | ||||
| import com.lagradost.cloudstream3.CommonActivity.showToast | ||||
| import com.lagradost.cloudstream3.mvvm.* | ||||
|  | @ -46,6 +49,7 @@ import com.lagradost.cloudstream3.ui.download.DOWNLOAD_NAVIGATE_TO | |||
| import com.lagradost.cloudstream3.ui.download.DownloadButtonSetup.handleDownloadClick | ||||
| import com.lagradost.cloudstream3.ui.download.EasyDownloadButton | ||||
| import com.lagradost.cloudstream3.ui.player.GeneratorPlayer | ||||
| import com.lagradost.cloudstream3.ui.player.RepoLinkGenerator | ||||
| import com.lagradost.cloudstream3.ui.player.SubtitleData | ||||
| import com.lagradost.cloudstream3.ui.quicksearch.QuickSearchFragment | ||||
| import com.lagradost.cloudstream3.ui.search.SearchAdapter | ||||
|  | @ -193,6 +197,167 @@ class ResultFragment : Fragment() { | |||
|         } | ||||
| 
 | ||||
|         private var updateUIListener: (() -> Unit)? = null | ||||
| 
 | ||||
|         suspend fun startDownload( | ||||
|             episode: ResultEpisode, | ||||
|             currentIsMovie: Boolean, | ||||
|             currentHeaderName: String, | ||||
|             currentType: TvType, | ||||
|             currentPoster: String, | ||||
|             apiName: String, | ||||
|             parentId: Int, | ||||
|             url: String, | ||||
|             links: List<ExtractorLink>, | ||||
|             subs: List<SubtitleData>? | ||||
|         ) { | ||||
|             val titleName = sanitizeFilename(currentHeaderName) | ||||
| 
 | ||||
|             val meta = VideoDownloadManager.DownloadEpisodeMetadata( | ||||
|                 episode.id, | ||||
|                 titleName, | ||||
|                 apiName, | ||||
|                 episode.poster ?: currentPoster, | ||||
|                 episode.name, | ||||
|                 if (currentIsMovie) null else episode.season, | ||||
|                 if (currentIsMovie) null else episode.episode | ||||
|             ) | ||||
| 
 | ||||
|             val folder = when (currentType) { | ||||
|                 TvType.Anime -> "Anime/$titleName" | ||||
|                 TvType.Movie -> "Movies" | ||||
|                 TvType.AnimeMovie -> "Movies" | ||||
|                 TvType.TvSeries -> "TVSeries/$titleName" | ||||
|                 TvType.OVA -> "OVA" | ||||
|                 TvType.Cartoon -> "Cartoons/$titleName" | ||||
|                 TvType.Torrent -> "Torrent" | ||||
|                 TvType.Documentary -> "Documentaries" | ||||
|             } | ||||
| 
 | ||||
|             val src = "$DOWNLOAD_NAVIGATE_TO/$parentId" // url ?: return@let | ||||
| 
 | ||||
|             // SET VISUAL KEYS | ||||
|             setKey( | ||||
|                 DOWNLOAD_HEADER_CACHE, | ||||
|                 parentId.toString(), | ||||
|                 VideoDownloadHelper.DownloadHeaderCached( | ||||
|                     apiName, | ||||
|                     url, | ||||
|                     currentType, | ||||
|                     currentHeaderName, | ||||
|                     currentPoster, | ||||
|                     parentId, | ||||
|                     System.currentTimeMillis(), | ||||
|                 ) | ||||
|             ) | ||||
| 
 | ||||
|             setKey( | ||||
|                 getFolderName( | ||||
|                     DOWNLOAD_EPISODE_CACHE, | ||||
|                     parentId.toString() | ||||
|                 ), // 3 deep folder for faster acess | ||||
|                 episode.id.toString(), | ||||
|                 VideoDownloadHelper.DownloadEpisodeCached( | ||||
|                     episode.name, | ||||
|                     episode.poster, | ||||
|                     episode.episode, | ||||
|                     episode.season, | ||||
|                     episode.id, | ||||
|                     parentId, | ||||
|                     episode.rating, | ||||
|                     episode.description, | ||||
|                     System.currentTimeMillis(), | ||||
|                 ) | ||||
|             ) | ||||
| 
 | ||||
|             // DOWNLOAD VIDEO | ||||
|             VideoDownloadManager.downloadEpisodeUsingWorker( | ||||
|                 context ?: return, | ||||
|                 src,//url ?: return, | ||||
|                 folder, | ||||
|                 meta, | ||||
|                 links | ||||
|             ) | ||||
|             // 1. Checks if the lang should be downloaded | ||||
|             // 2. Makes it into the download format | ||||
|             // 3. Downloads it as a .vtt file | ||||
|             val downloadList = getDownloadSubsLanguageISO639_1() | ||||
|             subs?.let { subsList -> | ||||
|                 subsList.filter { | ||||
|                     downloadList.contains( | ||||
|                         SubtitleHelper.fromLanguageToTwoLetters( | ||||
|                             it.name, | ||||
|                             true | ||||
|                         ) | ||||
|                     ) | ||||
|                 } | ||||
|                     .map { ExtractorSubtitleLink(it.name, it.url, "") } | ||||
|                     .forEach { link -> | ||||
|                         val epName = meta.name | ||||
|                             ?: "${context?.getString(R.string.episode)} ${meta.episode}" | ||||
|                         val fileName = | ||||
|                             sanitizeFilename(epName + if (downloadList.size > 1) " ${link.name}" else "") | ||||
| 
 | ||||
|                         withContext(Dispatchers.IO) { | ||||
|                             normalSafeApiCall { | ||||
|                                 VideoDownloadManager.downloadThing( | ||||
|                                     context ?: return@normalSafeApiCall, | ||||
|                                     link, | ||||
|                                     fileName, | ||||
|                                     folder, | ||||
|                                     "vtt", | ||||
|                                     false, | ||||
|                                     null | ||||
|                                 ) { | ||||
|                                     // no notification | ||||
|                                 } | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         suspend fun downloadEpisode( | ||||
|             activity: Activity?, | ||||
|             episode: ResultEpisode, | ||||
|             currentIsMovie: Boolean, | ||||
|             currentHeaderName: String, | ||||
|             currentType: TvType, | ||||
|             currentPoster: String, | ||||
|             apiName: String, | ||||
|             parentId: Int, | ||||
|             url: String, | ||||
|         ) { | ||||
|             safeApiCall { | ||||
|                 val generator = RepoLinkGenerator(listOf(episode)) | ||||
|                 val currentLinks = mutableSetOf<ExtractorLink>() | ||||
|                 val currentSubs = mutableSetOf<SubtitleData>() | ||||
|                 generator.generateLinks(clearCache = false, isCasting = false, callback = { | ||||
|                     it.first?.let { link -> | ||||
|                         currentLinks.add(link) | ||||
|                     } | ||||
|                 }, subtitleCallback = { sub -> | ||||
|                     currentSubs.add(sub) | ||||
|                 }) | ||||
| 
 | ||||
|                 if (currentLinks.isEmpty()) { | ||||
|                     showToast(activity, R.string.no_links_found_toast, Toast.LENGTH_SHORT) | ||||
|                     return@safeApiCall | ||||
|                 } | ||||
| 
 | ||||
|                 startDownload( | ||||
|                     episode, | ||||
|                     currentIsMovie, | ||||
|                     currentHeaderName, | ||||
|                     currentType, | ||||
|                     currentPoster, | ||||
|                     apiName, | ||||
|                     parentId, | ||||
|                     url, | ||||
|                     sortUrls(currentLinks), | ||||
|                     sortSubs(currentSubs), | ||||
|                 ) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private var currentLoadingCount = | ||||
|  | @ -449,6 +614,28 @@ class ResultFragment : Fragment() { | |||
|         } | ||||
| 
 | ||||
|         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<ExtractorLink>? = null | ||||
|             var currentSubs: Set<SubtitleData>? = null | ||||
| 
 | ||||
|  | @ -498,121 +685,6 @@ class ResultFragment : Fragment() { | |||
|                 ) | ||||
|             } | ||||
| 
 | ||||
|             fun startDownload(links: List<ExtractorLink>, subs: List<SubtitleData>?) { | ||||
|                 val isMovie = currentIsMovie ?: return | ||||
|                 val titleName = sanitizeFilename(currentHeaderName ?: return) | ||||
| 
 | ||||
|                 val meta = VideoDownloadManager.DownloadEpisodeMetadata( | ||||
|                     episodeClick.data.id, | ||||
|                     titleName, | ||||
|                     apiName, | ||||
|                     episodeClick.data.poster ?: currentPoster, | ||||
|                     episodeClick.data.name, | ||||
|                     if (isMovie) null else episodeClick.data.season, | ||||
|                     if (isMovie) null else episodeClick.data.episode | ||||
|                 ) | ||||
| 
 | ||||
|                 val folder = when (currentType) { | ||||
|                     TvType.Anime -> "Anime/$titleName" | ||||
|                     TvType.Movie -> "Movies" | ||||
|                     TvType.AnimeMovie -> "Movies" | ||||
|                     TvType.TvSeries -> "TVSeries/$titleName" | ||||
|                     TvType.OVA -> "OVA" | ||||
|                     TvType.Cartoon -> "Cartoons/$titleName" | ||||
|                     TvType.Torrent -> "Torrent" | ||||
|                     TvType.Documentary -> "Documentaries" | ||||
|                     null -> null | ||||
|                 } | ||||
| 
 | ||||
|                 context?.let { ctx -> | ||||
|                     val parentId = currentId ?: return@let | ||||
|                     val src = "$DOWNLOAD_NAVIGATE_TO/$parentId" // url ?: return@let | ||||
| 
 | ||||
|                     // SET VISUAL KEYS | ||||
|                     ctx.setKey( | ||||
|                         DOWNLOAD_HEADER_CACHE, | ||||
|                         parentId.toString(), | ||||
|                         VideoDownloadHelper.DownloadHeaderCached( | ||||
|                             apiName, | ||||
|                             url ?: return@let, | ||||
|                             currentType ?: return@let, | ||||
|                             currentHeaderName ?: return@let, | ||||
|                             currentPoster, | ||||
|                             currentId ?: return@let, | ||||
|                             System.currentTimeMillis(), | ||||
|                         ) | ||||
|                     ) | ||||
| 
 | ||||
|                     val epData = episodeClick.data | ||||
|                     ctx.setKey( | ||||
|                         getFolderName( | ||||
|                             DOWNLOAD_EPISODE_CACHE, | ||||
|                             parentId.toString() | ||||
|                         ), // 3 deep folder for faster acess | ||||
|                         epData.id.toString(), | ||||
|                         VideoDownloadHelper.DownloadEpisodeCached( | ||||
|                             epData.name, | ||||
|                             epData.poster, | ||||
|                             epData.episode, | ||||
|                             epData.season, | ||||
|                             epData.id, | ||||
|                             parentId, | ||||
|                             epData.rating, | ||||
|                             epData.description, | ||||
|                             System.currentTimeMillis(), | ||||
|                         ) | ||||
|                     ) | ||||
| 
 | ||||
|                     // DOWNLOAD VIDEO | ||||
|                     VideoDownloadManager.downloadEpisodeUsingWorker( | ||||
|                         ctx, | ||||
|                         src,//url ?: return, | ||||
|                         folder, | ||||
|                         meta, | ||||
|                         links | ||||
|                     ) | ||||
|                     // 1. Checks if the lang should be downloaded | ||||
|                     // 2. Makes it into the download format | ||||
|                     // 3. Downloads it as a .vtt file | ||||
|                     val downloadList = getDownloadSubsLanguageISO639_1() | ||||
|                     main { | ||||
|                         subs?.let { subsList -> | ||||
|                             subsList.filter { | ||||
|                                 downloadList.contains( | ||||
|                                     SubtitleHelper.fromLanguageToTwoLetters( | ||||
|                                         it.name, | ||||
|                                         true | ||||
|                                     ) | ||||
|                                 ) | ||||
|                             } | ||||
|                                 .map { ExtractorSubtitleLink(it.name, it.url, "") } | ||||
|                                 .forEach { link -> | ||||
|                                     val epName = meta.name | ||||
|                                         ?: "${context?.getString(R.string.episode)} ${meta.episode}" | ||||
|                                     val fileName = | ||||
|                                         sanitizeFilename(epName + if (downloadList.size > 1) " ${link.name}" else "") | ||||
|                                     val topFolder = "$folder" | ||||
| 
 | ||||
|                                     withContext(Dispatchers.IO) { | ||||
|                                         normalSafeApiCall { | ||||
|                                             VideoDownloadManager.downloadThing( | ||||
|                                                 ctx, | ||||
|                                                 link, | ||||
|                                                 fileName, | ||||
|                                                 topFolder, | ||||
|                                                 "vtt", | ||||
|                                                 false, | ||||
|                                                 null | ||||
|                                             ) { | ||||
|                                                 // no notification | ||||
|                                             } | ||||
|                                         } | ||||
|                                     } | ||||
|                                 } | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             suspend fun requireLinks(isCasting: Boolean, displayLoading: Boolean = true): Boolean { | ||||
|                 val skipLoading = getApiFromName(apiName).instantLinkLoading | ||||
|  | @ -864,12 +936,6 @@ class ResultFragment : Fragment() { | |||
|                     viewModel.loadEpisode(episodeClick.data, false) | ||||
|                 } | ||||
| 
 | ||||
|                 ACTION_DOWNLOAD_EPISODE -> { | ||||
|                     startDownload( | ||||
|                         sortUrls(currentLinks ?: return@main), | ||||
|                         sortSubs(currentSubs ?: return@main) | ||||
|                     ) | ||||
|                 } | ||||
| 
 | ||||
|                 ACTION_DOWNLOAD_MIRROR -> { | ||||
|                     acquireSingleExtractorLink( | ||||
|  | @ -878,11 +944,21 @@ class ResultFragment : Fragment() { | |||
|                         ),//(currentLinks ?: return@main).filter { !it.isM3u8 }, | ||||
|                         getString(R.string.episode_action_download_mirror) | ||||
|                     ) { link -> | ||||
|                         showToast(activity, R.string.download_started, Toast.LENGTH_SHORT) | ||||
|                         startDownload( | ||||
|                             listOf(link), | ||||
|                             sortSubs(currentSubs ?: return@acquireSingleExtractorLink) | ||||
|                         ) | ||||
|                         main { | ||||
|                             startDownload( | ||||
|                                 episodeClick.data, | ||||
|                                 currentIsMovie ?: return@main, | ||||
|                                 currentHeaderName ?: return@main, | ||||
|                                 currentType ?: return@main, | ||||
|                                 currentPoster ?: return@main, | ||||
|                                 apiName, | ||||
|                                 currentId ?: return@main, | ||||
|                                 url ?: return@main, | ||||
|                                 listOf(link), | ||||
|                                 sortSubs(currentSubs ?: return@main) | ||||
|                             ) | ||||
|                             showToast(activity, R.string.download_started, Toast.LENGTH_SHORT) | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue