This commit is contained in:
LagradOst 2021-05-20 17:22:28 +02:00
parent 68605fb563
commit c5364ecd9a
14 changed files with 92 additions and 35 deletions

View file

@ -6,6 +6,7 @@ import com.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.databind.json.JsonMapper import com.fasterxml.jackson.databind.json.JsonMapper
import com.fasterxml.jackson.module.kotlin.KotlinModule import com.fasterxml.jackson.module.kotlin.KotlinModule
import com.lagradost.cloudstream3.animeproviders.ShiroProvider import com.lagradost.cloudstream3.animeproviders.ShiroProvider
import com.lagradost.cloudstream3.utils.ExtractorLink
import java.util.* import java.util.*
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
@ -51,7 +52,8 @@ abstract class MainAPI {
return null return null
} }
open fun loadLinks(data: Any, id: Int): Boolean { // callback is fired once a link is found, will return true if method is executed successfully
open fun loadLinks(data: Any, isCasting : Boolean, callback: (ExtractorLink) -> Unit): Boolean {
return false return false
} }
} }

View file

@ -1,5 +1,7 @@
package com.lagradost.cloudstream3 package com.lagradost.cloudstream3
import kotlinx.coroutines.async
import kotlinx.coroutines.runBlocking
import java.util.* import java.util.*
import java.util.concurrent.ExecutorService import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors import java.util.concurrent.Executors
@ -26,3 +28,7 @@ fun <T, R> Iterable<T>.pmap(
return ArrayList<R>(destination) return ArrayList<R>(destination)
} }
fun <A, B>List<A>.apmap(f: suspend (A) -> B): List<B> = runBlocking {
map { async { f(it) } }.map { it.await() }
}

View file

@ -3,6 +3,8 @@ package com.lagradost.cloudstream3.animeproviders
import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.annotation.JsonProperty
import com.fasterxml.jackson.module.kotlin.readValue import com.fasterxml.jackson.module.kotlin.readValue
import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.extractors.Vidstream
import java.net.URLEncoder import java.net.URLEncoder
import java.util.* import java.util.*
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
@ -153,7 +155,7 @@ class ShiroProvider : MainAPI() {
val episodeCount = i.episodeCount.toInt() val episodeCount = i.episodeCount.toInt()
returnValue.add(AnimeSearchResponse( returnValue.add(AnimeSearchResponse(
i.name.replace("Dubbed",""), // i.english ?: i.canonicalTitle, i.name.replace("Dubbed", ""), // i.english ?: i.canonicalTitle,
"$mainUrl/${i.slug}", "$mainUrl/${i.slug}",
i.slug, i.slug,
this.name, this.name,
@ -202,4 +204,13 @@ class ShiroProvider : MainAPI() {
null, null,
) )
} }
override fun loadLinks(data: Any, isCasting: Boolean, callback: (ExtractorLink) -> Unit): Boolean {
if (data is ShiroEpisodes) {
return Vidstream().getUrl(data._id, isCasting) {
callback.invoke(it)
}
}
return false
}
} }

View file

