diff --git a/app/build.gradle b/app/build.gradle index 1cd978ee..349ff003 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -74,6 +74,8 @@ android { } } compileOptions { + coreLibraryDesugaringEnabled true + sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } @@ -143,7 +145,7 @@ dependencies { implementation 'com.jaredrummler:colorpicker:1.1.0' //run JS - implementation 'org.mozilla:rhino:1.7R4' + implementation 'org.mozilla:rhino:1.7.14' // TorrentStream //implementation 'com.github.TorrentStream:TorrentStream-Android:2.7.0' @@ -175,6 +177,11 @@ dependencies { // used for subtitle decoding https://github.com/albfernandez/juniversalchardet implementation 'com.github.albfernandez:juniversalchardet:2.4.0' - // play yt - implementation 'com.github.HaarigerHarald:android-youtubeExtractor:master-SNAPSHOT' + // slow af yt + //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' + } \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/DownloaderTestImpl.kt b/app/src/main/java/com/lagradost/cloudstream3/DownloaderTestImpl.kt new file mode 100644 index 00000000..e562ee17 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/DownloaderTestImpl.kt @@ -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> = 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() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt index 5a145d63..823c62b5 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt @@ -19,6 +19,7 @@ import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.malApi import com.lagradost.cloudstream3.ui.player.SubtitleData import com.lagradost.cloudstream3.utils.AppUtils.toJson import com.lagradost.cloudstream3.utils.ExtractorLink +import com.lagradost.cloudstream3.utils.loadExtractor import okhttp3.Interceptor import java.text.SimpleDateFormat import java.util.* @@ -854,7 +855,7 @@ interface LoadResponse { var rating: Int? // 0-10000 var tags: List? var duration: Int? // in minutes - var trailers: List? + var trailers: List? var recommendations: List? var actors: List? var comingSoon: Boolean @@ -897,27 +898,34 @@ interface LoadResponse { addImdbId(imdbUrlToIdNullable(url)) } - /**better to set trailers directly instead of calling this multiple times*/ - fun LoadResponse.addTrailer(trailerUrl: String?) { + /**better to call addTrailer with mutible trailers directly instead of calling this multiple times*/ + suspend fun LoadResponse.addTrailer(trailerUrl: String?, referer: String? = null) { if (trailerUrl == null) return + val newTrailers = loadExtractor(trailerUrl, referer) + addTrailer(newTrailers) + } + + fun LoadResponse.addTrailer(newTrailers: List) { if (this.trailers == null) { - this.trailers = listOf(trailerUrl) + this.trailers = newTrailers } else { - val update = this.trailers?.toMutableList() - update?.add(trailerUrl) + val update = this.trailers?.toMutableList() ?: mutableListOf() + update.addAll(newTrailers) this.trailers = update } } - fun LoadResponse.addTrailer(trailerUrls: List?) { + suspend fun LoadResponse.addTrailer(trailerUrls: List?, referer: String? = null) { if (trailerUrls == null) return - if (this.trailers == null) { - this.trailers = trailerUrls - } else { - val update = this.trailers?.toMutableList() - update?.addAll(trailerUrls) - this.trailers = update - } + val newTrailers = trailerUrls.apmap { trailerUrl -> + try { + loadExtractor(trailerUrl, referer) + } catch (e: Exception) { + logError(e) + emptyList() + } + }.flatten().distinct() + addTrailer(newTrailers) } fun LoadResponse.addImdbId(id: String?) { @@ -997,7 +1005,7 @@ data class TorrentLoadResponse( override var rating: Int? = null, override var tags: List? = null, override var duration: Int? = null, - override var trailers: List? = null, + override var trailers: List? = null, override var recommendations: List? = null, override var actors: List? = null, override var comingSoon: Boolean = false, @@ -1025,7 +1033,7 @@ data class AnimeLoadResponse( override var rating: Int? = null, override var duration: Int? = null, - override var trailers: List? = null, + override var trailers: List? = null, override var recommendations: List? = null, override var actors: List? = null, override var comingSoon: Boolean = false, @@ -1038,12 +1046,12 @@ fun AnimeLoadResponse.addEpisodes(status: DubStatus, episodes: List?) { this.episodes[status] = episodes } -fun MainAPI.newAnimeLoadResponse( +suspend fun MainAPI.newAnimeLoadResponse( name: String, url: String, type: TvType, comingSoonIfNone: Boolean = true, - initializer: AnimeLoadResponse.() -> Unit = { }, + initializer: suspend AnimeLoadResponse.() -> Unit = { }, ): AnimeLoadResponse { val builder = AnimeLoadResponse(name = name, url = url, apiName = this.name, type = type) builder.initializer() @@ -1072,7 +1080,7 @@ data class MovieLoadResponse( override var rating: Int? = null, override var tags: List? = null, override var duration: Int? = null, - override var trailers: List? = null, + override var trailers: List? = null, override var recommendations: List? = null, override var actors: List? = null, override var comingSoon: Boolean = false, @@ -1080,12 +1088,12 @@ data class MovieLoadResponse( override var posterHeaders: Map? = null, ) : LoadResponse -fun MainAPI.newMovieLoadResponse( +suspend fun MainAPI.newMovieLoadResponse( name: String, url: String, type: TvType, data: T?, - initializer: MovieLoadResponse.() -> Unit = { } + initializer: suspend MovieLoadResponse.() -> Unit = { } ): MovieLoadResponse { // just in case if (data is String) return newMovieLoadResponse( @@ -1108,12 +1116,12 @@ fun MainAPI.newMovieLoadResponse( return builder } -fun MainAPI.newMovieLoadResponse( +suspend fun MainAPI.newMovieLoadResponse( name: String, url: String, type: TvType, dataUrl: String, - initializer: MovieLoadResponse.() -> Unit = { } + initializer: suspend MovieLoadResponse.() -> Unit = { } ): MovieLoadResponse { val builder = MovieLoadResponse( name = name, @@ -1193,7 +1201,7 @@ data class TvSeriesLoadResponse( override var rating: Int? = null, override var tags: List? = null, override var duration: Int? = null, - override var trailers: List? = null, + override var trailers: List? = null, override var recommendations: List? = null, override var actors: List? = null, override var comingSoon: Boolean = false, @@ -1201,12 +1209,12 @@ data class TvSeriesLoadResponse( override var posterHeaders: Map? = null, ) : LoadResponse -fun MainAPI.newTvSeriesLoadResponse( +suspend fun MainAPI.newTvSeriesLoadResponse( name: String, url: String, type: TvType, episodes: List, - initializer: TvSeriesLoadResponse.() -> Unit = { } + initializer: suspend TvSeriesLoadResponse.() -> Unit = { } ): TvSeriesLoadResponse { val builder = TvSeriesLoadResponse( name = name, diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt index 3216b1f3..fc270d35 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt @@ -76,6 +76,7 @@ import kotlinx.android.synthetic.main.fragment_result_swipe.* import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext +import org.schabi.newpipe.extractor.NewPipe import java.io.File import kotlin.concurrent.thread @@ -366,9 +367,23 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { } 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 { @@ -598,6 +613,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { loadCache() test() + NewPipe.init(DownloaderTestImpl.getInstance()) /*nav_view.setOnNavigationItemSelectedListener { item -> when (item.itemId) { R.id.navigation_home -> { diff --git a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/KuronimeProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/KuronimeProvider.kt index d3203810..5eec3a07 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/KuronimeProvider.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/KuronimeProvider.kt @@ -3,10 +3,11 @@ package com.lagradost.cloudstream3.animeproviders import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer 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.nodes.Element -import java.util.ArrayList class KuronimeProvider : MainAPI() { override var mainUrl = "https://185.231.223.254" @@ -139,7 +140,6 @@ class KuronimeProvider : MainAPI() { plot = description addTrailer(trailer) this.tags = tags - trailers = listOf(trailer) } } diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/YoutubeExtractor.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/YoutubeExtractor.kt new file mode 100644 index 00000000..8539b3fb --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/YoutubeExtractor.kt @@ -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> = mutableMapOf() + } + + override fun getExtractorUrl(id: String): String { + return "https://www.youtube.com/watch?v=$id" + } + + override suspend fun getUrl(url: String, referer: String?): List? { + 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 + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/metaproviders/TmdbProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/metaproviders/TmdbProvider.kt index fdec2fc1..13cbd335 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/metaproviders/TmdbProvider.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/metaproviders/TmdbProvider.kt @@ -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 } ?.mapNotNull { season -> 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( this.title ?: this.original_title, getUrl(id, false), TvType.Movie, TmdbLink( this.imdb_id, diff --git a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/NginxProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/NginxProvider.kt index b9472f60..4711d9cb 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/NginxProvider.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/NginxProvider.kt @@ -1,6 +1,7 @@ package com.lagradost.cloudstream3.movieproviders import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.Qualities @@ -95,7 +96,7 @@ class NginxProvider : MainAPI() { this.plot = description this.rating = ratingAverage this.tags = tagsList - this.trailers = trailer + addTrailer(trailer) addPoster(poster, authHeader) } } else // a tv serie diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt index 96fb8def..9a70da17 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt @@ -1,17 +1,11 @@ package com.lagradost.cloudstream3.ui.player -import android.annotation.SuppressLint import android.content.Context import android.net.Uri import android.os.Handler import android.os.Looper import android.util.Log -import android.util.SparseArray 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.database.StandaloneDatabaseProvider 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.video.VideoSize import com.lagradost.cloudstream3.APIHolder.getApiFromName -import com.lagradost.cloudstream3.ErrorLoadingException import com.lagradost.cloudstream3.USER_AGENT import com.lagradost.cloudstream3.app 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.utils.ExtractorLink import com.lagradost.cloudstream3.utils.ExtractorUri -import com.lagradost.cloudstream3.utils.Qualities import com.lagradost.cloudstream3.utils.SubtitleHelper.fromTwoLettersToLanguage import java.io.File import javax.net.ssl.HttpsURLConnection @@ -332,7 +324,6 @@ class CS3IPlayer : IPlayer { } companion object { - private var ytVideos: MutableMap = mutableMapOf() private var simpleCache: SimpleCache? = null var requestSubtitleUpdate: (() -> Unit)? = null @@ -883,57 +874,9 @@ class CS3IPlayer : IPlayer { 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) { Log.i(TAG, "loadOnlinePlayer $link") 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?, - 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 if (ignoreSSL) { diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt index 7ac6d149..fb5f940b 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt @@ -602,7 +602,7 @@ class ResultFragment : ResultTrailerPlayer() { setFormatText(result_meta_rating, R.string.rating_format, rating?.div(1000f)) } - var currentTrailers: List = emptyList() + var currentTrailers: List = emptyList() var currentTrailerIndex = 0 override fun nextMirror() { @@ -626,13 +626,7 @@ class ResultFragment : ResultTrailerPlayer() { player.loadPlayer( ctx, false, - ExtractorLink( - "", - "Trailer", - trailer, - "", - Qualities.Unknown.value - ), + trailer, null, startPosition = 0L, subtitles = emptySet(), @@ -649,14 +643,14 @@ class ResultFragment : ResultTrailerPlayer() { result_trailer_loading?.isVisible = isSuccess } - private fun setTrailers(trailers: List?) { + private fun setTrailers(trailers: List?) { context?.let { ctx -> if (ctx.isTvSettings()) return val settingsManager = PreferenceManager.getDefaultSharedPreferences(ctx) val showTrailers = settingsManager.getBoolean(ctx.getString(R.string.show_trailers_key), true) if (!showTrailers) return - currentTrailers = trailers ?: emptyList() + currentTrailers = trailers?.sortedBy { -it.quality } ?: emptyList() loadTrailer() } } @@ -769,7 +763,7 @@ class ResultFragment : ResultTrailerPlayer() { player_open_source?.setOnClickListener { currentTrailers.getOrNull(currentTrailerIndex)?.let { - context?.openBrowser(it) + context?.openBrowser(it.url) } } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel.kt index 633e32a4..934be97f 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel.kt @@ -118,7 +118,7 @@ class ResultViewModel : ViewModel() { } 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 lastMeta = meta 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") (result.value as? Resource.Success?)?.value?.let { resp -> _resultResponse.postValue(Resource.Success(applyMeta(resp, meta))) diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt index 65c3c2c7..bccc9a21 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt @@ -106,6 +106,19 @@ suspend fun loadExtractor( return false } +suspend fun loadExtractor( + url: String, + referer: String? = null, +): List { + for (extractor in extractorApis) { + if (url.startsWith(extractor.mainUrl)) { + return extractor.getSafeUrl(url, referer) ?: emptyList() + + } + } + return emptyList() +} + val extractorApis: Array = arrayOf( //AllProvider(), WcoStream(), @@ -205,6 +218,7 @@ val extractorApis: Array = arrayOf( KotakAnimeid(), Neonime8n(), Neonime7n(), + YoutubeExtractor(), ) fun getExtractorApiFromName(name: String): ExtractorApi {