switched yt implementation to newpipe

This commit is contained in:
LagradOst 2022-06-17 19:25:41 +02:00
parent f64b2f7f1b
commit 3d00015a0c
12 changed files with 240 additions and 106 deletions

View File

@ -74,6 +74,8 @@ android {
} }
} }
compileOptions { compileOptions {
coreLibraryDesugaringEnabled true
sourceCompatibility JavaVersion.VERSION_1_8 sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8
} }
@ -143,7 +145,7 @@ dependencies {
implementation 'com.jaredrummler:colorpicker:1.1.0' implementation 'com.jaredrummler:colorpicker:1.1.0'
//run JS //run JS
implementation 'org.mozilla:rhino:1.7R4' implementation 'org.mozilla:rhino:1.7.14'
// TorrentStream // TorrentStream
//implementation 'com.github.TorrentStream:TorrentStream-Android:2.7.0' //implementation 'com.github.TorrentStream:TorrentStream-Android:2.7.0'
@ -175,6 +177,11 @@ dependencies {
// used for subtitle decoding https://github.com/albfernandez/juniversalchardet // used for subtitle decoding https://github.com/albfernandez/juniversalchardet
implementation 'com.github.albfernandez:juniversalchardet:2.4.0' implementation 'com.github.albfernandez:juniversalchardet:2.4.0'
// play yt // slow af yt
implementation 'com.github.HaarigerHarald:android-youtubeExtractor:master-SNAPSHOT' //implementation 'com.github.HaarigerHarald:android-youtubeExtractor:master-SNAPSHOT'
// newpipe yt
implementation 'com.github.TeamNewPipe:NewPipeExtractor:master-SNAPSHOT'
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
} }

View File

@ -0,0 +1,83 @@
package com.lagradost.cloudstream3
import okhttp3.OkHttpClient
import okhttp3.RequestBody
import org.schabi.newpipe.extractor.downloader.Downloader
import org.schabi.newpipe.extractor.downloader.Request
import org.schabi.newpipe.extractor.downloader.Response
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException
import java.util.concurrent.TimeUnit
class DownloaderTestImpl private constructor(builder: OkHttpClient.Builder) : Downloader() {
private val client: OkHttpClient
override fun execute(request: Request): Response {
val httpMethod: String = request.httpMethod()
val url: String = request.url()
val headers: Map<String, List<String>> = request.headers()
val dataToSend: ByteArray? = request.dataToSend()
var requestBody: RequestBody? = null
if (dataToSend != null) {
requestBody = RequestBody.create(null, dataToSend)
}
val requestBuilder: okhttp3.Request.Builder = okhttp3.Request.Builder()
.method(httpMethod, requestBody).url(url)
.addHeader("User-Agent", USER_AGENT)
for ((headerName, headerValueList) in headers) {
if (headerValueList.size > 1) {
requestBuilder.removeHeader(headerName)
for (headerValue in headerValueList) {
requestBuilder.addHeader(headerName, headerValue)
}
} else if (headerValueList.size == 1) {
requestBuilder.header(headerName, headerValueList[0])
}
}
val response = client.newCall(requestBuilder.build()).execute()
if (response.code == 429) {
response.close()
throw ReCaptchaException("reCaptcha Challenge requested", url)
}
val body = response.body
var responseBodyToReturn: String? = null
if (body != null) {
responseBodyToReturn = body.string()
}
val latestUrl = response.request.url.toString()
return Response(
response.code, response.message, response.headers.toMultimap(),
responseBodyToReturn, latestUrl
)
}
companion object {
private const val USER_AGENT =
"Mozilla/5.0 (Windows NT 10.0; rv:91.0) Gecko/20100101 Firefox/91.0"
private var instance: DownloaderTestImpl? = null
/**
* It's recommended to call exactly once in the entire lifetime of the application.
*
* @param builder if null, default builder will be used
* @return a new instance of [DownloaderTestImpl]
*/
fun init(builder: OkHttpClient.Builder?): DownloaderTestImpl? {
instance = DownloaderTestImpl(
builder ?: OkHttpClient.Builder()
)
return instance
}
fun getInstance(): DownloaderTestImpl? {
if (instance == null) {
init(null)
}
return instance
}
}
init {
client = builder.readTimeout(30, TimeUnit.SECONDS).build()
}
}

