mirror of
https://github.com/gotson/komga.git
synced 2025-12-20 07:23:34 +01:00
feat(api): read progress v2 for Tachiyomi
uses numberSort instead of index of book in series
This commit is contained in:
parent
202abe1f48
commit
9d92b2594d
8 changed files with 174 additions and 71 deletions
|
|
@ -17,6 +17,7 @@ import org.gotson.komga.jooq.tables.records.BookMetadataRecord
|
|||
import org.gotson.komga.jooq.tables.records.BookRecord
|
||||
import org.gotson.komga.jooq.tables.records.MediaRecord
|
||||
import org.gotson.komga.jooq.tables.records.ReadProgressRecord
|
||||
import org.jooq.AggregateFunction
|
||||
import org.jooq.Condition
|
||||
import org.jooq.DSLContext
|
||||
import org.jooq.Record
|
||||
|
|
@ -31,6 +32,7 @@ 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.math.BigDecimal
|
||||
import java.net.URL
|
||||
|
||||
@Component
|
||||
|
|
@ -48,6 +50,10 @@ class BookDtoDao(
|
|||
private val rlb = Tables.READLIST_BOOK
|
||||
private val bt = Tables.BOOK_METADATA_TAG
|
||||
|
||||
private val countUnread: AggregateFunction<BigDecimal> = DSL.sum(DSL.`when`(r.COMPLETED.isNull, 1).otherwise(0))
|
||||
private val countRead: AggregateFunction<BigDecimal> = DSL.sum(DSL.`when`(r.COMPLETED.isTrue, 1).otherwise(0))
|
||||
private val countInProgress: AggregateFunction<BigDecimal> = DSL.sum(DSL.`when`(r.COMPLETED.isFalse, 1).otherwise(0))
|
||||
|
||||
private val sorts = mapOf(
|
||||
"name" to b.NAME.collate(SqliteUdfDataSource.collationUnicode3),
|
||||
"created" to b.CREATED_DATE,
|
||||
|
|
@ -169,9 +175,9 @@ class BookDtoDao(
|
|||
.leftJoin(r).on(b.ID.eq(r.BOOK_ID)).and(readProgressCondition(userId))
|
||||
.apply { filterOnLibraryIds?.let { where(s.LIBRARY_ID.`in`(it)) } }
|
||||
.groupBy(s.ID)
|
||||
.having(SeriesDtoDao.countUnread.ge(inline(1.toBigDecimal())))
|
||||
.and(SeriesDtoDao.countRead.ge(inline(1.toBigDecimal())))
|
||||
.and(SeriesDtoDao.countInProgress.eq(inline(0.toBigDecimal())))
|
||||
.having(countUnread.ge(inline(1.toBigDecimal())))
|
||||
.and(countRead.ge(inline(1.toBigDecimal())))
|
||||
.and(countInProgress.eq(inline(0.toBigDecimal())))
|
||||
.orderBy(DSL.max(r.LAST_MODIFIED_DATE).desc())
|
||||
.fetchInto(String::class.java)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +1,17 @@
|
|||
package org.gotson.komga.infrastructure.jooq
|
||||
|
||||
import org.gotson.komga.interfaces.rest.dto.TachiyomiReadProgressDto
|
||||
import org.gotson.komga.interfaces.rest.dto.TachiyomiReadProgressV2Dto
|
||||
import org.gotson.komga.interfaces.rest.persistence.ReadProgressDtoRepository
|
||||
import org.gotson.komga.jooq.Tables
|
||||
import org.jooq.AggregateFunction
|
||||
import org.jooq.Condition
|
||||
import org.jooq.DSLContext
|
||||
import org.jooq.Record
|
||||
import org.jooq.Record2
|
||||
import org.jooq.impl.DSL
|
||||
import org.jooq.impl.DSL.rowNumber
|
||||
import org.springframework.stereotype.Component
|
||||
import java.math.BigDecimal
|
||||
|
||||
@Component
|
||||
class ReadProgressDtoDao(
|
||||
|
|
@ -20,6 +23,10 @@ class ReadProgressDtoDao(
|
|||
private val d = Tables.BOOK_METADATA
|
||||
private val r = Tables.READ_PROGRESS
|
||||
|
||||
private val countUnread: AggregateFunction<BigDecimal> = DSL.sum(DSL.`when`(r.COMPLETED.isNull, 1).otherwise(0))
|
||||
private val countRead: AggregateFunction<BigDecimal> = DSL.sum(DSL.`when`(r.COMPLETED.isTrue, 1).otherwise(0))
|
||||
private val countInProgress: AggregateFunction<BigDecimal> = DSL.sum(DSL.`when`(r.COMPLETED.isFalse, 1).otherwise(0))
|
||||
|
||||
override fun findProgressBySeries(seriesId: String, userId: String): TachiyomiReadProgressDto {
|
||||
val indexedReadProgress = dsl.select(
|
||||
rowNumber().over().orderBy(d.NUMBER_SORT),
|
||||
|
|
@ -33,19 +40,46 @@ class ReadProgressDtoDao(
|
|||
.fetch()
|
||||
.toList()
|
||||
|
||||
val booksCountRecord = dsl
|
||||
.select(SeriesDtoDao.countUnread.`as`(BOOKS_UNREAD_COUNT))
|
||||
.select(SeriesDtoDao.countRead.`as`(BOOKS_READ_COUNT))
|
||||
.select(SeriesDtoDao.countInProgress.`as`(BOOKS_IN_PROGRESS_COUNT))
|
||||
val booksCount = getSeriesBooksCount(seriesId, userId)
|
||||
|
||||
return booksCountToDto(booksCount, indexedReadProgress.lastRead() ?: 0)
|
||||
}
|
||||
|
||||
override fun findProgressV2BySeries(seriesId: String, userId: String): TachiyomiReadProgressV2Dto {
|
||||
val numberSortReadProgress = dsl.select(
|
||||
d.NUMBER_SORT,
|
||||
r.COMPLETED,
|
||||
)
|
||||
.from(b)
|
||||
.leftJoin(r).on(b.ID.eq(r.BOOK_ID)).and(readProgressCondition(userId))
|
||||
.leftJoin(d).on(b.ID.eq(d.BOOK_ID))
|
||||
.where(b.SERIES_ID.eq(seriesId))
|
||||
.orderBy(d.NUMBER_SORT)
|
||||
.fetch()
|
||||
.first()
|
||||
.toList()
|
||||
|
||||
return booksCountToDto(booksCountRecord, indexedReadProgress)
|
||||
val booksCount = getSeriesBooksCount(seriesId, userId)
|
||||
|
||||
return booksCountToDtoV2(booksCount, numberSortReadProgress.lastRead() ?: 0F)
|
||||
}
|
||||
|
||||
private fun getSeriesBooksCount(seriesId: String, userId: String) = dsl
|
||||
.select(countUnread.`as`(BOOKS_UNREAD_COUNT))
|
||||
.select(countRead.`as`(BOOKS_READ_COUNT))
|
||||
.select(countInProgress.`as`(BOOKS_IN_PROGRESS_COUNT))
|
||||
.from(b)
|
||||
.leftJoin(r).on(b.ID.eq(r.BOOK_ID)).and(readProgressCondition(userId))
|
||||
.where(b.SERIES_ID.eq(seriesId))
|
||||
.fetch()
|
||||
.first()
|
||||
.map {
|
||||
BooksCount(
|
||||
unreadCount = it.get(BOOKS_UNREAD_COUNT, Int::class.java),
|
||||
readCount = it.get(BOOKS_READ_COUNT, Int::class.java),
|
||||
inProgressCount = it.get(BOOKS_IN_PROGRESS_COUNT, Int::class.java),
|
||||
)
|
||||
}
|
||||
|
||||
override fun findProgressByReadList(readListId: String, userId: String): TachiyomiReadProgressDto {
|
||||
val indexedReadProgress = dsl.select(
|
||||
rowNumber().over().orderBy(rlb.NUMBER),
|
||||
|
|
@ -60,9 +94,9 @@ class ReadProgressDtoDao(
|
|||
.toList()
|
||||
|
||||
val booksCountRecord = dsl
|
||||
.select(SeriesDtoDao.countUnread.`as`(BOOKS_UNREAD_COUNT))
|
||||
.select(SeriesDtoDao.countRead.`as`(BOOKS_READ_COUNT))
|
||||
.select(SeriesDtoDao.countInProgress.`as`(BOOKS_IN_PROGRESS_COUNT))
|
||||
.select(countUnread.`as`(BOOKS_UNREAD_COUNT))
|
||||
.select(countRead.`as`(BOOKS_READ_COUNT))
|
||||
.select(countInProgress.`as`(BOOKS_IN_PROGRESS_COUNT))
|
||||
.from(b)
|
||||
.leftJoin(r).on(b.ID.eq(r.BOOK_ID)).and(readProgressCondition(userId))
|
||||
.leftJoin(rlb).on(b.ID.eq(rlb.BOOK_ID))
|
||||
|
|
@ -70,27 +104,46 @@ class ReadProgressDtoDao(
|
|||
.fetch()
|
||||
.first()
|
||||
|
||||
return booksCountToDto(booksCountRecord, indexedReadProgress)
|
||||
val booksCount = BooksCount(
|
||||
unreadCount = booksCountRecord.get(BOOKS_UNREAD_COUNT, Int::class.java),
|
||||
readCount = booksCountRecord.get(BOOKS_READ_COUNT, Int::class.java),
|
||||
inProgressCount = booksCountRecord.get(BOOKS_IN_PROGRESS_COUNT, Int::class.java),
|
||||
)
|
||||
|
||||
return booksCountToDto(booksCount, indexedReadProgress.lastRead() ?: 0)
|
||||
}
|
||||
|
||||
private fun booksCountToDto(booksCountRecord: Record, indexedReadProgress: List<Record2<Int, Boolean>>): TachiyomiReadProgressDto {
|
||||
val booksUnreadCount = booksCountRecord.get(BOOKS_UNREAD_COUNT, Int::class.java)
|
||||
val booksReadCount = booksCountRecord.get(BOOKS_READ_COUNT, Int::class.java)
|
||||
val booksInProgressCount = booksCountRecord.get(BOOKS_IN_PROGRESS_COUNT, Int::class.java)
|
||||
|
||||
val lastReadContinuousIndex = indexedReadProgress
|
||||
.takeWhile { it.component2() == true }
|
||||
.lastOrNull()
|
||||
?.component1() ?: 0
|
||||
|
||||
return TachiyomiReadProgressDto(
|
||||
booksCount = booksUnreadCount + booksReadCount + booksInProgressCount,
|
||||
booksUnreadCount = booksUnreadCount,
|
||||
booksInProgressCount = booksInProgressCount,
|
||||
booksReadCount = booksReadCount,
|
||||
private fun booksCountToDto(booksCount: BooksCount, lastReadContinuousIndex: Int): TachiyomiReadProgressDto =
|
||||
TachiyomiReadProgressDto(
|
||||
booksCount = booksCount.totalCount,
|
||||
booksUnreadCount = booksCount.unreadCount,
|
||||
booksInProgressCount = booksCount.inProgressCount,
|
||||
booksReadCount = booksCount.readCount,
|
||||
lastReadContinuousIndex = lastReadContinuousIndex,
|
||||
)
|
||||
}
|
||||
|
||||
private fun booksCountToDtoV2(booksCount: BooksCount, lastReadContinuousNumberSort: Float): TachiyomiReadProgressV2Dto =
|
||||
TachiyomiReadProgressV2Dto(
|
||||
booksCount = booksCount.totalCount,
|
||||
booksUnreadCount = booksCount.unreadCount,
|
||||
booksInProgressCount = booksCount.inProgressCount,
|
||||
booksReadCount = booksCount.readCount,
|
||||
lastReadContinuousNumberSort = lastReadContinuousNumberSort,
|
||||
)
|
||||
|
||||
private fun readProgressCondition(userId: String): Condition = r.USER_ID.eq(userId).or(r.USER_ID.isNull)
|
||||
|
||||
private fun <T> List<Record2<T, Boolean>>.lastRead(): T? =
|
||||
this.takeWhile { it.component2() == true }
|
||||
.lastOrNull()
|
||||
?.component1()
|
||||
|
||||
private data class BooksCount(
|
||||
val unreadCount: Int,
|
||||
val readCount: Int,
|
||||
val inProgressCount: Int,
|
||||
) {
|
||||
val totalCount
|
||||
get() = unreadCount + readCount + inProgressCount
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ import org.gotson.komga.jooq.Tables
|
|||
import org.gotson.komga.jooq.tables.records.BookMetadataAggregationRecord
|
||||
import org.gotson.komga.jooq.tables.records.SeriesMetadataRecord
|
||||
import org.gotson.komga.jooq.tables.records.SeriesRecord
|
||||
import org.jooq.AggregateFunction
|
||||
import org.jooq.Condition
|
||||
import org.jooq.DSLContext
|
||||
import org.jooq.Record
|
||||
|
|
@ -34,7 +33,6 @@ 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.math.BigDecimal
|
||||
import java.net.URL
|
||||
|
||||
private val logger = KotlinLogging.logger {}
|
||||
|
|
@ -50,22 +48,15 @@ class SeriesDtoDao(
|
|||
private val luceneHelper: LuceneHelper,
|
||||
) : SeriesDtoRepository {
|
||||
|
||||
companion object {
|
||||
private val s = Tables.SERIES
|
||||
private val d = Tables.SERIES_METADATA
|
||||
private val r = Tables.READ_PROGRESS
|
||||
private val rs = Tables.READ_PROGRESS_SERIES
|
||||
private val cs = Tables.COLLECTION_SERIES
|
||||
private val g = Tables.SERIES_METADATA_GENRE
|
||||
private val st = Tables.SERIES_METADATA_TAG
|
||||
private val bma = Tables.BOOK_METADATA_AGGREGATION
|
||||
private val bmaa = Tables.BOOK_METADATA_AGGREGATION_AUTHOR
|
||||
private val bmat = Tables.BOOK_METADATA_AGGREGATION_TAG
|
||||
|
||||
val countUnread: AggregateFunction<BigDecimal> = DSL.sum(DSL.`when`(r.COMPLETED.isNull, 1).otherwise(0))
|
||||
val countRead: AggregateFunction<BigDecimal> = DSL.sum(DSL.`when`(r.COMPLETED.isTrue, 1).otherwise(0))
|
||||
val countInProgress: AggregateFunction<BigDecimal> = DSL.sum(DSL.`when`(r.COMPLETED.isFalse, 1).otherwise(0))
|
||||
}
|
||||
private val s = Tables.SERIES
|
||||
private val d = Tables.SERIES_METADATA
|
||||
private val rs = Tables.READ_PROGRESS_SERIES
|
||||
private val cs = Tables.COLLECTION_SERIES
|
||||
private val g = Tables.SERIES_METADATA_GENRE
|
||||
private val st = Tables.SERIES_METADATA_TAG
|
||||
private val bma = Tables.BOOK_METADATA_AGGREGATION
|
||||
private val bmaa = Tables.BOOK_METADATA_AGGREGATION_AUTHOR
|
||||
private val bmat = Tables.BOOK_METADATA_AGGREGATION_TAG
|
||||
|
||||
private val groupFields = arrayOf(
|
||||
*s.fields(),
|
||||
|
|
|
|||
|
|
@ -44,6 +44,8 @@ import org.gotson.komga.interfaces.rest.dto.SeriesDto
|
|||
import org.gotson.komga.interfaces.rest.dto.SeriesMetadataUpdateDto
|
||||
import org.gotson.komga.interfaces.rest.dto.TachiyomiReadProgressDto
|
||||
import org.gotson.komga.interfaces.rest.dto.TachiyomiReadProgressUpdateDto
|
||||
import org.gotson.komga.interfaces.rest.dto.TachiyomiReadProgressUpdateV2Dto
|
||||
import org.gotson.komga.interfaces.rest.dto.TachiyomiReadProgressV2Dto
|
||||
import org.gotson.komga.interfaces.rest.dto.restrictUrl
|
||||
import org.gotson.komga.interfaces.rest.dto.toDto
|
||||
import org.gotson.komga.interfaces.rest.persistence.BookDtoRepository
|
||||
|
|
@ -81,7 +83,7 @@ import javax.validation.Valid
|
|||
private val logger = KotlinLogging.logger {}
|
||||
|
||||
@RestController
|
||||
@RequestMapping("api/v1/series", produces = [MediaType.APPLICATION_JSON_VALUE])
|
||||
@RequestMapping("api", produces = [MediaType.APPLICATION_JSON_VALUE])
|
||||
class SeriesController(
|
||||
private val taskReceiver: TaskReceiver,
|
||||
private val seriesRepository: SeriesRepository,
|
||||
|
|
@ -104,7 +106,7 @@ class SeriesController(
|
|||
`in` = ParameterIn.QUERY, name = "search_regex", schema = Schema(type = "string")
|
||||
)
|
||||
)
|
||||
@GetMapping
|
||||
@GetMapping("v1/series")
|
||||
fun getAllSeries(
|
||||
@AuthenticationPrincipal principal: KomgaPrincipal,
|
||||
@RequestParam(name = "search", required = false) searchTerm: String?,
|
||||
|
|
@ -173,7 +175,7 @@ class SeriesController(
|
|||
`in` = ParameterIn.QUERY, name = "search_regex", schema = Schema(type = "string")
|
||||
)
|
||||
)
|
||||
@GetMapping("alphabetical-groups")
|
||||
@GetMapping("v1/series/alphabetical-groups")
|
||||
fun getAlphabeticalGroups(
|
||||
@AuthenticationPrincipal principal: KomgaPrincipal,
|
||||
@RequestParam(name = "search", required = false) searchTerm: String?,
|
||||
|
|
@ -220,7 +222,7 @@ class SeriesController(
|
|||
|
||||
@Operation(description = "Return recently added or updated series.")
|
||||
@PageableWithoutSortAsQueryParam
|
||||
@GetMapping("/latest")
|
||||
@GetMapping("v1/series//latest")
|
||||
fun getLatestSeries(
|
||||
@AuthenticationPrincipal principal: KomgaPrincipal,
|
||||
@RequestParam(name = "library_id", required = false) libraryIds: List<String>?,
|
||||
|
|
@ -250,7 +252,7 @@ class SeriesController(
|
|||
|
||||
@Operation(description = "Return newly added series.")
|
||||
@PageableWithoutSortAsQueryParam
|
||||
@GetMapping("/new")
|
||||
@GetMapping("v1/series/new")
|
||||
fun getNewSeries(
|
||||
@AuthenticationPrincipal principal: KomgaPrincipal,
|
||||
@RequestParam(name = "library_id", required = false) libraryIds: List<String>?,
|
||||
|
|
@ -280,7 +282,7 @@ class SeriesController(
|
|||
|
||||
@Operation(description = "Return recently updated series, but not newly added ones.")
|
||||
@PageableWithoutSortAsQueryParam
|
||||
@GetMapping("/updated")
|
||||
@GetMapping("v1/series/updated")
|
||||
fun getUpdatedSeries(
|
||||
@AuthenticationPrincipal principal: KomgaPrincipal,
|
||||
@RequestParam(name = "library_id", required = false) libraryIds: List<String>?,
|
||||
|
|
@ -308,7 +310,7 @@ class SeriesController(
|
|||
).map { it.restrictUrl(!principal.user.roleAdmin) }
|
||||
}
|
||||
|
||||
@GetMapping("{seriesId}")
|
||||
@GetMapping("v1/series/{seriesId}")
|
||||
fun getOneSeries(
|
||||
@AuthenticationPrincipal principal: KomgaPrincipal,
|
||||
@PathVariable(name = "seriesId") id: String
|
||||
|
|
@ -319,7 +321,7 @@ class SeriesController(
|
|||
} ?: throw ResponseStatusException(HttpStatus.NOT_FOUND)
|
||||
|
||||
@ApiResponse(content = [Content(schema = Schema(type = "string", format = "binary"))])
|
||||
@GetMapping(value = ["{seriesId}/thumbnail"], produces = [MediaType.IMAGE_JPEG_VALUE])
|
||||
@GetMapping(value = ["v1/series/{seriesId}/thumbnail"], produces = [MediaType.IMAGE_JPEG_VALUE])
|
||||
fun getSeriesThumbnail(
|
||||
@AuthenticationPrincipal principal: KomgaPrincipal,
|
||||
@PathVariable(name = "seriesId") seriesId: String
|
||||
|
|
@ -334,7 +336,7 @@ class SeriesController(
|
|||
|
||||
@PageableAsQueryParam
|
||||
@AuthorsAsQueryParam
|
||||
@GetMapping("{seriesId}/books")
|
||||
@GetMapping("v1/series/{seriesId}/books")
|
||||
fun getAllBooksBySeries(
|
||||
@AuthenticationPrincipal principal: KomgaPrincipal,
|
||||
@PathVariable(name = "seriesId") seriesId: String,
|
||||
|
|
@ -375,7 +377,7 @@ class SeriesController(
|
|||
).map { it.restrictUrl(!principal.user.roleAdmin) }
|
||||
}
|
||||
|
||||
@GetMapping("{seriesId}/collections")
|
||||
@GetMapping("v1/series/{seriesId}/collections")
|
||||
fun getAllCollectionsBySeries(
|
||||
@AuthenticationPrincipal principal: KomgaPrincipal,
|
||||
@PathVariable(name = "seriesId") seriesId: String
|
||||
|
|
@ -388,7 +390,7 @@ class SeriesController(
|
|||
.map { it.toDto() }
|
||||
}
|
||||
|
||||
@PostMapping("{seriesId}/analyze")
|
||||
@PostMapping("v1/series/{seriesId}/analyze")
|
||||
@PreAuthorize("hasRole('$ROLE_ADMIN')")
|
||||
@ResponseStatus(HttpStatus.ACCEPTED)
|
||||
fun analyze(@PathVariable seriesId: String) {
|
||||
|
|
@ -397,7 +399,7 @@ class SeriesController(
|
|||
}
|
||||
}
|
||||
|
||||
@PostMapping("{seriesId}/metadata/refresh")
|
||||
@PostMapping("v1/series/{seriesId}/metadata/refresh")
|
||||
@PreAuthorize("hasRole('$ROLE_ADMIN')")
|
||||
@ResponseStatus(HttpStatus.ACCEPTED)
|
||||
fun refreshMetadata(@PathVariable seriesId: String) {
|
||||
|
|
@ -408,7 +410,7 @@ class SeriesController(
|
|||
taskReceiver.refreshSeriesLocalArtwork(seriesId, priority = HIGH_PRIORITY)
|
||||
}
|
||||
|
||||
@PatchMapping("{seriesId}/metadata")
|
||||
@PatchMapping("v1/series/{seriesId}/metadata")
|
||||
@PreAuthorize("hasRole('$ROLE_ADMIN')")
|
||||
@ResponseStatus(HttpStatus.NO_CONTENT)
|
||||
fun updateMetadata(
|
||||
|
|
@ -454,7 +456,7 @@ class SeriesController(
|
|||
} ?: throw ResponseStatusException(HttpStatus.NOT_FOUND)
|
||||
|
||||
@Operation(description = "Mark all book for series as read")
|
||||
@PostMapping("{seriesId}/read-progress")
|
||||
@PostMapping("v1/series/{seriesId}/read-progress")
|
||||
@ResponseStatus(HttpStatus.NO_CONTENT)
|
||||
fun markAsRead(
|
||||
@PathVariable seriesId: String,
|
||||
|
|
@ -468,7 +470,7 @@ class SeriesController(
|
|||
}
|
||||
|
||||
@Operation(description = "Mark all book for series as unread")
|
||||
@DeleteMapping("{seriesId}/read-progress")
|
||||
@DeleteMapping("v1/series/{seriesId}/read-progress")
|
||||
@ResponseStatus(HttpStatus.NO_CONTENT)
|
||||
fun markAsUnread(
|
||||
@PathVariable seriesId: String,
|
||||
|
|
@ -481,7 +483,8 @@ class SeriesController(
|
|||
seriesLifecycle.deleteReadProgress(seriesId, principal.user)
|
||||
}
|
||||
|
||||
@GetMapping("{seriesId}/read-progress/tachiyomi")
|
||||
@Deprecated("Use v2 for proper handling of chapter number with numberSort")
|
||||
@GetMapping("v1/series/{seriesId}/read-progress/tachiyomi")
|
||||
fun getReadProgressTachiyomi(
|
||||
@PathVariable seriesId: String,
|
||||
@AuthenticationPrincipal principal: KomgaPrincipal,
|
||||
|
|
@ -491,7 +494,18 @@ class SeriesController(
|
|||
return readProgressDtoRepository.findProgressBySeries(seriesId, principal.user.id)
|
||||
} ?: throw ResponseStatusException(HttpStatus.NOT_FOUND)
|
||||
|
||||
@PutMapping("{seriesId}/read-progress/tachiyomi")
|
||||
@GetMapping("v2/series/{seriesId}/read-progress/tachiyomi")
|
||||
fun getReadProgressTachiyomiV2(
|
||||
@PathVariable seriesId: String,
|
||||
@AuthenticationPrincipal principal: KomgaPrincipal,
|
||||
): TachiyomiReadProgressV2Dto =
|
||||
seriesRepository.getLibraryId(seriesId)?.let {
|
||||
if (!principal.user.canAccessLibrary(it)) throw ResponseStatusException(HttpStatus.FORBIDDEN)
|
||||
return readProgressDtoRepository.findProgressV2BySeries(seriesId, principal.user.id)
|
||||
} ?: throw ResponseStatusException(HttpStatus.NOT_FOUND)
|
||||
|
||||
@Deprecated("Use v2 for proper handling of chapter number with numberSort")
|
||||
@PutMapping("v1/series/{seriesId}/read-progress/tachiyomi")
|
||||
@ResponseStatus(HttpStatus.NO_CONTENT)
|
||||
fun markReadProgressTachiyomi(
|
||||
@PathVariable seriesId: String,
|
||||
|
|
@ -513,7 +527,29 @@ class SeriesController(
|
|||
}
|
||||
}
|
||||
|
||||
@GetMapping("{seriesId}/file", produces = [MediaType.APPLICATION_OCTET_STREAM_VALUE])
|
||||
@PutMapping("v2/series/{seriesId}/read-progress/tachiyomi")
|
||||
@ResponseStatus(HttpStatus.NO_CONTENT)
|
||||
fun markReadProgressTachiyomiV2(
|
||||
@PathVariable seriesId: String,
|
||||
@RequestBody readProgress: TachiyomiReadProgressUpdateV2Dto,
|
||||
@AuthenticationPrincipal principal: KomgaPrincipal,
|
||||
) {
|
||||
seriesRepository.getLibraryId(seriesId)?.let {
|
||||
if (!principal.user.canAccessLibrary(it)) throw ResponseStatusException(HttpStatus.FORBIDDEN)
|
||||
} ?: throw ResponseStatusException(HttpStatus.NOT_FOUND)
|
||||
|
||||
bookDtoRepository.findAll(
|
||||
BookSearchWithReadProgress(seriesIds = listOf(seriesId)),
|
||||
principal.user.id,
|
||||
UnpagedSorted(Sort.by(Sort.Order.asc("metadata.numberSort"))),
|
||||
).toList().filter { book -> book.metadata.numberSort <= readProgress.lastBookNumberSortRead }
|
||||
.forEach { book ->
|
||||
if (book.readProgress?.completed != true)
|
||||
bookLifecycle.markReadProgressCompleted(book.id, principal.user)
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("v1/series/{seriesId}/file", produces = [MediaType.APPLICATION_OCTET_STREAM_VALUE])
|
||||
@PreAuthorize("hasRole('$ROLE_FILE_DOWNLOAD')")
|
||||
fun getSeriesFile(
|
||||
@AuthenticationPrincipal principal: KomgaPrincipal,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ package org.gotson.komga.interfaces.rest.dto
|
|||
import com.fasterxml.jackson.annotation.JsonFormat
|
||||
import java.time.LocalDate
|
||||
import java.time.LocalDateTime
|
||||
import javax.validation.constraints.PositiveOrZero
|
||||
|
||||
data class SeriesDto(
|
||||
val id: String,
|
||||
|
|
@ -71,7 +70,3 @@ data class BookMetadataAggregationDto(
|
|||
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
|
||||
val lastModified: LocalDateTime
|
||||
)
|
||||
|
||||
data class TachiyomiReadProgressUpdateDto(
|
||||
@get:PositiveOrZero val lastBookRead: Int,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
package org.gotson.komga.interfaces.rest.dto
|
||||
|
||||
import javax.validation.constraints.PositiveOrZero
|
||||
|
||||
data class TachiyomiReadProgressUpdateDto(
|
||||
@get:PositiveOrZero val lastBookRead: Int,
|
||||
)
|
||||
|
||||
data class TachiyomiReadProgressUpdateV2Dto(
|
||||
val lastBookNumberSortRead: Float,
|
||||
)
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package org.gotson.komga.interfaces.rest.dto
|
||||
|
||||
data class TachiyomiReadProgressV2Dto(
|
||||
val booksCount: Int,
|
||||
val booksReadCount: Int,
|
||||
val booksUnreadCount: Int,
|
||||
val booksInProgressCount: Int,
|
||||
val lastReadContinuousNumberSort: Float,
|
||||
)
|
||||
|
|
@ -1,8 +1,10 @@
|
|||
package org.gotson.komga.interfaces.rest.persistence
|
||||
|
||||
import org.gotson.komga.interfaces.rest.dto.TachiyomiReadProgressDto
|
||||
import org.gotson.komga.interfaces.rest.dto.TachiyomiReadProgressV2Dto
|
||||
|
||||
interface ReadProgressDtoRepository {
|
||||
fun findProgressBySeries(seriesId: String, userId: String,): TachiyomiReadProgressDto
|
||||
fun findProgressV2BySeries(seriesId: String, userId: String,): TachiyomiReadProgressV2Dto
|
||||
fun findProgressByReadList(readListId: String, userId: String,): TachiyomiReadProgressDto
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue