108 lines
3.1 KiB
JavaScript
108 lines
3.1 KiB
JavaScript
import path from 'path';
|
|
import fs from 'fs';
|
|
import { fileURLToPath } from 'url';
|
|
import sqlite3 from 'sqlite3';
|
|
import { open } from 'sqlite';
|
|
import { spawn } from 'child_process';
|
|
|
|
// Find ffmpeg binary
|
|
const FFMPEG = ['/opt/homebrew/bin/ffmpeg', '/usr/bin/ffmpeg'].find(p => fs.existsSync(p)) || 'ffmpeg';
|
|
|
|
const __filename = fileURLToPath(import.meta.url);
|
|
const __dirname = path.dirname(__filename);
|
|
const mediaDir = path.join(__dirname, '..', 'public', 'media');
|
|
|
|
const transcodeToMp4 = (inputPath, outputPath) => new Promise((resolve, reject) => {
|
|
const args = [
|
|
'-y',
|
|
'-i', inputPath,
|
|
'-vf', 'scale=trunc(iw/2)*2:trunc(ih/2)*2',
|
|
'-c:v', 'libx264',
|
|
'-profile:v', 'baseline',
|
|
'-level', '3.0',
|
|
'-pix_fmt', 'yuv420p',
|
|
'-c:a', 'aac',
|
|
'-b:a', '128k',
|
|
'-movflags', '+faststart',
|
|
outputPath
|
|
];
|
|
|
|
const ffmpeg = spawn(FFMPEG, args, { stdio: 'ignore' });
|
|
ffmpeg.on('error', reject);
|
|
ffmpeg.on('close', (code) => {
|
|
if (code === 0) {
|
|
resolve();
|
|
} else {
|
|
reject(new Error(`ffmpeg exited with code ${code}`));
|
|
}
|
|
});
|
|
});
|
|
|
|
const toMediaPath = (filename) => `/media/${filename}`;
|
|
|
|
async function run() {
|
|
const db = await open({
|
|
filename: path.join(__dirname, 'signsync.db'),
|
|
driver: sqlite3.Database
|
|
});
|
|
|
|
const addColumnIfMissing = async (columnDef) => {
|
|
try {
|
|
await db.exec(`ALTER TABLE recordings ADD COLUMN ${columnDef}`);
|
|
} catch (error) {
|
|
if (!String(error.message).toLowerCase().includes('duplicate column')) {
|
|
console.error('Error adding column:', error);
|
|
}
|
|
}
|
|
};
|
|
|
|
await addColumnIfMissing('recorded_video_processing INTEGER DEFAULT 0');
|
|
await addColumnIfMissing('recorded_video_processing_2 INTEGER DEFAULT 0');
|
|
|
|
const rows = await db.all('SELECT id, recorded_video_path, recorded_video_path_2 FROM recordings');
|
|
|
|
for (const row of rows) {
|
|
const updates = {};
|
|
|
|
for (const key of ['recorded_video_path', 'recorded_video_path_2']) {
|
|
const currentPath = row[key];
|
|
if (!currentPath || !currentPath.endsWith('.webm')) continue;
|
|
|
|
const inputPath = path.join(mediaDir, path.basename(currentPath));
|
|
if (!fs.existsSync(inputPath)) {
|
|
console.warn(`Missing file for ${key} on row ${row.id}: ${inputPath}`);
|
|
continue;
|
|
}
|
|
|
|
const outputPath = inputPath.replace(/\.webm$/i, '.mp4');
|
|
if (!fs.existsSync(outputPath)) {
|
|
console.log(`Transcoding ${inputPath} -> ${outputPath}`);
|
|
await transcodeToMp4(inputPath, outputPath);
|
|
}
|
|
|
|
updates[key] = toMediaPath(path.basename(outputPath));
|
|
fs.unlinkSync(inputPath);
|
|
}
|
|
|
|
if (Object.keys(updates).length > 0) {
|
|
await db.run(
|
|
`UPDATE recordings
|
|
SET recorded_video_path = COALESCE(?, recorded_video_path),
|
|
recorded_video_path_2 = COALESCE(?, recorded_video_path_2),
|
|
recorded_video_processing = 0,
|
|
recorded_video_processing_2 = 0
|
|
WHERE id = ?`,
|
|
[updates.recorded_video_path || null, updates.recorded_video_path_2 || null, row.id]
|
|
);
|
|
}
|
|
}
|
|
|
|
await db.close();
|
|
console.log('Conversion complete.');
|
|
}
|
|
|
|
run().catch((err) => {
|
|
console.error(err);
|
|
process.exit(1);
|
|
});
|