feat(sanitizer): Sanitize manga titles for special characters before searching (#956)
This commit is contained in:
parent
b1773eb08f
commit
aaa1d855ff
9 changed files with 75 additions and 10 deletions
|
|
@ -32,6 +32,7 @@ import kotlinx.coroutines.launch
|
|||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.serialization.json.Json
|
||||
import mihon.domain.manga.model.toDomainManga
|
||||
import tachiyomi.core.common.util.QuerySanitizer.sanitize
|
||||
import tachiyomi.core.common.util.lang.launchIO
|
||||
import tachiyomi.core.common.util.lang.launchNonCancellable
|
||||
import tachiyomi.core.common.util.lang.withIOContext
|
||||
|
|
@ -287,7 +288,7 @@ open class FeedScreenModel(
|
|||
} else {
|
||||
itemUI.source.getSearchManga(
|
||||
1,
|
||||
itemUI.savedSearch.query.orEmpty(),
|
||||
itemUI.savedSearch.query?.sanitize().orEmpty(),
|
||||
getFilterList(itemUI.savedSearch, itemUI.source),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ import kotlinx.coroutines.launch
|
|||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.serialization.json.Json
|
||||
import mihon.domain.manga.model.toDomainManga
|
||||
import tachiyomi.core.common.util.QuerySanitizer.sanitize
|
||||
import tachiyomi.core.common.util.lang.launchIO
|
||||
import tachiyomi.core.common.util.lang.launchNonCancellable
|
||||
import tachiyomi.core.common.util.lang.withIOContext
|
||||
|
|
@ -238,7 +239,7 @@ open class SourceFeedScreenModel(
|
|||
is SourceFeedUI.Latest -> source.getLatestUpdates(1)
|
||||
is SourceFeedUI.SourceSavedSearch -> source.getSearchManga(
|
||||
page = 1,
|
||||
query = sourceFeed.savedSearch.query.orEmpty(),
|
||||
query = sourceFeed.savedSearch.query?.sanitize().orEmpty(),
|
||||
filters = getFilterList(sourceFeed.savedSearch, source),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,13 +19,13 @@ import kotlinx.coroutines.asCoroutineDispatcher
|
|||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import mihon.domain.manga.model.toDomainManga
|
||||
import tachiyomi.core.common.preference.toggle
|
||||
import tachiyomi.core.common.util.QuerySanitizer.sanitize
|
||||
import tachiyomi.domain.manga.interactor.GetManga
|
||||
import tachiyomi.domain.manga.interactor.NetworkToLocalManga
|
||||
import tachiyomi.domain.manga.model.Manga
|
||||
|
|
@ -186,7 +186,7 @@ abstract class SearchScreenModel(
|
|||
|
||||
try {
|
||||
val page = withContext(coroutineDispatcher) {
|
||||
source.getSearchManga(1, query, source.getFilterList())
|
||||
source.getSearchManga(1, query.sanitize(), source.getFilterList())
|
||||
}
|
||||
|
||||
val titles = page.mangas
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@ import kotlinx.coroutines.flow.update
|
|||
import kotlinx.coroutines.launch
|
||||
import logcat.LogPriority
|
||||
import tachiyomi.core.common.i18n.stringResource
|
||||
import tachiyomi.core.common.util.QuerySanitizer.sanitize
|
||||
import tachiyomi.core.common.util.lang.launchNonCancellable
|
||||
import tachiyomi.core.common.util.lang.withIOContext
|
||||
import tachiyomi.core.common.util.lang.withUIContext
|
||||
|
|
@ -714,7 +715,7 @@ data class TrackerSearchScreen(
|
|||
|
||||
val result = withIOContext {
|
||||
try {
|
||||
val results = tracker.search(query)
|
||||
val results = tracker.search(query.sanitize())
|
||||
Result.success(results)
|
||||
} catch (e: Throwable) {
|
||||
Result.failure(e)
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import kotlinx.coroutines.Dispatchers
|
|||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.supervisorScope
|
||||
import mihon.domain.manga.model.toDomainManga
|
||||
import tachiyomi.core.common.util.QuerySanitizer.sanitize
|
||||
import tachiyomi.domain.manga.model.Manga
|
||||
import java.util.Locale
|
||||
|
||||
|
|
@ -30,7 +31,7 @@ class SmartSearchEngine(
|
|||
query
|
||||
}
|
||||
|
||||
val searchResults = source.getSearchManga(1, builtQuery, FilterList())
|
||||
val searchResults = source.getSearchManga(1, builtQuery.sanitize(), FilterList())
|
||||
|
||||
searchResults.mangas.map {
|
||||
val cleanedMangaTitle = cleanSmartSearchTitle(it.originalTitle)
|
||||
|
|
@ -53,7 +54,7 @@ class SmartSearchEngine(
|
|||
} else {
|
||||
title
|
||||
}
|
||||
val searchResults = source.getSearchManga(1, searchQuery, FilterList())
|
||||
val searchResults = source.getSearchManga(1, searchQuery.sanitize(), FilterList())
|
||||
|
||||
if (searchResults.mangas.size == 1) {
|
||||
return@supervisorScope listOf(SearchEntry(searchResults.mangas.first(), 0.0))
|
||||
|
|
|
|||
|
|
@ -0,0 +1,58 @@
|
|||
package tachiyomi.core.common.util
|
||||
|
||||
object QuerySanitizer {
|
||||
|
||||
fun String.sanitize(removePrefix: String = ""): String {
|
||||
return trim()
|
||||
.removePrefix(removePrefix)
|
||||
.trim(*CHARACTER_TRIM_CHARS)
|
||||
.replaceSpecialChar()
|
||||
}
|
||||
|
||||
// Replaces special characters with their standard equivalents.
|
||||
private fun String.replaceSpecialChar(): String {
|
||||
return replace("’", "'")
|
||||
.replace("‘", "'")
|
||||
.replace("“", "\"")
|
||||
.replace("”", "\"")
|
||||
.replace("–", "-")
|
||||
.replace("—", "-")
|
||||
.replace("…", "...")
|
||||
}
|
||||
|
||||
private val CHARACTER_TRIM_CHARS = arrayOf(
|
||||
// Whitespace
|
||||
' ',
|
||||
'\u0009',
|
||||
'\u000A',
|
||||
'\u000B',
|
||||
'\u000C',
|
||||
'\u000D',
|
||||
'\u0020',
|
||||
'\u0085',
|
||||
'\u00A0',
|
||||
'\u1680',
|
||||
'\u2000',
|
||||
'\u2001',
|
||||
'\u2002',
|
||||
'\u2003',
|
||||
'\u2004',
|
||||
'\u2005',
|
||||
'\u2006',
|
||||
'\u2007',
|
||||
'\u2008',
|
||||
'\u2009',
|
||||
'\u200A',
|
||||
'\u2028',
|
||||
'\u2029',
|
||||
'\u202F',
|
||||
'\u205F',
|
||||
'\u3000',
|
||||
|
||||
// Separators
|
||||
'-',
|
||||
'_',
|
||||
',',
|
||||
':',
|
||||
).toCharArray()
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ import eu.kanade.tachiyomi.source.model.MangasPage
|
|||
import eu.kanade.tachiyomi.source.model.MetadataMangasPage
|
||||
import exh.metadata.metadata.RaisedSearchMetadata
|
||||
import mihon.domain.manga.model.toDomainManga
|
||||
import tachiyomi.core.common.util.QuerySanitizer.sanitize
|
||||
import tachiyomi.domain.manga.model.Manga
|
||||
|
||||
abstract class EHentaiPagingSource(
|
||||
|
|
@ -40,7 +41,7 @@ class EHentaiSearchPagingSource(
|
|||
val filters: FilterList,
|
||||
) : EHentaiPagingSource(source) {
|
||||
override suspend fun requestNextPage(currentPage: Int): MangasPage {
|
||||
return source.getSearchManga(currentPage, query, filters)
|
||||
return source.getSearchManga(currentPage, query.sanitize(), filters)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import eu.kanade.tachiyomi.source.model.MangasPage
|
|||
import eu.kanade.tachiyomi.source.model.MetadataMangasPage
|
||||
import exh.metadata.metadata.RaisedSearchMetadata
|
||||
import mihon.domain.manga.model.toDomainManga
|
||||
import tachiyomi.core.common.util.QuerySanitizer.sanitize
|
||||
import tachiyomi.core.common.util.lang.withIOContext
|
||||
import tachiyomi.domain.manga.interactor.NetworkToLocalManga
|
||||
import tachiyomi.domain.manga.model.Manga
|
||||
|
|
@ -20,7 +21,7 @@ class SourceSearchPagingSource(
|
|||
private val filters: FilterList,
|
||||
) : BaseSourcePagingSource(source) {
|
||||
override suspend fun requestNextPage(currentPage: Int): MangasPage {
|
||||
return source?.getSearchManga(currentPage, query, filters)
|
||||
return source?.getSearchManga(currentPage, query.sanitize(), filters)
|
||||
// KMK -->
|
||||
?: MangasPage(emptyList(), false)
|
||||
// KMK <--
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import kotlinx.coroutines.coroutineScope
|
|||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.supervisorScope
|
||||
import logcat.LogPriority
|
||||
import tachiyomi.core.common.util.QuerySanitizer.sanitize
|
||||
import tachiyomi.core.common.util.system.logcat
|
||||
|
||||
/**
|
||||
|
|
@ -187,7 +188,7 @@ interface CatalogueSource : Source {
|
|||
words.map { keyword ->
|
||||
launch {
|
||||
runCatching {
|
||||
getSearchManga(1, keyword, FilterList()).mangas
|
||||
getSearchManga(1, keyword.sanitize(), FilterList()).mangas
|
||||
}
|
||||
.onSuccess { if (it.isNotEmpty()) pushResults(Pair(keyword, it), false) }
|
||||
.onFailure { e ->
|
||||
|
|
|
|||
Loading…
Reference in a new issue