]> git.ipfire.org Git - thirdparty/tvheadend.git/log
thirdparty/tvheadend.git
5 days agoDisable Pcloud downloads in GH actions to rely on native cache fix-staticlib-cache-keys 2174/head
Flole [Tue, 14 Jul 2026 12:59:11 +0000 (14:59 +0200)] 
Disable Pcloud downloads in GH actions to rely on native cache

5 days agoFix staticlib cache keys referencing wrong matrix variables in build-cloudsmith.yml
Flole [Tue, 14 Jul 2026 12:54:28 +0000 (14:54 +0200)] 
Fix staticlib cache keys referencing wrong matrix variables in build-cloudsmith.yml

9 days agowebui: honour webroot (--http_root) in the Vue UI
Oliver Sluke [Thu, 9 Jul 2026 19:22:20 +0000 (21:22 +0200)] 
webui: honour webroot (--http_root) in the Vue UI

With a webroot configured the Vue UI blanked and looped, growing one
<webroot>/gui segment per redirect cycle: the bundle baked absolute
asset URLs and a /gui/ router base, and every server URL (api, comet,
play, images, login, ...) was hardcoded root-absolute — so behind a
webroot each of them missed, and the server's page_no_webroot catch-all
turned the misses into 302 cycles instead of clean failures. The server
side has always been ready: http_path_add registers every route under
tvheadend_webroot and http_redirect prefixes absolute targets.

Make the mount point a runtime discovery with one anchor:

- index.html carries <base href="/gui/static/">; vue.c rewrites the
  href to {webroot}/gui/static/ at serve time (the no-webroot path
  still streams the file untouched).
- The production Vite base becomes relative ('./'): index.html assets
  resolve through the base tag, lazy chunks via import.meta.url, so
  the bundle works at any mount point.
- New utils/base.ts derives the Vue Router base and the server root
  from document.baseURI, falling back to the root-mounted defaults
  when no matching <base> is present (dev server, tests).
- Every server URL construction goes through serverUrl(): the api
  client and comet poll choke points plus play/stream/dvrfile links,
  channel icons, static images, locale + marked loaders, markdown
  help fetches, login/logout, the Classic UI link, and the wizard
  finish redirect. The skip-link jumps via JS since a bare #fragment
  href would resolve against the base tag.

Also complete the webroot normalisation in main.c: "/" and "" left
tvheadend_webroot pointing at the raw value instead of NULL, which
prefixed every registered route with "/" and broke the whole HTTP
surface — both now normalise to no webroot.

A source-scan test fences regressions (new root-absolute server URLs
outside utils/base.ts fail the suite), unit tests cover the base
derivation, and a contract test pins the Vite base + index.html tag
the vue.c rewrite depends on.

Fixes #2167

Signed-off-by: Oliver Sluke <22557015+oliversluke@users.noreply.github.com>
10 days agowebui: keep the EPG viewport following "now" over long sessions
Oliver Sluke [Thu, 9 Jul 2026 08:40:05 +0000 (10:40 +0200)] 
webui: keep the EPG viewport following "now" over long sessions

The EPG scrolled to "now" only once, at mount. Left open for a few
hours the red now-cursor drifted across a frozen viewport while the
server expired already-aired programmes, leaving a growing empty gap
on the left with current/upcoming shows bunched near the drifted
cursor. Both Timeline and Magazine were affected (shared view state).

Add an opt-in "follow now": while the user is parked at the live edge,
a wall-clock tick re-pins the viewport so "now" stays at the leading
edge (left for Timeline, top for Magazine). Following starts on the
initial scroll-to-now, stops the moment the user scrolls away to
browse ahead, and re-engages via the existing Now button.

It lives in useEpgScrollDaySync, which already owns the scroll
listener, the Now button, and the intent-scroll suppression latch:
the re-pin routes through that latch so its own scroll emit isn't read
as a user scroll, and any unlatched scroll disables following. The
re-pin is driven by the visibility-aware nowEpoch tick and drift-gated
against the half-hour-snapped target, so it is a no-op between :30
boundaries and fires only when the boundary advances or the tab
regains focus after being hidden. trackStart stays frozen — only the
scroll offset moves.

Signed-off-by: Oliver Sluke <22557015+oliversluke@users.noreply.github.com>
10 days agowebui: activate PrimeVue dark palette under the Access theme
Oliver Sluke [Thu, 9 Jul 2026 06:32:21 +0000 (08:32 +0200)] 
webui: activate PrimeVue dark palette under the Access theme

PrimeVue's darkModeSelector was set to [data-theme="dark"], a theme
that was never shipped — the app's only dark theme is Access
([data-theme="access"]). So Aura's dark palette never activated, and
teleported overlays (Select dropdown, MultiSelect, Datepicker, menus,
the paginator rows-per-page popup) kept Aura's light surface — a white
panel on the dark Access page. Our own components themed correctly
because they use the --tvh-* tokens; the overlays use Aura's own
component tokens (e.g. select.overlay.background) that primevue.css
doesn't map, so they depended entirely on Aura's light/dark mode.

Point darkModeSelector at [data-theme="access"] so Aura switches to
its dark palette exactly when the Access theme is active; Blue and Gray
stay light. Refresh the now-stale comments in main.ts and the EPG event
drawer's surface override, which still described the old dark selector.

Signed-off-by: Oliver Sluke <22557015+oliversluke@users.noreply.github.com>
10 days agobuild: run the Vue UI pcloud steps only when PCLOUD_TOKEN is available
Pim Zandbergen [Wed, 8 Jul 2026 22:12:55 +0000 (00:12 +0200)] 
build: run the Vue UI pcloud steps only when PCLOUD_TOKEN is available

The 'Check pcloud' and 'Upload to pcloud' steps were gated only on
github.ref == 'refs/heads/master', which is also true on forks, where
the PCLOUD_TOKEN secret is absent and pcloud.py exits with an error,
failing the whole job.

Additionally, the check step never received the token at all, so
listfolder always failed silently (stderr discarded) and reported
'Missing from pcloud', causing a redundant re-upload on every
upstream master build.

Gate both steps on the secret being present and inject the token
into the check step as well.

12 days agobuild: cache staticlib locally in GH actions and intelligently upload to pcloud
Flole [Mon, 6 Jul 2026 22:47:53 +0000 (00:47 +0200)] 
build: cache staticlib locally in GH actions and intelligently upload to pcloud

12 days agobuild: improve GitHub Actions Vue UI caching and RPM builds
Flole [Tue, 7 Jul 2026 08:04:32 +0000 (10:04 +0200)] 
build: improve GitHub Actions Vue UI caching and RPM builds

12 days agobuild: integrate Vue UI build + pcloud-cache delivery
Oliver Sluke [Wed, 24 Jun 2026 18:05:07 +0000 (20:05 +0200)] 
build: integrate Vue UI build + pcloud-cache delivery

Build-system glue for the Vue admin UI shipped in the preceding
commit. Two delivery paths sit side-by-side:

- `--enable-vue_build` (default with node + npm on PATH) runs the
  local Vite/npm build during `make`. Configure auto-detects.

- `--enable-vue_cache` downloads a pre-built dist tarball from
  pcloud instead. Off by default; downstream packagers without a
  Node toolchain set `--disable-vue_build --enable-vue_cache` to
  ship the UI without building it. Requires `--enable-pcloud_cache`
  (default yes).

Either flag (or both) causes the dist to be bundled into the
binary.

Files
- `configure` adds the `vue_cache` option entry and a sanity
  check that errors if `vue_cache` is enabled without
  `pcloud_cache`.
- `Makefile` gains a `vue_cache_get` target that delegates to
  Makefile.webui-vue's `libcacheget`, plus a `BUNDLES-$(CONFIG_VUE_
  CACHE) += ...` clause so the dist is bundled whether it came
  from a local build or a cache fetch.
- `Makefile.webui-vue` mirrors the shape of Makefile.ffmpeg with
  `libcacheget` / `libcacheput` / `build` / `source-hash` targets.
  The tarball is named `webui-vue-<source-hash>.tgz` where the
  hash is sha256 over the inputs that actually affect the dist
  output (Vue src/ tree, package.json, package-lock.json,
  vite.config.ts, tsconfig.json, index.html). Tests and lint /
  format configs don't contribute.
- `TVHEADEND_FILE_CACHE` is honoured by `libcacheget` for
  reproducer / air-gapped builds, matching the same env-var
  support/lib.sh:DOWNLOAD already implements.
- New `.github/workflows/webui-vue-cache.yml` builds the UI on a
  Node-equipped runner and uploads the dist to pcloud. Triggers
  on push to master / webui/v2-vue (paths-filtered to the source
  tree) and on workflow_dispatch. A skip-if-exists check via
  `pcloud.py listfolder` avoids rebuilding when the source hash
  already has an artifact. The upload step is internally guarded
  by PCLOUD_USER/PASS being non-empty, so the workflow is safe
  to land before the org-level secrets are populated.
- `.github/workflows/build-ci.yml` and
  `.github/workflows/build-cloudsmith.yml` are untouched in this
  commit. Flipping them to `--enable-vue_cache` to consume the
  cached dist is a follow-up once the secrets are confirmed
  working — premature flipping would break their build before
  the artifacts exist.

`libcacheget` is fatal on miss (unlike Makefile.static which
falls through to local build). When the user explicitly picks
`--enable-vue_cache`, silently producing a binary without the UI
bundled would be worse than failing the build outright.

Verified locally with three configurations:
- `--enable-vue_build` (default): unchanged, npm-builds locally.
- `--disable-vue_build --enable-vue_cache` plus
  `TVHEADEND_FILE_CACHE` pointing at a pre-packed tarball: dist
  populates from the tarball, binary builds + bundles cleanly.
- `--enable-vue_cache` without `--enable-pcloud_cache`: configure
  errors with an actionable message.

Signed-off-by: Oliver Sluke <22557015+oliversluke@users.noreply.github.com>
12 days agowebui: new Vue 3 admin UI at /gui
Oliver Sluke [Wed, 24 Jun 2026 18:05:07 +0000 (20:05 +0200)] 
webui: new Vue 3 admin UI at /gui

A second admin UI served at /gui alongside the existing ExtJS UI
at /extjs.html. Built in Vue 3 + Pinia + PrimeVue 4 with the same
access contract (HTTP digest, same idnode permissions, same comet
notifications). The desktop root (/) now redirects to /gui by
default; the ExtJS UI keeps working unchanged and stays reachable
from the new UI's Classic UI menu entry until it is eventually
decommissioned. The simple.html redirect for legacy/WAP clients is
unchanged.

Scope of this commit covers the application layer only:

- The full Vue source tree under src/webui/static-vue/. Pinia
  stores, composables, view components, the EPG (Timeline /
  Magazine / Table) renderer, DVR views (Upcoming / Finished /
  AutoRec / TimeRec), Configuration tabs, the Setup Wizard port,
  Status views, the Help dialog, the i18n bridge over the
  existing /locale.js endpoint, error / toast plumbing, and the
  unit-test suite (Vitest, 2500+ tests).

- C-side plumbing in src/webui/vue.c (route registration for the
  /gui tree, mount via webui_init() in src/webui/webui.c). A
  minimal SPA-style fallback page is served when the dist tree
  isn't present, so the route stays functional even without the
  built UI.

- The build artifacts in src/webui/static-vue/package.json,
  package-lock.json, vite.config.ts, tsconfig.json, index.html,
  ESLint / Stylelint / Prettier configs, and the SPDX header
  enforcement script — everything the npm-side toolchain needs
  to produce the dist.

The companion build-system glue (configure flag, Makefile
recipes, Makefile.webui-vue, GitHub Actions workflow) lives in a
follow-on build: commit so the application and the infrastructure
review and bisect cleanly as separate units.

Signed-off-by: Oliver Sluke <22557015+oliversluke@users.noreply.github.com>
3 weeks agointl: Translate intl/tvheadend.pot in zh-Hans
transifex-integration[bot] [Tue, 23 Jun 2026 03:23:50 +0000 (03:23 +0000)] 
intl: Translate intl/tvheadend.pot in zh-Hans

100% translated source file: 'intl/tvheadend.pot'
on 'zh-Hans'.

3 weeks agointl: Translate tvheadend.js.pot in zh-Hans
transifex-integration[bot] [Tue, 23 Jun 2026 03:23:26 +0000 (03:23 +0000)] 
intl: Translate tvheadend.js.pot in zh-Hans

100% translated source file: 'tvheadend.js.pot'
on 'zh-Hans'.

3 weeks agointl: Translate tvheadend.doc.pot in zh-Hans
transifex-integration[bot] [Tue, 23 Jun 2026 03:23:13 +0000 (03:23 +0000)] 
intl: Translate tvheadend.doc.pot in zh-Hans

100% translated source file: 'tvheadend.doc.pot'
on 'zh-Hans'.

3 weeks agoUpdate Internationalisation Templates
Flole998 [Mon, 22 Jun 2026 03:11:03 +0000 (03:11 +0000)] 
Update Internationalisation Templates

3 weeks agointl: Translate intl/js/tvheadend.js.pot in zh
transifex-integration[bot] [Mon, 22 Jun 2026 17:07:31 +0000 (17:07 +0000)] 
intl: Translate intl/js/tvheadend.js.pot in zh

100% translated source file: 'intl/js/tvheadend.js.pot'
on 'zh'.

3 weeks agointl: Translate intl/tvheadend.pot in zh
transifex-integration[bot] [Mon, 22 Jun 2026 17:06:39 +0000 (17:06 +0000)] 
intl: Translate intl/tvheadend.pot in zh

100% translated source file: 'intl/tvheadend.pot'
on 'zh'.

3 weeks agointl: Translate tvheadend.doc.pot in zh-Hans
transifex-integration[bot] [Mon, 22 Jun 2026 17:06:10 +0000 (17:06 +0000)] 
intl: Translate tvheadend.doc.pot in zh-Hans

100% translated source file: 'tvheadend.doc.pot'
on 'zh-Hans'.

3 weeks agointl: Translate tvheadend.js.pot in zh-Hans
transifex-integration[bot] [Mon, 22 Jun 2026 17:04:36 +0000 (17:04 +0000)] 
intl: Translate tvheadend.js.pot in zh-Hans

100% translated source file: 'tvheadend.js.pot'
on 'zh-Hans'.

3 weeks agointl: Translate intl/tvheadend.pot in zh-Hans
transifex-integration[bot] [Mon, 22 Jun 2026 17:02:58 +0000 (17:02 +0000)] 
intl: Translate intl/tvheadend.pot in zh-Hans

100% translated source file: 'intl/tvheadend.pot'
on 'zh-Hans'.

4 weeks agointl: Translate intl/js/tvheadend.js.pot in pl
transifex-integration[bot] [Thu, 18 Jun 2026 20:32:51 +0000 (20:32 +0000)] 
intl: Translate intl/js/tvheadend.js.pot in pl

100% translated source file: 'intl/js/tvheadend.js.pot'
on 'pl'.

4 weeks agointl: Translate intl/js/tvheadend.js.pot in pl
transifex-integration[bot] [Thu, 18 Jun 2026 18:31:27 +0000 (18:31 +0000)] 
intl: Translate intl/js/tvheadend.js.pot in pl

99% of minimum 80% translated source file: 'intl/js/tvheadend.js.pot'
on 'pl'.

Sync of partially translated files:
untranslated content is included with an empty translation
or source language content depending on file format

4 weeks agointl: Translate intl/js/tvheadend.js.pot in pt
transifex-integration[bot] [Thu, 18 Jun 2026 18:31:15 +0000 (18:31 +0000)] 
intl: Translate intl/js/tvheadend.js.pot in pt

82% of minimum 80% translated source file: 'intl/js/tvheadend.js.pot'
on 'pt'.

Sync of partially translated files:
untranslated content is included with an empty translation
or source language content depending on file format

4 weeks agointl: Translate intl/js/tvheadend.js.pot in fr
transifex-integration[bot] [Thu, 18 Jun 2026 18:31:01 +0000 (18:31 +0000)] 
intl: Translate intl/js/tvheadend.js.pot in fr

96% of minimum 80% translated source file: 'intl/js/tvheadend.js.pot'
on 'fr'.

Sync of partially translated files:
untranslated content is included with an empty translation
or source language content depending on file format

4 weeks agointl: Translate intl/js/tvheadend.js.pot in ko
transifex-integration[bot] [Thu, 18 Jun 2026 18:30:47 +0000 (18:30 +0000)] 
intl: Translate intl/js/tvheadend.js.pot in ko

80% of minimum 80% translated source file: 'intl/js/tvheadend.js.pot'
on 'ko'.

Sync of partially translated files:
untranslated content is included with an empty translation
or source language content depending on file format

4 weeks agointl: Translate intl/js/tvheadend.js.pot in et
transifex-integration[bot] [Thu, 18 Jun 2026 18:30:34 +0000 (18:30 +0000)] 
intl: Translate intl/js/tvheadend.js.pot in et

83% of minimum 80% translated source file: 'intl/js/tvheadend.js.pot'
on 'et'.

Sync of partially translated files:
untranslated content is included with an empty translation
or source language content depending on file format

4 weeks agointl: Translate intl/js/tvheadend.js.pot in es
transifex-integration[bot] [Thu, 18 Jun 2026 18:30:20 +0000 (18:30 +0000)] 
intl: Translate intl/js/tvheadend.js.pot in es

82% of minimum 80% translated source file: 'intl/js/tvheadend.js.pot'
on 'es'.

Sync of partially translated files:
untranslated content is included with an empty translation
or source language content depending on file format

4 weeks agointl: Translate intl/js/tvheadend.js.pot in en_US
transifex-integration[bot] [Thu, 18 Jun 2026 18:30:07 +0000 (18:30 +0000)] 
intl: Translate intl/js/tvheadend.js.pot in en_US

99% of minimum 80% translated source file: 'intl/js/tvheadend.js.pot'
on 'en_US'.

Sync of partially translated files:
untranslated content is included with an empty translation
or source language content depending on file format

4 weeks agointl: Translate intl/js/tvheadend.js.pot in en_GB
transifex-integration[bot] [Thu, 18 Jun 2026 18:29:54 +0000 (18:29 +0000)] 
intl: Translate intl/js/tvheadend.js.pot in en_GB

99% of minimum 80% translated source file: 'intl/js/tvheadend.js.pot'
on 'en_GB'.

Sync of partially translated files:
untranslated content is included with an empty translation
or source language content depending on file format

4 weeks agointl: Translate intl/js/tvheadend.js.pot in de
transifex-integration[bot] [Thu, 18 Jun 2026 18:29:43 +0000 (18:29 +0000)] 
intl: Translate intl/js/tvheadend.js.pot in de

82% of minimum 80% translated source file: 'intl/js/tvheadend.js.pot'
on 'de'.

Sync of partially translated files:
untranslated content is included with an empty translation
or source language content depending on file format

4 weeks agobuild: clean up doozer build system references
Flole [Fri, 19 Jun 2026 19:50:35 +0000 (21:50 +0200)] 
build: clean up doozer build system references

4 weeks agorpm: generate local tarball using git archive to fix build errors
Flole [Thu, 18 Jun 2026 13:24:32 +0000 (15:24 +0200)] 
rpm: generate local tarball using git archive to fix build errors

4 weeks agoRename 'Parent disabled' to 'Inactive services' (fixes #1694)
Flole [Thu, 18 Jun 2026 13:23:14 +0000 (15:23 +0200)] 
Rename 'Parent disabled' to 'Inactive services' (fixes #1694)

4 weeks agointl: Translate intl/tvheadend.pot in pl
transifex-integration[bot] [Mon, 15 Jun 2026 15:21:49 +0000 (15:21 +0000)] 
intl: Translate intl/tvheadend.pot in pl

100% translated source file: 'intl/tvheadend.pot'
on 'pl'.

4 weeks agoratinglabels: document why enabled_notify is intentionally empty
Pim Zandbergen [Sun, 14 Jun 2026 13:42:55 +0000 (15:42 +0200)] 
ratinglabels: document why enabled_notify is intentionally empty

The notify callback for the enabled property is a registered no-op.
Add a comment explaining the empty body rather than removing the
function, addressing review feedback.

4 weeks agoFix GCC 16 -Werror build failures (satip, ratinglabels)
Pim Zandbergen [Sat, 13 Jun 2026 22:50:50 +0000 (00:50 +0200)] 
Fix GCC 16 -Werror build failures (satip, ratinglabels)

GCC 16's stricter checks turn two issues into -Werror build failures:

* satip_rtsp.c: the fixed 16-byte _w buffer cannot hold "&tvhweight=%d"
  for the full range of int weight (worst case 23 bytes incl. NUL),
  flagged as -Werror=format-truncation. Enlarge _w to 24 bytes. While
  here, size the buf VLA from the floor-clamped max_pids_len (so a small
  or negative caller value cannot under-size or invalidate it) and enlarge
  buf (max_pids_len + 32 -> + 64) so the combined "delpids=%s&addpids=%s%s"
  formatting (del+add up to max_pids_len, plus the larger _w) cannot
  silently truncate.

* ratinglabels.c: remove the dead body of ratinglabel_class_enabled_notify
  (a set-but-unused local plus an unused obj), which GCC 16 rejects.

Note: the buf resize and floor-clamped sizing reach slightly beyond the
original -Werror fix. They were added while iterating on this PR's Gemini
Code Assist review, which flagged those adjacent latent truncation /
VLA-sizing issues in the lines being touched.

4 weeks agobuild: never reuse a stale .config.mk from a failed configure
Pim Zandbergen [Sun, 14 Jun 2026 14:28:19 +0000 (16:28 +0200)] 
build: never reuse a stale .config.mk from a failed configure

write_config() is the sole writer of .config.mk and only runs after every
dependency check has passed. If configure instead dies on a missing
dependency (any of the ~40 `check_* || die` gates) - or is interrupted by a
signal - it returned without touching .config.mk, leaving the previous run's
file in place. The Makefile unconditionally `include`s that file, so make
would silently build against a stale configuration. In practice this could
link the wrong ffmpeg: e.g. a re-configure that died because libtheora-dev
was missing left behind a .config.mk from an earlier system-libav run,
pulling in the distro's ffmpeg instead of the bundled static one.

Establish the invariant "the .config.mk exists only if a configure run
completed":

- Remove any .config.mk up-front, right after argument parsing and before
  the checks, so that whatever ends the run early (die or signal) leaves no
  stale file and make fails loudly instead of using the wrong config. Placed
  after parsing so --help, which exits earlier, never deletes a valid config.
- Write .config.mk to a temp file in write_config() and rename it into place
  atomically at the very end, so it is never observed half-written.

4 weeks agointl: Translate intl/tvheadend.pot in fr
transifex-integration[bot] [Mon, 15 Jun 2026 12:08:20 +0000 (12:08 +0000)] 
intl: Translate intl/tvheadend.pot in fr

94% of minimum 80% translated source file: 'intl/tvheadend.pot'
on 'fr'.

Sync of partially translated files:
untranslated content is included with an empty translation
or source language content depending on file format

4 weeks agointl: Translate intl/tvheadend.pot in en_US
transifex-integration[bot] [Mon, 15 Jun 2026 12:08:07 +0000 (12:08 +0000)] 
intl: Translate intl/tvheadend.pot in en_US

98% of minimum 80% translated source file: 'intl/tvheadend.pot'
on 'en_US'.

Sync of partially translated files:
untranslated content is included with an empty translation
or source language content depending on file format

4 weeks agointl: Translate intl/tvheadend.pot in pl
transifex-integration[bot] [Mon, 15 Jun 2026 12:07:55 +0000 (12:07 +0000)] 
intl: Translate intl/tvheadend.pot in pl

99% of minimum 80% translated source file: 'intl/tvheadend.pot'
on 'pl'.

Sync of partially translated files:
untranslated content is included with an empty translation
or source language content depending on file format

4 weeks agointl: Translate intl/tvheadend.pot in en_GB
transifex-integration[bot] [Mon, 15 Jun 2026 12:07:41 +0000 (12:07 +0000)] 
intl: Translate intl/tvheadend.pot in en_GB

98% of minimum 80% translated source file: 'intl/tvheadend.pot'
on 'en_GB'.

Sync of partially translated files:
untranslated content is included with an empty translation
or source language content depending on file format

4 weeks agoUpdate Internationalisation Templates
Flole998 [Mon, 15 Jun 2026 03:11:24 +0000 (03:11 +0000)] 
Update Internationalisation Templates

5 weeks agoaccess: store the superuser password obfuscated like normal accounts
Sam Nazarko [Sat, 13 Jun 2026 07:11:50 +0000 (08:11 +0100)] 
access: store the superuser password obfuscated like normal accounts

The debian postinst wrote the superuser password to the superuser file
in cleartext, whereas normal user accounts are stored using the reversible
"TVHeadend-Hide-" base64 obfuscation. Write the superuser password the
same way (a new "password2" field) and teach access_init() to decode it,
falling back to the plaintext "password" field so existing superuser
files keep working.

If base64/tr are not available on the host (e.g. a stripped-down busybox
system), the postinst falls back to writing the cleartext "password"
field, so the superuser file is always valid regardless of userland.

This is obfuscation for consistency, not encryption: HTTP Digest auth
requires a recoverable password, so a one-way hash is not possible here.
The superuser file is still created mode 0600.

5 weeks agodebian: fix superuser file location and config directory ownership
Sam Nazarko [Sat, 13 Jun 2026 07:07:43 +0000 (08:07 +0100)] 
debian: fix superuser file location and config directory ownership

The postinst wrote the superuser account into $HOME (/var/lib/tvheadend),
but Tvheadend resolves its configuration directory via config_get_dir(),
which only uses /var/lib/tvheadend when that directory is owned by the
service user. On a stock package install /var/lib/tvheadend is owned by
root (adduser does not chown a pre-existing home directory), so the daemon
falls back to ~hts/.config/hts and never reads a superuser file placed in
the parent - the initial login then fails with "403 Forbidden".

- Take ownership of /var/lib/tvheadend so config_get_dir() uses it as the
  canonical configuration directory. This is skipped when an earlier
  install already has configuration under a fallback location, so existing
  settings are never stranded.
- Resolve the configuration directory in the postinst the same way
  config_get_dir() does (using "stat -L" to match the daemon's symlink
  dereferencing) and write the superuser file there.
- Document the ownership requirement of the /var/lib/tvheadend shortcut in
  config_get_dir().

5 weeks agoci: remove PCLOUD_TOKEN from build-ci.yml
Flole [Sat, 13 Jun 2026 21:08:30 +0000 (23:08 +0200)] 
ci: remove PCLOUD_TOKEN from build-ci.yml

5 weeks agoFix pcloud caching
Flole [Fri, 12 Jun 2026 13:39:39 +0000 (13:39 +0000)] 
Fix pcloud caching

5 weeks agointl: Translate intl/docs/tvheadend.doc.pot in fr
transifex-integration[bot] [Mon, 8 Jun 2026 16:06:09 +0000 (16:06 +0000)] 
intl: Translate intl/docs/tvheadend.doc.pot in fr

94% of minimum 80% translated source file: 'intl/docs/tvheadend.doc.pot'
on 'fr'.

Sync of partially translated files:
untranslated content is included with an empty translation
or source language content depending on file format

5 weeks agointl: Translate intl/docs/tvheadend.doc.pot in pl
transifex-integration[bot] [Mon, 8 Jun 2026 15:53:34 +0000 (15:53 +0000)] 
intl: Translate intl/docs/tvheadend.doc.pot in pl

100% translated source file: 'intl/docs/tvheadend.doc.pot'
on 'pl'.

5 weeks agointl: Translate intl/tvheadend.pot in pl
transifex-integration[bot] [Mon, 8 Jun 2026 15:53:18 +0000 (15:53 +0000)] 
intl: Translate intl/tvheadend.pot in pl

100% translated source file: 'intl/tvheadend.pot'
on 'pl'.

5 weeks agointl: Translate intl/tvheadend.pot in en_GB
transifex-integration[bot] [Mon, 8 Jun 2026 13:44:37 +0000 (13:44 +0000)] 
intl: Translate intl/tvheadend.pot in en_GB

98% of minimum 80% translated source file: 'intl/tvheadend.pot'
on 'en_GB'.

Sync of partially translated files:
untranslated content is included with an empty translation
or source language content depending on file format

5 weeks agointl: Translate intl/tvheadend.pot in en_US
transifex-integration[bot] [Mon, 8 Jun 2026 13:44:23 +0000 (13:44 +0000)] 
intl: Translate intl/tvheadend.pot in en_US

98% of minimum 80% translated source file: 'intl/tvheadend.pot'
on 'en_US'.

Sync of partially translated files:
untranslated content is included with an empty translation
or source language content depending on file format

5 weeks agointl: Translate intl/tvheadend.pot in pl
transifex-integration[bot] [Mon, 8 Jun 2026 13:44:11 +0000 (13:44 +0000)] 
intl: Translate intl/tvheadend.pot in pl

99% of minimum 80% translated source file: 'intl/tvheadend.pot'
on 'pl'.

Sync of partially translated files:
untranslated content is included with an empty translation
or source language content depending on file format

5 weeks agointl: Translate tvheadend.doc.pot in en_US
transifex-integration[bot] [Mon, 8 Jun 2026 13:43:54 +0000 (13:43 +0000)] 
intl: Translate tvheadend.doc.pot in en_US

98% of minimum 80% translated source file: 'tvheadend.doc.pot'
on 'en_US'.

Sync of partially translated files:
untranslated content is included with an empty translation
or source language content depending on file format

5 weeks agointl: Translate intl/docs/tvheadend.doc.pot in pl
transifex-integration[bot] [Mon, 8 Jun 2026 13:43:43 +0000 (13:43 +0000)] 
intl: Translate intl/docs/tvheadend.doc.pot in pl

98% of minimum 80% translated source file: 'intl/docs/tvheadend.doc.pot'
on 'pl'.

Sync of partially translated files:
untranslated content is included with an empty translation
or source language content depending on file format

5 weeks agointl: Translate intl/docs/tvheadend.doc.pot in fr
transifex-integration[bot] [Mon, 8 Jun 2026 13:43:29 +0000 (13:43 +0000)] 
intl: Translate intl/docs/tvheadend.doc.pot in fr

94% of minimum 80% translated source file: 'intl/docs/tvheadend.doc.pot'
on 'fr'.

Sync of partially translated files:
untranslated content is included with an empty translation
or source language content depending on file format

5 weeks agointl: Translate tvheadend.doc.pot in en_GB
transifex-integration[bot] [Mon, 8 Jun 2026 13:43:15 +0000 (13:43 +0000)] 
intl: Translate tvheadend.doc.pot in en_GB

98% of minimum 80% translated source file: 'tvheadend.doc.pot'
on 'en_GB'.

Sync of partially translated files:
untranslated content is included with an empty translation
or source language content depending on file format

5 weeks agointl: Translate intl/tvheadend.pot in fr
transifex-integration[bot] [Mon, 8 Jun 2026 13:43:02 +0000 (13:43 +0000)] 
intl: Translate intl/tvheadend.pot in fr

94% of minimum 80% translated source file: 'intl/tvheadend.pot'
on 'fr'.

Sync of partially translated files:
untranslated content is included with an empty translation
or source language content depending on file format

5 weeks agosatip: expose DVB-S frequency override and fix matching priority
maedula [Fri, 5 Jun 2026 19:19:24 +0000 (21:19 +0200)] 
satip: expose DVB-S frequency override and fix matching priority

Expose the existing but hidden mm_dvb_satip_dvbs_freq field in the
DVB-S mux editor UI as an advanced property. This allows SAT>IP clients
to address DVB-S2X MIS muxes that share the same physical frequency but
have different ISI values, by assigning each mux a unique virtual
frequency within the Ku-band range.

Also move the SAT>IP frequency override matching (for DVB-S/C/T) to run
before normal dvb_network_find_mux() matching. This ensures virtual
frequency overrides always take priority, preventing accidental matches
against real transponder frequencies.

Guard the override match against unconfigured (zero) values using a
switch statement, so deltaU32(freq, 0) cannot produce false matches
when no override is configured.

Background: SAT>IP RTSP does not pass ISI (dmc_fe_stream_id), so
dvb_network_find_mux() can never match MIS muxes from SAT>IP clients.
The frequency override is the only path to reach MIS streams via SAT>IP.

5 weeks agoUpdate Internationalisation Templates
Flole998 [Mon, 8 Jun 2026 03:10:35 +0000 (03:10 +0000)] 
Update Internationalisation Templates

6 weeks agoepggrab: per-service EIT processing policy with global default
Oliver Sluke [Thu, 21 May 2026 22:04:08 +0000 (00:04 +0200)] 
epggrab: per-service EIT processing policy with global default

DVB EIT comes in two forms. Actual transport stream tables (table_id
0x4e present/following, 0x50-0x5f schedule) describe the multiplex
that carries them. Other transport stream tables (0x4f, 0x60-0x6f)
describe services on a different multiplex. The OTA grabber tunes
every multiplex, so when one multiplex carries other-TS EIT for a
service whose own multiplex is also grabbed, both descriptions reach
the EPG store and the last writer wins. A coarse other-TS placeholder
from a neighbouring multiplex can then overwrite a service's own
detailed schedule, and vice versa, so the EPG flips between the two
on successive grabs.

Replace the per-service "Ignore EPG (EIT)" boolean with an "EIT
processing" enum, and add a matching "EIT processing (default)"
global setting. Per-service values:

  Default       Defer to the global default
  None          Ignore EIT for this service
  Actual only   Accept only actual-TS sub-tables (0x4e, 0x50-0x5f)
  Other only    Accept only other-TS sub-tables (0x4f, 0x60-0x6f)
  Either        Accept both (today's behaviour, no precedence)
  Adaptive      Accept both, but once the service's own actual-TS
                schedule has been seen, drop further other-TS for
                it so a neighbour's coarse description cannot
                overwrite the detailed schedule

The global default exposes the same enum minus Default. New installs
get Either, which matches today's behaviour with no surprise
migrations.

The legacy "dvb_ignore_eit" key is read on first load: true migrates
to None (same observable behaviour), false to Default (defers to the
global, also same observable behaviour while global stays at Either).
The old key disappears from the config on next save.

The EIT handler resolves the effective policy per call (per-service
unless Default, in which case the global applies) and filters tableid
acceptance accordingly. Adaptive tracks per-service "actual-TS
schedule seen" at runtime; the flag is not persisted, so it is
relearned each session. P/F (0x4e / 0x4f) is only two events per
service and is not used as the seen trigger.

Full per-value help text lives in docs/property/eit_processing.md and
docs/property/eit_processing_default.md, wired in via the PROP_DOC
macro; the in-form ".desc" tooltips are short pointers to the Help
icon, matching the existing pattern (e.g. PROP_DOC(cron),
PROP_DOC(ota_genre_translation)).

Signed-off-by: Oliver Sluke <22557015+oliversluke@users.noreply.github.com>
6 weeks agodvr: make data error threshold configurable
Bernhard Berger [Sun, 31 May 2026 21:28:26 +0000 (23:28 +0200)] 
dvr: make data error threshold configurable

6 weeks agodvr: size subscription-title buffer to the title length
Oliver Sluke [Mon, 25 May 2026 11:46:16 +0000 (13:46 +0200)] 
dvr: size subscription-title buffer to the title length

`dvr_rec_subscribe` formatted the subscription title into a fixed
100-byte buffer via snprintf. snprintf truncates byte-wise, so a
long multibyte title (CJK at 3 bytes/char, or 4-byte emoji) could
leave a dangling partial UTF-8 sequence at the end. That title
flowed into the comet/ws JSON feed; the invalid byte made the
browser drop the frame, and the web UI stayed silent until the
recording ended.

Size the buffer to the actual title via alloca() so the title
isn't truncated at all. On the common case of short ASCII titles
the buffer is smaller than the previous fixed 100 bytes; on long
multibyte titles it grows just enough. Matches the existing
alloca pattern used elsewhere in the tree (cron.c, http.c,
string_list.c, rtsp.c, htsmsg_xml.c).

Fixes: #2121
Signed-off-by: Oliver Sluke <22557015+oliversluke@users.noreply.github.com>
6 weeks agoepg: fix channel-name DESC sort (was calling description sort)
Oliver Sluke [Sat, 16 May 2026 21:17:29 +0000 (23:17 +0200)] 
epg: fix channel-name DESC sort (was calling description sort)

src/epg.c:2607-2610 — `_epg_sort_channel_descending` delegates to
`_epg_sort_description_ascending` and negates, so a request for
channel-name DESC returns events ordered by reverse description
text instead of reverse channel name. Rows cluster by adjacent
description prose, not by channel.

The sibling `_epg_sort_channel_ascending` (lines 2598-2605) uses
`channel_get_name` correctly, and every other `_descending`
variant in the file delegates to its own `_ascending` (title,
subtitle, summary, description, extratext, genre). Channel is
the only outlier — a copy-paste typo.

Fix: delegate to `_epg_sort_channel_ascending`.

Verifiable via:
  curl 'http://<host>/api/epg/events/grid?sort=channelName&dir=DESC&limit=20'

Pre-fix: rows interleave by description text. Post-fix: rows
cluster by channel in reverse alphabetical order.

Signed-off-by: Oliver Sluke <22557015+oliversluke@users.noreply.github.com>
6 weeks agoAdd Ubuntu 26.04 (resolute) CI and Cloudsmith builds
copilot-swe-agent[bot] [Mon, 4 May 2026 13:24:40 +0000 (13:24 +0000)] 
Add Ubuntu 26.04 (resolute) CI and Cloudsmith builds

Agent-Logs-Url: https://github.com/tvheadend/tvheadend/sessions/6f069c7c-8b1f-4063-8606-70d3fe938d77

Co-authored-by: Flole998 <9951871+Flole998@users.noreply.github.com>
2 months agoFix const qualifier assignments
copilot-swe-agent[bot] [Sun, 17 May 2026 16:29:42 +0000 (16:29 +0000)] 
Fix const qualifier assignments

Agent-Logs-Url: https://github.com/tvheadend/tvheadend/sessions/72422d26-8fa7-4f11-a236-b5e8eef20c2d

Co-authored-by: Flole998 <9951871+Flole998@users.noreply.github.com>
2 months agoFix discarded-qualifiers warnings in channels.c, httpc.c, webui.c
copilot-swe-agent[bot] [Wed, 6 May 2026 09:30:02 +0000 (09:30 +0000)] 
Fix discarded-qualifiers warnings in channels.c, httpc.c, webui.c

Agent-Logs-Url: https://github.com/tvheadend/tvheadend/sessions/bc91d39b-dbd2-4eac-8b9d-3caf92a47726

Co-authored-by: Flole998 <9951871+Flole998@users.noreply.github.com>
2 months agowizard: fix SIGSEGV when unknown network type is submitted; add PO_LISTONLY for stric...
Copilot [Fri, 15 May 2026 21:18:52 +0000 (23:18 +0200)] 
wizard: fix SIGSEGV when unknown network type is submitted; add PO_LISTONLY for strict dropdowns (#2102)

* Fix crash when invalid network type is entered in wizard; add PO_LISTONLY for dropdown-only combo

Agent-Logs-Url: https://github.com/tvheadend/tvheadend/sessions/3e2bd0ab-3c09-47ce-af52-3bf7788e242f

Co-authored-by: Flole998 <9951871+Flole998@users.noreply.github.com>
2 months agompegts: align DVB delivery-system and modulation defaults with the enum
Oliver Sluke [Sat, 9 May 2026 06:01:58 +0000 (08:01 +0200)] 
mpegts: align DVB delivery-system and modulation defaults with the enum

Several mux classes in mpegts_mux_dvb.c declare a default value
that the corresponding *_enum callback never emits. Each enum
function iterates a small list of DVB_* constants and emits the
canonical string returned by the matching dvb_*2str helper —
which scans the relevant strtab in dvb_support.c and returns
the first matching row.

Two distinct mismatches:

1. Delivery-system defaults

   Three classes used the unhyphenated spelling and one carried
   the wrong constant entirely (copy-pasted from the dvbt class
   above):

     - dvb_mux_dvbt_class.delsys     "DVBT"  -> "DVB-T"
     - dvb_mux_dvbs_class.delsys     "DVBS"  -> "DVB-S"
     - dvb_mux_isdb_s_class.delsys   "ISDBS" -> "ISDB-S"
     - dvb_mux_dtmb_class.delsys     "DVBT"  -> "DTMB"

   delsystab carries both spellings on the same DVB_SYS_*
   constant, so configs persisted with the legacy form continue
   to load unchanged.

2. QAM constellation / modulation defaults

   qamtab carries TWO entries for "auto" — { "AUTO", DVB_MOD_AUTO }
   and { "QAM/AUTO", DVB_MOD_QAM_AUTO }. The enum arrays for the
   QAM-based modulation classes pass DVB_MOD_QAM_AUTO, so the
   dropdown publishes "QAM/AUTO" — but each class declared the
   default as "AUTO", which parses to a different constant and
   isn't a member of the published enum.

     - dvb_mux_dvbt_class.constellation        "AUTO" -> "QAM/AUTO"
     - dvb_mux_dvbc_class.constellation        "AUTO" -> "QAM/AUTO"
     - dvb_mux_atsc_t_class.modulation         "AUTO" -> "QAM/AUTO"
     - dvb_mux_atsc_c_class.constellation      "AUTO" -> "QAM/AUTO"
     - dvb_mux_isdb_t_class.layera_mod         "AUTO" -> "QAM/AUTO"
     - dvb_mux_isdb_t_class.layerb_mod         "AUTO" -> "QAM/AUTO"
     - dvb_mux_isdb_t_class.layerc_mod         "AUTO" -> "QAM/AUTO"
     - dvb_mux_isdb_c_class.constellation      "AUTO" -> "QAM/AUTO"
     - dvb_mux_dtmb_class.constellation        "AUTO" -> "QAM/AUTO"

   The other DVB tables (modetab, guardtab, hiertab) only carry
   a single AUTO entry, and bw / fec fall through to
   dvb_common2str which canonicalises value 1 to "AUTO" — so the
   remaining 22 MUX_PROP_STR(..., N_("AUTO")) declarations are
   already in alignment.

Effect: clients that validate enum membership on submit refuse
to send the default — the user has to manually re-pick the
canonical value before the form will save. The ExtJS combobox
doesn't enforce membership on submit, so the bug went unnoticed
there.

Migration: the delsys side has no on-disk impact (legacy and
canonical strings map to the same constant). For the QAM cases
"AUTO" parses to DVB_MOD_AUTO and "QAM/AUTO" parses to
DVB_MOD_QAM_AUTO — different constants. Existing configs
persisted with the legacy "AUTO" continue to load and tune
correctly (both signal "let the driver decide"); the dropdown
synthesises a current-value option so the form remains editable
until the user re-saves with the canonical choice.

Signed-off-by: Oliver Sluke <22557015+oliversluke@users.noreply.github.com>
2 months agointl: include docs translations in the runtime catalog
Oliver Sluke [Wed, 13 May 2026 21:37:28 +0000 (23:37 +0200)] 
intl: include docs translations in the runtime catalog

Makefile line 668 references $(LANGUAGES-DOC) — a variable that
is never defined anywhere in the repository. GNU Make expands
undefined variables to empty, so the docs catalog
(intl/docs/tvheadend.doc.<lang>.po) never reaches PO-FILES, the
poc.py generator, or src/tvh_locale_inc.c. Every msgid sourced
from docs/class/*.md, docs/property/*.md, and docs/wizard/*.md
falls back to English on every language at runtime via
tvh_gettext_lang(), even when the matching translation exists.

User-visible surfaces affected: the setup wizard's per-step
description paragraphs, every per-class help page served via
/markdown/<class>, and every per-property help text.

Repro: build with LANGUAGES=pl, then
  grep -c "Welcome to Tvheadend" src/tvh_locale_inc.c
returns 0, even though intl/docs/tvheadend.doc.pl.po carries
the Polish translation
  "__Witamy w Tvheadend – Twoim serwerze do transmisji
   strumieniowej TV i rejestratorze wideo__"
for the wizard's hello-step headline. After this fix the count
is non-zero and the Polish wizard renders its description in
Polish instead of English.

Coverage today (filled msgstr entries): Polish 100%, French
95.5%, English en_GB / en_US ~100%, Estonian 41.8%, Korean
33.5%, Spanish 20.7%, plus partial work in Norwegian, German,
Danish, Dutch, Swedish, and Bulgarian.

Origin: introduced in commit 7ec64dd70d ("intl: fix the broken
build", 2016-04-13), which added $(wildcard ...) to both
PO-FILES lines but also renamed the docs-line variable from
$(LANGUAGES) to $(LANGUAGES-DOC) without adding a definition.
No prior pull request or GitHub issue has tracked the bug — the
docs catalog has been silently absent from the runtime since.

Two coordinated changes:

  1. Makefile — use $(LANGUAGES) on the docs line, matching the
     main-catalog line directly above. The existing
     `make LANGUAGES="..."` override now affects both catalogs
     consistently.

  2. support/poc.py — rewrite cstr() to emit every non-ASCII
     byte as an octal \NNN escape, producing an ASCII-only
     src/tvh_locale_inc.c. The compiled binary contains the
     same UTF-8 byte sequence as before (octal escapes resolve
     to single bytes at compile time). This avoids tripping
     GCC's -Wbidi-chars Trojan-Source check on any
     translator-introduced bidi control character — concrete
     case: intl/docs/tvheadend.doc.pl.po:6935 translates "The"
     as a lone U+202E, almost certainly a stray keystroke.
     Octal escapes are bounded to exactly 3 digits by the C
     standard, so they don't suffer the greedy-consumption
     pitfall of \xHH hex escapes. The rewrite also correctly
     escapes literal backslashes, which the previous cstr()
     silently passed through.

Catalog ordering: PO-FILES lists the main catalog first and the
docs catalog second; poc.py's load() merges via dict.update(),
so on overlapping msgids the docs translation wins. For Polish
244 msgids overlap (227 identical, 17 different); for French
244 overlap (229 identical, 14 different); for en_GB 244 overlap
(243 identical, 0 different). The differing entries are minor
translator variants — accent / spelling / article-elision tweaks
— not semantic regressions. Examples:

  - French "Status":           main "Etat" → docs "État"
                                (accent fix — docs likely more correct)
  - French "Channel name":     main "Nom de la chaine" → docs
                                "Nom de chaine" (article dropped, stylistic)
  - Polish "Timeshift":        main "Funkcja Timeshift" → docs
                                "Timeshift" (shorter, stylistic)
  - Polish "Map unnamed channels":
                                main "Mapuj nienazwane kanały" → docs
                                "Mapuj nie nazwane kanały" (spelling variant)

Signed-off-by: Oliver Sluke <22557015+oliversluke@users.noreply.github.com>
2 months agoAdd ubuntu24.04 to run-on-arch workflows in CI and cloudsmith
copilot-swe-agent[bot] [Mon, 4 May 2026 13:33:37 +0000 (13:33 +0000)] 
Add ubuntu24.04 to run-on-arch workflows in CI and cloudsmith

Agent-Logs-Url: https://github.com/tvheadend/tvheadend/sessions/9eb64b34-4950-44fd-aefa-41b04b6029b5

Co-authored-by: Flole998 <9951871+Flole998@users.noreply.github.com>
2 months agoprop.c: refactor PT_U32 split-int parsing to use prop_intsplit_from_str
copilot-swe-agent[bot] [Tue, 5 May 2026 11:44:42 +0000 (11:44 +0000)] 
prop.c: refactor PT_U32 split-int parsing to use prop_intsplit_from_str

Agent-Logs-Url: https://github.com/tvheadend/tvheadend/sessions/21b0e984-5867-49d1-8142-861f4b00d7c4

Co-authored-by: Flole998 <9951871+Flole998@users.noreply.github.com>
2 months agoprop.c: fix const qualifier warning in prop_write_values
copilot-swe-agent[bot] [Tue, 5 May 2026 11:30:34 +0000 (11:30 +0000)] 
prop.c: fix const qualifier warning in prop_write_values

Agent-Logs-Url: https://github.com/tvheadend/tvheadend/sessions/1de0ed71-e0c2-4820-937a-30c7f61f9972

Co-authored-by: Flole998 <9951871+Flole998@users.noreply.github.com>
2 months agohtsbuf: escape all JSON control bytes per RFC 8259 §7
Oliver Sluke [Sat, 2 May 2026 20:59:15 +0000 (22:59 +0200)] 
htsbuf: escape all JSON control bytes per RFC 8259 §7

`htsbuf_append_and_escape_jsonstr` is the single function every
JSON-emitting code path in tvheadend funnels through (see
`src/htsmsg_json.c:53,67,72,76` — every htsmsg_t serialised to
JSON, which covers every `api/...` REST response and the Comet
bus). Today it escapes only `"`, `\`, `\n`, `\r`, `\t`. The
other 27 ASCII control bytes (0x00–0x1F minus those three) are
appended verbatim.

