mirror of
https://github.com/kremalicious/blog.git
synced 2024-06-29 00:58:00 +02:00
new photos
This commit is contained in:
parent
f0bd16ec65
commit
974f6d860c
|
@ -220,13 +220,15 @@ npm run new "Hello World"
|
||||||
npm run new "Hello World" 2017-12-27
|
npm run new "Hello World" 2017-12-27
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Create a new photo post with date, title & description pre-filled from EXIF/IPTC data of a given image file:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm run new "Hello World" photo
|
npm run new photo /path/to/photo.jpg
|
||||||
npm run new "Hello World" photo 2017-12-27
|
|
||||||
```
|
```
|
||||||
|
|
||||||
- [`scripts/new.js`](scripts/new.js)
|
- [`scripts/new.js`](scripts/new.js)
|
||||||
- [`scripts/new.md`](scripts/new.md)
|
- [`scripts/new.md`](scripts/new.md)
|
||||||
|
- [`scripts/new-photo.md`](scripts/new-photo.md)
|
||||||
|
|
||||||
## 🚚 Deployment
|
## 🚚 Deployment
|
||||||
|
|
||||||
|
|
BIN
content/photos/2019-01-27-all-work-and-no-play.jpg
Normal file
BIN
content/photos/2019-01-27-all-work-and-no-play.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.3 MiB |
9
content/photos/2019-01-27-all-work-and-no-play.md
Normal file
9
content/photos/2019-01-27-all-work-and-no-play.md
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
---
|
||||||
|
type: photo
|
||||||
|
date: 2019-01-27T00:00:00.000Z
|
||||||
|
|
||||||
|
title: All Work And No Play
|
||||||
|
image: 2019-01-27-all-work-and-no-play.jpg
|
||||||
|
---
|
||||||
|
|
||||||
|
Inside the [“Stanley Kubrick” exhibition](https://www.cccb.org/en/exhibitions/file/stanley-kubrick/228237) in the [Centre de Cultura Contemporània de Barcelona (CCCB)](https://www.cccb.org) in Barcelona, Catalonia, Spain.
|
BIN
content/photos/2019-02-25-edificio-italia.jpg
Normal file
BIN
content/photos/2019-02-25-edificio-italia.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.4 MiB |
9
content/photos/2019-02-25-edificio-italia.md
Normal file
9
content/photos/2019-02-25-edificio-italia.md
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
---
|
||||||
|
type: photo
|
||||||
|
date: 2019-02-25T22:51:00.000Z
|
||||||
|
|
||||||
|
title: Edifício Itália
|
||||||
|
image: 2019-02-25-edificio-italia.jpg
|
||||||
|
---
|
||||||
|
|
||||||
|
View over São Paulo from the 46th floor of Edifício Itália.
|
Binary file not shown.
After Width: | Height: | Size: 2.3 MiB |
|
@ -0,0 +1,9 @@
|
||||||
|
---
|
||||||
|
type: photo
|
||||||
|
date: 2019-03-06T17:52:46.000Z
|
||||||
|
|
||||||
|
title: Brazilian Museum of Sculpture and Ecology
|
||||||
|
image: 2019-03-06-brazilian-museum-of-sculpture-and-ecology.jpg
|
||||||
|
---
|
||||||
|
|
||||||
|
On the grounds of the Brazilian Museum of Sculpture and Ecology (MuBE) in São Paulo, Brazil.
|
BIN
content/photos/2019-03-13-paraty-mirim.jpg
Normal file
BIN
content/photos/2019-03-13-paraty-mirim.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.1 MiB |
9
content/photos/2019-03-13-paraty-mirim.md
Normal file
9
content/photos/2019-03-13-paraty-mirim.md
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
---
|
||||||
|
type: photo
|
||||||
|
date: 2019-03-13T00:00:00.000Z
|
||||||
|
|
||||||
|
title: Paraty-Mirim
|
||||||
|
image: 2019-03-13-paraty-mirim.jpg
|
||||||
|
---
|
||||||
|
|
||||||
|
At the Paraty-Mirim beach in Paraty, Rio de Janeiro, Brazil.
|
BIN
content/photos/2019-09-29-arco-di-costantino.jpg
Normal file
BIN
content/photos/2019-09-29-arco-di-costantino.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.0 MiB |
9
content/photos/2019-09-29-arco-di-costantino.md
Normal file
9
content/photos/2019-09-29-arco-di-costantino.md
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
---
|
||||||
|
type: photo
|
||||||
|
date: 2019-09-29T13:57:00.000Z
|
||||||
|
|
||||||
|
title: Arco di Costantino
|
||||||
|
image: 2019-09-29-arco-di-costantino.jpg
|
||||||
|
---
|
||||||
|
|
||||||
|
The Arch of Constantine seen through the Collosseum walls in Rome, Italy.
|
BIN
content/photos/2019-09-29-foro-di-cesare.jpg
Normal file
BIN
content/photos/2019-09-29-foro-di-cesare.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.3 MiB |
9
content/photos/2019-09-29-foro-di-cesare.md
Normal file
9
content/photos/2019-09-29-foro-di-cesare.md
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
---
|
||||||
|
type: photo
|
||||||
|
date: 2019-09-29T15:31:00.000Z
|
||||||
|
|
||||||
|
title: Foro di Cesare
|
||||||
|
image: 2019-09-29-foro-di-cesare.jpg
|
||||||
|
---
|
||||||
|
|
||||||
|
View over the [Forum of Caesar](https://en.wikipedia.org/wiki/Forum_of_Caesar) within the [Imperial Forum](https://en.wikipedia.org/wiki/Imperial_fora) in Rome, Italy.
|
|
@ -54,6 +54,7 @@ const constructExifFields = (exifData, createNodeField, node) => {
|
||||||
FNumber,
|
FNumber,
|
||||||
ExposureTime,
|
ExposureTime,
|
||||||
FocalLength,
|
FocalLength,
|
||||||
|
FocalLengthIn35mmFormat,
|
||||||
ExposureBiasValue,
|
ExposureBiasValue,
|
||||||
ExposureMode,
|
ExposureMode,
|
||||||
LensModel
|
LensModel
|
||||||
|
@ -61,7 +62,7 @@ const constructExifFields = (exifData, createNodeField, node) => {
|
||||||
|
|
||||||
const iso = `ISO ${ISO}`
|
const iso = `ISO ${ISO}`
|
||||||
const fstop = `ƒ ${FNumber}`
|
const fstop = `ƒ ${FNumber}`
|
||||||
const focalLength = `${FocalLength}mm`
|
const focalLength = `${FocalLengthIn35mmFormat || FocalLength}mm`
|
||||||
|
|
||||||
// Shutter speed
|
// Shutter speed
|
||||||
const { n, d } = new Fraction(ExposureTime)
|
const { n, d } = new Fraction(ExposureTime)
|
||||||
|
|
|
@ -112,6 +112,7 @@
|
||||||
"identity-obj-proxy": "^3.0.0",
|
"identity-obj-proxy": "^3.0.0",
|
||||||
"jest": "^24.9.0",
|
"jest": "^24.9.0",
|
||||||
"markdownlint-cli": "^0.18.0",
|
"markdownlint-cli": "^0.18.0",
|
||||||
|
"node-iptc": "^1.0.5",
|
||||||
"node-sass": "^4.12.0",
|
"node-sass": "^4.12.0",
|
||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
"ora": "^4.0.0",
|
"ora": "^4.0.0",
|
||||||
|
|
|
@ -6,4 +6,4 @@ title: TITLE
|
||||||
image: DATE_SHORT-SLUG.jpg
|
image: DATE_SHORT-SLUG.jpg
|
||||||
---
|
---
|
||||||
|
|
||||||
Beep Boop.
|
DESCRIPTION
|
||||||
|
|
|
@ -2,6 +2,8 @@ import fs from 'fs-extra'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
import slugify from 'slugify'
|
import slugify from 'slugify'
|
||||||
import ora from 'ora'
|
import ora from 'ora'
|
||||||
|
import fastExif from 'fast-exif'
|
||||||
|
import iptc from 'node-iptc'
|
||||||
|
|
||||||
const templatePath = path.join(__dirname, 'new.md')
|
const templatePath = path.join(__dirname, 'new.md')
|
||||||
const templatePathPhoto = path.join(__dirname, 'new-photo.md')
|
const templatePathPhoto = path.join(__dirname, 'new-photo.md')
|
||||||
|
@ -14,24 +16,55 @@ if (!process.argv[2]) {
|
||||||
spinner.fail('Use the format `npm run new "Title of post"`')
|
spinner.fail('Use the format `npm run new "Title of post"`')
|
||||||
}
|
}
|
||||||
|
|
||||||
const title = process.argv[2]
|
let title = process.argv[2]
|
||||||
const isPhoto = process.argv[3] === 'photo'
|
const isPhoto = process.argv[2] === 'photo'
|
||||||
|
|
||||||
spinner.text = `Adding '${title}'.`
|
spinner.text = `Adding '${title}'.`
|
||||||
|
|
||||||
const titleSlug = slugify(title, { lower: true })
|
let titleSlug = slugify(title, { lower: true })
|
||||||
const postsPath = path.join('.', 'content', 'posts')
|
const postsPath = path.join('.', 'content', 'posts')
|
||||||
const photosPath = path.join('.', 'content', 'photos')
|
const photosPath = path.join('.', 'content', 'photos')
|
||||||
|
|
||||||
let date = new Date().toISOString()
|
let date = new Date().toISOString()
|
||||||
|
|
||||||
if (isPhoto) {
|
async function getIptc(imagePath) {
|
||||||
if (process.argv[4]) {
|
return new Promise((resolve, reject) => {
|
||||||
date = new Date(process.argv[4]).toISOString()
|
fs.readFile(imagePath, (err, data) => {
|
||||||
|
if (err) reject(err)
|
||||||
|
const iptcData = iptc(data)
|
||||||
|
return resolve(iptcData)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getExif(imagePath) {
|
||||||
|
let exifData
|
||||||
|
try {
|
||||||
|
exifData = await fastExif.read(imagePath, true)
|
||||||
|
} catch (error) {
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let iptcData
|
||||||
|
try {
|
||||||
|
iptcData = await getIptc(imagePath)
|
||||||
|
} catch (error) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return { ...exifData, iptc: { ...iptcData } }
|
||||||
|
}
|
||||||
|
|
||||||
|
async function createPhotoPost() {
|
||||||
|
const photo = process.argv[3]
|
||||||
|
const exifData = await getExif(photo)
|
||||||
|
title = exifData.iptc.object_name || exifData.iptc.title
|
||||||
|
titleSlug = slugify(title, { lower: true })
|
||||||
|
date = new Date(exifData.exif.DateTimeOriginal).toISOString()
|
||||||
const dateShort = date.slice(0, 10)
|
const dateShort = date.slice(0, 10)
|
||||||
const filePhoto = `${photosPath}/${dateShort}-${titleSlug}.md`
|
const description = exifData.iptc.caption
|
||||||
|
const fileName = `${dateShort}-${titleSlug}`
|
||||||
|
const postPhoto = `${photosPath}/${fileName}.md`
|
||||||
|
|
||||||
const newContentsPhoto = templatePhoto
|
const newContentsPhoto = templatePhoto
|
||||||
.split('TITLE')
|
.split('TITLE')
|
||||||
|
@ -42,13 +75,23 @@ if (isPhoto) {
|
||||||
.join(date)
|
.join(date)
|
||||||
.split('DATE_SHORT')
|
.split('DATE_SHORT')
|
||||||
.join(dateShort)
|
.join(dateShort)
|
||||||
|
.split('DESCRIPTION')
|
||||||
|
.join(description)
|
||||||
|
|
||||||
fs.appendFile(filePhoto, newContentsPhoto, err => {
|
// copy photo file in place
|
||||||
if (err) spinner.fail(`Error creating photo post: ${err}`)
|
fs.copyFile(photo, `${photosPath}/${fileName}.jpg`, err => {
|
||||||
spinner.succeed(
|
if (err) spinner.fail(`Error copying photo file: ${err}`)
|
||||||
`New photo post '${title}' created.\n\n Use ${dateShort}-${titleSlug}.jpg as the photo file name.`
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// create photo post file
|
||||||
|
fs.appendFile(postPhoto, newContentsPhoto, err => {
|
||||||
|
if (err) spinner.fail(`Error creating photo post: ${err}`)
|
||||||
|
spinner.succeed(`New photo post '${title}' as '${fileName}.md' created.`)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isPhoto) {
|
||||||
|
createPhotoPost()
|
||||||
} else {
|
} else {
|
||||||
if (process.argv[3]) {
|
if (process.argv[3]) {
|
||||||
date = new Date(process.argv[3]).toISOString()
|
date = new Date(process.argv[3]).toISOString()
|
||||||
|
|
Loading…
Reference in New Issue
Block a user