@ -10,24 +10,24 @@ import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.APIHolder.getApiFromName import com.lagradost.cloudstream3.APIHolder.getApiFromName
import com.lagradost.cloudstream3.utils.ExtractorLink
import kotlinx.android.synthetic.main.result_episode.view.* import kotlinx.android.synthetic.main.result_episode.view.*
class EpisodeAdapter( class EpisodeAdapter(
activity: Activity, private var activity: Activity,
animeList: ArrayList<ResultEpisode>, var cardList: ArrayList<ResultEpisode>,
resView: RecyclerView, val resView: RecyclerView,
val clickCallback: (ResultEpisode) -> Unit
) : ) :
RecyclerView.Adapter<RecyclerView.ViewHolder>() { RecyclerView.Adapter<RecyclerView.ViewHolder>() {
var cardList = animeList
private var activity: Activity = activity
var resView: RecyclerView? = resView
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return CardViewHolder( return CardViewHolder(
LayoutInflater.from(parent.context).inflate(R.layout.result_episode, parent, false), LayoutInflater.from(parent.context).inflate(R.layout.result_episode, parent, false),
activity, activity,
resView!! resView,
clickCallback
) )
} }
@ -44,13 +44,14 @@ class EpisodeAdapter(
} }
class CardViewHolder class CardViewHolder
constructor(itemView: View, _activity: Activity, resView: RecyclerView) : RecyclerView.ViewHolder(itemView) { constructor(itemView: View, _activity: Activity, resView: RecyclerView, clickCallback: (ResultEpisode) -> Unit) : RecyclerView.ViewHolder(itemView) {
val activity = _activity val activity = _activity
val episode_view_procentage: View = itemView.episode_view_procentage val episode_view_procentage: View = itemView.episode_view_procentage
val episode_view_procentage_off: View = itemView.episode_view_procentage_off val episode_view_procentage_off: View = itemView.episode_view_procentage_off
val episode_text: TextView = itemView.episode_text val episode_text: TextView = itemView.episode_text
val episode_extra: ImageView = itemView.episode_extra val episode_extra: ImageView = itemView.episode_extra
val episode_play: ImageView = itemView.episode_play val episode_play: ImageView = itemView.episode_play
val clickCallback = clickCallback
fun bind(card: ResultEpisode) { fun bind(card: ResultEpisode) {
episode_text.text = card.name ?: "Episode ${card.episode}" episode_text.text = card.name ?: "Episode ${card.episode}"
@ -67,7 +68,7 @@ class EpisodeAdapter(
setWidth(episode_view_procentage_off, 1 - card.watchProgress) setWidth(episode_view_procentage_off, 1 - card.watchProgress)
episode_play.setOnClickListener { episode_play.setOnClickListener {
getApiFromName(card.apiName).loadLinks(card.data, card.id) clickCallback.invoke(card)
} }
} }
} }

View file

@ -19,6 +19,7 @@ import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.bumptech.glide.load.model.GlideUrl import com.bumptech.glide.load.model.GlideUrl
import com.bumptech.glide.request.RequestOptions.bitmapTransform import com.bumptech.glide.request.RequestOptions.bitmapTransform
import com.lagradost.cloudstream3.APIHolder.getApiFromName
import com.lagradost.cloudstream3.AnimeLoadResponse import com.lagradost.cloudstream3.AnimeLoadResponse
import com.lagradost.cloudstream3.LoadResponse import com.lagradost.cloudstream3.LoadResponse
import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.R
@ -26,6 +27,8 @@ import com.lagradost.cloudstream3.UIHelper.fixPaddingStatusbar
import com.lagradost.cloudstream3.UIHelper.getStatusBarHeight import com.lagradost.cloudstream3.UIHelper.getStatusBarHeight
import com.lagradost.cloudstream3.mvvm.Resource import com.lagradost.cloudstream3.mvvm.Resource
import com.lagradost.cloudstream3.mvvm.observe import com.lagradost.cloudstream3.mvvm.observe
import com.lagradost.cloudstream3.mvvm.safeApiCall
import com.lagradost.cloudstream3.utils.ExtractorLink
import jp.wasabeef.glide.transformations.BlurTransformation import jp.wasabeef.glide.transformations.BlurTransformation
import kotlinx.android.synthetic.main.fragment_result.* import kotlinx.android.synthetic.main.fragment_result.*
import kotlinx.android.synthetic.main.fragment_search.* import kotlinx.android.synthetic.main.fragment_search.*
@ -77,10 +80,10 @@ class ResultFragment : Fragment() {
val apiName = arguments?.getString("apiName") val apiName = arguments?.getString("apiName")
result_scroll.setOnScrollChangeListener(NestedScrollView.OnScrollChangeListener { _, _, scrollY, _, oldScrollY -> result_scroll.setOnScrollChangeListener(NestedScrollView.OnScrollChangeListener { _, _, scrollY, _, oldScrollY ->
if(result_poster_blur == null) return@OnScrollChangeListener if (result_poster_blur == null) return@OnScrollChangeListener
result_poster_blur.alpha = maxOf(0f, (0.3f - scrollY / 1000f)) result_poster_blur.alpha = maxOf(0f, (0.3f - scrollY / 1000f))
result_barstatus.alpha = scrollY / 200f result_barstatus.alpha = scrollY / 200f
result_barstatus.visibility = if(scrollY > 0) View.VISIBLE else View.GONE result_barstatus.visibility = if (scrollY > 0) View.VISIBLE else View.GONE
}) })
result_toolbar.setNavigationIcon(R.drawable.ic_baseline_arrow_back_24) result_toolbar.setNavigationIcon(R.drawable.ic_baseline_arrow_back_24)
@ -123,7 +126,9 @@ class ResultFragment : Fragment() {
it, it,
ArrayList(), ArrayList(),
result_episodes, result_episodes,
) ) {
viewModel.loadEpisode(it)
}
} }
result_episodes.adapter = adapter result_episodes.adapter = adapter

