From e018f8341e3df69f20c06d1841296bd5c650d230 Mon Sep 17 00:00:00 2001 From: Vincent Schmandt Date: Tue, 21 Mar 2023 13:27:21 +0100 Subject: [PATCH 1/6] EPUB progress persistence --- client/components/readers/EpubReader.vue | 181 +++++++++++----------- client/components/readers/EpubReader2.vue | 88 ----------- client/components/readers/Reader.vue | 3 +- 3 files changed, 89 insertions(+), 183 deletions(-) delete mode 100644 client/components/readers/EpubReader2.vue diff --git a/client/components/readers/EpubReader.vue b/client/components/readers/EpubReader.vue index 9c19c3ec..47678c17 100644 --- a/client/components/readers/EpubReader.vue +++ b/client/components/readers/EpubReader.vue @@ -2,128 +2,123 @@
- chevron_left + chevron_left
-
+
- -
-

{{ progress }}%

-
- chevron_right + chevron_right
diff --git a/client/components/readers/EpubReader2.vue b/client/components/readers/EpubReader2.vue deleted file mode 100644 index 69839d78..00000000 --- a/client/components/readers/EpubReader2.vue +++ /dev/null @@ -1,88 +0,0 @@ - - - diff --git a/client/components/readers/Reader.vue b/client/components/readers/Reader.vue index 5481f250..c8d5c26b 100644 --- a/client/components/readers/Reader.vue +++ b/client/components/readers/Reader.vue @@ -37,8 +37,7 @@ export default { } }, componentName() { - if (this.ebookType === 'epub' && this.$isDev) return 'readers-epub-reader2' - else if (this.ebookType === 'epub') return 'readers-epub-reader' + if (this.ebookType === 'epub') return 'readers-epub-reader' else if (this.ebookType === 'mobi') return 'readers-mobi-reader' else if (this.ebookType === 'pdf') return 'readers-pdf-reader' else if (this.ebookType === 'comic') return 'readers-comic-reader' From 17b8cf19b7e02305894876921c8aedc3bc309ba9 Mon Sep 17 00:00:00 2001 From: Vincent Schmandt Date: Tue, 21 Mar 2023 13:34:21 +0100 Subject: [PATCH 2/6] Add Location Storage --- client/components/readers/EpubReader.vue | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/client/components/readers/EpubReader.vue b/client/components/readers/EpubReader.vue index 47678c17..6fd652f8 100644 --- a/client/components/readers/EpubReader.vue +++ b/client/components/readers/EpubReader.vue @@ -107,18 +107,29 @@ export default { height: window.innerHeight * 0.9 }); - reader.rendition.display(cfi); + reader.rendition.display(this.userMediaProgress?.currentTime); reader.book.ready.then(() => { reader.rendition.on('relocated', reader.relocated); reader.rendition.on('keydown', reader.keyUp) document.addEventListener('keydown', reader.keyUp, false); - reader.book.locations.generate(); + if (reader.userMediaProgress?.duration) { + reader.book.locations.load(reader.userMediaProgress.duration) + } else { + reader.book.locations.generate().then(() => { + var updatePayload = { + duration: reader.book.locations.save(), + } + this.$axios.$patch(`/api/me/progress/${this.libraryItemId}`, updatePayload).catch((error) => { + console.error('Failed', error) + }) + }); + } }); }, }, mounted() { - this.initEpub(this.userMediaProgress?.currentTime); + this.initEpub(); }, }; From 6c618d77604ccf2e7f744d9eb17128912cb97b55 Mon Sep 17 00:00:00 2001 From: Vincent Schmandt Date: Tue, 21 Mar 2023 13:36:06 +0100 Subject: [PATCH 3/6] Adjust height to fit metadata --- client/components/readers/EpubReader.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/components/readers/EpubReader.vue b/client/components/readers/EpubReader.vue index 6fd652f8..d60e41f4 100644 --- a/client/components/readers/EpubReader.vue +++ b/client/components/readers/EpubReader.vue @@ -6,7 +6,7 @@ class="material-icons text-white text-opacity-50 hover:text-opacity-80 cursor-pointer text-6xl" @mousedown.prevent @click="prev">chevron_left
-
+
@@ -104,7 +104,7 @@ export default { /** @type {ePub.Rendition} */ reader.rendition = reader.book.renderTo("viewer", { width: window.innerWidth - 200, - height: window.innerHeight * 0.9 + height: window.innerHeight * 0.8 }); reader.rendition.display(this.userMediaProgress?.currentTime); From 5078818295547dba0cb3662eef12131e73312667 Mon Sep 17 00:00:00 2001 From: Vincent Schmandt Date: Wed, 22 Mar 2023 11:16:01 +0100 Subject: [PATCH 4/6] Add MediaProgress fields Add Table of Contents --- client/components/readers/EpubReader.vue | 105 ++++++++++++----------- client/components/readers/Reader.vue | 58 +++++++++++-- server/objects/user/MediaProgress.js | 11 ++- 3 files changed, 114 insertions(+), 60 deletions(-) diff --git a/client/components/readers/EpubReader.vue b/client/components/readers/EpubReader.vue index d60e41f4..72205dcd 100644 --- a/client/components/readers/EpubReader.vue +++ b/client/components/readers/EpubReader.vue @@ -2,15 +2,16 @@
- chevron_left
-
+
- chevron_right
@@ -21,7 +22,7 @@ import ePub from "epubjs"; /** - * @typedef {EpubReader} + * @typedef {object} EpubReader * @property {ePub.Book} book * @property {ePub.Rendition} rendition */ @@ -30,7 +31,7 @@ export default { url: String, libraryItem: { type: Object, - default: () => {} + default: () => { } } }, data() { @@ -42,61 +43,59 @@ export default { }; }, computed: { - libraryItemId() { return this.libraryItem ? this.libraryItem.id : null }, - hasPrev() { return !this.rendition?.location.atStart }, - hasNext() { return !this.rendition?.location.atEnd }, + /** @returns {string} */ + libraryItemId() { return this.libraryItem?.id }, + hasPrev() { return !this.rendition?.location?.atStart }, + hasNext() { return !this.rendition?.location?.atEnd }, + /** @returns {Array} */ chapters() { return this.book ? this.book.navigation.toc : [] }, - title() { return this.book ? this.book.metadata.title : "" }, - author() { return this.book ? this.book.metadata.creator : "" }, userMediaProgress() { if (!this.libraryItemId) return return this.$store.getters['user/getUserMediaProgress'](this.libraryItemId) }, }, methods: { - changedChapter() { this.rendition?.display(this.selectedChapter) }, - prev() { this.rendition?.prev() }, - next() { this.rendition?.next() }, + prev() { return this.rendition?.prev() }, + next() { return this.rendition?.next() }, + goToChapter(href) { return this.rendition?.display(href) }, keyUp(e) { + const rtl = this.book.package.metadata.direction === 'rtl' if ((e.keyCode || e.which) == 37) { - this.prev(); + return rtl ? this.next() : this.prev(); } else if ((e.keyCode || e.which) == 39) { - this.next(); + return rtl ? this.prev() : this.next(); } }, + /** + * @param {object} payload + * @param {string} payload.ebookLocation - CFI of the current location + * @param {string} payload.ebookLocations - list of CFI tags + * @param {number} payload.progress - Progress Percentage + */ + updateProgress(payload) { + this.$axios.$patch(`/api/me/progress/${this.libraryItemId}`, payload).catch((error) => { + console.error('EpubReader.updateProgress failed:', error) + }) + }, + /** @param {string} location - CFI of the new location */ relocated(location) { - var cfi = location.start.cfi; - var cfiFragment = "#" + cfi; - - if(window.location.hash != cfiFragment) { - const url = new URL(window.location); - url.hash = cfiFragment; - history.pushState({}, '', url); - - var updatePayload = { - currentTime: cfi, - } - - var percentage = this.book.locations.percentageFromCfi(cfi); - if (percentage) { - updatePayload.progress = percentage - } - - this.$axios.$patch(`/api/me/progress/${this.libraryItemId}`, updatePayload).catch((error) => { - console.error('Failed', error) - }) + if (location.end.percentage) { + this.updateProgress({ + ebookLocation: location.start.cfi, + progress: location.end.percentage, + }); + } else { + this.updateProgress({ + ebookLocation: location.start.cfi, + }); } }, - initEpub(cfi) { + initEpub() { + /** @type {EpubReader} */ var reader = this; /** @type {ePub.Book} */ reader.book = new ePub(reader.url, { - storage: false, - worker: false, - manager: "continuous", - flow: "scrolled", - spreads: false, width: window.innerWidth - 200, height: window.innerHeight - 50, }); @@ -107,23 +106,27 @@ export default { height: window.innerHeight * 0.8 }); - reader.rendition.display(this.userMediaProgress?.currentTime); + // load saved progress + reader.rendition.display(this.userMediaProgress?.ebookLocation || reader.book.locations.start); + + // load style + reader.rendition.themes.default({ "*": { "color": "#fff!important" } }); + reader.book.ready.then(() => { + // set up event listeners reader.rendition.on('relocated', reader.relocated); reader.rendition.on('keydown', reader.keyUp) document.addEventListener('keydown', reader.keyUp, false); - if (reader.userMediaProgress?.duration) { - reader.book.locations.load(reader.userMediaProgress.duration) + // load ebook cfi locations + if (this.userMediaProgress?.ebookLocations) { + reader.book.locations.load(this.userMediaProgress?.ebookLocations) } else { reader.book.locations.generate().then(() => { - var updatePayload = { - duration: reader.book.locations.save(), - } - this.$axios.$patch(`/api/me/progress/${this.libraryItemId}`, updatePayload).catch((error) => { - console.error('Failed', error) - }) - }); + this.updateProgress({ + ebookLocations: reader.book.locations.save(), + }); + }); } }); }, diff --git a/client/components/readers/Reader.vue b/client/components/readers/Reader.vue index c8d5c26b..1d7ef6d6 100644 --- a/client/components/readers/Reader.vue +++ b/client/components/readers/Reader.vue @@ -1,24 +1,52 @@ diff --git a/client/components/readers/Reader.vue b/client/components/readers/Reader.vue index 1d7ef6d6..a7383fb7 100644 --- a/client/components/readers/Reader.vue +++ b/client/components/readers/Reader.vue @@ -1,13 +1,11 @@ @@ -145,13 +141,10 @@ export default { }, methods: { toggleToC() { - this.tocOpen = !this.tocOpen; - this.chapters = this.$refs.readerComponent.chapters; - this.$refs.tocContainer.classList.toggle('invisible') - this.$refs.tocOverlay.classList.toggle('opacity-0') - this.$refs.tocOverlay.classList.toggle('opacity-50') + this.tocOpen = !this.tocOpen + this.chapters = this.$refs.readerComponent.chapters }, - openSettings() { }, + openSettings() {}, hotkey(action) { console.log('Reader hotkey', action) if (!this.$refs.readerComponent) return @@ -192,4 +185,8 @@ export default { .ebook-viewer { height: calc(100% - 96px); } +.tocContent { + height: calc(100% - 36px); + overflow-y: auto; +} \ No newline at end of file diff --git a/client/pages/item/_id/index.vue b/client/pages/item/_id/index.vue index 988b89cc..dfcdf38b 100644 --- a/client/pages/item/_id/index.vue +++ b/client/pages/item/_id/index.vue @@ -156,7 +156,7 @@

{{ $strings.LabelYourProgress }}: {{ Math.round(progressPercent * 100) }}%

{{ $strings.LabelFinished }} {{ $formatDate(userProgressFinishedAt, dateFormat) }}

-

{{ $getString('LabelTimeRemaining', [$elapsedPretty(userTimeRemaining)]) }}

+

{{ $getString('LabelTimeRemaining', [$elapsedPretty(userTimeRemaining)]) }}

{{ $strings.LabelStarted }} {{ $formatDate(userProgressStartedAt, dateFormat) }}

@@ -471,8 +471,13 @@ export default { const duration = this.userMediaProgress.duration || this.duration return duration - this.userMediaProgress.currentTime }, + useEBookProgress() { + if (!this.userMediaProgress || this.userMediaProgress.progress) return false + return this.userMediaProgress.ebookProgress > 0 + }, progressPercent() { - return this.userMediaProgress ? Math.max(Math.min(1, Math.max(this.userMediaProgress.progress || 0, this.userMediaProgress.ebookProgress || 0)), 0) : 0 + if (this.useEBookProgress) return Math.max(Math.min(1, this.userMediaProgress.ebookProgress), 0) + return this.userMediaProgress ? Math.max(Math.min(1, this.userMediaProgress.progress), 0) : 0 }, userProgressStartedAt() { return this.userMediaProgress ? this.userMediaProgress.startedAt : 0