83 lines
2.3 KiB
JavaScript
83 lines
2.3 KiB
JavaScript
import { mkdir, rm } from 'node:fs/promises';
|
|
import { dirname, resolve } from 'node:path';
|
|
import { spawn } from 'node:child_process';
|
|
import { once } from 'node:events';
|
|
|
|
const root = resolve(import.meta.dirname, '..');
|
|
const sourceUrl = 'https://esahubble.org/media/archives/images/large/heic1509a.jpg';
|
|
const sourcePath = resolve(root, 'media/.hubble-westerlund-2-source.jpg');
|
|
const outputPath = resolve(root, 'media/hubble-westerlund-2-hdr.png');
|
|
|
|
async function run(command, args, options = {}) {
|
|
const child = spawn(command, args, { stdio: 'inherit', ...options });
|
|
const [code] = await once(child, 'close');
|
|
if (code !== 0) throw new Error(`${command} exited with code ${code}`);
|
|
}
|
|
|
|
function capture(command, args) {
|
|
return new Promise((resolveBuffer, reject) => {
|
|
const child = spawn(command, args, { stdio: ['ignore', 'pipe', 'inherit'] });
|
|
const chunks = [];
|
|
child.stdout.on('data', (chunk) => chunks.push(chunk));
|
|
child.on('error', reject);
|
|
child.on('close', (code) => {
|
|
if (code !== 0) {
|
|
reject(new Error(`${command} exited with code ${code}`));
|
|
return;
|
|
}
|
|
resolveBuffer(Buffer.concat(chunks));
|
|
});
|
|
});
|
|
}
|
|
|
|
await mkdir(dirname(outputPath), { recursive: true });
|
|
|
|
await run('curl', [
|
|
'-L',
|
|
'--fail',
|
|
'--silent',
|
|
'--show-error',
|
|
sourceUrl,
|
|
'-o',
|
|
sourcePath
|
|
]);
|
|
|
|
const frame = await capture('ffmpeg', [
|
|
'-hide_banner',
|
|
'-loglevel',
|
|
'error',
|
|
'-i', sourcePath,
|
|
'-vf',
|
|
'scale=2560:1440:force_original_aspect_ratio=increase,crop=2560:1440,eq=contrast=1.06:saturation=1.12:brightness=-0.02',
|
|
'-f', 'rawvideo',
|
|
'-pix_fmt', 'rgba64le',
|
|
'pipe:1'
|
|
]);
|
|
|
|
const encode = spawn('ffmpeg', [
|
|
'-y',
|
|
'-f', 'rawvideo',
|
|
'-pix_fmt', 'rgba64le',
|
|
'-s:v', '2560x1440',
|
|
'-i', 'pipe:0',
|
|
'-vf',
|
|
'setparams=range=full:color_primaries=bt2020:color_trc=smpte2084:colorspace=gbr',
|
|
'-frames:v', '1',
|
|
'-update', '1',
|
|
'-c:v', 'png',
|
|
'-pix_fmt', 'rgba64be',
|
|
'-pred', 'mixed',
|
|
'-color_primaries', 'bt2020',
|
|
'-color_trc', 'smpte2084',
|
|
outputPath
|
|
], { stdio: ['pipe', 'inherit', 'inherit'] });
|
|
|
|
encode.stdin.end(frame);
|
|
const [encodeCode] = await once(encode, 'close');
|
|
if (encodeCode !== 0) throw new Error(`ffmpeg png exited with code ${encodeCode}`);
|
|
|
|
await rm(sourcePath, { force: true });
|
|
|
|
console.log(`Wrote ${outputPath}`);
|
|
console.log('Source: NASA/ESA Hubble Westerlund 2, heic1509a');
|