mirror of
https://github.com/kremalicious/blog.git
synced 2024-11-15 09:35:21 +01:00
Merge pull request #182 from kremalicious/feature/exif
Refactor EXIF & IPTC extraction
This commit is contained in:
commit
545bb59c7a
@ -45,7 +45,7 @@ The whole [blog](https://kremalicious.com) is a React-based Single Page App buil
|
|||||||
|
|
||||||
### 🎆 EXIF extraction
|
### 🎆 EXIF extraction
|
||||||
|
|
||||||
Automatically extracts EXIF metadata from my photos on build time. For minimal overhead, [fast-exif](https://github.com/titarenko/fast-exif) parses every JPG file upon Gatsby file node creation and adds the extracted EXIF data as node fields.
|
Automatically extracts EXIF & IPTC metadata from my photos on build time. For minimal overhead, [fast-exif](https://github.com/titarenko/fast-exif) & [node-iptc](https://github.com/derekbaron/node-iptc) parse every JPG file upon Gatsby file node creation and add the extracted data as node fields.
|
||||||
|
|
||||||
This way, EXIF data is only extracted at build time and can be simply queried with GraphQL at run time.
|
This way, EXIF data is only extracted at build time and can be simply queried with GraphQL at run time.
|
||||||
|
|
||||||
@ -56,7 +56,7 @@ In the end looks like this, including location display with [pigeon-maps](https:
|
|||||||
If you want to know how this works, have a look at the respective component under
|
If you want to know how this works, have a look at the respective component under
|
||||||
|
|
||||||
- [`src/components/atoms/Exif.jsx`](src/components/atoms/Exif.jsx)
|
- [`src/components/atoms/Exif.jsx`](src/components/atoms/Exif.jsx)
|
||||||
- the EXIF node fields creation [`gatsby/createExifFields.js`](gatsby/createExifFields.js) running in [`gatsby-node.js`](gatsby-node.js)
|
- the EXIF node fields creation [`gatsby/createExif.js`](gatsby/createExif.js) running in [`gatsby-node.js`](gatsby-node.js)
|
||||||
|
|
||||||
### 💰 Cryptocurrency donation via Web3/MetaMask
|
### 💰 Cryptocurrency donation via Web3/MetaMask
|
||||||
|
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 100 KiB After Width: | Height: | Size: 24 KiB |
BIN
content/photos/2019-11-02-orszaghaz-i.jpg
Normal file
BIN
content/photos/2019-11-02-orszaghaz-i.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.8 MiB |
9
content/photos/2019-11-02-orszaghaz-i.md
Normal file
9
content/photos/2019-11-02-orszaghaz-i.md
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
---
|
||||||
|
type: photo
|
||||||
|
date: 2019-11-02T13:18:59.000Z
|
||||||
|
|
||||||
|
title: Országház I
|
||||||
|
image: 2019-11-02-orszaghaz-i.jpg
|
||||||
|
---
|
||||||
|
|
||||||
|
The Hungarian Parliament Building seen from across the Danube in Budapest, Hungary.
|
BIN
content/photos/2019-11-03-orszaghaz-ii.jpg
Normal file
BIN
content/photos/2019-11-03-orszaghaz-ii.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.6 MiB |
9
content/photos/2019-11-03-orszaghaz-ii.md
Normal file
9
content/photos/2019-11-03-orszaghaz-ii.md
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
---
|
||||||
|
type: photo
|
||||||
|
date: 2019-11-03T12:05:22.000Z
|
||||||
|
|
||||||
|
title: Országház II
|
||||||
|
image: 2019-11-03-orszaghaz-ii.jpg
|
||||||
|
---
|
||||||
|
|
||||||
|
Inside the Hungarian Parliament Building in Budapest, Hungary.
|
@ -1,6 +1,6 @@
|
|||||||
const webpack = require('webpack')
|
const webpack = require('webpack')
|
||||||
const { createMarkdownFields } = require('./gatsby/createMarkdownFields')
|
const { createMarkdownFields } = require('./gatsby/createMarkdownFields')
|
||||||
const { createExifFields } = require('./gatsby/createExifFields')
|
const { createExif } = require('./gatsby/createExif')
|
||||||
const {
|
const {
|
||||||
generatePostPages,
|
generatePostPages,
|
||||||
generateTagPages,
|
generateTagPages,
|
||||||
@ -9,17 +9,15 @@ const {
|
|||||||
const { generateJsonFeed } = require('./gatsby/feeds')
|
const { generateJsonFeed } = require('./gatsby/feeds')
|
||||||
const { itemsPerPage } = require('./config')
|
const { itemsPerPage } = require('./config')
|
||||||
|
|
||||||
exports.onCreateNode = ({ node, actions, getNode }) => {
|
exports.onCreateNode = ({ node, actions, getNode, createNodeId }) => {
|
||||||
const { createNodeField } = actions
|
|
||||||
|
|
||||||
// Markdown files
|
// Markdown files
|
||||||
if (node.internal.type === 'MarkdownRemark') {
|
if (node.internal.type === 'MarkdownRemark') {
|
||||||
createMarkdownFields(node, createNodeField, getNode)
|
createMarkdownFields(node, actions, getNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Image files
|
// Image files
|
||||||
if (node.internal.mediaType === 'image/jpeg') {
|
if (node.internal.mediaType === 'image/jpeg') {
|
||||||
createExifFields(node, createNodeField)
|
createExif(node, actions, createNodeId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
143
gatsby/createExif.js
Normal file
143
gatsby/createExif.js
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
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) 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
|
||||||
|
}
|
@ -1,94 +0,0 @@
|
|||||||
const fastExif = require('fast-exif')
|
|
||||||
const Fraction = require('fraction.js')
|
|
||||||
const dms2dec = require('dms2dec')
|
|
||||||
|
|
||||||
exports.createExifFields = async (node, createNodeField) => {
|
|
||||||
let exifData
|
|
||||||
try {
|
|
||||||
exifData = await fastExif.read(node.absolutePath, true)
|
|
||||||
if (!exifData) return
|
|
||||||
constructExifFields(exifData, createNodeField, node)
|
|
||||||
} catch (error) {
|
|
||||||
// console.error(`${node.name}: ${error.message}`)
|
|
||||||
return null // just silently fail when exif can't be extracted
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const getGps = gpsData => {
|
|
||||||
if (!gpsData) return
|
|
||||||
|
|
||||||
const { GPSLatitudeRef, GPSLatitude, GPSLongitudeRef, GPSLongitude } = gpsData
|
|
||||||
|
|
||||||
const GPSdec = dms2dec(
|
|
||||||
GPSLatitude,
|
|
||||||
GPSLatitudeRef,
|
|
||||||
GPSLongitude,
|
|
||||||
GPSLongitudeRef
|
|
||||||
)
|
|
||||||
|
|
||||||
const latitude = GPSdec[0]
|
|
||||||
const longitude = GPSdec[1]
|
|
||||||
|
|
||||||
return { latitude, longitude }
|
|
||||||
}
|
|
||||||
|
|
||||||
const getExposure = exposureMode => {
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
const constructExifFields = (exifData, createNodeField, node) => {
|
|
||||||
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 } = getGps(exifData.gps))
|
|
||||||
|
|
||||||
// Exposure
|
|
||||||
const exposure = getExposure(ExposureBiasValue || ExposureMode)
|
|
||||||
|
|
||||||
// add exif fields to type File
|
|
||||||
createNodeField({
|
|
||||||
node,
|
|
||||||
name: 'exif',
|
|
||||||
value: {
|
|
||||||
iso,
|
|
||||||
model: Model,
|
|
||||||
fstop,
|
|
||||||
shutterspeed,
|
|
||||||
focalLength,
|
|
||||||
lensModel: LensModel,
|
|
||||||
exposure,
|
|
||||||
gps: { latitude, longitude }
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
@ -2,6 +2,28 @@ const path = require('path')
|
|||||||
const { createFilePath } = require('gatsby-source-filesystem')
|
const { createFilePath } = require('gatsby-source-filesystem')
|
||||||
const { repoContentPath } = require('../config')
|
const { repoContentPath } = require('../config')
|
||||||
|
|
||||||
|
// Create slug, date & github file link for posts from file path values
|
||||||
|
exports.createMarkdownFields = (node, actions, getNode) => {
|
||||||
|
const { createNodeField } = actions
|
||||||
|
const fileNode = getNode(node.parent)
|
||||||
|
const parsedFilePath = path.parse(fileNode.relativePath)
|
||||||
|
const slugOriginal = createFilePath({ node, getNode })
|
||||||
|
|
||||||
|
createSlug(node, createNodeField, slugOriginal, parsedFilePath)
|
||||||
|
createDate(node, createNodeField, slugOriginal)
|
||||||
|
|
||||||
|
// github file link
|
||||||
|
const type = fileNode.sourceInstanceName
|
||||||
|
const file = fileNode.relativePath
|
||||||
|
const githubLink = `${repoContentPath}/${type}/${file}`
|
||||||
|
|
||||||
|
createNodeField({
|
||||||
|
node,
|
||||||
|
name: 'githubLink',
|
||||||
|
value: githubLink
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
function createSlug(node, createNodeField, slugOriginal, parsedFilePath) {
|
function createSlug(node, createNodeField, slugOriginal, parsedFilePath) {
|
||||||
let slug
|
let slug
|
||||||
|
|
||||||
@ -32,24 +54,3 @@ function createDate(node, createNodeField, slugOriginal) {
|
|||||||
value: date
|
value: date
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create slug, date & github file link for posts from file path values
|
|
||||||
exports.createMarkdownFields = (node, createNodeField, getNode) => {
|
|
||||||
const fileNode = getNode(node.parent)
|
|
||||||
const parsedFilePath = path.parse(fileNode.relativePath)
|
|
||||||
const slugOriginal = createFilePath({ node, getNode })
|
|
||||||
|
|
||||||
createSlug(node, createNodeField, slugOriginal, parsedFilePath)
|
|
||||||
createDate(node, createNodeField, slugOriginal)
|
|
||||||
|
|
||||||
// github file link
|
|
||||||
const type = fileNode.sourceInstanceName
|
|
||||||
const file = fileNode.relativePath
|
|
||||||
const githubLink = `${repoContentPath}/${type}/${file}`
|
|
||||||
|
|
||||||
createNodeField({
|
|
||||||
node,
|
|
||||||
name: 'githubLink',
|
|
||||||
value: githubLink
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
27
package.json
27
package.json
@ -35,18 +35,18 @@
|
|||||||
"dms2dec": "^1.1.0",
|
"dms2dec": "^1.1.0",
|
||||||
"fast-exif": "^1.0.1",
|
"fast-exif": "^1.0.1",
|
||||||
"fraction.js": "^4.0.12",
|
"fraction.js": "^4.0.12",
|
||||||
"gatsby": "^2.17.6",
|
"gatsby": "^2.17.7",
|
||||||
"gatsby-image": "^2.2.30",
|
"gatsby-image": "^2.2.30",
|
||||||
"gatsby-plugin-catch-links": "^2.1.15",
|
"gatsby-plugin-catch-links": "^2.1.15",
|
||||||
"gatsby-plugin-feed": "^2.3.19",
|
"gatsby-plugin-feed": "^2.3.19",
|
||||||
"gatsby-plugin-lunr": "^1.5.2",
|
"gatsby-plugin-lunr": "^1.5.2",
|
||||||
"gatsby-plugin-manifest": "^2.2.25",
|
"gatsby-plugin-manifest": "^2.2.26",
|
||||||
"gatsby-plugin-matomo": "^0.7.2",
|
"gatsby-plugin-matomo": "^0.7.2",
|
||||||
"gatsby-plugin-meta-redirect": "^1.1.1",
|
"gatsby-plugin-meta-redirect": "^1.1.1",
|
||||||
"gatsby-plugin-offline": "^3.0.17",
|
"gatsby-plugin-offline": "^3.0.17",
|
||||||
"gatsby-plugin-react-helmet": "^3.1.13",
|
"gatsby-plugin-react-helmet": "^3.1.13",
|
||||||
"gatsby-plugin-sass": "^2.1.20",
|
"gatsby-plugin-sass": "^2.1.20",
|
||||||
"gatsby-plugin-sharp": "^2.2.34",
|
"gatsby-plugin-sharp": "^2.2.36",
|
||||||
"gatsby-plugin-sitemap": "^2.2.19",
|
"gatsby-plugin-sitemap": "^2.2.19",
|
||||||
"gatsby-plugin-svgr": "^2.0.2",
|
"gatsby-plugin-svgr": "^2.0.2",
|
||||||
"gatsby-plugin-typescript": "^2.1.15",
|
"gatsby-plugin-typescript": "^2.1.15",
|
||||||
@ -57,11 +57,11 @@
|
|||||||
"gatsby-remark-copy-linked-files": "^2.1.28",
|
"gatsby-remark-copy-linked-files": "^2.1.28",
|
||||||
"gatsby-remark-images": "^3.1.28",
|
"gatsby-remark-images": "^3.1.28",
|
||||||
"gatsby-remark-smartypants": "^2.1.14",
|
"gatsby-remark-smartypants": "^2.1.14",
|
||||||
"gatsby-remark-vscode": "^1.2.0",
|
"gatsby-remark-vscode": "^1.3.0",
|
||||||
"gatsby-source-filesystem": "^2.1.35",
|
"gatsby-source-filesystem": "^2.1.35",
|
||||||
"gatsby-source-graphql": "^2.1.21",
|
"gatsby-source-graphql": "^2.1.21",
|
||||||
"gatsby-transformer-remark": "^2.6.32",
|
"gatsby-transformer-remark": "^2.6.32",
|
||||||
"gatsby-transformer-sharp": "^2.3.1",
|
"gatsby-transformer-sharp": "^2.3.2",
|
||||||
"graphql": "^14.5.8",
|
"graphql": "^14.5.8",
|
||||||
"intersection-observer": "^0.7.0",
|
"intersection-observer": "^0.7.0",
|
||||||
"js-scrypt": "^0.2.0",
|
"js-scrypt": "^0.2.0",
|
||||||
@ -79,7 +79,7 @@
|
|||||||
"react-transition-group": "^4.3.0",
|
"react-transition-group": "^4.3.0",
|
||||||
"remark": "^11.0.1",
|
"remark": "^11.0.1",
|
||||||
"remark-react": "^6.0.0",
|
"remark-react": "^6.0.0",
|
||||||
"slugify": "^1.3.4",
|
"slugify": "^1.3.6",
|
||||||
"use-dark-mode": "^2.3.1",
|
"use-dark-mode": "^2.3.1",
|
||||||
"web3": "^1.2.2"
|
"web3": "^1.2.2"
|
||||||
},
|
},
|
||||||
@ -88,21 +88,20 @@
|
|||||||
"@babel/preset-env": "^7.6.3",
|
"@babel/preset-env": "^7.6.3",
|
||||||
"@babel/preset-typescript": "^7.6.0",
|
"@babel/preset-typescript": "^7.6.0",
|
||||||
"@svgr/webpack": "^4.3.3",
|
"@svgr/webpack": "^4.3.3",
|
||||||
"@testing-library/jest-dom": "^4.2.0",
|
"@testing-library/jest-dom": "^4.2.3",
|
||||||
"@testing-library/react": "^9.3.0",
|
"@testing-library/react": "^9.3.2",
|
||||||
"@types/classnames": "^2.2.9",
|
"@types/classnames": "^2.2.9",
|
||||||
"@types/jest": "^24.0.20",
|
"@types/jest": "^24.0.21",
|
||||||
"@types/lunr": "^2.3.2",
|
"@types/lunr": "^2.3.2",
|
||||||
"@types/node": "^12.11.7",
|
"@types/node": "^12.12.5",
|
||||||
"@types/react": "^16.9.11",
|
"@types/react": "^16.9.11",
|
||||||
"@types/react-dom": "^16.9.3",
|
"@types/react-dom": "^16.9.3",
|
||||||
"@types/react-helmet": "^5.0.13",
|
"@types/react-helmet": "^5.0.14",
|
||||||
"@types/react-modal": "^3.10.0",
|
"@types/react-modal": "^3.10.0",
|
||||||
"@types/react-transition-group": "^4.2.3",
|
"@types/react-transition-group": "^4.2.3",
|
||||||
"@types/shortid": "0.0.29",
|
"@types/shortid": "0.0.29",
|
||||||
"@types/web3": "^1.0.20",
|
"@typescript-eslint/eslint-plugin": "^2.6.1",
|
||||||
"@typescript-eslint/eslint-plugin": "^2.6.0",
|
"@typescript-eslint/parser": "^2.6.1",
|
||||||
"@typescript-eslint/parser": "^2.6.0",
|
|
||||||
"babel-eslint": "^10.0.3",
|
"babel-eslint": "^10.0.3",
|
||||||
"babel-jest": "^24.9.0",
|
"babel-jest": "^24.9.0",
|
||||||
"eslint": "^6.6.0",
|
"eslint": "^6.6.0",
|
||||||
|
11
src/@types/Image.d.ts
vendored
11
src/@types/Image.d.ts
vendored
@ -15,7 +15,7 @@ export interface ImageNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Exif {
|
export interface ExifFormatted {
|
||||||
iso: string
|
iso: string
|
||||||
model: string
|
model: string
|
||||||
fstop: string
|
fstop: string
|
||||||
@ -28,3 +28,12 @@ export interface Exif {
|
|||||||
longitude: string
|
longitude: string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface Exif {
|
||||||
|
formatted?: ExifFormatted
|
||||||
|
exif?: any
|
||||||
|
image?: any
|
||||||
|
thumbnail?: any
|
||||||
|
gps?: any
|
||||||
|
iptc?: any
|
||||||
|
}
|
||||||
|
@ -4,6 +4,7 @@ import { render } from '@testing-library/react'
|
|||||||
import Exif from './Exif'
|
import Exif from './Exif'
|
||||||
|
|
||||||
const exif = {
|
const exif = {
|
||||||
|
formatted: {
|
||||||
iso: '500',
|
iso: '500',
|
||||||
model: 'Canon',
|
model: 'Canon',
|
||||||
fstop: '7.2',
|
fstop: '7.2',
|
||||||
@ -12,6 +13,7 @@ const exif = {
|
|||||||
lensModel: 'Hello',
|
lensModel: 'Hello',
|
||||||
exposure: '200',
|
exposure: '200',
|
||||||
gps: { latitude: '41.89007222222222', longitude: '12.491516666666666' }
|
gps: { latitude: '41.89007222222222', longitude: '12.491516666666666' }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('Exif', () => {
|
describe('Exif', () => {
|
||||||
|
@ -4,7 +4,15 @@ import styles from './Exif.module.scss'
|
|||||||
import { Exif as ExifMeta } from '../../@types/Image'
|
import { Exif as ExifMeta } from '../../@types/Image'
|
||||||
|
|
||||||
export default function Exif({ exif }: { exif: ExifMeta }) {
|
export default function Exif({ exif }: { exif: ExifMeta }) {
|
||||||
const { iso, model, fstop, shutterspeed, focalLength, exposure, gps } = exif
|
const {
|
||||||
|
iso,
|
||||||
|
model,
|
||||||
|
fstop,
|
||||||
|
shutterspeed,
|
||||||
|
focalLength,
|
||||||
|
exposure,
|
||||||
|
gps
|
||||||
|
} = exif.formatted
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<aside className={styles.exif}>
|
<aside className={styles.exif}>
|
||||||
@ -16,7 +24,7 @@ export default function Exif({ exif }: { exif: ExifMeta }) {
|
|||||||
{exposure && <span title="Exposure">{exposure}</span>}
|
{exposure && <span title="Exposure">{exposure}</span>}
|
||||||
{iso && <span title="ISO">{iso}</span>}
|
{iso && <span title="ISO">{iso}</span>}
|
||||||
</div>
|
</div>
|
||||||
{gps.latitude && (
|
{gps && gps.latitude && (
|
||||||
<div className={styles.map}>
|
<div className={styles.map}>
|
||||||
<ExifMap gps={gps} />
|
<ExifMap gps={gps} />
|
||||||
</div>
|
</div>
|
||||||
|
@ -79,6 +79,7 @@ export const pageQuery = graphql`
|
|||||||
}
|
}
|
||||||
fields {
|
fields {
|
||||||
exif {
|
exif {
|
||||||
|
formatted {
|
||||||
iso
|
iso
|
||||||
model
|
model
|
||||||
fstop
|
fstop
|
||||||
@ -93,6 +94,7 @@ export const pageQuery = graphql`
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
author
|
author
|
||||||
updated
|
updated
|
||||||
tags
|
tags
|
||||||
|
Loading…
Reference in New Issue
Block a user