From dd7bc9cbf63c51a7b9655d26d7c69c005f13417c Mon Sep 17 00:00:00 2001 From: Gauthier Roebroeck Date: Mon, 28 Sep 2020 18:35:55 +0800 Subject: [PATCH] feat(opds): recently added books closes #327 --- .../domain/persistence/BookRepository.kt | 3 ++ .../komga/infrastructure/jooq/BookDao.kt | 41 +++++++++++++++++++ .../komga/interfaces/opds/OpdsController.kt | 39 ++++++++++++++++++ 3 files changed, 83 insertions(+) diff --git a/komga/src/main/kotlin/org/gotson/komga/domain/persistence/BookRepository.kt b/komga/src/main/kotlin/org/gotson/komga/domain/persistence/BookRepository.kt index a09df5ac2..4eedce00d 100644 --- a/komga/src/main/kotlin/org/gotson/komga/domain/persistence/BookRepository.kt +++ b/komga/src/main/kotlin/org/gotson/komga/domain/persistence/BookRepository.kt @@ -2,12 +2,15 @@ package org.gotson.komga.domain.persistence import org.gotson.komga.domain.model.Book import org.gotson.komga.domain.model.BookSearch +import org.springframework.data.domain.Page +import org.springframework.data.domain.Pageable interface BookRepository { fun findByIdOrNull(bookId: String): Book? fun findBySeriesId(seriesId: String): Collection fun findAll(): Collection fun findAll(bookSearch: BookSearch): Collection + fun findAll(bookSearch: BookSearch, pageable: Pageable): Page fun getLibraryId(bookId: String): String? fun findFirstIdInSeries(seriesId: String): String? diff --git a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/BookDao.kt b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/BookDao.kt index 7803bcd66..3659dcc52 100644 --- a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/BookDao.kt +++ b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/BookDao.kt @@ -8,6 +8,11 @@ import org.gotson.komga.jooq.tables.records.BookRecord import org.jooq.Condition import org.jooq.DSLContext import org.jooq.impl.DSL +import org.springframework.data.domain.Page +import org.springframework.data.domain.PageImpl +import org.springframework.data.domain.PageRequest +import org.springframework.data.domain.Pageable +import org.springframework.data.domain.Sort import org.springframework.stereotype.Component import java.net.URL import java.time.LocalDateTime @@ -22,6 +27,10 @@ class BookDao( private val m = Tables.MEDIA private val d = Tables.BOOK_METADATA + private val sorts = mapOf( + "createdDate" to b.CREATED_DATE + ) + override fun findByIdOrNull(bookId: String): Book? = findByIdOrNull(dsl, bookId) @@ -52,6 +61,38 @@ class BookDao( .fetchInto(b) .map { it.toDomain() } + override fun findAll(bookSearch: BookSearch, pageable: Pageable): Page { + val conditions = bookSearch.toCondition() + + val count = dsl.selectCount() + .from(b) + .leftJoin(d).on(b.ID.eq(d.BOOK_ID)) + .leftJoin(m).on(b.ID.eq(m.BOOK_ID)) + .where(conditions) + .fetchOne(0, Long::class.java) + + val orderBy = pageable.sort.toOrderBy(sorts) + + + val items = dsl.select(*b.fields()) + .from(b) + .leftJoin(d).on(b.ID.eq(d.BOOK_ID)) + .leftJoin(m).on(b.ID.eq(m.BOOK_ID)) + .where(conditions) + .orderBy(orderBy) + .apply { if (pageable.isPaged) limit(pageable.pageSize).offset(pageable.offset) } + .fetchInto(b) + .map { it.toDomain() } + + val pageSort = if (orderBy.size > 1) pageable.sort else Sort.unsorted() + return PageImpl( + items, + if (pageable.isPaged) PageRequest.of(pageable.pageNumber, pageable.pageSize, pageSort) + else PageRequest.of(0, maxOf(count.toInt(), 20), pageSort), + count.toLong() + ) + } + override fun getLibraryId(bookId: String): String? = dsl.select(b.LIBRARY_ID) diff --git a/komga/src/main/kotlin/org/gotson/komga/interfaces/opds/OpdsController.kt b/komga/src/main/kotlin/org/gotson/komga/interfaces/opds/OpdsController.kt index aca0aba0a..89b91c7a7 100644 --- a/komga/src/main/kotlin/org/gotson/komga/interfaces/opds/OpdsController.kt +++ b/komga/src/main/kotlin/org/gotson/komga/interfaces/opds/OpdsController.kt @@ -36,6 +36,7 @@ import org.gotson.komga.interfaces.opds.dto.OpdsLinkPageStreaming import org.gotson.komga.interfaces.opds.dto.OpdsLinkRel import org.gotson.komga.interfaces.opds.dto.OpdsLinkSearch import org.gotson.komga.interfaces.opds.dto.OpenSearchDescription +import org.springframework.data.domain.PageRequest import org.springframework.data.domain.Sort import org.springframework.http.HttpHeaders import org.springframework.http.HttpStatus @@ -60,6 +61,7 @@ private const val ROUTE_BASE = "/opds/v1.2/" private const val ROUTE_CATALOG = "catalog" private const val ROUTE_SERIES_ALL = "series" private const val ROUTE_SERIES_LATEST = "series/latest" +private const val ROUTE_BOOKS_LATEST = "books/latest" private const val ROUTE_LIBRARIES_ALL = "libraries" private const val ROUTE_COLLECTIONS_ALL = "collections" private const val ROUTE_READLISTS_ALL = "readlists" @@ -68,6 +70,7 @@ private const val ROUTE_SEARCH = "search" private const val ID_SERIES_ALL = "allSeries" private const val ID_SERIES_LATEST = "latestSeries" +private const val ID_BOOKS_LATEST = "latestBooks" private const val ID_LIBRARIES_ALL = "allLibraries" private const val ID_COLLECTIONS_ALL = "allCollections" private const val ID_READLISTS_ALL = "allReadLists" @@ -123,6 +126,13 @@ class OpdsController( content = "Browse latest series", link = OpdsLinkFeedNavigation(OpdsLinkRel.SUBSECTION, "$routeBase$ROUTE_SERIES_LATEST") ), + OpdsEntryNavigation( + title = "Latest books", + updated = ZonedDateTime.now(), + id = ID_BOOKS_LATEST, + content = "Browse latest books", + link = OpdsLinkFeedNavigation(OpdsLinkRel.SUBSECTION, "$routeBase$ROUTE_BOOKS_LATEST") + ), OpdsEntryNavigation( title = "All libraries", updated = ZonedDateTime.now(), @@ -223,6 +233,35 @@ class OpdsController( ) } + @GetMapping(ROUTE_BOOKS_LATEST) + fun getLatestBooks( + @AuthenticationPrincipal principal: KomgaPrincipal, + @RequestHeader(name = HttpHeaders.USER_AGENT, required = false, defaultValue = "") userAgent: String + ): OpdsFeed { + val bookSearch = BookSearch( + libraryIds = principal.user.getAuthorizedLibraryIds(null), + mediaStatus = setOf(Media.Status.READY) + ) + val pageRequest = PageRequest.of(0, 50, Sort.by(Sort.Order.desc("createdDate"))) + + val entries = bookRepository.findAll(bookSearch, pageRequest) + .map { BookWithInfo(it, mediaRepository.findById(it.id), bookMetadataRepository.findById(it.id)) } + .content + .map { it.toOpdsEntry(shouldPrependBookNumbers(userAgent)) } + + return OpdsFeedAcquisition( + id = ID_BOOKS_LATEST, + title = "Latest books", + updated = ZonedDateTime.now(), + author = komgaAuthor, + links = listOf( + OpdsLinkFeedNavigation(OpdsLinkRel.SELF, "$routeBase$ROUTE_BOOKS_LATEST"), + linkStart + ), + entries = entries + ) + } + @GetMapping(ROUTE_LIBRARIES_ALL) fun getLibraries( @AuthenticationPrincipal principal: KomgaPrincipal