feat(live): add language-aware flexible channel search (#23)
Add backend language detection, flexible live channel search, iptv-org catalog caching, and protected built-in EPG source registration. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -1,6 +1,8 @@
|
||||
const express = require('express');
|
||||
const { requireAuth, requireAdmin } = require('../auth');
|
||||
const liveProjection = require('../services/liveProjectionService');
|
||||
const iptvOrgCatalog = require('../services/iptvOrgCatalogService');
|
||||
const { getDb } = require('../db/sqlite');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
@ -31,14 +33,25 @@ router.get('/channels', requireAuth, async (req, res) => {
|
||||
return res.status(400).json({ error: 'includeHidden must be true or false' });
|
||||
}
|
||||
const includeHidden = req.query.includeHidden === 'true';
|
||||
const q = String(req.query.q || '').slice(0, 200);
|
||||
const languages = String(req.query.languages || '').split(',').map(value => value.trim().toLowerCase()).filter(Boolean);
|
||||
if (languages.some(code => !/^(?:[a-z]{2,3}|und)$/.test(code))) {
|
||||
return res.status(400).json({ error: 'languages must be comma-separated language codes' });
|
||||
}
|
||||
|
||||
try {
|
||||
let state = liveProjection.projectionState();
|
||||
if (state.status !== 'success' && state.status !== 'building') {
|
||||
state = await liveProjection.startRebuild().promise;
|
||||
}
|
||||
const channels = liveProjection.getChannels({ sourceId, includeHidden, userId: req.user.id });
|
||||
return res.json({ projection: serializeState(state), channels });
|
||||
const allChannels = liveProjection.getChannels({ sourceId, includeHidden, userId: req.user.id });
|
||||
const availableLanguages = [...new Map(allChannels.map(channel => [channel.language.code, channel.language])).values()]
|
||||
.map(language => ({ code: language.code, short: language.short, label: language.label }))
|
||||
.sort((left, right) => left.label.localeCompare(right.label));
|
||||
const channels = allChannels.filter(channel =>
|
||||
(!languages.length || languages.includes(channel.language.code)) && liveProjection.matchesSearch(channel, q)
|
||||
);
|
||||
return res.json({ projection: serializeState(state), filters: { availableLanguages }, channels });
|
||||
} catch (error) {
|
||||
console.error('[LiveProjection] Failed to serve projected channels:', error.message);
|
||||
const state = liveProjection.projectionState();
|
||||
@ -74,5 +87,16 @@ router.post('/projection/rebuild', requireAuth, requireAdmin, (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
router.post('/projection/standards/refresh', requireAuth, requireAdmin, async (req, res) => {
|
||||
try {
|
||||
const result = await iptvOrgCatalog.refresh(getDb());
|
||||
await liveProjection.queueRebuild();
|
||||
return res.json(result);
|
||||
} catch (error) {
|
||||
console.error('[LiveProjection] iptv-org refresh failed:', error.message);
|
||||
return res.status(502).json({ error: 'Unable to refresh the iptv-org catalog' });
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
module.exports.serializeState = serializeState;
|
||||
|
||||
Reference in New Issue
Block a user