Add a Settings action to manually rerun Live TV stream analysis: it triggers an admin-guarded projection rebuild endpoint that groups streams into logical channels (covering channels present before the projection existed) and reruns language/Laya detection, with progress presented via liveAnalysisPresentation. Harden the Laya batch path that failed with 'Local AI language matching failed' by surfacing the underlying cause and falling back to standard matching cleanly. Guard admin-only rebuild through a new authz helper and cover routes, service and presentation with tests. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
62 lines
2.0 KiB
JavaScript
62 lines
2.0 KiB
JavaScript
const LIVE_PROJECTION_COLUMNS = {
|
|
logical_channel_id: 'TEXT',
|
|
grouping_fingerprint: 'TEXT',
|
|
grouping_input_hash: 'TEXT',
|
|
quality_label: 'TEXT',
|
|
quality_rank: 'INTEGER',
|
|
variant_rank: 'INTEGER',
|
|
language_code: 'TEXT',
|
|
language_confidence: 'REAL',
|
|
language_scores: 'JSON',
|
|
language_evidence: 'JSON',
|
|
language_detector: 'TEXT',
|
|
language_input_hash: 'TEXT'
|
|
};
|
|
|
|
const CATALOG_PROJECTION_STATE_COLUMNS = {
|
|
processed_count: 'INTEGER NOT NULL DEFAULT 0',
|
|
total_count: 'INTEGER NOT NULL DEFAULT 0'
|
|
};
|
|
|
|
const LIVE_PROJECTION_SCHEMA_SQL = `
|
|
CREATE INDEX IF NOT EXISTS idx_items_logical_channel ON playlist_items(logical_channel_id);
|
|
CREATE INDEX IF NOT EXISTS idx_items_grouping_fingerprint ON playlist_items(grouping_fingerprint);
|
|
|
|
CREATE TABLE IF NOT EXISTS logical_channels (
|
|
id TEXT PRIMARY KEY,
|
|
fingerprint TEXT NOT NULL UNIQUE,
|
|
display_name TEXT NOT NULL,
|
|
preferred_variant_id TEXT,
|
|
variant_count INTEGER NOT NULL DEFAULT 0,
|
|
created_at INTEGER NOT NULL,
|
|
updated_at INTEGER NOT NULL
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_logical_channels_fingerprint ON logical_channels(fingerprint);
|
|
|
|
CREATE TABLE IF NOT EXISTS catalog_projection_state (
|
|
projection TEXT PRIMARY KEY,
|
|
status TEXT NOT NULL,
|
|
started_at INTEGER,
|
|
completed_at INTEGER,
|
|
input_hash TEXT,
|
|
projected_count INTEGER NOT NULL DEFAULT 0,
|
|
processed_count INTEGER NOT NULL DEFAULT 0,
|
|
total_count INTEGER NOT NULL DEFAULT 0,
|
|
error TEXT
|
|
);
|
|
`;
|
|
|
|
function ensureColumns(database, table, columns) {
|
|
const existing = new Set(database.prepare(`PRAGMA table_info(${table})`).all().map(column => column.name));
|
|
for (const [name, definition] of Object.entries(columns)) {
|
|
if (!existing.has(name)) database.exec(`ALTER TABLE ${table} ADD COLUMN ${name} ${definition}`);
|
|
}
|
|
}
|
|
|
|
module.exports = {
|
|
CATALOG_PROJECTION_STATE_COLUMNS,
|
|
ensureColumns,
|
|
LIVE_PROJECTION_COLUMNS,
|
|
LIVE_PROJECTION_SCHEMA_SQL
|
|
};
|