mm.site/scripts/files2json.js

220 lines
5.8 KiB
JavaScript

import fs from 'fs/promises'
import path from 'path'
import { exec } from 'child_process'
// WAVEFORM GENERATION
// TODO@mx use node-js library
async function generateWaveform(audioFile, outputDir) {
const inputFile = path.join(outputDir, audioFile)
audioFile = audioFile.replace(/\.[^/.]+$/, '.png')
const outputPath = path.join(outputDir || '.', audioFile)
return new Promise((resolve, reject) => {
exec(
`audiowaveform -i '${inputFile}' -o '${outputPath}' --output-format png -z auto -w 640 -h 180 --background-color 00000000 --waveform-color FFFFFF50 --waveform-style bars --bar-style rounded --border-color 00000000 --bar-width 4 --bar-gap 6 --no-axis-labels`,
(error, stdOut) => {
if (error) {
console.error(error)
reject(error)
} else {
resolve(audioFile)
}
})
})
}
// MAIN FUNCTION
async function main() {
// variable for incoming variable
let mediaDir = ''
let outputDir = ''
let recurse = false
let imagesOnly = false
let dryRun = false
let formatted = null
let waveform = false
let update = []
// get command line arguments
process.argv.forEach(function (val, index) {
// console.log(index + ': ' + val)
switch (val) {
case '-dir':
mediaDir = process.argv[index + 1] || ''
break
case '-o':
outputDir = process.argv[index + 1] || ''
break
case '-u':
update = process.argv[index + 1].match(/(title|details|media|image[s]*|tracks|)/i)
? process.argv[index + 1]
.split(',')
.filter(a => [
'title',
'details',
'media',
'image',
'tracks',
'images'
].includes(a))
: true
break
case '-r':
recurse = true
break
case '--images-only':
imagesOnly = true
break
case '--dry-run':
dryRun = true
break
case '--formatted':
formatted = ['parent', 'recurse', 'both' ].includes(process.argv[index + 1]) ? process.argv[index + 1] : 'both'
break
case '--gen-waveform':
waveform = true
break
default:
break
}
})
// if no media dir passed jump out
if (!mediaDir) {
console.log('no directory passed to script')
return 0
}
//
// read directory
const dirList = await fs.readdir(mediaDir)
// variable to hold media
let media = []
let images = []
// If recursing we are making the "media" the directories where we will find the
// acutal media to playback or see
if (recurse) {
media = dirList.filter(i => !i.match(/\.[^/.]+$/))
} else {
media = dirList.filter(i => i.match(/.(mp\d|m\d\w)$/i))
}
// create an array of images found in the directory as well
images = dirList.filter(i => i.match(/.(jp\w*g|png)$/i))
// clean up passed directory for json
let parent_dir = mediaDir
.match(/(?<=\/)(\w|\d)+\/*$/i)[0]
.replace(/\/$/i, '')
// set an output directory if not set by flags
if (!outputDir) {
outputDir = '.'
}
// THE MAIN EVENT
const obj = await Promise.all(media.map(async m => {
// setup structure
const _r = {
...genMetadata(m, formatted == 'parent' || formatted == 'both'),
parent_dir,
media: m,
image: ''
}
// if we are scanning for images only, remove the 'image' property
// as it will be saved in the media property
if (imagesOnly) {
delete _r.image
} else {
if (waveform) {
// if waveform generate waveform
_r.image = await generateWaveform(m, mediaDir)
} else {
// if not compare the media name with the image name
// and if a match assign it to 'image' variable from earlier
_r.image = linkImage(m.replace(/\.[^/.]+$/, ''), images)
}
}
// if recurse (again) now we are looking for the media in the folders
if (recurse) {
// create full path from the passed dir, and the recuring dir
const rDir = path.join(mediaDir, m)
// read the dir
let files = await fs.readdir(rDir)
// filterout any rogue folders
// generate the metadata for each file
files = files.filter(f => f.match(/\.[^/.]+$/))
files = files.map(f => {
const { title, details } = genMetadata(f, formatted == 'recurse' || formatted == 'both')
return { title, details, media: f }
})
// if images only
if (imagesOnly) {
// setup object per image in array
_r.images = files
} else {
_r.album = true
_r.tracks = files
}
}
// return the result to the 'obj' variable
return _r
}))
// turn the obj variable into JSON
const json = JSON.stringify(obj, null, 2)
// set the output filename
let outputFile = `${parent_dir}.json`
// check if file already exists at location
// const dataFile = await fs.readFile(path.join(outputDir, outputFile), 'utf8')
// console.log(JSON.parse(dataFile) || 'no data file exists')
// update only new fields
// if dryrun
if (dryRun) {
// just console it out
console.log(json)
} else {
// write it to a file
fs.writeFile(path.join(outputDir, outputFile), json)
}
}
function genMetadata(dir, formatted) {
let title, details
// if formatted flag set
if (formatted) {
// split the incoming name from the map on the '_'
// and set the first element to title, and the second to details
let [ t, d ] = dir.split('_')
// assign to return object
title = t
details = d ? d.replace(/\.+(mp\d|m\dv)$/i, '') : ''
} else {
// else just set title to filename without extensions
title = dir.replace(/\.[^/.]+$/, '')
}
return { title, details }
}
function linkImage(name, images) {
return images.find(i => {
return i.replace(/\.[^/.]+$/, '') == name
})
}
main()