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:
blomios
2026-10-05 23:42:11 +02:00
parent 45b56d872f
commit a6759b116d
9 changed files with 282 additions and 21 deletions

View File

@ -344,7 +344,7 @@ class ChannelList {
}
}
async refreshLayaLanguages(mode = window.app?.player?.settings?.languageDetectionMode || 'algo') {
async refreshLayaLanguages(mode = window.app?.player?.settings?.languageDetectionMode || 'algo', options = {}) {
const requestId = ++this._languageDetectionRequest;
if (mode === 'algo') {
const hadWarning = Boolean(this.languageDetectionNotice || this.languageDetectionFallback);
@ -364,6 +364,7 @@ class ChannelList {
);
});
const results = new Map();
const totalBatches = Math.ceil(descriptors.length / 64);
try {
const status = await API.laya.getStatus();
const statusWarning = LayaStatusPresentation.channelListWarning(mode, status);
@ -379,9 +380,15 @@ class ChannelList {
this.languageDetectionNotice = '';
this.languageDetectionFallback = false;
this.render();
options.onProgress?.({ processed: 0, total: totalBatches });
for (let index = 0; index < descriptors.length; index += 64) {
const response = await API.laya.detectLanguages(descriptors.slice(index, index + 64));
(response.results || []).forEach(result => results.set(String(result.id), result));
if (requestId !== this._languageDetectionRequest) return;
options.onProgress?.({
processed: Math.floor(index / 64) + 1,
total: totalBatches
});
}
if (requestId !== this._languageDetectionRequest) return;
if (descriptors.length > 0 && results.size === 0) {
@ -904,7 +911,7 @@ class ChannelList {
/**
* Load channels from selected source
*/
async loadChannels() {
async loadChannels({ refreshLanguages = true } = {}) {
if (this.isLoading) return;
this.isLoading = true;
this.currentRenderId = null; // Reset render tracking
@ -921,7 +928,7 @@ class ChannelList {
if (!sourceValue) {
// Load from all sources
await this.loadAllChannels();
await this.loadAllChannels({ refreshLanguages });
this.isLoading = false;
return;
}
@ -933,7 +940,7 @@ class ChannelList {
if (await this.loadProjectedChannels(parseInt(id, 10))) {
this.render();
void this.refreshLayaLanguages();
if (refreshLanguages) void this.refreshLayaLanguages();
return;
}
@ -950,7 +957,7 @@ class ChannelList {
]);
this.render();
void this.refreshLayaLanguages();
if (refreshLanguages) void this.refreshLayaLanguages();
} catch (err) {
console.error('Error loading channels:', err);
if (previousState.channels.length > 0) {
@ -970,7 +977,7 @@ class ChannelList {
/**
* Load channels from all enabled sources
*/
async loadAllChannels() {
async loadAllChannels({ refreshLanguages = true } = {}) {
const previousState = {
channels: this.channels,
groups: this.groups,
@ -985,7 +992,7 @@ class ChannelList {
if (await this.loadProjectedChannels()) {
this.render();
void this.refreshLayaLanguages();
if (refreshLanguages) void this.refreshLayaLanguages();
return;
}
@ -1006,7 +1013,7 @@ class ChannelList {
this.loadFavorites()
]);
this.render();
void this.refreshLayaLanguages();
if (refreshLanguages) void this.refreshLayaLanguages();
} catch (err) {
console.error('Error loading all channels:', err);
if (previousState.channels.length > 0) {

View File

@ -197,11 +197,32 @@ class SettingsPage {
clearInterval(this.liveAnalysisPollTimer);
this.liveAnalysisPollTimer = null;
if (presentation.complete) {
await this.app.channelList?.loadChannels();
const mode = this.app.player?.settings?.languageDetectionMode || 'algo';
await this.app.channelList?.loadChannels({
refreshLanguages: mode !== 'laya' && mode !== 'hybrid'
});
if (mode === 'laya' || mode === 'hybrid') {
await this.app.channelList?.refreshLayaLanguages(mode);
this.liveAnalysisStatus = {
status: 'building',
phase: 'laya_language',
languageProcessed: null,
languageTotal: null
};
this.renderLiveAnalysisStatus(this.liveAnalysisStatus);
await this.app.channelList?.refreshLayaLanguages(mode, {
onProgress: ({ processed, total }) => {
this.liveAnalysisStatus = {
status: 'building',
phase: 'laya_language',
languageProcessed: processed,
languageTotal: total
};
this.renderLiveAnalysisStatus(this.liveAnalysisStatus);
}
});
}
this.liveAnalysisStatus = status;
this.renderLiveAnalysisStatus(status);
}
}
} finally {

View File

@ -10,9 +10,19 @@
return Math.max(0, Math.min(100, Math.round(number <= 1 ? number * 100 : number)));
}
function normalizeLanguageCode(value) {
const normalized = String(value || '').trim().toLowerCase();
const aliases = {
fr: 'fr', french: 'fr', français: 'fr', francais: 'fr',
en: 'en', english: 'en', anglais: 'en',
es: 'es', spanish: 'es', español: 'es', espanol: 'es'
};
return aliases[normalized] || 'unknown';
}
function layaDetection(result) {
const rawCode = result?.language?.code || result?.language || 'unknown';
const code = ['fr', 'en', 'es'].includes(rawCode) ? rawCode : 'unknown';
const rawCode = result?.language?.code || result?.language?.label || result?.language || 'unknown';
const code = normalizeLanguageCode(rawCode);
const confidence = normalizeConfidence(result?.language?.confidence ?? result?.confidence);
return {
language: {
@ -79,5 +89,5 @@
};
}
return { applyLanguageMode, buildLayaDescriptor, layaDetection, mergeHybrid, normalizeConfidence };
return { applyLanguageMode, buildLayaDescriptor, layaDetection, mergeHybrid, normalizeConfidence, normalizeLanguageCode };
});

View File

@ -14,6 +14,13 @@
prefix: 'AI batch'
};
}
if (phase === 'laya_language') {
return {
processed: status.languageProcessed,
total: status.languageTotal,
prefix: 'Language batch'
};
}
return { processed: status.processed, total: status.total, prefix: '', suffix: ' channels' };
}
@ -37,6 +44,8 @@
const statusLabel = busy
? phase === 'laya_identity'
? 'Analyzing with local AI…'
: phase === 'laya_language'
? 'Detecting stream languages…'
: phase === 'projecting'
? 'Building channel catalog…'
: phase === 'writing'