From 8e0655f29a24fa9d887d7022d40ba2642edb3199 Mon Sep 17 00:00:00 2001 From: Gauthier Roebroeck Date: Mon, 16 Jan 2023 17:30:09 +0800 Subject: [PATCH] feat: series metadata supports alternate titles Closes: #878 --- komga-webui/src/components/ReadMore.vue | 12 ++- .../components/dialogs/EditSeriesDialog.vue | 98 ++++++++++++++++++- komga-webui/src/locales/en.json | 6 ++ komga-webui/src/types/komga-series.ts | 13 ++- komga-webui/src/views/BrowseSeries.vue | 33 ++++++- ...20230116112647__series_alternate_title.sql | 13 +++ .../komga/domain/model/AlternateTitle.kt | 6 ++ .../komga/domain/model/SeriesMetadata.kt | 8 +- .../komga/infrastructure/jooq/SeriesDtoDao.kt | 13 ++- .../infrastructure/jooq/SeriesMetadataDao.kt | 43 +++++++- .../infrastructure/search/LuceneEntity.kt | 1 + .../interfaces/api/rest/SeriesController.kt | 5 + .../api/rest/dto/AlternateTitleDto.kt | 10 ++ .../api/rest/dto/AlternateTitleUpdateDto.kt | 11 +++ .../interfaces/api/rest/dto/SeriesDto.kt | 2 + .../api/rest/dto/SeriesMetadataUpdateDto.kt | 8 ++ .../jooq/SeriesMetadataDaoTest.kt | 15 +++ 17 files changed, 282 insertions(+), 15 deletions(-) create mode 100644 komga/src/flyway/resources/db/migration/sqlite/V20230116112647__series_alternate_title.sql create mode 100644 komga/src/main/kotlin/org/gotson/komga/domain/model/AlternateTitle.kt create mode 100644 komga/src/main/kotlin/org/gotson/komga/interfaces/api/rest/dto/AlternateTitleDto.kt create mode 100644 komga/src/main/kotlin/org/gotson/komga/interfaces/api/rest/dto/AlternateTitleUpdateDto.kt diff --git a/komga-webui/src/components/ReadMore.vue b/komga-webui/src/components/ReadMore.vue index b18f95ae79c..cb1075ad374 100644 --- a/komga-webui/src/components/ReadMore.vue +++ b/komga-webui/src/components/ReadMore.vue @@ -5,7 +5,7 @@ @@ -20,6 +20,16 @@ import Vue from 'vue' export default Vue.extend({ name: 'ReadMore', components: { VueReadMoreSmooth }, + props: { + i18nMore: { + type: String, + default: 'read_more.more', + }, + i18nLess: { + type: String, + default: 'read_more.less', + }, + }, }) diff --git a/komga-webui/src/components/dialogs/EditSeriesDialog.vue b/komga-webui/src/components/dialogs/EditSeriesDialog.vue index f0a682e3bed..fd49d350cdd 100644 --- a/komga-webui/src/components/dialogs/EditSeriesDialog.vue +++ b/komga-webui/src/components/dialogs/EditSeriesDialog.vue @@ -27,6 +27,10 @@ mdi-format-align-center {{ $t('dialog.edit_series.tab_general') }} + + mdi-format-title + {{ $t('dialog.edit_series.tab_titles') }} + mdi-tag-multiple {{ $t('dialog.edit_series.tab_tags') }} @@ -258,6 +262,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + mdi-plus + + + + + + + @@ -500,6 +575,7 @@ export default Vue.extend({ modal: false, tab: 0, linksValid: false, + titlesValid: false, form: { status: '', statusLock: false, @@ -527,6 +603,8 @@ export default Vue.extend({ sharingLabelsLock: false, links: [], linksLock: false, + alternateTitles: [], + alternateTitlesLock: false, }, mixed: { status: false, @@ -601,6 +679,7 @@ export default Vue.extend({ publisher: {}, totalBookCount: {minValue: minValue(1)}, links: {}, + alternateTitles: {}, }, }, computed: { @@ -645,6 +724,10 @@ export default Vue.extend({ }, }, methods: { + alternateTitleRules(text: string): boolean | string { + if (!!this.$_.trim(text)) return true + return this.$t('common.required').toString() + }, linksLabelRules(label: string): boolean | string { if (!!this.$_.trim(label)) return true return this.$t('common.required').toString() @@ -678,7 +761,8 @@ export default Vue.extend({ dialogReset(series: SeriesDto | SeriesDto[]) { this.tab = 0 this.$v.$reset(); - (this.$refs.linksForm as any)?.resetValidation() + (this.$refs.linksForm as any)?.resetValidation(); + (this.$refs.titlesForm 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)) @@ -732,11 +816,13 @@ export default Vue.extend({ this.form.sharingLabelsLock = sharingLabelsLock.length > 1 ? false : sharingLabelsLock[0] this.form.links = [] + this.form.alternateTitles = [] } else { this.form.genres = [] this.form.tags = [] this.form.sharingLabels = [] this.form.links = [] + this.form.alternateTitles = [] this.$_.merge(this.form, (series as SeriesDto).metadata) this.poster.selectedThumbnail = '' this.poster.deleteQueue = [] @@ -754,7 +840,10 @@ export default Vue.extend({ } }, validateForm(): any { - if (!this.$v.$invalid && (!this.single || !this.$refs.linksForm || (this.$refs.linksForm as any).validate())) { + if (!this.$v.$invalid + && (!this.single || !this.$refs.linksForm || (this.$refs.linksForm as any).validate()) + && (!this.single || !this.$refs.titlesForm || (this.$refs.titlesForm as any).validate()) + ) { const metadata = { statusLock: this.form.statusLock, readingDirectionLock: this.form.readingDirectionLock, @@ -766,6 +855,7 @@ export default Vue.extend({ totalBookCountLock: this.form.totalBookCountLock, sharingLabelsLock: this.form.sharingLabelsLock, linksLock: this.form.linksLock, + alternateTitlesLock: this.form.alternateTitlesLock, } if (this.$v.form?.status?.$dirty) { @@ -827,6 +917,10 @@ export default Vue.extend({ if (this.$v.form?.links?.$dirty || this.form.links.length != (this.series as SeriesDto).metadata.links?.length) { this.$_.merge(metadata, {links: this.form.links}) } + + if (this.$v.form?.alternateTitles?.$dirty || this.form.alternateTitles.length != (this.series as SeriesDto).metadata.alternateTitles?.length) { + this.$_.merge(metadata, {alternateTitles: this.form.alternateTitles}) + } } return metadata diff --git a/komga-webui/src/locales/en.json b/komga-webui/src/locales/en.json index b1a6dd8a1b3..63ccb52ea68 100644 --- a/komga-webui/src/locales/en.json +++ b/komga-webui/src/locales/en.json @@ -347,6 +347,7 @@ "button_confirm": "Save changes", "dialog_title_multiple": "Edit {count} book | Edit {count} books", "dialog_title_single": "Edit {book}", + "field_alternate_title": "Alternate title", "field_isbn": "ISBN", "field_isbn_error": "Must be a valid ISBN 13", "field_link_label": "Label", @@ -457,6 +458,7 @@ "tab_poster": "Poster", "tab_sharing": "Sharing", "tab_tags": "Tags", + "tab_titles": "Alternate Titles", "tags_notice_multiple_edit": "You are editing tags for multiple series. This will override existing tags of each series." }, "edit_user": { @@ -812,6 +814,10 @@ "tooltip_too_big": "File too big!", "tooltip_user_uploaded": "User uploaded" }, + "titles_more": { + "less": "Less titles", + "more": "More titles" + }, "user_roles": { "ADMIN": "Administrator", "FILE_DOWNLOAD": "File download", diff --git a/komga-webui/src/types/komga-series.ts b/komga-webui/src/types/komga-series.ts index 83523e495e5..bc2962c9437 100644 --- a/komga-webui/src/types/komga-series.ts +++ b/komga-webui/src/types/komga-series.ts @@ -47,8 +47,10 @@ export interface SeriesMetadataDto { totalBookCountLock: boolean, sharingLabels: string[], sharingLabelsLock: boolean, - links?: WebLinkDto[], - linksLock?: boolean + links: WebLinkDto[], + linksLock: boolean, + alternateTitles: AlternateTitleDto[], + alternateTitlesLock: boolean, } export interface SeriesBooksMetadataDto { @@ -88,6 +90,8 @@ export interface SeriesMetadataUpdateDto { sharingLabelsLock: boolean, links?: WebLinkDto[], linksLock?: boolean, + alternateTitles?: AlternateTitleDto[], + alternateTitlesLock?: boolean, } export interface GroupCountDto { @@ -101,3 +105,8 @@ export interface SeriesThumbnailDto { type: string, selected: boolean } + +export interface AlternateTitleDto { + label: string, + title: string +} diff --git a/komga-webui/src/views/BrowseSeries.vue b/komga-webui/src/views/BrowseSeries.vue index 61e1e7f5d86..6387cd798f3 100644 --- a/komga-webui/src/views/BrowseSeries.vue +++ b/komga-webui/src/views/BrowseSeries.vue @@ -113,7 +113,10 @@ {{ $t('browse_series.earliest_year_from_release_dates') }} @@ -166,6 +169,17 @@