mirror of
https://github.com/recloudstream/cloudstream.git
synced 2024-08-15 01:53:11 +00:00
SubDL subtitles provider
This commit is contained in:
parent
0a5399d9b6
commit
0556bff907
10 changed files with 132 additions and 10 deletions
|
@ -19,7 +19,7 @@ class AbstractSubtitleEntities {
|
||||||
|
|
||||||
data class SubtitleSearch(
|
data class SubtitleSearch(
|
||||||
var query: String = "",
|
var query: String = "",
|
||||||
var imdb: Long? = null,
|
var imdb: String? = null,
|
||||||
var lang: String? = null,
|
var lang: String? = null,
|
||||||
var epNumber: Int? = null,
|
var epNumber: Int? = null,
|
||||||
var seasonNumber: Int? = null,
|
var seasonNumber: Int? = null,
|
||||||
|
|
|
@ -3,7 +3,6 @@ package com.lagradost.cloudstream3.syncproviders
|
||||||
import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
|
import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
|
||||||
import com.lagradost.cloudstream3.AcraApplication.Companion.removeKeys
|
import com.lagradost.cloudstream3.AcraApplication.Companion.removeKeys
|
||||||
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
|
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
|
||||||
import com.lagradost.cloudstream3.syncproviders.providers.SubScene
|
|
||||||
import com.lagradost.cloudstream3.syncproviders.providers.*
|
import com.lagradost.cloudstream3.syncproviders.providers.*
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
|
@ -16,6 +15,7 @@ abstract class AccountManager(private val defIndex: Int) : AuthAPI {
|
||||||
val indexSubtitlesApi = IndexSubtitleApi()
|
val indexSubtitlesApi = IndexSubtitleApi()
|
||||||
val addic7ed = Addic7ed()
|
val addic7ed = Addic7ed()
|
||||||
val subScene = SubScene()
|
val subScene = SubScene()
|
||||||
|
val subDl = SubDL()
|
||||||
val localListApi = LocalList()
|
val localListApi = LocalList()
|
||||||
|
|
||||||
// used to login via app intent
|
// used to login via app intent
|
||||||
|
@ -44,7 +44,8 @@ abstract class AccountManager(private val defIndex: Int) : AuthAPI {
|
||||||
openSubtitlesApi,
|
openSubtitlesApi,
|
||||||
indexSubtitlesApi, // they got anti scraping measures in place :(
|
indexSubtitlesApi, // they got anti scraping measures in place :(
|
||||||
addic7ed,
|
addic7ed,
|
||||||
subScene
|
subScene,
|
||||||
|
subDl
|
||||||
)
|
)
|
||||||
|
|
||||||
const val appString = "cloudstreamapp"
|
const val appString = "cloudstreamapp"
|
||||||
|
|
|
@ -98,7 +98,7 @@ class IndexSubtitleApi : AbstractSubApi {
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun search(query: AbstractSubtitleEntities.SubtitleSearch): List<AbstractSubtitleEntities.SubtitleEntity> {
|
override suspend fun search(query: AbstractSubtitleEntities.SubtitleSearch): List<AbstractSubtitleEntities.SubtitleEntity> {
|
||||||
val imdbId = query.imdb ?: 0
|
val imdbId = query.imdb?.replace("tt", "")?.toLong() ?: 0
|
||||||
val lang = query.lang
|
val lang = query.lang
|
||||||
val queryLang = SubtitleHelper.fromTwoLettersToLanguage(lang.toString())
|
val queryLang = SubtitleHelper.fromTwoLettersToLanguage(lang.toString())
|
||||||
val queryText = query.query
|
val queryText = query.query
|
||||||
|
|
|
@ -185,7 +185,7 @@ class OpenSubtitlesApi(index: Int) : InAppAuthAPIManager(index), AbstractSubApi
|
||||||
throwIfCantDoRequest()
|
throwIfCantDoRequest()
|
||||||
val fixedLang = fixLanguage(query.lang)
|
val fixedLang = fixLanguage(query.lang)
|
||||||
|
|
||||||
val imdbId = query.imdb ?: 0
|
val imdbId = query.imdb?.replace("tt", "")?.toInt() ?: 0
|
||||||
val queryText = query.query
|
val queryText = query.query
|
||||||
val epNum = query.epNumber ?: 0
|
val epNum = query.epNumber ?: 0
|
||||||
val seasonNum = query.seasonNumber ?: 0
|
val seasonNum = query.seasonNumber ?: 0
|
||||||
|
|
|
@ -0,0 +1,114 @@
|
||||||
|
package com.lagradost.cloudstream3.syncproviders.providers
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty
|
||||||
|
import com.lagradost.cloudstream3.TvType
|
||||||
|
import com.lagradost.cloudstream3.app
|
||||||
|
import com.lagradost.cloudstream3.subtitles.AbstractSubProvider
|
||||||
|
import com.lagradost.cloudstream3.subtitles.AbstractSubtitleEntities
|
||||||
|
import com.lagradost.cloudstream3.subtitles.SubtitleResource
|
||||||
|
import com.lagradost.cloudstream3.utils.AppUtils
|
||||||
|
|
||||||
|
class SubDL : AbstractSubProvider {
|
||||||
|
//API Documentation: https://subdl.com/api-doc
|
||||||
|
val mainUrl = "https://subdl.com/"
|
||||||
|
val name = "SubDL"
|
||||||
|
override val idPrefix = "subdl"
|
||||||
|
companion object {
|
||||||
|
const val APIKEY = "zRJl5QA-8jNA2i0pE8cxANbEukANp7IM"
|
||||||
|
const val APIENDPOINT = "https://api.subdl.com/api/v1/subtitles"
|
||||||
|
const val DOWNLOADENDPOINT = "https://dl.subdl.com"
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun search(query: AbstractSubtitleEntities.SubtitleSearch): List<AbstractSubtitleEntities.SubtitleEntity>? {
|
||||||
|
|
||||||
|
val imdbId = query.imdb
|
||||||
|
val queryText = query.query
|
||||||
|
val epNum = query.epNumber ?: 0
|
||||||
|
val seasonNum = query.seasonNumber ?: 0
|
||||||
|
val yearNum = query.year ?: 0
|
||||||
|
|
||||||
|
val epQuery = if (epNum > 0) "&episode_number=$epNum" else ""
|
||||||
|
val seasonQuery = if (seasonNum > 0) "&season_number=$seasonNum" else ""
|
||||||
|
val yearQuery = if (yearNum > 0) "&year=$yearNum" else ""
|
||||||
|
|
||||||
|
val searchQueryUrl = when (imdbId) {
|
||||||
|
//Use imdb_id to search if its valid
|
||||||
|
null -> "$APIENDPOINT?api_key=$APIKEY&film_name=$queryText&languages=${query.lang}$epQuery$seasonQuery$yearQuery"
|
||||||
|
else -> "$APIENDPOINT?api_key=$APIKEY&imdb_id=$imdbId&languages=${query.lang}$epQuery$seasonQuery$yearQuery"
|
||||||
|
}
|
||||||
|
|
||||||
|
val req = app.get(
|
||||||
|
url = searchQueryUrl,
|
||||||
|
headers = mapOf(
|
||||||
|
"Accept" to "application/json"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!req.isSuccessful) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
val results = mutableListOf<AbstractSubtitleEntities.SubtitleEntity>()
|
||||||
|
|
||||||
|
AppUtils.tryParseJson<ApiResponse>(req.text)?.let {resp ->
|
||||||
|
|
||||||
|
resp.subtitles?.forEach { subtitle ->
|
||||||
|
|
||||||
|
val name = subtitle.releaseName
|
||||||
|
val lang = subtitle.lang.replaceFirstChar { it.uppercase() }
|
||||||
|
val resEpNum = subtitle.episode ?: query.epNumber
|
||||||
|
val resSeasonNum = subtitle.season ?: query.seasonNumber
|
||||||
|
val year = resp.results?.firstOrNull()?.year ?: query.year
|
||||||
|
val type = if ((resSeasonNum ?: 0) > 0) TvType.TvSeries else TvType.Movie
|
||||||
|
|
||||||
|
results.add(
|
||||||
|
AbstractSubtitleEntities.SubtitleEntity(
|
||||||
|
idPrefix = this.idPrefix,
|
||||||
|
name = name,
|
||||||
|
lang = lang,
|
||||||
|
data = "${DOWNLOADENDPOINT}${subtitle.url}",
|
||||||
|
type = type,
|
||||||
|
source = this.name,
|
||||||
|
epNumber = resEpNum,
|
||||||
|
seasonNumber = resSeasonNum,
|
||||||
|
year = year,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun SubtitleResource.getResources(data: AbstractSubtitleEntities.SubtitleEntity) {
|
||||||
|
this.addZipUrl(data.data) { name, _ ->
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class ApiResponse(
|
||||||
|
@JsonProperty("status") val status: Boolean? = null,
|
||||||
|
@JsonProperty("results") val results: List<Result>? = null,
|
||||||
|
@JsonProperty("subtitles") val subtitles: List<Subtitle>? = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
data class Result(
|
||||||
|
@JsonProperty("sd_id") val sdId: Int? = null,
|
||||||
|
@JsonProperty("type") val type: String? = null,
|
||||||
|
@JsonProperty("name") val name: String? = null,
|
||||||
|
@JsonProperty("imdb_id") val imdbId: String? = null,
|
||||||
|
@JsonProperty("tmdb_id") val tmdbId: Long? = null,
|
||||||
|
@JsonProperty("first_air_date") val firstAirDate: String? = null,
|
||||||
|
@JsonProperty("year") val year: Int? = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
data class Subtitle(
|
||||||
|
@JsonProperty("release_name") val releaseName: String,
|
||||||
|
@JsonProperty("name") val name: String,
|
||||||
|
@JsonProperty("lang") val lang: String,
|
||||||
|
@JsonProperty("author") val author: String? = null,
|
||||||
|
@JsonProperty("url") val url: String? = null,
|
||||||
|
@JsonProperty("season") val season: Int? = null,
|
||||||
|
@JsonProperty("episode") val episode: Int? = null,
|
||||||
|
)
|
||||||
|
}
|
|
@ -177,7 +177,7 @@ open class FullScreenPlayer : AbstractPlayerFragment() {
|
||||||
|
|
||||||
open fun openOnlineSubPicker(
|
open fun openOnlineSubPicker(
|
||||||
context: Context,
|
context: Context,
|
||||||
imdbId: Long?,
|
imdbId: String?,
|
||||||
dismissCallback: (() -> Unit)
|
dismissCallback: (() -> Unit)
|
||||||
) {
|
) {
|
||||||
throw NotImplementedError()
|
throw NotImplementedError()
|
||||||
|
|
|
@ -258,6 +258,7 @@ class GeneratorPlayer : FullScreenPlayer() {
|
||||||
var episode: Int? = null,
|
var episode: Int? = null,
|
||||||
var season: Int? = null,
|
var season: Int? = null,
|
||||||
var name: String? = null,
|
var name: String? = null,
|
||||||
|
var imdbId: String? = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun getMetaData(): TempMetaData {
|
private fun getMetaData(): TempMetaData {
|
||||||
|
@ -270,6 +271,7 @@ class GeneratorPlayer : FullScreenPlayer() {
|
||||||
meta.season = newMeta.season
|
meta.season = newMeta.season
|
||||||
}
|
}
|
||||||
meta.name = newMeta.headerName
|
meta.name = newMeta.headerName
|
||||||
|
meta.imdbId = newMeta.imdbId
|
||||||
}
|
}
|
||||||
|
|
||||||
is ExtractorUri -> {
|
is ExtractorUri -> {
|
||||||
|
@ -284,7 +286,7 @@ class GeneratorPlayer : FullScreenPlayer() {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun openOnlineSubPicker(
|
override fun openOnlineSubPicker(
|
||||||
context: Context, imdbId: Long?, dismissCallback: (() -> Unit)
|
context: Context, imdbId: String?, dismissCallback: (() -> Unit)
|
||||||
) {
|
) {
|
||||||
val providers = subsProviders
|
val providers = subsProviders
|
||||||
val isSingleProvider = subsProviders.size == 1
|
val isSingleProvider = subsProviders.size == 1
|
||||||
|
@ -424,7 +426,7 @@ class GeneratorPlayer : FullScreenPlayer() {
|
||||||
val search =
|
val search =
|
||||||
AbstractSubtitleEntities.SubtitleSearch(
|
AbstractSubtitleEntities.SubtitleSearch(
|
||||||
query = query ?: return@ioSafe,
|
query = query ?: return@ioSafe,
|
||||||
imdb = imdbId,
|
imdb = imdbId ?: currentTempMeta.imdbId,
|
||||||
epNumber = currentTempMeta.episode,
|
epNumber = currentTempMeta.episode,
|
||||||
seasonNumber = currentTempMeta.season,
|
seasonNumber = currentTempMeta.season,
|
||||||
lang = currentLanguageTwoLetters.ifBlank { null },
|
lang = currentLanguageTwoLetters.ifBlank { null },
|
||||||
|
|
|
@ -51,6 +51,7 @@ data class ResultEpisode(
|
||||||
/** Sum of all previous season episode counts + episode */
|
/** Sum of all previous season episode counts + episode */
|
||||||
val totalEpisodeIndex: Int? = null,
|
val totalEpisodeIndex: Int? = null,
|
||||||
val airDate: Long? = null,
|
val airDate: Long? = null,
|
||||||
|
val imdbId: String? = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
fun ResultEpisode.getRealPosition(): Long {
|
fun ResultEpisode.getRealPosition(): Long {
|
||||||
|
@ -87,6 +88,7 @@ fun buildResultEpisode(
|
||||||
parentId: Int,
|
parentId: Int,
|
||||||
totalEpisodeIndex: Int? = null,
|
totalEpisodeIndex: Int? = null,
|
||||||
airDate: Long? = null,
|
airDate: Long? = null,
|
||||||
|
imdbId: String? = null,
|
||||||
): ResultEpisode {
|
): ResultEpisode {
|
||||||
val posDur = getViewPos(id)
|
val posDur = getViewPos(id)
|
||||||
val videoWatchState = getVideoWatchState(id) ?: VideoWatchState.None
|
val videoWatchState = getVideoWatchState(id) ?: VideoWatchState.None
|
||||||
|
@ -111,6 +113,7 @@ fun buildResultEpisode(
|
||||||
videoWatchState,
|
videoWatchState,
|
||||||
totalEpisodeIndex,
|
totalEpisodeIndex,
|
||||||
airDate,
|
airDate,
|
||||||
|
imdbId,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -110,7 +110,7 @@ open class ResultTrailerPlayer : ResultFragmentPhone() {
|
||||||
|
|
||||||
override fun openOnlineSubPicker(
|
override fun openOnlineSubPicker(
|
||||||
context: Context,
|
context: Context,
|
||||||
imdbId: Long?,
|
imdbId: String?,
|
||||||
dismissCallback: () -> Unit
|
dismissCallback: () -> Unit
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ import com.lagradost.cloudstream3.CommonActivity.getCastSession
|
||||||
import com.lagradost.cloudstream3.CommonActivity.showToast
|
import com.lagradost.cloudstream3.CommonActivity.showToast
|
||||||
import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
|
import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
|
||||||
import com.lagradost.cloudstream3.LoadResponse.Companion.getAniListId
|
import com.lagradost.cloudstream3.LoadResponse.Companion.getAniListId
|
||||||
|
import com.lagradost.cloudstream3.LoadResponse.Companion.getImdbId
|
||||||
import com.lagradost.cloudstream3.LoadResponse.Companion.getMalId
|
import com.lagradost.cloudstream3.LoadResponse.Companion.getMalId
|
||||||
import com.lagradost.cloudstream3.LoadResponse.Companion.isMovie
|
import com.lagradost.cloudstream3.LoadResponse.Companion.isMovie
|
||||||
import com.lagradost.cloudstream3.metaproviders.SyncRedirector
|
import com.lagradost.cloudstream3.metaproviders.SyncRedirector
|
||||||
|
@ -2361,7 +2362,8 @@ class ResultViewModel2 : ViewModel() {
|
||||||
null,
|
null,
|
||||||
loadResponse.type,
|
loadResponse.type,
|
||||||
mainId,
|
mainId,
|
||||||
null
|
null,
|
||||||
|
imdbId = loadResponse.getImdbId(),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue