forked from recloudstream/cloudstream
Fix searching race condition with loading plugins
This commit is contained in:
parent
4832421bd2
commit
e9e4298a7a
2 changed files with 91 additions and 60 deletions
|
@ -22,11 +22,11 @@ import com.google.android.material.button.MaterialButton
|
||||||
import com.lagradost.cloudstream3.*
|
import com.lagradost.cloudstream3.*
|
||||||
import com.lagradost.cloudstream3.APIHolder.filterProviderByPreferredMedia
|
import com.lagradost.cloudstream3.APIHolder.filterProviderByPreferredMedia
|
||||||
import com.lagradost.cloudstream3.APIHolder.filterSearchResultByFilmQuality
|
import com.lagradost.cloudstream3.APIHolder.filterSearchResultByFilmQuality
|
||||||
import com.lagradost.cloudstream3.APIHolder.getApiFromName
|
|
||||||
import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull
|
import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull
|
||||||
import com.lagradost.cloudstream3.APIHolder.getApiProviderLangSettings
|
import com.lagradost.cloudstream3.APIHolder.getApiProviderLangSettings
|
||||||
import com.lagradost.cloudstream3.APIHolder.getApiSettings
|
import com.lagradost.cloudstream3.APIHolder.getApiSettings
|
||||||
import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey
|
import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey
|
||||||
|
import com.lagradost.cloudstream3.MainActivity.Companion.afterPluginsLoadedEvent
|
||||||
import com.lagradost.cloudstream3.mvvm.Resource
|
import com.lagradost.cloudstream3.mvvm.Resource
|
||||||
import com.lagradost.cloudstream3.mvvm.logError
|
import com.lagradost.cloudstream3.mvvm.logError
|
||||||
import com.lagradost.cloudstream3.mvvm.observe
|
import com.lagradost.cloudstream3.mvvm.observe
|
||||||
|
@ -36,6 +36,7 @@ import com.lagradost.cloudstream3.ui.home.HomeFragment.Companion.currentSpan
|
||||||
import com.lagradost.cloudstream3.ui.home.HomeFragment.Companion.loadHomepageList
|
import com.lagradost.cloudstream3.ui.home.HomeFragment.Companion.loadHomepageList
|
||||||
import com.lagradost.cloudstream3.ui.home.ParentItemAdapter
|
import com.lagradost.cloudstream3.ui.home.ParentItemAdapter
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings
|
||||||
|
import com.lagradost.cloudstream3.utils.Coroutines.main
|
||||||
import com.lagradost.cloudstream3.utils.DataStore.getKey
|
import com.lagradost.cloudstream3.utils.DataStore.getKey
|
||||||
import com.lagradost.cloudstream3.utils.DataStore.setKey
|
import com.lagradost.cloudstream3.utils.DataStore.setKey
|
||||||
import com.lagradost.cloudstream3.utils.SubtitleHelper
|
import com.lagradost.cloudstream3.utils.SubtitleHelper
|
||||||
|
@ -97,6 +98,16 @@ class SearchFragment : Fragment() {
|
||||||
super.onDestroyView()
|
super.onDestroyView()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
afterPluginsLoadedEvent += ::reloadRepos
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onStop() {
|
||||||
|
super.onStop()
|
||||||
|
afterPluginsLoadedEvent -= ::reloadRepos
|
||||||
|
}
|
||||||
|
|
||||||
var selectedSearchTypes = mutableListOf<TvType>()
|
var selectedSearchTypes = mutableListOf<TvType>()
|
||||||
var selectedApis = mutableSetOf<String>()
|
var selectedApis = mutableSetOf<String>()
|
||||||
|
|
||||||
|
@ -116,11 +127,73 @@ class SearchFragment : Fragment() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Null if defined as a variable
|
||||||
|
// This needs to be run after view created
|
||||||
|
private fun getPairList(): List<Pair<MaterialButton?, List<TvType>>> {
|
||||||
|
return HomeFragment.getPairList(
|
||||||
|
search_select_anime,
|
||||||
|
search_select_cartoons,
|
||||||
|
search_select_tv_series,
|
||||||
|
search_select_documentaries,
|
||||||
|
search_select_movies,
|
||||||
|
search_select_asian,
|
||||||
|
search_select_livestreams,
|
||||||
|
search_select_nsfw,
|
||||||
|
search_select_others
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun reloadRepos(success: Boolean = false) = main {
|
||||||
|
val pairList = getPairList()
|
||||||
|
|
||||||
|
searchViewModel.reloadRepos()
|
||||||
|
context?.filterProviderByPreferredMedia()?.let { validAPIs ->
|
||||||
|
for ((button, validTypes) in pairList) {
|
||||||
|
val isValid =
|
||||||
|
validAPIs.any { api -> validTypes.any { api.supportedTypes.contains(it) } }
|
||||||
|
button?.isVisible = isValid
|
||||||
|
if (isValid) {
|
||||||
|
fun buttonContains(): Boolean {
|
||||||
|
return selectedSearchTypes.any { validTypes.contains(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
button?.isSelected = buttonContains()
|
||||||
|
button?.setOnClickListener {
|
||||||
|
val last = selectedSearchTypes.toSet()
|
||||||
|
selectedSearchTypes.clear()
|
||||||
|
selectedSearchTypes.addAll(validTypes)
|
||||||
|
for ((otherButton, _) in pairList) {
|
||||||
|
otherButton?.isSelected = false
|
||||||
|
}
|
||||||
|
it?.context?.setKey(SEARCH_PREF_TAGS, selectedSearchTypes)
|
||||||
|
it?.isSelected = true
|
||||||
|
if (last != selectedSearchTypes.toSet()) // if you click the same button again the it does nothing
|
||||||
|
search(main_search?.query?.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
button?.setOnLongClickListener {
|
||||||
|
if (!buttonContains()) {
|
||||||
|
it?.isSelected = true
|
||||||
|
selectedSearchTypes.addAll(validTypes)
|
||||||
|
} else {
|
||||||
|
it?.isSelected = false
|
||||||
|
selectedSearchTypes.removeAll(validTypes)
|
||||||
|
}
|
||||||
|
it?.context?.setKey(SEARCH_PREF_TAGS, selectedSearchTypes)
|
||||||
|
search(main_search?.query?.toString())
|
||||||
|
return@setOnLongClickListener true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
context?.fixPaddingStatusbar(searchRoot)
|
context?.fixPaddingStatusbar(searchRoot)
|
||||||
fixGrid()
|
fixGrid()
|
||||||
|
reloadRepos()
|
||||||
|
|
||||||
val adapter: RecyclerView.Adapter<RecyclerView.ViewHolder>? = activity?.let {
|
val adapter: RecyclerView.Adapter<RecyclerView.ViewHolder>? = activity?.let {
|
||||||
SearchAdapter(
|
SearchAdapter(
|
||||||
|
@ -155,6 +228,7 @@ class SearchFragment : Fragment() {
|
||||||
var currentValidApis = listOf<MainAPI>()
|
var currentValidApis = listOf<MainAPI>()
|
||||||
val currentSelectedApis = if (selectedApis.isEmpty()) validAPIs.map { it.name }
|
val currentSelectedApis = if (selectedApis.isEmpty()) validAPIs.map { it.name }
|
||||||
.toMutableSet() else selectedApis
|
.toMutableSet() else selectedApis
|
||||||
|
|
||||||
val builder =
|
val builder =
|
||||||
BottomSheetDialog(ctx)
|
BottomSheetDialog(ctx)
|
||||||
|
|
||||||
|
@ -169,7 +243,8 @@ class SearchFragment : Fragment() {
|
||||||
val docs = dialog.findViewById<MaterialButton>(R.id.home_select_documentaries)
|
val docs = dialog.findViewById<MaterialButton>(R.id.home_select_documentaries)
|
||||||
val movies = dialog.findViewById<MaterialButton>(R.id.home_select_movies)
|
val movies = dialog.findViewById<MaterialButton>(R.id.home_select_movies)
|
||||||
val asian = dialog.findViewById<MaterialButton>(R.id.home_select_asian)
|
val asian = dialog.findViewById<MaterialButton>(R.id.home_select_asian)
|
||||||
val livestream = dialog.findViewById<MaterialButton>(R.id.home_select_livestreams)
|
val livestream =
|
||||||
|
dialog.findViewById<MaterialButton>(R.id.home_select_livestreams)
|
||||||
val nsfw = dialog.findViewById<MaterialButton>(R.id.home_select_nsfw)
|
val nsfw = dialog.findViewById<MaterialButton>(R.id.home_select_nsfw)
|
||||||
val other = dialog.findViewById<MaterialButton>(R.id.home_select_others)
|
val other = dialog.findViewById<MaterialButton>(R.id.home_select_others)
|
||||||
val cancelBtt = dialog.findViewById<MaterialButton>(R.id.cancel_btt)
|
val cancelBtt = dialog.findViewById<MaterialButton>(R.id.cancel_btt)
|
||||||
|
@ -221,7 +296,7 @@ class SearchFragment : Fragment() {
|
||||||
listView?.choiceMode = AbsListView.CHOICE_MODE_MULTIPLE
|
listView?.choiceMode = AbsListView.CHOICE_MODE_MULTIPLE
|
||||||
|
|
||||||
listView?.setOnItemClickListener { _, _, i, _ ->
|
listView?.setOnItemClickListener { _, _, i, _ ->
|
||||||
if (!currentValidApis.isNullOrEmpty()) {
|
if (currentValidApis.isNotEmpty()) {
|
||||||
val api = currentValidApis[i].name
|
val api = currentValidApis[i].name
|
||||||
if (currentSelectedApis.contains(api)) {
|
if (currentSelectedApis.contains(api)) {
|
||||||
listView.setItemChecked(i, false)
|
listView.setItemChecked(i, false)
|
||||||
|
@ -295,18 +370,6 @@ class SearchFragment : Fragment() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val pairList = HomeFragment.getPairList(
|
|
||||||
search_select_anime,
|
|
||||||
search_select_cartoons,
|
|
||||||
search_select_tv_series,
|
|
||||||
search_select_documentaries,
|
|
||||||
search_select_movies,
|
|
||||||
search_select_asian,
|
|
||||||
search_select_livestreams,
|
|
||||||
search_select_nsfw,
|
|
||||||
search_select_others
|
|
||||||
)
|
|
||||||
|
|
||||||
val settingsManager = context?.let { PreferenceManager.getDefaultSharedPreferences(it) }
|
val settingsManager = context?.let { PreferenceManager.getDefaultSharedPreferences(it) }
|
||||||
val isAdvancedSearch = settingsManager?.getBoolean("advanced_search", true) ?: true
|
val isAdvancedSearch = settingsManager?.getBoolean("advanced_search", true) ?: true
|
||||||
|
|
||||||
|
@ -315,6 +378,7 @@ class SearchFragment : Fragment() {
|
||||||
?.toMutableList()
|
?.toMutableList()
|
||||||
?: mutableListOf(TvType.Movie, TvType.TvSeries)
|
?: mutableListOf(TvType.Movie, TvType.TvSeries)
|
||||||
|
|
||||||
|
val pairList = getPairList()
|
||||||
fun updateSelectedList(list: MutableList<TvType>) {
|
fun updateSelectedList(list: MutableList<TvType>) {
|
||||||
selectedSearchTypes = list
|
selectedSearchTypes = list
|
||||||
for ((button, validTypes) in pairList) {
|
for ((button, validTypes) in pairList) {
|
||||||
|
@ -322,46 +386,6 @@ class SearchFragment : Fragment() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
context?.filterProviderByPreferredMedia()?.let { validAPIs ->
|
|
||||||
for ((button, validTypes) in pairList) {
|
|
||||||
val isValid =
|
|
||||||
validAPIs.any { api -> validTypes.any { api.supportedTypes.contains(it) } }
|
|
||||||
button?.isVisible = isValid
|
|
||||||
if (isValid) {
|
|
||||||
fun buttonContains(): Boolean {
|
|
||||||
return selectedSearchTypes.any { validTypes.contains(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
button?.isSelected = buttonContains()
|
|
||||||
button?.setOnClickListener {
|
|
||||||
val last = selectedSearchTypes.toSet()
|
|
||||||
selectedSearchTypes.clear()
|
|
||||||
selectedSearchTypes.addAll(validTypes)
|
|
||||||
for ((otherButton, _) in pairList) {
|
|
||||||
otherButton?.isSelected = false
|
|
||||||
}
|
|
||||||
it?.context?.setKey(SEARCH_PREF_TAGS, selectedSearchTypes)
|
|
||||||
it?.isSelected = true
|
|
||||||
if (last != selectedSearchTypes.toSet()) // if you click the same button again the it does nothing
|
|
||||||
search(main_search?.query?.toString())
|
|
||||||
}
|
|
||||||
|
|
||||||
button?.setOnLongClickListener {
|
|
||||||
if (!buttonContains()) {
|
|
||||||
it?.isSelected = true
|
|
||||||
selectedSearchTypes.addAll(validTypes)
|
|
||||||
} else {
|
|
||||||
it?.isSelected = false
|
|
||||||
selectedSearchTypes.removeAll(validTypes)
|
|
||||||
}
|
|
||||||
it?.context?.setKey(SEARCH_PREF_TAGS, selectedSearchTypes)
|
|
||||||
search(main_search?.query?.toString())
|
|
||||||
return@setOnLongClickListener true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (context?.isTrueTvSettings() == true) {
|
if (context?.isTrueTvSettings() == true) {
|
||||||
search_filter.isFocusable = true
|
search_filter.isFocusable = true
|
||||||
search_filter.isFocusableInTouchMode = true
|
search_filter.isFocusableInTouchMode = true
|
||||||
|
@ -431,8 +455,10 @@ class SearchFragment : Fragment() {
|
||||||
listLock.lock()
|
listLock.lock()
|
||||||
(search_master_recycler?.adapter as ParentItemAdapter?)?.apply {
|
(search_master_recycler?.adapter as ParentItemAdapter?)?.apply {
|
||||||
val newItems = list.map { ongoing ->
|
val newItems = list.map { ongoing ->
|
||||||
val dataList = if (ongoing.data is Resource.Success) ongoing.data.value else ArrayList()
|
val dataList =
|
||||||
val dataListFiltered = context?.filterSearchResultByFilmQuality(dataList) ?: dataList
|
if (ongoing.data is Resource.Success) ongoing.data.value else ArrayList()
|
||||||
|
val dataListFiltered =
|
||||||
|
context?.filterSearchResultByFilmQuality(dataList) ?: dataList
|
||||||
val ongoingList = HomePageList(
|
val ongoingList = HomePageList(
|
||||||
ongoing.apiName,
|
ongoing.apiName,
|
||||||
dataListFiltered
|
dataListFiltered
|
||||||
|
|
|
@ -36,7 +36,7 @@ class SearchViewModel : ViewModel() {
|
||||||
private val _currentHistory: MutableLiveData<List<SearchHistoryItem>> = MutableLiveData()
|
private val _currentHistory: MutableLiveData<List<SearchHistoryItem>> = MutableLiveData()
|
||||||
val currentHistory: LiveData<List<SearchHistoryItem>> get() = _currentHistory
|
val currentHistory: LiveData<List<SearchHistoryItem>> get() = _currentHistory
|
||||||
|
|
||||||
private val repos = apis.map { APIRepository(it) }
|
private var repos = apis.map { APIRepository(it) }
|
||||||
|
|
||||||
fun clearSearch() {
|
fun clearSearch() {
|
||||||
_searchResponse.postValue(Resource.Success(ArrayList()))
|
_searchResponse.postValue(Resource.Success(ArrayList()))
|
||||||
|
@ -45,6 +45,11 @@ class SearchViewModel : ViewModel() {
|
||||||
|
|
||||||
private var currentSearchIndex = 0
|
private var currentSearchIndex = 0
|
||||||
private var onGoingSearch: Job? = null
|
private var onGoingSearch: Job? = null
|
||||||
|
|
||||||
|
fun reloadRepos() {
|
||||||
|
repos = apis.map { APIRepository(it) }
|
||||||
|
}
|
||||||
|
|
||||||
fun searchAndCancel(
|
fun searchAndCancel(
|
||||||
query: String,
|
query: String,
|
||||||
providersActive: Set<String> = setOf(),
|
providersActive: Set<String> = setOf(),
|
||||||
|
@ -104,12 +109,12 @@ class SearchViewModel : ViewModel() {
|
||||||
(ignoreSettings || (providersActive.isEmpty() || providersActive.contains(a.name))) && (!isQuickSearch || a.hasQuickSearch)
|
(ignoreSettings || (providersActive.isEmpty() || providersActive.contains(a.name))) && (!isQuickSearch || a.hasQuickSearch)
|
||||||
}.apmap { a -> // Parallel
|
}.apmap { a -> // Parallel
|
||||||
val search = if (isQuickSearch) a.quickSearch(query) else a.search(query)
|
val search = if (isQuickSearch) a.quickSearch(query) else a.search(query)
|
||||||
if(currentSearchIndex != currentIndex) return@apmap
|
if (currentSearchIndex != currentIndex) return@apmap
|
||||||
currentList.add(OnGoingSearch(a.name, search))
|
currentList.add(OnGoingSearch(a.name, search))
|
||||||
_currentSearch.postValue(currentList)
|
_currentSearch.postValue(currentList)
|
||||||
}
|
}
|
||||||
|
|
||||||
if(currentSearchIndex != currentIndex) return@withContext // this should prevent rewrite of existing data bug
|
if (currentSearchIndex != currentIndex) return@withContext // this should prevent rewrite of existing data bug
|
||||||
|
|
||||||
_currentSearch.postValue(currentList)
|
_currentSearch.postValue(currentList)
|
||||||
val list = ArrayList<SearchResponse>()
|
val list = ArrayList<SearchResponse>()
|
||||||
|
|
Loading…
Reference in a new issue