mirror of
https://github.com/gotson/komga.git
synced 2025-01-09 04:08:00 +08:00
feat(kobo): update changed covers on Kobo
This commit is contained in:
parent
51cd7e0ccd
commit
870afffcf3
@ -0,0 +1,2 @@
|
||||
ALTER TABLE main.SYNC_POINT_BOOK
|
||||
ADD COLUMN BOOK_THUMBNAIL_ID varchar NULL;
|
@ -17,6 +17,7 @@ data class SyncPoint(
|
||||
val fileSize: Long,
|
||||
val fileHash: String,
|
||||
val metadataLastModifiedDate: ZonedDateTime,
|
||||
val thumbnailId: String?,
|
||||
val synced: Boolean,
|
||||
)
|
||||
|
||||
|
@ -23,6 +23,8 @@ interface ThumbnailBookRepository {
|
||||
size: Int,
|
||||
): Collection<String>
|
||||
|
||||
fun existsById(thumbnailId: String): Boolean
|
||||
|
||||
fun insert(thumbnail: ThumbnailBook)
|
||||
|
||||
fun update(thumbnail: ThumbnailBook)
|
||||
|
@ -234,9 +234,11 @@ class BookLifecycle(
|
||||
}
|
||||
}
|
||||
|
||||
fun getThumbnailBytesByThumbnailId(thumbnailId: String): ByteArray? =
|
||||
thumbnailBookRepository.findByIdOrNull(thumbnailId)?.let {
|
||||
getBytesFromThumbnailBook(it)
|
||||
fun getThumbnailBytesByThumbnailId(thumbnailId: String): TypedBytes? =
|
||||
thumbnailBookRepository.findByIdOrNull(thumbnailId)?.let { thumbnail ->
|
||||
getBytesFromThumbnailBook(thumbnail)?.let { bytes ->
|
||||
TypedBytes(bytes, thumbnail.mediaType)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getBytesFromThumbnailBook(thumbnail: ThumbnailBook): ByteArray? =
|
||||
|
@ -23,6 +23,7 @@ class KoboDtoDao(
|
||||
private val d = Tables.BOOK_METADATA
|
||||
private val a = Tables.BOOK_METADATA_AUTHOR
|
||||
private val sd = Tables.SERIES_METADATA
|
||||
private val bt = Tables.THUMBNAIL_BOOK
|
||||
|
||||
override fun findBookMetadataByIds(
|
||||
bookIds: Collection<String>,
|
||||
@ -46,10 +47,12 @@ class KoboDtoDao(
|
||||
m.EPUB_IS_KEPUB,
|
||||
m.EXTENSION_CLASS,
|
||||
m.EXTENSION_VALUE_BLOB,
|
||||
bt.ID,
|
||||
).from(b)
|
||||
.leftJoin(d).on(b.ID.eq(d.BOOK_ID))
|
||||
.leftJoin(sd).on(b.SERIES_ID.eq(sd.SERIES_ID))
|
||||
.leftJoin(m).on(b.ID.eq(m.BOOK_ID))
|
||||
.leftJoin(bt).on(b.ID.eq(bt.BOOK_ID)).and(bt.SELECTED.isTrue)
|
||||
.where(d.BOOK_ID.`in`(bookIds))
|
||||
.fetch()
|
||||
|
||||
@ -58,6 +61,7 @@ class KoboDtoDao(
|
||||
val dr = rec.into(d)
|
||||
val sr = rec.into(sd)
|
||||
val mr = rec.into(m)
|
||||
val btr = rec.into(bt)
|
||||
val mediaExtension = mapper.deserializeMediaExtension(mr.extensionClass, mr.extensionValueBlob) as? MediaExtensionEpub
|
||||
|
||||
val authors =
|
||||
@ -69,7 +73,7 @@ class KoboDtoDao(
|
||||
KoboBookMetadataDto(
|
||||
contributorRoles = authors[dr.bookId].orEmpty().map { ContributorDto(it.name) },
|
||||
contributors = authors[dr.bookId].orEmpty().map { it.name },
|
||||
coverImageId = dr.bookId,
|
||||
coverImageId = btr.id,
|
||||
crossRevisionId = dr.bookId,
|
||||
// if null or empty Kobo will not update it, force it to blank
|
||||
description = dr.summary.ifEmpty { " " },
|
||||
|
@ -32,6 +32,7 @@ class SyncPointDao(
|
||||
private val b = Tables.BOOK
|
||||
private val m = Tables.MEDIA
|
||||
private val d = Tables.BOOK_METADATA
|
||||
private val bt = Tables.THUMBNAIL_BOOK
|
||||
private val r = Tables.READ_PROGRESS
|
||||
private val sd = Tables.SERIES_METADATA
|
||||
private val sp = Tables.SYNC_POINT
|
||||
@ -76,6 +77,7 @@ class SyncPointDao(
|
||||
spb.BOOK_FILE_HASH,
|
||||
spb.BOOK_METADATA_LAST_MODIFIED_DATE,
|
||||
spb.BOOK_READ_PROGRESS_LAST_MODIFIED_DATE,
|
||||
spb.BOOK_THUMBNAIL_ID,
|
||||
).select(
|
||||
dsl.select(
|
||||
DSL.`val`(syncPointId),
|
||||
@ -87,11 +89,13 @@ class SyncPointDao(
|
||||
b.FILE_HASH,
|
||||
d.LAST_MODIFIED_DATE,
|
||||
r.LAST_MODIFIED_DATE,
|
||||
bt.ID,
|
||||
).from(b)
|
||||
.join(m).on(b.ID.eq(m.BOOK_ID))
|
||||
.join(d).on(b.ID.eq(d.BOOK_ID))
|
||||
.join(sd).on(b.SERIES_ID.eq(sd.SERIES_ID))
|
||||
.leftJoin(r).on(b.ID.eq(r.BOOK_ID)).and(r.USER_ID.eq(user.id))
|
||||
.leftJoin(bt).on(b.ID.eq(bt.BOOK_ID)).and(bt.SELECTED.isTrue)
|
||||
.where(conditions),
|
||||
).execute()
|
||||
|
||||
@ -236,7 +240,8 @@ class SyncPointDao(
|
||||
spb.BOOK_FILE_LAST_MODIFIED.ne(spbFrom.BOOK_FILE_LAST_MODIFIED)
|
||||
.or(spb.BOOK_FILE_SIZE.ne(spbFrom.BOOK_FILE_SIZE))
|
||||
.or(spb.BOOK_FILE_HASH.ne(spbFrom.BOOK_FILE_HASH).and(spbFrom.BOOK_FILE_HASH.isNotNull))
|
||||
.or(spb.BOOK_METADATA_LAST_MODIFIED_DATE.ne(spbFrom.BOOK_METADATA_LAST_MODIFIED_DATE)),
|
||||
.or(spb.BOOK_METADATA_LAST_MODIFIED_DATE.ne(spbFrom.BOOK_METADATA_LAST_MODIFIED_DATE))
|
||||
.or(spb.BOOK_THUMBNAIL_ID.ne(spbFrom.BOOK_THUMBNAIL_ID)),
|
||||
)
|
||||
|
||||
return queryToPageBook(query, pageable)
|
||||
@ -266,6 +271,7 @@ class SyncPointDao(
|
||||
.and(spb.BOOK_FILE_SIZE.eq(spbFrom.BOOK_FILE_SIZE))
|
||||
.and(spb.BOOK_FILE_HASH.eq(spbFrom.BOOK_FILE_HASH).or(spbFrom.BOOK_FILE_HASH.isNull))
|
||||
.and(spb.BOOK_METADATA_LAST_MODIFIED_DATE.eq(spbFrom.BOOK_METADATA_LAST_MODIFIED_DATE))
|
||||
.and(spb.BOOK_THUMBNAIL_ID.eq(spbFrom.BOOK_THUMBNAIL_ID))
|
||||
// with changed read progress
|
||||
.and(
|
||||
spb.BOOK_READ_PROGRESS_LAST_MODIFIED_DATE.ne(spbFrom.BOOK_READ_PROGRESS_LAST_MODIFIED_DATE)
|
||||
@ -477,6 +483,7 @@ class SyncPointDao(
|
||||
fileSize = it.bookFileSize,
|
||||
fileHash = it.bookFileHash,
|
||||
metadataLastModifiedDate = it.bookMetadataLastModifiedDate.atZone(ZoneId.of("Z")),
|
||||
thumbnailId = it.bookThumbnailId,
|
||||
synced = it.synced,
|
||||
)
|
||||
}
|
||||
|
@ -83,6 +83,8 @@ class ThumbnailBookDao(
|
||||
.and(tb.HEIGHT.lt(size))
|
||||
.fetch(tb.BOOK_ID)
|
||||
|
||||
override fun existsById(thumbnailId: String): Boolean = dsl.fetchExists(tb, tb.ID.eq(thumbnailId))
|
||||
|
||||
override fun insert(thumbnail: ThumbnailBook) {
|
||||
dsl.insertInto(tb)
|
||||
.set(tb.ID, thumbnail.id)
|
||||
|
@ -22,6 +22,7 @@ import org.gotson.komga.domain.persistence.BookRepository
|
||||
import org.gotson.komga.domain.persistence.MediaRepository
|
||||
import org.gotson.komga.domain.persistence.ReadProgressRepository
|
||||
import org.gotson.komga.domain.persistence.SyncPointRepository
|
||||
import org.gotson.komga.domain.persistence.ThumbnailBookRepository
|
||||
import org.gotson.komga.domain.service.BookLifecycle
|
||||
import org.gotson.komga.domain.service.SyncPointLifecycle
|
||||
import org.gotson.komga.infrastructure.configuration.KomgaProperties
|
||||
@ -169,6 +170,7 @@ class KoboController(
|
||||
private val commonBookController: CommonBookController,
|
||||
private val bookLifecycle: BookLifecycle,
|
||||
private val bookRepository: BookRepository,
|
||||
private val thumbnailBookRepository: ThumbnailBookRepository,
|
||||
private val readProgressRepository: ReadProgressRepository,
|
||||
private val imageConverter: ImageConverter,
|
||||
private val mediaRepository: MediaRepository,
|
||||
@ -670,26 +672,26 @@ class KoboController(
|
||||
|
||||
@GetMapping(
|
||||
value = [
|
||||
"v1/books/{bookId}/thumbnail/{width}/{height}/{isGreyScale}/image.jpg",
|
||||
"v1/books/{bookId}/thumbnail/{width}/{height}/{quality}/{isGreyScale}/image.jpg",
|
||||
"v1/books/{thumbnailId}/thumbnail/{width}/{height}/{isGreyScale}/image.jpg",
|
||||
"v1/books/{thumbnailId}/thumbnail/{width}/{height}/{quality}/{isGreyScale}/image.jpg",
|
||||
],
|
||||
produces = [MediaType.IMAGE_JPEG_VALUE],
|
||||
)
|
||||
fun getBookCover(
|
||||
@AuthenticationPrincipal principal: KomgaPrincipal,
|
||||
@PathVariable bookId: String,
|
||||
@PathVariable thumbnailId: String,
|
||||
@PathVariable width: String?,
|
||||
@PathVariable height: String?,
|
||||
@PathVariable quality: String?,
|
||||
@PathVariable isGreyScale: String?,
|
||||
): ResponseEntity<Any> =
|
||||
if (!bookRepository.existsById(bookId) && koboProxy.isEnabled()) {
|
||||
if (!thumbnailBookRepository.existsById(thumbnailId) && koboProxy.isEnabled()) {
|
||||
ResponseEntity
|
||||
.status(HttpStatus.TEMPORARY_REDIRECT)
|
||||
.location(UriComponentsBuilder.fromHttpUrl(koboProxy.imageHostUrl).buildAndExpand(bookId, width, height).toUri())
|
||||
.location(UriComponentsBuilder.fromHttpUrl(koboProxy.imageHostUrl).buildAndExpand(thumbnailId, width, height).toUri())
|
||||
.build()
|
||||
} else {
|
||||
val poster = bookLifecycle.getThumbnailBytes(bookId) ?: throw ResponseStatusException(HttpStatus.NOT_FOUND)
|
||||
val poster = bookLifecycle.getThumbnailBytesByThumbnailId(thumbnailId) ?: throw ResponseStatusException(HttpStatus.NOT_FOUND)
|
||||
val posterBytes =
|
||||
if (poster.mediaType != ImageType.JPEG.mediaType)
|
||||
imageConverter.convertImage(poster.bytes, ImageType.JPEG.imageIOFormat)
|
||||
|
@ -14,7 +14,7 @@ data class KoboBookMetadataDto(
|
||||
val categories: List<String> = listOf(DUMMY_ID),
|
||||
val contributorRoles: List<ContributorDto> = emptyList(),
|
||||
val contributors: List<String> = emptyList(),
|
||||
val coverImageId: String,
|
||||
val coverImageId: String? = null,
|
||||
val crossRevisionId: String,
|
||||
val currentDisplayPrice: AmountDto = AmountDto("USD", 0),
|
||||
val currentLoveDisplayPrice: AmountDto = AmountDto(totalAmount = 0),
|
||||
|
@ -302,7 +302,7 @@ class BookController(
|
||||
): ByteArray {
|
||||
contentRestrictionChecker.checkContentRestriction(principal.user, bookId)
|
||||
|
||||
return bookLifecycle.getThumbnailBytesByThumbnailId(thumbnailId)
|
||||
return bookLifecycle.getThumbnailBytesByThumbnailId(thumbnailId)?.bytes
|
||||
?: throw ResponseStatusException(HttpStatus.NOT_FOUND)
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user