This violates RFC 8259 §7 / ECMA-404, which both require all
characters in U+0000–U+001F to be escaped. The output is
rejected by every standards-compliant JSON parser (browser
JSON.parse, Python json.loads, jq, Go encoding/json, jansson,
serde_json). The bug surfaces whenever a stored string ever
contained one of those bytes — broadcaster EPG data is a
frequent source (DVB-EIT and XMLTV feeds occasionally carry
stray BEL / SOH / etc.), and any user-editable string field
can pick them up via API clients or paste-from-terminal.

Reproduction (verified on HEAD before this change): set any
DVR entry's `comment` field to a string containing `\a`
via `api/idnode/save`, fetch the entry back via the
corresponding grid endpoint, observe `Invalid control character
at: line 1 column N` from `python3 -c 'import json;
json.load(...)'`. Hex-dump confirms a literal 0x07 byte sits
unescaped inside the JSON string.

Fix: extend the trigger condition to `c < 0x20`, add `\b` and
`\f` shortform escapes (RFC 8259 §7 recognises them; matches
what every standard JSON serialiser emits), fall back to
`\uXXXX` for the remaining ~24 control bytes. Cast to
`unsigned char` so the comparison is well-defined on platforms
where plain `char` is signed. Switch statement replaces the
if-else cascade — same emitted code, easier to read.

