Skip to content

Instantly share code, notes, and snippets.

@brennancheung
Created June 17, 2021 20: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 brennancheung/620f66136e70a814b05f7d2ae95125f1 to your computer and use it in GitHub Desktop.
Save brennancheung/620f66136e70a814b05f7d2ae95125f1 to your computer and use it in GitHub Desktop.
const chokidar = require('chokidar')
const fs = require('fs')
const winPicsPath = '/c/Users/brenn/Pictures/exports'
const picsPath = '/mnt/c/Users/brenn/Pictures/exports'
const watchPaths = {
exports: `${picsPath}`,
}
const MB = 1024 * 1024
const getFileSize = path => fs.statSync(path).size
const fileExists = path => fs.existsSync(path)
const generateWinPath = fullPath => winPicsPath + '/' + fullPath
const hasEditingPath = path => /.*?\/editing\/.+/.test(path)
const hasStyledPath = path => /.*?\/styled\/.+/.test(path)
const hasOriginal = path => /.*\.psd_original/.test(path)
const hasTmpPath = path => /.*\.tmp/.test(path)
const isEditing = path => hasEditingPath(path) && !hasTmpPath(path)
const isStyled = path => hasStyledPath(path) && !hasTmpPath(path) && !hasOriginal(path)
const extractModelPathInfo = path => {
const [p, date, model, filename] = path.match(/(\d+-\d+-\d+)\/(.*?)\/.*?\/(.*)/)
const [_, baseFilename] = path.match(/.*\/(.*)\..{3}/)
return { date, model, filename, baseFilename, path: p }
}
let editQueue = []
let styledQueue = []
let isProcessing = false
let modelPath = ''
const mkdir = dir => {
if (fs.existsSync(dir)) return
console.log(`Creating directory ${dir}`)
fs.mkdirSync(dir)
}
const prepOtherFoldersSync = info => {
modelPath = `${picsPath}/${info.date}/${info.model}`
mkdir(`${modelPath}/final`)
mkdir(`${modelPath}/styled`)
}
// We don't want Photoshop trying to open the file before it is fully written.
const SECONDS = 1000
const ENQUEUE_DELAY = 10*SECONDS
const enqueueStyled = info => {
const enqueue = () => styledQueue.push(info)
console.log(`Queueing styled ${info.baseFilename}, delayed ${ENQUEUE_DELAY}`)
setTimeout(enqueue, ENQUEUE_DELAY)
}
const enqueueEditing = info => {
const enqueue = () => editQueue.push(info.path)
console.log(`Queueing editing ${info.baseFilename}, delayed ${ENQUEUE_DELAY}`)
setTimeout(enqueue, ENQUEUE_DELAY)
}
const start = async g => {
const getOrientation = ({ bounds }) => bounds.bottom >= bounds.right ? 'portrait' : 'landscape'
const isPortrait = doc => getOrientation(doc) === 'portrait'
const isLandscape = doc => getOrientation(doc) === 'portrait'
const playAction = (action, actionSet) => {
const str = `app.doAction("${action}", "${actionSet}")`
return g.evaluateJSXString(str)
}
const playBatchAction = () => playAction('Batch full', 'brennan')
const playToJpgAction = () => playAction('to jpg', 'brennan')
const closeCurrentDoc = (save=false) => {
const saveOption = 'SaveOptions.' + (save ? 'SAVECHANGES' : 'DONOTSAVECHANGES')
return g.evaluateJSXString(`app.activeDocument.close(${saveOption})`)
}
const shrinkPortrait = () => playAction('2000x3000', 'brennan')
const shrinkLandscape = () => playAction('3000x2000', 'brennan')
const processEditingDoc = async docId => {
const docInfo = await g.getDocumentInfo(docId)
const orientation = getOrientation(docInfo)
const { bounds } = docInfo
const isBig = Math.max(bounds.bottom, bounds.right) > 3000
console.log(`Editing: #${docId} ${docInfo.file} ${orientation}`)
if (!isBig) {
await closeCurrentDoc()
return console.log(`Skipping ${docInfo.file} because it is too small`)
}
const resizeFn = isPortrait(docInfo) ? shrinkPortrait : shrinkLandscape
await resizeFn()
await playBatchAction()
}
const processStyledDoc = async info => {
console.log(`To jpeg: ${info.path}`)
await playToJpgAction()
const srcPath = `${picsPath}/to-jpg/${info.baseFilename}.jpg`
const destPath = `${modelPath}/final/${info.baseFilename}.jpg`
console.log(`Moving ${srcPath} to ${destPath}`)
fs.renameSync(srcPath, destPath)
}
const openDoc = async docInfo => {
const winPath = generateWinPath(docInfo)
const str = `app.open(File("${winPath}"))`
return g.evaluateJSXString(str)
}
const runQueue = async () => {
if (isProcessing) return
if (editQueue.length > 0) {
isProcessing = true
const path = editQueue.shift()
await openDoc(path)
const ids = await g.getOpenDocumentIDs()
await processEditingDoc(ids[0])
isProcessing = false
return
}
if (styledQueue.length > 0) {
isProcessing = true
const info = styledQueue.shift()
await openDoc(info.path)
await processStyledDoc(info)
isProcessing = false
return
}
}
setInterval(runQueue, 200)
const handleFileAdd = async path => {
console.log(`\nNew file added: ${path}`)
if (isEditing(path)) {
if (getFileSize(path) < 20*MB) return console.log(`Skipping edit because file too small ${path}`)
const info = extractModelPathInfo(path)
prepOtherFoldersSync(info)
enqueueEditing(info)
}
if (isStyled(path)) {
const info = extractModelPathInfo(path)
modelPath = `${picsPath}/${info.date}/${info.model}`
const finalPath = `${modelPath}/final/${info.baseFilename}.jpg`
if (fileExists(finalPath)) return console.log(`Skipping styled because jpg already exists ${path}`)
enqueueStyled(info)
}
}
const watcher = chokidar.watch(watchPaths.exports, {
usePolling: true,
interval: 1000,
})
watcher.on('add', handleFileAdd)
}
(function () {
const init = async g => {
console.log('edit-workflow (init)')
start(g)
}
exports.init = init
})()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment