feat: download current page from webreader

closes #469
This commit is contained in:
Gauthier Roebroeck 2021-03-24 16:26:38 +08:00
parent 84ff250030
commit 93cec4e4e5
6 changed files with 55 additions and 1 deletions

View file

@ -12,6 +12,7 @@
"core-js": "^3.6.5", "core-js": "^3.6.5",
"date-fns": "^2.19.0", "date-fns": "^2.19.0",
"jquery": "^3.5.1", "jquery": "^3.5.1",
"js-file-downloader": "^1.1.16",
"language-tags": "^1.0.5", "language-tags": "^1.0.5",
"lodash": "^4.17.19", "lodash": "^4.17.19",
"qs": "^6.9.4", "qs": "^6.9.4",
@ -13480,6 +13481,11 @@
"node": ">=10" "node": ">=10"
} }
}, },
"node_modules/js-file-downloader": {
"version": "1.1.16",
"resolved": "https://registry.npmjs.org/js-file-downloader/-/js-file-downloader-1.1.16.tgz",
"integrity": "sha512-vj4ZpHvFJI7J7SluyreHzaAVZDrPulRcwjMMOUve1KOEB4oxYAiQZXzgmu2lPbMTqKlf7U91MNZYRMTq7DsMfQ=="
},
"node_modules/js-message": { "node_modules/js-message": {
"version": "1.0.5", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/js-message/-/js-message-1.0.5.tgz", "resolved": "https://registry.npmjs.org/js-message/-/js-message-1.0.5.tgz",
@ -32242,6 +32248,11 @@
} }
} }
}, },
"js-file-downloader": {
"version": "1.1.16",
"resolved": "https://registry.npmjs.org/js-file-downloader/-/js-file-downloader-1.1.16.tgz",
"integrity": "sha512-vj4ZpHvFJI7J7SluyreHzaAVZDrPulRcwjMMOUve1KOEB4oxYAiQZXzgmu2lPbMTqKlf7U91MNZYRMTq7DsMfQ=="
},
"js-message": { "js-message": {
"version": "1.0.5", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/js-message/-/js-message-1.0.5.tgz", "resolved": "https://registry.npmjs.org/js-message/-/js-message-1.0.5.tgz",

View file

@ -15,6 +15,7 @@
"core-js": "^3.6.5", "core-js": "^3.6.5",
"date-fns": "^2.19.0", "date-fns": "^2.19.0",
"jquery": "^3.5.1", "jquery": "^3.5.1",
"js-file-downloader": "^1.1.16",
"language-tags": "^1.0.5", "language-tags": "^1.0.5",
"lodash": "^4.17.19", "lodash": "^4.17.19",
"qs": "^6.9.4", "qs": "^6.9.4",

View file

@ -37,6 +37,7 @@
"cycling_page_layout": "Cycling Page Layout", "cycling_page_layout": "Cycling Page Layout",
"cycling_scale": "Cycling Scale", "cycling_scale": "Cycling Scale",
"cycling_side_padding": "Cycling Side Padding", "cycling_side_padding": "Cycling Side Padding",
"download_current_page": "Download current page",
"end_of_book": "You've reached the end of the book.", "end_of_book": "You've reached the end of the book.",
"from_series_metadata": "from series metadata", "from_series_metadata": "from series metadata",
"move_next": "Click or press \"Next\" again to move to the next book.", "move_next": "Click or press \"Next\" again to move to the next book.",

View file

@ -38,6 +38,19 @@
> >
<v-icon>mdi-cog</v-icon> <v-icon>mdi-cog</v-icon>
</v-btn> </v-btn>
<v-menu offset-y>
<template v-slot:activator="{ on }">
<v-btn icon v-on="on" @click.prevent="">
<v-icon>mdi-dots-vertical</v-icon>
</v-btn>
</template>
<v-list>
<v-list-item @click="downloadCurrentPage">
<v-list-item-title>{{ $t('bookreader.download_current_page') }}</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
</v-toolbar> </v-toolbar>
</v-slide-y-transition> </v-slide-y-transition>
@ -290,6 +303,7 @@ import {shortcutsSettingsContinuous} from '@/functions/shortcuts/continuous-read
import {BookDto, PageDto, PageDtoWithUrl} from '@/types/komga-books' import {BookDto, PageDto, PageDtoWithUrl} from '@/types/komga-books'
import {Context, ContextOrigin} from '@/types/context' import {Context, ContextOrigin} from '@/types/context'
import {SeriesDto} from "@/types/komga-series"; import {SeriesDto} from "@/types/komga-series";
import jsFileDownloader from "js-file-downloader"
const cookieFit = 'webreader.fit' const cookieFit = 'webreader.fit'
const cookieContinuousReaderFit = 'webreader.continuousReaderFit' const cookieContinuousReaderFit = 'webreader.continuousReaderFit'
@ -484,6 +498,9 @@ export default Vue.extend({
contextReadList (): boolean { contextReadList (): boolean {
return this.context.origin === ContextOrigin.READLIST return this.context.origin === ContextOrigin.READLIST
}, },
currentPage(): PageDtoWithUrl {
return this.pages[this.page - 1]
},
animations: { animations: {
get: function (): boolean { get: function (): boolean {
@ -782,6 +799,12 @@ export default Vue.extend({
async markProgress (page: number) { async markProgress (page: number) {
await this.$komgaBooks.updateReadProgress(this.bookId, { page: page }) await this.$komgaBooks.updateReadProgress(this.bookId, { page: page })
}, },
downloadCurrentPage() {
new jsFileDownloader({
url: this.currentPage.url,
withCredentials: true,
})
},
}, },
}) })
</script> </script>

View file

@ -36,4 +36,11 @@ class ContentDetector(
fun isImage(mediaType: String): Boolean = fun isImage(mediaType: String): Boolean =
mediaType.startsWith("image/") mediaType.startsWith("image/")
fun mediaTypeToExtension(mediaType: String): String? =
try {
tika.mimeRepository.forName(mediaType).extension
} catch (e: Exception) {
null
}
} }

View file

@ -24,6 +24,7 @@ import org.gotson.komga.domain.persistence.ReadListRepository
import org.gotson.komga.domain.service.BookLifecycle import org.gotson.komga.domain.service.BookLifecycle
import org.gotson.komga.infrastructure.image.ImageType import org.gotson.komga.infrastructure.image.ImageType
import org.gotson.komga.infrastructure.jooq.UnpagedSorted import org.gotson.komga.infrastructure.jooq.UnpagedSorted
import org.gotson.komga.infrastructure.mediacontainer.ContentDetector
import org.gotson.komga.infrastructure.security.KomgaPrincipal import org.gotson.komga.infrastructure.security.KomgaPrincipal
import org.gotson.komga.infrastructure.swagger.PageableAsQueryParam import org.gotson.komga.infrastructure.swagger.PageableAsQueryParam
import org.gotson.komga.infrastructure.swagger.PageableWithoutSortAsQueryParam import org.gotson.komga.infrastructure.swagger.PageableWithoutSortAsQueryParam
@ -78,7 +79,8 @@ class BookController(
private val bookMetadataRepository: BookMetadataRepository, private val bookMetadataRepository: BookMetadataRepository,
private val mediaRepository: MediaRepository, private val mediaRepository: MediaRepository,
private val bookDtoRepository: BookDtoRepository, private val bookDtoRepository: BookDtoRepository,
private val readListRepository: ReadListRepository private val readListRepository: ReadListRepository,
private val contentDetector: ContentDetector,
) { ) {
@PageableAsQueryParam @PageableAsQueryParam
@ -342,6 +344,15 @@ class BookController(
val pageContent = bookLifecycle.getBookPage(book, pageNum, convertFormat) val pageContent = bookLifecycle.getBookPage(book, pageNum, convertFormat)
ResponseEntity.ok() ResponseEntity.ok()
.headers(
HttpHeaders().apply {
val extension = contentDetector.mediaTypeToExtension(pageContent.mediaType) ?: "jpeg"
val imageFileName = "${book.name}-$pageNum$extension"
contentDisposition = ContentDisposition.builder("inline")
.filename(imageFileName)
.build()
}
)
.contentType(getMediaTypeOrDefault(pageContent.mediaType)) .contentType(getMediaTypeOrDefault(pageContent.mediaType))
.setNotModified(media) .setNotModified(media)
.body(pageContent.content) .body(pageContent.content)