mirror of
https://github.com/recloudstream/cloudstream.git
synced 2024-08-15 01:53:11 +00:00
quick search
This commit is contained in:
parent
850be93a8b
commit
9e434030a4
8 changed files with 179 additions and 42 deletions
|
@ -1,6 +1,8 @@
|
||||||
package com.lagradost.cloudstream3
|
package com.lagradost.cloudstream3
|
||||||
|
|
||||||
import com.lagradost.cloudstream3.APIHolder.apis
|
import com.lagradost.cloudstream3.APIHolder.apis
|
||||||
|
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
|
||||||
|
import com.lagradost.cloudstream3.mvvm.safeApiCall
|
||||||
|
|
||||||
class AllProvider : MainAPI() {
|
class AllProvider : MainAPI() {
|
||||||
override val name: String
|
override val name: String
|
||||||
|
@ -8,12 +10,52 @@ class AllProvider : MainAPI() {
|
||||||
|
|
||||||
var providersActive = HashSet<String>()
|
var providersActive = HashSet<String>()
|
||||||
|
|
||||||
|
override fun quickSearch(query: String): ArrayList<Any>? {
|
||||||
|
val list = apis.filter { a ->
|
||||||
|
a.name != this.name && (providersActive.size == 0 || providersActive.contains(a.name))
|
||||||
|
}.filter { a -> a.hasQuickSearch }.pmap { a ->
|
||||||
|
normalSafeApiCall {
|
||||||
|
a.quickSearch(query)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var maxCount = 0
|
||||||
|
var providerCount = 0
|
||||||
|
for (res in list) {
|
||||||
|
if (res != null) {
|
||||||
|
if (res.size > maxCount) {
|
||||||
|
maxCount = res.size
|
||||||
|
}
|
||||||
|
providerCount++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
println("PROV: " + providerCount + "|" + maxCount)
|
||||||
|
if (providerCount == 0) return null
|
||||||
|
if (maxCount == 0) return ArrayList()
|
||||||
|
|
||||||
|
val result = ArrayList<Any>()
|
||||||
|
for (i in 0..maxCount) {
|
||||||
|
for (res in list) {
|
||||||
|
if (res != null) {
|
||||||
|
if (i < res.size) {
|
||||||
|
result.add(res[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
override fun search(query: String): ArrayList<Any>? {
|
override fun search(query: String): ArrayList<Any>? {
|
||||||
val list = apis.filter { a ->
|
val list = apis.filter { a ->
|
||||||
a.name != this.name && (providersActive.size == 0 || providersActive.contains(a.name))
|
a.name != this.name && (providersActive.size == 0 || providersActive.contains(a.name))
|
||||||
}.pmap { a ->
|
}.pmap { a ->
|
||||||
|
normalSafeApiCall {
|
||||||
a.search(query)
|
a.search(query)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var maxCount = 0
|
var maxCount = 0
|
||||||
var providerCount = 0
|
var providerCount = 0
|
||||||
|
|
|
@ -17,6 +17,9 @@ val mapper = JsonMapper.builder().addModule(KotlinModule())
|
||||||
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false).build()!!
|
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false).build()!!
|
||||||
|
|
||||||
object APIHolder {
|
object APIHolder {
|
||||||
|
val unixTime: Long
|
||||||
|
get() = System.currentTimeMillis() / 1000L
|
||||||
|
|
||||||
val allApi = AllProvider()
|
val allApi = AllProvider()
|
||||||
|
|
||||||
private const val defProvider = 0
|
private const val defProvider = 0
|
||||||
|
@ -47,10 +50,15 @@ abstract class MainAPI {
|
||||||
open val name = "NONE"
|
open val name = "NONE"
|
||||||
open val mainUrl = "NONE"
|
open val mainUrl = "NONE"
|
||||||
open val instantLinkLoading = false // THIS IS IF THE LINK IS STORED IN THE "DATA"
|
open val instantLinkLoading = false // THIS IS IF THE LINK IS STORED IN THE "DATA"
|
||||||
|
open val hasQuickSearch = false
|
||||||
open fun search(query: String): ArrayList<Any>? { // SearchResponse
|
open fun search(query: String): ArrayList<Any>? { // SearchResponse
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
open fun quickSearch(query: String) : ArrayList<Any>? {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
open fun load(slug: String): Any? { //LoadResponse
|
open fun load(slug: String): Any? { //LoadResponse
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
@ -74,13 +82,6 @@ fun sortUrls(urls: List<ExtractorLink>): List<ExtractorLink> {
|
||||||
return urls.sortedBy { t -> -t.quality }
|
return urls.sortedBy { t -> -t.quality }
|
||||||
}
|
}
|
||||||
|
|
||||||
data class Link(
|
|
||||||
val name: String,
|
|
||||||
val url: String,
|
|
||||||
val quality: Int?,
|
|
||||||
val referer: String?,
|
|
||||||
)
|
|
||||||
|
|
||||||
enum class ShowStatus {
|
enum class ShowStatus {
|
||||||
Completed,
|
Completed,
|
||||||
Ongoing,
|
Ongoing,
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
package com.lagradost.cloudstream3.animeproviders
|
||||||
|
|
||||||
|
import com.lagradost.cloudstream3.APIHolder.unixTime
|
||||||
|
import com.lagradost.cloudstream3.MainAPI
|
||||||
|
import com.lagradost.cloudstream3.mapper
|
||||||
|
|
||||||
|
class DubbedAnimeProvider : MainAPI() {
|
||||||
|
override val mainUrl: String
|
||||||
|
get() = "https://bestdubbedanime.com"
|
||||||
|
override val name: String
|
||||||
|
get() = "DubbedAnime"
|
||||||
|
override val hasQuickSearch: Boolean
|
||||||
|
get() = true
|
||||||
|
|
||||||
|
override fun quickSearch(query: String): ArrayList<Any>? {
|
||||||
|
val url = "$mainUrl/xz/searchgrid.php?p=1&limit=12&s=$query&_=${unixTime}"
|
||||||
|
val response = khttp.get(url)
|
||||||
|
|
||||||
|
return super.quickSearch(query)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
override fun search(query: String): ArrayList<Any>? {
|
||||||
|
val url = "$mainUrl/search/$query"
|
||||||
|
|
||||||
|
mapper.readValue<>()
|
||||||
|
|
||||||
|
return super.search(query)
|
||||||
|
}*/
|
||||||
|
}
|
|
@ -14,7 +14,7 @@ class ShiroProvider : MainAPI() {
|
||||||
companion object {
|
companion object {
|
||||||
var token: String? = null
|
var token: String? = null
|
||||||
|
|
||||||
fun getType(t: String): TvType {
|
fun getType(t: String?): TvType {
|
||||||
return when (t) {
|
return when (t) {
|
||||||
"TV" -> TvType.Anime
|
"TV" -> TvType.Anime
|
||||||
"OVA" -> TvType.ONA
|
"OVA" -> TvType.ONA
|
||||||
|
@ -52,14 +52,17 @@ class ShiroProvider : MainAPI() {
|
||||||
override val name: String
|
override val name: String
|
||||||
get() = "Shiro"
|
get() = "Shiro"
|
||||||
|
|
||||||
|
override val hasQuickSearch: Boolean
|
||||||
|
get() = true
|
||||||
|
|
||||||
data class ShiroSearchResponseShow(
|
data class ShiroSearchResponseShow(
|
||||||
@JsonProperty("image") val image: String,
|
@JsonProperty("image") val image: String,
|
||||||
@JsonProperty("_id") val _id: String,
|
@JsonProperty("_id") val _id: String,
|
||||||
@JsonProperty("slug") val slug: String,
|
@JsonProperty("slug") val slug: String,
|
||||||
@JsonProperty("name") val name: String,
|
@JsonProperty("name") val name: String,
|
||||||
@JsonProperty("episodeCount") val episodeCount: String,
|
@JsonProperty("episodeCount") val episodeCount: String?,
|
||||||
@JsonProperty("language") val language: String,
|
@JsonProperty("language") val language: String?,
|
||||||
@JsonProperty("type") val type: String,
|
@JsonProperty("type") val type: String?,
|
||||||
@JsonProperty("year") val year: String?,
|
@JsonProperty("year") val year: String?,
|
||||||
@JsonProperty("canonicalTitle") val canonicalTitle: String,
|
@JsonProperty("canonicalTitle") val canonicalTitle: String,
|
||||||
@JsonProperty("english") val english: String?,
|
@JsonProperty("english") val english: String?,
|
||||||
|
@ -131,6 +134,56 @@ class ShiroProvider : MainAPI() {
|
||||||
@JsonProperty("status") val status: String,
|
@JsonProperty("status") val status: String,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
private fun turnSearchIntoResponse(data: ShiroSearchResponseShow): AnimeSearchResponse {
|
||||||
|
val type = getType(data.type)
|
||||||
|
val isDubbed =
|
||||||
|
if (data.language != null)
|
||||||
|
data.language == "dubbed"
|
||||||
|
else
|
||||||
|
data.slug.contains("dubbed")
|
||||||
|
val set: EnumSet<DubStatus> = EnumSet.noneOf(DubStatus::class.java)
|
||||||
|
|
||||||
|
if (isDubbed)
|
||||||
|
set.add(DubStatus.Dubbed)
|
||||||
|
else
|
||||||
|
set.add(DubStatus.Subbed)
|
||||||
|
val episodeCount = data.episodeCount?.toIntOrNull()
|
||||||
|
|
||||||
|
return AnimeSearchResponse(
|
||||||
|
data.name.replace("Dubbed", ""), // i.english ?: i.canonicalTitle,
|
||||||
|
"$mainUrl/anime/${data.slug}",
|
||||||
|
data.slug,
|
||||||
|
this.name,
|
||||||
|
type,
|
||||||
|
"https://cdn.shiro.is/${data.image}",
|
||||||
|
data.year?.toIntOrNull(),
|
||||||
|
data.canonicalTitle,
|
||||||
|
set,
|
||||||
|
if (isDubbed) episodeCount else null,
|
||||||
|
if (!isDubbed) episodeCount else null,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun quickSearch(query: String): ArrayList<Any> {
|
||||||
|
val returnValue: ArrayList<Any> = ArrayList()
|
||||||
|
|
||||||
|
val response = khttp.get("https://tapi.shiro.is/anime/auto-complete/${
|
||||||
|
URLEncoder.encode(
|
||||||
|
query,
|
||||||
|
"UTF-8"
|
||||||
|
)
|
||||||
|
}?token=$token".replace("+", "%20"))
|
||||||
|
if (response.text == "{\"status\":\"Found\",\"data\":[]}") return returnValue // OR ELSE WILL CAUSE WEIRD ERROR
|
||||||
|
println("QUICK: " + response.text)
|
||||||
|
val mapped = response.let { mapper.readValue<ShiroSearchResponse>(it.text) }
|
||||||
|
println("SIZE: " + mapped.data.size)
|
||||||
|
println("TOTAL: " + mapped)
|
||||||
|
for (i in mapped.data) {
|
||||||
|
returnValue.add(turnSearchIntoResponse(i))
|
||||||
|
}
|
||||||
|
return returnValue
|
||||||
|
}
|
||||||
|
|
||||||
override fun search(query: String): ArrayList<Any>? {
|
override fun search(query: String): ArrayList<Any>? {
|
||||||
if (!autoLoadToken()) return null
|
if (!autoLoadToken()) return null
|
||||||
val returnValue: ArrayList<Any> = ArrayList()
|
val returnValue: ArrayList<Any> = ArrayList()
|
||||||
|
@ -144,29 +197,7 @@ class ShiroProvider : MainAPI() {
|
||||||
|
|
||||||
val mapped = response.let { mapper.readValue<ShiroFullSearchResponse>(it.text) }
|
val mapped = response.let { mapper.readValue<ShiroFullSearchResponse>(it.text) }
|
||||||
for (i in mapped.data.nav.currentPage.items) {
|
for (i in mapped.data.nav.currentPage.items) {
|
||||||
val type = getType(i.type)
|
returnValue.add(turnSearchIntoResponse(i))
|
||||||
val isDubbed = i.language == "dubbed"
|
|
||||||
val set: EnumSet<DubStatus> = EnumSet.noneOf(DubStatus::class.java)
|
|
||||||
|
|
||||||
if (isDubbed)
|
|
||||||
set.add(DubStatus.Dubbed)
|
|
||||||
else
|
|
||||||
set.add(DubStatus.Subbed)
|
|
||||||
val episodeCount = i.episodeCount.toInt()
|
|
||||||
|
|
||||||
returnValue.add(AnimeSearchResponse(
|
|
||||||
i.name.replace("Dubbed", ""), // i.english ?: i.canonicalTitle,
|
|
||||||
"$mainUrl/anime/${i.slug}",
|
|
||||||
i.slug,
|
|
||||||
this.name,
|
|
||||||
type,
|
|
||||||
"https://cdn.shiro.is/${i.image}",
|
|
||||||
i.year?.toIntOrNull(),
|
|
||||||
i.canonicalTitle,
|
|
||||||
set,
|
|
||||||
if (isDubbed) episodeCount else null,
|
|
||||||
if (!isDubbed) episodeCount else null,
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
return returnValue
|
return returnValue
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,8 @@ class MeloMovieProvider : MainAPI() {
|
||||||
get() = "https://melomovie.com"
|
get() = "https://melomovie.com"
|
||||||
override val instantLinkLoading: Boolean
|
override val instantLinkLoading: Boolean
|
||||||
get() = true
|
get() = true
|
||||||
|
override val hasQuickSearch: Boolean
|
||||||
|
get() = true
|
||||||
|
|
||||||
data class MeloMovieSearchResult(
|
data class MeloMovieSearchResult(
|
||||||
@JsonProperty("id") val id: Int,
|
@JsonProperty("id") val id: Int,
|
||||||
|
@ -27,6 +29,10 @@ class MeloMovieProvider : MainAPI() {
|
||||||
|
|
||||||
data class MeloMovieLink(val name: String, val link: String)
|
data class MeloMovieLink(val name: String, val link: String)
|
||||||
|
|
||||||
|
override fun quickSearch(query: String): ArrayList<Any>? {
|
||||||
|
return search(query)
|
||||||
|
}
|
||||||
|
|
||||||
override fun search(query: String): ArrayList<Any>? {
|
override fun search(query: String): ArrayList<Any>? {
|
||||||
val url = "$mainUrl/movie/search/?name=$query"
|
val url = "$mainUrl/movie/search/?name=$query"
|
||||||
val returnValue: ArrayList<Any> = ArrayList()
|
val returnValue: ArrayList<Any> = ArrayList()
|
||||||
|
|
|
@ -29,6 +29,23 @@ sealed class Resource<out T> {
|
||||||
data class Loading(val url : String? = null) : Resource<Nothing>()
|
data class Loading(val url : String? = null) : Resource<Nothing>()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun logError(throwable: Throwable) {
|
||||||
|
Log.d("ApiError", "-------------------------------------------------------------------")
|
||||||
|
Log.d("ApiError", "safeApiCall: " + throwable.localizedMessage)
|
||||||
|
Log.d("ApiError", "safeApiCall: " + throwable.message)
|
||||||
|
throwable.printStackTrace()
|
||||||
|
Log.d("ApiError", "-------------------------------------------------------------------")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun<T> normalSafeApiCall(apiCall : () -> T) : T? {
|
||||||
|
return try {
|
||||||
|
apiCall.invoke()
|
||||||
|
} catch (throwable: Throwable) {
|
||||||
|
logError(throwable)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun <T> safeApiCall(
|
suspend fun <T> safeApiCall(
|
||||||
apiCall: suspend () -> T,
|
apiCall: suspend () -> T,
|
||||||
): Resource<T> {
|
): Resource<T> {
|
||||||
|
@ -36,11 +53,7 @@ suspend fun <T> safeApiCall(
|
||||||
try {
|
try {
|
||||||
Resource.Success(apiCall.invoke())
|
Resource.Success(apiCall.invoke())
|
||||||
} catch (throwable: Throwable) {
|
} catch (throwable: Throwable) {
|
||||||
Log.d("ApiError", "-------------------------------------------------------------------")
|
logError(throwable)
|
||||||
Log.d("ApiError", "safeApiCall: " + throwable.localizedMessage)
|
|
||||||
Log.d("ApiError", "safeApiCall: " + throwable.message)
|
|
||||||
throwable.printStackTrace()
|
|
||||||
Log.d("ApiError", "-------------------------------------------------------------------")
|
|
||||||
when (throwable) {
|
when (throwable) {
|
||||||
/*is HttpException -> {
|
/*is HttpException -> {
|
||||||
Resource.Failure(false, throwable.code(), throwable.response()?.errorBody(), throwable.localizedMessage)
|
Resource.Failure(false, throwable.code(), throwable.response()?.errorBody(), throwable.localizedMessage)
|
||||||
|
|
|
@ -119,6 +119,7 @@ class SearchFragment : Fragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onQueryTextChange(newText: String): Boolean {
|
override fun onQueryTextChange(newText: String): Boolean {
|
||||||
|
searchViewModel.quickSearch(newText)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -5,8 +5,6 @@ import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.lagradost.cloudstream3.APIHolder.allApi
|
import com.lagradost.cloudstream3.APIHolder.allApi
|
||||||
import com.lagradost.cloudstream3.APIHolder.apis
|
|
||||||
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 kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
@ -14,13 +12,28 @@ import kotlinx.coroutines.launch
|
||||||
class SearchViewModel : ViewModel() {
|
class SearchViewModel : ViewModel() {
|
||||||
private val _searchResponse: MutableLiveData<Resource<ArrayList<Any>>> = MutableLiveData()
|
private val _searchResponse: MutableLiveData<Resource<ArrayList<Any>>> = MutableLiveData()
|
||||||
val searchResponse: LiveData<Resource<ArrayList<Any>>> get() = _searchResponse
|
val searchResponse: LiveData<Resource<ArrayList<Any>>> get() = _searchResponse
|
||||||
|
var searchCounter = 0
|
||||||
|
|
||||||
fun search(query: String) = viewModelScope.launch {
|
fun search(query: String) = viewModelScope.launch {
|
||||||
|
searchCounter++
|
||||||
|
val localSearchCounter = searchCounter
|
||||||
_searchResponse.postValue(Resource.Loading())
|
_searchResponse.postValue(Resource.Loading())
|
||||||
val data = safeApiCall {
|
val data = safeApiCall {
|
||||||
allApi.search(query)
|
allApi.search(query)
|
||||||
}
|
}
|
||||||
|
if(localSearchCounter != searchCounter) return@launch
|
||||||
|
_searchResponse.postValue(data as Resource<ArrayList<Any>>?)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun quickSearch(query: String) = viewModelScope.launch {
|
||||||
|
searchCounter++
|
||||||
|
val localSearchCounter = searchCounter
|
||||||
|
_searchResponse.postValue(Resource.Loading())
|
||||||
|
val data = safeApiCall {
|
||||||
|
allApi.quickSearch(query)
|
||||||
|
}
|
||||||
|
|
||||||
|
if(localSearchCounter != searchCounter) return@launch
|
||||||
_searchResponse.postValue(data as Resource<ArrayList<Any>>?)
|
_searchResponse.postValue(data as Resource<ArrayList<Any>>?)
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in a new issue