Pārlūkot izejas kodu

Добавление отображения обложки (coverpage) в окне загруженных файлов

Book Pauk 2 gadi atpakaļ
vecāks
revīzija
7fc98fc7da

+ 15 - 2
client/api/reader.js

@@ -1,5 +1,6 @@
 import axios from 'axios';
 import * as utils from '../share/utils';
+import * as cryptoUtils from '../share/cryptoUtils';
 import wsc from './webSocketConnection';
 
 const api = axios.create({
@@ -225,8 +226,20 @@ class Reader {
         return response;
     }
 
-    async uploadFileBuf(buf) {
-        const response = await wsc.message(await wsc.send({action: 'upload-file-buf', buf}));
+    async uploadFileBuf(buf, urlCallback) {
+        const key = utils.toHex(cryptoUtils.sha256(buf));
+        const url = `disk://${key}`;
+
+        if (urlCallback)
+            urlCallback(url);
+
+        let response;
+        try {
+            await axios.head(`/upload/${key}`, {headers: {'Cache-Control': 'no-cache'}});
+            response = await wsc.message(await wsc.send({action: 'upload-file-touch', url}));
+        } catch (e) {
+            response = await wsc.message(await wsc.send({action: 'upload-file-buf', buf}));
+        }
 
         if (response.error)
             throw new Error(response.error);

+ 3 - 0
client/components/Reader/Reader.vue

@@ -194,6 +194,7 @@ import ReaderDialogs from './ReaderDialogs/ReaderDialogs.vue';
 
 import bookManager from './share/bookManager';
 import wallpaperStorage from './share/wallpaperStorage';
+import coversStorage from './share/coversStorage';
 import dynamicCss from '../../share/dynamicCss';
 
 import rstore from '../../store/modules/reader';
@@ -366,6 +367,8 @@ class Reader {
     mounted() {
         (async() => {
             await wallpaperStorage.init();
+            await coversStorage.init();
+            
             await bookManager.init(this.settings);
             bookManager.addEventListener(this.bookManagerEvent);
 

+ 43 - 1
client/components/Reader/RecentBooksPage/RecentBooksPage.vue

@@ -106,7 +106,8 @@
 
                     <div class="row-part column justify-center items-stretch" style="width: 80px">
                         <div class="col row justify-center items-center clickable" @click="loadBook(item)">
-                            <q-icon name="la la-book" size="40px" style="color: #dddddd" />
+                            <div v-show="isLoadedCover(item.coverPageUrl)" v-html="getCoverHtml(item.coverPageUrl)" />
+                            <q-icon v-show="!isLoadedCover(item.coverPageUrl)" name="la la-book" size="40px" style="color: #dddddd" />
                         </div>
 
                         <div v-show="!showSameBook && item.group && item.group.length > 0" class="row justify-center" style="font-size: 70%">
@@ -213,6 +214,7 @@ import LockQueue from '../../../share/LockQueue';
 import Window from '../../share/Window.vue';
 import bookManager from '../share/bookManager';
 import readerApi from '../../../api/reader';
+import coversStorage from '../share/coversStorage';
 
 const componentOptions = {
     components: {
@@ -240,6 +242,8 @@ class RecentBooksPage {
     showSameBook = false;
     archive = false;
 
+    covers = {};
+
     created() {
         this.commit = this.$store.commit;
 
@@ -337,6 +341,7 @@ class RecentBooksPage {
                     active: (activeBook.key == book.key),
                     activeParent: false,
                     inGroup: false,
+                    coverPageUrl: book.coverPageUrl,
 
                     //для сортировки
                     loadTimeRaw,
@@ -654,6 +659,43 @@ class RecentBooksPage {
         }
         return true;
     }
+
+    makeCoverHtml(data) {
+        return `<img src="${data}" style="height: 100%; width: 100%; object-fit: contain" />`;
+    }
+
+    isLoadedCover(coverPageUrl) {
+        if (!coverPageUrl)
+            return false;
+
+        let loadedCover = this.covers[coverPageUrl];
+        if (!loadedCover) {
+            (async() => {
+                //сначала заглянем в storage
+                let data = await coversStorage.getData(coverPageUrl);
+                if (data) {
+                   this.covers[coverPageUrl] = this.makeCoverHtml(data);
+                } else {//иначе идем на сервер
+                    try {
+                        data = await readerApi.getUploadedFileBuf(coverPageUrl);
+                        await coversStorage.setData(coverPageUrl, data);
+                        this.covers[coverPageUrl] = this.makeCoverHtml(data);
+                    } catch (e) {
+                        console.error(e);
+                    }
+                }
+            })();
+        }
+
+        return (loadedCover != undefined);
+    }
+
+    getCoverHtml(coverPageUrl) {
+        if (coverPageUrl && this.covers[coverPageUrl])
+            return this.covers[coverPageUrl];
+        else
+            return '';
+    }
 }
 
 export default vueComponent(RecentBooksPage);

+ 7 - 1
client/components/Reader/share/BookParser.js

@@ -85,6 +85,7 @@ export default class BookParser {
         let binaryId = '';
         let binaryType = '';
         let dimPromises = [];
+        this.coverPageId = '';
 
         //оглавление
         this.contents = [];
@@ -289,7 +290,7 @@ export default class BookParser {
                     const href = attrs.href.value;
                     const alt = (attrs.alt && attrs.alt.value ? attrs.alt.value : '');
                     const {id, local} = this.imageHrefToId(href);
-                    if (href[0] == '#') {//local
+                    if (local) {//local
                         imageNum++;
 
                         if (inPara && !this.sets.showInlineImagesInCenter && !center)
@@ -301,6 +302,11 @@ export default class BookParser {
 
                         if (inPara && this.sets.showInlineImagesInCenter)
                             newParagraph();
+
+                        //coverpage
+                        if (path == '/fictionbook/description/title-info/coverpage/image') {
+                            this.coverPageId = id;
+                        }
                     } else {//external
                         imageNum++;
 

+ 20 - 1
client/components/Reader/share/bookManager.js

@@ -2,8 +2,9 @@ import localForage from 'localforage';
 import path from 'path-browserify';
 import _ from 'lodash';
 
-import * as utils from '../../../share/utils';
 import BookParser from './BookParser';
+import readerApi from '../../../api/reader';
+import * as utils from '../../../share/utils';
 
 const maxDataSize = 500*1024*1024;//compressed bytes
 const maxRecentLength = 5000;
@@ -345,9 +346,27 @@ class BookManager {
         const parsed = new BookParser(this.settings);
 
         const parsedMeta = await parsed.parse(data, callback);
+
+        //cover page
+        let coverPageUrl = '';
+        if (parsed.coverPageId && parsed.binary[parsed.coverPageId]) {
+            const bin = parsed.binary[parsed.coverPageId];
+            const dataUrl = await utils.resizeImage(`data:${bin.type};base64,${bin.data}`);
+
+            //отправим dataUrl на сервер в /upload
+            try {
+                await readerApi.uploadFileBuf(dataUrl, (url) => {
+                    coverPageUrl = url;
+                });
+            } catch (e) {
+                console.error(e);
+            }
+        }
+
         const result = Object.assign({}, meta, parsedMeta, {
             length: data.length,
             textLength: parsed.textLength,
+            coverPageUrl,
             parsed
         });
 

+ 4 - 0
client/share/utils.js

@@ -363,4 +363,8 @@ export function getBookTitle(fb2) {
     ]).join(' - ');
 
     return result;
+}
+
+export async function resizeImage(dataUrl) {
+    return dataUrl;
 }