Add internal support for subtitle headers + season names

This commit is contained in:
Blatzar 2022-09-10 19:59:37 +02:00
parent c8cd6f921d
commit 53965b13fb
13 changed files with 71 additions and 30 deletions

View file

@ -13,7 +13,8 @@ class AbstractSubtitleEntities {
var epNumber: Int? = null, var epNumber: Int? = null,
var seasonNumber: Int? = null, var seasonNumber: Int? = null,
var year: Int? = null, var year: Int? = null,
var isHearingImpaired: Boolean = false var isHearingImpaired: Boolean = false,
var headers: Map<String, String> = emptyMap()
) )
data class SubtitleSearch( data class SubtitleSearch(

View file

@ -4,6 +4,7 @@ import android.util.Log
import com.lagradost.cloudstream3.TvType import com.lagradost.cloudstream3.TvType
import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.imdbUrlToIdNullable import com.lagradost.cloudstream3.imdbUrlToIdNullable
import com.lagradost.cloudstream3.network.CloudflareKiller
import com.lagradost.cloudstream3.subtitles.AbstractSubApi import com.lagradost.cloudstream3.subtitles.AbstractSubApi
import com.lagradost.cloudstream3.subtitles.AbstractSubtitleEntities import com.lagradost.cloudstream3.subtitles.AbstractSubtitleEntities
import com.lagradost.cloudstream3.utils.SubtitleHelper import com.lagradost.cloudstream3.utils.SubtitleHelper
@ -19,6 +20,8 @@ class IndexSubtitleApi : AbstractSubApi {
override fun logOut() {} override fun logOut() {}
private val interceptor = CloudflareKiller()
companion object { companion object {
const val host = "https://indexsubtitle.com" const val host = "https://indexsubtitle.com"
const val TAG = "INDEXSUBS" const val TAG = "INDEXSUBS"
@ -122,12 +125,13 @@ class IndexSubtitleApi : AbstractSubApi {
type = if (seasonNum > 0) TvType.TvSeries else TvType.Movie, type = if (seasonNum > 0) TvType.TvSeries else TvType.Movie,
epNumber = epNum, epNumber = epNum,
seasonNumber = seasonNum, 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 -> document.select("div.my-3.p-3 div.media").map { block ->
if (seasonNum > 0) { if (seasonNum > 0) {
@ -159,7 +163,7 @@ class IndexSubtitleApi : AbstractSubApi {
val urlItem = fixUrl( val urlItem = fixUrl(
it.selectFirst("a")!!.attr("href") it.selectFirst("a")!!.attr("href")
) )
val itemDoc = app.get(urlItem).document val itemDoc = app.get(urlItem, interceptor = interceptor).document
val id = imdbUrlToIdNullable( val id = imdbUrlToIdNullable(
itemDoc.selectFirst("div.d-flex span.badge.badge-primary")?.parent() itemDoc.selectFirst("div.d-flex span.badge.badge-primary")?.parent()
?.attr("href") ?.attr("href")
@ -198,7 +202,7 @@ class IndexSubtitleApi : AbstractSubApi {
val results = mutableListOf<AbstractSubtitleEntities.SubtitleEntity>() val results = mutableListOf<AbstractSubtitleEntities.SubtitleEntity>()
urlItems.forEach { url -> urlItems.forEach { url ->
val request = app.get(url) val request = app.get(url, interceptor = interceptor)
if (request.isSuccessful) { if (request.isSuccessful) {
request.document.select("div.my-3.p-3 div.media").map { block -> request.document.select("div.my-3.p-3 div.media").map { block ->
if (block.select("span.d-block span[data-original-title=Language]").text() if (block.select("span.d-block span[data-original-title=Language]").text()
@ -231,7 +235,7 @@ class IndexSubtitleApi : AbstractSubApi {
val seasonNum = data.seasonNumber val seasonNum = data.seasonNumber
val epNum = data.epNumber val epNum = data.epNumber
val req = app.get(data.data) val req = app.get(data.data, interceptor = interceptor)
if (req.isSuccessful) { if (req.isSuccessful) {
val document = req.document val document = req.document

View file

@ -20,6 +20,7 @@ import com.google.android.exoplayer2.ui.SubtitleView
import com.google.android.exoplayer2.upstream.DataSource import com.google.android.exoplayer2.upstream.DataSource
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory
import com.google.android.exoplayer2.upstream.DefaultHttpDataSource 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.CacheDataSource
import com.google.android.exoplayer2.upstream.cache.LeastRecentlyUsedCacheEvictor import com.google.android.exoplayer2.upstream.cache.LeastRecentlyUsedCacheEvictor
import com.google.android.exoplayer2.upstream.cache.SimpleCache import com.google.android.exoplayer2.upstream.cache.SimpleCache
@ -442,7 +443,14 @@ class CS3IPlayer : IPlayer {
var requestSubtitleUpdate: (() -> Unit)? = null 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 provider = getApiFromNameNull(link.source)
val interceptor = provider?.getVideoInterceptor(link) val interceptor = provider?.getVideoInterceptor(link)
@ -813,7 +821,8 @@ class CS3IPlayer : IPlayer {
// See setPreferredTextLanguage // See setPreferredTextLanguage
it.language!!, it.language!!,
SubtitleOrigin.EMBEDDED_IN_VIDEO, 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 mediaItem = getMediaItem(MimeTypes.VIDEO_MP4, data.uri)
val offlineSourceFactory = context.createOfflineSource() val offlineSourceFactory = context.createOfflineSource()
val onlineSourceFactory = createOnlineSource(emptyMap())
val (subSources, activeSubtitles) = getSubSources( val (subSources, activeSubtitles) = getSubSources(
onlineSourceFactory = offlineSourceFactory, onlineSourceFactory = onlineSourceFactory,
offlineSourceFactory = offlineSourceFactory, offlineSourceFactory = offlineSourceFactory,
subtitleHelper, subtitleHelper,
) )
@ -997,7 +1007,7 @@ class CS3IPlayer : IPlayer {
} }
private fun getSubSources( private fun getSubSources(
onlineSourceFactory: DataSource.Factory?, onlineSourceFactory: HttpDataSource.Factory?,
offlineSourceFactory: DataSource.Factory?, offlineSourceFactory: DataSource.Factory?,
subHelper: PlayerSubtitleHelper, subHelper: PlayerSubtitleHelper,
): Pair<List<SingleSampleMediaSource>, List<SubtitleData>> { ): Pair<List<SingleSampleMediaSource>, List<SubtitleData>> {
@ -1021,7 +1031,10 @@ class CS3IPlayer : IPlayer {
SubtitleOrigin.URL -> { SubtitleOrigin.URL -> {
if (onlineSourceFactory != null) { if (onlineSourceFactory != null) {
activeSubtitles.add(sub) activeSubtitles.add(sub)
SingleSampleMediaSource.Factory(onlineSourceFactory) SingleSampleMediaSource.Factory(onlineSourceFactory.apply {
if (sub.headers.isNotEmpty())
this.setDefaultRequestProperties(sub.headers)
})
.createMediaSource(subConfig, C.TIME_UNSET) .createMediaSource(subConfig, C.TIME_UNSET)
} else { } else {
null null

View file

@ -84,7 +84,8 @@ class DownloadFileGenerator(
realName.ifBlank { ctx.getString(R.string.default_subtitles) }, realName.ifBlank { ctx.getString(R.string.default_subtitles) },
file.second.toString(), file.second.toString(),
SubtitleOrigin.DOWNLOADED_FILE, SubtitleOrigin.DOWNLOADED_FILE,
name.toSubtitleMimeType() name.toSubtitleMimeType(),
emptyMap()
) )
) )
} }

View file

@ -399,7 +399,8 @@ class GeneratorPlayer : FullScreenPlayer() {
name = getName(currentSubtitle, true), name = getName(currentSubtitle, true),
url = url, url = url,
origin = SubtitleOrigin.URL, origin = SubtitleOrigin.URL,
mimeType = url.toSubtitleMimeType() mimeType = url.toSubtitleMimeType(),
headers = currentSubtitle.headers
) )
runOnMainThread { runOnMainThread {
addAndSelectSubtitles(subtitle) addAndSelectSubtitles(subtitle)
@ -483,7 +484,8 @@ class GeneratorPlayer : FullScreenPlayer() {
name, name,
uri.toString(), uri.toString(),
SubtitleOrigin.DOWNLOADED_FILE, SubtitleOrigin.DOWNLOADED_FILE,
name.toSubtitleMimeType() name.toSubtitleMimeType(),
emptyMap()
) )
addAndSelectSubtitles(subtitleData) addAndSelectSubtitles(subtitleData)

View file

@ -29,12 +29,14 @@ enum class SubtitleOrigin {
/** /**
* @param name To be displayed in the player * @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 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( data class SubtitleData(
val name: String, val name: String,
val url: String, val url: String,
val origin: SubtitleOrigin, val origin: SubtitleOrigin,
val mimeType: String, val mimeType: String,
val headers: Map<String, String>
) )
class PlayerSubtitleHelper { class PlayerSubtitleHelper {
@ -71,7 +73,8 @@ class PlayerSubtitleHelper {
name = subtitleFile.lang, name = subtitleFile.lang,
url = subtitleFile.url, url = subtitleFile.url,
origin = SubtitleOrigin.URL, origin = SubtitleOrigin.URL,
mimeType = subtitleFile.url.toSubtitleMimeType() mimeType = subtitleFile.url.toSubtitleMimeType(),
headers = emptyMap()
) )
} }
} }

View file

@ -409,6 +409,11 @@ class ResultViewModel2 : ViewModel() {
private const val EPISODE_RANGE_SIZE = 20 private const val EPISODE_RANGE_SIZE = 20
private const val EPISODE_RANGE_OVERLOAD = 30 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? { private fun filterName(name: String?): String? {
if (name == null) return null if (name == null) return null
Regex("[eE]pisode [0-9]*(.*)").find(name)?.groupValues?.get(1)?.let { 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 if (isMovie || currentSeasons.size <= 1) null else
when (indexer.season) { when (indexer.season) {
0 -> txt(R.string.no_season) 0 -> txt(R.string.no_season)
else -> txt( else -> {
R.string.season_format, val seasonNames = (currentResponse as? EpisodeResponse)?.seasonNames
txt(R.string.season), val seasonData =
indexer.season seasonNames.getSeason(indexer.season)
) //TODO FIX DISPLAYNAME 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 val id = mainId + episode + idIndex * 1000000
if (!existingEpisodes.contains(episode)) { if (!existingEpisodes.contains(episode)) {
existingEpisodes.add(id) existingEpisodes.add(id)
val seasonData = loadResponse.seasonNames.getSeason(i.season)
val eps = val eps =
buildResultEpisode( buildResultEpisode(
loadResponse.name, loadResponse.name,
@ -1585,7 +1598,7 @@ class ResultViewModel2 : ViewModel() {
i.posterUrl, i.posterUrl,
episode, episode,
null, null,
i.season, seasonData?.displaySeason ?: i.season,
i.data, i.data,
loadResponse.apiName, loadResponse.apiName,
id, id,
@ -1621,7 +1634,7 @@ class ResultViewModel2 : ViewModel() {
existingEpisodes.add(id) existingEpisodes.add(id)
val seasonIndex = episode.season?.minus(1) val seasonIndex = episode.season?.minus(1)
val currentSeason = val currentSeason =
loadResponse.seasonNames?.getOrNull(seasonIndex ?: -1) loadResponse.seasonNames.getSeason(episode.season)
val ep = val ep =
buildResultEpisode( buildResultEpisode(
@ -1630,7 +1643,7 @@ class ResultViewModel2 : ViewModel() {
episode.posterUrl, episode.posterUrl,
episodeIndex, episodeIndex,
seasonIndex, seasonIndex,
currentSeason?.season ?: episode.season, currentSeason?.displaySeason ?: episode.season,
episode.data, episode.data,
loadResponse.apiName, loadResponse.apiName,
id, id,
@ -1731,10 +1744,19 @@ class ResultViewModel2 : ViewModel() {
_dubSubSelections.postValue(dubSelection.map { txt(it) to it }) _dubSubSelections.postValue(dubSelection.map { txt(it) to it })
if (loadResponse is EpisodeResponse) { if (loadResponse is EpisodeResponse) {
_seasonSelections.postValue(seasonsSelection.map { seasonNumber -> _seasonSelections.postValue(seasonsSelection.map { seasonNumber ->
val seasonData = loadResponse.seasonNames.getSeason(seasonNumber)
val fixedSeasonNumber = seasonData?.displaySeason ?: seasonNumber
val suffix = seasonData?.name?.let { " $it" } ?: ""
val name = val name =
/*loadResponse.seasonNames?.firstOrNull { it.season == seasonNumber }?.name?.let { seasonData -> /*loadResponse.seasonNames?.firstOrNull { it.season == seasonNumber }?.name?.let { seasonData ->
txt(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 name to seasonNumber
}) })
} }

View file

@ -210,7 +210,6 @@
</string> </string>
<string name="season">Staffel</string> <string name="season">Staffel</string>
<string name="season_format">%s %d</string>
<string name="no_season">Keine Staffel</string> <string name="no_season">Keine Staffel</string>
<string name="episode">Episode</string> <string name="episode">Episode</string>
<string name="episodes">Episoden</string> <string name="episodes">Episoden</string>

View file

@ -228,7 +228,6 @@
</string> </string>
<string name="season">Sezona</string> <string name="season">Sezona</string>
<string name="season_format">%s %d</string>
<string name="no_season">Nema sezone</string> <string name="no_season">Nema sezone</string>
<string name="episode">Epizoda</string> <string name="episode">Epizoda</string>
<string name="episodes">Epizode</string> <string name="episodes">Epizode</string>

View file

@ -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="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">Seizoen</string>
<string name="season_format">%s %d</string>
<string name="no_season">Geen seizoen</string> <string name="no_season">Geen seizoen</string>
<string name="episode">Aflevering</string> <string name="episode">Aflevering</string>
<string name="episodes">afleveringen</string> <string name="episodes">afleveringen</string>

View file

@ -221,7 +221,6 @@
<string name="season">Mùa</string> <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="no_season">Không có mùa nào</string>
<string name="episode">Tập</string> <string name="episode">Tập</string>
<string name="episodes">Tập</string> <string name="episodes">Tập</string>

View file

@ -231,7 +231,6 @@
</string> </string>
<string name="season"></string> <string name="season"></string>
<string name="season_format">%s %d</string>
<string name="no_season">无季</string> <string name="no_season">无季</string>
<string name="episode"></string> <string name="episode"></string>
<string name="episodes"></string> <string name="episodes"></string>

View file

@ -290,7 +290,7 @@
</string> </string>
<string name="season">Season</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="no_season">No Season</string>
<string name="episode">Episode</string> <string name="episode">Episode</string>
<string name="episodes">Episodes</string> <string name="episodes">Episodes</string>