Swap photo metadata parsers

This commit is contained in:
Chuck Dries 2021-11-07 08:21:06 -08:00
parent 40ad616098
commit 846db750fc
6 changed files with 64 additions and 70 deletions

View File

@ -1,14 +1,9 @@
const fs = require("fs");
const util = require("util");
const path = require("path"); const path = require("path");
const { read } = require("fast-exif");
const iptc = require("node-iptc");
const Vibrant = require("node-vibrant"); const Vibrant = require("node-vibrant");
const chroma = require("chroma-js"); const chroma = require("chroma-js");
const chalk = require("chalk"); const chalk = require("chalk");
const R = require("ramda"); const R = require("ramda");
const exifr = require("exifr");
const readFile = util.promisify(fs.readFile);
const badContrast = (color1, color2) => chroma.contrast(color1, color2) < 4.5; const badContrast = (color1, color2) => chroma.contrast(color1, color2) < 4.5;
@ -89,32 +84,27 @@ function convertDMSToDD(dms, positiveDirection) {
return positiveDirection ? res : -res; return positiveDirection ? res : -res;
} }
function transformMetaToNodeData(exifData, iptcData, vibrantData, imagePath) { function transformMetaToNodeData(metaData, vibrantData, imagePath) {
const gps = { longitude: null, latitude: null }; // const gps = { longitude: null, latitude: null };
if (exifData) { // if (exifData) {
if (exifData.gps && exifData.gps.GPSLongitude && exifData.gps.GPSLatitude) { // if (exifData.gps && exifData.gps.GPSLongitude && exifData.gps.GPSLatitude) {
gps.longitude = convertDMSToDD( // gps.longitude = convertDMSToDD(
exifData.gps.GPSLongitude, // exifData.gps.GPSLongitude,
exifData.gps.GPSLongitudeRef === "E" // exifData.gps.GPSLongitudeRef === "E"
); // );
gps.latitude = convertDMSToDD( // gps.latitude = convertDMSToDD(
exifData.gps.GPSLatitude, // exifData.gps.GPSLatitude,
exifData.gps.GPSLatitudeRef === "N" // exifData.gps.GPSLatitudeRef === "N"
); // );
} // }
} // }
const vibrant = vibrantData ? processColors(vibrantData, imagePath) : null; const vibrant = vibrantData ? processColors(vibrantData, imagePath) : null;
return { return {
exif: { dateTaken: metaData.DateTimeOriginal,
...exifData?.exif, meta: metaData,
...exifData?.image
},
gps,
dateTaken: exifData?.exif?.DateTimeOriginal,
iptc: iptcData || undefined,
vibrant, vibrant,
}; };
} }
@ -122,9 +112,11 @@ function transformMetaToNodeData(exifData, iptcData, vibrantData, imagePath) {
exports.onCreateNode = async function ({ node, actions }) { exports.onCreateNode = async function ({ node, actions }) {
const { createNodeField } = actions; const { createNodeField } = actions;
if (node.internal.type === "File" && node.sourceInstanceName === "gallery") { if (node.internal.type === "File" && node.sourceInstanceName === "gallery") {
const file = await readFile(node.absolutePath); const metaData = await exifr.parse(node.absolutePath, {
const iptcData = iptc(file); iptc: true,
const exifData = await read(node.absolutePath); xmp: true,
// icc: true
});
const vibrantData = await Vibrant.from(node.absolutePath) const vibrantData = await Vibrant.from(node.absolutePath)
.quality(3) .quality(3)
.getPalette(); .getPalette();
@ -132,12 +124,7 @@ exports.onCreateNode = async function ({ node, actions }) {
createNodeField({ createNodeField({
node, node,
name: "imageMeta", name: "imageMeta",
value: transformMetaToNodeData( value: transformMetaToNodeData(metaData, vibrantData, node.absolutePath),
exifData,
iptcData,
vibrantData,
node.absolutePath
),
}); });
} }
}; };

View File

@ -34,6 +34,7 @@
"eslint-plugin-react": "^7.24.0", "eslint-plugin-react": "^7.24.0",
"eslint-plugin-react-hooks": "^4.2.0", "eslint-plugin-react-hooks": "^4.2.0",
"eslint-webpack-plugin": "^2.5.4", "eslint-webpack-plugin": "^2.5.4",
"exifr": "^7.1.3",
"fast-exif": "^1.0.1", "fast-exif": "^1.0.1",
"gatsby": "^3.4.1", "gatsby": "^3.4.1",
"gatsby-plugin-eslint": "^3.0.0", "gatsby-plugin-eslint": "^3.0.0",

View File

@ -60,10 +60,11 @@ const GalleryImage = ({ data, pageContext }) => {
}, [pageContext]); }, [pageContext]);
const name = getName(image); const name = getName(image);
const meta = getMeta(image); const {meta, dateTaken: dt} = getMeta(image);
// const locationString = meta.City;
let locationString; let locationString;
if (meta.iptc.city || meta.iptc.province_or_state) { if (meta.City || meta.State || meta.Location) {
const location = [meta.iptc.city, meta.iptc.province_or_state].filter( const location = [meta.Location, meta.City, meta.State].filter(
Boolean Boolean
); );
locationString = location.join(", "); locationString = location.join(", ");
@ -75,10 +76,10 @@ const GalleryImage = ({ data, pageContext }) => {
? "flex-col mx-auto" ? "flex-col mx-auto"
: "portrait:mx-auto landscape:mx-5 landscape:flex-row-reverse portrait:flex-col"; : "portrait:mx-auto landscape:mx-5 landscape:flex-row-reverse portrait:flex-col";
const shutterSpeed = React.useMemo( const shutterSpeed = React.useMemo(
() => getShutterFractionFromExposureTime(meta.exif.ExposureTime || 0), () => getShutterFractionFromExposureTime(meta.ExposureTime || 0),
[meta] [meta]
); );
const dateTaken = React.useMemo(() => new Date(meta.dateTaken), [meta]); const dateTaken = React.useMemo(() => new Date(dt), [dt]);
return ( return (
<> <>
<Helmet> <Helmet>
@ -144,7 +145,7 @@ const GalleryImage = ({ data, pageContext }) => {
</div> </div>
<div <div
className={classnames( className={classnames(
"mx-2 flex flex-row portrait:items-end", "px-2 flex flex-row portrait:items-end container mx-auto",
ar <= 1 ar <= 1
? "pt-5 flex-col flex-auto text-right" ? "pt-5 flex-col flex-auto text-right"
: "portrait:pt-5 portrait:flex-col portrait:text-right" : "portrait:pt-5 portrait:flex-col portrait:text-right"
@ -157,7 +158,7 @@ const GalleryImage = ({ data, pageContext }) => {
{hasName(image) && ( {hasName(image) && (
<h1 className="text-4xl mt-0 font-serif">{name}</h1> <h1 className="text-4xl mt-0 font-serif">{name}</h1>
)} )}
<p className="landscape:mr-2">{meta.iptc.caption}</p> <p className="landscape:mr-2">{meta.Caption}</p>
</div> </div>
{ {
<div <div
@ -173,21 +174,21 @@ const GalleryImage = ({ data, pageContext }) => {
/> />
<MetadataItem <MetadataItem
data={locationString} data={locationString}
icon="location" icon="map-outline"
title="location" title="location"
/> />
{(meta.exif.Make || meta.exif.Model) && ( {(meta.Make || meta.Model) && (
<MetadataItem <MetadataItem
data={[meta.exif.Make, meta.exif.Model].join(" ")} data={[meta.Make, meta.Model].join(" ")}
icon="camera-outline" icon="camera-outline"
title="camera" title="camera"
/> />
)} )}
{(meta.exif.LensModel || meta.exif.FocalLength) && ( {(meta.LensModel || meta.FocalLength) && (
<MetadataItem <MetadataItem
data={[ data={[
meta.exif.LensModel === "----" ? null : meta.exif.LensModel, meta.LensModel === "----" ? null : meta.LensModel,
meta.exif.FocalLength && `${meta.exif.FocalLength}mm`, meta.FocalLength && `${meta.FocalLength}mm`,
] ]
.filter(Boolean) .filter(Boolean)
.join(" @")} .join(" @")}
@ -200,15 +201,15 @@ const GalleryImage = ({ data, pageContext }) => {
icon="stopwatch-outline" icon="stopwatch-outline"
title="shutter speed" title="shutter speed"
/> />
{meta.exif.FNumber && ( {meta.FNumber && (
<MetadataItem <MetadataItem
data={`f/${meta.exif.FNumber}`} data={`f/${meta.FNumber}`}
icon="aperture-outline" icon="aperture-outline"
title="aperture" title="aperture"
/> />
)} )}
<MetadataItem <MetadataItem
data={meta.exif.ISO} data={meta.ISO}
icon="film-outline" icon="film-outline"
title="ISO" title="ISO"
/> />
@ -247,21 +248,23 @@ export const query = graphql`
fields { fields {
imageMeta { imageMeta {
dateTaken dateTaken
iptc { meta {
caption
object_name
keywords
city
province_or_state
}
exif {
FNumber
ExposureTime
FocalLength
ISO
LensModel
Make Make
Model Model
ExposureTime
FNumber
ISO
DateTimeOriginal
CreateDate
ShutterSpeedValue
ApertureValue
FocalLength
LensModel
ObjectName
Caption
Location
City
State
} }
vibrant { vibrant {
...VibrantColors ...VibrantColors

View File

@ -81,9 +81,7 @@ export const query = graphql`
fields { fields {
imageMeta { imageMeta {
dateTaken dateTaken
iptc { ObjectName
object_name
}
} }
} }
} }

View File

@ -3,12 +3,12 @@
export const getMeta = (image) => image.fields.imageMeta; export const getMeta = (image) => image.fields.imageMeta;
export const getName = (image) => export const getName = (image) =>
getMeta(image)?.iptc.object_name || image.base; getMeta(image)?.meta?.ObjectName || image.base;
// some pleasing default colors for SSR and initial hydration // some pleasing default colors for SSR and initial hydration
export const getVibrant = (image) => getMeta(image)?.vibrant; export const getVibrant = (image) => getMeta(image)?.vibrant;
export const hasName = (image) => Boolean(getMeta(image)?.iptc.object_name); export const hasName = (image) => Boolean(getMeta(image)?.meta?.ObjectName);
export const getAspectRatio = (image) => export const getAspectRatio = (image) =>
image.childImageSharp.fluid.aspectRatio; image.childImageSharp.fluid.aspectRatio;

View File

@ -5681,6 +5681,11 @@ exif-reader@^1.0.0:
resolved "https://registry.yarnpkg.com/exif-reader/-/exif-reader-1.0.3.tgz#8eb63f878aeb49ec89bd5b7be10e393db78c3c2e" resolved "https://registry.yarnpkg.com/exif-reader/-/exif-reader-1.0.3.tgz#8eb63f878aeb49ec89bd5b7be10e393db78c3c2e"
integrity sha512-tWMBj1+9jUSibgR/kv/GQ/fkR0biaN9GEZ5iPdf7jFeH//d2bSzgPoaWf1OfMv4MXFD4upwvpCCyeMvSyLWSfA== integrity sha512-tWMBj1+9jUSibgR/kv/GQ/fkR0biaN9GEZ5iPdf7jFeH//d2bSzgPoaWf1OfMv4MXFD4upwvpCCyeMvSyLWSfA==
exifr@^7.1.3:
version "7.1.3"
resolved "https://registry.yarnpkg.com/exifr/-/exifr-7.1.3.tgz#f6218012c36dbb7d843222011b27f065fddbab6f"
integrity sha512-g/aje2noHivrRSLbAUtBPWFbxKdKhgj/xr1vATDdUXPOFYJlQ62Ft0oy+72V6XLIpDJfHs6gXLbBLAolqOXYRw==
expand-brackets@^2.1.4: expand-brackets@^2.1.4:
version "2.1.4" version "2.1.4"
resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622"