Merge branch 'arabicSubs100%' of https://github.com/SANCTI-afk/cloudstream into arabicSubs100%

This commit is contained in:
SANCTI-afk 2022-09-10 20:57:06 +02:00
commit db70336d1b
14 changed files with 81 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

@ -4,10 +4,12 @@ import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.webkit.JavascriptInterface
import android.webkit.WebResourceRequest import android.webkit.WebResourceRequest
import android.webkit.WebView import android.webkit.WebView
import android.webkit.WebViewClient import android.webkit.WebViewClient
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.network.WebViewResolver import com.lagradost.cloudstream3.network.WebViewResolver
@ -46,6 +48,7 @@ class WebviewFragment : Fragment() {
return super.shouldOverrideUrlLoading(view, request) return super.shouldOverrideUrlLoading(view, request)
} }
} }
web_view.addJavascriptInterface(RepoApi(activity), "RepoApi")
web_view.settings.javaScriptEnabled = true web_view.settings.javaScriptEnabled = true
web_view.settings.domStorageEnabled = true web_view.settings.domStorageEnabled = true
@ -69,4 +72,11 @@ class WebviewFragment : Fragment() {
putString(WEBVIEW_URL, webViewUrl) putString(WEBVIEW_URL, webViewUrl)
} }
} }
private class RepoApi(val activity: FragmentActivity?) {
@JavascriptInterface
fun installRepo(repoUrl: String) {
activity?.loadRepository(repoUrl)
}
}
} }

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>