Files
mqtt-explorer/scripts/cutVideoSegments.sh
2025-12-24 14:28:25 +01:00

107 lines
2.9 KiB
Bash
Executable File

#!/bin/bash
set -e
# Read scenes.json and cut video into segments as GIFs
if [ ! -f "scenes.json" ]; then
echo "scenes.json not found"
exit 1
fi
if [ ! -f "ui-test.mp4" ]; then
echo "ui-test.mp4 not found"
exit 1
fi
echo "Cutting video into GIF segments based on scenes.json..."
export GIF_SCALE="1024"
# Parse scenes.json and cut video segments as GIFs
node -e "
const fs = require('fs');
const { spawn } = require('child_process');
const scenes = JSON.parse(fs.readFileSync('scenes.json', 'utf8'));
console.log('Creating GIF segments...');
// Sanitize scene name to prevent path traversal and command injection
function sanitizeName(name) {
// Remove any characters that aren't alphanumeric, dash, or underscore
return name.replace(/[^a-zA-Z0-9_-]/g, '-');
}
async function cutSegmentAsGif(scene, index) {
const safeName = sanitizeName(scene.name);
const segmentName = \`segment-\${String(index + 1).padStart(2, '0')}-\${safeName}\`;
const paletteFile = \`\${segmentName}-palette.png\`;
const outputFile = \`\${segmentName}.gif\`;
const startTime = scene.start / 1000; // Convert ms to seconds
const duration = scene.duration / 1000; // Convert ms to seconds
console.log(\`Creating \${outputFile} (start: \${startTime}s, duration: \${duration}s)\`);
// Step 1: Generate palette for this segment
await new Promise((resolve, reject) => {
const ffmpeg = spawn('ffmpeg', [
'-y',
'-ss', startTime.toString(),
'-t', duration.toString(),
'-i', 'ui-test.mp4',
'-vf', 'fps=10,scale=${process.env.GIF_SCALE || 1024}:-1:flags=lanczos,palettegen',
paletteFile
]);
ffmpeg.on('close', (code) => {
if (code === 0) {
resolve();
} else {
console.error(\`Failed to create palette for \${outputFile}\`);
reject(new Error(\`ffmpeg palette generation exited with code \${code}\`));
}
});
});
// Step 2: Create GIF using the palette
await new Promise((resolve, reject) => {
const ffmpeg = spawn('ffmpeg', [
'-y',
'-ss', startTime.toString(),
'-t', duration.toString(),
'-i', 'ui-test.mp4',
'-i', paletteFile,
'-filter_complex', 'fps=10,scale=${process.env.GIF_SCALE || 1024}:-1:flags=lanczos[x];[x][1:v]paletteuse',
outputFile
]);
ffmpeg.on('close', (code) => {
// Clean up palette file
try {
fs.unlinkSync(paletteFile);
} catch (e) {
// Ignore cleanup errors
}
if (code === 0) {
resolve();
} else {
console.error(\`Failed to create \${outputFile}\`);
reject(new Error(\`ffmpeg GIF creation exited with code \${code}\`));
}
});
});
}
(async () => {
for (let i = 0; i < scenes.length; i++) {
await cutSegmentAsGif(scenes[i], i);
}
console.log('All GIF segments created successfully');
})().catch(err => {
console.error(err);
process.exit(1);
});
"
echo "Video segments created successfully"