Behaviour delta: zero for any string that didn't already
contain a control byte. Only previously-malformed JSON output
becomes well-formed. UTF-8 multi-byte sequences pass through
unchanged (continuation 0x80–0xBF and lead 0xC0–0xFF don't
trigger c < 0x20).

Verified via a small standalone test that links against the
patched htsbuf.o, exercises the function over every byte
0x01–0x1F, and round-trips the output through Python's strict
parser. Output for byte 0x07: `"x\ay"` (escaped). For 0x0C:
`"x\fy"` (named shortform). Etc. All 31 round-trips succeed.

Independent of PR #2034 (cJSON replacement), which explicitly
retains this function.

Signed-off-by: Oliver Sluke <22557015+oliversluke@users.noreply.github.com>
2 months agointl: Translate intl/tvheadend.pot in pl
transifex-integration[bot] [Wed, 29 Apr 2026 18:10:41 +0000 (18:10 +0000)] 
intl: Translate intl/tvheadend.pot in pl

100% translated source file: 'intl/tvheadend.pot'
on 'pl'.

2 months agointl: Translate intl/docs/tvheadend.doc.pot in pl
transifex-integration[bot] [Wed, 29 Apr 2026 18:10:29 +0000 (18:10 +0000)] 
intl: Translate intl/docs/tvheadend.doc.pot in pl

