feat(search): add Jellyfin-backed search

This commit is contained in:
2026-05-02 17:40:56 +02:00
parent 36e89f35d9
commit 6433225014
7 changed files with 208 additions and 96 deletions

View File

@@ -0,0 +1,12 @@
package hu.bbara.purefin.data
import hu.bbara.purefin.feature.search.SearchResult
import hu.bbara.purefin.model.Genre
import kotlinx.coroutines.flow.StateFlow
interface SearchManager {
val searchResult : StateFlow<List<SearchResult>>
val genres: StateFlow<Set<Genre>>
fun setGenres(genres : Set<String>)
fun setSearchTerm(searchTerm: String)
}

View File

@@ -1,65 +1,31 @@
package hu.bbara.purefin.feature.search
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import hu.bbara.purefin.data.LocalMediaRepository
import hu.bbara.purefin.data.UserSessionRepository
import hu.bbara.purefin.image.ArtworkKind
import hu.bbara.purefin.image.ImageUrlBuilder
import hu.bbara.purefin.data.SearchManager
import hu.bbara.purefin.model.MediaKind
import hu.bbara.purefin.navigation.MovieDto
import hu.bbara.purefin.navigation.NavigationManager
import hu.bbara.purefin.navigation.Route
import hu.bbara.purefin.navigation.SeriesDto
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.launchIn
import java.util.UUID
import javax.inject.Inject
@HiltViewModel
@OptIn(FlowPreview::class)
class SearchViewModel @Inject constructor(
private val mediaCatalogReader: LocalMediaRepository,
private val userSessionRepository: UserSessionRepository,
private val searchManager: SearchManager,
private val navigationManager: NavigationManager,
) : ViewModel() {
val genres = mediaCatalogReader.genres
private val _searchResult = MutableStateFlow<List<SearchResult>>(emptyList())
val searchResult = _searchResult.asStateFlow()
private val query = MutableStateFlow("")
init {
combine(
query.debounce(300).distinctUntilChanged(),
mediaCatalogReader.movies,
mediaCatalogReader.series
) { currentQuery, movies, series ->
val filteredMovies = movies.filter {
it.value.title.contains(currentQuery, ignoreCase = true)
}
val filteredSeries = series.filter {
it.value.name.contains(currentQuery, ignoreCase = true)
}
_searchResult.value = filteredMovies.values.map {
SearchResult.create(it, createImageUrl(it.id))
} + filteredSeries.values.map {
SearchResult.create(it, createImageUrl(it.id))
}
}.launchIn(viewModelScope)
}
val genres = searchManager.genres
val searchResult = searchManager.searchResult
fun search(query: String) {
this.query.value = query
searchManager.setSearchTerm(query)
}
fun setSelectedGenre(genreName: String?) {
searchManager.setGenres(genreName?.let { setOf(it) } ?: emptySet())
}
fun onSearchResultSelected(searchResult: SearchResult) {
@@ -90,8 +56,4 @@ class SearchViewModel @Inject constructor(
)
}
private suspend fun createImageUrl(id: UUID) : String {
return ImageUrlBuilder.toImageUrl(userSessionRepository.serverUrl.first(), id,
ArtworkKind.PRIMARY)
}
}