mirror of
https://github.com/gotson/komga.git
synced 2025-12-18 22:43:42 +01:00
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:
parent
02c0847663
commit
49b7f592cb
4 changed files with 84 additions and 2 deletions
|
|
@ -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,
|
||||
|
|
|
|||
45
komga-webui/src/functions/resize-image.ts
Normal file
45
komga-webui/src/functions/resize-image.ts
Normal 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
|
||||
}
|
||||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
Loading…
Reference in a new issue