100% translated source file: 'intl/docs/tvheadend.doc.pot'
on 'pl'.

2 months agointl: Translate intl/tvheadend.pot in en_US
transifex-integration[bot] [Wed, 29 Apr 2026 12:43:50 +0000 (12:43 +0000)] 
intl: Translate intl/tvheadend.pot in en_US

99% of minimum 80% translated source file: 'intl/tvheadend.pot'
on 'en_US'.

Sync of partially translated files:
untranslated content is included with an empty translation
or source language content depending on file format

2 months agointl: Translate intl/tvheadend.pot in pl
transifex-integration[bot] [Wed, 29 Apr 2026 12:43:33 +0000 (12:43 +0000)] 
intl: Translate intl/tvheadend.pot in pl

99% of minimum 80% translated source file: 'intl/tvheadend.pot'
on 'pl'.

Sync of partially translated files:
untranslated content is included with an empty translation
or source language content depending on file format

2 months agointl: Translate tvheadend.doc.pot in en_GB
transifex-integration[bot] [Wed, 29 Apr 2026 12:43:17 +0000 (12:43 +0000)] 
intl: Translate tvheadend.doc.pot in en_GB

99% of minimum 80% translated source file: 'tvheadend.doc.pot'
on 'en_GB'.

Sync of partially translated files:
untranslated content is included with an empty translation
or source language content depending on file format

