diff --git a/public/css/main.css b/public/css/main.css
index e0bf2fe..2c41682 100644
--- a/public/css/main.css
+++ b/public/css/main.css
@@ -768,6 +768,10 @@ h4 {
background: var(--color-bg-tertiary);
}
+.laya-model-panel [hidden] {
+ display: none !important;
+}
+
.laya-model-heading,
.laya-model-actions {
display: flex;
diff --git a/public/index.html b/public/index.html
index d7f983d..bb4c2d2 100644
--- a/public/index.html
+++ b/public/index.html
@@ -10,7 +10,7 @@
nodecast-tv | IPTV Player
-
+
-
+
-
+
diff --git a/public/js/components/ChannelList.js b/public/js/components/ChannelList.js
index 6ee47a7..d985acd 100644
--- a/public/js/components/ChannelList.js
+++ b/public/js/components/ChannelList.js
@@ -363,14 +363,19 @@ class ChannelList {
const results = new Map();
try {
const status = await API.laya.getStatus();
- if (!status?.modelPresent || !status?.runtimeAvailable) {
+ const statusWarning = LayaStatusPresentation.channelListWarning(mode, status);
+ if (statusWarning) {
if (requestId !== this._languageDetectionRequest) return;
this.layaLanguageResults.clear();
this.languageDetectionFallback = true;
- this.languageDetectionNotice = 'Local AI service is unavailable. Standard channel matching is being used.';
+ this.languageDetectionNotice = statusWarning;
this.render();
return;
}
+ if (requestId !== this._languageDetectionRequest) return;
+ this.languageDetectionNotice = '';
+ this.languageDetectionFallback = false;
+ this.render();
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));
@@ -383,7 +388,7 @@ class ChannelList {
if (requestId !== this._languageDetectionRequest) return;
this.layaLanguageResults.clear();
this.languageDetectionFallback = true;
- this.languageDetectionNotice = 'Local AI service is unavailable. Standard channel matching is being used.';
+ this.languageDetectionNotice = LayaStatusPresentation.channelListWarning(mode, {}, true);
console.warn('[ChannelList] Local AI language detection unavailable:', err.message);
}
this.render();
@@ -442,6 +447,7 @@ class ChannelList {
window.app?.navigateTo?.('settings');
window.app?.pages?.settings?.switchTab?.('player');
});
+ notice.appendChild(document.createTextNode(' '));
notice.appendChild(settingsButton);
this.container.appendChild(notice);
}
diff --git a/public/js/services/layaStatusPresentation.js b/public/js/services/layaStatusPresentation.js
index 86327fe..e846bdf 100644
--- a/public/js/services/layaStatusPresentation.js
+++ b/public/js/services/layaStatusPresentation.js
@@ -77,5 +77,13 @@
};
}
- return { presentLayaStatus };
+ function channelListWarning(mode, status = {}, batchFailed = false) {
+ if (mode === 'algo') return '';
+ if (batchFailed || status.modelPresent !== true || status.runtimeAvailable !== true) {
+ return 'Local AI service is unavailable. Standard channel matching is being used.';
+ }
+ return '';
+ }
+
+ return { channelListWarning, presentLayaStatus };
});
diff --git a/test/layaStatusPresentation.test.js b/test/layaStatusPresentation.test.js
index 685a7bf..e216067 100644
--- a/test/layaStatusPresentation.test.js
+++ b/test/layaStatusPresentation.test.js
@@ -1,6 +1,6 @@
const test = require('node:test');
const assert = require('node:assert/strict');
-const { presentLayaStatus } = require('../public/js/services/layaStatusPresentation.js');
+const { channelListWarning, presentLayaStatus } = require('../public/js/services/layaStatusPresentation.js');
test('presents unknown-size downloads as indeterminate with downloaded bytes', () => {
const view = presentLayaStatus({
@@ -37,9 +37,25 @@ test('failed downloads expose retry, safe details and Standard fallback', () =>
});
test('ready models expose removal without download actions', () => {
- const view = presentLayaStatus({ phase: 'ready', modelPresent: true, runtimeAvailable: true });
+ const view = presentLayaStatus({ phase: 'idle', modelPresent: true, runtimeAvailable: true, sizeBytes: 678323814 });
+ assert.equal(view.statusLabel, 'Ready');
assert.equal(view.showRemove, true);
assert.equal(view.showDownload, false);
+ assert.equal(view.showCheckAgain, false);
+ assert.equal(view.showStartCommand, false);
+ assert.equal(view.availabilityMessage, '');
+});
+
+test('ChannelList warning only appears for active Laya modes when unavailable or failed', () => {
+ const ready = { modelPresent: true, runtimeAvailable: true };
+ assert.equal(channelListWarning('hybrid', ready), '');
+ assert.equal(channelListWarning('laya', ready), '');
+ assert.equal(channelListWarning('algo', { modelPresent: false, runtimeAvailable: false }), '');
+ assert.equal(
+ channelListWarning('hybrid', { modelPresent: true, runtimeAvailable: false }),
+ 'Local AI service is unavailable. Standard channel matching is being used.'
+ );
+ assert.equal(channelListWarning('laya', ready, true), 'Local AI service is unavailable. Standard channel matching is being used.');
});
test('installed model with unavailable service is not reported ready', () => {