SubDL subtitles provider (#1082)

This commit is contained in:
KingLucius 2024-05-18 19:15:23 +03:00 committed by GitHub
parent 4d5cd288ab
commit 469a71236b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 136 additions and 12 deletions

View file

@ -1,5 +1,6 @@
package com.lagradost.cloudstream3.subtitles
import com.lagradost.cloudstream3.LoadResponse
import com.lagradost.cloudstream3.TvType
class AbstractSubtitleEntities {
@ -19,8 +20,11 @@ class AbstractSubtitleEntities {
data class SubtitleSearch(
var query: String = "",
var imdb: Long? = null,
var lang: String? = null,
var imdbId: String? = null,
var tmdbId: Int? = null,
var malId: Int? = null,
var aniListId: Int? = null,
var epNumber: Int? = null,
var seasonNumber: Int? = null,
var year: Int? = null

View file

@ -3,7 +3,6 @@ package com.lagradost.cloudstream3.syncproviders
import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
import com.lagradost.cloudstream3.AcraApplication.Companion.removeKeys
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
import com.lagradost.cloudstream3.syncproviders.providers.SubScene
import com.lagradost.cloudstream3.syncproviders.providers.*
import java.util.concurrent.TimeUnit
@ -16,6 +15,7 @@ abstract class AccountManager(private val defIndex: Int) : AuthAPI {
val indexSubtitlesApi = IndexSubtitleApi()
val addic7ed = Addic7ed()
val subScene = SubScene()
val subDl = SubDL()
val localListApi = LocalList()
// used to login via app intent
@ -44,7 +44,8 @@ abstract class AccountManager(private val defIndex: Int) : AuthAPI {
openSubtitlesApi,
indexSubtitlesApi, // they got anti scraping measures in place :(
addic7ed,
subScene
subScene,
subDl
)
const val appString = "cloudstreamapp"

View file

@ -98,7 +98,7 @@ class IndexSubtitleApi : AbstractSubApi {
}
override suspend fun search(query: AbstractSubtitleEntities.SubtitleSearch): List<AbstractSubtitleEntities.SubtitleEntity> {
val imdbId = query.imdb ?: 0
val imdbId = query.imdbId?.replace("tt", "")?.toLong() ?: 0
val lang = query.lang
val queryLang = SubtitleHelper.fromTwoLettersToLanguage(lang.toString())
val queryText = query.query

View file

@ -185,7 +185,7 @@ class OpenSubtitlesApi(index: Int) : InAppAuthAPIManager(index), AbstractSubApi
throwIfCantDoRequest()
val fixedLang = fixLanguage(query.lang)
val imdbId = query.imdb ?: 0
val imdbId = query.imdbId?.replace("tt", "")?.toInt() ?: 0
val queryText = query.query
val epNum = query.epNumber ?: 0
val seasonNum = query.seasonNumber ?: 0

View file

@ -0,0 +1,102 @@
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
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 queryText = query.query
val epNum = query.epNumber ?: 0
val seasonNum = query.seasonNumber ?: 0
val yearNum = query.year ?: 0
val idQuery = when {
query.imdbId != null -> "&imdb_id=${query.imdbId}"
query.tmdbId != null -> "&tmdb_id=${query.tmdbId}"
else -> null
}
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 (idQuery) {
//Use imdb/tmdb 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$idQuery&languages=${query.lang}$epQuery$seasonQuery$yearQuery"
}
val req = app.get(
url = searchQueryUrl,
headers = mapOf(
"Accept" to "application/json"
)
)
return req.parsedSafe<ApiResponse>()?.subtitles?.map { 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 type = if ((resSeasonNum ?: 0) > 0) TvType.TvSeries else TvType.Movie
AbstractSubtitleEntities.SubtitleEntity(
idPrefix = this.idPrefix,
name = name,
lang = lang,
data = "${DOWNLOADENDPOINT}${subtitle.url}",
type = type,
source = this.name,
epNumber = resEpNum,
seasonNumber = resSeasonNum,
)
}
}
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,
)
}

View file

@ -32,6 +32,7 @@ import com.lagradost.cloudstream3.CommonActivity.keyEventListener
import com.lagradost.cloudstream3.CommonActivity.playerEventListener
import com.lagradost.cloudstream3.CommonActivity.screenHeight
import com.lagradost.cloudstream3.CommonActivity.screenWidth
import com.lagradost.cloudstream3.LoadResponse
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.databinding.PlayerCustomLayoutBinding
import com.lagradost.cloudstream3.databinding.SubtitleOffsetBinding
@ -177,7 +178,7 @@ open class FullScreenPlayer : AbstractPlayerFragment() {
open fun openOnlineSubPicker(
context: Context,
imdbId: Long?,
loadResponse: LoadResponse?,
dismissCallback: (() -> Unit)
) {
throw NotImplementedError()

View file

@ -25,6 +25,10 @@ import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
import com.lagradost.cloudstream3.CommonActivity.showToast
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.getTMDbId
import com.lagradost.cloudstream3.databinding.DialogOnlineSubtitlesBinding
import com.lagradost.cloudstream3.databinding.FragmentPlayerBinding
import com.lagradost.cloudstream3.databinding.PlayerSelectSourceAndSubsBinding
@ -39,7 +43,6 @@ import com.lagradost.cloudstream3.ui.player.PlayerSubtitleHelper.Companion.toSub
import com.lagradost.cloudstream3.ui.player.source_priority.QualityDataHelper
import com.lagradost.cloudstream3.ui.player.source_priority.QualityProfileDialog
import com.lagradost.cloudstream3.ui.result.*
import com.lagradost.cloudstream3.ui.settings.Globals
import com.lagradost.cloudstream3.ui.settings.Globals.EMULATOR
import com.lagradost.cloudstream3.ui.settings.Globals.TV
import com.lagradost.cloudstream3.ui.settings.Globals.isLayout
@ -258,6 +261,7 @@ class GeneratorPlayer : FullScreenPlayer() {
var episode: Int? = null,
var season: Int? = null,
var name: String? = null,
var imdbId: String? = null,
)
private fun getMetaData(): TempMetaData {
@ -284,7 +288,7 @@ class GeneratorPlayer : FullScreenPlayer() {
}
override fun openOnlineSubPicker(
context: Context, imdbId: Long?, dismissCallback: (() -> Unit)
context: Context, loadResponse: LoadResponse?, dismissCallback: (() -> Unit)
) {
val providers = subsProviders
val isSingleProvider = subsProviders.size == 1
@ -377,6 +381,7 @@ class GeneratorPlayer : FullScreenPlayer() {
}
val currentTempMeta = getMetaData()
// bruh idk why it is not correct
val color = ColorStateList.valueOf(context.colorFromAttribute(R.attr.colorAccent))
binding.searchLoadingBar.progressTintList = color
@ -424,7 +429,10 @@ class GeneratorPlayer : FullScreenPlayer() {
val search =
AbstractSubtitleEntities.SubtitleSearch(
query = query ?: return@ioSafe,
imdb = imdbId,
imdbId = loadResponse?.getImdbId(),
tmdbId = loadResponse?.getTMDbId()?.toInt(),
malId = loadResponse?.getMalId()?.toInt(),
aniListId = loadResponse?.getAniListId()?.toInt(),
epNumber = currentTempMeta.episode,
seasonNumber = currentTempMeta.season,
lang = currentLanguageTwoLetters.ifBlank { null },
@ -633,6 +641,8 @@ class GeneratorPlayer : FullScreenPlayer() {
}
if (subsProvidersIsActive) {
val currentLoadResponse = viewModel.getLoadResponse()
val loadFromOpenSubsFooter: TextView = layoutInflater.inflate(
R.layout.sort_bottom_footer_add_choice, null
) as TextView
@ -643,7 +653,7 @@ class GeneratorPlayer : FullScreenPlayer() {
loadFromOpenSubsFooter.setOnClickListener {
shouldDismiss = false
sourceDialog.dismissSafe(activity)
openOnlineSubPicker(it.context, null) {
openOnlineSubPicker(it.context, currentLoadResponse) {
dismiss()
}
}

View file

@ -5,6 +5,7 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.lagradost.cloudstream3.LoadResponse
import com.lagradost.cloudstream3.mvvm.Resource
import com.lagradost.cloudstream3.mvvm.launchSafe
import com.lagradost.cloudstream3.mvvm.logError
@ -111,6 +112,9 @@ class PlayerGeneratorViewModel : ViewModel() {
}
}
}
fun getLoadResponse(): LoadResponse? {
return normalSafeApiCall { (generator as? RepoLinkGenerator?)?.page }
}
fun getMeta(): Any? {
return normalSafeApiCall { generator?.getCurrent() }

View file

@ -12,6 +12,7 @@ import androidx.core.view.isGone
import androidx.core.view.isVisible
import com.lagradost.cloudstream3.CommonActivity.screenHeight
import com.lagradost.cloudstream3.CommonActivity.screenWidth
import com.lagradost.cloudstream3.LoadResponse
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.ui.player.CSPlayerEvent
import com.lagradost.cloudstream3.ui.player.PlayerEventSource
@ -110,7 +111,7 @@ open class ResultTrailerPlayer : ResultFragmentPhone() {
override fun openOnlineSubPicker(
context: Context,
imdbId: Long?,
loadResponse: LoadResponse?,
dismissCallback: () -> Unit
) {
}

View file

@ -27,6 +27,7 @@ import com.lagradost.cloudstream3.CommonActivity.getCastSession
import com.lagradost.cloudstream3.CommonActivity.showToast
import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
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.isMovie
import com.lagradost.cloudstream3.MainActivity.Companion.MPV
@ -2417,7 +2418,7 @@ class ResultViewModel2 : ViewModel() {
null,
loadResponse.type,
mainId,
null
null,
)
)
}