View File

@ -19,6 +19,7 @@ import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.malApi
import com.lagradost.cloudstream3.ui.player.SubtitleData import com.lagradost.cloudstream3.ui.player.SubtitleData
import com.lagradost.cloudstream3.utils.AppUtils.toJson import com.lagradost.cloudstream3.utils.AppUtils.toJson
import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.loadExtractor
import okhttp3.Interceptor import okhttp3.Interceptor
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.*
@ -854,7 +855,7 @@ interface LoadResponse {
var rating: Int? // 0-10000 var rating: Int? // 0-10000
var tags: List<String>? var tags: List<String>?
var duration: Int? // in minutes var duration: Int? // in minutes
var trailers: List<String>? var trailers: List<ExtractorLink>?
var recommendations: List<SearchResponse>? var recommendations: List<SearchResponse>?
var actors: List<ActorData>? var actors: List<ActorData>?
var comingSoon: Boolean var comingSoon: Boolean
@ -897,27 +898,34 @@ interface LoadResponse {
addImdbId(imdbUrlToIdNullable(url)) addImdbId(imdbUrlToIdNullable(url))
} }
/**better to set trailers directly instead of calling this multiple times*/ /**better to call addTrailer with mutible trailers directly instead of calling this multiple times*/
fun LoadResponse.addTrailer(trailerUrl: String?) { suspend fun LoadResponse.addTrailer(trailerUrl: String?, referer: String? = null) {
if (trailerUrl == null) return if (trailerUrl == null) return
val newTrailers = loadExtractor(trailerUrl, referer)
addTrailer(newTrailers)
}
fun LoadResponse.addTrailer(newTrailers: List<ExtractorLink>) {
if (this.trailers == null) { if (this.trailers == null) {
this.trailers = listOf(trailerUrl) this.trailers = newTrailers
} else { } else {
val update = this.trailers?.toMutableList() val update = this.trailers?.toMutableList() ?: mutableListOf()
update?.add(trailerUrl) update.addAll(newTrailers)
this.trailers = update this.trailers = update
} }
} }
fun LoadResponse.addTrailer(trailerUrls: List<String>?) { suspend fun LoadResponse.addTrailer(trailerUrls: List<String>?, referer: String? = null) {
if (trailerUrls == null) return if (trailerUrls == null) return
if (this.trailers == null) { val newTrailers = trailerUrls.apmap { trailerUrl ->
this.trailers = trailerUrls try {
} else { loadExtractor(trailerUrl, referer)
val update = this.trailers?.toMutableList() } catch (e: Exception) {
update?.addAll(trailerUrls) logError(e)
this.trailers = update emptyList()
} }
}.flatten().distinct()
addTrailer(newTrailers)
} }
fun LoadResponse.addImdbId(id: String?) { fun LoadResponse.addImdbId(id: String?) {
@ -997,7 +1005,7 @@ data class TorrentLoadResponse(
override var rating: Int? = null, override var rating: Int? = null,
override var tags: List<String>? = null, override var tags: List<String>? = null,
override var duration: Int? = null, override var duration: Int? = null,
override var trailers: List<String>? = null, override var trailers: List<ExtractorLink>? = null,
override var recommendations: List<SearchResponse>? = null, override var recommendations: List<SearchResponse>? = null,
override var actors: List<ActorData>? = null, override var actors: List<ActorData>? = null,
override var comingSoon: Boolean = false, override var comingSoon: Boolean = false,
@ -1025,7 +1033,7 @@ data class AnimeLoadResponse(
override var rating: Int? = null, override var rating: Int? = null,
override var duration: Int? = null, override var duration: Int? = null,
override var trailers: List<String>? = null, override var trailers: List<ExtractorLink>? = null,
override var recommendations: List<SearchResponse>? = null, override var recommendations: List<SearchResponse>? = null,
override var actors: List<ActorData>? = null, override var actors: List<ActorData>? = null,
override var comingSoon: Boolean = false, override var comingSoon: Boolean = false,
@ -1038,12 +1046,12 @@ fun AnimeLoadResponse.addEpisodes(status: DubStatus, episodes: List<Episode>?) {
this.episodes[status] = episodes this.episodes[status] = episodes
} }
fun MainAPI.newAnimeLoadResponse( suspend fun MainAPI.newAnimeLoadResponse(
name: String, name: String,
url: String, url: String,
type: TvType, type: TvType,
comingSoonIfNone: Boolean = true, comingSoonIfNone: Boolean = true,
initializer: AnimeLoadResponse.() -> Unit = { }, initializer: suspend AnimeLoadResponse.() -> Unit = { },
): AnimeLoadResponse { ): AnimeLoadResponse {
val builder = AnimeLoadResponse(name = name, url = url, apiName = this.name, type = type) val builder = AnimeLoadResponse(name = name, url = url, apiName = this.name, type = type)
builder.initializer() builder.initializer()
@ -1072,7 +1080,7 @@ data class MovieLoadResponse(
override var rating: Int? = null, override var rating: Int? = null,
override var tags: List<String>? = null, override var tags: List<String>? = null,
override var duration: Int? = null, override var duration: Int? = null,
override var trailers: List<String>? = null, override var trailers: List<ExtractorLink>? = null,
override var recommendations: List<SearchResponse>? = null, override var recommendations: List<SearchResponse>? = null,
override var actors: List<ActorData>? = null, override var actors: List<ActorData>? = null,
override var comingSoon: Boolean = false, override var comingSoon: Boolean = false,
@ -1080,12 +1088,12 @@ data class MovieLoadResponse(
override var posterHeaders: Map<String, String>? = null, override var posterHeaders: Map<String, String>? = null,
) : LoadResponse ) : LoadResponse
fun <T> MainAPI.newMovieLoadResponse( suspend fun <T> MainAPI.newMovieLoadResponse(
name: String, name: String,
url: String, url: String,
type: TvType, type: TvType,
data: T?, data: T?,
initializer: MovieLoadResponse.() -> Unit = { } initializer: suspend MovieLoadResponse.() -> Unit = { }
): MovieLoadResponse { ): MovieLoadResponse {
// just in case // just in case
if (data is String) return newMovieLoadResponse( if (data is String) return newMovieLoadResponse(
@ -1108,12 +1116,12 @@ fun <T> MainAPI.newMovieLoadResponse(
return builder return builder
} }
fun MainAPI.newMovieLoadResponse( suspend fun MainAPI.newMovieLoadResponse(
name: String, name: String,
url: String, url: String,
type: TvType, type: TvType,
dataUrl: String, dataUrl: String,
initializer: MovieLoadResponse.() -> Unit = { } initializer: suspend MovieLoadResponse.() -> Unit = { }
): MovieLoadResponse { ): MovieLoadResponse {
val builder = MovieLoadResponse( val builder = MovieLoadResponse(
name = name, name = name,
@ -1193,7 +1201,7 @@ data class TvSeriesLoadResponse(
override var rating: Int? = null, override var rating: Int? = null,
override var tags: List<String>? = null, override var tags: List<String>? = null,
override var duration: Int? = null, override var duration: Int? = null,
override var trailers: List<String>? = null, override var trailers: List<ExtractorLink>? = null,
override var recommendations: List<SearchResponse>? = null, override var recommendations: List<SearchResponse>? = null,
override var actors: List<ActorData>? = null, override var actors: List<ActorData>? = null,
override var comingSoon: Boolean = false, override var comingSoon: Boolean = false,
@ -1201,12 +1209,12 @@ data class TvSeriesLoadResponse(
override var posterHeaders: Map<String, String>? = null, override var posterHeaders: Map<String, String>? = null,
) : LoadResponse ) : LoadResponse
fun MainAPI.newTvSeriesLoadResponse( suspend fun MainAPI.newTvSeriesLoadResponse(
name: String, name: String,
url: String, url: String,
type: TvType, type: TvType,
episodes: List<Episode>, episodes: List<Episode>,
initializer: TvSeriesLoadResponse.() -> Unit = { } initializer: suspend TvSeriesLoadResponse.() -> Unit = { }
): TvSeriesLoadResponse { ): TvSeriesLoadResponse {
val builder = TvSeriesLoadResponse( val builder = TvSeriesLoadResponse(
name = name, name = name,

View File

@ -76,6 +76,7 @@ import kotlinx.android.synthetic.main.fragment_result_swipe.*
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.schabi.newpipe.extractor.NewPipe
import java.io.File import java.io.File
import kotlin.concurrent.thread import kotlin.concurrent.thread
@ -366,9 +367,23 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
} }
fun test() { fun test() {
//val youtubeLink = "https://www.youtube.com/watch?v=TxB48MEAmZw" /*thread {
val youtubeLink = "https://www.youtube.com/watch?v=Zxem9rqJ5S0"
val url = YoutubeStreamLinkHandlerFactory.getInstance().fromUrl(youtubeLink)
println("ID:::: ${url.id}")
NewPipe.init(DownloaderTestImpl.getInstance())
val service = ServiceList.YouTube
val s = object : YoutubeStreamExtractor(
service,
url
) {
}
s.fetchPage()
val streams = s.videoStreams
println("STREAMS: ${streams.map { "url = "+ it.url + " extra= " + it.height + "|" + it.isVideoOnly + "\n" }}")
}*/
/* /*
runBlocking { runBlocking {
@ -598,6 +613,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
loadCache() loadCache()
test() test()
NewPipe.init(DownloaderTestImpl.getInstance())
/*nav_view.setOnNavigationItemSelectedListener { item -> /*nav_view.setOnNavigationItemSelectedListener { item ->
when (item.itemId) { when (item.itemId) {
R.id.navigation_home -> { R.id.navigation_home -> {

View File

@ -3,10 +3,11 @@ package com.lagradost.cloudstream3.animeproviders
import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
import com.lagradost.cloudstream3.mvvm.safeApiCall import com.lagradost.cloudstream3.mvvm.safeApiCall
import com.lagradost.cloudstream3.utils.* import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.getQualityFromName
import com.lagradost.cloudstream3.utils.loadExtractor
import org.jsoup.Jsoup import org.jsoup.Jsoup
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
import java.util.ArrayList
class KuronimeProvider : MainAPI() { class KuronimeProvider : MainAPI() {
override var mainUrl = "https://185.231.223.254" override var mainUrl = "https://185.231.223.254"
@ -139,7 +140,6 @@ class KuronimeProvider : MainAPI() {
plot = description plot = description
addTrailer(trailer) addTrailer(trailer)
this.tags = tags this.tags = tags
trailers = listOf(trailer)
} }
} }

View File

@ -0,0 +1,68 @@
package com.lagradost.cloudstream3.extractors
import com.lagradost.cloudstream3.ErrorLoadingException
import com.lagradost.cloudstream3.mvvm.Resource
import com.lagradost.cloudstream3.mvvm.safeApiCall
import com.lagradost.cloudstream3.utils.ExtractorApi
import com.lagradost.cloudstream3.utils.ExtractorLink
import org.schabi.newpipe.extractor.ServiceList
import org.schabi.newpipe.extractor.services.youtube.extractors.YoutubeStreamExtractor
import org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeStreamLinkHandlerFactory
import org.schabi.newpipe.extractor.stream.VideoStream
class YoutubeExtractor : ExtractorApi() {
override val mainUrl = "https://www.youtube.com"
override val requiresReferer = false
override val name = "YouTube"
companion object {
private var ytVideos: MutableMap<String, List<VideoStream>> = mutableMapOf()
}
override fun getExtractorUrl(id: String): String {
return "https://www.youtube.com/watch?v=$id"
}
override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink>? {
val streams = safeApiCall {
val streams = ytVideos[url] ?: let {
val link =
YoutubeStreamLinkHandlerFactory.getInstance().fromUrl(url)
val s = object : YoutubeStreamExtractor(
ServiceList.YouTube,
link
) {
}
s.fetchPage()
val streams = s.videoStreams ?: return@let emptyList()
ytVideos[url] = streams
streams
}
if (streams.isEmpty()) {
throw ErrorLoadingException("No Youtube streams")
}
streams
//streams.sortedBy { it.height }
// .firstOrNull { !it.isVideoOnly && it.height > 0 }
// ?: throw ErrorLoadingException("No valid Youtube stream")
}
if (streams is Resource.Success) {
return streams.value.mapNotNull {
if (it.isVideoOnly || it.height <= 0) return@mapNotNull null
ExtractorLink(
this.name,
this.name,
it.url ?: return@mapNotNull null,
"",
it.height
)
}
} else {
return null
}
}
}

View File

@ -95,7 +95,7 @@ open class TmdbProvider : MainAPI() {
} }
} }
private fun TvShow.toLoadResponse(): TvSeriesLoadResponse { private suspend fun TvShow.toLoadResponse(): TvSeriesLoadResponse {
val episodes = this.seasons?.filter { !disableSeasonZero || (it.season_number ?: 0) != 0 } val episodes = this.seasons?.filter { !disableSeasonZero || (it.season_number ?: 0) != 0 }
?.mapNotNull { season -> ?.mapNotNull { season ->
season.episodes?.map { episode -> season.episodes?.map { episode ->
@ -167,7 +167,7 @@ open class TmdbProvider : MainAPI() {
} }
} }
private fun Movie.toLoadResponse(): MovieLoadResponse { private suspend fun Movie.toLoadResponse(): MovieLoadResponse {
return newMovieLoadResponse( return newMovieLoadResponse(
this.title ?: this.original_title, getUrl(id, false), TvType.Movie, TmdbLink( this.title ?: this.original_title, getUrl(id, false), TvType.Movie, TmdbLink(
this.imdb_id, this.imdb_id,

View File

@ -1,6 +1,7 @@
package com.lagradost.cloudstream3.movieproviders package com.lagradost.cloudstream3.movieproviders
import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.Qualities import com.lagradost.cloudstream3.utils.Qualities
@ -95,7 +96,7 @@ class NginxProvider : MainAPI() {
this.plot = description this.plot = description
this.rating = ratingAverage this.rating = ratingAverage
this.tags = tagsList this.tags = tagsList
this.trailers = trailer addTrailer(trailer)
addPoster(poster, authHeader) addPoster(poster, authHeader)
} }
} else // a tv serie } else // a tv serie

View File

@ -1,17 +1,11 @@
package com.lagradost.cloudstream3.ui.player package com.lagradost.cloudstream3.ui.player
import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.net.Uri import android.net.Uri
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import android.util.Log import android.util.Log
import android.util.SparseArray
import android.widget.FrameLayout import android.widget.FrameLayout
import androidx.core.util.forEach
import at.huber.youtubeExtractor.VideoMeta
import at.huber.youtubeExtractor.YouTubeExtractor
import at.huber.youtubeExtractor.YtFile
import com.google.android.exoplayer2.* import com.google.android.exoplayer2.*
import com.google.android.exoplayer2.database.StandaloneDatabaseProvider import com.google.android.exoplayer2.database.StandaloneDatabaseProvider
import com.google.android.exoplayer2.ext.okhttp.OkHttpDataSource import com.google.android.exoplayer2.ext.okhttp.OkHttpDataSource
@ -31,7 +25,6 @@ import com.google.android.exoplayer2.upstream.cache.SimpleCache
import com.google.android.exoplayer2.util.MimeTypes import com.google.android.exoplayer2.util.MimeTypes
import com.google.android.exoplayer2.video.VideoSize import com.google.android.exoplayer2.video.VideoSize
import com.lagradost.cloudstream3.APIHolder.getApiFromName import com.lagradost.cloudstream3.APIHolder.getApiFromName
import com.lagradost.cloudstream3.ErrorLoadingException
import com.lagradost.cloudstream3.USER_AGENT import com.lagradost.cloudstream3.USER_AGENT
import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.mvvm.logError
@ -39,7 +32,6 @@ import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
import com.lagradost.cloudstream3.ui.subtitles.SaveCaptionStyle import com.lagradost.cloudstream3.ui.subtitles.SaveCaptionStyle
import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.ExtractorUri import com.lagradost.cloudstream3.utils.ExtractorUri
import com.lagradost.cloudstream3.utils.Qualities
import com.lagradost.cloudstream3.utils.SubtitleHelper.fromTwoLettersToLanguage import com.lagradost.cloudstream3.utils.SubtitleHelper.fromTwoLettersToLanguage
import java.io.File import java.io.File
import javax.net.ssl.HttpsURLConnection import javax.net.ssl.HttpsURLConnection
@ -332,7 +324,6 @@ class CS3IPlayer : IPlayer {
} }
companion object { companion object {
private var ytVideos: MutableMap<String, YtFile> = mutableMapOf()
private var simpleCache: SimpleCache? = null private var simpleCache: SimpleCache? = null
var requestSubtitleUpdate: (() -> Unit)? = null var requestSubtitleUpdate: (() -> Unit)? = null
@ -883,57 +874,9 @@ class CS3IPlayer : IPlayer {
return Pair(subSources, activeSubtitles) return Pair(subSources, activeSubtitles)
} }
fun loadYtFile(context: Context, yt: YtFile) {
loadOnlinePlayer(
context,
ExtractorLink(
"YouTube",
"",
yt.url,
"",
yt.format?.height ?: Qualities.Unknown.value
)
)
}
private fun loadOnlinePlayer(context: Context, link: ExtractorLink) { private fun loadOnlinePlayer(context: Context, link: ExtractorLink) {
Log.i(TAG, "loadOnlinePlayer $link") Log.i(TAG, "loadOnlinePlayer $link")
try { try {
if (link.url.contains("youtube.com")) {
val ytLink = link.url.replace("/embed/", "/watch?v=")
ytVideos[ytLink]?.let {
loadYtFile(context, it)
return
}
val ytExtractor =
@SuppressLint("StaticFieldLeak")
object : YouTubeExtractor(context) {
override fun onExtractionComplete(
ytFiles: SparseArray<YtFile>?,
videoMeta: VideoMeta?
) {
var yt: YtFile? = null
ytFiles?.forEach { _, value ->
if ((yt?.format?.height ?: 0) < (value.format?.height
?: -1) && (value.format?.audioBitrate ?: -1) > 0
) {
yt = value
}
}
yt?.let { ytf ->
ytVideos[ytLink] = ytf
loadYtFile(context, ytf)
} ?: run {
playerError?.invoke(ErrorLoadingException("No Link"))
}
}
}
Log.i(TAG, "YouTube extraction on $ytLink")
ytExtractor.extract(ytLink)
return
}
currentLink = link currentLink = link
if (ignoreSSL) { if (ignoreSSL) {

View File

@ -602,7 +602,7 @@ class ResultFragment : ResultTrailerPlayer() {
setFormatText(result_meta_rating, R.string.rating_format, rating?.div(1000f)) setFormatText(result_meta_rating, R.string.rating_format, rating?.div(1000f))
} }
var currentTrailers: List<String> = emptyList() var currentTrailers: List<ExtractorLink> = emptyList()
var currentTrailerIndex = 0 var currentTrailerIndex = 0
override fun nextMirror() { override fun nextMirror() {
@ -626,13 +626,7 @@ class ResultFragment : ResultTrailerPlayer() {
player.loadPlayer( player.loadPlayer(
ctx, ctx,
false, false,
ExtractorLink( trailer,
"",
"Trailer",
trailer,
"",
Qualities.Unknown.value
),
null, null,
startPosition = 0L, startPosition = 0L,
subtitles = emptySet(), subtitles = emptySet(),
@ -649,14 +643,14 @@ class ResultFragment : ResultTrailerPlayer() {
result_trailer_loading?.isVisible = isSuccess result_trailer_loading?.isVisible = isSuccess
} }
private fun setTrailers(trailers: List<String>?) { private fun setTrailers(trailers: List<ExtractorLink>?) {
context?.let { ctx -> context?.let { ctx ->
if (ctx.isTvSettings()) return if (ctx.isTvSettings()) return
val settingsManager = PreferenceManager.getDefaultSharedPreferences(ctx) val settingsManager = PreferenceManager.getDefaultSharedPreferences(ctx)
val showTrailers = val showTrailers =
settingsManager.getBoolean(ctx.getString(R.string.show_trailers_key), true) settingsManager.getBoolean(ctx.getString(R.string.show_trailers_key), true)
if (!showTrailers) return if (!showTrailers) return
currentTrailers = trailers ?: emptyList() currentTrailers = trailers?.sortedBy { -it.quality } ?: emptyList()
loadTrailer() loadTrailer()
} }
} }
@ -769,7 +763,7 @@ class ResultFragment : ResultTrailerPlayer() {
player_open_source?.setOnClickListener { player_open_source?.setOnClickListener {
currentTrailers.getOrNull(currentTrailerIndex)?.let { currentTrailers.getOrNull(currentTrailerIndex)?.let {
context?.openBrowser(it) context?.openBrowser(it.url)
} }
} }

View File

@ -118,7 +118,7 @@ class ResultViewModel : ViewModel() {
} }
var lastMeta: SyncAPI.SyncResult? = null var lastMeta: SyncAPI.SyncResult? = null
private fun applyMeta(resp: LoadResponse, meta: SyncAPI.SyncResult?): LoadResponse { private suspend fun applyMeta(resp: LoadResponse, meta: SyncAPI.SyncResult?): LoadResponse {
if (meta == null) return resp if (meta == null) return resp
lastMeta = meta lastMeta = meta
return resp.apply { return resp.apply {
@ -145,7 +145,7 @@ class ResultViewModel : ViewModel() {
} }
} }
fun setMeta(meta: SyncAPI.SyncResult) { fun setMeta(meta: SyncAPI.SyncResult) = viewModelScope.launch {
Log.i(TAG, "setMeta") Log.i(TAG, "setMeta")
(result.value as? Resource.Success<LoadResponse>?)?.value?.let { resp -> (result.value as? Resource.Success<LoadResponse>?)?.value?.let { resp ->
_resultResponse.postValue(Resource.Success(applyMeta(resp, meta))) _resultResponse.postValue(Resource.Success(applyMeta(resp, meta)))

View File

@ -106,6 +106,19 @@ suspend fun loadExtractor(
return false return false
} }
suspend fun loadExtractor(
url: String,
referer: String? = null,
): List<ExtractorLink> {
for (extractor in extractorApis) {
if (url.startsWith(extractor.mainUrl)) {
return extractor.getSafeUrl(url, referer) ?: emptyList()
}
}
return emptyList()
}
val extractorApis: Array<ExtractorApi> = arrayOf( val extractorApis: Array<ExtractorApi> = arrayOf(
//AllProvider(), //AllProvider(),
WcoStream(), WcoStream(),
@ -205,6 +218,7 @@ val extractorApis: Array<ExtractorApi> = arrayOf(
KotakAnimeid(), KotakAnimeid(),
Neonime8n(), Neonime8n(),
Neonime7n(), Neonime7n(),
YoutubeExtractor(),
) )
fun getExtractorApiFromName(name: String): ExtractorApi { fun getExtractorApiFromName(name: String): ExtractorApi {