Skip to content

Instantly share code, notes, and snippets.

@kellymears
Last active May 16, 2020 02:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kellymears/90f53c1387c6817dc82793f38e1533da to your computer and use it in GitHub Desktop.
Save kellymears/90f53c1387c6817dc82793f38e1533da to your computer and use it in GitHub Desktop.
Asynchronously process a set of images using rxjs and sharp
const fs = require('fs-extra')
const sharp = require('sharp')
const globby = require('globby')
const {Observable, from} = require('rxjs')
const {concatMap} = require('rxjs/operators')
const pino = require('pino')
const prettifier = require('pino-pretty')
/**
* Logger util
*/
const logger = pino({
sync: false,
prettyPrint: {
levelFirst: true,
},
prettifier,
})
/**
* Media sources
*/
const SOURCES = [
'web/app/uploads/**/*.jpg',
'web/app/uploads/**/*.jpeg',
'web/app/uploads/**/*.png',
'web/app/themes/sage/resources/assets/images/*.jpg',
'web/app/themes/sage/resources/assets/images/*.jpeg',
'web/app/themes/sage/resources/assets/images/*.png',
]
/**
* Options
*/
const options = {
png: {
progressive: true,
quality: 50,
compressionLevel: 6,
},
jpeg: {
progressive: true,
quality: 50,
optimizeScans: true,
},
resize: {
width: 2560,
height: 1440,
withoutEnlargement: true,
},
}
/**
* Return the sharp method for
* a given image file
*/
const sharpHandler = path => {
const exts = {
png: 'png',
jpg: 'jpeg',
jpeg: 'jpeg',
}
const ext = exts[path.split('.').pop()]
return ext ? ext : null
}
/**
* Return tmp path for any given file.
*/
const tmp = path =>
path.replace('web/', 'tmp/')
/**
* Asynchronously process a set of images
* using the Lovell/Sharp library
*
* @param {array} sources -- glob strings
*/
const process = async sources => {
/** Resolve array of image paths */
const media = await globby(sources)
/**
* Process images as a metastream
* of functional observables.
*/
const processing = new Observable(obs => {
from(media).pipe(
concatMap(async img => {
/** Copy to tmp */
try {
await fs.copy(img, tmp(img))
} catch (e) {
logger.error(e)
}
/**
* Process image.
*/
try {
await sharp(tmp(img))[`${sharpHandler(img)}`](
options[`${sharpHandler(img)}`]
).resize(options.resize).toFile(img)
} catch (e) {
logger.error(e)
}
/** Remove tmp */
try {
await fs.remove(tmp(img))
} catch (e) {
logger.error(e)
}
obs.next(img)
})
)
.subscribe({
next: next => obs.next(next),
})
})
/**
* Log metastream's emitted events
*/
processing.subscribe({
next: next => next && logger.info(next),
})
}
process(SOURCES)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment