function toCamelJob(row) { if (!row) return null; return { id: row.id, uuid: row.uuid, status: row.status, family: row.family, sourceFormat: row.source_format, targetFormat: row.target_format, originalFilename: row.original_filename, inputPath: row.input_path, outputPath: row.output_path, inputMimeType: row.input_mime_type, outputMimeType: row.output_mime_type, inputSizeBytes: row.input_size_bytes, outputSizeBytes: row.output_size_bytes, conversionDurationSeconds: row.conversion_duration_seconds, errorMessage: row.error_message, createdAt: row.created_at, updatedAt: row.updated_at, expiresAt: row.expires_at, cleanedAt: row.cleaned_at, }; } export async function createJob(pool, job) { await pool.query( `INSERT INTO conversion_jobs (uuid, status, family, source_format, target_format, original_filename, input_path, input_mime_type, input_size_bytes, expires_at) VALUES (?, 'pending', ?, ?, ?, ?, ?, ?, ?, ?)`, [ job.uuid, job.family, job.sourceFormat, job.targetFormat, job.originalFilename, job.inputPath, job.inputMimeType, job.inputSizeBytes, job.expiresAt, ] ); } export async function getJobByUuid(pool, uuid) { const rows = await pool.query( `SELECT id, uuid, status, family, source_format, target_format, original_filename, input_path, output_path, input_mime_type, output_mime_type, input_size_bytes, output_size_bytes, conversion_duration_seconds, error_message, created_at, updated_at, expires_at, cleaned_at FROM conversion_jobs WHERE uuid = ?`, [uuid] ); return toCamelJob(rows[0]); } export async function getJobErrorLog(pool, id) { const rows = await pool.query('SELECT error_log FROM conversion_jobs WHERE id = ?', [id]); return rows[0]?.error_log ?? null; } export async function markProcessing(pool, id) { await pool.query("UPDATE conversion_jobs SET status = 'processing' WHERE id = ?", [id]); } export async function markDone(pool, id, { outputPath, outputMimeType, outputSizeBytes, conversionDurationSeconds }) { await pool.query( `UPDATE conversion_jobs SET status = 'done', output_path = ?, output_mime_type = ?, output_size_bytes = ?, conversion_duration_seconds = ? WHERE id = ?`, [outputPath, outputMimeType, outputSizeBytes, conversionDurationSeconds, id] ); } export async function markFailed(pool, id, { errorMessage, errorLog }) { await pool.query( "UPDATE conversion_jobs SET status = 'failed', error_message = ?, error_log = ? WHERE id = ?", [errorMessage, errorLog, id] ); } export async function findPendingJobs(pool, limit) { const rows = await pool.query( `SELECT id, uuid, status, family, source_format, target_format, original_filename, input_path, output_path, input_mime_type, output_mime_type, input_size_bytes, output_size_bytes, conversion_duration_seconds, error_message, created_at, updated_at, expires_at, cleaned_at FROM conversion_jobs WHERE status = 'pending' ORDER BY created_at ASC LIMIT ?`, [limit] ); return rows.map(toCamelJob); } export async function findExpiredJobs(pool) { const rows = await pool.query( `SELECT id, uuid, status, family, source_format, target_format, original_filename, input_path, output_path, input_mime_type, output_mime_type, input_size_bytes, output_size_bytes, conversion_duration_seconds, error_message, created_at, updated_at, expires_at, cleaned_at FROM conversion_jobs WHERE expires_at < NOW() AND cleaned_at IS NULL` ); return rows.map(toCamelJob); } export async function markCleaned(pool, id) { await pool.query('UPDATE conversion_jobs SET cleaned_at = NOW() WHERE id = ?', [id]); }