forked from recloudstream/cloudstream
Add tv type support
This commit is contained in:
parent
b7a11048f0
commit
bcfa3cff77
6 changed files with 203 additions and 44 deletions
|
@ -56,6 +56,7 @@ data class PluginData(
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
|
null,
|
||||||
null
|
null
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,7 +57,7 @@ data class SitePlugin(
|
||||||
// Might be used to go directly to the plugin repo in the future
|
// Might be used to go directly to the plugin repo in the future
|
||||||
@JsonProperty("repositoryUrl") val repositoryUrl: String?,
|
@JsonProperty("repositoryUrl") val repositoryUrl: String?,
|
||||||
// These types are yet to be mapped and used, ignore for now
|
// These types are yet to be mapped and used, ignore for now
|
||||||
// @JsonProperty("tvTypes") val tvTypes: List<String>?,
|
@JsonProperty("tvTypes") val tvTypes: List<String>?,
|
||||||
@JsonProperty("language") val language: String?,
|
@JsonProperty("language") val language: String?,
|
||||||
@JsonProperty("iconUrl") val iconUrl: String?,
|
@JsonProperty("iconUrl") val iconUrl: String?,
|
||||||
// Set to true to get an 18+ symbol next to the plugin
|
// Set to true to get an 18+ symbol next to the plugin
|
||||||
|
|
|
@ -3,10 +3,12 @@ package com.lagradost.cloudstream3.ui.settings.extensions
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.*
|
import android.view.*
|
||||||
|
import androidx.core.view.isVisible
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.activityViewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
import com.lagradost.cloudstream3.R
|
import com.lagradost.cloudstream3.R
|
||||||
import com.lagradost.cloudstream3.mvvm.observe
|
import com.lagradost.cloudstream3.mvvm.observe
|
||||||
|
import com.lagradost.cloudstream3.ui.home.HomeFragment.Companion.getPairList
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
|
||||||
import kotlinx.android.synthetic.main.fragment_plugins.*
|
import kotlinx.android.synthetic.main.fragment_plugins.*
|
||||||
|
|
||||||
|
@ -55,7 +57,7 @@ class PluginsFragment : Fragment() {
|
||||||
// Don't go back if active query
|
// Don't go back if active query
|
||||||
settings_toolbar?.setNavigationOnClickListener {
|
settings_toolbar?.setNavigationOnClickListener {
|
||||||
if (searchView?.isIconified == false) {
|
if (searchView?.isIconified == false) {
|
||||||
searchView?.isIconified = true
|
searchView.isIconified = true
|
||||||
} else {
|
} else {
|
||||||
activity?.onBackPressed()
|
activity?.onBackPressed()
|
||||||
}
|
}
|
||||||
|
@ -88,7 +90,7 @@ class PluginsFragment : Fragment() {
|
||||||
pluginViewModel.handlePluginAction(activity, url, it, isLocal)
|
pluginViewModel.handlePluginAction(activity, url, it, isLocal)
|
||||||
}
|
}
|
||||||
|
|
||||||
observe(pluginViewModel.plugins) {
|
observe(pluginViewModel.filteredPlugins) {
|
||||||
(plugin_recycler_view?.adapter as? PluginAdapter?)?.updateList(it)
|
(plugin_recycler_view?.adapter as? PluginAdapter?)?.updateList(it)
|
||||||
plugin_recycler_view?.scrollToPosition(0)
|
plugin_recycler_view?.scrollToPosition(0)
|
||||||
}
|
}
|
||||||
|
@ -100,6 +102,55 @@ class PluginsFragment : Fragment() {
|
||||||
} else {
|
} else {
|
||||||
pluginViewModel.updatePluginList(url)
|
pluginViewModel.updatePluginList(url)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 💀💀💀💀💀💀💀 Recyclerview when
|
||||||
|
val pairList = getPairList(
|
||||||
|
home_select_anime,
|
||||||
|
home_select_cartoons,
|
||||||
|
home_select_tv_series,
|
||||||
|
home_select_documentaries,
|
||||||
|
home_select_movies,
|
||||||
|
home_select_asian,
|
||||||
|
home_select_livestreams
|
||||||
|
)
|
||||||
|
|
||||||
|
// Copy pasted code
|
||||||
|
for ((button, validTypes) in pairList) {
|
||||||
|
val validTypesMapped = validTypes.map { it.name }
|
||||||
|
val isValid =
|
||||||
|
true //validAPIs.any { api -> validTypes.any { api.supportedTypes.contains(it) } }
|
||||||
|
button?.isVisible = isValid
|
||||||
|
if (isValid) {
|
||||||
|
fun buttonContains(): Boolean {
|
||||||
|
return pluginViewModel.tvTypes.any { validTypesMapped.contains(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
button?.isSelected = buttonContains()
|
||||||
|
button?.setOnClickListener {
|
||||||
|
pluginViewModel.tvTypes.clear()
|
||||||
|
pluginViewModel.tvTypes.addAll(validTypesMapped)
|
||||||
|
for ((otherButton, _) in pairList) {
|
||||||
|
otherButton?.isSelected = false
|
||||||
|
}
|
||||||
|
button.isSelected = true
|
||||||
|
pluginViewModel.updateFilteredPlugins()
|
||||||
|
}
|
||||||
|
|
||||||
|
button?.setOnLongClickListener {
|
||||||
|
if (!buttonContains()) {
|
||||||
|
button.isSelected = true
|
||||||
|
pluginViewModel.tvTypes.addAll(validTypesMapped)
|
||||||
|
} else {
|
||||||
|
button.isSelected = false
|
||||||
|
pluginViewModel.tvTypes.removeAll(validTypesMapped)
|
||||||
|
}
|
||||||
|
pluginViewModel.updateFilteredPlugins()
|
||||||
|
return@setOnLongClickListener true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
|
@ -24,8 +24,15 @@ import me.xdrop.fuzzywuzzy.FuzzySearch
|
||||||
typealias Plugin = Pair<String, SitePlugin>
|
typealias Plugin = Pair<String, SitePlugin>
|
||||||
|
|
||||||
class PluginsViewModel : ViewModel() {
|
class PluginsViewModel : ViewModel() {
|
||||||
private val _plugins = MutableLiveData<List<PluginViewData>>()
|
/** plugins is an unaltered list of plugins */
|
||||||
val plugins: LiveData<List<PluginViewData>> = _plugins
|
private var plugins: List<PluginViewData> = emptyList()
|
||||||
|
|
||||||
|
/** filteredPlugins is a subset of plugins following the current search query and tv type selection */
|
||||||
|
private var _filteredPlugins = MutableLiveData<List<PluginViewData>>()
|
||||||
|
var filteredPlugins: LiveData<List<PluginViewData>> = _filteredPlugins
|
||||||
|
|
||||||
|
val tvTypes = mutableListOf<String>()
|
||||||
|
private var currentQuery: String? = null
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val repositoryCache: MutableMap<String, List<Plugin>> = mutableMapOf()
|
private val repositoryCache: MutableMap<String, List<Plugin>> = mutableMapOf()
|
||||||
|
@ -161,7 +168,27 @@ class PluginsViewModel : ViewModel() {
|
||||||
PluginViewData(plugin, isDownloaded(plugin, stored))
|
PluginViewData(plugin, isDownloaded(plugin, stored))
|
||||||
}
|
}
|
||||||
|
|
||||||
_plugins.postValue(list)
|
this.plugins = list
|
||||||
|
_filteredPlugins.postValue(list.filterTvTypes().sortByQuery(currentQuery))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perhaps can be optimized?
|
||||||
|
private fun List<PluginViewData>.filterTvTypes(): List<PluginViewData> {
|
||||||
|
if (tvTypes.isEmpty()) return this
|
||||||
|
return this.filter { it.plugin.second.tvTypes?.any { type -> tvTypes.contains(type) } == true }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun List<PluginViewData>.sortByQuery(query: String?): List<PluginViewData> {
|
||||||
|
return if (query == null) {
|
||||||
|
// Return list to base state if no query
|
||||||
|
this.sortedBy { it.plugin.second.name }
|
||||||
|
} else {
|
||||||
|
this.sortedBy { -FuzzySearch.ratio(it.plugin.second.name, query) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateFilteredPlugins() {
|
||||||
|
_filteredPlugins.postValue(plugins.filterTvTypes().sortByQuery(currentQuery))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updatePluginList(repositoryUrl: String) = viewModelScope.launch {
|
fun updatePluginList(repositoryUrl: String) = viewModelScope.launch {
|
||||||
|
@ -170,20 +197,10 @@ class PluginsViewModel : ViewModel() {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun search(query: String?) {
|
fun search(query: String?) {
|
||||||
val currentPlugins = plugins.value ?: return
|
currentQuery = query
|
||||||
|
_filteredPlugins.postValue(filteredPlugins.value?.sortByQuery(query))
|
||||||
// Return list to base state if no query
|
|
||||||
val newValue = if (query == null) {
|
|
||||||
currentPlugins.sortedBy { it.plugin.second.name }
|
|
||||||
} else {
|
|
||||||
currentPlugins.sortedBy {
|
|
||||||
-FuzzySearch.ratio(it.plugin.second.name, query)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_plugins.postValue(newValue)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the list but only with the local data. Used for file management.
|
* Update the list but only with the local data. Used for file management.
|
||||||
* */
|
* */
|
||||||
|
@ -196,6 +213,7 @@ class PluginsViewModel : ViewModel() {
|
||||||
PluginViewData("" to it.toSitePlugin(), true)
|
PluginViewData("" to it.toSitePlugin(), true)
|
||||||
}
|
}
|
||||||
|
|
||||||
_plugins.postValue(downloadedPlugins)
|
plugins = downloadedPlugins
|
||||||
|
_filteredPlugins.postValue(downloadedPlugins.filterTvTypes().sortByQuery(currentQuery))
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,38 +1,125 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:id="@+id/extensions_root"
|
android:id="@+id/extensions_root"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<com.google.android.material.appbar.AppBarLayout
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
android:background="@android:color/transparent"
|
android:layout_width="match_parent"
|
||||||
android:layout_width="match_parent"
|
android:layout_height="wrap_content"
|
||||||
android:layout_height="wrap_content">
|
android:background="@android:color/transparent">
|
||||||
|
|
||||||
<com.google.android.material.appbar.MaterialToolbar
|
<com.google.android.material.appbar.MaterialToolbar
|
||||||
app:menu="@menu/repository"
|
android:id="@+id/settings_toolbar"
|
||||||
android:id="@+id/settings_toolbar"
|
android:layout_width="match_parent"
|
||||||
android:paddingTop="@dimen/navbar_height"
|
android:layout_height="wrap_content"
|
||||||
tools:title="Overlord"
|
android:background="?attr/primaryGrayBackground"
|
||||||
android:background="?attr/primaryGrayBackground"
|
android:paddingTop="@dimen/navbar_height"
|
||||||
app:navigationIconTint="?attr/iconColor"
|
app:layout_scrollFlags="scroll|enterAlways"
|
||||||
app:titleTextColor="?attr/textColor"
|
app:menu="@menu/repository"
|
||||||
app:layout_scrollFlags="scroll|enterAlways"
|
app:navigationIconTint="?attr/iconColor"
|
||||||
android:layout_width="match_parent"
|
app:titleTextColor="?attr/textColor"
|
||||||
android:layout_height="wrap_content" />
|
tools:title="Overlord" />
|
||||||
|
|
||||||
|
<HorizontalScrollView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?attr/primaryGrayBackground"
|
||||||
|
android:clipToPadding="true"
|
||||||
|
android:fadingEdge="horizontal"
|
||||||
|
android:paddingStart="10dp"
|
||||||
|
android:paddingEnd="10dp"
|
||||||
|
android:requiresFadingEdge="horizontal"
|
||||||
|
app:layout_scrollFlags="scroll|enterAlways">
|
||||||
|
|
||||||
|
<!-- Man what the fuck we need a recyclerview -->
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
<!-- android:minWidth="0dp"
|
||||||
|
app:iconTint="?attr/textColor"
|
||||||
|
android:insetLeft="0dp"
|
||||||
|
android:insetTop="0dp"
|
||||||
|
android:insetRight="0dp"
|
||||||
|
android:insetBottom="0dp"
|
||||||
|
app:iconGravity="textStart"
|
||||||
|
app:iconPadding="0dp"
|
||||||
|
android:layout_height="35dp"-->
|
||||||
|
<!-- <com.google.android.material.button.MaterialButton
|
||||||
|
android:nextFocusRight="@id/home_select_tv_series"
|
||||||
|
app:icon="@drawable/ic_baseline_close_24"
|
||||||
|
|
||||||
|
android:id="@+id/home_select_none"
|
||||||
|
style="@style/RoundedSelectableButtonIcon"/>-->
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/home_select_movies"
|
||||||
|
|
||||||
|
style="@style/RoundedSelectableButton"
|
||||||
|
android:nextFocusRight="@id/home_select_tv_series"
|
||||||
|
android:text="@string/movies" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/home_select_tv_series"
|
||||||
|
style="@style/RoundedSelectableButton"
|
||||||
|
|
||||||
|
android:nextFocusLeft="@id/home_select_movies"
|
||||||
|
android:nextFocusRight="@id/home_select_anime"
|
||||||
|
android:text="@string/tv_series" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/home_select_anime"
|
||||||
|
style="@style/RoundedSelectableButton"
|
||||||
|
|
||||||
|
android:nextFocusLeft="@id/home_select_tv_series"
|
||||||
|
android:nextFocusRight="@id/home_select_asian"
|
||||||
|
android:text="@string/anime" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/home_select_asian"
|
||||||
|
style="@style/RoundedSelectableButton"
|
||||||
|
|
||||||
|
android:nextFocusLeft="@id/home_select_anime"
|
||||||
|
android:nextFocusRight="@id/home_select_cartoons"
|
||||||
|
android:text="@string/asian_drama" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/home_select_cartoons"
|
||||||
|
style="@style/RoundedSelectableButton"
|
||||||
|
|
||||||
|
android:nextFocusLeft="@id/home_select_asian"
|
||||||
|
android:nextFocusRight="@id/home_select_documentaries"
|
||||||
|
android:text="@string/cartoons" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/home_select_documentaries"
|
||||||
|
|
||||||
|
style="@style/RoundedSelectableButton"
|
||||||
|
android:nextFocusLeft="@id/home_select_cartoons"
|
||||||
|
android:text="@string/documentaries" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/home_select_livestreams"
|
||||||
|
style="@style/RoundedSelectableButton"
|
||||||
|
android:nextFocusLeft="@id/home_select_documentaries"
|
||||||
|
android:text="@string/livestreams" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
</HorizontalScrollView>
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
android:id="@+id/plugin_recycler_view"
|
||||||
tools:listitem="@layout/repository_item"
|
android:layout_width="match_parent"
|
||||||
android:id="@+id/plugin_recycler_view"
|
android:layout_height="match_parent"
|
||||||
android:layout_width="match_parent"
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||||
android:layout_height="match_parent"
|
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
|
tools:listitem="@layout/repository_item" />
|
||||||
|
|
||||||
|
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
|
|
|
@ -31,6 +31,8 @@
|
||||||
android:paddingEnd="10dp"
|
android:paddingEnd="10dp"
|
||||||
android:requiresFadingEdge="horizontal">
|
android:requiresFadingEdge="horizontal">
|
||||||
|
|
||||||
|
<!-- Man what the fuck we need a recyclerview -->
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|
Loading…
Reference in a new issue