2 months agointl: Translate intl/docs/tvheadend.doc.pot in pl
transifex-integration[bot] [Wed, 29 Apr 2026 12:42:59 +0000 (12:42 +0000)] 
intl: Translate intl/docs/tvheadend.doc.pot in pl

99% of minimum 80% translated source file: 'intl/docs/tvheadend.doc.pot'
on 'pl'.

Sync of partially translated files:
untranslated content is included with an empty translation
or source language content depending on file format

2 months agointl: Translate intl/docs/tvheadend.doc.pot in fr
transifex-integration[bot] [Wed, 29 Apr 2026 12:42:41 +0000 (12:42 +0000)] 
intl: Translate intl/docs/tvheadend.doc.pot in fr

95% of minimum 80% translated source file: 'intl/docs/tvheadend.doc.pot'
on 'fr'.

Sync of partially translated files:
untranslated content is included with an empty translation
or source language content depending on file format

2 months agointl: Translate tvheadend.doc.pot in en_US
transifex-integration[bot] [Wed, 29 Apr 2026 12:42:24 +0000 (12:42 +0000)] 
intl: Translate tvheadend.doc.pot in en_US

99% of minimum 80% translated source file: 'tvheadend.doc.pot'
on 'en_US'.

Sync of partially translated files:
untranslated content is included with an empty translation
or source language content depending on file format

