mirror of
https://github.com/adityachandelgit/BookLore.git
synced 2026-02-18 03:07:40 +01:00
WIP: ePub support
This commit is contained in:
@@ -55,6 +55,8 @@ dependencies {
|
||||
// MapStruct
|
||||
implementation 'org.mapstruct:mapstruct:1.6.3'
|
||||
annotationProcessor 'org.mapstruct:mapstruct-processor:1.6.3'
|
||||
|
||||
implementation 'io.documentnode:epub4j-core:4.2.2'
|
||||
}
|
||||
|
||||
hibernate {
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.adityachandel.booklore.controller;
|
||||
|
||||
import com.adityachandel.booklore.service.EpubService;
|
||||
import org.springframework.core.io.ByteArrayResource;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/epub")
|
||||
public class EpubController {
|
||||
|
||||
private final EpubService epubService;
|
||||
|
||||
public EpubController(EpubService epubService) {
|
||||
this.epubService = epubService;
|
||||
}
|
||||
|
||||
// New endpoint to serve the entire EPUB file
|
||||
@GetMapping("/{bookId}/download")
|
||||
public ResponseEntity<ByteArrayResource> downloadEpub(@PathVariable Long bookId) throws IOException {
|
||||
ByteArrayResource epubFile = epubService.getEpubFile(bookId);
|
||||
return ResponseEntity.ok()
|
||||
.header("Content-Type", "application/epub+zip")
|
||||
.header("Content-Disposition", "attachment; filename=\"book.epub\"")
|
||||
.body(epubFile);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.adityachandel.booklore.service;
|
||||
|
||||
import org.springframework.core.io.ByteArrayResource;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
@Service
|
||||
public class EpubService {
|
||||
|
||||
// Method to retrieve the EPUB file as a ByteArrayResource
|
||||
public ByteArrayResource getEpubFile(Long bookId) throws IOException {
|
||||
String bookPath = "/Users/aditya.chandel/Downloads/Harry1.epub"; // Example path
|
||||
File file = new File(bookPath);
|
||||
if (!file.exists()) {
|
||||
throw new IOException("EPUB file not found for book id: " + bookId);
|
||||
}
|
||||
|
||||
// Convert the EPUB file to a byte array
|
||||
try (FileInputStream inputStream = new FileInputStream(file)) {
|
||||
byte[] fileContent = inputStream.readAllBytes();
|
||||
return new ByteArrayResource(fileContent);
|
||||
}
|
||||
}
|
||||
}
|
||||
252
booklore-ui/package-lock.json
generated
252
booklore-ui/package-lock.json
generated
@@ -20,6 +20,7 @@
|
||||
"@primeng/themes": "19.0.2",
|
||||
"@stomp/rx-stomp": "^2.0.0",
|
||||
"@stomp/stompjs": "^7.0.0",
|
||||
"epubjs": "^0.3.93",
|
||||
"ng-lazyload-image": "^9.1.3",
|
||||
"ngx-extended-pdf-viewer": "^22.0.0",
|
||||
"ngx-infinite-scroll": "^19.0.0",
|
||||
@@ -5473,6 +5474,16 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/localforage": {
|
||||
"version": "0.0.34",
|
||||
"resolved": "https://registry.npmjs.org/@types/localforage/-/localforage-0.0.34.tgz",
|
||||
"integrity": "sha512-tJxahnjm9dEI1X+hQSC5f2BSd/coZaqbIl1m3TCl0q9SVuC52XcXfV0XmoCU1+PmjyucuVITwoTnN8OlTbEXXA==",
|
||||
"deprecated": "This is a stub types definition for localforage (https://github.com/localForage/localForage). localforage provides its own type definitions, so you don't need @types/localforage installed!",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"localforage": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/mime": {
|
||||
"version": "1.3.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz",
|
||||
@@ -6402,6 +6413,16 @@
|
||||
"@xtuc/long": "4.2.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@xmldom/xmldom": {
|
||||
"version": "0.7.13",
|
||||
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.7.13.tgz",
|
||||
"integrity": "sha512-lm2GW5PkosIzccsaZIz7tp8cPADSIlIHWDFTR1N0SzfinhhYgeIQjFMz4rYzanCScr3DqQLeomUDArp6MWKm+g==",
|
||||
"deprecated": "this version is no longer supported, please update to at least 0.8.*",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@xtuc/ieee754": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
|
||||
@@ -7792,6 +7813,17 @@
|
||||
"webpack": "^5.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/core-js": {
|
||||
"version": "3.40.0",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.40.0.tgz",
|
||||
"integrity": "sha512-7vsMc/Lty6AGnn7uFpYT56QesI5D2Y/UkgKounk87OP9Z2H9Z8kj6jzcSGAxFmUtDOS0ntK6lbQz+Nsa0Jj6mQ==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/core-js"
|
||||
}
|
||||
},
|
||||
"node_modules/core-js-compat": {
|
||||
"version": "3.39.0",
|
||||
"resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.39.0.tgz",
|
||||
@@ -7810,7 +7842,6 @@
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
|
||||
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/cors": {
|
||||
@@ -7968,6 +7999,19 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/d": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/d/-/d-1.0.2.tgz",
|
||||
"integrity": "sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"es5-ext": "^0.10.64",
|
||||
"type": "^2.7.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.12"
|
||||
}
|
||||
},
|
||||
"node_modules/date-format": {
|
||||
"version": "4.0.14",
|
||||
"resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.14.tgz",
|
||||
@@ -8409,6 +8453,23 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/epubjs": {
|
||||
"version": "0.3.93",
|
||||
"resolved": "https://registry.npmjs.org/epubjs/-/epubjs-0.3.93.tgz",
|
||||
"integrity": "sha512-c06pNSdBxcXv3dZSbXAVLE1/pmleRhOT6mXNZo6INKmvuKpYB65MwU/lO7830czCtjIiK9i+KR+3S+p0wtljrw==",
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"@types/localforage": "0.0.34",
|
||||
"@xmldom/xmldom": "^0.7.5",
|
||||
"core-js": "^3.18.3",
|
||||
"event-emitter": "^0.3.5",
|
||||
"jszip": "^3.7.1",
|
||||
"localforage": "^1.10.0",
|
||||
"lodash": "^4.17.21",
|
||||
"marks-pane": "^1.0.9",
|
||||
"path-webpack": "0.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/err-code": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz",
|
||||
@@ -8470,6 +8531,46 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/es5-ext": {
|
||||
"version": "0.10.64",
|
||||
"resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz",
|
||||
"integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==",
|
||||
"hasInstallScript": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"es6-iterator": "^2.0.3",
|
||||
"es6-symbol": "^3.1.3",
|
||||
"esniff": "^2.0.1",
|
||||
"next-tick": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/es6-iterator": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
|
||||
"integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"d": "1",
|
||||
"es5-ext": "^0.10.35",
|
||||
"es6-symbol": "^3.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/es6-symbol": {
|
||||
"version": "3.1.4",
|
||||
"resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.4.tgz",
|
||||
"integrity": "sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"d": "^1.0.2",
|
||||
"ext": "^1.7.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.12"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild": {
|
||||
"version": "0.24.0",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.0.tgz",
|
||||
@@ -8792,6 +8893,21 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/esniff": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz",
|
||||
"integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"d": "^1.0.1",
|
||||
"es5-ext": "^0.10.62",
|
||||
"event-emitter": "^0.3.5",
|
||||
"type": "^2.7.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/espree": {
|
||||
"version": "10.3.0",
|
||||
"resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz",
|
||||
@@ -8899,6 +9015,16 @@
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/event-emitter": {
|
||||
"version": "0.3.5",
|
||||
"resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz",
|
||||
"integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"d": "1",
|
||||
"es5-ext": "~0.10.14"
|
||||
}
|
||||
},
|
||||
"node_modules/eventemitter3": {
|
||||
"version": "4.0.7",
|
||||
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
|
||||
@@ -9032,6 +9158,15 @@
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/ext": {
|
||||
"version": "1.7.0",
|
||||
"resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz",
|
||||
"integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"type": "^2.7.2"
|
||||
}
|
||||
},
|
||||
"node_modules/extend": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
|
||||
@@ -9968,6 +10103,12 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/immediate": {
|
||||
"version": "3.0.6",
|
||||
"resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
|
||||
"integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/immutable": {
|
||||
"version": "5.0.3",
|
||||
"resolved": "https://registry.npmjs.org/immutable/-/immutable-5.0.3.tgz",
|
||||
@@ -10028,7 +10169,6 @@
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/ini": {
|
||||
@@ -10270,7 +10410,6 @@
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/isbinaryfile": {
|
||||
@@ -10556,6 +10695,48 @@
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/jszip": {
|
||||
"version": "3.10.1",
|
||||
"resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz",
|
||||
"integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==",
|
||||
"license": "(MIT OR GPL-3.0-or-later)",
|
||||
"dependencies": {
|
||||
"lie": "~3.3.0",
|
||||
"pako": "~1.0.2",
|
||||
"readable-stream": "~2.3.6",
|
||||
"setimmediate": "^1.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/jszip/node_modules/readable-stream": {
|
||||
"version": "2.3.8",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
|
||||
"integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"core-util-is": "~1.0.0",
|
||||
"inherits": "~2.0.3",
|
||||
"isarray": "~1.0.0",
|
||||
"process-nextick-args": "~2.0.0",
|
||||
"safe-buffer": "~5.1.1",
|
||||
"string_decoder": "~1.1.1",
|
||||
"util-deprecate": "~1.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/jszip/node_modules/safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/jszip/node_modules/string_decoder": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"safe-buffer": "~5.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/karma": {
|
||||
"version": "6.4.4",
|
||||
"resolved": "https://registry.npmjs.org/karma/-/karma-6.4.4.tgz",
|
||||
@@ -11038,6 +11219,15 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/lie": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
|
||||
"integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"immediate": "~3.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/lilconfig": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz",
|
||||
@@ -11188,6 +11378,24 @@
|
||||
"node": ">= 12.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/localforage": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/localforage/-/localforage-1.10.0.tgz",
|
||||
"integrity": "sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"lie": "3.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/localforage/node_modules/lie": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz",
|
||||
"integrity": "sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"immediate": "~3.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/locate-path": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz",
|
||||
@@ -11208,7 +11416,6 @@
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash-es": {
|
||||
@@ -11622,6 +11829,12 @@
|
||||
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/marks-pane": {
|
||||
"version": "1.0.9",
|
||||
"resolved": "https://registry.npmjs.org/marks-pane/-/marks-pane-1.0.9.tgz",
|
||||
"integrity": "sha512-Ahs4oeG90tbdPWwAJkAAoHg2lRR8lAs9mZXETNPO9hYg3AkjUJBKi1NQ4aaIQZVGrig7c/3NUV1jANl8rFTeMg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/media-typer": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
|
||||
@@ -12169,6 +12382,12 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/next-tick": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz",
|
||||
"integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/ng-lazyload-image": {
|
||||
"version": "9.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ng-lazyload-image/-/ng-lazyload-image-9.1.3.tgz",
|
||||
@@ -13001,6 +13220,12 @@
|
||||
"node": "^18.17.0 || >=20.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/pako": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
|
||||
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
|
||||
"license": "(MIT AND Zlib)"
|
||||
},
|
||||
"node_modules/parchment": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/parchment/-/parchment-3.0.0.tgz",
|
||||
@@ -13184,6 +13409,12 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/path-webpack": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/path-webpack/-/path-webpack-0.0.3.tgz",
|
||||
"integrity": "sha512-AmeDxedoo5svf7aB3FYqSAKqMxys014lVKBzy1o/5vv9CtU7U4wgGWL1dA2o6MOzcD53ScN4Jmiq6VbtLz1vIQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/picocolors": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
||||
@@ -13559,7 +13790,6 @@
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
||||
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/promise-inflight": {
|
||||
@@ -14480,6 +14710,12 @@
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/setimmediate": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
|
||||
"integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/setprototypeof": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
|
||||
@@ -15782,6 +16018,12 @@
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/type": {
|
||||
"version": "2.7.3",
|
||||
"resolved": "https://registry.npmjs.org/type/-/type-2.7.3.tgz",
|
||||
"integrity": "sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/type-check": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
"@primeng/themes": "19.0.2",
|
||||
"@stomp/rx-stomp": "^2.0.0",
|
||||
"@stomp/stompjs": "^7.0.0",
|
||||
"epubjs": "^0.3.93",
|
||||
"ng-lazyload-image": "^9.1.3",
|
||||
"ngx-extended-pdf-viewer": "^22.0.0",
|
||||
"ngx-infinite-scroll": "^19.0.0",
|
||||
@@ -54,4 +55,4 @@
|
||||
"typescript": "~5.6.2",
|
||||
"typescript-eslint": "8.18.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
import {Routes} from '@angular/router';
|
||||
import {PdfViewerComponent} from './book/components/pdf-viewer/pdf-viewer.component';
|
||||
import {MainDashboardComponent} from './dashboard/components/main-dashboard/main-dashboard.component';
|
||||
import {BookBrowserComponent} from './book/components/book-browser/book-browser.component';
|
||||
import {AppLayoutComponent} from './layout/component/layout-main/app.layout.component';
|
||||
import {EpubViewerComponent} from './epub-viewer/component/epub-viewer.component';
|
||||
|
||||
export const routes: Routes = [
|
||||
{
|
||||
path: '', component: AppLayoutComponent,
|
||||
path: '', component: EpubViewerComponent,
|
||||
children: [
|
||||
{
|
||||
path: '', component: MainDashboardComponent,
|
||||
path: '', component: EpubViewerComponent,
|
||||
},
|
||||
{
|
||||
path: 'all-books', component: BookBrowserComponent,
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
<div class="epub-viewer-container">
|
||||
|
||||
<p-button class="menu-toggle-button" (click)="toggleDrawer()" icon="pi pi-bars"></p-button>
|
||||
|
||||
<p-drawer [(visible)]="isDrawerVisible" [modal]="false" [position]="'left'" header="Chapters">
|
||||
<ul class="chapter-list">
|
||||
<li *ngFor="let chapter of chapters" (click)="navigateToChapter(chapter); $event.stopPropagation()" class="chapter-item">
|
||||
{{ chapter.label }}
|
||||
</li>
|
||||
</ul>
|
||||
</p-drawer>
|
||||
|
||||
<p-button class="settings-toggle-button" (click)="toggleSettingsDrawer()" icon="pi pi-cog"></p-button>
|
||||
<p-drawer [(visible)]="isSettingsDrawerVisible" [modal]="false" [position]="'right'" header="Settings">
|
||||
<div class="flex flex-col">
|
||||
<div class="settings-content">
|
||||
<p>Font Size: {{ fontSize }}%</p>
|
||||
<div class="flex flex-row gap-4">
|
||||
<p-button icon="pi pi-minus" (click)="decreaseFontSize()"></p-button>
|
||||
<p-button icon="pi pi-plus" (click)="increaseFontSize()"></p-button>
|
||||
</div>
|
||||
<p-divider></p-divider>
|
||||
<label for="font-type">Font Type:</label>
|
||||
<p-dropdown id="font-type" [options]="fontTypes" [(ngModel)]="selectedFontType" (onChange)="changeFontType($event)">
|
||||
</p-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
</p-drawer>
|
||||
|
||||
<div id="epubContainer" #epubContainer></div>
|
||||
|
||||
<button class="epub-controls-left" (click)="prevPage()">←</button>
|
||||
<button class="epub-controls-right" (click)="nextPage()">→</button>
|
||||
</div>
|
||||
@@ -0,0 +1,106 @@
|
||||
.epub-viewer-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#epubContainer {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.menu-toggle-button,
|
||||
.settings-toggle-button {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
z-index: 1000;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.menu-toggle-button {
|
||||
left: 10px;
|
||||
}
|
||||
|
||||
.settings-toggle-button {
|
||||
right: 10px;
|
||||
}
|
||||
|
||||
.epub-controls-left,
|
||||
.epub-controls-right {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
z-index: 10;
|
||||
font-size: 30px;
|
||||
color: rgba(0, 0, 0, 0.5);
|
||||
cursor: pointer;
|
||||
background: none;
|
||||
border: none;
|
||||
padding: 10px;
|
||||
transition: color 0.3s ease, transform 0.2s ease;
|
||||
}
|
||||
|
||||
.epub-controls-left {
|
||||
left: 10px;
|
||||
}
|
||||
|
||||
.epub-controls-right {
|
||||
right: 10px;
|
||||
}
|
||||
|
||||
.epub-controls-left:hover,
|
||||
.epub-controls-right:hover {
|
||||
color: rgba(0, 0, 0, 0.8);
|
||||
transform: scale(1.1) translateY(-50%);
|
||||
}
|
||||
|
||||
.epub-controls-left:focus,
|
||||
.epub-controls-right:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.chapter-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.chapter-item {
|
||||
padding: 0.5px 0.5px;
|
||||
font-size: 14px;
|
||||
color: var(--text-color);
|
||||
cursor: pointer;
|
||||
transition: background-color 0.3s ease, color 0.3s ease;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.chapter-item:hover {
|
||||
color: greenyellow;
|
||||
}
|
||||
|
||||
.chapter-item:active {
|
||||
background-color: var(--card-background);
|
||||
}
|
||||
|
||||
.settings-content {
|
||||
padding: 10px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.settings-content label {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.settings-content input[type="range"] {
|
||||
width: 100%;
|
||||
margin-top: 5px;
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
import {Component, ElementRef, OnDestroy, OnInit, ViewChild} from '@angular/core';
|
||||
import ePub from 'epubjs';
|
||||
import {EpubService} from '../service/epub.service';
|
||||
import {Drawer} from 'primeng/drawer';
|
||||
import {Button} from 'primeng/button';
|
||||
import {NgForOf} from '@angular/common';
|
||||
import {FormsModule} from '@angular/forms';
|
||||
import {Divider} from 'primeng/divider';
|
||||
import {DropdownModule} from 'primeng/dropdown';
|
||||
|
||||
@Component({
|
||||
selector: 'app-epub-viewer',
|
||||
templateUrl: './epub-viewer.component.html',
|
||||
styleUrls: ['./epub-viewer.component.scss'],
|
||||
imports: [
|
||||
Drawer,
|
||||
Button,
|
||||
NgForOf,
|
||||
FormsModule,
|
||||
Divider,
|
||||
DropdownModule
|
||||
]
|
||||
})
|
||||
export class EpubViewerComponent implements OnInit, OnDestroy {
|
||||
|
||||
@ViewChild('epubContainer', {static: true}) epubContainer!: ElementRef;
|
||||
bookId = 1;
|
||||
chapters: { label: string; href: string }[] = [];
|
||||
isDrawerVisible = false;
|
||||
isSettingsDrawerVisible: boolean = false;
|
||||
private book: any;
|
||||
private rendition: any;
|
||||
private keyListener: (e: KeyboardEvent) => void = () => {
|
||||
};
|
||||
fontSize: number = 100;
|
||||
|
||||
fontTypes: any[] = [
|
||||
{label: 'Serif', value: 'serif'},
|
||||
{label: 'Sans Serif', value: 'sans-serif'},
|
||||
{label: 'Roboto', value: 'roboto'},
|
||||
{label: 'Cursive', value: 'cursive'},
|
||||
{label: 'Monospace', value: 'monospace'}
|
||||
];
|
||||
selectedFontType: string = 'serif';
|
||||
|
||||
constructor(private epubService: EpubService) {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.loadEpub();
|
||||
}
|
||||
|
||||
loadEpub(): void {
|
||||
this.epubService.downloadEpub(this.bookId).subscribe(
|
||||
(data: Blob) => {
|
||||
const fileReader = new FileReader();
|
||||
fileReader.onload = () => {
|
||||
const epubData = fileReader.result as ArrayBuffer;
|
||||
this.book = ePub(epubData);
|
||||
|
||||
this.book.loaded.navigation.then((nav: any) => {
|
||||
this.chapters = nav.toc.map((chapter: any) => ({
|
||||
label: chapter.label,
|
||||
href: chapter.href,
|
||||
}));
|
||||
});
|
||||
|
||||
this.rendition = this.book.renderTo(this.epubContainer.nativeElement, {
|
||||
flow: 'paginated',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
allowScriptedContent: true,
|
||||
});
|
||||
this.rendition.display();
|
||||
this.setupKeyListener();
|
||||
this.updateFontSize();
|
||||
};
|
||||
fileReader.readAsArrayBuffer(data);
|
||||
},
|
||||
(error) => {
|
||||
console.error('Failed to load the EPUB:', error);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
if (this.rendition) {
|
||||
this.rendition.off('keyup', this.keyListener);
|
||||
}
|
||||
document.removeEventListener('keyup', this.keyListener);
|
||||
}
|
||||
|
||||
updateFontSize(): void {
|
||||
if (this.rendition) {
|
||||
this.rendition.themes.fontSize(`${this.fontSize}%`);
|
||||
}
|
||||
}
|
||||
|
||||
increaseFontSize(): void {
|
||||
this.fontSize += 10;
|
||||
if (this.fontSize > 200) {
|
||||
this.fontSize = 200;
|
||||
}
|
||||
this.updateFontSize();
|
||||
}
|
||||
|
||||
decreaseFontSize(): void {
|
||||
this.fontSize -= 10;
|
||||
if (this.fontSize < 50) {
|
||||
this.fontSize = 50;
|
||||
}
|
||||
this.updateFontSize();
|
||||
}
|
||||
|
||||
changeFontType(event: any): void {
|
||||
if (this.rendition) {
|
||||
this.rendition.themes.font(this.selectedFontType);
|
||||
}
|
||||
}
|
||||
|
||||
navigateToChapter(chapter: { label: string; href: string }): void {
|
||||
if (this.book && chapter.href) {
|
||||
this.book.rendition.display(chapter.href);
|
||||
}
|
||||
}
|
||||
|
||||
nextPage(): void {
|
||||
if (this.rendition) {
|
||||
this.rendition.next();
|
||||
}
|
||||
}
|
||||
|
||||
prevPage(): void {
|
||||
if (this.rendition) {
|
||||
this.rendition.prev();
|
||||
}
|
||||
}
|
||||
|
||||
toggleDrawer(): void {
|
||||
this.isDrawerVisible = !this.isDrawerVisible;
|
||||
}
|
||||
|
||||
toggleSettingsDrawer(): void {
|
||||
this.isSettingsDrawerVisible = !this.isSettingsDrawerVisible;
|
||||
}
|
||||
|
||||
private setupKeyListener(): void {
|
||||
this.keyListener = (e: KeyboardEvent) => {
|
||||
switch (e.key) {
|
||||
case 'ArrowLeft':
|
||||
this.prevPage();
|
||||
break;
|
||||
case 'ArrowRight':
|
||||
this.nextPage();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
if (this.rendition) {
|
||||
this.rendition.on('keyup', this.keyListener);
|
||||
}
|
||||
document.addEventListener('keyup', this.keyListener);
|
||||
}
|
||||
}
|
||||
18
booklore-ui/src/app/epub-viewer/service/epub.service.ts
Normal file
18
booklore-ui/src/app/epub-viewer/service/epub.service.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class EpubService {
|
||||
private baseUrl = 'http://localhost:8080/api/epub';
|
||||
|
||||
constructor(private http: HttpClient) {}
|
||||
|
||||
// New method to fetch the entire EPUB file
|
||||
downloadEpub(bookId: number): Observable<Blob> {
|
||||
const url = `${this.baseUrl}/${bookId}/download`;
|
||||
return this.http.get(url, { responseType: 'blob' });
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user