Add a channelLanguageDetection service that scores language evidence per IPTV variant (country/language prefixes in name and group, source metadata, original title words) and picks the highest-scored language. Integrate detected languages into channel variant grouping and add a language selector to the live player to switch to a variant of the logical channel in the desired language, keeping raw streams non-destructive. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
67 lines
2.9 KiB
JavaScript
67 lines
2.9 KiB
JavaScript
const test = require('node:test');
|
|
const assert = require('node:assert/strict');
|
|
const detection = require('../public/js/services/channelLanguageDetection.js');
|
|
const grouping = require('../public/js/services/channelVariantGrouping.js');
|
|
|
|
const channel = (id, name, extra = {}) => ({
|
|
id, streamId: id, sourceId: 1, name, groupTitle: 'Sports', ...extra
|
|
});
|
|
|
|
test('scores explicit French prefixes with strong confidence', () => {
|
|
for (const name of ['|FR| BEIN SPORTS 1 UHD', 'FR-CAR| bein sports 1']) {
|
|
const result = detection.detectLanguage(channel(name, name));
|
|
assert.equal(result.language.code, 'fr');
|
|
assert.match(result.language.status, /medium|high/);
|
|
assert.ok(result.languageScores.fr >= 75);
|
|
assert.ok(result.languageEvidence.some(item => item.type === 'name-prefix'));
|
|
}
|
|
});
|
|
|
|
test('maps UK group evidence to English with medium confidence', () => {
|
|
const result = detection.detectLanguage(channel('1', 'Sports One', { groupTitle: 'UK Sports' }));
|
|
assert.equal(result.language.code, 'en');
|
|
assert.equal(result.language.status, 'medium');
|
|
});
|
|
|
|
test('uses a France source as weak French evidence', () => {
|
|
const result = detection.detectLanguage(channel('1', 'Sports One'), { sourceName: 'France IPTV' });
|
|
assert.equal(result.language.code, 'fr');
|
|
assert.equal(result.language.status, 'low');
|
|
});
|
|
|
|
test('does not force multilingual BE to a language', () => {
|
|
const result = detection.detectLanguage(channel('1', 'BE Sports 1'));
|
|
assert.equal(result.language.code, 'unknown');
|
|
});
|
|
|
|
test('classifies contradictory close evidence as unknown', () => {
|
|
const result = detection.detectLanguage(channel('1', 'French English Sports'));
|
|
assert.equal(result.language.code, 'unknown');
|
|
assert.equal(result.language.status, 'unknown');
|
|
});
|
|
|
|
test('global and per-channel language preferences resolve tied variants', () => {
|
|
const variants = [channel('fr', '|FR| Sports One HD'), channel('en', '|UK| Sports One HD')];
|
|
const globalEnglish = grouping.groupChannels(variants, { preferredLanguage: 'en' })[0];
|
|
assert.equal(globalEnglish.preferredVariant.id, 'en');
|
|
|
|
const perChannel = grouping.groupChannels(variants, {
|
|
preferredLanguage: 'en',
|
|
languagePreferences: new Map([['sports one', 'fr']])
|
|
})[0];
|
|
assert.equal(perChannel.preferredVariant.id, 'fr');
|
|
});
|
|
|
|
test('explicit language preference beats quality and hidden variants are ignored', () => {
|
|
const frenchSd = channel('fr', '|FR| Sports One SD');
|
|
const english4k = channel('en', '|UK| Sports One 4K');
|
|
const preferredFrench = grouping.groupChannels([frenchSd, english4k], { preferredLanguage: 'fr' })[0];
|
|
assert.equal(preferredFrench.preferredVariant.id, 'fr');
|
|
|
|
const hiddenFrench = grouping.groupChannels([frenchSd, english4k], {
|
|
preferredLanguage: 'fr',
|
|
isHidden: variant => variant.id === 'fr'
|
|
})[0];
|
|
assert.equal(hiddenFrench.preferredVariant.id, 'en');
|
|
});
|