const fs = require('fs') const util = require('util') const fastExif = require('fast-exif') const Fraction = require('fraction.js') const getCoordinates = require('dms2dec') const iptc = require('node-iptc') const readFile = util.promisify(fs.readFile) exports.createExif = async (node, actions, createNodeId) => { try { // exif const exifData = await fastExif.read(node.absolutePath, true) if (!exifData) return // iptc const file = await readFile(node.absolutePath) const iptcData = iptc(file) createNodes(exifData, iptcData, node, actions, createNodeId) } catch (error) { console.error(`${node.name}: ${error.message}`) } } function createNodes(exifData, iptcData, node, actions, createNodeId) { const { createNodeField, createNode, createParentChildLink } = actions const exifDataFormatted = formatExif(exifData) const exif = { ...exifData, iptc: { ...iptcData }, formatted: { ...exifDataFormatted } } const exifNode = { id: createNodeId(`${node.id} >> ImageExif`), children: [], ...exif, parent: node.id, internal: { contentDigest: `${node.internal.contentDigest}`, type: 'ImageExif' } } // add exif fields to existing type file createNodeField({ node, name: 'exif', value: exif }) // create new nodes from all exif data // allowing to be queried with imageExif & AllImageExif createNode(exifNode) createParentChildLink({ parent: node, child: exifNode }) } function formatExif(exifData) { if (!exifData.exif) return const { Model } = exifData.image const { ISO, FNumber, ExposureTime, FocalLength, FocalLengthIn35mmFormat, ExposureBiasValue, ExposureMode, LensModel } = exifData.exif const iso = `ISO ${ISO}` const fstop = `ƒ/${FNumber}` const focalLength = `${FocalLengthIn35mmFormat || FocalLength}mm` // Shutter speed const { n, d } = new Fraction(ExposureTime) const shutterspeed = `${n}/${d}s` // GPS let latitude let longitude if (exifData.gps) ({ latitude, longitude } = formatGps(exifData.gps)) // Exposure const exposure = formatExposure(ExposureBiasValue || ExposureMode) return { iso, model: Model, fstop, shutterspeed, focalLength, lensModel: LensModel, exposure, gps: { latitude, longitude } } } function formatGps(gpsData) { if (!gpsData) return const { GPSLatitudeRef, GPSLatitude, GPSLongitudeRef, GPSLongitude } = gpsData const GPSdec = getCoordinates( GPSLatitude, GPSLatitudeRef, GPSLongitude, GPSLongitudeRef ) const latitude = GPSdec[0] const longitude = GPSdec[1] return { latitude, longitude } } function formatExposure(exposureMode) { if (exposureMode === null || exposureMode === undefined) return const exposureShortened = parseFloat(exposureMode.toFixed(2)) let exposure if (exposureMode === 0) { exposure = `+/- ${exposureShortened} ev` } else if (exposureMode > 0) { exposure = `+ ${exposureShortened} ev` } else { exposure = `${exposureShortened} ev` } return exposure }