2 months agointl: Translate intl/tvheadend.pot in fr
transifex-integration[bot] [Wed, 29 Apr 2026 12:42:10 +0000 (12:42 +0000)] 
intl: Translate intl/tvheadend.pot in fr

95% of minimum 80% translated source file: 'intl/tvheadend.pot'
on 'fr'.

Sync of partially translated files:
untranslated content is included with an empty translation
or source language content depending on file format

2 months agointl: Translate intl/tvheadend.pot in en_GB
transifex-integration[bot] [Wed, 29 Apr 2026 12:41:57 +0000 (12:41 +0000)] 
intl: Translate intl/tvheadend.pot in en_GB

99% of minimum 80% translated source file: 'intl/tvheadend.pot'
on 'en_GB'.

Sync of partially translated files:
untranslated content is included with an empty translation
or source language content depending on file format

2 months agoUpdate Internationalisation Templates
Flole998 [Mon, 27 Apr 2026 03:07:20 +0000 (03:07 +0000)] 
Update Internationalisation Templates

2 months ago[WebUI] Option to hide seconds in DVR Dialogue.
DeltaMikeCharlie [Sun, 26 Apr 2026 01:39:26 +0000 (11:39 +1000)] 
[WebUI] Option to hide seconds in DVR Dialogue.

