diff --git a/komga/src/flyway/resources/db/migration/sqlite/V20220224112015__series_sharing_labels.sql b/komga/src/flyway/resources/db/migration/sqlite/V20220224112015__series_sharing_labels.sql new file mode 100644 index 000000000..f02aa5e65 --- /dev/null +++ b/komga/src/flyway/resources/db/migration/sqlite/V20220224112015__series_sharing_labels.sql @@ -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; diff --git a/komga/src/main/kotlin/org/gotson/komga/domain/model/SeriesMetadata.kt b/komga/src/main/kotlin/org/gotson/komga/domain/model/SeriesMetadata.kt index 6cb60ff72..c97a0fd4e 100644 --- a/komga/src/main/kotlin/org/gotson/komga/domain/model/SeriesMetadata.kt +++ b/komga/src/main/kotlin/org/gotson/komga/domain/model/SeriesMetadata.kt @@ -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 = emptySet(), tags: Set = emptySet(), val totalBookCount: Int? = null, + sharingLabels: Set = 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 = this.genres, tags: Set = this.tags, totalBookCount: Int? = this.totalBookCount, + sharingLabels: Set = 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)" } diff --git a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/SeriesDtoDao.kt b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/SeriesDtoDao.kt index 71fa38aec..516aff7fa 100644 --- a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/SeriesDtoDao.kt +++ b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/SeriesDtoDao.kt @@ -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, tags: Set) = + private fun SeriesMetadataRecord.toDto(genres: Set, tags: Set, sharingLabels: Set) = 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, tags: Set) = diff --git a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/SeriesMetadataDao.kt b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/SeriesMetadataDao.kt index 129b1f73b..431503b2a 100644 --- a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/SeriesMetadataDao.kt +++ b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/SeriesMetadataDao.kt @@ -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, tags: Set) = + private fun SeriesMetadataRecord.toDomain(genres: Set, tags: Set, sharingLabels: Set) = 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, diff --git a/komga/src/main/kotlin/org/gotson/komga/interfaces/api/rest/dto/SeriesDto.kt b/komga/src/main/kotlin/org/gotson/komga/interfaces/api/rest/dto/SeriesDto.kt index d6cd6b154..fd7a9d87a 100644 --- a/komga/src/main/kotlin/org/gotson/komga/interfaces/api/rest/dto/SeriesDto.kt +++ b/komga/src/main/kotlin/org/gotson/komga/interfaces/api/rest/dto/SeriesDto.kt @@ -50,6 +50,8 @@ data class SeriesMetadataDto( val tagsLock: Boolean, val totalBookCount: Int?, val totalBookCountLock: Boolean, + val sharingLabels: Set, + val sharingLabelsLock: Boolean, @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss") val created: LocalDateTime, diff --git a/komga/src/test/kotlin/org/gotson/komga/infrastructure/jooq/SeriesMetadataDaoTest.kt b/komga/src/test/kotlin/org/gotson/komga/infrastructure/jooq/SeriesMetadataDaoTest.kt index abf9b874f..f423ba12b 100644 --- a/komga/src/test/kotlin/org/gotson/komga/infrastructure/jooq/SeriesMetadataDaoTest.kt +++ b/komga/src/test/kotlin/org/gotson/komga/infrastructure/jooq/SeriesMetadataDaoTest.kt @@ -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 } }