diff --git a/src/common/reader.js b/src/common/reader.js index 1ec93059..3642a7c7 100644 --- a/src/common/reader.js +++ b/src/common/reader.js @@ -1029,6 +1029,15 @@ class Reader { return this._annotationManager.mergeAnnotations(ids); } + /** + * @param {BufferSource} metadata + * @returns {{ count: number, lastModified?: Date }} + */ + getKOReaderAnnotationStats(metadata) { + this._ensureType('epub'); + return this._primaryView.getKOReaderAnnotationStats(metadata); + } + /** * @param {BufferSource} metadata */ @@ -1037,6 +1046,15 @@ class Reader { this._primaryView.importAnnotationsFromKOReaderMetadata(metadata); } + /** + * @param {string} metadata + * @returns {{ count: number, lastModified?: Date }} + */ + getCalibreAnnotationStats(metadata) { + this._ensureType('epub'); + return this._primaryView.getCalibreAnnotationStats(metadata); + } + /** * @param {string} metadata */ diff --git a/src/dom/epub/epub-view.ts b/src/dom/epub/epub-view.ts index 9c26c5c2..1a7f3c0b 100644 --- a/src/dom/epub/epub-view.ts +++ b/src/dom/epub/epub-view.ts @@ -491,6 +491,22 @@ class EPUBView extends DOMView { } } + getKOReaderAnnotationStats(metadata: BufferSource): { count: number, lastModified?: Date } { + try { + let annotations = parseAnnotationsFromKOReaderMetadata(metadata); + if (annotations.length) { + return { + count: annotations.length, + lastModified: annotations.map(a => new Date(a.datetime)).reduce( + (max, cur) => (cur > max ? cur : max) + ), + }; + } + } + catch (e) {} + return { count: 0 }; + } + importAnnotationsFromKOReaderMetadata(metadata: BufferSource) { for (let koReaderAnnotation of parseAnnotationsFromKOReaderMetadata(metadata)) { let range = koReaderAnnotationToRange(koReaderAnnotation, this._sectionRenderers); @@ -514,6 +530,22 @@ class EPUBView extends DOMView { } } + getCalibreAnnotationStats(metadata: string): { count: number, lastModified?: Date } { + try { + let annotations = parseAnnotationsFromCalibreMetadata(metadata); + if (annotations.length) { + return { + count: annotations.length, + lastModified: annotations.map(a => new Date(a.timestamp)).reduce( + (max, cur) => (cur > max ? cur : max) + ), + }; + } + } + catch (e) {} + return { count: 0 }; + } + importAnnotationsFromCalibreMetadata(metadata: string) { for (let calibreAnnotation of parseAnnotationsFromCalibreMetadata(metadata)) { let range = calibreAnnotationToRange(calibreAnnotation, this._sectionRenderers); diff --git a/src/dom/epub/lib/calibre.ts b/src/dom/epub/lib/calibre.ts index dc750b3e..51d02530 100644 --- a/src/dom/epub/lib/calibre.ts +++ b/src/dom/epub/lib/calibre.ts @@ -80,4 +80,5 @@ export type CalibreAnnotation = { kind: 'decoration'; which: 'wavy' | 'strikeout' | string; }; + timestamp: string; }; diff --git a/src/dom/epub/lib/koreader.ts b/src/dom/epub/lib/koreader.ts index 6bb40455..14328b95 100644 --- a/src/dom/epub/lib/koreader.ts +++ b/src/dom/epub/lib/koreader.ts @@ -111,9 +111,10 @@ export function parseAnnotationsFromKOReaderMetadata(metadata: BufferSource): KO pos0: findField(annotationTable, 'pos0'), pos1: findField(annotationTable, 'pos1'), text: findField(annotationTable, 'text'), + datetime: findField(annotationTable, 'datetime'), }; for (let [key, value] of Object.entries(annotationFields)) { - if (['pos0', 'pos1', 'text'].includes(key) && !value) { + if (['pos0', 'pos1', 'text', 'datetime'].includes(key) && !value) { throw new Error(`Invalid KOReader metadata: annotation is missing required field "${key}"`); } if (value && value.type !== 'StringLiteral') { @@ -125,6 +126,7 @@ export function parseAnnotationsFromKOReaderMetadata(metadata: BufferSource): KO pos0: parseKOReaderPosition((annotationFields.pos0 as StringLiteral).value), pos1: parseKOReaderPosition((annotationFields.pos1 as StringLiteral).value), text: (annotationFields.text as StringLiteral).value, + datetime: (annotationFields.datetime as StringLiteral).value, }); } return annotations; @@ -136,6 +138,7 @@ export type KOReaderAnnotation = { pos0: KOReaderPosition; pos1: KOReaderPosition; text: string; + datetime: string; }; export type KOReaderPosition = {