forked from recloudstream/cloudstream
homepage somewhat working
This commit is contained in:
parent
f270f9f551
commit
61323b5c56
15 changed files with 545 additions and 51 deletions
|
@ -47,6 +47,13 @@ object APIHolder {
|
|||
return apis[defProvider]
|
||||
}
|
||||
|
||||
fun getApiFromNameNull(apiName: String?): MainAPI? {
|
||||
for (api in apis) {
|
||||
if (apiName == api.name)
|
||||
return api
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
fun LoadResponse.getId(): Int {
|
||||
return url.replace(getApiFromName(apiName).mainUrl, "").hashCode()
|
||||
|
@ -70,14 +77,19 @@ abstract class MainAPI {
|
|||
/**If link is stored in the "data" string, so links can be instantly loaded*/
|
||||
open val instantLinkLoading = false
|
||||
|
||||
open val hasQuickSearch = false
|
||||
|
||||
/**Set false if links require referer or for some reason cant be played on a chromecast*/
|
||||
open val hasChromecastSupport = true
|
||||
|
||||
/**If all links are m3u8 then set this to false*/
|
||||
open val hasDownloadSupport = true
|
||||
|
||||
open val hasMainPage = false
|
||||
open val hasQuickSearch = false
|
||||
|
||||
open fun getMainPage() : HomePageResponse? {
|
||||
return null
|
||||
}
|
||||
|
||||
open fun search(query: String): ArrayList<SearchResponse>? {
|
||||
return null
|
||||
}
|
||||
|
@ -161,6 +173,15 @@ fun TvType.isMovieType(): Boolean {
|
|||
|
||||
data class SubtitleFile(val lang: String, val url: String)
|
||||
|
||||
class HomePageResponse(
|
||||
val items: List<HomePageList>
|
||||
)
|
||||
|
||||
class HomePageList(
|
||||
val name: String,
|
||||
val list: List<SearchResponse>
|
||||
)
|
||||
|
||||
interface SearchResponse {
|
||||
val name: String
|
||||
val url: String // PUBLIC URL FOR OPEN IN APP
|
||||
|
|
|
@ -9,6 +9,7 @@ import java.net.URLEncoder
|
|||
import java.util.*
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
const val SHIRO_TIMEOUT_TIME = 60.0
|
||||
|
||||
class ShiroProvider : MainAPI() {
|
||||
companion object {
|
||||
|
@ -55,6 +56,9 @@ class ShiroProvider : MainAPI() {
|
|||
override val hasQuickSearch: Boolean
|
||||
get() = true
|
||||
|
||||
override val hasMainPage: Boolean
|
||||
get() = true
|
||||
|
||||
data class ShiroSearchResponseShow(
|
||||
@JsonProperty("image") val image: String,
|
||||
@JsonProperty("_id") val _id: String,
|
||||
|
@ -134,6 +138,46 @@ class ShiroProvider : MainAPI() {
|
|||
@JsonProperty("status") val status: String,
|
||||
)
|
||||
|
||||
data class ShiroHomePageData(
|
||||
@JsonProperty("trending_animes") val trending_animes: List<AnimePageData>,
|
||||
@JsonProperty("ongoing_animes") val ongoing_animes: List<AnimePageData>,
|
||||
@JsonProperty("latest_animes") val latest_animes: List<AnimePageData>,
|
||||
@JsonProperty("latest_episodes") val latest_episodes: List<ShiroEpisodes>,
|
||||
)
|
||||
|
||||
data class ShiroHomePage(
|
||||
@JsonProperty("status") val status: String,
|
||||
@JsonProperty("data") val data: ShiroHomePageData,
|
||||
@JsonProperty("random") var random: AnimePage?,
|
||||
)
|
||||
|
||||
private fun toHomePageList(list: List<AnimePageData>, name: String): HomePageList {
|
||||
return HomePageList(name, list.map { data ->
|
||||
val type = getType(data.type)
|
||||
val isDubbed =
|
||||
data.language == "dubbed"
|
||||
|
||||
val set: EnumSet<DubStatus> =
|
||||
EnumSet.of(if (isDubbed) DubStatus.Dubbed else DubStatus.Subbed)
|
||||
|
||||
val episodeCount = data.episodeCount?.toIntOrNull()
|
||||
|
||||
return@map 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,
|
||||
)
|
||||
}.toList())
|
||||
}
|
||||
|
||||
private fun turnSearchIntoResponse(data: ShiroSearchResponseShow): AnimeSearchResponse {
|
||||
val type = getType(data.type)
|
||||
val isDubbed =
|
||||
|
@ -141,12 +185,9 @@ class ShiroProvider : MainAPI() {
|
|||
data.language == "dubbed"
|
||||
else
|
||||
data.slug.contains("dubbed")
|
||||
val set: EnumSet<DubStatus> = EnumSet.noneOf(DubStatus::class.java)
|
||||
val set: EnumSet<DubStatus> =
|
||||
EnumSet.of(if (isDubbed) DubStatus.Dubbed else DubStatus.Subbed)
|
||||
|
||||
if (isDubbed)
|
||||
set.add(DubStatus.Dubbed)
|
||||
else
|
||||
set.add(DubStatus.Subbed)
|
||||
val episodeCount = data.episodeCount?.toIntOrNull()
|
||||
|
||||
return AnimeSearchResponse(
|
||||
|
@ -164,7 +205,26 @@ class ShiroProvider : MainAPI() {
|
|||
)
|
||||
}
|
||||
|
||||
override fun quickSearch(query: String): ArrayList<SearchResponse> {
|
||||
override fun getMainPage(): HomePageResponse? {
|
||||
if (!autoLoadToken()) return null
|
||||
|
||||
val url = "https://tapi.shiro.is/latest?token=$token"
|
||||
val response = khttp.get(url, timeout = SHIRO_TIMEOUT_TIME)
|
||||
val res = response.text.let { mapper.readValue<ShiroHomePage>(it) }
|
||||
|
||||
val d = res.data
|
||||
return HomePageResponse(
|
||||
listOf(
|
||||
toHomePageList(d.trending_animes, "Trending"),
|
||||
toHomePageList(d.ongoing_animes, "Ongoing"),
|
||||
toHomePageList(d.latest_animes, "Latest")
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override fun quickSearch(query: String): ArrayList<SearchResponse>? {
|
||||
if (!autoLoadToken()) return null
|
||||
|
||||
val returnValue: ArrayList<SearchResponse> = ArrayList()
|
||||
|
||||
val response = khttp.get(
|
||||
|
@ -214,8 +274,8 @@ class ShiroProvider : MainAPI() {
|
|||
val episodes =
|
||||
ArrayList<AnimeEpisode>(
|
||||
data.episodes?.distinctBy { it.episode_number }?.sortedBy { it.episode_number }
|
||||
?.map { AnimeEpisode(it.videos[0].video_id) }
|
||||
?: ArrayList<AnimeEpisode>())
|
||||
?.map { AnimeEpisode(it.videos[0].video_id) }
|
||||
?: ArrayList<AnimeEpisode>())
|
||||
val status = when (data.status) {
|
||||
"current" -> ShowStatus.Ongoing
|
||||
"finished" -> ShowStatus.Completed
|
||||
|
|
|
@ -5,6 +5,7 @@ import androidx.lifecycle.LifecycleOwner
|
|||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.Observer
|
||||
import com.bumptech.glide.load.HttpException
|
||||
import com.lagradost.cloudstream3.ui.ErrorLoadingException
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.net.SocketTimeoutException
|
||||
|
@ -67,6 +68,9 @@ suspend fun <T> safeApiCall(
|
|||
is UnknownHostException -> {
|
||||
Resource.Failure(true, null, null, "Cannot connect to server, try again later.")
|
||||
}
|
||||
is ErrorLoadingException -> {
|
||||
Resource.Failure(true, null, null, "Error loading, try again later.")
|
||||
}
|
||||
else -> {
|
||||
val stackTraceMsg = throwable.localizedMessage + "\n\n" + throwable.stackTrace.joinToString(
|
||||
separator = "\n"
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
package com.lagradost.cloudstream3.ui
|
||||
|
||||
import com.lagradost.cloudstream3.*
|
||||
import com.lagradost.cloudstream3.mvvm.Resource
|
||||
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
|
||||
import com.lagradost.cloudstream3.mvvm.safeApiCall
|
||||
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||
|
||||
class ErrorLoadingException(message: String) : Exception(message)
|
||||
|
||||
class APIRepository(val api: MainAPI) {
|
||||
val name : String get() = api.name
|
||||
val mainUrl : String get() = api.mainUrl
|
||||
|
||||
suspend fun load(url: String): Resource<LoadResponse> {
|
||||
return safeApiCall {
|
||||
api.load(url) ?: throw ErrorLoadingException("Error Loading")
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun search(query: String): Resource<ArrayList<SearchResponse>> {
|
||||
return safeApiCall {
|
||||
api.search(query) ?: throw ErrorLoadingException("Error Loading")
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun quickSearch(query: String): Resource<ArrayList<SearchResponse>> {
|
||||
return safeApiCall {
|
||||
api.quickSearch(query) ?: throw ErrorLoadingException("Error Loading")
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun getMainPage(): Resource<HomePageResponse> {
|
||||
return safeApiCall {
|
||||
api.getMainPage() ?: throw ErrorLoadingException("Error Loading")
|
||||
}
|
||||
}
|
||||
|
||||
fun loadLinks(
|
||||
data: String,
|
||||
isCasting: Boolean,
|
||||
subtitleCallback: (SubtitleFile) -> Unit,
|
||||
callback: (ExtractorLink) -> Unit
|
||||
): Boolean {
|
||||
return normalSafeApiCall { api.loadLinks(data, isCasting, subtitleCallback, callback) } ?: false
|
||||
}
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
package com.lagradost.cloudstream3.ui.home
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.cardview.widget.CardView
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.load.model.GlideUrl
|
||||
import com.lagradost.cloudstream3.*
|
||||
import kotlinx.android.synthetic.main.home_result_grid.view.*
|
||||
|
||||
class HomeChildItemAdapter(
|
||||
var cardList: List<Any>,
|
||||
private val clickCallback: (SearchResponse) -> Unit
|
||||
) :
|
||||
RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
val layout = R.layout.home_result_grid
|
||||
return CardViewHolder(
|
||||
LayoutInflater.from(parent.context).inflate(layout, parent, false), clickCallback
|
||||
)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
when (holder) {
|
||||
is CardViewHolder -> {
|
||||
holder.bind(cardList[position])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return cardList.size
|
||||
}
|
||||
|
||||
class CardViewHolder
|
||||
constructor(itemView: View, private val clickCallback: (SearchResponse) -> Unit) :
|
||||
RecyclerView.ViewHolder(itemView) {
|
||||
val cardView: ImageView = itemView.imageView
|
||||
private val cardText: TextView = itemView.imageText
|
||||
private val textType: TextView? = itemView.text_type
|
||||
// val search_result_lang: ImageView? = itemView.search_result_lang
|
||||
|
||||
private val textIsDub: View? = itemView.text_is_dub
|
||||
private val textIsSub: View? = itemView.text_is_sub
|
||||
|
||||
//val cardTextExtra: TextView? = itemView.imageTextExtra
|
||||
//val imageTextProvider: TextView? = itemView.imageTextProvider
|
||||
private val bg: CardView = itemView.backgroundCard
|
||||
|
||||
fun bind(card: Any) {
|
||||
if (card is SearchResponse) { // GENERIC
|
||||
|
||||
textType?.text = when (card.type) {
|
||||
TvType.Anime -> "Anime"
|
||||
TvType.Movie -> "Movie"
|
||||
TvType.ONA -> "ONA"
|
||||
TvType.TvSeries -> "TV"
|
||||
}
|
||||
// search_result_lang?.visibility = View.GONE
|
||||
|
||||
textIsDub?.visibility = View.GONE
|
||||
textIsSub?.visibility = View.GONE
|
||||
|
||||
cardText.text = card.name
|
||||
|
||||
//imageTextProvider.text = card.apiName
|
||||
if (!card.posterUrl.isNullOrEmpty()) {
|
||||
|
||||
val glideUrl =
|
||||
GlideUrl(card.posterUrl)
|
||||
|
||||
Glide.with(cardView.context)
|
||||
.load(glideUrl)
|
||||
.into(cardView)
|
||||
|
||||
}
|
||||
|
||||
bg.setOnClickListener {
|
||||
clickCallback.invoke(card)
|
||||
// (activity as AppCompatActivity).loadResult(card.url, card.slug, card.apiName)
|
||||
}
|
||||
|
||||
when (card) {
|
||||
is AnimeSearchResponse -> {
|
||||
if (card.dubStatus?.size == 1) {
|
||||
//search_result_lang?.visibility = View.VISIBLE
|
||||
if (card.dubStatus.contains(DubStatus.Dubbed)) {
|
||||
textIsDub?.visibility = View.VISIBLE
|
||||
//search_result_lang?.setColorFilter(ContextCompat.getColor(activity, R.color.dubColor))
|
||||
} else if (card.dubStatus.contains(DubStatus.Subbed)) {
|
||||
//search_result_lang?.setColorFilter(ContextCompat.getColor(activity, R.color.subColor))
|
||||
textIsSub?.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,14 +4,20 @@ 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.Observer
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.lagradost.cloudstream3.R
|
||||
import com.lagradost.cloudstream3.mvvm.Resource
|
||||
import com.lagradost.cloudstream3.mvvm.observe
|
||||
import com.lagradost.cloudstream3.utils.DataStore.getKey
|
||||
import com.lagradost.cloudstream3.utils.DataStore.setKey
|
||||
import com.lagradost.cloudstream3.utils.HOMEPAGE_API
|
||||
import kotlinx.android.synthetic.main.fragment_child_downloads.*
|
||||
import kotlinx.android.synthetic.main.fragment_home.*
|
||||
|
||||
class HomeFragment : Fragment() {
|
||||
|
||||
private lateinit var homeViewModel: HomeViewModel
|
||||
|
||||
override fun onCreateView(
|
||||
|
@ -21,11 +27,40 @@ class HomeFragment : Fragment() {
|
|||
): View? {
|
||||
homeViewModel =
|
||||
ViewModelProvider(this).get(HomeViewModel::class.java)
|
||||
val root = inflater.inflate(R.layout.fragment_home, container, false)
|
||||
val textView: TextView = root.findViewById(R.id.text_home)
|
||||
homeViewModel.text.observe(viewLifecycleOwner, Observer {
|
||||
textView.text = it
|
||||
})
|
||||
return root
|
||||
|
||||
return inflater.inflate(R.layout.fragment_home, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
observe(homeViewModel.apiName) {
|
||||
context?.setKey(HOMEPAGE_API, it)
|
||||
}
|
||||
|
||||
observe(homeViewModel.page) {
|
||||
when (it) {
|
||||
is Resource.Success -> {
|
||||
val d = it.value
|
||||
(home_master_recycler?.adapter as ParentItemAdapter?)?.itemList = d.items
|
||||
home_master_recycler?.adapter?.notifyDataSetChanged()
|
||||
}
|
||||
is Resource.Failure -> {
|
||||
|
||||
}
|
||||
is Resource.Loading -> {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val adapter: RecyclerView.Adapter<RecyclerView.ViewHolder> = ParentItemAdapter(listOf()) {
|
||||
|
||||
}
|
||||
|
||||
home_master_recycler.adapter = adapter
|
||||
home_master_recycler.layoutManager = GridLayoutManager(context, 1)
|
||||
|
||||
homeViewModel.load(context?.getKey(HOMEPAGE_API))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
package com.lagradost.cloudstream3.ui.home
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.lagradost.cloudstream3.HomePageList
|
||||
import com.lagradost.cloudstream3.R
|
||||
import com.lagradost.cloudstream3.SearchResponse
|
||||
import kotlinx.android.synthetic.main.fragment_home.*
|
||||
import kotlinx.android.synthetic.main.homepage_parent.view.*
|
||||
|
||||
class ParentItemAdapter(
|
||||
var itemList: List<HomePageList>,
|
||||
private val clickCallback: (SearchResponse) -> Unit
|
||||
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||
override fun onCreateViewHolder(parent: ViewGroup, i: Int): ParentViewHolder {
|
||||
val layout = R.layout.homepage_parent
|
||||
return ParentViewHolder(
|
||||
LayoutInflater.from(parent.context).inflate(layout, parent, false), clickCallback
|
||||
)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
when (holder) {
|
||||
is ParentViewHolder -> {
|
||||
holder.bind(itemList[position])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return itemList.size
|
||||
}
|
||||
|
||||
class ParentViewHolder
|
||||
constructor(itemView: View, private val clickCallback: (SearchResponse) -> Unit) :
|
||||
RecyclerView.ViewHolder(itemView) {
|
||||
val title: TextView = itemView.home_parent_item_title
|
||||
val recyclerView: RecyclerView = itemView.home_child_recyclerview
|
||||
fun bind(info: HomePageList) {
|
||||
title.text = info.name
|
||||
recyclerView.adapter = HomeChildItemAdapter(info.list, clickCallback)
|
||||
recyclerView.layoutManager = GridLayoutManager(itemView.context, 1)
|
||||
(recyclerView.adapter as HomeChildItemAdapter).notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -3,11 +3,35 @@ package com.lagradost.cloudstream3.ui.home
|
|||
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.APIHolder.getApiFromNameNull
|
||||
import com.lagradost.cloudstream3.HomePageResponse
|
||||
import com.lagradost.cloudstream3.mvvm.Resource
|
||||
import com.lagradost.cloudstream3.ui.APIRepository
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class HomeViewModel : ViewModel() {
|
||||
var repo: APIRepository? = null
|
||||
|
||||
private val _text = MutableLiveData<String>().apply {
|
||||
value = "This is home Fragment"
|
||||
private val _apiName = MutableLiveData<String>()
|
||||
val apiName: LiveData<String> = _apiName
|
||||
|
||||
private val _page = MutableLiveData<Resource<HomePageResponse>>()
|
||||
val page: LiveData<Resource<HomePageResponse>> = _page
|
||||
|
||||
private fun autoloadRepo(): APIRepository {
|
||||
return APIRepository(apis.first { it.hasMainPage })
|
||||
}
|
||||
|
||||
fun load(preferredApiName: String?) = viewModelScope.launch {
|
||||
val api = getApiFromNameNull(preferredApiName)
|
||||
repo = if (api?.hasMainPage == true) {
|
||||
APIRepository(api)
|
||||
} else {
|
||||
autoloadRepo()
|
||||
}
|
||||
_page.postValue(Resource.Loading())
|
||||
_page.postValue(repo?.getMainPage())
|
||||
}
|
||||
val text: LiveData<String> = _text
|
||||
}
|
|
@ -7,6 +7,7 @@ import com.lagradost.cloudstream3.APIHolder.getApiFromName
|
|||
import com.lagradost.cloudstream3.APIHolder.getId
|
||||
import com.lagradost.cloudstream3.mvvm.Resource
|
||||
import com.lagradost.cloudstream3.mvvm.safeApiCall
|
||||
import com.lagradost.cloudstream3.ui.APIRepository
|
||||
import com.lagradost.cloudstream3.ui.WatchType
|
||||
import com.lagradost.cloudstream3.utils.DataStoreHelper.getResultSeason
|
||||
import com.lagradost.cloudstream3.utils.DataStoreHelper.getResultWatchState
|
||||
|
@ -20,6 +21,8 @@ const val EPISODE_RANGE_SIZE = 50
|
|||
const val EPISODE_RANGE_OVERLOAD = 60
|
||||
|
||||
class ResultViewModel : ViewModel() {
|
||||
var repo : APIRepository? = null
|
||||
|
||||
private val _resultResponse: MutableLiveData<Resource<Any?>> = MutableLiveData()
|
||||
private val _episodes: MutableLiveData<List<ResultEpisode>> = MutableLiveData()
|
||||
private val _publicEpisodes: MutableLiveData<List<ResultEpisode>> = MutableLiveData()
|
||||
|
@ -137,19 +140,14 @@ class ResultViewModel : ViewModel() {
|
|||
updateEpisodes(context, null, copy, selectedSeason.value)
|
||||
}
|
||||
|
||||
// THIS SHOULD AT LEAST CLEAN IT UP, SO APIS CAN SWITCH DOMAIN
|
||||
private fun getId(url: String, api: MainAPI): Int {
|
||||
return url.replace(api.mainUrl, "").hashCode()
|
||||
}
|
||||
|
||||
fun load(context: Context, url: String, apiName: String) = viewModelScope.launch {
|
||||
_resultResponse.postValue(Resource.Loading(url))
|
||||
|
||||
_apiName.postValue(apiName)
|
||||
val api = getApiFromName(apiName)
|
||||
val data = safeApiCall {
|
||||
api.load(url)
|
||||
}
|
||||
repo = APIRepository(api)
|
||||
|
||||
val data = repo?.load(url)
|
||||
|
||||
_resultResponse.postValue(data)
|
||||
|
||||
|
@ -278,7 +276,7 @@ class ResultViewModel : ViewModel() {
|
|||
val links = ArrayList<ExtractorLink>()
|
||||
val subs = ArrayList<SubtitleFile>()
|
||||
return safeApiCall {
|
||||
getApiFromName(_apiName.value).loadLinks(data, isCasting, { subtitleFile ->
|
||||
repo?.loadLinks(data, isCasting, { subtitleFile ->
|
||||
if (!subs.any { it.url == subtitleFile.url }) {
|
||||
subs.add(subtitleFile)
|
||||
_allEpisodesSubs.value?.set(id, subs)
|
||||
|
|
|
@ -7,12 +7,14 @@ import androidx.lifecycle.viewModelScope
|
|||
import com.lagradost.cloudstream3.APIHolder.allApi
|
||||
import com.lagradost.cloudstream3.mvvm.Resource
|
||||
import com.lagradost.cloudstream3.mvvm.safeApiCall
|
||||
import com.lagradost.cloudstream3.ui.APIRepository
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class SearchViewModel : ViewModel() {
|
||||
private val _searchResponse: MutableLiveData<Resource<ArrayList<Any>>> = MutableLiveData()
|
||||
val searchResponse: LiveData<Resource<ArrayList<Any>>> get() = _searchResponse
|
||||
var searchCounter = 0
|
||||
private val repo = APIRepository(allApi)
|
||||
|
||||
private fun clearSearch() {
|
||||
_searchResponse.postValue(Resource.Success(ArrayList()))
|
||||
|
@ -26,9 +28,8 @@ class SearchViewModel : ViewModel() {
|
|||
}
|
||||
val localSearchCounter = searchCounter
|
||||
_searchResponse.postValue(Resource.Loading())
|
||||
val data = safeApiCall {
|
||||
allApi.search(query)
|
||||
}
|
||||
val data = repo.search(query)
|
||||
|
||||
if(localSearchCounter != searchCounter) return@launch
|
||||
_searchResponse.postValue(data as Resource<ArrayList<Any>>?)
|
||||
}
|
||||
|
@ -41,9 +42,7 @@ class SearchViewModel : ViewModel() {
|
|||
}
|
||||
val localSearchCounter = searchCounter
|
||||
_searchResponse.postValue(Resource.Loading())
|
||||
val data = safeApiCall {
|
||||
allApi.quickSearch(query)
|
||||
}
|
||||
val data = repo.quickSearch(query)
|
||||
|
||||
if(localSearchCounter != searchCounter) return@launch
|
||||
_searchResponse.postValue(data as Resource<ArrayList<Any>>?)
|
||||
|
|
|
@ -9,6 +9,7 @@ import com.fasterxml.jackson.module.kotlin.KotlinModule
|
|||
const val DOWNLOAD_HEADER_CACHE = "download_header_cache"
|
||||
const val DOWNLOAD_EPISODE_CACHE = "download_episode_cache"
|
||||
const val VIDEO_PLAYER_BRIGHTNESS = "video_player_alpha"
|
||||
const val HOMEPAGE_API = "home_api_used"
|
||||
|
||||
const val PREFERENCES_NAME: String = "rebuild_preference"
|
||||
|
||||
|
|
|
@ -7,17 +7,10 @@
|
|||
android:layout_height="match_parent"
|
||||
tools:context=".ui.home.HomeFragment">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_home"
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/home_master_recycler"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:textAlignment="center"
|
||||
android:textSize="20sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"/>
|
||||
android:layout_height="match_parent"
|
||||
tools:listitem="@layout/homepage_parent"
|
||||
/>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
125
app/src/main/res/layout/home_result_grid.xml
Normal file
125
app/src/main/res/layout/home_result_grid.xml
Normal file
|
@ -0,0 +1,125 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<!-- android:layout_width="114dp"
|
||||
android:layout_height="180dp"-->
|
||||
<androidx.cardview.widget.CardView
|
||||
android:foreground="?android:attr/selectableItemBackgroundBorderless"
|
||||
android:layout_margin="2dp"
|
||||
android:layout_width="114dp"
|
||||
android:layout_height="180dp"
|
||||
android:layout_marginBottom="2dp"
|
||||
android:elevation="10dp"
|
||||
app:cardCornerRadius="@dimen/roundedImageRadius"
|
||||
android:id="@+id/backgroundCard"
|
||||
app:cardBackgroundColor="@color/darkBackground"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
<ImageView
|
||||
android:duplicateParentState="true"
|
||||
android:id="@+id/imageView"
|
||||
tools:src="@drawable/example_poster"
|
||||
android:scaleType="centerCrop"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:foreground="?android:attr/selectableItemBackgroundBorderless"
|
||||
android:contentDescription="@string/search_poster_descript"/>
|
||||
<ImageView
|
||||
android:focusable="false"
|
||||
android:clickable="false"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="50dp"
|
||||
android:src="@drawable/title_shadow"
|
||||
android:layout_gravity="bottom" android:contentDescription="@string/shadow_descript">
|
||||
</ImageView>
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:layout_gravity="bottom"
|
||||
android:paddingBottom="5dp"
|
||||
android:paddingTop="5dp"
|
||||
android:textColor="@color/textColor"
|
||||
android:id="@+id/imageText"
|
||||
android:textStyle="bold"
|
||||
android:maxLines="2"
|
||||
android:paddingStart="5dp"
|
||||
android:paddingEnd="5dp"
|
||||
android:ellipsize="end"
|
||||
/>
|
||||
<TextView
|
||||
android:text="Movie"
|
||||
android:visibility="gone"
|
||||
android:id="@+id/text_type"
|
||||
android:textColor="@color/textColor"
|
||||
android:paddingRight="10dp"
|
||||
android:paddingLeft="10dp"
|
||||
android:paddingTop="4dp"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:layout_gravity="start"
|
||||
android:paddingBottom="8dp"
|
||||
android:minWidth="50dp"
|
||||
android:gravity="center"
|
||||
android:background="@drawable/type_bg_color"
|
||||
android:layout_width="wrap_content" android:layout_height="wrap_content">
|
||||
</TextView>
|
||||
<!--<View
|
||||
android:id="@+id/search_result_lang"
|
||||
android:layout_gravity="bottom"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="4dp"
|
||||
android:alpha="0.9">
|
||||
|
||||
</View>-->
|
||||
<!--<ImageView
|
||||
android:src="@drawable/ic_baseline_bookmark_24"
|
||||
android:id="@+id/search_result_lang"
|
||||
android:layout_gravity="right"
|
||||
android:layout_marginTop="-5dp"
|
||||
android:layout_marginRight="-6.5dp"
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="30dp">
|
||||
</ImageView>-->
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_gravity="end"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<!--
|
||||
<ImageView android:id="@+id/text_is_dub" android:tint="@color/colorPrimary"
|
||||
android:src="@drawable/ic_baseline_subtitles_24" android:layout_width="wrap_content"
|
||||
android:layout_height="20dp">
|
||||
|
||||
</ImageView>-->
|
||||
<TextView
|
||||
android:text="@string/app_dubbed_text"
|
||||
android:id="@+id/text_is_dub"
|
||||
android:textColor="@color/textColor"
|
||||
android:paddingRight="10dp"
|
||||
android:paddingLeft="10dp"
|
||||
android:paddingTop="4dp"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:layout_gravity="end"
|
||||
android:paddingBottom="4dp"
|
||||
android:minWidth="50dp"
|
||||
android:gravity="center"
|
||||
android:background="@drawable/dub_bg_color"
|
||||
android:layout_width="wrap_content" android:layout_height="wrap_content">
|
||||
</TextView>
|
||||
<TextView
|
||||
android:id="@+id/text_is_sub"
|
||||
android:text="@string/app_subbed_text"
|
||||
android:layout_gravity="end"
|
||||
android:textColor="@color/textColor"
|
||||
android:paddingRight="10dp"
|
||||
android:paddingLeft="10dp"
|
||||
android:paddingTop="4dp"
|
||||
android:paddingBottom="4dp"
|
||||
android:minWidth="50dp"
|
||||
android:gravity="center"
|
||||
android:background="@drawable/sub_bg_color"
|
||||
android:layout_width="wrap_content" android:layout_height="wrap_content"
|
||||
>
|
||||
</TextView>
|
||||
</LinearLayout>
|
||||
</androidx.cardview.widget.CardView>
|
33
app/src/main/res/layout/homepage_parent.xml
Normal file
33
app/src/main/res/layout/homepage_parent.xml
Normal file
|
@ -0,0 +1,33 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:orientation="vertical"
|
||||
android:background="@color/colorPrimary"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/home_parent_item_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="12sp"
|
||||
android:textSize="18sp"
|
||||
tools:text="Trending"
|
||||
/>
|
||||
<RelativeLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="230dp"
|
||||
android:layout_marginBottom="20dp"
|
||||
>
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
android:id="@+id/home_child_recyclerview"
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
tools:listitem="@layout/home_result_grid"
|
||||
/>
|
||||
</RelativeLayout>
|
||||
</LinearLayout>
|
|
@ -9,10 +9,8 @@
|
|||
android:focusable="true"
|
||||
android:clickable="true"
|
||||
android:id="@+id/search_result_root"
|
||||
|
||||
>
|
||||
<androidx.cardview.widget.CardView
|
||||
|
||||
android:foreground="?android:attr/selectableItemBackgroundBorderless"
|
||||
android:layout_margin="2dp"
|
||||
android:layout_width="match_parent"
|
||||
|
|
Loading…
Reference in a new issue