Add a languageDetectionMode setting (algo | laya | laya+algo) exposed in Settings with three engine choices. When Laya is enabled, a laya service (Dockerfile.laya, optional compose profile, models stored outside the repo under ./data/models/laya) can be started, with model download and deletion from the UI via /laya routes to manage disk space. Add a channelLanguageFusion service combining Laya results with the existing algorithm, and wire detection mode through grouping, channel list and player. Cover engine selection, fusion and laya service behavior with tests. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
84 lines
3.8 KiB
JavaScript
84 lines
3.8 KiB
JavaScript
(function (root, factory) {
|
|
const service = factory();
|
|
if (typeof module === 'object' && module.exports) module.exports = service;
|
|
if (root) root.ChannelLanguageFusion = service;
|
|
})(typeof globalThis !== 'undefined' ? globalThis : this, function () {
|
|
const LABELS = { fr: 'French', en: 'English', es: 'Spanish', unknown: 'Unknown' };
|
|
|
|
function normalizeConfidence(value) {
|
|
const number = Number(value) || 0;
|
|
return Math.max(0, Math.min(100, Math.round(number <= 1 ? number * 100 : number)));
|
|
}
|
|
|
|
function layaDetection(result) {
|
|
const rawCode = result?.language?.code || result?.language || 'unknown';
|
|
const code = ['fr', 'en', 'es'].includes(rawCode) ? rawCode : 'unknown';
|
|
const confidence = normalizeConfidence(result?.language?.confidence ?? result?.confidence);
|
|
return {
|
|
language: {
|
|
code,
|
|
label: LABELS[code],
|
|
confidence: code === 'unknown' ? 0 : confidence,
|
|
status: code === 'unknown' ? 'unknown' : confidence >= 75 ? 'high' : confidence >= 60 ? 'medium' : 'low'
|
|
},
|
|
languageScores: code === 'unknown' ? {} : { [code]: confidence },
|
|
languageEvidence: code === 'unknown' ? [] : [{ code, weight: confidence, type: 'laya', value: 'Local AI model' }]
|
|
};
|
|
}
|
|
|
|
function mergeHybrid(algo, result) {
|
|
if (!result) return algo;
|
|
const laya = layaDetection(result);
|
|
if (laya.language.code === 'unknown') return {
|
|
...algo,
|
|
languageEvidence: [...algo.languageEvidence, ...laya.languageEvidence]
|
|
};
|
|
const combined = { ...algo.languageScores };
|
|
combined[laya.language.code] = (combined[laya.language.code] || 0) + laya.language.confidence;
|
|
const total = Object.values(combined).reduce((sum, value) => sum + value, 0);
|
|
const scores = Object.fromEntries(Object.entries(combined).map(([code, value]) => [code, Math.round(value / total * 100)]));
|
|
const ranked = Object.entries(scores).sort((a, b) => b[1] - a[1]);
|
|
const [code, confidence] = ranked[0];
|
|
const margin = confidence - (ranked[1]?.[1] || 0);
|
|
const certain = confidence >= 45 && margin >= 10;
|
|
return {
|
|
language: certain ? {
|
|
code,
|
|
label: LABELS[code] || code.toUpperCase(),
|
|
confidence,
|
|
status: confidence >= 75 ? 'high' : confidence >= 60 ? 'medium' : 'low'
|
|
} : { code: 'unknown', label: 'Unknown', confidence: 0, status: 'unknown' },
|
|
languageScores: scores,
|
|
languageEvidence: [...algo.languageEvidence, ...laya.languageEvidence]
|
|
};
|
|
}
|
|
|
|
function applyLanguageMode(algo, result, mode) {
|
|
if (mode === 'algo') return algo;
|
|
if (mode === 'hybrid') return mergeHybrid(algo, result);
|
|
return result ? layaDetection(result) : {
|
|
language: { code: 'unknown', label: 'Unknown', confidence: 0, status: 'unknown' },
|
|
languageScores: {},
|
|
languageEvidence: []
|
|
};
|
|
}
|
|
|
|
function buildLayaDescriptor(variant, variantKey, algo) {
|
|
const fields = [
|
|
['sourceId', variant.sourceId],
|
|
['name', variant.name],
|
|
['groupTitle', variant.groupTitle],
|
|
['sourceName', variant.sourceName],
|
|
['tvgId', variant.tvgId],
|
|
['algorithmScores', JSON.stringify(algo.languageScores || {})]
|
|
];
|
|
return {
|
|
id: variantKey,
|
|
text: fields.filter(([, value]) => value !== undefined && value !== null && value !== '')
|
|
.map(([key, value]) => `${key}: ${value}`).join('\n')
|
|
};
|
|
}
|
|
|
|
return { applyLanguageMode, buildLayaDescriptor, layaDetection, mergeHybrid, normalizeConfidence };
|
|
});
|