feat: precompute stream grouping during source sync (#13)
Move variant grouping work out of Live TV rendering into the catalog pipeline: a liveProjectionService builds the logical channel projection (schema in liveProjectionSchema) whenever a source is added, synced or modified (name change, new address), and the projection is served through a /live route. ChannelList now consumes the precomputed projection via a liveChannelProjection client service, recalculating only when the catalog changes. Cover projection building, incremental recompute and client consumption with tests. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
46
server/routes/live.js
Normal file
46
server/routes/live.js
Normal file
@ -0,0 +1,46 @@
|
||||
const express = require('express');
|
||||
const { requireAuth } = require('../auth');
|
||||
const liveProjection = require('../services/liveProjectionService');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
function serializeState(state) {
|
||||
return {
|
||||
status: state.status,
|
||||
stale: state.status !== 'success',
|
||||
startedAt: state.started_at,
|
||||
completedAt: state.completed_at,
|
||||
inputHash: state.input_hash,
|
||||
projectedCount: state.projected_count,
|
||||
error: state.error
|
||||
};
|
||||
}
|
||||
|
||||
router.get('/channels', requireAuth, async (req, res) => {
|
||||
const sourceId = req.query.sourceId === undefined ? null : Number(req.query.sourceId);
|
||||
if (sourceId !== null && (!Number.isInteger(sourceId) || sourceId <= 0)) {
|
||||
return res.status(400).json({ error: 'sourceId must be a positive integer' });
|
||||
}
|
||||
if (req.query.includeHidden !== undefined && !['true', 'false'].includes(req.query.includeHidden)) {
|
||||
return res.status(400).json({ error: 'includeHidden must be true or false' });
|
||||
}
|
||||
const includeHidden = req.query.includeHidden === 'true';
|
||||
|
||||
try {
|
||||
let state = liveProjection.projectionState();
|
||||
if (state.status !== 'success') state = liveProjection.rebuild();
|
||||
const channels = liveProjection.getChannels({ sourceId, includeHidden, userId: req.user.id });
|
||||
return res.json({ projection: serializeState(state), channels });
|
||||
} catch (error) {
|
||||
console.error('[LiveProjection] Failed to serve projected channels:', error.message);
|
||||
const state = liveProjection.projectionState();
|
||||
return res.status(503).json({
|
||||
projection: serializeState(state),
|
||||
channels: [],
|
||||
error: 'Live channel projection is unavailable'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
module.exports.serializeState = serializeState;
|
||||
Reference in New Issue
Block a user