View file

@ -9,17 +9,34 @@ import com.lagradost.cloudstream3.APIHolder.getApiFromName
import com.lagradost.cloudstream3.MainAPI import com.lagradost.cloudstream3.MainAPI
import com.lagradost.cloudstream3.mvvm.Resource import com.lagradost.cloudstream3.mvvm.Resource
import com.lagradost.cloudstream3.mvvm.safeApiCall import com.lagradost.cloudstream3.mvvm.safeApiCall
import com.lagradost.cloudstream3.utils.ExtractorLink
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
class ResultViewModel : ViewModel() { class ResultViewModel : ViewModel() {
private val _resultResponse: MutableLiveData<Resource<Any?>> = MutableLiveData() private val _resultResponse: MutableLiveData<Resource<Any?>> = MutableLiveData()
val resultResponse: LiveData<Resource<Any?>> get() = _resultResponse val resultResponse: LiveData<Resource<Any?>> get() = _resultResponse
fun load(url: String, apiName:String) = viewModelScope.launch { fun load(url: String, apiName: String) = viewModelScope.launch {
val data = safeApiCall { val data = safeApiCall {
getApiFromName(apiName).load(url) getApiFromName(apiName).load(url)
} }
_resultResponse.postValue(data) _resultResponse.postValue(data)
} }
val allEpisodes : MutableLiveData<HashMap<Int,ArrayList<ExtractorLink>>> = MutableLiveData()
private val _episodeResponse: MutableLiveData<Resource<Boolean>> = MutableLiveData()
val episodeResponse: LiveData<Resource<Boolean>> get() = _episodeResponse
fun loadEpisode(episode: ResultEpisode) = viewModelScope.launch {
if(allEpisodes.value?.contains(episode.id) == true) {
allEpisodes.value?.remove(episode.id)
}
val data = safeApiCall {
getApiFromName(episode.apiName).loadLinks(episode.data, false) { //TODO IMPLEMENT CASTING
allEpisodes.value?.get(episode.id)?.add(it)
}
}
_episodeResponse.postValue(data)
}
} }

View file

