feat(webui): set the currently viewed page as poster for book/series/readlist

Closes #838
Co-authored-by: Gauthier Roebroeck <gauthier.roebroeck@gmail.com>
This commit is contained in:
Kevin Alberts 2022-04-22 05:16:00 +02:00 committed by GitHub
parent 02c0847663
commit 49b7f592cb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 84 additions and 2 deletions

View file

@ -1,7 +1,7 @@
import filesize from 'filesize'
export async function getFileFromUrl(url: string, name: string = url, defaultType = 'image/jpeg') {
const response = await fetch(url)
export async function getFileFromUrl(url: string, name: string = url, defaultType = 'image/jpeg', fetchOptions = {}) {
const response = await fetch(url, fetchOptions)
const data = await response.blob()
return new File([data], name, {
type: data.type || defaultType,

View file

@ -0,0 +1,45 @@
function getCanvasBlob(canvas: HTMLCanvasElement): Promise<Blob | null> {
return new Promise((resolve) => {
canvas.toBlob((blob: Blob | null) => { resolve(blob) })
})
}
export async function resizeImageFile(imageFile: File, maxWidth: number = 500, maxHeight: number = 500): Promise<File> {
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d')
if (ctx !== null) {
const img = new Image()
img.src = URL.createObjectURL(imageFile)
await img.decode()
let width = img.width
let height = img.height
// Calculate width and height of the smaller image
if (width > height) {
if (width > maxWidth) {
height = height * (maxWidth / width)
width = maxWidth
}
} else {
if (height > maxHeight) {
width = width * (maxHeight / height)
height = maxHeight
}
}
canvas.width = width
canvas.height = height
// Release blob data to save browser memory
URL.revokeObjectURL(img.src)
ctx.drawImage(img, 0, 0, width, height)
const blob = await getCanvasBlob(canvas)
if (blob !== null) {
return new File([blob], 'poster', {lastModified: Date.now()})
}
}
// If we have not returned before here, resizing has failed for some reason.
// Maybe the browser doesn't support 2D canvas or image loading was not possible.
// Just return the original image.
return imageFile
}

View file

@ -89,6 +89,9 @@
"move_next": "Click or press \"Next\" again to move to the next book.",
"move_next_exit": "Click or press \"Next\" again to exit the reader.",
"move_previous": "Click or press \"Previous\" again to move to the previous book.",
"notification_poster_set_book": "Book poster is now set to the current page.",
"notification_poster_set_readlist": "Read list poster is now set to the current page.",
"notification_poster_set_series": "Series poster is now set to the current page.",
"paged_reader_layout": {
"double": "Double pages",
"double_no_cover": "Double pages (no cover)",
@ -104,6 +107,9 @@
"width": "Fit width",
"width_shrink_only": "Fit width (shrink only)"
},
"set_current_page_as_book_poster": "Set page as poster for book",
"set_current_page_as_readlist_poster": "Set page as poster for read list",
"set_current_page_as_series_poster": "Set page as poster for series",
"settings": {
"always_fullscreen": "Always Full Screen",
"animate_page_transitions": "Animate page transitions",

View file

@ -63,6 +63,15 @@
<v-list-item @click="downloadCurrentPage">
<v-list-item-title>{{ $t('bookreader.download_current_page') }}</v-list-item-title>
</v-list-item>
<v-list-item @click="setCurrentPageAsPoster(ItemTypes.BOOK)">
<v-list-item-title>{{ $t('bookreader.set_current_page_as_book_poster') }}</v-list-item-title>
</v-list-item>
<v-list-item @click="setCurrentPageAsPoster(ItemTypes.SERIES)">
<v-list-item-title>{{ $t('bookreader.set_current_page_as_series_poster') }}</v-list-item-title>
</v-list-item>
<v-list-item v-if="contextReadList" @click="setCurrentPageAsPoster(ItemTypes.READLIST)">
<v-list-item-title>{{ $t('bookreader.set_current_page_as_readlist_poster') }}</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
</v-toolbar>
@ -307,6 +316,8 @@ import ShortcutHelpDialog from '@/components/dialogs/ShortcutHelpDialog.vue'
import {getBookTitleCompact} from '@/functions/book-title'
import {checkImageSupport, ImageFeature} from '@/functions/check-image'
import {bookPageUrl} from '@/functions/urls'
import {getFileFromUrl} from '@/functions/file'
import {resizeImageFile} from '@/functions/resize-image'
import {ReadingDirection} from '@/types/enum-books'
import Vue from 'vue'
import {Location} from 'vue-router'
@ -327,6 +338,7 @@ import {Context, ContextOrigin} from '@/types/context'
import {SeriesDto} from '@/types/komga-series'
import jsFileDownloader from 'js-file-downloader'
import screenfull from 'screenfull'
import {ItemTypes} from '@/types/items'
export default Vue.extend({
name: 'BookReader',
@ -340,6 +352,7 @@ export default Vue.extend({
},
data: function () {
return {
ItemTypes,
screenfull,
fullscreenIcon: 'mdi-fullscreen',
book: {} as BookDto,
@ -857,6 +870,24 @@ export default Vue.extend({
forceDesktopMode: true,
})
},
async setCurrentPageAsPoster(type: ItemTypes) {
const imageFile = await getFileFromUrl(this.currentPage.url, 'poster', 'image/jpeg', {credentials: 'include'})
const newImageFile = await resizeImageFile(imageFile)
switch (type) {
case ItemTypes.BOOK:
await this.$komgaBooks.uploadThumbnail(this.book.id, newImageFile, true)
this.sendNotification(`${this.$t('bookreader.notification_poster_set_book')}`)
break
case ItemTypes.SERIES:
await this.$komgaSeries.uploadThumbnail(this.series.id, newImageFile, true)
this.sendNotification(`${this.$t('bookreader.notification_poster_set_series')}`)
break
case ItemTypes.READLIST:
await this.$komgaReadLists.uploadThumbnail(this.context.id, newImageFile, true)
this.sendNotification(`${this.$t('bookreader.notification_poster_set_readlist')}`)
break
}
},
},
})
</script>