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>
49 lines
2.3 KiB
JavaScript
49 lines
2.3 KiB
JavaScript
(function (root, factory) {
|
|
const service = factory();
|
|
if (typeof module === 'object' && module.exports) module.exports = service;
|
|
if (root) root.LiveAnalysisPresentation = service;
|
|
})(typeof globalThis !== 'undefined' ? globalThis : this, function () {
|
|
const BUSY_STATUSES = new Set(['queued', 'building', 'running']);
|
|
const FAILED_STATUSES = new Set(['error', 'failed']);
|
|
|
|
function present(status = {}) {
|
|
const state = String(status.status || 'idle').toLowerCase();
|
|
const busy = BUSY_STATUSES.has(state);
|
|
const failed = FAILED_STATUSES.has(state);
|
|
const complete = state === 'success';
|
|
const processed = Number(status.processed);
|
|
const total = Number(status.total);
|
|
const determined = busy && status.processed !== null && status.processed !== undefined &&
|
|
status.total !== null && status.total !== undefined &&
|
|
Number.isFinite(processed) && Number.isFinite(total) && total > 0;
|
|
const progressPercent = determined
|
|
? Math.max(0, Math.min(100, Math.round(processed / total * 100)))
|
|
: null;
|
|
const previouslyCompleted = Boolean(status.completedAt) || Number(status.projectedCount) > 0;
|
|
|
|
return {
|
|
state,
|
|
busy,
|
|
complete,
|
|
failed,
|
|
statusLabel: busy ? 'Analyzing channels…' : complete ? 'Analysis complete' : failed ? 'Analysis failed' : 'Ready to analyze',
|
|
buttonLabel: failed ? 'Try again' : complete || previouslyCompleted ? 'Analyze again' : 'Analyze channels',
|
|
progressIndeterminate: busy && progressPercent === null,
|
|
progressPercent,
|
|
progressText: progressPercent === null
|
|
? (busy ? 'Analysis in progress…' : '')
|
|
: `${processed} of ${total} · ${progressPercent}%`,
|
|
errorDetail: failed && status.error ? String(status.error) : ''
|
|
};
|
|
}
|
|
|
|
function layaFallbackMessage(mode, layaStatus = {}) {
|
|
if (!['laya', 'hybrid'].includes(mode)) return '';
|
|
return layaStatus.modelPresent === false || layaStatus.runtimeAvailable === false
|
|
? 'Local AI is unavailable. Standard channel matching will be used for this analysis.'
|
|
: '';
|
|
}
|
|
|
|
return { layaFallbackMessage, present };
|
|
});
|