mirror of
https://github.com/gotson/komga.git
synced 2025-12-16 13:33:49 +01:00
feat: sharing labels for series
This commit is contained in:
parent
3a218c0afa
commit
496ebb0aac
6 changed files with 83 additions and 16 deletions
|
|
@ -0,0 +1,9 @@
|
|||
CREATE TABLE SERIES_METADATA_SHARING
|
||||
(
|
||||
LABEL varchar NOT NULL,
|
||||
SERIES_ID varchar NOT NULL,
|
||||
FOREIGN KEY (SERIES_ID) REFERENCES SERIES (ID)
|
||||
);
|
||||
|
||||
alter table SERIES_METADATA
|
||||
add column SHARING_LABELS_LOCK boolean NOT NULL DEFAULT 0;
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
package org.gotson.komga.domain.model
|
||||
|
||||
import org.gotson.komga.language.lowerNotBlank
|
||||
import java.time.LocalDateTime
|
||||
|
||||
class SeriesMetadata(
|
||||
|
|
@ -14,6 +15,7 @@ class SeriesMetadata(
|
|||
genres: Set<String> = emptySet(),
|
||||
tags: Set<String> = emptySet(),
|
||||
val totalBookCount: Int? = null,
|
||||
sharingLabels: Set<String> = emptySet(),
|
||||
|
||||
val statusLock: Boolean = false,
|
||||
val titleLock: Boolean = false,
|
||||
|
|
@ -26,6 +28,7 @@ class SeriesMetadata(
|
|||
val genresLock: Boolean = false,
|
||||
val tagsLock: Boolean = false,
|
||||
val totalBookCountLock: Boolean = false,
|
||||
val sharingLabelsLock: Boolean = false,
|
||||
|
||||
val seriesId: String = "",
|
||||
|
||||
|
|
@ -37,8 +40,9 @@ class SeriesMetadata(
|
|||
val summary = summary.trim()
|
||||
val publisher = publisher.trim()
|
||||
val language = language.trim().lowercase()
|
||||
val tags = tags.map { it.lowercase().trim() }.filter { it.isNotBlank() }.toSet()
|
||||
val genres = genres.map { it.lowercase().trim() }.filter { it.isNotBlank() }.toSet()
|
||||
val tags = tags.lowerNotBlank().toSet()
|
||||
val genres = genres.lowerNotBlank().toSet()
|
||||
val sharingLabels = sharingLabels.lowerNotBlank().toSet()
|
||||
|
||||
fun copy(
|
||||
status: Status = this.status,
|
||||
|
|
@ -52,6 +56,7 @@ class SeriesMetadata(
|
|||
genres: Set<String> = this.genres,
|
||||
tags: Set<String> = this.tags,
|
||||
totalBookCount: Int? = this.totalBookCount,
|
||||
sharingLabels: Set<String> = this.sharingLabels,
|
||||
statusLock: Boolean = this.statusLock,
|
||||
titleLock: Boolean = this.titleLock,
|
||||
titleSortLock: Boolean = this.titleSortLock,
|
||||
|
|
@ -63,6 +68,7 @@ class SeriesMetadata(
|
|||
genresLock: Boolean = this.genresLock,
|
||||
tagsLock: Boolean = this.tagsLock,
|
||||
totalBookCountLock: Boolean = this.totalBookCountLock,
|
||||
sharingLabelsLock: Boolean = this.sharingLabelsLock,
|
||||
seriesId: String = this.seriesId,
|
||||
createdDate: LocalDateTime = this.createdDate,
|
||||
lastModifiedDate: LocalDateTime = this.lastModifiedDate,
|
||||
|
|
@ -79,6 +85,7 @@ class SeriesMetadata(
|
|||
genres = genres,
|
||||
tags = tags,
|
||||
totalBookCount = totalBookCount,
|
||||
sharingLabels = sharingLabels,
|
||||
statusLock = statusLock,
|
||||
titleLock = titleLock,
|
||||
titleSortLock = titleSortLock,
|
||||
|
|
@ -90,6 +97,7 @@ class SeriesMetadata(
|
|||
genresLock = genresLock,
|
||||
tagsLock = tagsLock,
|
||||
totalBookCountLock = totalBookCountLock,
|
||||
sharingLabelsLock = sharingLabelsLock,
|
||||
seriesId = seriesId,
|
||||
createdDate = createdDate,
|
||||
lastModifiedDate = lastModifiedDate,
|
||||
|
|
@ -106,7 +114,6 @@ class SeriesMetadata(
|
|||
WEBTOON
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "SeriesMetadata(status=$status, readingDirection=$readingDirection, ageRating=$ageRating, language='$language', totalBookCount=$totalBookCount, statusLock=$statusLock, titleLock=$titleLock, titleSortLock=$titleSortLock, summaryLock=$summaryLock, readingDirectionLock=$readingDirectionLock, publisherLock=$publisherLock, ageRatingLock=$ageRatingLock, languageLock=$languageLock, genresLock=$genresLock, tagsLock=$tagsLock, totalBookCountLock=$totalBookCountLock, seriesId='$seriesId', createdDate=$createdDate, lastModifiedDate=$lastModifiedDate, title='$title', titleSort='$titleSort', summary='$summary', publisher='$publisher', tags=$tags, genres=$genres)"
|
||||
}
|
||||
override fun toString(): String =
|
||||
"SeriesMetadata(status=$status, readingDirection=$readingDirection, ageRating=$ageRating, totalBookCount=$totalBookCount, statusLock=$statusLock, titleLock=$titleLock, titleSortLock=$titleSortLock, summaryLock=$summaryLock, readingDirectionLock=$readingDirectionLock, publisherLock=$publisherLock, ageRatingLock=$ageRatingLock, languageLock=$languageLock, genresLock=$genresLock, tagsLock=$tagsLock, totalBookCountLock=$totalBookCountLock, sharingLabelsLock=$sharingLabelsLock, seriesId='$seriesId', createdDate=$createdDate, lastModifiedDate=$lastModifiedDate, title='$title', titleSort='$titleSort', summary='$summary', publisher='$publisher', language='$language', tags=$tags, genres=$genres, sharingLabels=$sharingLabels)"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ class SeriesDtoDao(
|
|||
private val cs = Tables.COLLECTION_SERIES
|
||||
private val g = Tables.SERIES_METADATA_GENRE
|
||||
private val st = Tables.SERIES_METADATA_TAG
|
||||
private val sl = Tables.SERIES_METADATA_SHARING
|
||||
private val bma = Tables.BOOK_METADATA_AGGREGATION
|
||||
private val bmaa = Tables.BOOK_METADATA_AGGREGATION_AUTHOR
|
||||
private val bmat = Tables.BOOK_METADATA_AGGREGATION_TAG
|
||||
|
|
@ -235,6 +236,11 @@ class SeriesDtoDao(
|
|||
.where(st.SERIES_ID.eq(sr.id))
|
||||
.fetchSet(st.TAG)
|
||||
|
||||
val sharingLabels = dsl.select(sl.LABEL)
|
||||
.from(sl)
|
||||
.where(sl.SERIES_ID.eq(sr.id))
|
||||
.fetchSet(sl.LABEL)
|
||||
|
||||
val aggregatedAuthors = dsl.selectFrom(bmaa)
|
||||
.where(bmaa.SERIES_ID.eq(sr.id))
|
||||
.fetchInto(bmaa)
|
||||
|
|
@ -250,7 +256,7 @@ class SeriesDtoDao(
|
|||
booksReadCount,
|
||||
booksUnreadCount,
|
||||
booksInProgressCount,
|
||||
dr.toDto(genres, tags),
|
||||
dr.toDto(genres, tags, sharingLabels),
|
||||
bmar.toDto(aggregatedAuthors, aggregatedTags),
|
||||
)
|
||||
}
|
||||
|
|
@ -346,7 +352,7 @@ class SeriesDtoDao(
|
|||
deleted = deletedDate != null,
|
||||
)
|
||||
|
||||
private fun SeriesMetadataRecord.toDto(genres: Set<String>, tags: Set<String>) =
|
||||
private fun SeriesMetadataRecord.toDto(genres: Set<String>, tags: Set<String>, sharingLabels: Set<String>) =
|
||||
SeriesMetadataDto(
|
||||
status = status,
|
||||
statusLock = statusLock,
|
||||
|
|
@ -372,6 +378,8 @@ class SeriesDtoDao(
|
|||
tagsLock = tagsLock,
|
||||
totalBookCount = totalBookCount,
|
||||
totalBookCountLock = totalBookCountLock,
|
||||
sharingLabels = sharingLabels,
|
||||
sharingLabelsLock = sharingLabelsLock,
|
||||
)
|
||||
|
||||
private fun BookMetadataAggregationRecord.toDto(authors: List<AuthorDto>, tags: Set<String>) =
|
||||
|
|
|
|||
|
|
@ -20,12 +20,13 @@ class SeriesMetadataDao(
|
|||
private val d = Tables.SERIES_METADATA
|
||||
private val g = Tables.SERIES_METADATA_GENRE
|
||||
private val st = Tables.SERIES_METADATA_TAG
|
||||
private val sl = Tables.SERIES_METADATA_SHARING
|
||||
|
||||
override fun findById(seriesId: String): SeriesMetadata =
|
||||
findOne(seriesId)!!.toDomain(findGenres(seriesId), findTags(seriesId))
|
||||
findOne(seriesId)!!.toDomain(findGenres(seriesId), findTags(seriesId), findSharingLabels(seriesId))
|
||||
|
||||
override fun findByIdOrNull(seriesId: String): SeriesMetadata? =
|
||||
findOne(seriesId)?.toDomain(findGenres(seriesId), findTags(seriesId))
|
||||
findOne(seriesId)?.toDomain(findGenres(seriesId), findTags(seriesId), findSharingLabels(seriesId))
|
||||
|
||||
private fun findOne(seriesId: String) =
|
||||
dsl.selectFrom(d)
|
||||
|
|
@ -36,17 +37,19 @@ class SeriesMetadataDao(
|
|||
dsl.select(g.GENRE)
|
||||
.from(g)
|
||||
.where(g.SERIES_ID.eq(seriesId))
|
||||
.fetchInto(g)
|
||||
.mapNotNull { it.genre }
|
||||
.toSet()
|
||||
.fetchSet(g.GENRE)
|
||||
|
||||
private fun findTags(seriesId: String) =
|
||||
dsl.select(st.TAG)
|
||||
.from(st)
|
||||
.where(st.SERIES_ID.eq(seriesId))
|
||||
.fetchInto(st)
|
||||
.mapNotNull { it.tag }
|
||||
.toSet()
|
||||
.fetchSet(st.TAG)
|
||||
|
||||
private fun findSharingLabels(seriesId: String) =
|
||||
dsl.select(sl.LABEL)
|
||||
.from(sl)
|
||||
.where(sl.SERIES_ID.eq(seriesId))
|
||||
.fetchSet(sl.LABEL)
|
||||
|
||||
@Transactional
|
||||
override fun insert(metadata: SeriesMetadata) {
|
||||
|
|
@ -72,10 +75,12 @@ class SeriesMetadataDao(
|
|||
.set(d.TAGS_LOCK, metadata.tagsLock)
|
||||
.set(d.TOTAL_BOOK_COUNT, metadata.totalBookCount)
|
||||
.set(d.TOTAL_BOOK_COUNT_LOCK, metadata.totalBookCountLock)
|
||||
.set(d.SHARING_LABELS_LOCK, metadata.sharingLabelsLock)
|
||||
.execute()
|
||||
|
||||
insertGenres(metadata)
|
||||
insertTags(metadata)
|
||||
insertSharingLabels(metadata)
|
||||
}
|
||||
|
||||
@Transactional
|
||||
|
|
@ -101,6 +106,7 @@ class SeriesMetadataDao(
|
|||
.set(d.TAGS_LOCK, metadata.tagsLock)
|
||||
.set(d.TOTAL_BOOK_COUNT, metadata.totalBookCount)
|
||||
.set(d.TOTAL_BOOK_COUNT_LOCK, metadata.totalBookCountLock)
|
||||
.set(d.SHARING_LABELS_LOCK, metadata.sharingLabelsLock)
|
||||
.set(d.LAST_MODIFIED_DATE, LocalDateTime.now(ZoneId.of("Z")))
|
||||
.where(d.SERIES_ID.eq(metadata.seriesId))
|
||||
.execute()
|
||||
|
|
@ -113,8 +119,13 @@ class SeriesMetadataDao(
|
|||
.where(st.SERIES_ID.eq(metadata.seriesId))
|
||||
.execute()
|
||||
|
||||
dsl.deleteFrom(sl)
|
||||
.where(sl.SERIES_ID.eq(metadata.seriesId))
|
||||
.execute()
|
||||
|
||||
insertGenres(metadata)
|
||||
insertTags(metadata)
|
||||
insertSharingLabels(metadata)
|
||||
}
|
||||
|
||||
private fun insertGenres(metadata: SeriesMetadata) {
|
||||
|
|
@ -147,10 +158,26 @@ class SeriesMetadataDao(
|
|||
}
|
||||
}
|
||||
|
||||
private fun insertSharingLabels(metadata: SeriesMetadata) {
|
||||
if (metadata.sharingLabels.isNotEmpty()) {
|
||||
metadata.sharingLabels.chunked(batchSize).forEach { chunk ->
|
||||
dsl.batch(
|
||||
dsl.insertInto(sl, sl.SERIES_ID, sl.LABEL)
|
||||
.values(null as String?, null),
|
||||
).also { step ->
|
||||
chunk.forEach {
|
||||
step.bind(metadata.seriesId, it)
|
||||
}
|
||||
}.execute()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
override fun delete(seriesId: String) {
|
||||
dsl.deleteFrom(g).where(g.SERIES_ID.eq(seriesId)).execute()
|
||||
dsl.deleteFrom(st).where(st.SERIES_ID.eq(seriesId)).execute()
|
||||
dsl.deleteFrom(sl).where(sl.SERIES_ID.eq(seriesId)).execute()
|
||||
dsl.deleteFrom(d).where(d.SERIES_ID.eq(seriesId)).execute()
|
||||
}
|
||||
|
||||
|
|
@ -160,12 +187,13 @@ class SeriesMetadataDao(
|
|||
|
||||
dsl.deleteFrom(g).where(g.SERIES_ID.`in`(dsl.selectTempStrings())).execute()
|
||||
dsl.deleteFrom(st).where(st.SERIES_ID.`in`(dsl.selectTempStrings())).execute()
|
||||
dsl.deleteFrom(sl).where(sl.SERIES_ID.`in`(dsl.selectTempStrings())).execute()
|
||||
dsl.deleteFrom(d).where(d.SERIES_ID.`in`(dsl.selectTempStrings())).execute()
|
||||
}
|
||||
|
||||
override fun count(): Long = dsl.fetchCount(d).toLong()
|
||||
|
||||
private fun SeriesMetadataRecord.toDomain(genres: Set<String>, tags: Set<String>) =
|
||||
private fun SeriesMetadataRecord.toDomain(genres: Set<String>, tags: Set<String>, sharingLabels: Set<String>) =
|
||||
SeriesMetadata(
|
||||
status = SeriesMetadata.Status.valueOf(status),
|
||||
title = title,
|
||||
|
|
@ -180,6 +208,7 @@ class SeriesMetadataDao(
|
|||
genres = genres,
|
||||
tags = tags,
|
||||
totalBookCount = totalBookCount,
|
||||
sharingLabels = sharingLabels,
|
||||
|
||||
statusLock = statusLock,
|
||||
titleLock = titleLock,
|
||||
|
|
@ -192,6 +221,7 @@ class SeriesMetadataDao(
|
|||
genresLock = genresLock,
|
||||
tagsLock = tagsLock,
|
||||
totalBookCountLock = totalBookCountLock,
|
||||
sharingLabelsLock = sharingLabelsLock,
|
||||
|
||||
seriesId = seriesId,
|
||||
|
||||
|
|
|
|||
|
|
@ -50,6 +50,8 @@ data class SeriesMetadataDto(
|
|||
val tagsLock: Boolean,
|
||||
val totalBookCount: Int?,
|
||||
val totalBookCountLock: Boolean,
|
||||
val sharingLabels: Set<String>,
|
||||
val sharingLabelsLock: Boolean,
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
|
||||
val created: LocalDateTime,
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@ class SeriesMetadataDaoTest(
|
|||
tags = setOf("tag", "another"),
|
||||
language = "en",
|
||||
totalBookCount = 5,
|
||||
sharingLabels = setOf("kids"),
|
||||
titleLock = true,
|
||||
titleSortLock = true,
|
||||
summaryLock = true,
|
||||
|
|
@ -72,6 +73,7 @@ class SeriesMetadataDaoTest(
|
|||
languageLock = true,
|
||||
tagsLock = true,
|
||||
totalBookCountLock = true,
|
||||
sharingLabelsLock = true,
|
||||
seriesId = series.id,
|
||||
)
|
||||
|
||||
|
|
@ -93,6 +95,7 @@ class SeriesMetadataDaoTest(
|
|||
assertThat(created.genres).containsAll(metadata.genres)
|
||||
assertThat(created.tags).containsAll(metadata.tags)
|
||||
assertThat(created.totalBookCount).isEqualTo(metadata.totalBookCount)
|
||||
assertThat(created.sharingLabels).containsAll(metadata.sharingLabels)
|
||||
|
||||
assertThat(created.titleLock).isEqualTo(metadata.titleLock)
|
||||
assertThat(created.titleSortLock).isEqualTo(metadata.titleSortLock)
|
||||
|
|
@ -105,6 +108,7 @@ class SeriesMetadataDaoTest(
|
|||
assertThat(created.languageLock).isEqualTo(metadata.languageLock)
|
||||
assertThat(created.tagsLock).isEqualTo(metadata.tagsLock)
|
||||
assertThat(created.totalBookCountLock).isEqualTo(metadata.totalBookCountLock)
|
||||
assertThat(created.sharingLabelsLock).isEqualTo(metadata.sharingLabelsLock)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -135,6 +139,7 @@ class SeriesMetadataDaoTest(
|
|||
assertThat(created.genres).isEmpty()
|
||||
assertThat(created.tags).isEmpty()
|
||||
assertThat(created.totalBookCount).isNull()
|
||||
assertThat(created.sharingLabels).isEmpty()
|
||||
|
||||
assertThat(created.titleLock).isFalse
|
||||
assertThat(created.titleSortLock).isFalse
|
||||
|
|
@ -147,6 +152,7 @@ class SeriesMetadataDaoTest(
|
|||
assertThat(created.languageLock).isFalse
|
||||
assertThat(created.tagsLock).isFalse
|
||||
assertThat(created.totalBookCountLock).isFalse
|
||||
assertThat(created.sharingLabelsLock).isFalse
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -198,6 +204,7 @@ class SeriesMetadataDaoTest(
|
|||
genres = setOf("Action"),
|
||||
tags = setOf("tag"),
|
||||
totalBookCount = 3,
|
||||
sharingLabels = setOf("kids"),
|
||||
seriesId = series.id,
|
||||
)
|
||||
seriesMetadataDao.insert(metadata)
|
||||
|
|
@ -218,6 +225,7 @@ class SeriesMetadataDaoTest(
|
|||
genres = setOf("Adventure"),
|
||||
tags = setOf("Another"),
|
||||
totalBookCount = 8,
|
||||
sharingLabels = setOf("adult"),
|
||||
statusLock = true,
|
||||
titleLock = true,
|
||||
titleSortLock = true,
|
||||
|
|
@ -229,6 +237,7 @@ class SeriesMetadataDaoTest(
|
|||
genresLock = true,
|
||||
tagsLock = true,
|
||||
totalBookCountLock = true,
|
||||
sharingLabelsLock = true,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -251,6 +260,7 @@ class SeriesMetadataDaoTest(
|
|||
assertThat(modified.genres).containsAll(updated.genres)
|
||||
assertThat(modified.tags).containsAll(updated.tags)
|
||||
assertThat(modified.totalBookCount).isEqualTo(updated.totalBookCount)
|
||||
assertThat(modified.sharingLabels).containsAll(updated.sharingLabels)
|
||||
|
||||
assertThat(modified.titleLock).isTrue
|
||||
assertThat(modified.titleSortLock).isTrue
|
||||
|
|
@ -263,5 +273,6 @@ class SeriesMetadataDaoTest(
|
|||
assertThat(modified.publisherLock).isTrue
|
||||
assertThat(modified.tagsLock).isTrue
|
||||
assertThat(modified.totalBookCountLock).isTrue
|
||||
assertThat(modified.sharingLabelsLock).isTrue
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue