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'; 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); });