Personal-Website/gatsby-node.js
2021-07-12 18:54:08 -07:00

196 lines
5.5 KiB
JavaScript

const fs = require("fs");
const util = require("util");
const path = require("path");
const { read } = require("fast-exif");
const iptc = require("node-iptc");
const Vibrant = require("node-vibrant");
const chroma = require("chroma-js");
const chalk = require("chalk");
const R = require("ramda");
const readFile = util.promisify(fs.readFile);
const badContrast = (color1, color2) => chroma.contrast(color1, color2) < 4.5;
const logColorsWithContrast = (color1, color2, text) => {
const c1hex = color1.hex();
const c2hex = color2.hex();
console.log(
chalk.hex(c1hex).bgHex(c2hex)(
`${text} ${c1hex}/${c2hex} ${chroma.contrast(color1, color2)}`
)
);
};
function processColors(vibrantData, imagePath) {
let Vibrant = chroma(vibrantData.Vibrant.getRgb());
let DarkVibrant = chroma(vibrantData.DarkVibrant.getRgb());
let LightVibrant = chroma(vibrantData.LightVibrant.getRgb());
let Muted = chroma(vibrantData.Muted.getRgb());
let DarkMuted = chroma(vibrantData.DarkMuted.getRgb());
let LightMuted = chroma(vibrantData.LightMuted.getRgb());
// first pass - darken bg and lighten relevant fg colors
if (
badContrast(DarkVibrant, Vibrant) ||
badContrast(DarkVibrant, LightMuted)
) {
DarkVibrant = DarkVibrant.darken();
if (badContrast(DarkVibrant, Vibrant)) {
Vibrant = Vibrant.brighten();
}
if (badContrast(DarkVibrant, Vibrant)) {
Vibrant = Vibrant.brighten();
}
}
// second pass - first doesn't always do enough
if (
badContrast(DarkVibrant, Vibrant) ||
badContrast(DarkVibrant, LightMuted)
) {
// DarkVibrant = DarkVibrant.darken();
if (badContrast(DarkVibrant, Vibrant)) {
Vibrant = Vibrant.brighten(2);
}
if (badContrast(DarkVibrant, LightMuted)) {
LightMuted = LightMuted.brighten(2);
}
}
if (badContrast(DarkVibrant, Vibrant)) {
console.warn("contrast still too low", imagePath);
logColorsWithContrast(Vibrant, DarkVibrant, "V-DV");
}
if (badContrast(DarkVibrant, LightMuted)) {
console.warn("contrast still too low", imagePath);
logColorsWithContrast(LightMuted, DarkVibrant, "LM-DV");
}
return {
Vibrant: Vibrant.rgb(),
DarkVibrant: DarkVibrant.rgb(),
LightVibrant: LightVibrant.rgb(),
Muted: Muted.rgb(),
DarkMuted: DarkMuted.rgb(),
LightMuted: LightMuted.rgb(),
};
}
function convertDMSToDD(dms, positiveDirection) {
const res = dms
.map((item, i) => {
return item / Math.pow(60, i);
})
.reduce((a, b) => a + b);
return positiveDirection ? res : -res;
}
function transformMetaToNodeData(exifData, iptcData, vibrantData, imagePath) {
const gps = { longitude: null, latitude: null };
if (exifData) {
if (exifData.gps && exifData.gps.GPSLongitude && exifData.gps.GPSLatitude) {
gps.longitude = convertDMSToDD(
exifData.gps.GPSLongitude,
exifData.gps.GPSLongitudeRef === "E"
);
gps.latitude = convertDMSToDD(
exifData.gps.GPSLatitude,
exifData.gps.GPSLatitudeRef === "N"
);
}
}
const vibrant = vibrantData ? processColors(vibrantData, imagePath) : null;
return {
exif: exifData?.exif,
gps,
dateTaken: exifData?.exif?.DateTimeOriginal,
iptc: iptcData || undefined,
vibrant,
};
}
exports.onCreateNode = async function ({ node, actions }) {
const { createNodeField } = actions;
if (node.internal.type === "File" && node.sourceInstanceName === "gallery") {
const file = await readFile(node.absolutePath);
const iptcData = iptc(file);
const exifData = await read(node.absolutePath);
const vibrantData = await Vibrant.from(node.absolutePath)
.quality(3)
.getPalette();
createNodeField({
node,
name: "imageMeta",
value: transformMetaToNodeData(
exifData,
iptcData,
vibrantData,
node.absolutePath
),
});
}
};
// Implement the Gatsby API “createPages”. This is called once the
// data layer is bootstrapped to let plugins create pages from data.
exports.createPages = async ({ graphql, actions, reporter }) => {
const { createPage } = actions;
// get all images
const galleryImages = await graphql(`
{
allFile(filter: { sourceInstanceName: { eq: "gallery" } }) {
edges {
node {
relativePath
base
fields {
imageMeta {
dateTaken
}
}
}
}
}
}
`);
// Handle errors
if (galleryImages.errors) {
reporter.panicOnBuild("Error while running GraphQL query.");
return;
}
// Create pages for each markdown file.
const galleryImageTemplate = path.resolve(
"src/components/GalleryImage/GalleryImage.js"
);
// const diffDate = (a, b) =>
// new Date(R.path(['node', 'childImageSharp', 'fields', 'imageMeta', 'dateTaken'], a)).getTime() - new Date(R.path(['node', 'childImageSharp', 'fields', 'imageMeta', 'dateTaken'],b)).getTime();
const edges = R.sort(
R.descend(
(edge) =>
new Date(R.path(["node", "fields", "imageMeta", "dateTaken"], edge))
),
galleryImages.data.allFile.edges
);
edges.forEach(({ node }, index) => {
const nextImage =
index === edges.length - 1 ? null : edges[index + 1].node.base;
const prevImage = index === 0 ? null : edges[index - 1].node.base;
const page = {
path: `photogallery/${node.base}`,
component: galleryImageTemplate,
context: {
imageFilename: node.base,
nextImage,
prevImage,
},
};
createPage(page);
});
};