2 months agoOption to set default tab by system and user.
DeltaMikeCharlie [Wed, 11 Mar 2026 18:52:48 +0000 (05:52 +1100)] 
Option to set default tab by system and user.

2 months agochannels: fix reentrancy guard in channel_class_changed()
Oliver Sluke [Thu, 23 Apr 2026 21:12:49 +0000 (23:12 +0200)] 
channels: fix reentrancy guard in channel_class_changed()

atomic_dec(&ch->ch_changed_ref, 0) subtracts 0 instead of 1, so
the reentrancy counter never resets after the first call. All
subsequent calls see atomic_add() > 0 and return early, permanently
suppressing HTSP channelUpdate notifications, bouquet propagation,
and tag updates for that channel.

Fix by decrementing by 1 so the guard properly resets to 0.

2 months agohtsp: add absolute signal fields for DVBv5 decibel-scale hardware
Oliver Sluke [Fri, 24 Apr 2026 13:41:44 +0000 (15:41 +0200)] 
htsp: add absolute signal fields for DVBv5 decibel-scale hardware

The signalStatus message only sends feSNR/feSignal for
SIGNAL_STATUS_SCALE_RELATIVE drivers, silently dropping values from
modern DVBv5 hardware that reports in SIGNAL_STATUS_SCALE_DECIBEL.

Add feAbsoluteSNR (s64, dB x 1000) and feAbsoluteSignal (s64,
dBm x 1000) fields for SCALE_DECIBEL drivers, carrying the raw
kernel values with no conversion. Clients check which field is
present to determine the value type.

Bump HTSP protocol version to 44.

2 months agoFix typo in comment message for squashing pull requests
Flole [Fri, 24 Apr 2026 15:30:14 +0000 (17:30 +0200)] 
Fix typo in comment message for squashing pull requests

3 months agoUpdate license entry in tvheadend.spec.in
Flole [Tue, 7 Apr 2026 13:53:55 +0000 (15:53 +0200)] 
Update license entry in tvheadend.spec.in

Removed GPL license file from package specification.

3 months agoFormat license in order to allow GitHub to auto-detect it
Flole [Tue, 7 Apr 2026 13:23:30 +0000 (13:23 +0000)] 
Format license in order to allow GitHub to auto-detect it

3 months agoUpdate Internationalisation Templates (#2075)
github-actions[bot] [Mon, 30 Mar 2026 06:40:12 +0000 (10:40 +0400)] 
Update Internationalisation Templates (#2075)

Co-authored-by: Flole998 <Flole998@users.noreply.github.com>
4 months ago idnode.c: unlock idnode on failed uuid_set
Enrico Lorenzoni [Mon, 9 Mar 2026 08:31:28 +0000 (08:31 +0000)] 
 idnode.c: unlock idnode on failed uuid_set

4 months agoFix cutlist extension handling for filenames without dots
Flole [Sun, 8 Mar 2026 01:35:57 +0000 (02:35 +0100)] 
Fix cutlist extension handling for filenames without dots

4 months agochannels: fix sort order in channel dropdown lists
Pim Zandbergen [Tue, 3 Mar 2026 16:34:24 +0000 (17:34 +0100)] 
channels: fix sort order in channel dropdown lists

Channel dropdowns such as "Reuse EPG from" in channel configuration,
and the channel selectors in DVR, autorec, timerec and service mapper,
all use channel_class_get_list() to build their property descriptor.

The descriptor lacked an stype field, causing the ExtJS data store in
idnode.js to re-sort the server-provided list alphabetically by the
displayed string after loading, regardless of the order delivered by
the API.

Fix by adding stype=none to the descriptor to preserve server sort
order, and explicitly requesting the appropriate sort from the API
based on the channel display configuration:

- With channel numbers enabled (chname_num): request sort=numname so
  channels are ordered numerically, matching the visible number prefix.
  Previously the ExtJS re-sort would produce alphabetical ordering on
  the prefixed strings, e.g. "1 ...", "10 ...", "2 ..." instead of
  "1 ...", "2 ...", "10 ...".

- With channel numbers disabled: request sort=name to preserve the
  existing alphabetical-by-name ordering that users without channel
  numbers would expect. This is a pure bug fix with no behaviour
  change for this configuration.

4 months agofix profile when overwritten by user with the wrong value
Ukn Unknown [Sat, 17 Jan 2026 02:49:30 +0000 (18:49 -0800)] 
fix profile when overwritten by user with the wrong value

- fix profile when overwritten by user with the wrong value
- related to the problem :  https://github.com/tvheadend/tvheadend/pull/1933#issuecomment-3392478998
- before transferring the profile to ffmpeg we confirm that profile is available.

4 months agofixes crash when user is forcing pix_fmt or when codec is not available
Ukn Unknown [Sun, 18 Jan 2026 00:44:32 +0000 (16:44 -0800)] 
fixes crash when user is forcing pix_fmt or when codec is not available

- fixes https://github.com/tvheadend/tvheadend/issues/1980
- in context.c I added two checks to ensure we don't search for a helper if codec is not available and is not trying to open (tvh_codec_profile_open()) --> this will create a panic.
- in order to avoid getting to that 'late point decision'; at am also checking if pix_fmt is available in ffmpeg before calling.
- if the user will try to force (from web interface) a different pix_fmt some warnings will be printed and video will not open.

4 months agoclean up low_power from vaapi
Ukn Unknown [Fri, 16 Jan 2026 17:29:47 +0000 (09:29 -0800)] 
clean up low_power from vaapi

- low power should be moved from tvh_codec_profile to tvh_codec_profile_vaapi_t after  https://github.com/tvheadend/tvheadend/pull/1996

4 months agoconfig.c: disable HDHomeRun configuration when not built in
Trujulu [Sun, 18 Jan 2026 14:58:25 +0000 (15:58 +0100)] 
config.c: disable HDHomeRun configuration when not built in

4 months agoFixed a typo in profile_video_class.c
Ukn Unknown [Mon, 19 Jan 2026 22:33:30 +0000 (14:33 -0800)] 
Fixed a typo in profile_video_class.c

- fixed a typo in profile_video_class.c

4 months agointl: Translate tvheadend.doc.pot in en_US
transifex-integration[bot] [Mon, 26 Jan 2026 13:43:47 +0000 (13:43 +0000)] 
intl: Translate tvheadend.doc.pot in en_US

100% translated source file: 'tvheadend.doc.pot'
on 'en_US'.