From ce11ecc757e5aff3ba81a1d5af96d84346f89f0a Mon Sep 17 00:00:00 2001 From: blomios Date: Fri, 2 Oct 2026 11:13:34 +0200 Subject: [PATCH] feat: add optional Laya engine mode for grouping and language detection (#12) 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 --- Dockerfile.laya | 23 +++ docker-compose.yml | 15 ++ public/css/main.css | 65 +++++++ public/index.html | 43 +++- public/js/api.js | 7 + public/js/components/ChannelList.js | 60 ++++++ public/js/components/VideoPlayer.js | 1 + public/js/pages/Settings.js | 95 +++++++++ public/js/services/channelLanguageFusion.js | 83 ++++++++ public/js/services/channelVariantGrouping.js | 19 +- server/db.js | 1 + server/db/sqlite.js | 38 +++- server/index.js | 1 + server/routes/laya.js | 42 ++++ server/services/layaService.js | 195 +++++++++++++++++++ test/channelLanguageFusion.test.js | 41 ++++ test/channelVariantGrouping.test.js | 16 ++ test/layaService.test.js | 44 +++++ test/settingsDefaults.test.js | 4 + 19 files changed, 780 insertions(+), 13 deletions(-) create mode 100644 Dockerfile.laya create mode 100644 public/js/services/channelLanguageFusion.js create mode 100644 server/routes/laya.js create mode 100644 server/services/layaService.js create mode 100644 test/channelLanguageFusion.test.js create mode 100644 test/layaService.test.js diff --git a/Dockerfile.laya b/Dockerfile.laya new file mode 100644 index 0000000..d1e380b --- /dev/null +++ b/Dockerfile.laya @@ -0,0 +1,23 @@ +FROM python:3.12-slim + +ARG TORCH_VERSION=2.14.1 + +# Install the CPU wheel explicitly before Laya so pip does not select the much +# larger CUDA build. Model weights remain runtime-only in the mounted cache. +RUN python -m pip install --no-cache-dir \ + "torch==${TORCH_VERSION}" \ + --index-url https://download.pytorch.org/whl/cpu \ + && python -m pip install --no-cache-dir "laya[serve]>=0.3.22,<0.4" \ + && python -m pip check \ + && python -c "import torch; assert torch.version.cuda is None, torch.version.cuda" + +ENV HF_HOME=/models \ + LAYA_HOST=0.0.0.0 \ + LAYA_PORT=8000 \ + LAYA_PRELOAD=0 \ + LAYA_DEVICE=cpu + +VOLUME ["/models"] +EXPOSE 8000 + +CMD ["laya-serve"] diff --git a/docker-compose.yml b/docker-compose.yml index b088f7d..8231126 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -13,5 +13,20 @@ services: environment: - NODE_ENV=production - PORT=3000 # Internal container port + - LAYA_BASE_URL=http://laya:8000 devices: - /dev/dri:/dev/dri # Required for VAAPI/QuickSync/AMF (Linux) + + laya: + profiles: ["laya"] + build: + context: . + dockerfile: Dockerfile.laya + volumes: + - ./data/models/laya:/models + environment: + - HF_HOME=/models + - LAYA_PRELOAD=0 + - LAYA_HOST=0.0.0.0 + - LAYA_PORT=8000 + restart: unless-stopped diff --git a/public/css/main.css b/public/css/main.css index 32f45a1..740d055 100644 --- a/public/css/main.css +++ b/public/css/main.css @@ -725,6 +725,71 @@ h4 { white-space: nowrap; } +.channel-matching-settings { + margin-top: var(--space-lg); + padding-top: var(--space-md); + border-top: 1px solid var(--color-border); +} + +.matching-mode-options { + display: grid; + gap: var(--space-sm); + margin: var(--space-md) 0; +} + +.matching-mode-card { + display: flex; + align-items: flex-start; + gap: var(--space-sm); + padding: var(--space-md); + border: 1px solid var(--color-border); + border-radius: var(--radius-sm); + cursor: pointer; +} + +.matching-mode-card:has(input:checked) { + border-color: var(--color-accent); + background: var(--color-accent-dim); +} + +.matching-mode-card span, +.matching-mode-card small { + display: block; +} + +.matching-mode-card small { + margin-top: 4px; + color: var(--color-text-muted); +} + +.laya-model-panel { + padding: var(--space-md); + border-radius: var(--radius-sm); + background: var(--color-bg-tertiary); +} + +.laya-model-heading, +.laya-model-actions { + display: flex; + align-items: center; + justify-content: space-between; + gap: var(--space-sm); +} + +.laya-model-actions { + justify-content: flex-start; + margin: var(--space-md) 0; +} + +.laya-language-notice { + margin: var(--space-sm) var(--space-md); + padding: var(--space-sm); + border-radius: var(--radius-sm); + background: var(--color-bg-tertiary); + color: var(--color-text-muted); + font-size: .75rem; +} + .variant-name { overflow: hidden; text-overflow: ellipsis; diff --git a/public/index.html b/public/index.html index c65bf4e..47dfdea 100644 --- a/public/index.html +++ b/public/index.html @@ -10,7 +10,7 @@ nodecast-tv | IPTV Player - + Turn off to show all channels in a single list. Stream variants remain grouped by channel. +
+

Channel matching

+

Choose how Nodecast TV identifies matching streams and languages.

+
+ + + +
+
+
+
Local AI model · Laya
Checking status…
+ +
+
+
+ + +
+

The model runs on this server. Channel names and metadata are not sent to an AI service.

+
+
@@ -1137,11 +1167,12 @@ - - + + - - + + + @@ -1149,7 +1180,7 @@ - +