diff --git a/Viewer/features/sharp.js b/Viewer/features/sharp.js index 4dd94c61..1c1866a2 100755 --- a/Viewer/features/sharp.js +++ b/Viewer/features/sharp.js @@ -82,6 +82,8 @@ var SharpActions = actions.Actions({ // 'files' 'preview-progress-mode': 'gids', + 'preview-generate-threshold': 2000, + // NOTE: this uses 'preview-sizes' and 'preview-path-template' // from filesystem.IndexFormat... }, @@ -422,8 +424,13 @@ var SharpActions = actions.Actions({ // XXX should this update all images or just the ones that have no metadata??? + // XXX would be nice to be able to abort this... + // ...and/or have a generic abort protocol triggered when loading... + // XXX make each section optional... // XXX revise name... cacheImageMetadata: ['- Sharp|Image/', + core.doc` + `, function(images, logger){ var that = this @@ -452,34 +459,57 @@ var SharpActions = actions.Actions({ logger = logger && logger.push('Caching image metadata') logger && logger.emit('queued', images) + // NOTE: we are caching this to avoid messing things up when + // loading before this was finished... + var cached_images = this.images + var loaded = this.ribbons - && this.ribbons.getImageGIDs() + && new Set(this.ribbons.getImageGIDs()) return images .mapChunks(function(gid){ - var img = that.images[gid] return sharp(that.getImagePath(gid)) .metadata() - .then(function(data){ - var o = normalizeOrientation(data.orientation) + .catch(function(){ + logger && logger.emit('skipping', gid) }) + .then(function(metadata){ + // XXX what should we return in case of an error??? + if(metadata == null){ + return } + + var img = cached_images[gid] + + var o = normalizeOrientation(metadata.orientation) // NOTE: we need to set orientation to something // or we'll check it again and again... img.orientation = o.orientation || 0 img.flipped = o.flipped + /* XXX should generate previews in a temp dir or as data-urls... + // if image too large, generate preview(s)... + var size_threshold = that.config['preview-generate-threshold'] + if(size_threshold + && Math.max(metadata.width, metadata.height) > size_threshold){ + logger && logger.emit('Image too large', gid) + // XXX might be a good idea to only generate + // a single preview... + // XXX + this.makePreviews(gid) } + //*/ + // XXX EXIF -- keep compatible with exiftool... // - dates // - camera / lens / ... - var exif = data.exif - && exifReader(data.exif) + var exif = metadata.exif + && exifReader(metadata.exif) // XXX // xmp:Rating... - var rating = data.xmp + var rating = metadata.xmp // NOTE: we do not need the full XML // fluff here, just get some values... && parseInt( - (data.xmp.toString() + (metadata.xmp.toString() .match(/(?<(xmp:Rating)[^>]*>(?.*)<\/\2>)/i) || {groups: {}}) .groups.value) @@ -487,24 +517,20 @@ var SharpActions = actions.Actions({ && (img.metadata = img.metadata || {}) && (img.metadata.rating = rating || 0) - // XXX check size and create preview if needed... - // ...might be a good idea to keep previews - // as data urls (or tmp-dir) if no index exists and - // queue their creation on save... - that.markChanged('images', [gid]) logger && logger.emit('done', gid) // update image to use the orientation... loaded - && loaded.includes(gid) - && that.ribbons.updateImage(gid) }) }) }], + && loaded.has(gid) + && that.ribbons.updateImage(gid) + + return gid }) }) }], }) // XXX need to auto-generate previews for very large images... -// XXX read exif/xmp/... var Sharp = module.Sharp = core.ImageGridFeatures.Feature({ title: '', @@ -528,13 +554,22 @@ module.Sharp = core.ImageGridFeatures.Feature({ //*/ // set orientation if not defined... - // XXX this would be a great place to parse EXIF metadata... - // XXX check size and create preview if needed... + // NOTE: progress on this is not shown so as to avoid spamming + // the UI... + // XXX should this be pre or post??? + // ...creating a preview would be more logical than trying + // to load a gigantic image, maybe even loading a placeholder + // while doing so... + //['updateImage.pre', + // function(gid){ ['updateImage', function(_, gid){ - this.images[gid] - && this.images[gid].orientation == null - && this.cacheImageMetadata(gid, false) }], + var img = this.images[gid] + img + && img.orientation == null + && this.cacheImageMetadata(gid, false) + && this.logger + && this.logger.emit('Caching metadata for', gid) }], // XXX need to: // - if image too large to set the preview to "loading..." diff --git a/Viewer/package-lock.json b/Viewer/package-lock.json index 39fe7c30..fc4d3fbc 100755 --- a/Viewer/package-lock.json +++ b/Viewer/package-lock.json @@ -636,9 +636,9 @@ } }, "electron": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/electron/-/electron-9.3.2.tgz", - "integrity": "sha512-0lleEf9msAXGDi2GukAuiGdw3VDgSTlONOnJgqDEz1fuSEVsXz5RX+hNPKDsVDerLTFg/C34RuJf4LwHvkKcBA==", + "version": "9.3.3", + "resolved": "https://registry.npmjs.org/electron/-/electron-9.3.3.tgz", + "integrity": "sha512-xghKeUY1qgnEcJ5w2rXo/toH+8NT2Dktx2aAxBNPV7CIJr3mejJJAPwLbycwtddzr37tgKxHeHlc8ivfKtMkJQ==", "requires": { "@electron/get": "^1.0.1", "@types/node": "^12.0.12", @@ -646,9 +646,9 @@ }, "dependencies": { "@types/node": { - "version": "12.12.64", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.64.tgz", - "integrity": "sha512-UV1/ZJMC+HcP902wWdpC43cAcGu0IQk/I5bXjP2aSuCjsk3cE74mDvFrLKga7oDC170ugOAYBwfT4DSQW3akDA==" + "version": "12.19.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.19.3.tgz", + "integrity": "sha512-8Jduo8wvvwDzEVJCOvS/G6sgilOLvvhn1eMmK3TW8/T217O7u1jdrK6ImKLv80tVryaPSVeKu6sjDEiFjd4/eg==" } } }, @@ -1117,9 +1117,9 @@ "integrity": "sha512-EzT4CP6d6lI8bnknNgT3W8mUQhSVXflO0yPbKD4dKsFcINiC6npjoEBz+8m3VQmWJhc+36pXD4JLwNxUEgzi+Q==" }, "ig-types": { - "version": "2.0.16", - "resolved": "https://registry.npmjs.org/ig-types/-/ig-types-2.0.16.tgz", - "integrity": "sha512-p1qm26MkphItN1lj9CbcE/oKeLA55xpsqt17r8RVorWDbQBTtl1vtNdiAtBcVrCj+1CbJhZN74/ODi/pJR7kIQ==", + "version": "2.0.21", + "resolved": "https://registry.npmjs.org/ig-types/-/ig-types-2.0.21.tgz", + "integrity": "sha512-s+Hu9MU50iohoS/5SUwuoS+P2EHk7Z2zKx9wX3syiBsaL9HYq8g/0Yp/4yz9JkME1zpbS8r9aviR0O2rjBXwHQ==", "requires": { "ig-object": "^5.2.8", "object-run": "^1.0.1" @@ -2361,9 +2361,9 @@ "dev": true }, "v8-compile-cache": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz", - "integrity": "sha512-8OQ9CL+VWyt3JStj7HX7/ciTL2V3Rl1Wf5OL+SNTm0yK1KvtReVulksyeRnCANHHuUxHlQig+JJDlUhBt1NQDQ==" + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.2.0.tgz", + "integrity": "sha512-gTpR5XQNKFwOd4clxfnhaqvfqMpqEwr4tOtCyz4MtYZX2JYhfr1JvBFKdS+7K/9rfpZR3VLX+YWBbKoxCgS43Q==" }, "verror": { "version": "1.10.0", diff --git a/Viewer/package.json b/Viewer/package.json index 259965ee..5c4dea3f 100755 --- a/Viewer/package.json +++ b/Viewer/package.json @@ -20,7 +20,7 @@ "dependencies": { "app-module-path": "^1.0.6", "async-json": "0.0.2", - "electron": "^9.3.2", + "electron": "^9.3.3", "exif-reader": "^1.0.3", "exiftool": "^0.0.3", "fs-extra": "^7.0.1", @@ -32,14 +32,14 @@ "ig-argv": "^2.15.0", "ig-features": "^3.4.2", "ig-object": "^5.2.8", - "ig-types": "^2.0.16", + "ig-types": "^2.0.21", "moment": "^2.29.1", "object-run": "^1.0.1", "requirejs": "^2.3.6", "requirejs-plugins": "^1.0.2", "sharp": "^0.25.4", "strip-json-comments": "^2.0.1", - "v8-compile-cache": "^2.1.1", + "v8-compile-cache": "^2.2.0", "wildglob": "^0.1.1" }, "dependencies-disabled": {