fix(opds): better titles for entries

force sort for Chunky via prepending
display series titles for books when needed
This commit is contained in:
Gauthier Roebroeck 2022-02-15 16:16:40 +08:00
parent b9c12bc228
commit 9f2808dfdc

View file

@ -218,13 +218,14 @@ class OpdsController(
@GetMapping(ROUTE_ON_DECK) @GetMapping(ROUTE_ON_DECK)
fun getOnDeck( fun getOnDeck(
@AuthenticationPrincipal principal: KomgaPrincipal, @AuthenticationPrincipal principal: KomgaPrincipal,
@RequestHeader(name = HttpHeaders.USER_AGENT, required = false, defaultValue = "") userAgent: String,
@Parameter(hidden = true) page: Pageable, @Parameter(hidden = true) page: Pageable,
): OpdsFeed { ): OpdsFeed {
val entries = bookDtoRepository.findAllOnDeck( val bookPage = bookDtoRepository.findAllOnDeck(
principal.user.id, principal.user.id,
principal.user.getAuthorizedLibraryIds(null), principal.user.getAuthorizedLibraryIds(null),
page, page,
).map { it.toOpdsEntry(mediaRepository.findById(it.id), false) } )
val builder = uriBuilder(ROUTE_ON_DECK) val builder = uriBuilder(ROUTE_ON_DECK)
@ -236,9 +237,9 @@ class OpdsController(
links = listOf( links = listOf(
OpdsLinkFeedNavigation(OpdsLinkRel.SELF, builder.toUriString()), OpdsLinkFeedNavigation(OpdsLinkRel.SELF, builder.toUriString()),
linkStart, linkStart,
*linkPage(builder, entries).toTypedArray(), *linkPage(builder, bookPage).toTypedArray(),
), ),
entries = entries.content, entries = bookPage.getEntriesWithSeriesTitle(userAgent),
) )
} }
@ -246,6 +247,7 @@ class OpdsController(
@GetMapping(ROUTE_KEEP_READING) @GetMapping(ROUTE_KEEP_READING)
fun getKeepReading( fun getKeepReading(
@AuthenticationPrincipal principal: KomgaPrincipal, @AuthenticationPrincipal principal: KomgaPrincipal,
@RequestHeader(name = HttpHeaders.USER_AGENT, required = false, defaultValue = "") userAgent: String,
@Parameter(hidden = true) page: Pageable, @Parameter(hidden = true) page: Pageable,
): OpdsFeed { ): OpdsFeed {
val pageable = PageRequest.of(page.pageNumber, page.pageSize, Sort.by(Sort.Order.desc("readProgress.readDate"))) val pageable = PageRequest.of(page.pageNumber, page.pageSize, Sort.by(Sort.Order.desc("readProgress.readDate")))
@ -257,11 +259,11 @@ class OpdsController(
deleted = false, deleted = false,
) )
val entries = bookDtoRepository.findAll( val bookPage = bookDtoRepository.findAll(
bookSearch, bookSearch,
principal.user.id, principal.user.id,
pageable, pageable,
).map { it.toOpdsEntry(mediaRepository.findById(it.id), false) } )
val builder = uriBuilder(ROUTE_ON_DECK) val builder = uriBuilder(ROUTE_ON_DECK)
@ -273,9 +275,9 @@ class OpdsController(
links = listOf( links = listOf(
OpdsLinkFeedNavigation(OpdsLinkRel.SELF, builder.toUriString()), OpdsLinkFeedNavigation(OpdsLinkRel.SELF, builder.toUriString()),
linkStart, linkStart,
*linkPage(builder, entries).toTypedArray(), *linkPage(builder, bookPage).toTypedArray(),
), ),
entries = entries.content, entries = bookPage.getEntriesWithSeriesTitle(userAgent),
) )
} }
@ -323,6 +325,7 @@ class OpdsController(
@GetMapping(ROUTE_SERIES_LATEST) @GetMapping(ROUTE_SERIES_LATEST)
fun getLatestSeries( fun getLatestSeries(
@AuthenticationPrincipal principal: KomgaPrincipal, @AuthenticationPrincipal principal: KomgaPrincipal,
@RequestHeader(name = HttpHeaders.USER_AGENT, required = false, defaultValue = "") userAgent: String,
@Parameter(hidden = true) page: Pageable, @Parameter(hidden = true) page: Pageable,
): OpdsFeed { ): OpdsFeed {
val pageable = PageRequest.of(page.pageNumber, page.pageSize, Sort.by(Sort.Order.desc("lastModified"))) val pageable = PageRequest.of(page.pageNumber, page.pageSize, Sort.by(Sort.Order.desc("lastModified")))
@ -336,6 +339,11 @@ class OpdsController(
val uriBuilder = uriBuilder(ROUTE_SERIES_LATEST) val uriBuilder = uriBuilder(ROUTE_SERIES_LATEST)
val pageIndex = seriesPage.number * seriesPage.size
val entries = seriesPage.mapIndexed { index, seriesDto ->
seriesDto.toOpdsEntry(if (shouldEnforceSort(userAgent)) pageIndex + index + 1 else null)
}
return OpdsFeedNavigation( return OpdsFeedNavigation(
id = ID_SERIES_LATEST, id = ID_SERIES_LATEST,
title = "Latest series", title = "Latest series",
@ -346,7 +354,7 @@ class OpdsController(
linkStart, linkStart,
*linkPage(uriBuilder, seriesPage).toTypedArray(), *linkPage(uriBuilder, seriesPage).toTypedArray(),
), ),
entries = seriesPage.content.map { it.toOpdsEntry() }, entries = entries,
) )
} }
@ -364,8 +372,7 @@ class OpdsController(
) )
val pageable = PageRequest.of(page.pageNumber, page.pageSize, Sort.by(Sort.Order.desc("createdDate"))) val pageable = PageRequest.of(page.pageNumber, page.pageSize, Sort.by(Sort.Order.desc("createdDate")))
val entries = bookDtoRepository.findAll(bookSearch, principal.user.id, pageable) val bookPage = bookDtoRepository.findAll(bookSearch, principal.user.id, pageable)
.map { it.toOpdsEntry(mediaRepository.findById(it.id), shouldPrependBookNumbers(userAgent)) }
val uriBuilder = uriBuilder(ROUTE_BOOKS_LATEST) val uriBuilder = uriBuilder(ROUTE_BOOKS_LATEST)
@ -377,9 +384,9 @@ class OpdsController(
links = listOf( links = listOf(
OpdsLinkFeedNavigation(OpdsLinkRel.SELF, uriBuilder.toUriString()), OpdsLinkFeedNavigation(OpdsLinkRel.SELF, uriBuilder.toUriString()),
linkStart, linkStart,
*linkPage(uriBuilder, entries).toTypedArray(), *linkPage(uriBuilder, bookPage).toTypedArray(),
), ),
entries = entries.content, entries = bookPage.getEntriesWithSeriesTitle(userAgent),
) )
} }
@ -517,7 +524,7 @@ class OpdsController(
val pageable = PageRequest.of(page.pageNumber, page.pageSize, Sort.by(Sort.Order.asc("metadata.numberSort"))) val pageable = PageRequest.of(page.pageNumber, page.pageSize, Sort.by(Sort.Order.asc("metadata.numberSort")))
val entries = bookDtoRepository.findAll(bookSearch, principal.user.id, pageable) val entries = bookDtoRepository.findAll(bookSearch, principal.user.id, pageable)
.map { it.toOpdsEntry(mediaRepository.findById(it.id), shouldPrependBookNumbers(userAgent)) } .map { bookDto -> bookDto.toOpdsEntry(mediaRepository.findById(bookDto.id)) { if (shouldEnforceSort(userAgent)) "${decimalFormat.format(it.metadata.numberSort)} - " else "" } }
val uriBuilder = uriBuilder("series/$id") val uriBuilder = uriBuilder("series/$id")
@ -575,6 +582,7 @@ class OpdsController(
@GetMapping("collections/{id}") @GetMapping("collections/{id}")
fun getOneCollection( fun getOneCollection(
@AuthenticationPrincipal principal: KomgaPrincipal, @AuthenticationPrincipal principal: KomgaPrincipal,
@RequestHeader(name = HttpHeaders.USER_AGENT, required = false, defaultValue = "") userAgent: String,
@PathVariable id: String, @PathVariable id: String,
@Parameter(hidden = true) page: Pageable, @Parameter(hidden = true) page: Pageable,
): OpdsFeed = ): OpdsFeed =
@ -591,7 +599,7 @@ class OpdsController(
val entries = seriesDtoRepository.findAllByCollectionId(collection.id, seriesSearch, principal.user.id, pageable) val entries = seriesDtoRepository.findAllByCollectionId(collection.id, seriesSearch, principal.user.id, pageable)
.map { seriesDto -> .map { seriesDto ->
val index = if (collection.ordered) collection.seriesIds.indexOf(seriesDto.id) + 1 else null val index = if (shouldEnforceSort(userAgent)) collection.seriesIds.indexOf(seriesDto.id) + 1 else null
seriesDto.toOpdsEntry(index) seriesDto.toOpdsEntry(index)
} }
@ -615,6 +623,7 @@ class OpdsController(
@GetMapping("readlists/{id}") @GetMapping("readlists/{id}")
fun getOneReadList( fun getOneReadList(
@AuthenticationPrincipal principal: KomgaPrincipal, @AuthenticationPrincipal principal: KomgaPrincipal,
@RequestHeader(name = HttpHeaders.USER_AGENT, required = false, defaultValue = "") userAgent: String,
@PathVariable id: String, @PathVariable id: String,
@Parameter(hidden = true) page: Pageable, @Parameter(hidden = true) page: Pageable,
): OpdsFeed = ): OpdsFeed =
@ -636,7 +645,7 @@ class OpdsController(
val entries = booksPage.map { bookDto -> val entries = booksPage.map { bookDto ->
val index = readList.bookIds.filterValues { it == bookDto.id }.keys.first() val index = readList.bookIds.filterValues { it == bookDto.id }.keys.first()
bookDto.toOpdsEntry(mediaRepository.findById(bookDto.id), prependNumber = false, prepend = index + 1) bookDto.toOpdsEntry(mediaRepository.findById(bookDto.id)) { "${if (shouldEnforceSort(userAgent)) "${decimalFormat.format(index + 1)} - " else ""}${it.seriesTitle} ${it.metadata.number}: " }
} }
val uriBuilder = uriBuilder("readlists/$id") val uriBuilder = uriBuilder("readlists/$id")
@ -666,7 +675,7 @@ class OpdsController(
) )
} }
private fun BookDto.toOpdsEntry(media: Media, prependNumber: Boolean, prepend: Int? = null): OpdsEntryAcquisition { private fun BookDto.toOpdsEntry(media: Media, prepend: (BookDto) -> String = { "" }): OpdsEntryAcquisition {
val mediaTypes = media.pages.map { it.mediaType }.distinct() val mediaTypes = media.pages.map { it.mediaType }.distinct()
val opdsLinkPageStreaming = if (mediaTypes.size == 1 && mediaTypes.first() in opdsPseSupportedFormats) { val opdsLinkPageStreaming = if (mediaTypes.size == 1 && mediaTypes.first() in opdsPseSupportedFormats) {
@ -675,9 +684,8 @@ class OpdsController(
OpdsLinkPageStreaming("image/jpeg", "${routeBase}books/$id/pages/{pageNumber}?convert=jpeg&zero_based=true", media.pages.size, readProgress?.page) OpdsLinkPageStreaming("image/jpeg", "${routeBase}books/$id/pages/{pageNumber}?convert=jpeg&zero_based=true", media.pages.size, readProgress?.page)
} }
val pre = prepend?.let { decimalFormat.format(it) + " - " } ?: ""
return OpdsEntryAcquisition( return OpdsEntryAcquisition(
title = "$pre${if (prependNumber) "${decimalFormat.format(metadata.numberSort)} - " else ""}${metadata.title}", title = "${prepend(this)}${metadata.title}",
updated = lastModified.toCurrentTimeZone().atZone(ZoneId.systemDefault()) ?: ZonedDateTime.now(), updated = lastModified.toCurrentTimeZone().atZone(ZoneId.systemDefault()) ?: ZonedDateTime.now(),
id = id, id = id,
content = buildString { content = buildString {
@ -721,7 +729,14 @@ class OpdsController(
link = OpdsLinkFeedNavigation(OpdsLinkRel.SUBSECTION, uriBuilder("readlists/$id").toUriString()), link = OpdsLinkFeedNavigation(OpdsLinkRel.SUBSECTION, uriBuilder("readlists/$id").toUriString()),
) )
private fun shouldPrependBookNumbers(userAgent: String) = private fun Page<BookDto>.getEntriesWithSeriesTitle(userAgent: String): List<OpdsEntryAcquisition> {
val pageIndex = (this.number * this.size)
return this.mapIndexed { index, bookDto ->
bookDto.toOpdsEntry(mediaRepository.findById(bookDto.id)) { "${if (shouldEnforceSort(userAgent)) "${pageIndex + index + 1} - " else ""}${it.seriesTitle} ${it.metadata.number}: " }
}
}
private fun shouldEnforceSort(userAgent: String) =
userAgent.contains("chunky", ignoreCase = true) userAgent.contains("chunky", ignoreCase = true)
private fun sanitize(fileName: String): String = private fun sanitize(fileName: String): String =