forked from recloudstream/cloudstream
		
	Add internal support for subtitle headers + season names
This commit is contained in:
		
							parent
							
								
									c8cd6f921d
								
							
						
					
					
						commit
						53965b13fb
					
				
					 13 changed files with 71 additions and 30 deletions
				
			
		|  | @ -13,7 +13,8 @@ class AbstractSubtitleEntities { | |||
|         var epNumber: Int? = null, | ||||
|         var seasonNumber: Int? = null, | ||||
|         var year: Int? = null, | ||||
|         var isHearingImpaired: Boolean = false | ||||
|         var isHearingImpaired: Boolean = false, | ||||
|         var headers: Map<String, String> = emptyMap() | ||||
|     ) | ||||
| 
 | ||||
|     data class SubtitleSearch( | ||||
|  |  | |||
|  | @ -4,6 +4,7 @@ import android.util.Log | |||
| import com.lagradost.cloudstream3.TvType | ||||
| import com.lagradost.cloudstream3.app | ||||
| import com.lagradost.cloudstream3.imdbUrlToIdNullable | ||||
| import com.lagradost.cloudstream3.network.CloudflareKiller | ||||
| import com.lagradost.cloudstream3.subtitles.AbstractSubApi | ||||
| import com.lagradost.cloudstream3.subtitles.AbstractSubtitleEntities | ||||
| import com.lagradost.cloudstream3.utils.SubtitleHelper | ||||
|  | @ -19,6 +20,8 @@ class IndexSubtitleApi : AbstractSubApi { | |||
| 
 | ||||
|     override fun logOut() {} | ||||
| 
 | ||||
|     private val interceptor = CloudflareKiller() | ||||
| 
 | ||||
|     companion object { | ||||
|         const val host = "https://indexsubtitle.com" | ||||
|         const val TAG = "INDEXSUBS" | ||||
|  | @ -122,12 +125,13 @@ class IndexSubtitleApi : AbstractSubApi { | |||
|                     type = if (seasonNum > 0) TvType.TvSeries else TvType.Movie, | ||||
|                     epNumber = epNum, | ||||
|                     seasonNumber = seasonNum, | ||||
|                     year = yearNum | ||||
|                     year = yearNum, | ||||
|                     headers = interceptor.getCookieHeaders(link).toMap() | ||||
|                 ) | ||||
|             ) | ||||
|         } | ||||
| 
 | ||||
|         val document = app.get("$host/?search=$queryText").document | ||||
|         val document = app.get("$host/?search=$queryText", interceptor = interceptor).document | ||||
| 
 | ||||
|         document.select("div.my-3.p-3 div.media").map { block -> | ||||
|             if (seasonNum > 0) { | ||||
|  | @ -159,7 +163,7 @@ class IndexSubtitleApi : AbstractSubApi { | |||
|                             val urlItem = fixUrl( | ||||
|                                 it.selectFirst("a")!!.attr("href") | ||||
|                             ) | ||||
|                             val itemDoc = app.get(urlItem).document | ||||
|                             val itemDoc = app.get(urlItem, interceptor = interceptor).document | ||||
|                             val id = imdbUrlToIdNullable( | ||||
|                                 itemDoc.selectFirst("div.d-flex span.badge.badge-primary")?.parent() | ||||
|                                     ?.attr("href") | ||||
|  | @ -198,7 +202,7 @@ class IndexSubtitleApi : AbstractSubApi { | |||
|         val results = mutableListOf<AbstractSubtitleEntities.SubtitleEntity>() | ||||
| 
 | ||||
|         urlItems.forEach { url -> | ||||
|             val request = app.get(url) | ||||
|             val request = app.get(url, interceptor = interceptor) | ||||
|             if (request.isSuccessful) { | ||||
|                 request.document.select("div.my-3.p-3 div.media").map { block -> | ||||
|                     if (block.select("span.d-block span[data-original-title=Language]").text() | ||||
|  | @ -231,7 +235,7 @@ class IndexSubtitleApi : AbstractSubApi { | |||
|         val seasonNum = data.seasonNumber | ||||
|         val epNum = data.epNumber | ||||
| 
 | ||||
|         val req = app.get(data.data) | ||||
|         val req = app.get(data.data, interceptor = interceptor) | ||||
| 
 | ||||
|         if (req.isSuccessful) { | ||||
|             val document = req.document | ||||
|  |  | |||
|  | @ -20,6 +20,7 @@ import com.google.android.exoplayer2.ui.SubtitleView | |||
| import com.google.android.exoplayer2.upstream.DataSource | ||||
| import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory | ||||
| import com.google.android.exoplayer2.upstream.DefaultHttpDataSource | ||||
| import com.google.android.exoplayer2.upstream.HttpDataSource | ||||
| import com.google.android.exoplayer2.upstream.cache.CacheDataSource | ||||
| import com.google.android.exoplayer2.upstream.cache.LeastRecentlyUsedCacheEvictor | ||||
| import com.google.android.exoplayer2.upstream.cache.SimpleCache | ||||
|  | @ -442,7 +443,14 @@ class CS3IPlayer : IPlayer { | |||
| 
 | ||||
|         var requestSubtitleUpdate: (() -> Unit)? = null | ||||
| 
 | ||||
|         private fun createOnlineSource(link: ExtractorLink): DataSource.Factory { | ||||
|         private fun createOnlineSource(headers: Map<String, String>): HttpDataSource.Factory { | ||||
|             val source = OkHttpDataSource.Factory(app.baseClient).setUserAgent(USER_AGENT) | ||||
|             return source.apply { | ||||
|                 setDefaultRequestProperties(headers) | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         private fun createOnlineSource(link: ExtractorLink): HttpDataSource.Factory { | ||||
|             val provider = getApiFromNameNull(link.source) | ||||
|             val interceptor = provider?.getVideoInterceptor(link) | ||||
| 
 | ||||
|  | @ -813,7 +821,8 @@ class CS3IPlayer : IPlayer { | |||
|                                 // See setPreferredTextLanguage | ||||
|                                 it.language!!, | ||||
|                                 SubtitleOrigin.EMBEDDED_IN_VIDEO, | ||||
|                                 it.sampleMimeType ?: MimeTypes.APPLICATION_SUBRIP | ||||
|                                 it.sampleMimeType ?: MimeTypes.APPLICATION_SUBRIP, | ||||
|                                 emptyMap() | ||||
|                             ) | ||||
|                         } | ||||
| 
 | ||||
|  | @ -981,9 +990,10 @@ class CS3IPlayer : IPlayer { | |||
| 
 | ||||
|             val mediaItem = getMediaItem(MimeTypes.VIDEO_MP4, data.uri) | ||||
|             val offlineSourceFactory = context.createOfflineSource() | ||||
|             val onlineSourceFactory = createOnlineSource(emptyMap()) | ||||
| 
 | ||||
|             val (subSources, activeSubtitles) = getSubSources( | ||||
|                 onlineSourceFactory = offlineSourceFactory, | ||||
|                 onlineSourceFactory = onlineSourceFactory, | ||||
|                 offlineSourceFactory = offlineSourceFactory, | ||||
|                 subtitleHelper, | ||||
|             ) | ||||
|  | @ -997,7 +1007,7 @@ class CS3IPlayer : IPlayer { | |||
|     } | ||||
| 
 | ||||
|     private fun getSubSources( | ||||
|         onlineSourceFactory: DataSource.Factory?, | ||||
|         onlineSourceFactory: HttpDataSource.Factory?, | ||||
|         offlineSourceFactory: DataSource.Factory?, | ||||
|         subHelper: PlayerSubtitleHelper, | ||||
|     ): Pair<List<SingleSampleMediaSource>, List<SubtitleData>> { | ||||
|  | @ -1021,7 +1031,10 @@ class CS3IPlayer : IPlayer { | |||
|                 SubtitleOrigin.URL -> { | ||||
|                     if (onlineSourceFactory != null) { | ||||
|                         activeSubtitles.add(sub) | ||||
|                         SingleSampleMediaSource.Factory(onlineSourceFactory) | ||||
|                         SingleSampleMediaSource.Factory(onlineSourceFactory.apply { | ||||
|                             if (sub.headers.isNotEmpty()) | ||||
|                                 this.setDefaultRequestProperties(sub.headers) | ||||
|                         }) | ||||
|                             .createMediaSource(subConfig, C.TIME_UNSET) | ||||
|                     } else { | ||||
|                         null | ||||
|  |  | |||
|  | @ -84,7 +84,8 @@ class DownloadFileGenerator( | |||
|                                 realName.ifBlank { ctx.getString(R.string.default_subtitles) }, | ||||
|                                 file.second.toString(), | ||||
|                                 SubtitleOrigin.DOWNLOADED_FILE, | ||||
|                                 name.toSubtitleMimeType() | ||||
|                                 name.toSubtitleMimeType(), | ||||
|                                 emptyMap() | ||||
|                             ) | ||||
|                         ) | ||||
|                     } | ||||
|  |  | |||
|  | @ -399,7 +399,8 @@ class GeneratorPlayer : FullScreenPlayer() { | |||
|                             name = getName(currentSubtitle, true), | ||||
|                             url = url, | ||||
|                             origin = SubtitleOrigin.URL, | ||||
|                             mimeType = url.toSubtitleMimeType() | ||||
|                             mimeType = url.toSubtitleMimeType(), | ||||
|                             headers = currentSubtitle.headers | ||||
|                         ) | ||||
|                         runOnMainThread { | ||||
|                             addAndSelectSubtitles(subtitle) | ||||
|  | @ -483,7 +484,8 @@ class GeneratorPlayer : FullScreenPlayer() { | |||
|                     name, | ||||
|                     uri.toString(), | ||||
|                     SubtitleOrigin.DOWNLOADED_FILE, | ||||
|                     name.toSubtitleMimeType() | ||||
|                     name.toSubtitleMimeType(), | ||||
|                     emptyMap() | ||||
|                 ) | ||||
| 
 | ||||
|                 addAndSelectSubtitles(subtitleData) | ||||
|  |  | |||
|  | @ -29,12 +29,14 @@ enum class SubtitleOrigin { | |||
| /** | ||||
|  * @param name To be displayed in the player | ||||
|  * @param url Url for the subtitle, when EMBEDDED_IN_VIDEO this variable is used as the real backend language | ||||
|  * @param headers if empty it will use the base onlineDataSource headers else only the specified headers | ||||
|  * */ | ||||
| data class SubtitleData( | ||||
|     val name: String, | ||||
|     val url: String, | ||||
|     val origin: SubtitleOrigin, | ||||
|     val mimeType: String, | ||||
|     val headers: Map<String, String> | ||||
| ) | ||||
| 
 | ||||
| class PlayerSubtitleHelper { | ||||
|  | @ -71,7 +73,8 @@ class PlayerSubtitleHelper { | |||
|                 name = subtitleFile.lang, | ||||
|                 url = subtitleFile.url, | ||||
|                 origin = SubtitleOrigin.URL, | ||||
|                 mimeType = subtitleFile.url.toSubtitleMimeType() | ||||
|                 mimeType = subtitleFile.url.toSubtitleMimeType(), | ||||
|                 headers = emptyMap() | ||||
|             ) | ||||
|         } | ||||
|     } | ||||
|  |  | |||
|  | @ -409,6 +409,11 @@ class ResultViewModel2 : ViewModel() { | |||
|         private const val EPISODE_RANGE_SIZE = 20 | ||||
|         private const val EPISODE_RANGE_OVERLOAD = 30 | ||||
| 
 | ||||
|         private fun List<SeasonData>?.getSeason(season: Int?): SeasonData? { | ||||
|             if (season == null) return null | ||||
|             return this?.firstOrNull { it.season == season } | ||||
|         } | ||||
| 
 | ||||
|         private fun filterName(name: String?): String? { | ||||
|             if (name == null) return null | ||||
|             Regex("[eE]pisode [0-9]*(.*)").find(name)?.groupValues?.get(1)?.let { | ||||
|  | @ -1476,11 +1481,18 @@ class ResultViewModel2 : ViewModel() { | |||
|                 if (isMovie || currentSeasons.size <= 1) null else | ||||
|                     when (indexer.season) { | ||||
|                         0 -> txt(R.string.no_season) | ||||
|                         else -> txt( | ||||
|                             R.string.season_format, | ||||
|                             txt(R.string.season), | ||||
|                             indexer.season | ||||
|                         ) //TODO FIX DISPLAYNAME | ||||
|                         else -> { | ||||
|                             val seasonNames = (currentResponse as? EpisodeResponse)?.seasonNames | ||||
|                             val seasonData = | ||||
|                                 seasonNames.getSeason(indexer.season) | ||||
|                             val suffix = seasonData?.name?.let { " $it" } ?: "" | ||||
|                             txt( | ||||
|                                 R.string.season_format, | ||||
|                                 txt(R.string.season), | ||||
|                                 seasonData?.displaySeason ?: indexer.season, | ||||
|                                 suffix | ||||
|                             ) | ||||
|                         } | ||||
|                     } | ||||
|             ) | ||||
|         ) | ||||
|  | @ -1578,6 +1590,7 @@ class ResultViewModel2 : ViewModel() { | |||
|                         val id = mainId + episode + idIndex * 1000000 | ||||
|                         if (!existingEpisodes.contains(episode)) { | ||||
|                             existingEpisodes.add(id) | ||||
|                             val seasonData = loadResponse.seasonNames.getSeason(i.season) | ||||
|                             val eps = | ||||
|                                 buildResultEpisode( | ||||
|                                     loadResponse.name, | ||||
|  | @ -1585,7 +1598,7 @@ class ResultViewModel2 : ViewModel() { | |||
|                                     i.posterUrl, | ||||
|                                     episode, | ||||
|                                     null, | ||||
|                                     i.season, | ||||
|                                     seasonData?.displaySeason ?: i.season, | ||||
|                                     i.data, | ||||
|                                     loadResponse.apiName, | ||||
|                                     id, | ||||
|  | @ -1621,7 +1634,7 @@ class ResultViewModel2 : ViewModel() { | |||
|                         existingEpisodes.add(id) | ||||
|                         val seasonIndex = episode.season?.minus(1) | ||||
|                         val currentSeason = | ||||
|                             loadResponse.seasonNames?.getOrNull(seasonIndex ?: -1) | ||||
|                             loadResponse.seasonNames.getSeason(episode.season) | ||||
| 
 | ||||
|                         val ep = | ||||
|                             buildResultEpisode( | ||||
|  | @ -1630,7 +1643,7 @@ class ResultViewModel2 : ViewModel() { | |||
|                                 episode.posterUrl, | ||||
|                                 episodeIndex, | ||||
|                                 seasonIndex, | ||||
|                                 currentSeason?.season ?: episode.season, | ||||
|                                 currentSeason?.displaySeason ?: episode.season, | ||||
|                                 episode.data, | ||||
|                                 loadResponse.apiName, | ||||
|                                 id, | ||||
|  | @ -1731,10 +1744,19 @@ class ResultViewModel2 : ViewModel() { | |||
|         _dubSubSelections.postValue(dubSelection.map { txt(it) to it }) | ||||
|         if (loadResponse is EpisodeResponse) { | ||||
|             _seasonSelections.postValue(seasonsSelection.map { seasonNumber -> | ||||
|                 val seasonData = loadResponse.seasonNames.getSeason(seasonNumber) | ||||
|                 val fixedSeasonNumber = seasonData?.displaySeason ?: seasonNumber | ||||
|                 val suffix = seasonData?.name?.let { " $it" } ?: "" | ||||
| 
 | ||||
|                 val name = | ||||
|                     /*loadResponse.seasonNames?.firstOrNull { it.season == seasonNumber }?.name?.let { seasonData -> | ||||
|                         txt(seasonData) | ||||
|                     } ?:*/txt(R.string.season_format, txt(R.string.season), seasonNumber) //TODO FIX | ||||
|                     } ?:*/txt( | ||||
|                     R.string.season_format, | ||||
|                     txt(R.string.season), | ||||
|                     fixedSeasonNumber, | ||||
|                     suffix | ||||
|                 ) | ||||
|                 name to seasonNumber | ||||
|             }) | ||||
|         } | ||||
|  |  | |||
|  | @ -210,7 +210,6 @@ | |||
|     </string> | ||||
| 
 | ||||
|     <string name="season">Staffel</string> | ||||
|     <string name="season_format">%s %d</string> | ||||
|     <string name="no_season">Keine Staffel</string> | ||||
|     <string name="episode">Episode</string> | ||||
|     <string name="episodes">Episoden</string> | ||||
|  |  | |||
|  | @ -228,7 +228,6 @@ | |||
|     </string> | ||||
| 
 | ||||
|     <string name="season">Sezona</string> | ||||
|     <string name="season_format">%s %d</string> | ||||
|     <string name="no_season">Nema sezone</string> | ||||
|     <string name="episode">Epizoda</string> | ||||
|     <string name="episodes">Epizode</string> | ||||
|  |  | |||
|  | @ -208,7 +208,6 @@ | |||
|     <string name="acra_report_toast">Sorry, de applicatie is gecrasht. Er wordt een anoniem bugrapport naar de ontwikkelaars gestuurd </string> | ||||
| 
 | ||||
|     <string name="season">Seizoen</string> | ||||
|     <string name="season_format">%s %d</string> | ||||
|     <string name="no_season">Geen seizoen</string> | ||||
|     <string name="episode">Aflevering</string> | ||||
|     <string name="episodes">afleveringen</string> | ||||
|  |  | |||
|  | @ -221,7 +221,6 @@ | |||
| 
 | ||||
| 
 | ||||
|     <string name="season">Mùa</string> | ||||
|     <string name="season_format">%s %d</string> | ||||
|     <string name="no_season">Không có mùa nào</string> | ||||
|     <string name="episode">Tập</string> | ||||
|     <string name="episodes">Tập</string> | ||||
|  |  | |||
|  | @ -231,7 +231,6 @@ | |||
|     </string> | ||||
| 
 | ||||
|     <string name="season">季</string> | ||||
|     <string name="season_format">%s %d</string> | ||||
|     <string name="no_season">无季</string> | ||||
|     <string name="episode">集</string> | ||||
|     <string name="episodes">集</string> | ||||
|  |  | |||
|  | @ -290,7 +290,7 @@ | |||
|     </string> | ||||
| 
 | ||||
|     <string name="season">Season</string> | ||||
|     <string name="season_format">%s %d</string> | ||||
|     <string name="season_format">%s %d%s</string> | ||||
|     <string name="no_season">No Season</string> | ||||
|     <string name="episode">Episode</string> | ||||
|     <string name="episodes">Episodes</string> | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue