forked from recloudstream/cloudstream
api stuff
This commit is contained in:
parent
22f2a8810e
commit
0fa16e3bbc
8 changed files with 228 additions and 94 deletions
|
@ -143,6 +143,8 @@ interface LoadResponse {
|
|||
}
|
||||
|
||||
data class AnimeLoadResponse(
|
||||
val engName: String?,
|
||||
val japName: String?,
|
||||
override val name: String,
|
||||
override val url: String,
|
||||
override val apiName: String,
|
||||
|
@ -151,9 +153,8 @@ data class AnimeLoadResponse(
|
|||
override val posterUrl: String?,
|
||||
override val year: Int?,
|
||||
|
||||
val dubEpisodes: ArrayList<String>?,
|
||||
val subEpisodes: ArrayList<String>?,
|
||||
val otherName: String?,
|
||||
val dubEpisodes: ArrayList<Any>?,
|
||||
val subEpisodes: ArrayList<Any>?,
|
||||
val showStatus: ShowStatus?,
|
||||
|
||||
val tags: ArrayList<String>?,
|
||||
|
|
|
@ -5,11 +5,21 @@ import com.fasterxml.jackson.module.kotlin.readValue
|
|||
import com.lagradost.cloudstream3.*
|
||||
import java.net.URLEncoder
|
||||
import java.util.*
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
|
||||
class ShiroProvider : MainAPI() {
|
||||
companion object {
|
||||
var token: String? = null
|
||||
|
||||
fun getType(t: String): TvType {
|
||||
return when (t) {
|
||||
"TV" -> TvType.Anime
|
||||
"OVA" -> TvType.ONA
|
||||
"movie" -> TvType.Movie
|
||||
else -> TvType.Anime
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun autoLoadToken(): Boolean {
|
||||
|
@ -45,7 +55,7 @@ class ShiroProvider : MainAPI() {
|
|||
@JsonProperty("episodeCount") val episodeCount: String,
|
||||
@JsonProperty("language") val language: String,
|
||||
@JsonProperty("type") val type: String,
|
||||
@JsonProperty("year") val year: String,
|
||||
@JsonProperty("year") val year: String?,
|
||||
@JsonProperty("canonicalTitle") val canonicalTitle: String,
|
||||
@JsonProperty("english") val english: String?,
|
||||
)
|
||||
|
@ -72,51 +82,121 @@ class ShiroProvider : MainAPI() {
|
|||
@JsonProperty("status") val status: String,
|
||||
)
|
||||
|
||||
data class ShiroVideo(
|
||||
@JsonProperty("video_id") val video_id: String,
|
||||
@JsonProperty("host") val host: String,
|
||||
)
|
||||
|
||||
data class ShiroEpisodes(
|
||||
@JsonProperty("anime") val anime: AnimePageData?,
|
||||
@JsonProperty("anime_slug") val anime_slug: String,
|
||||
@JsonProperty("create") val create: String,
|
||||
@JsonProperty("dayOfTheWeek") val dayOfTheWeek: String,
|
||||
@JsonProperty("episode_number") val episode_number: Int,
|
||||
@JsonProperty("slug") val slug: String,
|
||||
@JsonProperty("update") val update: String,
|
||||
@JsonProperty("_id") val _id: String,
|
||||
@JsonProperty("videos") val videos: List<ShiroVideo>,
|
||||
)
|
||||
|
||||
data class AnimePageData(
|
||||
@JsonProperty("banner") val banner: String?,
|
||||
@JsonProperty("canonicalTitle") val canonicalTitle: String?,
|
||||
@JsonProperty("episodeCount") val episodeCount: String,
|
||||
@JsonProperty("genres") val genres: List<String>?,
|
||||
@JsonProperty("image") val image: String,
|
||||
@JsonProperty("japanese") val japanese: String?,
|
||||
@JsonProperty("english") val english: String?,
|
||||
@JsonProperty("language") val language: String,
|
||||
@JsonProperty("name") val name: String,
|
||||
@JsonProperty("slug") val slug: String,
|
||||
@JsonProperty("synopsis") val synopsis: String,
|
||||
@JsonProperty("type") val type: String?,
|
||||
@JsonProperty("views") val views: Int?,
|
||||
@JsonProperty("year") val year: String?,
|
||||
@JsonProperty("_id") val _id: String,
|
||||
@JsonProperty("episodes") var episodes: List<ShiroEpisodes>?,
|
||||
@JsonProperty("synonyms") var synonyms: List<String>?,
|
||||
@JsonProperty("status") val status: String?,
|
||||
@JsonProperty("schedule") val schedule: String?,
|
||||
)
|
||||
|
||||
data class AnimePage(
|
||||
@JsonProperty("data") val data: AnimePageData,
|
||||
@JsonProperty("status") val status: String,
|
||||
)
|
||||
|
||||
override fun search(query: String): ArrayList<Any>? {
|
||||
try {
|
||||
if (!autoLoadToken()) return null
|
||||
val returnValue: ArrayList<Any> = ArrayList()
|
||||
val response = khttp.get("https://tapi.shiro.is/advanced?search=${
|
||||
URLEncoder.encode(
|
||||
query,
|
||||
"UTF-8"
|
||||
)
|
||||
}&token=$token".replace("+", "%20"))
|
||||
println(response.text)
|
||||
val mapped = response.let { mapper.readValue<ShiroFullSearchResponse>(it.text) }
|
||||
for (i in mapped.data.nav.currentPage.items) {
|
||||
if (!autoLoadToken()) return null
|
||||
val returnValue: ArrayList<Any> = ArrayList()
|
||||
val response = khttp.get("https://tapi.shiro.is/advanced?search=${
|
||||
URLEncoder.encode(
|
||||
query,
|
||||
"UTF-8"
|
||||
)
|
||||
}&token=$token".replace("+", "%20"))
|
||||
if (response.text == "{\"status\":\"Found\",\"data\":[]}") return returnValue // OR ELSE WILL CAUSE WEIRD ERROR
|
||||
|
||||
val type = when (i.type) {
|
||||
"TV" -> TvType.Anime
|
||||
"OVA" -> TvType.ONA
|
||||
"movie" -> TvType.Movie
|
||||
else -> TvType.Anime
|
||||
}
|
||||
val isDubbed = i.language == "dubbed"
|
||||
val set: EnumSet<DubStatus> = EnumSet.noneOf(DubStatus::class.java)
|
||||
val mapped = response.let { mapper.readValue<ShiroFullSearchResponse>(it.text) }
|
||||
for (i in mapped.data.nav.currentPage.items) {
|
||||
val type = getType(i.type)
|
||||
val isDubbed = i.language == "dubbed"
|
||||
val set: EnumSet<DubStatus> = EnumSet.noneOf(DubStatus::class.java)
|
||||
|
||||
if (isDubbed)
|
||||
set.add(DubStatus.HasDub)
|
||||
else
|
||||
set.add(DubStatus.HasSub)
|
||||
val episodeCount = i.episodeCount.toInt()
|
||||
if (isDubbed)
|
||||
set.add(DubStatus.HasDub)
|
||||
else
|
||||
set.add(DubStatus.HasSub)
|
||||
val episodeCount = i.episodeCount.toInt()
|
||||
|
||||
returnValue.add(AnimeSearchResponse(
|
||||
i.english ?: i.canonicalTitle,
|
||||
"$mainUrl/${i.slug}",
|
||||
this.name,
|
||||
type,
|
||||
"https://cdn.shiro.is/${i.image}",
|
||||
i.year.toInt(),
|
||||
i.canonicalTitle,
|
||||
set,
|
||||
if (isDubbed) episodeCount else null,
|
||||
if (!isDubbed) episodeCount else null,
|
||||
))
|
||||
}
|
||||
return returnValue
|
||||
} catch (e: Exception) {
|
||||
return null
|
||||
returnValue.add(AnimeSearchResponse(
|
||||
i.english ?: i.canonicalTitle,
|
||||
"$mainUrl/${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
|
||||
}
|
||||
|
||||
override fun load(url: String): Any? {
|
||||
if (!autoLoadToken()) return null
|
||||
val rurl = "https://tapi.shiro.is/anime/slug/${url}?token=${token}"
|
||||
val response = khttp.get(rurl, timeout = 120.0)
|
||||
val mapped = response.let { mapper.readValue<AnimePage>(it.text) }
|
||||
val data = mapped.data
|
||||
val isDubbed = data.language == "dubbed"
|
||||
val episodes = ArrayList<Any>(data.episodes ?: ArrayList())
|
||||
val status = when (data.status) {
|
||||
"current" -> ShowStatus.Ongoing
|
||||
"finished" -> ShowStatus.Completed
|
||||
else -> null
|
||||
}
|
||||
|
||||
return AnimeLoadResponse(
|
||||
data.english,
|
||||
data.japanese,
|
||||
data.canonicalTitle ?: data.name.replace("Dubbed", ""),
|
||||
url,
|
||||
this.name,
|
||||
getType(data.type ?: ""),
|
||||
"https://cdn.shiro.is/${data.image}",
|
||||
data.year?.toIntOrNull(),
|
||||
if (isDubbed) episodes else null,
|
||||
if (!isDubbed) episodes else null,
|
||||
status,
|
||||
ArrayList(data.genres ?: ArrayList()),
|
||||
data.synopsis,
|
||||
ArrayList(data.synonyms ?: ArrayList()),
|
||||
null,
|
||||
null,
|
||||
)
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package com.lagradost.cloudstream3.ui.result
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.lagradost.cloudstream3.R
|
||||
|
||||
class ResultFragment : Fragment() {
|
||||
private lateinit var viewModel: ResultViewModel
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
viewModel =
|
||||
ViewModelProvider(this).get(ResultViewModel::class.java)
|
||||
|
||||
return inflater.inflate(R.layout.fragment_result, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package com.lagradost.cloudstream3.ui.result
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
|
||||
class ResultViewModel : ViewModel() {
|
||||
|
||||
}
|
|
@ -1,18 +1,15 @@
|
|||
package com.lagradost.cloudstream3.ui.search
|
||||
|
||||
import android.content.DialogInterface
|
||||
import android.content.res.Configuration
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.widget.SearchView
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.preference.PreferenceManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
|
@ -22,8 +19,9 @@ import com.lagradost.cloudstream3.APIHolder.getApiSettings
|
|||
import com.lagradost.cloudstream3.R
|
||||
import com.lagradost.cloudstream3.UIHelper.fixPaddingStatusbar
|
||||
import com.lagradost.cloudstream3.UIHelper.getGridIsCompact
|
||||
import com.lagradost.cloudstream3.mvvm.Resource
|
||||
import com.lagradost.cloudstream3.mvvm.observe
|
||||
import kotlinx.android.synthetic.main.fragment_search.*
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
class SearchFragment : Fragment() {
|
||||
|
||||
|
@ -106,19 +104,7 @@ class SearchFragment : Fragment() {
|
|||
override fun onQueryTextSubmit(query: String): Boolean {
|
||||
search_exit_icon.alpha = 0f
|
||||
search_loading_bar.alpha = 1f
|
||||
thread {
|
||||
val data = allApi.search(query)//MainActivity.activeAPI.search(query)
|
||||
activity?.runOnUiThread {
|
||||
if (data == null) {
|
||||
Toast.makeText(activity, "Server error", Toast.LENGTH_LONG).show()
|
||||
} else {
|
||||
(cardSpace.adapter as SearchAdapter).cardList = data
|
||||
(cardSpace.adapter as SearchAdapter).notifyDataSetChanged()
|
||||
}
|
||||
search_exit_icon.alpha = 1f
|
||||
search_loading_bar.alpha = 0f
|
||||
}
|
||||
}
|
||||
searchViewModel.search(query)
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -127,6 +113,20 @@ class SearchFragment : Fragment() {
|
|||
}
|
||||
})
|
||||
|
||||
observe(searchViewModel.searchResponse) {
|
||||
when (it) {
|
||||
is Resource.Success -> {
|
||||
(cardSpace.adapter as SearchAdapter).cardList = it.value
|
||||
(cardSpace.adapter as SearchAdapter).notifyDataSetChanged()
|
||||
}
|
||||
is Resource.Failure -> {
|
||||
Toast.makeText(activity, "Server error", Toast.LENGTH_LONG).show()
|
||||
}
|
||||
}
|
||||
search_exit_icon.alpha = 1f
|
||||
search_loading_bar.alpha = 0f
|
||||
}
|
||||
|
||||
main_search.onActionViewExpanded()
|
||||
|
||||
}
|
||||
|
|
|
@ -3,20 +3,24 @@ package com.lagradost.cloudstream3.ui.search
|
|||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.lagradost.cloudstream3.APIHolder.apis
|
||||
import com.lagradost.cloudstream3.MainAPI
|
||||
import com.lagradost.cloudstream3.mvvm.Resource
|
||||
import com.lagradost.cloudstream3.mvvm.safeApiCall
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class SearchViewModel : ViewModel() {
|
||||
val api: MainAPI = apis[0]
|
||||
|
||||
private val _text = MutableLiveData<String>().apply {
|
||||
value = "This is dashboard Fragment"
|
||||
}
|
||||
val text: LiveData<String> = _text
|
||||
private val _searchResponse: MutableLiveData<Resource<ArrayList<Any>>> = MutableLiveData()
|
||||
val searchResponse: LiveData<Resource<ArrayList<Any>>> get() = _searchResponse
|
||||
|
||||
val api : MainAPI = apis[0]
|
||||
fun search(query: String) = viewModelScope.launch {
|
||||
val data = safeApiCall {
|
||||
api.search(query)
|
||||
}
|
||||
|
||||
suspend fun search(query: String) = safeApiCall {
|
||||
api.search(query)
|
||||
_searchResponse.postValue(data as Resource<ArrayList<Any>>?)
|
||||
}
|
||||
}
|
|
@ -1,35 +1,42 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
<FrameLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"
|
||||
android:paddingTop="0dp">
|
||||
|
||||
<com.google.android.material.bottomnavigation.BottomNavigationView
|
||||
android:id="@+id/nav_view"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="0dp"
|
||||
android:layout_marginEnd="0dp"
|
||||
android:background="@color/darkBackground"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:menu="@menu/bottom_nav_menu"/>
|
||||
|
||||
<fragment
|
||||
android:id="@+id/nav_host_fragment"
|
||||
android:name="androidx.navigation.fragment.NavHostFragment"
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:defaultNavHost="true"
|
||||
app:layout_constraintBottom_toTopOf="@id/nav_view"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:navGraph="@navigation/mobile_navigation"
|
||||
/>
|
||||
android:id="@+id/homeRoot"
|
||||
>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout android:layout_height="match_parent"
|
||||
android:layout_width="match_parent">
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
<com.google.android.material.bottomnavigation.BottomNavigationView
|
||||
android:id="@+id/nav_view"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/darkBackground"
|
||||
app:itemRippleColor="@color/colorRipple"
|
||||
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:menu="@menu/bottom_nav_menu"
|
||||
app:layout_constraintBottom_toBottomOf="parent"/>
|
||||
<fragment
|
||||
android:id="@+id/nav_host_fragment"
|
||||
android:name="androidx.navigation.fragment.NavHostFragment"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
app:defaultNavHost="true"
|
||||
app:layout_constraintBottom_toTopOf="@+id/nav_view"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:navGraph="@navigation/mobile_navigation"
|
||||
app:layout_constraintEnd_toEndOf="parent"/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</FrameLayout>
|
||||
</FrameLayout>
|
6
app/src/main/res/layout/fragment_result.xml
Normal file
6
app/src/main/res/layout/fragment_result.xml
Normal file
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
Loading…
Reference in a new issue