mirror of
https://github.com/gotson/komga.git
synced 2025-12-06 16:42:24 +01:00
fix: don't strip accents on input data and sort series title with unicode collation
This commit is contained in:
parent
860274079d
commit
c2c697fba7
11 changed files with 37 additions and 39 deletions
|
|
@ -155,7 +155,7 @@ class SeriesLifecycle(
|
||||||
seriesMetadataRepository.insert(
|
seriesMetadataRepository.insert(
|
||||||
SeriesMetadata(
|
SeriesMetadata(
|
||||||
title = series.name,
|
title = series.name,
|
||||||
titleSort = series.name.stripAccents(),
|
titleSort = series.name,
|
||||||
seriesId = series.id,
|
seriesId = series.id,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,7 @@ class BookDtoDao(
|
||||||
private val sorts =
|
private val sorts =
|
||||||
mapOf(
|
mapOf(
|
||||||
"name" to b.NAME.collate(SqliteUdfDataSource.COLLATION_UNICODE_3),
|
"name" to b.NAME.collate(SqliteUdfDataSource.COLLATION_UNICODE_3),
|
||||||
"series" to sd.TITLE_SORT.noCase(),
|
"series" to sd.TITLE_SORT.collate(SqliteUdfDataSource.COLLATION_UNICODE_3),
|
||||||
"created" to b.CREATED_DATE,
|
"created" to b.CREATED_DATE,
|
||||||
"createdDate" to b.CREATED_DATE,
|
"createdDate" to b.CREATED_DATE,
|
||||||
"lastModified" to b.LAST_MODIFIED_DATE,
|
"lastModified" to b.LAST_MODIFIED_DATE,
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@ import org.gotson.komga.infrastructure.jooq.SeriesSearchHelper
|
||||||
import org.gotson.komga.infrastructure.jooq.csAlias
|
import org.gotson.komga.infrastructure.jooq.csAlias
|
||||||
import org.gotson.komga.infrastructure.jooq.inOrNoCondition
|
import org.gotson.komga.infrastructure.jooq.inOrNoCondition
|
||||||
import org.gotson.komga.infrastructure.jooq.insertTempStrings
|
import org.gotson.komga.infrastructure.jooq.insertTempStrings
|
||||||
import org.gotson.komga.infrastructure.jooq.noCase
|
|
||||||
import org.gotson.komga.infrastructure.jooq.selectTempStrings
|
import org.gotson.komga.infrastructure.jooq.selectTempStrings
|
||||||
import org.gotson.komga.infrastructure.jooq.sortByValues
|
import org.gotson.komga.infrastructure.jooq.sortByValues
|
||||||
import org.gotson.komga.infrastructure.jooq.toSortField
|
import org.gotson.komga.infrastructure.jooq.toSortField
|
||||||
|
|
@ -85,7 +84,7 @@ class SeriesDtoDao(
|
||||||
|
|
||||||
private val sorts =
|
private val sorts =
|
||||||
mapOf(
|
mapOf(
|
||||||
"metadata.titleSort" to d.TITLE_SORT.noCase(),
|
"metadata.titleSort" to d.TITLE_SORT.collate(SqliteUdfDataSource.COLLATION_UNICODE_3),
|
||||||
"createdDate" to s.CREATED_DATE,
|
"createdDate" to s.CREATED_DATE,
|
||||||
"created" to s.CREATED_DATE,
|
"created" to s.CREATED_DATE,
|
||||||
"lastModifiedDate" to s.LAST_MODIFIED_DATE,
|
"lastModifiedDate" to s.LAST_MODIFIED_DATE,
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@ import org.gotson.komga.infrastructure.metadata.BookMetadataProvider
|
||||||
import org.gotson.komga.infrastructure.metadata.SeriesMetadataFromBookProvider
|
import org.gotson.komga.infrastructure.metadata.SeriesMetadataFromBookProvider
|
||||||
import org.gotson.komga.infrastructure.metadata.comicrack.dto.ComicInfo
|
import org.gotson.komga.infrastructure.metadata.comicrack.dto.ComicInfo
|
||||||
import org.gotson.komga.infrastructure.metadata.comicrack.dto.Manga
|
import org.gotson.komga.infrastructure.metadata.comicrack.dto.Manga
|
||||||
import org.gotson.komga.language.stripAccents
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired
|
import org.springframework.beans.factory.annotation.Autowired
|
||||||
import org.springframework.stereotype.Service
|
import org.springframework.stereotype.Service
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
|
|
@ -142,7 +141,7 @@ class ComicInfoProvider(
|
||||||
|
|
||||||
return SeriesMetadataPatch(
|
return SeriesMetadataPatch(
|
||||||
title = series,
|
title = series,
|
||||||
titleSort = series?.stripAccents(),
|
titleSort = series,
|
||||||
status = null,
|
status = null,
|
||||||
summary = null,
|
summary = null,
|
||||||
readingDirection = readingDirection,
|
readingDirection = readingDirection,
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,6 @@ import org.gotson.komga.domain.model.SeriesMetadataPatch
|
||||||
import org.gotson.komga.infrastructure.mediacontainer.epub.getPackageFileContent
|
import org.gotson.komga.infrastructure.mediacontainer.epub.getPackageFileContent
|
||||||
import org.gotson.komga.infrastructure.metadata.BookMetadataProvider
|
import org.gotson.komga.infrastructure.metadata.BookMetadataProvider
|
||||||
import org.gotson.komga.infrastructure.metadata.SeriesMetadataFromBookProvider
|
import org.gotson.komga.infrastructure.metadata.SeriesMetadataFromBookProvider
|
||||||
import org.gotson.komga.language.stripAccents
|
|
||||||
import org.jsoup.Jsoup
|
import org.jsoup.Jsoup
|
||||||
import org.jsoup.parser.Parser
|
import org.jsoup.parser.Parser
|
||||||
import org.jsoup.safety.Safelist
|
import org.jsoup.safety.Safelist
|
||||||
|
|
@ -138,7 +137,7 @@ class EpubMetadataProvider(
|
||||||
|
|
||||||
return SeriesMetadataPatch(
|
return SeriesMetadataPatch(
|
||||||
title = series,
|
title = series,
|
||||||
titleSort = series?.stripAccents(),
|
titleSort = series,
|
||||||
status = null,
|
status = null,
|
||||||
readingDirection = direction,
|
readingDirection = direction,
|
||||||
publisher = publisher,
|
publisher = publisher,
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@ import org.gotson.komga.domain.model.Sidecar
|
||||||
import org.gotson.komga.infrastructure.metadata.SeriesMetadataProvider
|
import org.gotson.komga.infrastructure.metadata.SeriesMetadataProvider
|
||||||
import org.gotson.komga.infrastructure.metadata.mylar.dto.Status
|
import org.gotson.komga.infrastructure.metadata.mylar.dto.Status
|
||||||
import org.gotson.komga.infrastructure.sidecar.SidecarSeriesConsumer
|
import org.gotson.komga.infrastructure.sidecar.SidecarSeriesConsumer
|
||||||
import org.gotson.komga.language.stripAccents
|
|
||||||
import org.springframework.stereotype.Service
|
import org.springframework.stereotype.Service
|
||||||
import kotlin.io.path.notExists
|
import kotlin.io.path.notExists
|
||||||
import org.gotson.komga.infrastructure.metadata.mylar.dto.Series as MylarSeries
|
import org.gotson.komga.infrastructure.metadata.mylar.dto.Series as MylarSeries
|
||||||
|
|
@ -47,7 +46,7 @@ class MylarSeriesProvider(
|
||||||
|
|
||||||
return SeriesMetadataPatch(
|
return SeriesMetadataPatch(
|
||||||
title = title,
|
title = title,
|
||||||
titleSort = title.stripAccents(),
|
titleSort = title,
|
||||||
status =
|
status =
|
||||||
when (metadata.status) {
|
when (metadata.status) {
|
||||||
Status.Ended -> SeriesMetadata.Status.ENDED
|
Status.Ended -> SeriesMetadata.Status.ENDED
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,9 @@ fun LocalDateTime.notEquals(
|
||||||
precision: TemporalUnit = ChronoUnit.MILLIS,
|
precision: TemporalUnit = ChronoUnit.MILLIS,
|
||||||
) = this.truncatedTo(precision) != other.truncatedTo(precision)
|
) = this.truncatedTo(precision) != other.truncatedTo(precision)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Warning: This affects the Unicode code points of Korean Hangul.
|
||||||
|
*/
|
||||||
fun String.stripAccents(): String = StringUtils.stripAccents(this)
|
fun String.stripAccents(): String = StringUtils.stripAccents(this)
|
||||||
|
|
||||||
fun LocalDate.toDate(): Date = Date.from(this.atStartOfDay(ZoneId.of("Z")).toInstant())
|
fun LocalDate.toDate(): Date = Date.from(this.atStartOfDay(ZoneId.of("Z")).toInstant())
|
||||||
|
|
|
||||||
|
|
@ -168,33 +168,6 @@ class SeriesLifecycleTest(
|
||||||
assertThat(savedBooks.map { it.number }).containsExactly(1, 2, 3, 4, 5)
|
assertThat(savedBooks.map { it.number }).containsExactly(1, 2, 3, 4, 5)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `given series name with diacritics when creating series then diacritics are stripped from metadata titlesort`() {
|
|
||||||
// given
|
|
||||||
val series1 = makeSeries("À l'assaut", library.id)
|
|
||||||
val series2 = makeSeries("Être ou ne pas être", library.id)
|
|
||||||
val series3 = makeSeries("Écarlate", library.id)
|
|
||||||
|
|
||||||
// when
|
|
||||||
val created1 = seriesLifecycle.createSeries(series1)
|
|
||||||
val created2 = seriesLifecycle.createSeries(series2)
|
|
||||||
val created3 = seriesLifecycle.createSeries(series3)
|
|
||||||
|
|
||||||
// then
|
|
||||||
with(seriesMetadataRepository.findById(created1.id)) {
|
|
||||||
assertThat(title).isEqualTo(series1.name)
|
|
||||||
assertThat(titleSort).isEqualTo("A l'assaut")
|
|
||||||
}
|
|
||||||
with(seriesMetadataRepository.findById(created2.id)) {
|
|
||||||
assertThat(title).isEqualTo(series2.name)
|
|
||||||
assertThat(titleSort).isEqualTo("Etre ou ne pas etre")
|
|
||||||
}
|
|
||||||
with(seriesMetadataRepository.findById(created3.id)) {
|
|
||||||
assertThat(title).isEqualTo(series3.name)
|
|
||||||
assertThat(titleSort).isEqualTo("Ecarlate")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nested
|
@Nested
|
||||||
inner class Transactions {
|
inner class Transactions {
|
||||||
@Test
|
@Test
|
||||||
|
|
|
||||||
|
|
@ -127,6 +127,32 @@ class SeriesDtoDaoTest(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
inner class SortCriteria {
|
||||||
|
@Test
|
||||||
|
fun `given series when sorting by title sort then results are ordered`() {
|
||||||
|
// given
|
||||||
|
seriesLifecycle.createSeries(makeSeries("Éb", library.id))
|
||||||
|
seriesLifecycle.createSeries(makeSeries("Ea", library.id))
|
||||||
|
seriesLifecycle.createSeries(makeSeries("Ec", library.id))
|
||||||
|
|
||||||
|
searchIndexLifecycle.rebuildIndex()
|
||||||
|
Thread.sleep(500) // index rebuild is done asynchronously, and need a slight delay to be updated
|
||||||
|
|
||||||
|
// when
|
||||||
|
val found =
|
||||||
|
seriesDtoDao
|
||||||
|
.findAll(
|
||||||
|
SeriesSearch(),
|
||||||
|
SearchContext(user),
|
||||||
|
UnpagedSorted(Sort.by("metadata.titleSort")),
|
||||||
|
).content
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertThat(found.map { it.metadata.title }).containsExactly("Ea", "Éb", "Ec")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Nested
|
@Nested
|
||||||
inner class ReadProgress {
|
inner class ReadProgress {
|
||||||
@Test
|
@Test
|
||||||
|
|
|
||||||
|
|
@ -389,7 +389,7 @@ class ComicInfoProviderTest {
|
||||||
|
|
||||||
with(patch) {
|
with(patch) {
|
||||||
assertThat(title).isEqualTo("séries")
|
assertThat(title).isEqualTo("séries")
|
||||||
assertThat(titleSort).isEqualTo("series")
|
assertThat(titleSort).isEqualTo("séries")
|
||||||
assertThat(status).isNull()
|
assertThat(status).isNull()
|
||||||
assertThat(collections).containsExactlyInAnyOrder("collections", "multiple")
|
assertThat(collections).containsExactlyInAnyOrder("collections", "multiple")
|
||||||
assertThat(publisher).isEqualTo("publisher")
|
assertThat(publisher).isEqualTo("publisher")
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ class MylarSeriesProviderTest {
|
||||||
|
|
||||||
with(patch) {
|
with(patch) {
|
||||||
assertThat(title).isEqualTo("Sàndman")
|
assertThat(title).isEqualTo("Sàndman")
|
||||||
assertThat(titleSort).isEqualTo("Sandman")
|
assertThat(titleSort).isEqualTo("Sàndman")
|
||||||
assertThat(status).isEqualTo(SeriesMetadata.Status.ENDED)
|
assertThat(status).isEqualTo(SeriesMetadata.Status.ENDED)
|
||||||
assertThat(summary).isEqualTo("Sandman comics formatted")
|
assertThat(summary).isEqualTo("Sandman comics formatted")
|
||||||
assertThat(readingDirection).isNull()
|
assertThat(readingDirection).isNull()
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue