From 22a93a28e2f5bfb6d56056a76f53b518386fad56 Mon Sep 17 00:00:00 2001 From: Ross Williams Date: Fri, 21 Feb 2025 04:15:37 +0000 Subject: [PATCH] Refactor epub reader progress calculation The code in epub-progress.js was causing the epub file to be loaded twice over the network, because it created a second instance of the epub.js library. I moved all the progress percentage display code into epub.js. Only the locationchange polyfill code was left in the separate file, renamed to locationchange-polyfill.js. I rewrote the percentage progress code to use epub.js events, giving more succinct and readable code. Also, I added localStorage caching of the epub location calculations, required for displaying percentage progress, which can take up to 60-90 seconds to calculate on longer ebooks. This localStorage caching approach is recommended by epub.js in the `locations.html` example in their repo. Signed-off-by: Ross Williams --- cps/static/css/main.css | 1 + cps/static/js/reading/epub-progress.js | 54 ------------------- cps/static/js/reading/epub.js | 26 +++++++++ .../js/reading/locationchange-polyfill.js | 21 ++++++++ cps/templates/read.html | 2 +- 5 files changed, 49 insertions(+), 55 deletions(-) delete mode 100644 cps/static/js/reading/epub-progress.js create mode 100644 cps/static/js/reading/locationchange-polyfill.js diff --git a/cps/static/css/main.css b/cps/static/css/main.css index 3afaf7a5..75e33943 100644 --- a/cps/static/css/main.css +++ b/cps/static/css/main.css @@ -831,4 +831,5 @@ input:-moz-placeholder { color: #454545; } bottom: 4rem; width: fit-content; position: absolute; + visibility: hidden; } diff --git a/cps/static/js/reading/epub-progress.js b/cps/static/js/reading/epub-progress.js deleted file mode 100644 index b8fc11d0..00000000 --- a/cps/static/js/reading/epub-progress.js +++ /dev/null @@ -1,54 +0,0 @@ -/** - * waits until queue is finished, meaning the book is done loading - * @param callback - */ -function qFinished(callback){ - let timeout=setInterval(()=>{ - if(reader.rendition.q.running===undefined) - clearInterval(timeout); - callback(); - },300 - ) -} - -function calculateProgress(){ - let data=reader.rendition.location.end; - return Math.round(epub.locations.percentageFromCfi(data.cfi)*100); -} - -// register new event emitter locationchange that fires on urlchange -// source: https://stackoverflow.com/a/52809105/21941129 -(() => { - let oldPushState = history.pushState; - history.pushState = function pushState() { - let ret = oldPushState.apply(this, arguments); - window.dispatchEvent(new Event('locationchange')); - return ret; - }; - - let oldReplaceState = history.replaceState; - history.replaceState = function replaceState() { - let ret = oldReplaceState.apply(this, arguments); - window.dispatchEvent(new Event('locationchange')); - return ret; - }; - - window.addEventListener('popstate', () => { - window.dispatchEvent(new Event('locationchange')); - }); -})(); - -window.addEventListener('locationchange',()=>{ - let newPos=calculateProgress(); - progressDiv.textContent=newPos+"%"; -}); - -var epub=ePub(calibre.bookUrl) - -let progressDiv=document.getElementById("progress"); - -qFinished(()=>{ - epub.locations.generate().then(()=> { - window.dispatchEvent(new Event('locationchange')) -}); -}) diff --git a/cps/static/js/reading/epub.js b/cps/static/js/reading/epub.js index a552da05..8d299ce8 100644 --- a/cps/static/js/reading/epub.js +++ b/cps/static/js/reading/epub.js @@ -52,6 +52,32 @@ var reader; } }); + // Update progress percentage + let progressDiv = document.getElementById("progress"); + reader.book.ready.then((()=>{ + let locations_key = reader.book.key()+'-locations'; + let stored_locations = localStorage.getItem(locations_key); + let make_locations, save_locations; + if (stored_locations) { + make_locations = Promise.resolve(reader.book.locations.load(stored_locations)); + // No-op because locations are already saved + save_locations = ()=>{}; + } else { + make_locations = reader.book.locations.generate(); + save_locations = ()=>{ + localStorage.setItem(locations_key, reader.book.locations.save()); + }; + } + make_locations.then(()=>{ + reader.rendition.on('relocated', (location)=>{ + let percentage = Math.round(location.end.percentage*100); + progressDiv.textContent=percentage+"%"; + }); + reader.rendition.reportLocation(); + progressDiv.style.visibility = "visible"; + }).then(save_locations); + })); + /** * @param {string} action - Add or remove bookmark * @param {string|int} location - Location or zero diff --git a/cps/static/js/reading/locationchange-polyfill.js b/cps/static/js/reading/locationchange-polyfill.js new file mode 100644 index 00000000..4845ea84 --- /dev/null +++ b/cps/static/js/reading/locationchange-polyfill.js @@ -0,0 +1,21 @@ +// register new event emitter locationchange that fires on urlchange +// source: https://stackoverflow.com/a/52809105/21941129 +(() => { + let oldPushState = history.pushState; + history.pushState = function pushState() { + let ret = oldPushState.apply(this, arguments); + window.dispatchEvent(new Event('locationchange')); + return ret; + }; + + let oldReplaceState = history.replaceState; + history.replaceState = function replaceState() { + let ret = oldReplaceState.apply(this, arguments); + window.dispatchEvent(new Event('locationchange')); + return ret; + }; + + window.addEventListener('popstate', () => { + window.dispatchEvent(new Event('locationchange')); + }); +})(); diff --git a/cps/templates/read.html b/cps/templates/read.html index bc5bd6a8..496267af 100644 --- a/cps/templates/read.html +++ b/cps/templates/read.html @@ -215,6 +215,6 @@ - +