fix: track Laya language batches and normalize labels (#22)
Report post-projection Laya language detection as a busy analysis phase and normalize Laya language labels before caching and display. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -92,6 +92,18 @@ function projectedChannels() {
|
||||
};
|
||||
}
|
||||
|
||||
function waitFor(condition, message) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const started = Date.now();
|
||||
const poll = () => {
|
||||
if (condition()) return resolve();
|
||||
if (Date.now() - started > 1500) return reject(new Error(message));
|
||||
setTimeout(poll, 5);
|
||||
};
|
||||
poll();
|
||||
});
|
||||
}
|
||||
|
||||
test('Settings renders Laya batch progress and removes misleading values when its total is unknown', () => {
|
||||
const { window } = createDom();
|
||||
const settingsPage = Object.create(window.SettingsPage.prototype);
|
||||
@ -129,6 +141,67 @@ test('Settings renders Laya batch progress and removes misleading values when it
|
||||
assert.equal(bar.style.width, '');
|
||||
});
|
||||
|
||||
test('analysis remains visibly busy while Laya language batches run after projection completes', async () => {
|
||||
const { window } = createDom();
|
||||
let rebuildClicked = false;
|
||||
let languageRefreshStarted = false;
|
||||
let releaseLanguageRefresh;
|
||||
const languageRefresh = new Promise(resolve => { releaseLanguageRefresh = resolve; });
|
||||
window.API = {
|
||||
live: {
|
||||
getProjectionStatus: async () => rebuildClicked
|
||||
? { status: 'success', phase: 'complete', processed: 128, total: 128, projectedCount: 64 }
|
||||
: { status: 'idle', processed: 0, total: 128 },
|
||||
rebuildProjection: async () => {
|
||||
rebuildClicked = true;
|
||||
return {
|
||||
projection: {
|
||||
status: 'building',
|
||||
phase: 'laya_identity',
|
||||
layaProcessed: 4,
|
||||
layaTotal: 4,
|
||||
processed: 0,
|
||||
total: 128
|
||||
}
|
||||
};
|
||||
}
|
||||
},
|
||||
laya: {
|
||||
getStatus: async () => ({ modelPresent: true, runtimeAvailable: true })
|
||||
}
|
||||
};
|
||||
const app = {
|
||||
player: {
|
||||
settings: { languageDetectionMode: 'laya' },
|
||||
saveSettings() {}
|
||||
},
|
||||
channelList: {
|
||||
loadChannels: async () => {},
|
||||
refreshLayaLanguages: async () => {
|
||||
languageRefreshStarted = true;
|
||||
await languageRefresh;
|
||||
}
|
||||
}
|
||||
};
|
||||
window.app = app;
|
||||
const settingsPage = Object.create(window.SettingsPage.prototype);
|
||||
settingsPage.app = app;
|
||||
settingsPage.layaStatus = { modelPresent: true, runtimeAvailable: true };
|
||||
|
||||
settingsPage.initLiveAnalysis();
|
||||
await Promise.resolve();
|
||||
window.document.getElementById('live-analysis-button').click();
|
||||
|
||||
try {
|
||||
await waitFor(() => languageRefreshStarted, 'Expected Laya language refresh to start after projection completion');
|
||||
assert.notEqual(window.document.getElementById('live-analysis-status').textContent, 'Analysis complete');
|
||||
assert.equal(window.document.getElementById('live-analysis-progress').hidden, false);
|
||||
} finally {
|
||||
releaseLanguageRefresh();
|
||||
window.clearInterval(settingsPage.liveAnalysisPollTimer);
|
||||
}
|
||||
});
|
||||
|
||||
test('user analysis click keeps visible channels after Local AI language failure', async () => {
|
||||
const { window } = createDom();
|
||||
let rebuildClicked = 0;
|
||||
@ -348,6 +421,53 @@ test('successful Laya language batch updates visible variant languages without S
|
||||
assert.equal(window.document.querySelector('.laya-language-notice'), null);
|
||||
});
|
||||
|
||||
test('usable Laya language labels do not leave visible variants as unknown', async () => {
|
||||
const { window } = createDom();
|
||||
window.API = {
|
||||
live: {
|
||||
getChannels: async () => projectedChannels()
|
||||
},
|
||||
laya: {
|
||||
getStatus: async () => ({ modelPresent: true, runtimeAvailable: true }),
|
||||
detectLanguages: async items => ({
|
||||
runtimeAvailable: true,
|
||||
model: 'multilingual',
|
||||
results: items.map(item => ({
|
||||
id: item.id,
|
||||
language: item.id.endsWith('fr-sd') ? 'French' : 'English',
|
||||
confidence: 0.91
|
||||
}))
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
const app = {
|
||||
player: {
|
||||
settings: { liveChannelListMode: 'grouped', languageDetectionMode: 'laya' },
|
||||
saveSettings() {}
|
||||
},
|
||||
epgGuide: null,
|
||||
navigateTo() {},
|
||||
pages: { settings: { switchTab() {} } }
|
||||
};
|
||||
window.app = app;
|
||||
const channelList = new window.ChannelList();
|
||||
channelList.sources = [{ id: 1, type: 'm3u', name: 'Fixture Provider' }];
|
||||
app.channelList = channelList;
|
||||
|
||||
await channelList.loadChannels();
|
||||
await channelList.refreshLayaLanguages('laya');
|
||||
channelList.expandAll();
|
||||
|
||||
const badges = [...window.document.querySelectorAll('.variant-language')].map(item => item.textContent);
|
||||
|
||||
window.clearInterval(channelList._epgRefreshTimer);
|
||||
|
||||
assert.deepEqual([...new Set(badges)].sort(), ['EN 91%', 'FR 91%']);
|
||||
assert.equal(channelList.languageDetectionFallback, false);
|
||||
assert.equal(channelList.languageDetectionNotice, '');
|
||||
});
|
||||
|
||||
test('analysis click does not replace visible channels with an empty successful projection', async () => {
|
||||
const { window } = createDom();
|
||||
let rebuildClicked = 0;
|
||||
|
||||
Reference in New Issue
Block a user