From 2293ecd59d7e3bd8c56891d15793ea29999eae65 Mon Sep 17 00:00:00 2001 From: DatLag Date: Wed, 15 Nov 2023 01:09:01 +0100 Subject: [PATCH] display all search items and query search --- .../ui/custom/VerticalScrollbar.android.kt | 26 +++- .../burningseries/common/ExtendCompose.kt | 24 +++ .../ui/custom/VerticalScrollbar.kt | 6 + .../screen/initial/search/SearchComponent.kt | 7 + .../ui/screen/initial/search/SearchScreen.kt | 145 ++++++++++++++++-- .../initial/search/SearchScreenComponent.kt | 49 +++++- .../ui/screen/initial/series/SeriesScreen.kt | 9 +- .../commonMain/resources/MR/base/strings.xml | 1 + .../ui/custom/VerticalScrollbar.desktop.kt | 8 + .../ui/custom/VerticalScrollbar.ios.kt | 34 ++-- .../model/algorithm/JaroWinkler.kt | 50 ++++++ .../network/scraper/BurningSeries.kt | 8 +- 12 files changed, 320 insertions(+), 47 deletions(-) create mode 100644 model/src/commonMain/kotlin/dev/datlag/burningseries/model/algorithm/JaroWinkler.kt diff --git a/app/shared/src/androidMain/kotlin/dev/datlag/burningseries/ui/custom/VerticalScrollbar.android.kt b/app/shared/src/androidMain/kotlin/dev/datlag/burningseries/ui/custom/VerticalScrollbar.android.kt index dd6ee8ac..99dca967 100644 --- a/app/shared/src/androidMain/kotlin/dev/datlag/burningseries/ui/custom/VerticalScrollbar.android.kt +++ b/app/shared/src/androidMain/kotlin/dev/datlag/burningseries/ui/custom/VerticalScrollbar.android.kt @@ -1,5 +1,6 @@ package dev.datlag.burningseries.ui.custom +import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.grid.LazyGridState import androidx.compose.runtime.Composable import androidx.compose.runtime.remember @@ -12,7 +13,23 @@ actual fun VerticalScrollbar(adapter: ScrollbarAdapter, modifier: Modifier) { @Composable actual fun rememberScrollbarAdapter( scrollState: LazyGridState, -): ScrollbarAdapter = remember { object : ScrollbarAdapter { +): ScrollbarAdapter = remember { DEFAULT_SCROLLBAR_ADAPTER } + +@Composable +actual fun rememberScrollbarAdapter( + scrollState: LazyListState +): ScrollbarAdapter = remember { + DEFAULT_SCROLLBAR_ADAPTER +} + +actual interface ScrollbarAdapter { + actual val scrollOffset: Double + actual val contentSize: Double + actual val viewportSize: Double + actual suspend fun scrollTo(scrollOffset: Double) +} + +private val DEFAULT_SCROLLBAR_ADAPTER = object : ScrollbarAdapter { override val scrollOffset: Double get() = 0.0 override val contentSize: Double @@ -21,11 +38,4 @@ actual fun rememberScrollbarAdapter( get() = 0.0 override suspend fun scrollTo(scrollOffset: Double) { } -} } - -actual interface ScrollbarAdapter { - actual val scrollOffset: Double - actual val contentSize: Double - actual val viewportSize: Double - actual suspend fun scrollTo(scrollOffset: Double) } \ No newline at end of file diff --git a/app/shared/src/commonMain/kotlin/dev/datlag/burningseries/common/ExtendCompose.kt b/app/shared/src/commonMain/kotlin/dev/datlag/burningseries/common/ExtendCompose.kt index 2fe24d9a..302da558 100644 --- a/app/shared/src/commonMain/kotlin/dev/datlag/burningseries/common/ExtendCompose.kt +++ b/app/shared/src/commonMain/kotlin/dev/datlag/burningseries/common/ExtendCompose.kt @@ -3,6 +3,7 @@ package dev.datlag.burningseries.common import androidx.compose.animation.core.animateFloatAsState import androidx.compose.foundation.gestures.awaitFirstDown import androidx.compose.foundation.gestures.waitForUpOrCancellation +import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.grid.GridItemSpan import androidx.compose.foundation.lazy.grid.LazyGridItemScope import androidx.compose.foundation.lazy.grid.LazyGridScope @@ -15,6 +16,7 @@ import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.unit.DpSize import dev.datlag.burningseries.ui.theme.shape.DiagonalShape +import kotlin.math.max inline fun Modifier.ifTrue(predicate: Boolean, builder: Modifier.() -> Modifier) = then(if (predicate) builder() else Modifier) inline fun Modifier.ifFalse(predicate: Boolean, builder: Modifier.() -> Modifier) = then(if (!predicate) builder() else Modifier) @@ -74,4 +76,26 @@ fun DpSize.toSize(): Size { width = this.width.value, height = this.height.value ) +} + +@Composable +fun LazyListState.OnBottomReached(enabled: Boolean = true, buffer: Int = 0, block: () -> Unit) { + if (enabled) { + val maxBuffer = max(0, buffer) + + val shouldCallBlock = remember { + derivedStateOf { + val lastVisibleItem = layoutInfo.visibleItemsInfo.lastOrNull() ?: return@derivedStateOf true + lastVisibleItem.index == layoutInfo.totalItemsCount - 1 - maxBuffer + } + } + + LaunchedEffect(shouldCallBlock) { + snapshotFlow { shouldCallBlock.value }.collect { + if (it) { + block() + } + } + } + } } \ No newline at end of file diff --git a/app/shared/src/commonMain/kotlin/dev/datlag/burningseries/ui/custom/VerticalScrollbar.kt b/app/shared/src/commonMain/kotlin/dev/datlag/burningseries/ui/custom/VerticalScrollbar.kt index e8859fca..ad9b419d 100644 --- a/app/shared/src/commonMain/kotlin/dev/datlag/burningseries/ui/custom/VerticalScrollbar.kt +++ b/app/shared/src/commonMain/kotlin/dev/datlag/burningseries/ui/custom/VerticalScrollbar.kt @@ -1,5 +1,6 @@ package dev.datlag.burningseries.ui.custom +import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.grid.LazyGridState import androidx.compose.runtime.Composable import androidx.compose.runtime.remember @@ -13,6 +14,11 @@ expect fun rememberScrollbarAdapter( scrollState: LazyGridState, ): ScrollbarAdapter +@Composable +expect fun rememberScrollbarAdapter( + scrollState: LazyListState, +): ScrollbarAdapter + expect interface ScrollbarAdapter { val scrollOffset: Double val contentSize: Double diff --git a/app/shared/src/commonMain/kotlin/dev/datlag/burningseries/ui/screen/initial/search/SearchComponent.kt b/app/shared/src/commonMain/kotlin/dev/datlag/burningseries/ui/screen/initial/search/SearchComponent.kt index 85d84b46..50cc2ba4 100644 --- a/app/shared/src/commonMain/kotlin/dev/datlag/burningseries/ui/screen/initial/search/SearchComponent.kt +++ b/app/shared/src/commonMain/kotlin/dev/datlag/burningseries/ui/screen/initial/search/SearchComponent.kt @@ -1,5 +1,6 @@ package dev.datlag.burningseries.ui.screen.initial.search +import dev.datlag.burningseries.model.Genre import dev.datlag.burningseries.model.state.SearchState import dev.datlag.burningseries.ui.navigation.Component import kotlinx.coroutines.flow.StateFlow @@ -7,6 +8,12 @@ import kotlinx.coroutines.flow.StateFlow interface SearchComponent : Component { val searchState: StateFlow + val genres: StateFlow> + val canLoadMoreGenres: StateFlow + + val searchItems: StateFlow> fun retryLoadingSearch(): Any? + fun loadMoreGenres(): Any? + fun searchQuery(text: String) } \ No newline at end of file diff --git a/app/shared/src/commonMain/kotlin/dev/datlag/burningseries/ui/screen/initial/search/SearchScreen.kt b/app/shared/src/commonMain/kotlin/dev/datlag/burningseries/ui/screen/initial/search/SearchScreen.kt index 436f18eb..29e98a6f 100644 --- a/app/shared/src/commonMain/kotlin/dev/datlag/burningseries/ui/screen/initial/search/SearchScreen.kt +++ b/app/shared/src/commonMain/kotlin/dev/datlag/burningseries/ui/screen/initial/search/SearchScreen.kt @@ -1,27 +1,44 @@ package dev.datlag.burningseries.ui.screen.initial.search +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.ExperimentalAnimationApi +import androidx.compose.animation.animateContentSize import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue +import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Clear +import androidx.compose.material.icons.filled.Search +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import dev.datlag.burningseries.common.OnBottomReached import dev.datlag.burningseries.common.lifecycle.collectAsStateWithLifecycle +import dev.datlag.burningseries.common.onClick import dev.datlag.burningseries.model.state.SearchState import dev.datlag.burningseries.shared.SharedRes +import dev.datlag.burningseries.ui.custom.VerticalScrollbar +import dev.datlag.burningseries.ui.custom.rememberScrollbarAdapter import dev.datlag.burningseries.ui.custom.state.ErrorState import dev.datlag.burningseries.ui.custom.state.LoadingState +import dev.icerock.moko.resources.compose.stringResource @OptIn(ExperimentalFoundationApi::class) @Composable fun SearchScreen(component: SearchComponent) { val state by component.searchState.collectAsStateWithLifecycle() - when (val current = state) { + when (state) { is SearchState.Loading -> { LoadingState(SharedRes.strings.loading_search) } @@ -31,22 +48,116 @@ fun SearchScreen(component: SearchComponent) { } } is SearchState.Success -> { - val selectedGenre = current.genres.first() - - LazyColumn( - modifier = Modifier.fillMaxWidth() + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(2.dp) ) { - stickyHeader { - Text( - text = selectedGenre.title, - style = MaterialTheme.typography.titleLarge, - fontWeight = FontWeight.Bold - ) + val genres by component.genres.collectAsStateWithLifecycle() + val canLoadMore by component.canLoadMoreGenres.collectAsStateWithLifecycle() + val listState = rememberLazyListState() + + LazyColumn( + modifier = Modifier.weight(1F), + state = listState + ) { + stickyHeader { + SearchBar(component) + } + genres.forEach { genre -> + item { + Text( + modifier = Modifier.fillMaxWidth(), + text = genre.title, + style = MaterialTheme.typography.headlineLarge, + fontWeight = FontWeight.Bold, + textAlign = TextAlign.Center + ) + } + items(genre.items) { + Text( + modifier = Modifier.fillMaxWidth().onClick { + + }.padding(8.dp), + text = it.title, + softWrap = true, + overflow = TextOverflow.Ellipsis + ) + } + } + if (canLoadMore) { + item(key = canLoadMore) { + Row( + modifier = Modifier.fillMaxWidth().padding(16.dp), + horizontalArrangement = Arrangement.Center, + verticalAlignment = Alignment.CenterVertically + ) { + CircularProgressIndicator() + } + } + } } - items(selectedGenre.items) { - Text(text = it.title) + + VerticalScrollbar(rememberScrollbarAdapter(listState)) + + listState.OnBottomReached(canLoadMore) { + component.loadMoreGenres() + } + } + } + } +} + +@OptIn(ExperimentalMaterial3Api::class, ExperimentalAnimationApi::class) +@Composable +private fun SearchBar(component: SearchComponent) { + val items by component.searchItems.collectAsStateWithLifecycle() + var queryComp by remember { mutableStateOf(String()) } + + DockedSearchBar( + query = queryComp, + onQueryChange = { + queryComp = it + }, + onSearch = { + queryComp = it + }, + modifier = Modifier.fillMaxWidth().padding(8.dp).animateContentSize(), + active = queryComp.isNotBlank() && items.isNotEmpty(), + onActiveChange = {}, + leadingIcon = { + Icon( + imageVector = Icons.Default.Search, + contentDescription = stringResource(SharedRes.strings.search) + ) + }, + trailingIcon = { + AnimatedVisibility(visible = queryComp.isNotBlank()) { + IconButton( + onClick = { + queryComp = String() + } + ) { + Icon( + imageVector = Icons.Default.Clear, + contentDescription = stringResource(SharedRes.strings.clear) + ) } } } + ) { + items.forEach { item -> + Text( + modifier = Modifier.fillMaxWidth().onClick { + + }.padding(8.dp), + text = item.title, + softWrap = true, + overflow = TextOverflow.Ellipsis + ) + } + } + + LaunchedEffect(queryComp) { + component.searchQuery(queryComp) } } \ No newline at end of file diff --git a/app/shared/src/commonMain/kotlin/dev/datlag/burningseries/ui/screen/initial/search/SearchScreenComponent.kt b/app/shared/src/commonMain/kotlin/dev/datlag/burningseries/ui/screen/initial/search/SearchScreenComponent.kt index e7060937..23cea419 100644 --- a/app/shared/src/commonMain/kotlin/dev/datlag/burningseries/ui/screen/initial/search/SearchScreenComponent.kt +++ b/app/shared/src/commonMain/kotlin/dev/datlag/burningseries/ui/screen/initial/search/SearchScreenComponent.kt @@ -5,13 +5,14 @@ import com.arkivanov.decompose.ComponentContext import dev.datlag.burningseries.common.ioDispatcher import dev.datlag.burningseries.common.ioScope import dev.datlag.burningseries.common.launchIO +import dev.datlag.burningseries.common.runBlockingIO +import dev.datlag.burningseries.model.Genre +import dev.datlag.burningseries.model.algorithm.JaroWinkler +import dev.datlag.burningseries.model.common.safeSubList import dev.datlag.burningseries.model.state.SearchAction import dev.datlag.burningseries.model.state.SearchState import dev.datlag.burningseries.network.state.SearchStateMachine -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.flowOn -import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.flow.* import org.kodein.di.DI import org.kodein.di.instance @@ -23,6 +24,38 @@ class SearchScreenComponent( private val searchStateMachine: SearchStateMachine by di.instance() override val searchState: StateFlow = searchStateMachine.state.flowOn(ioDispatcher()).stateIn(ioScope(), SharingStarted.Lazily, SearchState.Loading) + private val allGenres = searchState.mapNotNull { it as SearchState.Success }.map { it.genres }.stateIn(ioScope(), SharingStarted.Lazily, emptyList()) + private val allItems = allGenres.map { it.flatMap { g -> g.items } } + private val maxGenres = allGenres.map { it.size } + private val loadedGenres = MutableStateFlow(1) + + override val genres: StateFlow> = combine(allGenres, loadedGenres) { t1, t2 -> + t1.safeSubList(0, t2) + }.stateIn(ioScope(), SharingStarted.Lazily, emptyList()) + + override val canLoadMoreGenres: StateFlow = combine(loadedGenres, maxGenres) { t1, t2 -> + t1 < t2 + }.stateIn(ioScope(), SharingStarted.Lazily, allGenres.value.size > loadedGenres.value) + + + private val searchQuery: MutableStateFlow = MutableStateFlow(String()) + override val searchItems: StateFlow> = combine(allItems, searchQuery) { t1, t2 -> + if (t2.isBlank()) { + emptyList() + } else { + t1.map { + when { + it.title.equals(t2, true) -> it to 1.0 + it.title.startsWith(t2, true) -> it to 0.95 + it.title.contains(t2, true) -> it to 0.9 + else -> it to JaroWinkler.distance(it.title, t2) + } + }.filter { + it.second > 0.85 + }.sortedByDescending { it.second }.map { it.first }.safeSubList(0, 10) + } + }.stateIn(ioScope(), SharingStarted.Lazily, emptyList()) + @Composable override fun render() { SearchScreen(this) @@ -31,4 +64,12 @@ class SearchScreenComponent( override fun retryLoadingSearch(): Any? = ioScope().launchIO { searchStateMachine.dispatch(SearchAction.Retry) } + + override fun loadMoreGenres(): Any? = ioScope().launchIO { + loadedGenres.update { it + 1 } + } + + override fun searchQuery(text: String) { + searchQuery.value = text.trim() + } } \ No newline at end of file diff --git a/app/shared/src/commonMain/kotlin/dev/datlag/burningseries/ui/screen/initial/series/SeriesScreen.kt b/app/shared/src/commonMain/kotlin/dev/datlag/burningseries/ui/screen/initial/series/SeriesScreen.kt index 6d4277e2..43347726 100644 --- a/app/shared/src/commonMain/kotlin/dev/datlag/burningseries/ui/screen/initial/series/SeriesScreen.kt +++ b/app/shared/src/commonMain/kotlin/dev/datlag/burningseries/ui/screen/initial/series/SeriesScreen.kt @@ -210,11 +210,16 @@ private fun CompactScreen(component: SeriesComponent) { modifier = Modifier.fillMaxSize(), contentPadding = PaddingValues(horizontal = 16.dp) ) { - item { + item(key = current.series.description) { DescriptionText(current.series.description) } - item { + item(key = listOf( + current.series.currentSeason, + current.series.currentLanguage, + current.series.seasons, + current.series.languages + )) { SeasonAndLanguageButtons( modifier = Modifier.padding(vertical = 16.dp), selectedSeason = current.series.currentSeason, diff --git a/app/shared/src/commonMain/resources/MR/base/strings.xml b/app/shared/src/commonMain/resources/MR/base/strings.xml index 30e06cd0..a58d6e9f 100644 --- a/app/shared/src/commonMain/resources/MR/base/strings.xml +++ b/app/shared/src/commonMain/resources/MR/base/strings.xml @@ -26,4 +26,5 @@ Select Language Confirm Close + Clear \ No newline at end of file diff --git a/app/shared/src/desktopMain/kotlin/dev/datlag/burningseries/ui/custom/VerticalScrollbar.desktop.kt b/app/shared/src/desktopMain/kotlin/dev/datlag/burningseries/ui/custom/VerticalScrollbar.desktop.kt index 3b7a1c5f..0a187a95 100644 --- a/app/shared/src/desktopMain/kotlin/dev/datlag/burningseries/ui/custom/VerticalScrollbar.desktop.kt +++ b/app/shared/src/desktopMain/kotlin/dev/datlag/burningseries/ui/custom/VerticalScrollbar.desktop.kt @@ -1,5 +1,6 @@ package dev.datlag.burningseries.ui.custom +import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.grid.LazyGridState import androidx.compose.runtime.Composable import androidx.compose.runtime.remember @@ -20,4 +21,11 @@ actual fun rememberScrollbarAdapter( androidx.compose.foundation.ScrollbarAdapter(scrollState) } +@Composable +actual fun rememberScrollbarAdapter( + scrollState: LazyListState +): ScrollbarAdapter = remember(scrollState) { + androidx.compose.foundation.ScrollbarAdapter(scrollState) +} + actual typealias ScrollbarAdapter = androidx.compose.foundation.v2.ScrollbarAdapter \ No newline at end of file diff --git a/app/shared/src/iosMain/kotlin/dev/datlag/burningseries/ui/custom/VerticalScrollbar.ios.kt b/app/shared/src/iosMain/kotlin/dev/datlag/burningseries/ui/custom/VerticalScrollbar.ios.kt index dd6ee8ac..c3937bcb 100644 --- a/app/shared/src/iosMain/kotlin/dev/datlag/burningseries/ui/custom/VerticalScrollbar.ios.kt +++ b/app/shared/src/iosMain/kotlin/dev/datlag/burningseries/ui/custom/VerticalScrollbar.ios.kt @@ -1,31 +1,41 @@ package dev.datlag.burningseries.ui.custom +import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.grid.LazyGridState import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.Modifier @Composable -actual fun VerticalScrollbar(adapter: ScrollbarAdapter, modifier: Modifier) { -} +actual fun VerticalScrollbar(adapter: ScrollbarAdapter, modifier: Modifier) { } @Composable actual fun rememberScrollbarAdapter( scrollState: LazyGridState, -): ScrollbarAdapter = remember { object : ScrollbarAdapter { - override val scrollOffset: Double - get() = 0.0 - override val contentSize: Double - get() = 0.0 - override val viewportSize: Double - get() = 0.0 +): ScrollbarAdapter = remember { + DEFAULT_SCROLLBAR_ADAPTER +} - override suspend fun scrollTo(scrollOffset: Double) { } -} } +@Composable +actual fun rememberScrollbarAdapter( + scrollState: LazyListState +): ScrollbarAdapter = remember { + DEFAULT_SCROLLBAR_ADAPTER +} actual interface ScrollbarAdapter { actual val scrollOffset: Double actual val contentSize: Double actual val viewportSize: Double actual suspend fun scrollTo(scrollOffset: Double) -} \ No newline at end of file +} + +private val DEFAULT_SCROLLBAR_ADAPTER = object : ScrollbarAdapter { + override val scrollOffset: Double + get() = 0.0 + override val contentSize: Double + get() = 0.0 + override val viewportSize: Double + get() = 0.0 + + override suspend fun scrollTo(scrollOffset: Double) { } \ No newline at end of file diff --git a/model/src/commonMain/kotlin/dev/datlag/burningseries/model/algorithm/JaroWinkler.kt b/model/src/commonMain/kotlin/dev/datlag/burningseries/model/algorithm/JaroWinkler.kt new file mode 100644 index 00000000..4da6a0e8 --- /dev/null +++ b/model/src/commonMain/kotlin/dev/datlag/burningseries/model/algorithm/JaroWinkler.kt @@ -0,0 +1,50 @@ +package dev.datlag.burningseries.model.algorithm + +import kotlin.math.max +import kotlin.math.min + +data object JaroWinkler { + fun distance(s1: String, s2: String): Double { + val s1Len = s1.length + val s2Len = s2.length + + if ((s1Len == 0 && s2Len == 0) || s1.equals(s2, true)) { + return 1.0 + } + + val matchDistance = max(s1Len, s2Len) / 2 - 1 + val s1Matches = BooleanArray(s1Len) + val s2Matches = BooleanArray(s2Len) + var matches = 0 + + for (i in 0 until s1Len) { + val start = max(0, i - matchDistance) + val end = min(i + matchDistance + 1, s2Len) + (start until end).find { j -> !s2Matches[j] && s1[i].equals(s2[j], true) }?.let { + s1Matches[i] = true + s2Matches[it] = true + matches++ + } + } + + if (matches == 0) { + return 0.0 + } + + var t = 0.0 + var k = 0 + + (0 until s1Len).filter { s1Matches[it] }.forEach { i -> + while (!s2Matches[k]) { + k++ + } + if (!s1[i].equals(s2[k], true)) { + t += 0.5 + } + k++ + } + + val m = matches.toDouble() + return (m / s1Len + m / s2Len + (m - t) / m) / 3.0 + } +} \ No newline at end of file diff --git a/network/src/commonMain/kotlin/dev/datlag/burningseries/network/scraper/BurningSeries.kt b/network/src/commonMain/kotlin/dev/datlag/burningseries/network/scraper/BurningSeries.kt index 1e0a1e67..277cd8af 100644 --- a/network/src/commonMain/kotlin/dev/datlag/burningseries/network/scraper/BurningSeries.kt +++ b/network/src/commonMain/kotlin/dev/datlag/burningseries/network/scraper/BurningSeries.kt @@ -233,12 +233,12 @@ data object BurningSeries { title = genre, items = element.querySelectorAll("li").mapNotNull { item -> val title = item.querySelector("a")?.textContent()?.trim() ?: String() - val href = BSUtil.normalizeHref(item.querySelector("a")?.getHref() ?: String()) + val href = BSUtil.normalizeHref(item.querySelector("a")?.getHref()?.trim() ?: String()) if (title.isNotBlank() && href.isNotBlank()) { Genre.Item( - title = title, - href = href + title = title.trim(), + href = href.trim() ) } else { null @@ -252,7 +252,7 @@ data object BurningSeries { } private suspend fun getCover(client: HttpClient, href: String): Pair { - return getCover(getDocument(client, href)) + return getCover(getDocument(client, BSUtil.commonSeriesHref(href))) } private suspend fun getCover(document: KtSoupDocument?): Pair {