fix(sequences): coerce NUMERIC frame_rate to float in all API responses
node-postgres returns NUMERIC columns as strings by default. Add a mapSeq() helper that parses frame_rate to a JS float before any response is sent. Affected routes: GET /, POST /, PUT /:id, GET /:id.
This commit is contained in:
parent
bfc2649909
commit
4d0e715982
1 changed files with 16 additions and 7 deletions
|
|
@ -7,6 +7,15 @@ import { requireAuth } from '../middleware/auth.js';
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
router.use(requireAuth);
|
router.use(requireAuth);
|
||||||
|
|
||||||
|
// ── Row mapper ────────────────────────────────────────────────────────────────
|
||||||
|
// node-postgres returns NUMERIC columns as strings. Coerce frame_rate to a
|
||||||
|
// JS float before sending any sequence object to clients.
|
||||||
|
|
||||||
|
function mapSeq(row) {
|
||||||
|
if (!row) return row;
|
||||||
|
return { ...row, frame_rate: parseFloat(row.frame_rate) || 59.94 };
|
||||||
|
}
|
||||||
|
|
||||||
// ── Timecode helpers ──────────────────────────────────────────────────────────
|
// ── Timecode helpers ──────────────────────────────────────────────────────────
|
||||||
//
|
//
|
||||||
// generateEDL emits CMX3600 timecode using the sequence's frame_rate.
|
// generateEDL emits CMX3600 timecode using the sequence's frame_rate.
|
||||||
|
|
@ -19,7 +28,7 @@ router.use(requireAuth);
|
||||||
function pad2(n) { return String(Math.floor(n)).padStart(2, '0'); }
|
function pad2(n) { return String(Math.floor(n)).padStart(2, '0'); }
|
||||||
|
|
||||||
function framesToTC(totalFrames, fps) {
|
function framesToTC(totalFrames, fps) {
|
||||||
fps = fps || 59.94;
|
fps = parseFloat(fps) || 59.94;
|
||||||
const fc = Math.max(0, Math.round(totalFrames));
|
const fc = Math.max(0, Math.round(totalFrames));
|
||||||
|
|
||||||
// 29.97 DF ─ drop 2 frames per minute except every 10th
|
// 29.97 DF ─ drop 2 frames per minute except every 10th
|
||||||
|
|
@ -77,7 +86,7 @@ function framesToTC(totalFrames, fps) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateEDL(seqName, clips, fps) {
|
function generateEDL(seqName, clips, fps) {
|
||||||
fps = fps || 59.94;
|
fps = parseFloat(fps) || 59.94;
|
||||||
const lines = [`TITLE: ${seqName}`, ''];
|
const lines = [`TITLE: ${seqName}`, ''];
|
||||||
clips.forEach((c, i) => {
|
clips.forEach((c, i) => {
|
||||||
const num = String(i + 1).padStart(3, '0');
|
const num = String(i + 1).padStart(3, '0');
|
||||||
|
|
@ -105,7 +114,7 @@ router.get('/', async (req, res, next) => {
|
||||||
`SELECT * FROM sequences WHERE project_id = $1 ORDER BY updated_at DESC`,
|
`SELECT * FROM sequences WHERE project_id = $1 ORDER BY updated_at DESC`,
|
||||||
[project_id]
|
[project_id]
|
||||||
);
|
);
|
||||||
res.json(r.rows);
|
res.json(r.rows.map(mapSeq));
|
||||||
} catch (e) { next(e); }
|
} catch (e) { next(e); }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -125,7 +134,7 @@ router.post('/', async (req, res, next) => {
|
||||||
VALUES ($1, $2, $3, $4, $5) RETURNING *`,
|
VALUES ($1, $2, $3, $4, $5) RETURNING *`,
|
||||||
[project_id, name, frame_rate, width, height]
|
[project_id, name, frame_rate, width, height]
|
||||||
);
|
);
|
||||||
res.status(201).json(r.rows[0]);
|
res.status(201).json(mapSeq(r.rows[0]));
|
||||||
} catch (e) { next(e); }
|
} catch (e) { next(e); }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -160,7 +169,7 @@ router.get('/:id', async (req, res, next) => {
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
res.json({ ...seqR.rows[0], clips });
|
res.json({ ...mapSeq(seqR.rows[0]), clips });
|
||||||
} catch (e) { next(e); }
|
} catch (e) { next(e); }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -183,7 +192,7 @@ router.put('/:id', async (req, res, next) => {
|
||||||
params
|
params
|
||||||
);
|
);
|
||||||
if (!r.rows.length) return res.status(404).json({ error: 'Sequence not found' });
|
if (!r.rows.length) return res.status(404).json({ error: 'Sequence not found' });
|
||||||
res.json(r.rows[0]);
|
res.json(mapSeq(r.rows[0]));
|
||||||
} catch (e) { next(e); }
|
} catch (e) { next(e); }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -254,7 +263,7 @@ router.post('/:id/export/edl', async (req, res, next) => {
|
||||||
try {
|
try {
|
||||||
const seqR = await pool.query(`SELECT * FROM sequences WHERE id = $1`, [req.params.id]);
|
const seqR = await pool.query(`SELECT * FROM sequences WHERE id = $1`, [req.params.id]);
|
||||||
if (!seqR.rows.length) return res.status(404).json({ error: 'Sequence not found' });
|
if (!seqR.rows.length) return res.status(404).json({ error: 'Sequence not found' });
|
||||||
const seq = seqR.rows[0];
|
const seq = mapSeq(seqR.rows[0]);
|
||||||
|
|
||||||
// Export V1 clips only (primary video track) sorted by position
|
// Export V1 clips only (primary video track) sorted by position
|
||||||
const clipsR = await pool.query(
|
const clipsR = await pool.query(
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue