fix(live): keep channel search responsive (#25)
Filter projected live channels locally in the browser and apply server-side search and language filters before costly enrichment work. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -46,13 +46,9 @@ router.get('/channels', requireAuth, async (req, res) => {
|
||||
state = await liveProjection.startRebuild().promise;
|
||||
}
|
||||
const epgSources = await sources.getByType('epg');
|
||||
const allChannels = liveProjection.getChannels({ sourceId, includeHidden, userId: req.user.id, epgSources });
|
||||
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)
|
||||
);
|
||||
const { channels, availableLanguages } = liveProjection.getChannelPage({
|
||||
sourceId, includeHidden, userId: req.user.id, epgSources, q, languages
|
||||
});
|
||||
return res.json({ projection: serializeState(state), filters: { availableLanguages }, channels });
|
||||
} catch (error) {
|
||||
console.error('[LiveProjection] Failed to serve projected channels:', error.message);
|
||||
|
||||
@ -346,8 +346,10 @@ function parseData(value) {
|
||||
try { return value ? JSON.parse(value) : {}; } catch { return {}; }
|
||||
}
|
||||
|
||||
function getChannels({ sourceId = null, includeHidden = false, userId = null, q = '', languages = [], epgSources = [] }, database = null) {
|
||||
function getChannelPage({ sourceId = null, includeHidden = false, userId = null, q = '', languages = [], epgSources = [] }, database = null, dependencies = {}) {
|
||||
database ||= defaultDatabase();
|
||||
const standardCatalog = dependencies.iptvOrgCatalog || iptvOrgCatalog;
|
||||
const epgMatcher = dependencies.epgMatching || epgMatching;
|
||||
const params = [];
|
||||
let sourceFilter = '';
|
||||
if (sourceId !== null) {
|
||||
@ -366,18 +368,28 @@ function getChannels({ sourceId = null, includeHidden = false, userId = null, q
|
||||
WHERE p.type = 'live' ${sourceFilter}
|
||||
ORDER BY l.display_name, p.variant_rank
|
||||
`).all(userId ?? -1, ...params);
|
||||
return serializeChannels(rows, includeHidden).map(channel => {
|
||||
const match = iptvOrgCatalog.findMatch(database, channel);
|
||||
const serialized = serializeChannels(rows, includeHidden);
|
||||
const availableLanguages = [...new Map(serialized.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 filtered = serialized.filter(channel =>
|
||||
(!languages.length || languages.includes(channel.language.code)) && matchesSearch(channel, q)
|
||||
);
|
||||
const channels = filtered.map(channel => {
|
||||
const match = standardCatalog.findMatch(database, channel);
|
||||
if (match) {
|
||||
channel.standardName = match.name;
|
||||
channel.standard = { provider: 'iptv-org', channelId: match.channelId, country: match.country, confidence: match.confidence };
|
||||
}
|
||||
const epgMatch = epgMatching.findBestMatch(database, channel, epgSources);
|
||||
const epgMatch = epgMatcher.findBestMatch(database, channel, epgSources);
|
||||
if (epgMatch) channel.epg = { ...channel.epg, ...epgMatch, language: channel.language.code };
|
||||
return channel;
|
||||
}).filter(channel =>
|
||||
(!languages.length || languages.includes(channel.language.code)) && matchesSearch(channel, q)
|
||||
);
|
||||
});
|
||||
return { channels, availableLanguages };
|
||||
}
|
||||
|
||||
function getChannels(options, database = null) {
|
||||
return getChannelPage(options, database).channels;
|
||||
}
|
||||
|
||||
function serializeChannels(rows, includeHidden = false) {
|
||||
@ -455,6 +467,7 @@ module.exports = {
|
||||
collectIdentityHints,
|
||||
collectOptionalIdentityHints,
|
||||
displayNameForFingerprint,
|
||||
getChannelPage,
|
||||
getChannels,
|
||||
identityCandidatePairs,
|
||||
normalizeChannelName,
|
||||
|
||||
Reference in New Issue
Block a user