@ -7,6 +7,7 @@ import com.lagradost.cloudstream3.cloudstream3.extractors.StreamTape
import com.lagradost.cloudstream3.utils.extractors.XStreamCdn import com.lagradost.cloudstream3.utils.extractors.XStreamCdn
data class ExtractorLink( data class ExtractorLink(
val source: String,
val name: String, val name: String,
val url: String, val url: String,
val referer: String, val referer: String,
@ -32,7 +33,7 @@ fun getAndUnpack(string: String): String? {
return JsUnpacker(packedText).unpack() return JsUnpacker(packedText).unpack()
} }
val APIS: Array<ExtractorApi> = arrayOf( val extractorApis: Array<ExtractorApi> = arrayOf(
//AllProvider(), //AllProvider(),
Shiro(), Shiro(),
Mp4Upload(), Mp4Upload(),
@ -41,6 +42,17 @@ val APIS: Array<ExtractorApi> = arrayOf(
XStreamCdn() XStreamCdn()
) )
fun getExtractorApiFromName(name: String): ExtractorApi {
for (api in extractorApis) {
if (api.name == name) return api
}
return extractorApis[0]
}
fun requireReferer(name: String): Boolean {
return getExtractorApiFromName(name).requiresReferer
}
fun httpsify(url: String): String { fun httpsify(url: String): String {
return if (url.startsWith("//")) "https:$url" else url return if (url.startsWith("//")) "https:$url" else url
} }
@ -52,7 +64,7 @@ abstract class ExtractorApi {
abstract fun getUrl(url: String, referer: String? = null): List<ExtractorLink>? abstract fun getUrl(url: String, referer: String? = null): List<ExtractorLink>?
open fun getExtractorUrl(id: String): String{ open fun getExtractorUrl(id: String): String {
return id return id
} }
} }

View file

@ -19,6 +19,7 @@ class MixDrop : ExtractorApi() {
srcRegex.find(unpackedText)?.groupValues?.get(1)?.let { link -> srcRegex.find(unpackedText)?.groupValues?.get(1)?.let { link ->
return listOf( return listOf(
ExtractorLink( ExtractorLink(
name,
name, name,
httpsify(link), httpsify(link),
url, url,

View file

@ -15,6 +15,7 @@ class Mp4Upload : ExtractorApi() {
srcRegex.find(unpackedText)?.groupValues?.get(1)?.let { link -> srcRegex.find(unpackedText)?.groupValues?.get(1)?.let { link ->
return listOf( return listOf(
ExtractorLink( ExtractorLink(
name,
name, name,
link, link,
url, url,

View file

@ -38,6 +38,7 @@ class MultiQuality : ExtractorApi() {
m3u8Regex.findAll(this.text).forEach { match -> m3u8Regex.findAll(this.text).forEach { match ->
extractedLinksList.add( extractedLinksList.add(
ExtractorLink( ExtractorLink(
name,
"$name ${match.groupValues[1]}p", "$name ${match.groupValues[1]}p",
urlRegex.find(this.url)!!.groupValues[1] + match.groupValues[0], urlRegex.find(this.url)!!.groupValues[1] + match.groupValues[0],
url, url,
@ -51,6 +52,7 @@ class MultiQuality : ExtractorApi() {
} else if (extractedUrl.endsWith(".mp4")) { } else if (extractedUrl.endsWith(".mp4")) {
extractedLinksList.add( extractedLinksList.add(
ExtractorLink( ExtractorLink(
name,
"$name ${sourceMatch.groupValues[2]}", "$name ${sourceMatch.groupValues[2]}",
extractedUrl, extractedUrl,
url.replace(" ", "%20"), url.replace(" ", "%20"),

View file

@ -20,6 +20,7 @@ class Shiro : ExtractorApi() {
Jsoup.parse(res).select("source").firstOrNull()?.attr("src")?.replace("&amp;", "?")?.let { Jsoup.parse(res).select("source").firstOrNull()?.attr("src")?.replace("&amp;", "?")?.let {
return listOf( return listOf(
ExtractorLink( ExtractorLink(
name,
name, name,
it.replace(" ", "%20"), it.replace(" ", "%20"),
"https://cherry.subsplea.se/", "https://cherry.subsplea.se/",

View file

@ -20,6 +20,7 @@ class StreamTape : ExtractorApi() {
val extractedUrl = "https://streamtape.com/get_video?${it.groupValues[1]}".replace("""" + '""", "") val extractedUrl = "https://streamtape.com/get_video?${it.groupValues[1]}".replace("""" + '""", "")
return listOf( return listOf(
ExtractorLink( ExtractorLink(
name,
name, name,
extractedUrl, extractedUrl,
url, url,

View file

@ -1,9 +1,8 @@
package com.lagradost.cloudstream3.utils.extractors package com.lagradost.cloudstream3.utils.extractors
import com.lagradost.cloudstream3.pmap
import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.APIS import com.lagradost.cloudstream3.utils.extractorApis
import com.lagradost.cloudstream3.utils.extractors.MultiQuality
import com.lagradost.cloudstream3.utils.extractors.Shiro
import org.jsoup.Jsoup import org.jsoup.Jsoup
class Vidstream { class Vidstream {
@ -13,9 +12,10 @@ class Vidstream {
private fun getExtractorUrl(id: String): String { private fun getExtractorUrl(id: String): String {
return "$mainUrl/streaming.php?id=$id" return "$mainUrl/streaming.php?id=$id"
} }
private val normalApis = arrayListOf(Shiro(), MultiQuality())
// https://gogo-stream.com/streaming.php?id=MTE3NDg5 // https://gogo-stream.com/streaming.php?id=MTE3NDg5
fun getUrl(id: String, isCasting: Boolean = false): List<ExtractorLink> { fun getUrl(id: String, isCasting: Boolean = false, callback: (ExtractorLink) -> Unit) : Boolean {
try { try {
val url = getExtractorUrl(id) val url = getExtractorUrl(id)
with(khttp.get(url)) { with(khttp.get(url)) {
@ -23,15 +23,11 @@ class Vidstream {
val primaryLinks = document.select("ul.list-server-items > li.linkserver") val primaryLinks = document.select("ul.list-server-items > li.linkserver")
val extractedLinksList: MutableList<ExtractorLink> = mutableListOf() val extractedLinksList: MutableList<ExtractorLink> = mutableListOf()
// --- Shiro --- normalApis.pmap { api ->
val shiroUrl = Shiro().getExtractorUrl(id) val url = api.getExtractorUrl(id)
val shiroSource = Shiro().getUrl(shiroUrl) val source = api.getUrl(url)
shiroSource?.forEach { extractedLinksList.add(it) } source?.forEach { callback.invoke(it) }
// --- MultiQuality --- }
val multiQualityUrl = MultiQuality().getExtractorUrl(id)
val multiQualitySource = MultiQuality().getUrl(multiQualityUrl)
multiQualitySource?.forEach { extractedLinksList.add(it) }
// --------------------
// All vidstream links passed to extractors // All vidstream links passed to extractors
primaryLinks.forEach { element -> primaryLinks.forEach { element ->
@ -39,21 +35,21 @@ class Vidstream {
//val name = element.text() //val name = element.text()
// Matches vidstream links with extractors // Matches vidstream links with extractors
APIS.filter { !it.requiresReferer || !isCasting}.forEach { api -> extractorApis.filter { !it.requiresReferer || !isCasting}.pmap { api ->
if (link.startsWith(api.mainUrl)) { if (link.startsWith(api.mainUrl)) {
val extractedLinks = api.getUrl(link, url) val extractedLinks = api.getUrl(link, url)
if (extractedLinks?.isNotEmpty() == true) { if (extractedLinks?.isNotEmpty() == true) {
extractedLinks.forEach { extractedLinks.forEach {
extractedLinksList.add(it) callback.invoke(it)
} }
} }
} }
} }
} }
return extractedLinksList return true
} }
} catch (e: Exception) { } catch (e: Exception) {
return listOf() return false
} }
} }
} }

View file

@ -51,6 +51,7 @@ class XStreamCdn : ExtractorApi() {
it.data.forEach { data -> it.data.forEach { data ->
extractedLinksList.add( extractedLinksList.add(
ExtractorLink( ExtractorLink(
name,
"$name ${data.label}", "$name ${data.label}",
data.file, data.file,
url, url,