diff --git a/komga-webui/src/components/ItemCard.vue b/komga-webui/src/components/ItemCard.vue
index 908994244..59f61a20c 100644
--- a/komga-webui/src/components/ItemCard.vue
+++ b/komga-webui/src/components/ItemCard.vue
@@ -129,8 +129,18 @@ import {RawLocation} from 'vue-router'
import ReadListActionsMenu from '@/components/menus/ReadListActionsMenu.vue'
import {BookDto} from '@/types/komga-books'
import {SeriesDto} from '@/types/komga-series'
-import {THUMBNAILBOOK_ADDED, THUMBNAILSERIES_ADDED} from '@/types/events'
-import {ThumbnailBookSseDto, ThumbnailSeriesSseDto} from '@/types/komga-sse'
+import {
+ THUMBNAILBOOK_ADDED, THUMBNAILBOOK_DELETED,
+ THUMBNAILCOLLECTION_ADDED, THUMBNAILCOLLECTION_DELETED,
+ THUMBNAILREADLIST_ADDED, THUMBNAILREADLIST_DELETED,
+ THUMBNAILSERIES_ADDED, THUMBNAILSERIES_DELETED,
+} from '@/types/events'
+import {
+ ThumbnailBookSseDto,
+ ThumbnailCollectionSseDto,
+ ThumbnailReadListSseDto,
+ ThumbnailSeriesSseDto,
+} from '@/types/komga-sse'
import {coverBase64} from '@/types/image'
export default Vue.extend({
@@ -194,12 +204,30 @@ export default Vue.extend({
}
},
created() {
- this.$eventHub.$on(THUMBNAILBOOK_ADDED, this.thumbnailBookAdded)
- this.$eventHub.$on(THUMBNAILSERIES_ADDED, this.thumbnailSeriesAdded)
+ this.$eventHub.$on(THUMBNAILBOOK_ADDED, this.thumbnailBookChanged)
+ this.$eventHub.$on(THUMBNAILBOOK_DELETED, this.thumbnailBookChanged)
+
+ this.$eventHub.$on(THUMBNAILSERIES_ADDED, this.thumbnailSeriesChanged)
+ this.$eventHub.$on(THUMBNAILSERIES_DELETED, this.thumbnailSeriesChanged)
+
+ this.$eventHub.$on(THUMBNAILREADLIST_ADDED, this.thumbnailReadListChanged)
+ this.$eventHub.$on(THUMBNAILREADLIST_DELETED, this.thumbnailReadListChanged)
+
+ this.$eventHub.$on(THUMBNAILCOLLECTION_ADDED, this.thumbnailCollectionChanged)
+ this.$eventHub.$on(THUMBNAILCOLLECTION_DELETED, this.thumbnailCollectionChanged)
},
beforeDestroy() {
- this.$eventHub.$off(THUMBNAILBOOK_ADDED, this.thumbnailBookAdded)
- this.$eventHub.$off(THUMBNAILSERIES_ADDED, this.thumbnailSeriesAdded)
+ this.$eventHub.$off(THUMBNAILBOOK_ADDED, this.thumbnailBookChanged)
+ this.$eventHub.$off(THUMBNAILBOOK_DELETED, this.thumbnailBookChanged)
+
+ this.$eventHub.$off(THUMBNAILSERIES_ADDED, this.thumbnailSeriesChanged)
+ this.$eventHub.$off(THUMBNAILSERIES_DELETED, this.thumbnailSeriesChanged)
+
+ this.$eventHub.$off(THUMBNAILREADLIST_ADDED, this.thumbnailReadListChanged)
+ this.$eventHub.$off(THUMBNAILREADLIST_DELETED, this.thumbnailReadListChanged)
+
+ this.$eventHub.$off(THUMBNAILCOLLECTION_ADDED, this.thumbnailCollectionChanged)
+ this.$eventHub.$off(THUMBNAILCOLLECTION_DELETED, this.thumbnailCollectionChanged)
},
computed: {
canReadPages(): boolean {
@@ -259,15 +287,25 @@ export default Vue.extend({
},
},
methods: {
- thumbnailBookAdded(event: ThumbnailBookSseDto) {
- if (this.thumbnailError &&
- ((this.computedItem.type() === ItemTypes.BOOK && event.bookId === this.item.id) || (this.computedItem.type() === ItemTypes.SERIES && event.seriesId === this.item.id))
+ thumbnailBookChanged(event: ThumbnailBookSseDto) {
+ if (event.selected && (this.computedItem.type() === ItemTypes.BOOK && event.bookId === this.item.id)
+ || (this.computedItem.type() === ItemTypes.SERIES && event.seriesId === this.item.id)
) {
this.thumbnailCacheBust = '?' + this.$_.random(1000)
}
},
- thumbnailSeriesAdded(event: ThumbnailSeriesSseDto) {
- if (this.computedItem.type() === ItemTypes.SERIES && event.seriesId === this.item.id) {
+ thumbnailSeriesChanged(event: ThumbnailSeriesSseDto) {
+ if (event.selected && this.computedItem.type() === ItemTypes.SERIES && event.seriesId === this.item.id) {
+ this.thumbnailCacheBust = '?' + this.$_.random(1000)
+ }
+ },
+ thumbnailReadListChanged(event: ThumbnailReadListSseDto) {
+ if (event.selected && this.computedItem.type() === ItemTypes.READLIST && event.readListId === this.item.id) {
+ this.thumbnailCacheBust = '?' + this.$_.random(1000)
+ }
+ },
+ thumbnailCollectionChanged(event: ThumbnailCollectionSseDto) {
+ if (event.selected && this.computedItem.type() === ItemTypes.COLLECTION && event.collectionId === this.item.id) {
this.thumbnailCacheBust = '?' + this.$_.random(1000)
}
},
diff --git a/komga-webui/src/components/ThumbnailCard.vue b/komga-webui/src/components/ThumbnailCard.vue
index 5a59a486a..4c5dc8fa8 100644
--- a/komga-webui/src/components/ThumbnailCard.vue
+++ b/komga-webui/src/components/ThumbnailCard.vue
@@ -56,7 +56,13 @@
diff --git a/komga-webui/src/components/dialogs/EditBooksDialog.vue b/komga-webui/src/components/dialogs/EditBooksDialog.vue
index 4b6f06364..a3739b3f0 100644
--- a/komga-webui/src/components/dialogs/EditBooksDialog.vue
+++ b/komga-webui/src/components/dialogs/EditBooksDialog.vue
@@ -39,6 +39,10 @@
mdi-link
{{ $t('dialog.edit_books.tab_links') }}
+
+ mdi-image
+ {{ $t('dialog.edit_books.tab_poster') }}
+
@@ -357,6 +361,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -374,16 +409,19 @@ import {groupAuthorsByRole} from '@/functions/authors'
import {authorRoles} from '@/types/author-roles'
import Vue from 'vue'
import {helpers, requiredIf} from 'vuelidate/lib/validators'
-import {BookDto} from '@/types/komga-books'
+import {BookDto, BookThumbnailDto} from '@/types/komga-books'
import IsbnVerify from '@saekitominaga/isbn-verify'
import {isMatch} from 'date-fns'
-import {ERROR} from '@/types/events'
+import {ERROR, ErrorEvent} from '@/types/events'
+import DropZone from '@/components/DropZone.vue'
+import ThumbnailCard from '@/components/ThumbnailCard.vue'
const validDate = (value: string) => !helpers.req(value) || isMatch(value, 'yyyy-MM-dd')
const validIsbn = (value: string) => !helpers.req(value) || new IsbnVerify(value).isIsbn13({check_digit: true})
export default Vue.extend({
name: 'EditBooksDialog',
+ components: {ThumbnailCard, DropZone},
data: () => {
return {
modal: false,
@@ -412,6 +450,12 @@ export default Vue.extend({
links: [],
linksLock: false,
},
+ poster: {
+ selectedThumbnail: '',
+ uploadQueue: [] as File[],
+ deleteQueue: [] as BookThumbnailDto[],
+ bookThumbnails: [] as BookThumbnailDto[],
+ },
authorSearch: [],
authorSearchResults: [] as string[],
tagsAvailable: [] as string[],
@@ -430,6 +474,7 @@ export default Vue.extend({
},
modal(val) {
!val && this.dialogCancel()
+ val && this.getThumbnails(this.books)
},
books: {
immediate: true,
@@ -571,6 +616,10 @@ export default Vue.extend({
const book = books as BookDto
this.$_.merge(this.form, book.metadata)
this.form.authors = groupAuthorsByRole(book.metadata.authors)
+ this.poster.selectedThumbnail = ''
+ this.poster.deleteQueue = []
+ this.poster.uploadQueue = []
+ this.poster.bookThumbnails = []
}
},
dialogCancel() {
@@ -646,6 +695,36 @@ export default Vue.extend({
return null
},
async editBooks(): Promise {
+ if (this.single && this.poster.uploadQueue.length > 0) {
+ const book = this.books as BookDto
+ let hadErrors = false
+ for (const file of this.poster.uploadQueue.slice()) {
+ try {
+ await this.$komgaBooks.uploadThumbnail(book.id, file, file.name === this.poster.selectedThumbnail)
+ this.deleteThumbnail(file)
+ } catch (e) {
+ this.$eventHub.$emit(ERROR, {message: e.message} as ErrorEvent)
+ hadErrors = true
+ }
+ }
+ if (hadErrors) {
+ await this.getThumbnails(book)
+ return false
+ }
+ }
+
+ if (this.single && this.poster.selectedThumbnail !== '') {
+ const id = this.poster.selectedThumbnail
+ const book = this.books as BookDto
+ if (this.poster.bookThumbnails.find(value => value.id === id)) {
+ await this.$komgaBooks.markThumbnailAsSelected(book.id, id)
+ }
+ }
+
+ if (this.single && this.poster.deleteQueue.length > 0) {
+ this.poster.deleteQueue.forEach(toDelete => this.$komgaBooks.deleteThumbnail(toDelete.bookId, toDelete.id))
+ }
+
const metadata = this.validateForm()
if (metadata) {
const toUpdate = (this.single ? [this.books] : this.books) as BookDto[]
@@ -659,6 +738,73 @@ export default Vue.extend({
return true
} else return false
},
+ addThumbnail(files: File[]) {
+ let hasSelected = false
+ for (const file of files) {
+ if (!this.poster.uploadQueue.find(value => value.name === file.name)) {
+ this.poster.uploadQueue.push(file)
+ if (!hasSelected) {
+ this.selectThumbnail(file)
+ hasSelected = true
+ }
+ }
+ }
+
+ (this.$refs.thumbnailsUpload as any).reset()
+ },
+ async getThumbnails(book: BookDto | BookDto[]) {
+ if (Array.isArray(book)) return
+
+ const thumbnails = await this.$komgaBooks.getThumbnails(book.id)
+
+ this.selectThumbnail(thumbnails.find(x => x.selected))
+
+ this.poster.bookThumbnails = thumbnails
+ },
+ isThumbnailSelected(item: File | BookThumbnailDto): boolean {
+ return item instanceof File ? item.name === this.poster.selectedThumbnail : item.id === this.poster.selectedThumbnail
+ },
+ selectThumbnail(item: File | BookThumbnailDto | undefined) {
+ if (!item) {
+ return
+ } else if (item instanceof File) {
+ this.poster.selectedThumbnail = item.name
+ } else {
+ const index = this.poster.deleteQueue.indexOf(item, 0)
+ if (index > -1) this.poster.deleteQueue.splice(index, 1)
+
+ this.poster.selectedThumbnail = item.id
+ }
+ },
+ isThumbnailToBeDeleted(item: File | BookThumbnailDto) {
+ if (item instanceof File) {
+ return false
+ } else {
+ return this.poster.deleteQueue.includes(item)
+ }
+ },
+ deleteThumbnail(item: File | BookThumbnailDto) {
+ if (item instanceof File) {
+ const index = this.poster.uploadQueue.indexOf(item, 0)
+ if (index > -1) {
+ this.poster.uploadQueue.splice(index, 1)
+ }
+ if (item.name === this.poster.selectedThumbnail) {
+ this.poster.selectedThumbnail = ''
+ }
+ } else {
+ // if thumbnail was marked for deletion, unmark it
+ if (this.isThumbnailToBeDeleted(item)) {
+ const index = this.poster.deleteQueue.indexOf(item, 0)
+ if (index > -1) {
+ this.poster.deleteQueue.splice(index, 1)
+ }
+ } else {
+ this.poster.deleteQueue.push(item)
+ if (item.id === this.poster.selectedThumbnail) this.poster.selectedThumbnail = ''
+ }
+ }
+ },
},
})
diff --git a/komga-webui/src/components/dialogs/EditSeriesDialog.vue b/komga-webui/src/components/dialogs/EditSeriesDialog.vue
index 2a32954f9..6334417ec 100644
--- a/komga-webui/src/components/dialogs/EditSeriesDialog.vue
+++ b/komga-webui/src/components/dialogs/EditSeriesDialog.vue
@@ -737,6 +737,9 @@ export default Vue.extend({
} else if (item instanceof File) {
this.poster.selectedThumbnail = item.name
} else {
+ const index = this.poster.deleteQueue.indexOf(item, 0)
+ if (index > -1) this.poster.deleteQueue.splice(index, 1)
+
this.poster.selectedThumbnail = item.id
}
},
@@ -765,6 +768,7 @@ export default Vue.extend({
}
} else {
this.poster.deleteQueue.push(item)
+ if (item.id === this.poster.selectedThumbnail) this.poster.selectedThumbnail = ''
}
}
},
diff --git a/komga-webui/src/components/dialogs/ReadListEditDialog.vue b/komga-webui/src/components/dialogs/ReadListEditDialog.vue
index 94c2eee9f..4e860e07a 100644
--- a/komga-webui/src/components/dialogs/ReadListEditDialog.vue
+++ b/komga-webui/src/components/dialogs/ReadListEditDialog.vue
@@ -1,66 +1,130 @@
-
- {{ $t('dialog.edit_readlist.dialog_title') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t('dialog.edit_readlist.button_cancel') }}
+ {{ $t('dialog.edit_readlist.button_confirm') }}
+
+
+
+
diff --git a/komga-webui/src/functions/urls.ts b/komga-webui/src/functions/urls.ts
index e592aa0c3..a54774c1e 100644
--- a/komga-webui/src/functions/urls.ts
+++ b/komga-webui/src/functions/urls.ts
@@ -12,15 +12,19 @@ const urls = {
export default urls
-export function bookThumbnailUrl (bookId: string): string {
+export function bookThumbnailUrl(bookId: string): string {
return `${urls.originNoSlash}/api/v1/books/${bookId}/thumbnail`
}
-export function bookFileUrl (bookId: string): string {
+export function bookThumbnailUrlByThumbnailId(bookId: string, thumbnailId: string) {
+ return `${urls.originNoSlash}/api/v1/books/${bookId}/thumbnails/${thumbnailId}`
+}
+
+export function bookFileUrl(bookId: string): string {
return `${urls.originNoSlash}/api/v1/books/${bookId}/file`
}
-export function bookPageUrl (bookId: string, page: number, convertTo?: string): string {
+export function bookPageUrl(bookId: string, page: number, convertTo?: string): string {
let url = `${urls.originNoSlash}/api/v1/books/${bookId}/pages/${page}`
if (convertTo) {
url += `?convert=${convertTo}`
@@ -28,15 +32,15 @@ export function bookPageUrl (bookId: string, page: number, convertTo?: string):
return url
}
-export function bookPageThumbnailUrl (bookId: string, page: number): string {
+export function bookPageThumbnailUrl(bookId: string, page: number): string {
return `${urls.originNoSlash}/api/v1/books/${bookId}/pages/${page}/thumbnail`
}
-export function seriesFileUrl (seriesId: string): string {
+export function seriesFileUrl(seriesId: string): string {
return `${urls.originNoSlash}/api/v1/series/${seriesId}/file`
}
-export function seriesThumbnailUrl (seriesId: string): string {
+export function seriesThumbnailUrl(seriesId: string): string {
return `${urls.originNoSlash}/api/v1/series/${seriesId}/thumbnail`
}
@@ -44,18 +48,26 @@ export function seriesThumbnailUrlByThumbnailId(seriesId: string, thumbnailId: s
return `${urls.originNoSlash}/api/v1/series/${seriesId}/thumbnails/${thumbnailId}`
}
-export function collectionThumbnailUrl (collectionId: string): string {
+export function collectionThumbnailUrl(collectionId: string): string {
return `${urls.originNoSlash}/api/v1/collections/${collectionId}/thumbnail`
}
-export function readListThumbnailUrl (readListId: string): string {
+export function collectionThumbnailUrlByThumbnailId(collectionId: string, thumbnailId: string) {
+ return `${urls.originNoSlash}/api/v1/collections/${collectionId}/thumbnails/${thumbnailId}`
+}
+
+export function readListThumbnailUrl(readListId: string): string {
return `${urls.originNoSlash}/api/v1/readlists/${readListId}/thumbnail`
}
-export function readListFileUrl (readListId: string): string {
+export function readListFileUrl(readListId: string): string {
return `${urls.originNoSlash}/api/v1/readlists/${readListId}/file`
}
-export function transientBookPageUrl (transientBookId: string, page: number): string {
+export function readListThumbnailUrlByThumbnailId(readListId: string, thumbnailId: string) {
+ return `${urls.originNoSlash}/api/v1/readlists/${readListId}/thumbnails/${thumbnailId}`
+}
+
+export function transientBookPageUrl(transientBookId: string, page: number): string {
return `${urls.originNoSlash}/api/v1/transient-books/${transientBookId}/pages/${page}`
}
diff --git a/komga-webui/src/locales/en.json b/komga-webui/src/locales/en.json
index f7dfd093e..3e63dc2f1 100644
--- a/komga-webui/src/locales/en.json
+++ b/komga-webui/src/locales/en.json
@@ -345,6 +345,7 @@
"tab_authors": "Authors",
"tab_general": "General",
"tab_links": "Links",
+ "tab_poster": "Poster",
"tab_tags": "Tags",
"tags_notice_multiple_edit": "You are editing tags for multiple books. This will override existing tags of each book."
},
@@ -353,7 +354,9 @@
"button_confirm": "Save changes",
"dialog_title": "Edit collection",
"field_manual_ordering": "Manual ordering",
- "label_ordering": "By default, series in a collection will be ordered by name. You can enable manual ordering to define your own order."
+ "label_ordering": "By default, series in a collection will be ordered by name. You can enable manual ordering to define your own order.",
+ "tab_general": "General",
+ "tab_poster": "Poster"
},
"edit_library": {
"button_browse": "Browse",
@@ -405,7 +408,9 @@
"button_confirm": "Save changes",
"dialog_title": "Edit read list",
"field_name": "Name",
- "field_summary": "Summary"
+ "field_summary": "Summary",
+ "tab_general": "General",
+ "tab_poster": "Poster"
},
"edit_series": {
"button_cancel": "Cancel",
@@ -716,6 +721,7 @@
},
"thumbnail_card": {
"tooltip_delete": "Delete",
+ "tooltip_generated": "Generated artwork",
"tooltip_mark_as_selected": "Mark as selected",
"tooltip_selected": "Selected",
"tooltip_sidecar": "Local artwork",
diff --git a/komga-webui/src/services/komga-books.service.ts b/komga-webui/src/services/komga-books.service.ts
index d97af4d09..9db5211f6 100644
--- a/komga-webui/src/services/komga-books.service.ts
+++ b/komga-webui/src/services/komga-books.service.ts
@@ -4,6 +4,7 @@ import {
BookImportBatchDto,
BookMetadataUpdateBatchDto,
BookMetadataUpdateDto,
+ BookThumbnailDto,
PageDto,
ReadProgressUpdateDto,
} from '@/types/komga-books'
@@ -239,4 +240,55 @@ export default class KomgaBooksService {
throw new Error(msg)
}
}
+
+ async getThumbnails(bookId: string): Promise {
+ try {
+ return (await this.http.get(`${API_BOOKS}/${bookId}/thumbnails`)).data
+ } catch (e) {
+ let msg = `An error occurred while trying to retrieve thumbnails for book '${bookId}'`
+ if (e.response.data.message) {
+ msg += `: ${e.response.data.message}`
+ }
+ throw new Error(msg)
+ }
+ }
+
+ async uploadThumbnail(bookId: string, file: File, selected: boolean) {
+ try {
+ const body = new FormData()
+ body.append('file', file)
+ body.append('selected', `${selected}`)
+ await this.http.post(`${API_BOOKS}/${bookId}/thumbnails`, body)
+ } catch (e) {
+ let msg = `An error occurred while trying to upload thumbnail for book '${bookId}'`
+ if (e.response.data.message) {
+ msg += `: ${e.response.data.message}`
+ }
+ throw new Error(msg)
+ }
+ }
+
+ async deleteThumbnail(bookId: string, thumbnailId: string) {
+ try {
+ await this.http.delete(`${API_BOOKS}/${bookId}/thumbnails/${thumbnailId}`)
+ } catch (e) {
+ let msg = `An error occurred while trying to delete thumbnail for book '${bookId}'`
+ if (e.response.data.message) {
+ msg += `: ${e.response.data.message}`
+ }
+ throw new Error(msg)
+ }
+ }
+
+ async markThumbnailAsSelected(bookId: string, thumbnailId: string) {
+ try {
+ await this.http.put(`${API_BOOKS}/${bookId}/thumbnails/${thumbnailId}/selected`)
+ } catch (e) {
+ let msg = `An error occurred while trying to mark thumbnail as selected for book '${bookId}'`
+ if (e.response.data.message) {
+ msg += `: ${e.response.data.message}`
+ }
+ throw new Error(msg)
+ }
+ }
}
diff --git a/komga-webui/src/services/komga-collections.service.ts b/komga-webui/src/services/komga-collections.service.ts
index 332495d17..951639b42 100644
--- a/komga-webui/src/services/komga-collections.service.ts
+++ b/komga-webui/src/services/komga-collections.service.ts
@@ -104,7 +104,58 @@ export default class KomgaCollectionsService {
paramsSerializer: params => qs.stringify(params, {indices: false}),
})).data
} catch (e) {
- let msg = 'An error occurred while trying to retrieve series'
+ let msg = `An error occurred while trying to retrieve series for collection '${collectionId}'`
+ if (e.response.data.message) {
+ msg += `: ${e.response.data.message}`
+ }
+ throw new Error(msg)
+ }
+ }
+
+ async getThumbnails(collectionId: string): Promise {
+ try {
+ return (await this.http.get(`${API_COLLECTIONS}/${collectionId}/thumbnails`)).data
+ } catch (e) {
+ let msg = `An error occurred while trying to retrieve thumbnails for collection '${collectionId}'`
+ if (e.response.data.message) {
+ msg += `: ${e.response.data.message}`
+ }
+ throw new Error(msg)
+ }
+ }
+
+ async uploadThumbnail(collecitonId: string, file: File, selected: boolean) {
+ try {
+ const body = new FormData()
+ body.append('file', file)
+ body.append('selected', `${selected}`)
+ await this.http.post(`${API_COLLECTIONS}/${collecitonId}/thumbnails`, body)
+ } catch (e) {
+ let msg = `An error occurred while trying to upload thumbnail for collection '${collecitonId}'`
+ if (e.response.data.message) {
+ msg += `: ${e.response.data.message}`
+ }
+ throw new Error(msg)
+ }
+ }
+
+ async deleteThumbnail(collectionId: string, thumbnailId: string) {
+ try {
+ await this.http.delete(`${API_COLLECTIONS}/${collectionId}/thumbnails/${thumbnailId}`)
+ } catch (e) {
+ let msg = `An error occurred while trying to delete thumbnail for collection '${collectionId}'`
+ if (e.response.data.message) {
+ msg += `: ${e.response.data.message}`
+ }
+ throw new Error(msg)
+ }
+ }
+
+ async markThumbnailAsSelected(collectionId: string, thumbnailId: string) {
+ try {
+ await this.http.put(`${API_COLLECTIONS}/${collectionId}/thumbnails/${thumbnailId}/selected`)
+ } catch (e) {
+ let msg = `An error occurred while trying to mark thumbnail as selected for collection '${collectionId}'`
if (e.response.data.message) {
msg += `: ${e.response.data.message}`
}
diff --git a/komga-webui/src/services/komga-readlists.service.ts b/komga-webui/src/services/komga-readlists.service.ts
index e7d259a41..4aa040c00 100644
--- a/komga-webui/src/services/komga-readlists.service.ts
+++ b/komga-webui/src/services/komga-readlists.service.ts
@@ -109,7 +109,7 @@ export default class KomgaReadListsService {
return (await this.http.get(`${API_READLISTS}/${readListId}/books`, {
params: params,
- paramsSerializer: params => qs.stringify(params, { indices: false }),
+ paramsSerializer: params => qs.stringify(params, {indices: false}),
})).data
} catch (e) {
let msg = 'An error occurred while trying to retrieve books'
@@ -143,4 +143,55 @@ export default class KomgaReadListsService {
throw new Error(msg)
}
}
+
+ async getThumbnails(readListId: string): Promise {
+ try {
+ return (await this.http.get(`${API_READLISTS}/${readListId}/thumbnails`)).data
+ } catch (e) {
+ let msg = `An error occurred while trying to retrieve thumbnails for readlist '${readListId}'`
+ if (e.response.data.message) {
+ msg += `: ${e.response.data.message}`
+ }
+ throw new Error(msg)
+ }
+ }
+
+ async uploadThumbnail(readListId: string, file: File, selected: boolean) {
+ try {
+ const body = new FormData()
+ body.append('file', file)
+ body.append('selected', `${selected}`)
+ await this.http.post(`${API_READLISTS}/${readListId}/thumbnails`, body)
+ } catch (e) {
+ let msg = `An error occurred while trying to upload thumbnail for readlist '${readListId}'`
+ if (e.response.data.message) {
+ msg += `: ${e.response.data.message}`
+ }
+ throw new Error(msg)
+ }
+ }
+
+ async deleteThumbnail(readListId: string, thumbnailId: string) {
+ try {
+ await this.http.delete(`${API_READLISTS}/${readListId}/thumbnails/${thumbnailId}`)
+ } catch (e) {
+ let msg = `An error occurred while trying to delete thumbnail for readlist '${readListId}'`
+ if (e.response.data.message) {
+ msg += `: ${e.response.data.message}`
+ }
+ throw new Error(msg)
+ }
+ }
+
+ async markThumbnailAsSelected(readListId: string, thumbnailId: string) {
+ try {
+ await this.http.put(`${API_READLISTS}/${readListId}/thumbnails/${thumbnailId}/selected`)
+ } catch (e) {
+ let msg = `An error occurred while trying to mark thumbnail as selected for readlist '${readListId}'`
+ if (e.response.data.message) {
+ msg += `: ${e.response.data.message}`
+ }
+ throw new Error(msg)
+ }
+ }
}
diff --git a/komga-webui/src/services/komga-sse.service.ts b/komga-webui/src/services/komga-sse.service.ts
index d287a49fd..c020dbeeb 100644
--- a/komga-webui/src/services/komga-sse.service.ts
+++ b/komga-webui/src/services/komga-sse.service.ts
@@ -21,7 +21,13 @@ import {
SERIES_CHANGED,
SERIES_DELETED,
THUMBNAILBOOK_ADDED,
+ THUMBNAILBOOK_DELETED,
+ THUMBNAILCOLLECTION_ADDED,
+ THUMBNAILCOLLECTION_DELETED,
+ THUMBNAILREADLIST_ADDED,
+ THUMBNAILREADLIST_DELETED,
THUMBNAILSERIES_ADDED,
+ THUMBNAILSERIES_DELETED,
} from '@/types/events'
import Vue from 'vue'
import {TaskQueueSseDto} from '@/types/komga-sse'
@@ -83,7 +89,16 @@ export default class KomgaSseService {
// Thumbnails
this.eventSource.addEventListener('ThumbnailBookAdded', (event: any) => this.emit(THUMBNAILBOOK_ADDED, event))
+ this.eventSource.addEventListener('ThumbnailBookDeleted', (event: any) => this.emit(THUMBNAILBOOK_DELETED, event))
+
this.eventSource.addEventListener('ThumbnailSeriesAdded', (event: any) => this.emit(THUMBNAILSERIES_ADDED, event))
+ this.eventSource.addEventListener('ThumbnailSeriesDeleted', (event: any) => this.emit(THUMBNAILSERIES_DELETED, event))
+
+ this.eventSource.addEventListener('ThumbnailReadListAdded', (event: any) => this.emit(THUMBNAILREADLIST_ADDED, event))
+ this.eventSource.addEventListener('ThumbnailReadListDeleted', (event: any) => this.emit(THUMBNAILREADLIST_DELETED, event))
+
+ this.eventSource.addEventListener('ThumbnailSeriesCollectionAdded', (event: any) => this.emit(THUMBNAILCOLLECTION_ADDED, event))
+ this.eventSource.addEventListener('ThumbnailSeriesCollectionDeleted', (event: any) => this.emit(THUMBNAILCOLLECTION_DELETED, event))
this.eventSource.addEventListener('TaskQueueStatus', (event: any) => this.updateTaskCount(event))
}
diff --git a/komga-webui/src/types/events.ts b/komga-webui/src/types/events.ts
index cbb9a9c49..07fc29fda 100644
--- a/komga-webui/src/types/events.ts
+++ b/komga-webui/src/types/events.ts
@@ -25,7 +25,16 @@ export const READPROGRESS_SERIES_CHANGED = 'readprogress-series-changed'
export const READPROGRESS_SERIES_DELETED = 'readprogress-series-deleted'
export const THUMBNAILBOOK_ADDED = 'thumbnailbook-added'
+export const THUMBNAILBOOK_DELETED = 'thumbnailbook-deleted'
+
export const THUMBNAILSERIES_ADDED = 'thumbnailseries-added'
+export const THUMBNAILSERIES_DELETED = 'thumbnailseries-deleted'
+
+export const THUMBNAILREADLIST_ADDED = 'thumbnailreadlist-added'
+export const THUMBNAILREADLIST_DELETED = 'thumbnailreadlist-deleted'
+
+export const THUMBNAILCOLLECTION_ADDED = 'thumbnailcollection-added'
+export const THUMBNAILCOLLECTION_DELETED = 'thumbnailcollection-deleted'
export const ERROR = 'error'
export const NOTIFICATION = 'notification'
diff --git a/komga-webui/src/types/komga-books.ts b/komga-webui/src/types/komga-books.ts
index a3d01300f..628750d9b 100644
--- a/komga-webui/src/types/komga-books.ts
+++ b/komga-webui/src/types/komga-books.ts
@@ -134,3 +134,10 @@ export interface BookImportDto {
upgradeBookId?: string,
destinationName?: string,
}
+
+export interface BookThumbnailDto {
+ id: string,
+ bookId: string,
+ type: string,
+ selected: boolean
+}
diff --git a/komga-webui/src/types/komga-collections.ts b/komga-webui/src/types/komga-collections.ts
index 0e47826fc..5b1cb2d79 100644
--- a/komga-webui/src/types/komga-collections.ts
+++ b/komga-webui/src/types/komga-collections.ts
@@ -19,3 +19,10 @@ interface CollectionUpdateDto {
ordered?: boolean,
seriesIds?: string[]
}
+
+interface CollectionThumbnailDto {
+ id: string,
+ collectionId: string,
+ type: string,
+ selected: boolean
+}
diff --git a/komga-webui/src/types/komga-readlists.ts b/komga-webui/src/types/komga-readlists.ts
index 47a47a2d0..847d35cea 100644
--- a/komga-webui/src/types/komga-readlists.ts
+++ b/komga-webui/src/types/komga-readlists.ts
@@ -36,3 +36,10 @@ interface ReadListRequestBookDto {
series: string,
number: string,
}
+
+interface ReadListThumbnailDto {
+ id: string,
+ readListId: string,
+ type: string,
+ selected: boolean
+}
diff --git a/komga-webui/src/types/komga-sse.ts b/komga-webui/src/types/komga-sse.ts
index 1a58a8f9d..2d08649cb 100644
--- a/komga-webui/src/types/komga-sse.ts
+++ b/komga-webui/src/types/komga-sse.ts
@@ -36,10 +36,22 @@ export interface ReadProgressSeriesSseDto {
export interface ThumbnailBookSseDto {
bookId: string,
seriesId: string,
+ selected: boolean,
}
export interface ThumbnailSeriesSseDto {
seriesId: string,
+ selected: boolean,
+}
+
+export interface ThumbnailReadListSseDto {
+ readListId: string,
+ selected: boolean,
+}
+
+export interface ThumbnailCollectionSseDto {
+ collectionId: string,
+ selected: boolean,
}
export interface TaskQueueSseDto {
diff --git a/komga/src/main/kotlin/org/gotson/komga/domain/service/BookLifecycle.kt b/komga/src/main/kotlin/org/gotson/komga/domain/service/BookLifecycle.kt
index 0ebd02bfc..45f75fbc1 100644
--- a/komga/src/main/kotlin/org/gotson/komga/domain/service/BookLifecycle.kt
+++ b/komga/src/main/kotlin/org/gotson/komga/domain/service/BookLifecycle.kt
@@ -128,23 +128,19 @@ class BookLifecycle(
}
}
- when (markSelected) {
- MarkSelectedPreference.YES -> {
- thumbnailBookRepository.markSelected(thumbnail)
- }
+ val selected = when (markSelected) {
+ MarkSelectedPreference.YES -> true
MarkSelectedPreference.IF_NONE_OR_GENERATED -> {
val selectedThumbnail = thumbnailBookRepository.findSelectedByBookIdOrNull(thumbnail.bookId)
-
- if (selectedThumbnail == null || selectedThumbnail.type == ThumbnailBook.Type.GENERATED)
- thumbnailBookRepository.markSelected(thumbnail)
- else thumbnailsHouseKeeping(thumbnail.bookId)
- }
- MarkSelectedPreference.NO -> {
- thumbnailsHouseKeeping(thumbnail.bookId)
+ selectedThumbnail == null || selectedThumbnail.type == ThumbnailBook.Type.GENERATED
}
+ MarkSelectedPreference.NO -> false
}
- eventPublisher.publishEvent(DomainEvent.ThumbnailBookAdded(thumbnail))
+ if (selected) thumbnailBookRepository.markSelected(thumbnail)
+ else thumbnailsHouseKeeping(thumbnail.bookId)
+
+ eventPublisher.publishEvent(DomainEvent.ThumbnailBookAdded(thumbnail.copy(selected = selected)))
}
fun deleteThumbnailForBook(thumbnail: ThumbnailBook) {
diff --git a/komga/src/main/kotlin/org/gotson/komga/domain/service/ReadListLifecycle.kt b/komga/src/main/kotlin/org/gotson/komga/domain/service/ReadListLifecycle.kt
index c6f1a6ceb..163501091 100644
--- a/komga/src/main/kotlin/org/gotson/komga/domain/service/ReadListLifecycle.kt
+++ b/komga/src/main/kotlin/org/gotson/komga/domain/service/ReadListLifecycle.kt
@@ -92,7 +92,7 @@ class ReadListLifecycle(
fun markSelectedThumbnail(thumbnail: ThumbnailReadList) {
thumbnailReadListRepository.markSelected(thumbnail)
- eventPublisher.publishEvent(DomainEvent.ThumbnailReadListAdded(thumbnail))
+ eventPublisher.publishEvent(DomainEvent.ThumbnailReadListAdded(thumbnail.copy(selected = true)))
}
fun deleteThumbnail(thumbnail: ThumbnailReadList) {
diff --git a/komga/src/main/kotlin/org/gotson/komga/domain/service/SeriesCollectionLifecycle.kt b/komga/src/main/kotlin/org/gotson/komga/domain/service/SeriesCollectionLifecycle.kt
index 94756bceb..89df88953 100644
--- a/komga/src/main/kotlin/org/gotson/komga/domain/service/SeriesCollectionLifecycle.kt
+++ b/komga/src/main/kotlin/org/gotson/komga/domain/service/SeriesCollectionLifecycle.kt
@@ -88,7 +88,7 @@ class SeriesCollectionLifecycle(
fun markSelectedThumbnail(thumbnail: ThumbnailSeriesCollection) {
thumbnailSeriesCollectionRepository.markSelected(thumbnail)
- eventPublisher.publishEvent(DomainEvent.ThumbnailSeriesCollectionAdded(thumbnail))
+ eventPublisher.publishEvent(DomainEvent.ThumbnailSeriesCollectionAdded(thumbnail.copy(selected = true)))
}
fun deleteThumbnail(thumbnail: ThumbnailSeriesCollection) {
diff --git a/komga/src/main/kotlin/org/gotson/komga/domain/service/SeriesLifecycle.kt b/komga/src/main/kotlin/org/gotson/komga/domain/service/SeriesLifecycle.kt
index 6034a1b9a..b01ab075a 100644
--- a/komga/src/main/kotlin/org/gotson/komga/domain/service/SeriesLifecycle.kt
+++ b/komga/src/main/kotlin/org/gotson/komga/domain/service/SeriesLifecycle.kt
@@ -274,15 +274,17 @@ class SeriesLifecycle(
}
thumbnailsSeriesRepository.insert(thumbnail.copy(selected = false))
- if (markSelected == MarkSelectedPreference.YES ||
- (
- markSelected == MarkSelectedPreference.IF_NONE_OR_GENERATED &&
- thumbnailsSeriesRepository.findSelectedBySeriesIdOrNull(thumbnail.seriesId) == null
- )
- ) {
- thumbnailsSeriesRepository.markSelected(thumbnail)
- eventPublisher.publishEvent(DomainEvent.ThumbnailSeriesAdded(thumbnail))
+ val selected = when (markSelected) {
+ MarkSelectedPreference.YES -> true
+ MarkSelectedPreference.IF_NONE_OR_GENERATED -> {
+ thumbnailsSeriesRepository.findSelectedBySeriesIdOrNull(thumbnail.seriesId) == null
+ }
+ MarkSelectedPreference.NO -> false
}
+
+ if (selected) thumbnailsSeriesRepository.markSelected(thumbnail)
+
+ eventPublisher.publishEvent(DomainEvent.ThumbnailSeriesAdded(thumbnail.copy(selected = selected)))
}
fun deleteThumbnailForSeries(thumbnail: ThumbnailSeries) {
diff --git a/komga/src/main/kotlin/org/gotson/komga/interfaces/api/rest/BookController.kt b/komga/src/main/kotlin/org/gotson/komga/interfaces/api/rest/BookController.kt
index 2b54dedc8..d584391d2 100644
--- a/komga/src/main/kotlin/org/gotson/komga/interfaces/api/rest/BookController.kt
+++ b/komga/src/main/kotlin/org/gotson/komga/interfaces/api/rest/BookController.kt
@@ -346,7 +346,7 @@ class BookController(
thumbnailBookRepository.findByIdOrNull(thumbnailId)?.let {
thumbnailBookRepository.markSelected(it)
- eventPublisher.publishEvent(DomainEvent.ThumbnailBookAdded(it))
+ eventPublisher.publishEvent(DomainEvent.ThumbnailBookAdded(it.copy(selected = true)))
} ?: throw ResponseStatusException(HttpStatus.NOT_FOUND)
}
diff --git a/komga/src/main/kotlin/org/gotson/komga/interfaces/api/rest/ReadListController.kt b/komga/src/main/kotlin/org/gotson/komga/interfaces/api/rest/ReadListController.kt
index 344e637b9..45c28fc6a 100644
--- a/komga/src/main/kotlin/org/gotson/komga/interfaces/api/rest/ReadListController.kt
+++ b/komga/src/main/kotlin/org/gotson/komga/interfaces/api/rest/ReadListController.kt
@@ -8,8 +8,10 @@ import mu.KotlinLogging
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream
import org.apache.commons.io.IOUtils
+import org.gotson.komga.application.events.EventPublisher
import org.gotson.komga.domain.model.Author
import org.gotson.komga.domain.model.BookSearchWithReadProgress
+import org.gotson.komga.domain.model.DomainEvent
import org.gotson.komga.domain.model.DuplicateNameException
import org.gotson.komga.domain.model.Media
import org.gotson.komga.domain.model.ROLE_ADMIN
@@ -86,6 +88,7 @@ class ReadListController(
private val thumbnailReadListRepository: ThumbnailReadListRepository,
private val contentDetector: ContentDetector,
private val bookLifecycle: BookLifecycle,
+ private val eventPublisher: EventPublisher,
) {
@PageableWithoutSortAsQueryParam
@@ -217,6 +220,7 @@ class ReadListController(
readListRepository.findByIdOrNull(id, principal.user.getAuthorizedLibraryIds(null))?.let {
thumbnailReadListRepository.findByIdOrNull(thumbnailId)?.let {
readListLifecycle.markSelectedThumbnail(it)
+ eventPublisher.publishEvent(DomainEvent.ThumbnailReadListAdded(it.copy(selected = true)))
}
} ?: throw ResponseStatusException(HttpStatus.NOT_FOUND)
}
diff --git a/komga/src/main/kotlin/org/gotson/komga/interfaces/api/rest/SeriesCollectionController.kt b/komga/src/main/kotlin/org/gotson/komga/interfaces/api/rest/SeriesCollectionController.kt
index fccfa3d43..09943e871 100644
--- a/komga/src/main/kotlin/org/gotson/komga/interfaces/api/rest/SeriesCollectionController.kt
+++ b/komga/src/main/kotlin/org/gotson/komga/interfaces/api/rest/SeriesCollectionController.kt
@@ -5,7 +5,9 @@ import io.swagger.v3.oas.annotations.media.Content
import io.swagger.v3.oas.annotations.media.Schema
import io.swagger.v3.oas.annotations.responses.ApiResponse
import mu.KotlinLogging
+import org.gotson.komga.application.events.EventPublisher
import org.gotson.komga.domain.model.Author
+import org.gotson.komga.domain.model.DomainEvent
import org.gotson.komga.domain.model.DuplicateNameException
import org.gotson.komga.domain.model.ROLE_ADMIN
import org.gotson.komga.domain.model.ReadStatus
@@ -66,6 +68,7 @@ class SeriesCollectionController(
private val seriesDtoRepository: SeriesDtoRepository,
private val contentDetector: ContentDetector,
private val thumbnailSeriesCollectionRepository: ThumbnailSeriesCollectionRepository,
+ private val eventPublisher: EventPublisher,
) {
@PageableWithoutSortAsQueryParam
@@ -179,6 +182,7 @@ class SeriesCollectionController(
collectionRepository.findByIdOrNull(id, principal.user.getAuthorizedLibraryIds(null))?.let {
thumbnailSeriesCollectionRepository.findByIdOrNull(thumbnailId)?.let {
collectionLifecycle.markSelectedThumbnail(it)
+ eventPublisher.publishEvent(DomainEvent.ThumbnailSeriesCollectionAdded(it.copy(selected = true)))
}
} ?: throw ResponseStatusException(HttpStatus.NOT_FOUND)
}
diff --git a/komga/src/main/kotlin/org/gotson/komga/interfaces/api/rest/SeriesController.kt b/komga/src/main/kotlin/org/gotson/komga/interfaces/api/rest/SeriesController.kt
index 157cca1b4..553069384 100644
--- a/komga/src/main/kotlin/org/gotson/komga/interfaces/api/rest/SeriesController.kt
+++ b/komga/src/main/kotlin/org/gotson/komga/interfaces/api/rest/SeriesController.kt
@@ -407,7 +407,7 @@ class SeriesController(
seriesRepository.findByIdOrNull(seriesId) ?: throw ResponseStatusException(HttpStatus.NOT_FOUND)
thumbnailsSeriesRepository.findByIdOrNull(thumbnailId)?.let {
thumbnailsSeriesRepository.markSelected(it)
- eventPublisher.publishEvent(DomainEvent.ThumbnailSeriesAdded(it))
+ eventPublisher.publishEvent(DomainEvent.ThumbnailSeriesAdded(it.copy(selected = true)))
} ?: throw ResponseStatusException(HttpStatus.NOT_FOUND)
}
diff --git a/komga/src/main/kotlin/org/gotson/komga/interfaces/sse/SseController.kt b/komga/src/main/kotlin/org/gotson/komga/interfaces/sse/SseController.kt
index 6110aa4e4..4e0296b57 100644
--- a/komga/src/main/kotlin/org/gotson/komga/interfaces/sse/SseController.kt
+++ b/komga/src/main/kotlin/org/gotson/komga/interfaces/sse/SseController.kt
@@ -101,14 +101,14 @@ class SseController(
is DomainEvent.ReadProgressSeriesChanged -> emitSse("ReadProgressSeriesChanged", ReadProgressSeriesSseDto(event.seriesId, event.userId), userIdOnly = event.userId)
is DomainEvent.ReadProgressSeriesDeleted -> emitSse("ReadProgressSeriesDeleted", ReadProgressSeriesSseDto(event.seriesId, event.userId), userIdOnly = event.userId)
- is DomainEvent.ThumbnailBookAdded -> emitSse("ThumbnailBookAdded", ThumbnailBookSseDto(event.thumbnail.bookId, bookRepository.getSeriesIdOrNull(event.thumbnail.bookId).orEmpty()))
- is DomainEvent.ThumbnailBookDeleted -> emitSse("ThumbnailBookDeleted", ThumbnailBookSseDto(event.thumbnail.bookId, bookRepository.getSeriesIdOrNull(event.thumbnail.bookId).orEmpty()))
- is DomainEvent.ThumbnailSeriesAdded -> emitSse("ThumbnailSeriesAdded", ThumbnailSeriesSseDto(event.thumbnail.seriesId))
- is DomainEvent.ThumbnailSeriesDeleted -> emitSse("ThumbnailSeriesDeleted", ThumbnailSeriesSseDto(event.thumbnail.seriesId))
- is DomainEvent.ThumbnailSeriesCollectionAdded -> emitSse("ThumbnailSeriesCollectionAdded", ThumbnailSeriesCollectionSseDto(event.thumbnail.collectionId))
- is DomainEvent.ThumbnailSeriesCollectionDeleted -> emitSse("ThumbnailSeriesCollectionDeleted", ThumbnailSeriesCollectionSseDto(event.thumbnail.collectionId))
- is DomainEvent.ThumbnailReadListAdded -> emitSse("ThumbnailReadListAdded", ThumbnailReadListSseDto(event.thumbnail.readListId))
- is DomainEvent.ThumbnailReadListDeleted -> emitSse("ThumbnailReadListDeleted", ThumbnailReadListSseDto(event.thumbnail.readListId))
+ is DomainEvent.ThumbnailBookAdded -> emitSse("ThumbnailBookAdded", ThumbnailBookSseDto(event.thumbnail.bookId, bookRepository.getSeriesIdOrNull(event.thumbnail.bookId).orEmpty(), event.thumbnail.selected))
+ is DomainEvent.ThumbnailBookDeleted -> emitSse("ThumbnailBookDeleted", ThumbnailBookSseDto(event.thumbnail.bookId, bookRepository.getSeriesIdOrNull(event.thumbnail.bookId).orEmpty(), event.thumbnail.selected))
+ is DomainEvent.ThumbnailSeriesAdded -> emitSse("ThumbnailSeriesAdded", ThumbnailSeriesSseDto(event.thumbnail.seriesId, event.thumbnail.selected))
+ is DomainEvent.ThumbnailSeriesDeleted -> emitSse("ThumbnailSeriesDeleted", ThumbnailSeriesSseDto(event.thumbnail.seriesId, event.thumbnail.selected))
+ is DomainEvent.ThumbnailSeriesCollectionAdded -> emitSse("ThumbnailSeriesCollectionAdded", ThumbnailSeriesCollectionSseDto(event.thumbnail.collectionId, event.thumbnail.selected))
+ is DomainEvent.ThumbnailSeriesCollectionDeleted -> emitSse("ThumbnailSeriesCollectionDeleted", ThumbnailSeriesCollectionSseDto(event.thumbnail.collectionId, event.thumbnail.selected))
+ is DomainEvent.ThumbnailReadListAdded -> emitSse("ThumbnailReadListAdded", ThumbnailReadListSseDto(event.thumbnail.readListId, event.thumbnail.selected))
+ is DomainEvent.ThumbnailReadListDeleted -> emitSse("ThumbnailReadListDeleted", ThumbnailReadListSseDto(event.thumbnail.readListId, event.thumbnail.selected))
}
}
diff --git a/komga/src/main/kotlin/org/gotson/komga/interfaces/sse/dto/ThumbnailBookSseDto.kt b/komga/src/main/kotlin/org/gotson/komga/interfaces/sse/dto/ThumbnailBookSseDto.kt
index f0978ffaa..ba0a20715 100644
--- a/komga/src/main/kotlin/org/gotson/komga/interfaces/sse/dto/ThumbnailBookSseDto.kt
+++ b/komga/src/main/kotlin/org/gotson/komga/interfaces/sse/dto/ThumbnailBookSseDto.kt
@@ -3,4 +3,5 @@ package org.gotson.komga.interfaces.sse.dto
data class ThumbnailBookSseDto(
val bookId: String,
val seriesId: String,
+ val selected: Boolean,
)
diff --git a/komga/src/main/kotlin/org/gotson/komga/interfaces/sse/dto/ThumbnailReadListSseDto.kt b/komga/src/main/kotlin/org/gotson/komga/interfaces/sse/dto/ThumbnailReadListSseDto.kt
index 5a87ef662..83f0b8921 100644
--- a/komga/src/main/kotlin/org/gotson/komga/interfaces/sse/dto/ThumbnailReadListSseDto.kt
+++ b/komga/src/main/kotlin/org/gotson/komga/interfaces/sse/dto/ThumbnailReadListSseDto.kt
@@ -2,4 +2,5 @@ package org.gotson.komga.interfaces.sse.dto
data class ThumbnailReadListSseDto(
val readListId: String,
+ val selected: Boolean,
)
diff --git a/komga/src/main/kotlin/org/gotson/komga/interfaces/sse/dto/ThumbnailSeriesCollectionSseDto.kt b/komga/src/main/kotlin/org/gotson/komga/interfaces/sse/dto/ThumbnailSeriesCollectionSseDto.kt
index 52f5725ef..74e111ce1 100644
--- a/komga/src/main/kotlin/org/gotson/komga/interfaces/sse/dto/ThumbnailSeriesCollectionSseDto.kt
+++ b/komga/src/main/kotlin/org/gotson/komga/interfaces/sse/dto/ThumbnailSeriesCollectionSseDto.kt
@@ -2,4 +2,5 @@ package org.gotson.komga.interfaces.sse.dto
data class ThumbnailSeriesCollectionSseDto(
val collectionId: String,
+ val selected: Boolean,
)
diff --git a/komga/src/main/kotlin/org/gotson/komga/interfaces/sse/dto/ThumbnailSeriesSseDto.kt b/komga/src/main/kotlin/org/gotson/komga/interfaces/sse/dto/ThumbnailSeriesSseDto.kt
index 5a2a43438..aca2c7e1e 100644
--- a/komga/src/main/kotlin/org/gotson/komga/interfaces/sse/dto/ThumbnailSeriesSseDto.kt
+++ b/komga/src/main/kotlin/org/gotson/komga/interfaces/sse/dto/ThumbnailSeriesSseDto.kt
@@ -2,4 +2,5 @@ package org.gotson.komga.interfaces.sse.dto
data class ThumbnailSeriesSseDto(
val seriesId: String,
+ val selected: Boolean,
)