mirror of
https://github.com/gotson/komga.git
synced 2025-01-09 04:08:00 +08:00
parent
7303606c36
commit
f9f02a395b
@ -31,6 +31,10 @@
|
||||
<v-icon left class="hidden-xs-only">mdi-tag-multiple</v-icon>
|
||||
{{ $t('dialog.edit_series.tab_tags') }}
|
||||
</v-tab>
|
||||
<v-tab class="justify-start" v-if="single">
|
||||
<v-icon left class="hidden-xs-only">mdi-link</v-icon>
|
||||
{{ $t('dialog.edit_books.tab_links') }}
|
||||
</v-tab>
|
||||
<v-tab class="justify-start" v-if="single">
|
||||
<v-icon left class="hidden-xs-only">mdi-image</v-icon>
|
||||
{{ $t('dialog.edit_series.tab_poster') }}
|
||||
@ -320,6 +324,77 @@
|
||||
</v-card>
|
||||
</v-tab-item>
|
||||
|
||||
<!-- Tab: Links -->
|
||||
<v-tab-item v-if="single">
|
||||
<v-card flat min-height="100">
|
||||
<v-container fluid>
|
||||
<!-- Links -->
|
||||
<v-form
|
||||
v-model="linksValid"
|
||||
ref="linksForm"
|
||||
>
|
||||
<v-row
|
||||
v-for="(link, i) in form.links"
|
||||
:key="i"
|
||||
>
|
||||
<v-col cols="4" class="py-0">
|
||||
<v-text-field v-model="form.links[i].label"
|
||||
:label="$t('dialog.edit_books.field_link_label')"
|
||||
filled
|
||||
dense
|
||||
:rules="[linksLabelRules]"
|
||||
@input="$v.form.links.$touch()"
|
||||
@blur="$v.form.links.$touch()"
|
||||
@change="form.linksLock = true"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<v-icon :color="form.linksLock ? 'secondary' : ''"
|
||||
@click="form.linksLock = !form.linksLock"
|
||||
>
|
||||
{{ form.linksLock ? 'mdi-lock' : 'mdi-lock-open' }}
|
||||
</v-icon>
|
||||
</template>
|
||||
</v-text-field>
|
||||
</v-col>
|
||||
|
||||
<v-col cols="8" class="py-0">
|
||||
<v-text-field v-model="form.links[i].url"
|
||||
:label="$t('dialog.edit_books.field_link_url')"
|
||||
filled
|
||||
dense
|
||||
:rules="[linksUrlRules]"
|
||||
@input="$v.form.links.$touch()"
|
||||
@blur="$v.form.links.$touch()"
|
||||
@change="form.linksLock = true"
|
||||
>
|
||||
<template v-slot:append-outer>
|
||||
<v-icon @click="form.links.splice(i, 1)">mdi-delete</v-icon>
|
||||
</template>
|
||||
</v-text-field>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-form>
|
||||
|
||||
<v-row>
|
||||
<v-spacer/>
|
||||
<v-col cols="auto">
|
||||
<v-btn
|
||||
elevation="2"
|
||||
fab
|
||||
small
|
||||
bottom
|
||||
right
|
||||
color="primary"
|
||||
@click="form.links.push({label:'', url:''})"
|
||||
>
|
||||
<v-icon>mdi-plus</v-icon>
|
||||
</v-btn>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</v-card>
|
||||
</v-tab-item>
|
||||
|
||||
<!-- Tab: Thumbnails -->
|
||||
<v-tab-item v-if="single">
|
||||
<v-card flat>
|
||||
@ -424,6 +499,7 @@ export default Vue.extend({
|
||||
return {
|
||||
modal: false,
|
||||
tab: 0,
|
||||
linksValid: false,
|
||||
form: {
|
||||
status: '',
|
||||
statusLock: false,
|
||||
@ -449,6 +525,8 @@ export default Vue.extend({
|
||||
totalBookCountLock: false,
|
||||
sharingLabels: [],
|
||||
sharingLabelsLock: false,
|
||||
links: [],
|
||||
linksLock: false,
|
||||
},
|
||||
mixed: {
|
||||
status: false,
|
||||
@ -522,6 +600,7 @@ export default Vue.extend({
|
||||
readingDirection: {},
|
||||
publisher: {},
|
||||
totalBookCount: {minValue: minValue(1)},
|
||||
links: {},
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
@ -566,6 +645,20 @@ export default Vue.extend({
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
linksLabelRules(label: string): boolean | string {
|
||||
if (!!this.$_.trim(label)) return true
|
||||
return this.$t('common.required').toString()
|
||||
},
|
||||
linksUrlRules(value: string): boolean | string {
|
||||
let url
|
||||
try {
|
||||
url = new URL(value)
|
||||
} catch (_) {
|
||||
return this.$t('dialog.edit_books.field_link_url_error_url').toString()
|
||||
}
|
||||
if (url.protocol === 'http:' || url.protocol === 'https:') return true
|
||||
return this.$t('dialog.edit_books.field_link_url_error_protocol').toString()
|
||||
},
|
||||
async loadAvailableTags() {
|
||||
this.tagsAvailable = await this.$komgaReferential.getTags()
|
||||
},
|
||||
@ -584,7 +677,8 @@ export default Vue.extend({
|
||||
},
|
||||
dialogReset(series: SeriesDto | SeriesDto[]) {
|
||||
this.tab = 0
|
||||
this.$v.$reset()
|
||||
this.$v.$reset();
|
||||
(this.$refs.linksForm as any)?.resetValidation()
|
||||
if (Array.isArray(series) && series.length === 0) return
|
||||
if (Array.isArray(series) && series.length > 0) {
|
||||
const status = this.$_.uniq(series.map(x => x.metadata.status))
|
||||
@ -636,10 +730,13 @@ export default Vue.extend({
|
||||
|
||||
const sharingLabelsLock = this.$_.uniq(series.map(x => x.metadata.sharingLabelsLock))
|
||||
this.form.sharingLabelsLock = sharingLabelsLock.length > 1 ? false : sharingLabelsLock[0]
|
||||
|
||||
this.form.links = []
|
||||
} else {
|
||||
this.form.genres = []
|
||||
this.form.tags = []
|
||||
this.form.sharingLabels = []
|
||||
this.form.links = []
|
||||
this.$_.merge(this.form, (series as SeriesDto).metadata)
|
||||
this.poster.selectedThumbnail = ''
|
||||
this.poster.deleteQueue = []
|
||||
@ -657,7 +754,7 @@ export default Vue.extend({
|
||||
}
|
||||
},
|
||||
validateForm(): any {
|
||||
if (!this.$v.$invalid) {
|
||||
if (!this.$v.$invalid && (!this.single || !this.$refs.linksForm || (this.$refs.linksForm as any).validate())) {
|
||||
const metadata = {
|
||||
statusLock: this.form.statusLock,
|
||||
readingDirectionLock: this.form.readingDirectionLock,
|
||||
@ -668,6 +765,7 @@ export default Vue.extend({
|
||||
tagsLock: this.form.tagsLock,
|
||||
totalBookCountLock: this.form.totalBookCountLock,
|
||||
sharingLabelsLock: this.form.sharingLabelsLock,
|
||||
linksLock: this.form.linksLock,
|
||||
}
|
||||
|
||||
if (this.$v.form?.status?.$dirty) {
|
||||
@ -725,6 +823,10 @@ export default Vue.extend({
|
||||
if (this.$v.form?.totalBookCount?.$dirty) {
|
||||
this.$_.merge(metadata, {totalBookCount: this.form.totalBookCount})
|
||||
}
|
||||
|
||||
if (this.$v.form?.links?.$dirty || this.form.links.length != (this.series as SeriesDto).metadata.links?.length) {
|
||||
this.$_.merge(metadata, {links: this.form.links})
|
||||
}
|
||||
}
|
||||
|
||||
return metadata
|
||||
|
@ -1,4 +1,4 @@
|
||||
import {AuthorDto} from '@/types/komga-books'
|
||||
import {AuthorDto, WebLinkDto} from '@/types/komga-books'
|
||||
import {Context} from '@/types/context'
|
||||
|
||||
export interface SeriesDto {
|
||||
@ -47,6 +47,8 @@ export interface SeriesMetadataDto {
|
||||
totalBookCountLock: boolean,
|
||||
sharingLabels: string[],
|
||||
sharingLabelsLock: boolean,
|
||||
links?: WebLinkDto[],
|
||||
linksLock?: boolean
|
||||
}
|
||||
|
||||
export interface SeriesBooksMetadataDto {
|
||||
@ -84,6 +86,8 @@ export interface SeriesMetadataUpdateDto {
|
||||
totalBookCountLock: boolean,
|
||||
sharingLabels?: string[],
|
||||
sharingLabelsLock: boolean,
|
||||
links?: WebLinkDto[],
|
||||
linksLock?: boolean,
|
||||
}
|
||||
|
||||
export interface GroupCountDto {
|
||||
|
@ -331,6 +331,32 @@
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row v-if="series.metadata.links.length > 0" class="align-center text-caption">
|
||||
<v-col class="py-1" cols="4" sm="3" md="2" xl="1">{{ $t('browse_book.links') }}</v-col>
|
||||
<v-col class="py-1" cols="8" sm="9" md="10" xl="11">
|
||||
<v-chip
|
||||
v-for="(link, i) in series.metadata.links"
|
||||
:href="link.url"
|
||||
target="_blank"
|
||||
class="me-2"
|
||||
label
|
||||
small
|
||||
outlined
|
||||
link
|
||||
:key="i"
|
||||
>
|
||||
{{ link.label }}
|
||||
<v-icon
|
||||
x-small
|
||||
color="grey"
|
||||
class="ps-1"
|
||||
>
|
||||
mdi-open-in-new
|
||||
</v-icon>
|
||||
</v-chip>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-divider v-if="series.booksMetadata.authors.length > 0" class="my-3"/>
|
||||
<v-row class="align-center text-caption"
|
||||
v-for="role in displayedRoles"
|
||||
|
@ -0,0 +1,13 @@
|
||||
CREATE TABLE SERIES_METADATA_LINK
|
||||
(
|
||||
LABEL varchar NOT NULL,
|
||||
URL varchar NOT NULL,
|
||||
SERIES_ID varchar NOT NULL,
|
||||
FOREIGN KEY (SERIES_ID) REFERENCES SERIES (ID)
|
||||
);
|
||||
|
||||
alter table series_metadata
|
||||
add column LINKS_LOCK boolean NOT NULL DEFAULT 0;
|
||||
|
||||
create index idx__series_metadata_link__series_id
|
||||
on SERIES_METADATA_LINK (SERIES_ID);
|
@ -16,6 +16,7 @@ class SeriesMetadata(
|
||||
tags: Set<String> = emptySet(),
|
||||
val totalBookCount: Int? = null,
|
||||
sharingLabels: Set<String> = emptySet(),
|
||||
val links: List<WebLink> = emptyList(),
|
||||
|
||||
val statusLock: Boolean = false,
|
||||
val titleLock: Boolean = false,
|
||||
@ -29,6 +30,7 @@ class SeriesMetadata(
|
||||
val tagsLock: Boolean = false,
|
||||
val totalBookCountLock: Boolean = false,
|
||||
val sharingLabelsLock: Boolean = false,
|
||||
val linksLock: Boolean = false,
|
||||
|
||||
val seriesId: String = "",
|
||||
|
||||
@ -57,6 +59,7 @@ class SeriesMetadata(
|
||||
tags: Set<String> = this.tags,
|
||||
totalBookCount: Int? = this.totalBookCount,
|
||||
sharingLabels: Set<String> = this.sharingLabels,
|
||||
links: List<WebLink> = this.links,
|
||||
statusLock: Boolean = this.statusLock,
|
||||
titleLock: Boolean = this.titleLock,
|
||||
titleSortLock: Boolean = this.titleSortLock,
|
||||
@ -69,6 +72,7 @@ class SeriesMetadata(
|
||||
tagsLock: Boolean = this.tagsLock,
|
||||
totalBookCountLock: Boolean = this.totalBookCountLock,
|
||||
sharingLabelsLock: Boolean = this.sharingLabelsLock,
|
||||
linksLock: Boolean = this.linksLock,
|
||||
seriesId: String = this.seriesId,
|
||||
createdDate: LocalDateTime = this.createdDate,
|
||||
lastModifiedDate: LocalDateTime = this.lastModifiedDate,
|
||||
@ -86,6 +90,7 @@ class SeriesMetadata(
|
||||
tags = tags,
|
||||
totalBookCount = totalBookCount,
|
||||
sharingLabels = sharingLabels,
|
||||
links = links,
|
||||
statusLock = statusLock,
|
||||
titleLock = titleLock,
|
||||
titleSortLock = titleSortLock,
|
||||
@ -98,6 +103,7 @@ class SeriesMetadata(
|
||||
tagsLock = tagsLock,
|
||||
totalBookCountLock = totalBookCountLock,
|
||||
sharingLabelsLock = sharingLabelsLock,
|
||||
linksLock = linksLock,
|
||||
seriesId = seriesId,
|
||||
createdDate = createdDate,
|
||||
lastModifiedDate = lastModifiedDate,
|
||||
@ -115,5 +121,5 @@ class SeriesMetadata(
|
||||
}
|
||||
|
||||
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)"
|
||||
"SeriesMetadata(status=$status, readingDirection=$readingDirection, ageRating=$ageRating, totalBookCount=$totalBookCount, links=$links, statusLock=$statusLock, titleLock=$titleLock, titleSortLock=$titleSortLock, summaryLock=$summaryLock, readingDirectionLock=$readingDirectionLock, publisherLock=$publisherLock, ageRatingLock=$ageRatingLock, languageLock=$languageLock, genresLock=$genresLock, tagsLock=$tagsLock, totalBookCountLock=$totalBookCountLock, sharingLabelsLock=$sharingLabelsLock, linksLock=$linksLock, seriesId='$seriesId', createdDate=$createdDate, lastModifiedDate=$lastModifiedDate, title='$title', titleSort='$titleSort', summary='$summary', publisher='$publisher', language='$language', tags=$tags, genres=$genres, sharingLabels=$sharingLabels)"
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ import org.gotson.komga.interfaces.api.rest.dto.BookMetadataAggregationDto
|
||||
import org.gotson.komga.interfaces.api.rest.dto.GroupCountDto
|
||||
import org.gotson.komga.interfaces.api.rest.dto.SeriesDto
|
||||
import org.gotson.komga.interfaces.api.rest.dto.SeriesMetadataDto
|
||||
import org.gotson.komga.interfaces.api.rest.dto.WebLinkDto
|
||||
import org.gotson.komga.jooq.Tables
|
||||
import org.gotson.komga.jooq.tables.records.BookMetadataAggregationRecord
|
||||
import org.gotson.komga.jooq.tables.records.SeriesMetadataRecord
|
||||
@ -60,6 +61,7 @@ class SeriesDtoDao(
|
||||
private val g = Tables.SERIES_METADATA_GENRE
|
||||
private val st = Tables.SERIES_METADATA_TAG
|
||||
private val sl = Tables.SERIES_METADATA_SHARING
|
||||
private val slk = Tables.SERIES_METADATA_LINK
|
||||
private val bma = Tables.BOOK_METADATA_AGGREGATION
|
||||
private val bmaa = Tables.BOOK_METADATA_AGGREGATION_AUTHOR
|
||||
private val bmat = Tables.BOOK_METADATA_AGGREGATION_TAG
|
||||
@ -227,6 +229,7 @@ class SeriesDtoDao(
|
||||
lateinit var genres: Map<String, List<String>>
|
||||
lateinit var tags: Map<String, List<String>>
|
||||
lateinit var sharingLabels: Map<String, List<String>>
|
||||
lateinit var links: Map<String, List<WebLinkDto>>
|
||||
lateinit var aggregatedAuthors: Map<String, List<AuthorDto>>
|
||||
lateinit var aggregatedTags: Map<String, List<String>>
|
||||
transactionTemplate.executeWithoutResult {
|
||||
@ -243,6 +246,10 @@ class SeriesDtoDao(
|
||||
.where(sl.SERIES_ID.`in`(dsl.selectTempStrings()))
|
||||
.groupBy({ it.seriesId }, { it.label })
|
||||
|
||||
links = dsl.selectFrom(slk)
|
||||
.where(slk.SERIES_ID.`in`(dsl.selectTempStrings()))
|
||||
.groupBy({ it.seriesId }, { WebLinkDto(it.label, it.url) })
|
||||
|
||||
aggregatedAuthors = dsl.selectFrom(bmaa)
|
||||
.where(bmaa.SERIES_ID.`in`(dsl.selectTempStrings()))
|
||||
.filter { it.name != null }
|
||||
@ -268,7 +275,7 @@ class SeriesDtoDao(
|
||||
booksReadCount,
|
||||
booksUnreadCount,
|
||||
booksInProgressCount,
|
||||
dr.toDto(genres[sr.id].orEmpty().toSet(), tags[sr.id].orEmpty().toSet(), sharingLabels[sr.id].orEmpty().toSet()),
|
||||
dr.toDto(genres[sr.id].orEmpty().toSet(), tags[sr.id].orEmpty().toSet(), sharingLabels[sr.id].orEmpty().toSet(), links[sr.id].orEmpty()),
|
||||
bmar.toDto(aggregatedAuthors[sr.id].orEmpty(), aggregatedTags[sr.id].orEmpty().toSet()),
|
||||
)
|
||||
}
|
||||
@ -365,7 +372,7 @@ class SeriesDtoDao(
|
||||
deleted = deletedDate != null,
|
||||
)
|
||||
|
||||
private fun SeriesMetadataRecord.toDto(genres: Set<String>, tags: Set<String>, sharingLabels: Set<String>) =
|
||||
private fun SeriesMetadataRecord.toDto(genres: Set<String>, tags: Set<String>, sharingLabels: Set<String>, links: List<WebLinkDto>) =
|
||||
SeriesMetadataDto(
|
||||
status = status,
|
||||
statusLock = statusLock,
|
||||
@ -393,6 +400,8 @@ class SeriesDtoDao(
|
||||
totalBookCountLock = totalBookCountLock,
|
||||
sharingLabels = sharingLabels,
|
||||
sharingLabelsLock = sharingLabelsLock,
|
||||
links = links,
|
||||
linksLock = linksLock,
|
||||
)
|
||||
|
||||
private fun BookMetadataAggregationRecord.toDto(authors: List<AuthorDto>, tags: Set<String>) =
|
||||
|
@ -1,6 +1,7 @@
|
||||
package org.gotson.komga.infrastructure.jooq
|
||||
|
||||
import org.gotson.komga.domain.model.SeriesMetadata
|
||||
import org.gotson.komga.domain.model.WebLink
|
||||
import org.gotson.komga.domain.persistence.SeriesMetadataRepository
|
||||
import org.gotson.komga.jooq.Tables
|
||||
import org.gotson.komga.jooq.tables.records.SeriesMetadataRecord
|
||||
@ -8,6 +9,7 @@ import org.jooq.DSLContext
|
||||
import org.springframework.beans.factory.annotation.Value
|
||||
import org.springframework.stereotype.Component
|
||||
import org.springframework.transaction.annotation.Transactional
|
||||
import java.net.URI
|
||||
import java.time.LocalDateTime
|
||||
import java.time.ZoneId
|
||||
|
||||
@ -21,12 +23,13 @@ class SeriesMetadataDao(
|
||||
private val g = Tables.SERIES_METADATA_GENRE
|
||||
private val st = Tables.SERIES_METADATA_TAG
|
||||
private val sl = Tables.SERIES_METADATA_SHARING
|
||||
private val slk = Tables.SERIES_METADATA_LINK
|
||||
|
||||
override fun findById(seriesId: String): SeriesMetadata =
|
||||
findOne(seriesId)!!.toDomain(findGenres(seriesId), findTags(seriesId), findSharingLabels(seriesId))
|
||||
findOne(seriesId)!!.toDomain(findGenres(seriesId), findTags(seriesId), findSharingLabels(seriesId), findLinks(seriesId))
|
||||
|
||||
override fun findByIdOrNull(seriesId: String): SeriesMetadata? =
|
||||
findOne(seriesId)?.toDomain(findGenres(seriesId), findTags(seriesId), findSharingLabels(seriesId))
|
||||
findOne(seriesId)?.toDomain(findGenres(seriesId), findTags(seriesId), findSharingLabels(seriesId), findLinks(seriesId))
|
||||
|
||||
private fun findOne(seriesId: String) =
|
||||
dsl.selectFrom(d)
|
||||
@ -50,6 +53,12 @@ class SeriesMetadataDao(
|
||||
.from(sl)
|
||||
.where(sl.SERIES_ID.eq(seriesId))
|
||||
.fetchSet(sl.LABEL)
|
||||
private fun findLinks(seriesId: String) =
|
||||
dsl.select(slk.LABEL, slk.URL)
|
||||
.from(slk)
|
||||
.where(slk.SERIES_ID.eq(seriesId))
|
||||
.fetchInto(slk)
|
||||
.map { WebLink(it.label, URI(it.url)) }
|
||||
|
||||
@Transactional
|
||||
override fun insert(metadata: SeriesMetadata) {
|
||||
@ -76,11 +85,13 @@ class SeriesMetadataDao(
|
||||
.set(d.TOTAL_BOOK_COUNT, metadata.totalBookCount)
|
||||
.set(d.TOTAL_BOOK_COUNT_LOCK, metadata.totalBookCountLock)
|
||||
.set(d.SHARING_LABELS_LOCK, metadata.sharingLabelsLock)
|
||||
.set(d.LINKS_LOCK, metadata.linksLock)
|
||||
.execute()
|
||||
|
||||
insertGenres(metadata)
|
||||
insertTags(metadata)
|
||||
insertSharingLabels(metadata)
|
||||
insertLinks(metadata)
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@ -107,6 +118,7 @@ class SeriesMetadataDao(
|
||||
.set(d.TOTAL_BOOK_COUNT, metadata.totalBookCount)
|
||||
.set(d.TOTAL_BOOK_COUNT_LOCK, metadata.totalBookCountLock)
|
||||
.set(d.SHARING_LABELS_LOCK, metadata.sharingLabelsLock)
|
||||
.set(d.LINKS_LOCK, metadata.linksLock)
|
||||
.set(d.LAST_MODIFIED_DATE, LocalDateTime.now(ZoneId.of("Z")))
|
||||
.where(d.SERIES_ID.eq(metadata.seriesId))
|
||||
.execute()
|
||||
@ -123,9 +135,14 @@ class SeriesMetadataDao(
|
||||
.where(sl.SERIES_ID.eq(metadata.seriesId))
|
||||
.execute()
|
||||
|
||||
dsl.deleteFrom(slk)
|
||||
.where(slk.SERIES_ID.eq(metadata.seriesId))
|
||||
.execute()
|
||||
|
||||
insertGenres(metadata)
|
||||
insertTags(metadata)
|
||||
insertSharingLabels(metadata)
|
||||
insertLinks(metadata)
|
||||
}
|
||||
|
||||
private fun insertGenres(metadata: SeriesMetadata) {
|
||||
@ -173,11 +190,27 @@ class SeriesMetadataDao(
|
||||
}
|
||||
}
|
||||
|
||||
private fun insertLinks(metadata: SeriesMetadata) {
|
||||
if (metadata.links.isNotEmpty()) {
|
||||
metadata.links.chunked(batchSize).forEach { chunk ->
|
||||
dsl.batch(
|
||||
dsl.insertInto(slk, slk.SERIES_ID, slk.LABEL, slk.URL)
|
||||
.values(null as String?, null, null),
|
||||
).also { step ->
|
||||
chunk.forEach {
|
||||
step.bind(metadata.seriesId, it.label, it.url.toString())
|
||||
}
|
||||
}.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(slk).where(slk.SERIES_ID.eq(seriesId)).execute()
|
||||
dsl.deleteFrom(d).where(d.SERIES_ID.eq(seriesId)).execute()
|
||||
}
|
||||
|
||||
@ -188,12 +221,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(slk).where(slk.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>, sharingLabels: Set<String>) =
|
||||
private fun SeriesMetadataRecord.toDomain(genres: Set<String>, tags: Set<String>, sharingLabels: Set<String>, links: List<WebLink>) =
|
||||
SeriesMetadata(
|
||||
status = SeriesMetadata.Status.valueOf(status),
|
||||
title = title,
|
||||
@ -209,6 +243,7 @@ class SeriesMetadataDao(
|
||||
tags = tags,
|
||||
totalBookCount = totalBookCount,
|
||||
sharingLabels = sharingLabels,
|
||||
links = links,
|
||||
|
||||
statusLock = statusLock,
|
||||
titleLock = titleLock,
|
||||
@ -222,6 +257,7 @@ class SeriesMetadataDao(
|
||||
tagsLock = tagsLock,
|
||||
totalBookCountLock = totalBookCountLock,
|
||||
sharingLabelsLock = sharingLabelsLock,
|
||||
linksLock = linksLock,
|
||||
|
||||
seriesId = seriesId,
|
||||
|
||||
|
@ -30,6 +30,7 @@ import org.gotson.komga.domain.model.SeriesMetadata
|
||||
import org.gotson.komga.domain.model.SeriesSearch
|
||||
import org.gotson.komga.domain.model.SeriesSearchWithReadProgress
|
||||
import org.gotson.komga.domain.model.ThumbnailSeries
|
||||
import org.gotson.komga.domain.model.WebLink
|
||||
import org.gotson.komga.domain.persistence.BookRepository
|
||||
import org.gotson.komga.domain.persistence.SeriesCollectionRepository
|
||||
import org.gotson.komga.domain.persistence.SeriesMetadataRepository
|
||||
@ -88,6 +89,7 @@ import org.springframework.web.multipart.MultipartFile
|
||||
import org.springframework.web.server.ResponseStatusException
|
||||
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody
|
||||
import java.io.OutputStream
|
||||
import java.net.URI
|
||||
import java.nio.charset.StandardCharsets.UTF_8
|
||||
import java.util.zip.Deflater
|
||||
import javax.validation.Valid
|
||||
@ -544,6 +546,10 @@ class SeriesController(
|
||||
if (sharingLabels != null) sharingLabels!! else emptySet()
|
||||
} else existing.sharingLabels,
|
||||
sharingLabelsLock = sharingLabelsLock ?: existing.sharingLabelsLock,
|
||||
links = if (isSet("links")) {
|
||||
if (links != null) links!!.map { WebLink(it.label!!, URI(it.url!!)) } else emptyList()
|
||||
} else existing.links,
|
||||
linksLock = linksLock ?: existing.linksLock,
|
||||
)
|
||||
}
|
||||
seriesMetadataRepository.update(updated)
|
||||
|
@ -52,6 +52,8 @@ data class SeriesMetadataDto(
|
||||
val totalBookCountLock: Boolean,
|
||||
val sharingLabels: Set<String>,
|
||||
val sharingLabelsLock: Boolean,
|
||||
val links: List<WebLinkDto>,
|
||||
val linksLock: Boolean,
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
|
||||
val created: LocalDateTime,
|
||||
|
@ -3,6 +3,7 @@ package org.gotson.komga.interfaces.api.rest.dto
|
||||
import org.gotson.komga.domain.model.SeriesMetadata
|
||||
import org.gotson.komga.infrastructure.validation.NullOrBlankOrBCP47
|
||||
import org.gotson.komga.infrastructure.validation.NullOrNotBlank
|
||||
import javax.validation.Valid
|
||||
import javax.validation.constraints.Positive
|
||||
import javax.validation.constraints.PositiveOrZero
|
||||
import kotlin.properties.Delegates
|
||||
@ -81,4 +82,12 @@ class SeriesMetadataUpdateDto {
|
||||
}
|
||||
|
||||
var sharingLabelsLock: Boolean? = null
|
||||
|
||||
@get:Valid
|
||||
var links: List<WebLinkUpdateDto>?
|
||||
by Delegates.observable(null) { prop, _, _ ->
|
||||
isSet[prop.name] = true
|
||||
}
|
||||
|
||||
var linksLock: Boolean? = null
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package org.gotson.komga.infrastructure.jooq
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.assertj.core.api.Assertions.catchThrowable
|
||||
import org.gotson.komga.domain.model.SeriesMetadata
|
||||
import org.gotson.komga.domain.model.WebLink
|
||||
import org.gotson.komga.domain.model.makeLibrary
|
||||
import org.gotson.komga.domain.model.makeSeries
|
||||
import org.gotson.komga.domain.persistence.LibraryRepository
|
||||
@ -15,6 +16,7 @@ import org.junit.jupiter.api.extension.ExtendWith
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.boot.test.context.SpringBootTest
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension
|
||||
import java.net.URI
|
||||
import java.time.LocalDateTime
|
||||
|
||||
@ExtendWith(SpringExtension::class)
|
||||
@ -63,6 +65,7 @@ class SeriesMetadataDaoTest(
|
||||
language = "en",
|
||||
totalBookCount = 5,
|
||||
sharingLabels = setOf("kids"),
|
||||
links = listOf(WebLink("Comicvine", URI("https://comicvine.gamespot.com/doctor-strange/4050-2676/"))),
|
||||
titleLock = true,
|
||||
titleSortLock = true,
|
||||
summaryLock = true,
|
||||
@ -74,6 +77,7 @@ class SeriesMetadataDaoTest(
|
||||
tagsLock = true,
|
||||
totalBookCountLock = true,
|
||||
sharingLabelsLock = true,
|
||||
linksLock = true,
|
||||
seriesId = series.id,
|
||||
)
|
||||
|
||||
@ -96,6 +100,10 @@ class SeriesMetadataDaoTest(
|
||||
assertThat(created.tags).containsAll(metadata.tags)
|
||||
assertThat(created.totalBookCount).isEqualTo(metadata.totalBookCount)
|
||||
assertThat(created.sharingLabels).containsAll(metadata.sharingLabels)
|
||||
with(created.links.first()) {
|
||||
assertThat(label).isEqualTo(metadata.links.first().label)
|
||||
assertThat(url).isEqualTo(metadata.links.first().url)
|
||||
}
|
||||
|
||||
assertThat(created.titleLock).isEqualTo(metadata.titleLock)
|
||||
assertThat(created.titleSortLock).isEqualTo(metadata.titleSortLock)
|
||||
@ -109,6 +117,7 @@ class SeriesMetadataDaoTest(
|
||||
assertThat(created.tagsLock).isEqualTo(metadata.tagsLock)
|
||||
assertThat(created.totalBookCountLock).isEqualTo(metadata.totalBookCountLock)
|
||||
assertThat(created.sharingLabelsLock).isEqualTo(metadata.sharingLabelsLock)
|
||||
assertThat(created.linksLock).isEqualTo(metadata.linksLock)
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -140,6 +149,7 @@ class SeriesMetadataDaoTest(
|
||||
assertThat(created.tags).isEmpty()
|
||||
assertThat(created.totalBookCount).isNull()
|
||||
assertThat(created.sharingLabels).isEmpty()
|
||||
assertThat(created.links).isEmpty()
|
||||
|
||||
assertThat(created.titleLock).isFalse
|
||||
assertThat(created.titleSortLock).isFalse
|
||||
@ -153,6 +163,7 @@ class SeriesMetadataDaoTest(
|
||||
assertThat(created.tagsLock).isFalse
|
||||
assertThat(created.totalBookCountLock).isFalse
|
||||
assertThat(created.sharingLabelsLock).isFalse
|
||||
assertThat(created.linksLock).isFalse
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -205,6 +216,7 @@ class SeriesMetadataDaoTest(
|
||||
tags = setOf("tag"),
|
||||
totalBookCount = 3,
|
||||
sharingLabels = setOf("kids"),
|
||||
links = listOf(WebLink("Comicvine", URI("https://comicvine.gamespot.com/doctor-strange/4050-2676/"))),
|
||||
seriesId = series.id,
|
||||
)
|
||||
seriesMetadataDao.insert(metadata)
|
||||
@ -226,6 +238,7 @@ class SeriesMetadataDaoTest(
|
||||
tags = setOf("Another"),
|
||||
totalBookCount = 8,
|
||||
sharingLabels = setOf("adult"),
|
||||
links = emptyList(),
|
||||
statusLock = true,
|
||||
titleLock = true,
|
||||
titleSortLock = true,
|
||||
@ -238,6 +251,7 @@ class SeriesMetadataDaoTest(
|
||||
tagsLock = true,
|
||||
totalBookCountLock = true,
|
||||
sharingLabelsLock = true,
|
||||
linksLock = true,
|
||||
)
|
||||
}
|
||||
|
||||
@ -261,6 +275,7 @@ class SeriesMetadataDaoTest(
|
||||
assertThat(modified.tags).containsAll(updated.tags)
|
||||
assertThat(modified.totalBookCount).isEqualTo(updated.totalBookCount)
|
||||
assertThat(modified.sharingLabels).containsAll(updated.sharingLabels)
|
||||
assertThat(modified.links).isEmpty()
|
||||
|
||||
assertThat(modified.titleLock).isTrue
|
||||
assertThat(modified.titleSortLock).isTrue
|
||||
@ -274,5 +289,6 @@ class SeriesMetadataDaoTest(
|
||||
assertThat(modified.tagsLock).isTrue
|
||||
assertThat(modified.totalBookCountLock).isTrue
|
||||
assertThat(modified.sharingLabelsLock).isTrue
|
||||
assertThat(modified.linksLock).isTrue
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user