68 Commits

Author SHA1 Message Date
TrueCharts Bot b1a9061e5c feat(helm-deps): update chart common 29.6.0 → 29.7.1 (#49740)
This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [common](https://truecharts.org/charts/library/common)
([source](https://ghcr.io/cloudnative-pg/postgis)) | minor | `29.6.0` →
`29.7.1` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the [Dependency
Dashboard](../issues/18710) for more information.

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Renovate
Bot](https://redirect.github.com/renovatebot/renovate).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xMzAuMSIsInVwZGF0ZWRJblZlciI6IjQzLjEzMC4xIiwidGFyZ2V0QnJhbmNoIjoibWFzdGVyIiwibGFiZWxzIjpbImFwcC96d2F2ZWpzMm1xdHQiLCJhdXRvbWVyZ2UiLCJyZW5vdmF0ZS9jb250YWluZXIiLCJ0eXBlL21pbm9yIl19-->

Signed-off-by: Alfred Göppel <43101280+alfi0812@users.noreply.github.com>
Co-authored-by: Alfred Göppel <43101280+alfi0812@users.noreply.github.com>
2026-07-02 09:11:49 +02:00
TrueCharts Bot 1fda2be3c4 fix(dispatcharr): update image docker.io/dispatcharr/dispatcharr 0.27.1 → 0.27.2 (#49705)
This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
|
[docker.io/dispatcharr/dispatcharr](https://redirect.github.com/Dispatcharr/Dispatcharr)
| patch | `f5cbc82` → `f2b5ea3` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the [Dependency
Dashboard](../issues/18710) for more information.

Add the preset `:preserveSemverRanges` to your config if you don't want
to pin your dependencies.

---

### Release Notes

<details>
<summary>Dispatcharr/Dispatcharr
(docker.io/dispatcharr/dispatcharr)</summary>

###
[`v0.27.2`](https://redirect.github.com/Dispatcharr/Dispatcharr/blob/HEAD/CHANGELOG.md#0272---2026-06-30)

[Compare
Source](https://redirect.github.com/Dispatcharr/Dispatcharr/compare/v0.27.1...v0.27.2)

##### Added

- **New proxy setting: Client Connect Grace Period
(`channel_client_wait_period`, default 5s).** Adds a dedicated timeout
for channels that have filled their buffer but still have no viewers
(`waiting_for_clients`). Previously that window reused
`channel_shutdown_delay` (default 0s), so those channels were torn down
almost immediately.

##### Changed

- **Proxy grace-period settings are now split into three distinct
timeouts.** The cleanup watchdog already applied
`channel_init_grace_period` while a channel was still connecting (buffer
not ready) and reused `channel_shutdown_delay` once
`connection_ready_time` was set, including for `waiting_for_clients`
with zero viewers. With the default `channel_shutdown_delay` of 0s, a
buffered channel waiting for its first viewer was stopped almost
immediately; raising shutdown delay was the only workaround, but that
also delayed teardown after real disconnects. Behaviour is now:
- **`channel_init_grace_period` (default 60s, max 300s):** how long the
proxy may spend connecting and cycling failover streams before giving up
on startup.
- **`channel_client_wait_period` (default 5s):** how long a ready
channel with no viewers stays up waiting for the first client (the
original grace-period use case).
- **`channel_shutdown_delay` (default 0s):** delay after the last client
disconnects only; no longer applies when the buffer is ready but no
viewer has connected yet.
- **Migration 0026 bumps `channel_init_grace_period` to 60s when the
stored value is below 60.** Existing installs on the old 5s default (or
any custom value under 60) are raised automatically. Values already at
60s or higher are unchanged. If you previously raised
`channel_shutdown_delay` to keep buffered channels alive with no
viewers, set `channel_client_wait_period` instead (Settings → Proxy →
Advanced).
- **Proxy settings UI: less-used options moved under Advanced.**
Settings → Proxy now shows the day-to-day tuning fields by default
(`buffering_timeout`, `buffering_speed`, `channel_shutdown_delay`,
`new_client_behind_seconds`). **Buffer Chunk TTL**, **Channel
Initialization Timeout**, and **Client Connect Grace Period** are tucked
under **Show Advanced Settings**.

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Renovate
Bot](https://redirect.github.com/renovatebot/renovate).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xMzAuMSIsInVwZGF0ZWRJblZlciI6IjQzLjEzMC4xIiwidGFyZ2V0QnJhbmNoIjoibWFzdGVyIiwibGFiZWxzIjpbImFwcC9kaXNwYXRjaGFyciIsImF1dG9tZXJnZSIsInJlbm92YXRlL2NvbnRhaW5lciIsInR5cGUvcGF0Y2giXX0=-->
2026-06-30 21:15:26 +02:00
TrueCharts Bot 21581d92d5 feat(helm-deps): update chart common 29.5.3 → 29.6.0 (#49561)
This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [common](https://truecharts.org/charts/library/common)
([source](https://ghcr.io/cloudnative-pg/postgis)) | minor | `29.5.3` →
`29.6.0` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the [Dependency
Dashboard](../issues/18710) for more information.

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Renovate
Bot](https://redirect.github.com/renovatebot/renovate).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xMzAuMSIsInVwZGF0ZWRJblZlciI6IjQzLjEzMC4xIiwidGFyZ2V0QnJhbmNoIjoibWFzdGVyIiwibGFiZWxzIjpbImFwcC96d2F2ZWpzMm1xdHQiLCJhdXRvbWVyZ2UiLCJyZW5vdmF0ZS9jb250YWluZXIiLCJ0eXBlL21pbm9yIl19-->

Signed-off-by: Alfred Göppel <43101280+alfi0812@users.noreply.github.com>
Co-authored-by: Alfred Göppel <43101280+alfi0812@users.noreply.github.com>
2026-06-27 14:33:38 +02:00
TrueCharts Bot 0551aa8e0f fix(dispatcharr): update image docker.io/dispatcharr/dispatcharr 0.27.0 → 0.27.1 (#49493)
This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
|
[docker.io/dispatcharr/dispatcharr](https://redirect.github.com/Dispatcharr/Dispatcharr)
| patch | `4251290` → `f5cbc82` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the [Dependency
Dashboard](../issues/18710) for more information.

Add the preset `:preserveSemverRanges` to your config if you don't want
to pin your dependencies.

---

### Release Notes

<details>
<summary>Dispatcharr/Dispatcharr
(docker.io/dispatcharr/dispatcharr)</summary>

###
[`v0.27.1`](https://redirect.github.com/Dispatcharr/Dispatcharr/blob/HEAD/CHANGELOG.md#0271---2026-06-25)

[Compare
Source](https://redirect.github.com/Dispatcharr/Dispatcharr/compare/v0.27.0...v0.27.1)

##### Security

- Updated `Django` 6.0.5 → 6.0.6, resolving the following CVEs:
- **CVE-2026-6873**: Signed cookie salt namespace collision in
`HttpRequest.get_signed_cookie()`.
- **CVE-2026-7666**: Potential unencrypted email transmission via
STARTTLS in the SMTP backend.
- **CVE-2026-8404**: Potential private data exposure via case-sensitive
`Cache-Control` directives in `UpdateCacheMiddleware`.
- **CVE-2026-35193**: Potential private data exposure via missing `Vary:
Authorization` in `UpdateCacheMiddleware`.
- **CVE-2026-48587**: Potential private data exposure via whitespace
padding in the `Vary` header.
- Updated frontend npm dependencies to resolve 4 audit vulnerabilities
(1 low, 2 moderate, 1 high):
- Updated `vite` 7.3.2 → 7.3.5, resolving **moderate** NTLMv2 hash
disclosure via UNC path handling on Windows
([GHSA-v6wh-96g9-6wx3](https://redirect.github.com/advisories/GHSA-v6wh-96g9-6wx3))
and **high** `server.fs.deny` bypass on Windows alternate paths
([GHSA-fx2h-pf6j-xcff](https://redirect.github.com/advisories/GHSA-fx2h-pf6j-xcff))
- Updated `js-yaml` 4.1.1 → 5.1.0, resolving **moderate**
quadratic-complexity DoS in merge key handling via repeated aliases
([GHSA-h67p-54hq-rp68](https://redirect.github.com/advisories/GHSA-h67p-54hq-rp68))
- Updated `esbuild` 0.27.3 → 0.28.1, resolving **low** arbitrary file
read when running the development server on Windows
([GHSA-g7r4-m6w7-qqqr](https://redirect.github.com/advisories/GHSA-g7r4-m6w7-qqqr))

##### Added

- **Isolated backend test settings (`dispatcharr.settings_test`).**
`python manage.py test` now switches to this module automatically (via
`manage.py`). It creates an empty PostgreSQL `test_<dbname>` database
(same engine as production), uses the standard Postgres backend instead
of geventpool so `TestCase` transactions isolate correctly, and leaves
Celery tasks queued (no eager `post_save` signal runs during tests). Set
`TEST_USE_SQLITE=1` for an in-memory SQLite fallback when Postgres is
unavailable.

##### Performance

- **`get_vod_streams` and `get_series` XC API endpoints are faster and
no longer exhaust Docker `/dev/shm`.** Large libraries (e.g. 125k
movies) previously ran one wide `DISTINCT ON` query with parallel
workers, which could fail with `could not resize shared memory segment …
No space left on device` on the default 64MB container shm. Both
endpoints now fetch display columns via `.values()` (no ORM model
instantiation per row). Redundant `category` and `logo` joins were
dropped in favor of FK ids; alphabetical sort runs in SQL. Typical
full-library response time drops from \~23–28s to \~8–10s with stable
shm usage.
- **XMLTV EPG export is faster and no longer balloons worker memory.**
`generate_epg()` was reworked end-to-end for large guides. (Fixes
[#&#8203;1366](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/1366))
- Streams incrementally: on a cache miss each chunk is pushed to a Redis
list as it is yielded (no `''.join()` in the worker); repeat requests
within 300s stream chunks back from Redis. `malloc_trim` runs after cold
builds.
- Channel streams are prefetched once (only `id`/`name`) instead of one
query per custom-dummy channel; dummy `EPGData` programme existence is
bulk-checked in a single query.
- The primary channel id is escaped once per `epg_id` group instead of
once per programme (\~750k fewer `html.escape` calls on a large guide).
- The channel query no longer JOINs multi-MB `programme_index` blobs per
channel (\~13s saved on a \~2000-channel guide; indices live in
`EPGSourceIndex`).
- Programme export uses `(epg_id, id)` keyset pagination with a
per-source `start_time` sort; a matching composite index on
`ProgramData` (created `CONCURRENTLY` on PostgreSQL) lets each chunk use
an ordered index range scan instead of re-sorting every chunk.
- **EPG grid endpoint releases its payload memory back to the OS.**
`/api/epg/grid/` drops the redundant full-list copy when appending dummy
programmes and runs `malloc_trim` once the response is sent, so worker
RSS no longer ratchets up \~20MB per request.

##### Changed

- **`programme_index` moved off `EPGSource` into a dedicated
`EPGSourceIndex` table.** The multi-MB byte-offset index was repeatedly
pulled into web and Celery workers by ordinary `EPGSource` queries and
`select_related` JOINs (which ignore manager-level `defer()`). It now
lives in a one-to-one `EPGSourceIndex` row, read only when explicitly
accessed through the `EPGSource.programme_index` property, so no list,
detail, or JOIN query can load it by accident. Migration `0026` copies
existing indices across. No API or index-build behavior change.

- **EPG generation extracted into `apps/output/epg.py`.** All XMLTV
output logic (`generate_epg`, `generate_dummy_programs`,
`generate_custom_dummy_programs`, `generate_dummy_epg`, and supporting
helpers) moved from `apps/output/views.py` into a dedicated module.
`views.py` retains the thin HTTP endpoint wrappers and auth checks;
`epg.py` handles all content generation. No behavior change.

- **Channel list with nested streams loads faster.** `GET
/api/channels/channels/?include_streams=true` (Channels UI and
single-channel fetch) now builds nested stream payloads from the
prefetched `channelstream_set` instead of issuing one extra streams M2M
query per channel.

- Dependency updates:
  - `Django` 6.0.5 → 6.0.6 (security patch; see Security section)
  - `requests` 2.33.1 → 2.34.2
  - `gevent` 26.4.0 → 26.5.0
  - `torch` 2.11.0+cpu → 2.12.1+cpu
  - `sentence-transformers` 5.4.1 → 5.6.0
  - `lxml` 6.1.0 → 6.1.1

##### Fixed

- **Channel Initialization Grace Period is honoured during live stream
startup.** Preview and playback no longer abort after a hardcoded 10s
while the channel is still connecting with an empty buffer; the TS
generator init-wait stall check and upstream health monitor now use the
configured `channel_init_grace_period` (same as the server cleanup
watchdog) instead of `CONNECTION_TIMEOUT`. (Fixes
[#&#8203;1380](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/1380))
- **Channels are marked `active` as soon as the buffer threshold is met
and a client is streaming.** Once the initial buffer fills, state is set
to `active` immediately when viewers are attached, or
`waiting_for_clients` when the buffer is ready but no client is
connected yet (e.g. proxy API warmup). The cleanup watchdog no longer
waits an extra grace period before promoting to `active`. Proxy settings
copy now describes `channel_init_grace_period` as an initialization
buffer timeout.
- **DVR recording playback auth is complete for native video, HLS
segments, and redirects.** Completed recordings use `/file/` with native
`<video src>`, which cannot send `Authorization` headers. Playback
accepts `?token=` query-param JWT (matching VOD streams), and the
floating video player appends the token when assigning native URLs. The
explicit HLS URL route (`/api/channels/recordings/.../hls/...`) now
registers the same authenticators as the ViewSet `@action`
handlers—previously only header JWT applied on that path, so `?token=`
failed for native HLS clients. When the request used `?token=`,
rewritten playlist segment URLs and `/file/`↔`/hls/` redirects preserve
the token; hls.js clients that authenticate via `Authorization` are
unchanged. `QueryParamJWTAuthentication` reads `request.query_params` on
DRF requests.
- **In-progress DVR playback no longer jumps to the live edge.** The
floating video player still opens in-progress recordings at the start of
the seekable range, but hls.js was configured with
`liveMaxLatencyDurationCount: 10`, which forced the playhead forward to
"now" shortly after playback began. Live-edge sync is now disabled for
recording HLS so users can watch, pause, and scrub from the beginning
while the recording continues. (Fixes
[#&#8203;1329](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/1329))
- **`refresh_single_m3u_account` no longer re-raises after setting
account ERROR.** Matches `refresh_epg_data`: the account status and UI
already reflect the failure; Celery no longer marks the task FAILED
after the terminal error state is persisted.
- **M3U refresh recovers DB connections and account status after
failures.** Celery workers now call `close_old_connections()` before and
after every task; `refresh_single_m3u_account` also resets its DB
connection at task start and retries account loads once after a
connection reset (avoids opaque `list index out of range` errors from
poisoned worker connections). Accounts leave `fetching`/`parsing` for a
terminal `error` or `success` state when refresh aborts. Error-path
status updates use `QuerySet.update()` on a clean connection instead of
`save()` on a connection that may already be INTRANS after
`refresh_m3u_groups` or batch ORM failures. Failed group downloads now
set `error` instead of returning silently. Refresh start replaces stale
`last_message` text. `refresh_account_profiles` releases DB connections
after each profile failure. (Fixes
[#&#8203;1338](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/1338))
- **EPG refresh recovers DB connections and source status after
failures.** `refresh_epg_data` now mirrors the M3U hardening: connection
reset at task start/end, retried source load after a poisoned-worker
connection reset, `QuerySet.update()` for error status on a clean
connection, and a `finally` guard that moves sources stuck in
`fetching`/`parsing` to `error`. Programme index builds are skipped when
programme parsing fails. `parse_channels_only` now persists the error
status when a missing file has no URL to refetch.
- **M3U `custom_properties` JSON normalization.** Legacy rows and some
API writes could store a JSON-encoded string instead of an object in
`custom_properties`, causing refresh, profile sync, and auto-sync to
fail with `'str' object has no attribute 'get'`. M3U account, profile,
and group-relation models normalize on `save()`; group settings bulk
writes and read/merge paths use shared helpers in `core.utils` without
re-parsing dict values.
- **`POST /api/epg/import/` no longer loads the full EPG source row.**
The dummy-source guard uses a narrow existence query instead of
`objects.get()`, keeping the import trigger lightweight.
Request/response shape is unchanged for the UI, plugins, and external
importers.
- **XC live playback URL building now normalizes account server URLs.**
On-demand live URLs (introduced for Server Group profile rotation in
0.27.0) rebuild `/live/{user}/{pass}/{stream_id}.ts` from current
credentials instead of reusing the synced `stream.url`. That path now
runs `normalize_server_url()` so pasted API URLs (e.g. `/player_api.php`
with query params) are stripped while sub-paths like `/server1` are
preserved. `get_transformed_credentials()` normalizes the base URL at
the source; VOD movie and episode relations use the same helper instead
of constructing a throwaway `XCClient` (which also avoided per-request
HTTP session setup). (Fixes
[#&#8203;1363](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/1363))
- **Live proxy now releases geventpool DB connections on more paths.**
`stream_ts()` calls `close_old_connections()` before
`StreamingHttpResponse` so clients waiting on channel init do not keep a
pool slot while the generator polls Redis. `generate_stream_url()`,
`get_stream_info_for_switch()`, `get_alternate_streams()`, and
`get_connections_left()` release in `finally` blocks after ORM work on
stream-manager failover paths that run outside Django's request cycle.
TS client disconnect cleanup matches fMP4, and `ChannelService` metadata
helpers release after their ORM lookups.
- **OpenAPI schema paths for nested M3U profiles and filters no longer
contain escaped slashes.** Router regex prefixes used unnecessary `\/`,
which drf-spectacular serialized literally into the schema and broke
client generators (e.g. oapi-codegen). Runtime URL matching is
unchanged. (Fixes
[#&#8203;1384](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/1384))
- **Auto-sync range conflict warning no longer flags a group's own
channels when using a channel-group override.** The M3U group settings
"Range conflict" check compared occupant channel groups against the
source group being configured. Auto-sync with `group_override` creates
channels in the override target group, so those channels were
misclassified as conflicts. The frontend now resolves the effective
target group (override target when set, otherwise the source group) for
classification. Genuine conflicts—manual channels, other accounts,
different groups, or user-pinned numbers—still surface. (Fixes
[#&#8203;1331](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/1331))
— Thanks [@&#8203;CodeBormen](https://redirect.github.com/CodeBormen)

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Renovate
Bot](https://redirect.github.com/renovatebot/renovate).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xMzAuMSIsInVwZGF0ZWRJblZlciI6IjQzLjEzMC4xIiwidGFyZ2V0QnJhbmNoIjoibWFzdGVyIiwibGFiZWxzIjpbImFwcC9kaXNwYXRjaGFyciIsImF1dG9tZXJnZSIsInJlbm92YXRlL2NvbnRhaW5lciIsInR5cGUvcGF0Y2giXX0=-->
2026-06-26 06:06:08 +02:00
TrueCharts Bot a0461550ed chore(dispatcharr): update image alpine digest to 28bd5fe (#49261)
This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
| alpine | digest | `a2d49ea` → `28bd5fe` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the [Dependency
Dashboard](../issues/18710) for more information.

Add the preset `:preserveSemverRanges` to your config if you don't want
to pin your dependencies.

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Renovate
Bot](https://redirect.github.com/renovatebot/renovate).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xMzAuMSIsInVwZGF0ZWRJblZlciI6IjQzLjEzMC4xIiwidGFyZ2V0QnJhbmNoIjoibWFzdGVyIiwibGFiZWxzIjpbImFwcC9kaXNwYXRjaGFyciIsImF1dG9tZXJnZSIsInJlbm92YXRlL2NvbnRhaW5lciIsInR5cGUvZGlnZXN0Il19-->
2026-06-19 01:31:00 +02:00
TrueCharts Bot e38dcd81f7 feat(dispatcharr): update image docker.io/dispatcharr/dispatcharr 0.26.0 → 0.27.0 (#49205)
This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
|
[docker.io/dispatcharr/dispatcharr](https://redirect.github.com/Dispatcharr/Dispatcharr)
| minor | `9275b0c` → `4251290` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the [Dependency
Dashboard](../issues/18710) for more information.

Add the preset `:preserveSemverRanges` to your config if you don't want
to pin your dependencies.

---

### Release Notes

<details>
<summary>Dispatcharr/Dispatcharr
(docker.io/dispatcharr/dispatcharr)</summary>

###
[`v0.27.0`](https://redirect.github.com/Dispatcharr/Dispatcharr/blob/HEAD/CHANGELOG.md#0270---2026-06-16)

[Compare
Source](https://redirect.github.com/Dispatcharr/Dispatcharr/compare/v0.26.0...v0.27.0)

##### Added

- **Manual Server Groups for shared M3U connection limits.** The
existing `ServerGroup` model and account FK are now wired into live and
VOD playback. Accounts assigned to the same group share a
credential-scoped Redis counter when their provider logins match (hashed
fingerprint); unrelated logins in the same group keep separate counters.
Enforcement uses each profile's `max_streams` - not a group-wide cap -
and profiles with `max_streams=0` skip credential pooling for that
profile while still rotating on their own per-profile counter. New
`apps/m3u/connection_pool.py` centralizes reserve/release, profile
rotation, and credential moves on profile switch. (Closes
[#&#8203;1137](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/1137))
— Thanks
[@&#8203;Goldenfreddy0703](https://redirect.github.com/Goldenfreddy0703)
- **Server Groups manager UI** on the M3U Accounts page: create, rename,
and delete groups with account counts and delete confirmation. Groups
load on login via a new Zustand store and REST helpers
(`/api/m3u/server-groups/`).
- **M3U account form** adds a Server Group picker (including inline “add
group”), reorganized into three columns (source/auth, connection limits,
sync/content), and a Manage server groups shortcut.
- **VOD profile selection** uses `pool_has_capacity_for_profile()` so
grouped accounts respect shared credential limits before a profile is
chosen (non-grouped accounts behave as before).
- **Live profile switches** move the shared credential counter when the
new profile uses a different provider login; same-login switches leave
the credential counter unchanged.
- **Credential release keys** stored at reserve time allow counters to
be released even if the M3U profile row is deleted afterward.
- **PostgreSQL `application_name` tagging by process role.** Pool
connections now set `application_name` at connect time (e.g.
`Dispatcharr-uwsgi-{pid}`, `Dispatcharr-celery-worker-{pid}`,
`Dispatcharr-celery-dvr-{pid}`) so `pg_stat_activity` shows which
Dispatcharr process owns each backend instead of a generic client label.

##### Changed

- **Plugin repo manifests support split download and metadata base
URLs.** `download_base_url` and `metadata_base_url` are optional
alternatives to `root_url` in the plugin repository manifest, so repo
authors can serve metadata (manifests, icons) and release zips from
different origins without absolute URLs everywhere. Manifest and icon
URLs resolve via `metadata_base_url` then `root_url`; latest/download
URLs resolve via `download_base_url` then `root_url`. When
`metadata_base_url` is set and a plugin entry has `manifest_url` but no
`icon_url`, the icon defaults to `logo.png` in the same directory as the
per-plugin manifest. Manifests using only `root_url` behave identically
to before. — Thanks [@&#8203;sethwv](https://redirect.github.com/sethwv)
- **Live and VOD connection slot logic now routes through
`connection_pool`.** `Channel.get_stream()`, direct
`Stream.get_stream()`, VOD profile selection, and the multi-worker VOD
connection manager all call `reserve_profile_slot()` /
`release_profile_slot()` instead of inline Redis INCR/DECR. VOD profile
selection also checks `pool_has_capacity_for_profile()` before choosing
a profile.
- **Live XC upstream URLs use current credentials.**
`_resolve_live_stream_url()` builds `/live/{user}/{pass}/{stream_id}.ts`
from transformed account credentials and the stream's provider
`stream_id`, so playback stays aligned with the active login after
credential or profile changes instead of reusing a stale `stream.url`
from sync.
- **Channel stream switches check pooled capacity.**
`get_stream_info_for_switch()` uses
`profile_available_for_channel_switch()` when targeting a specific
stream, and releases a reserved slot if URL assembly fails after
`get_stream()` allocated one.
- **Live proxy init reads Redis assignment once.** After
`generate_stream_url()` reserves a slot, the stream handler reads
`channel_stream` / `stream_profile` from Redis instead of calling
`get_stream()` again, avoiding double INCR under concurrent release.
- **Centralized Dispatcharr User-Agent construction in `core.utils`.**
Outbound HTTP calls (Schedules Direct API, update checks, logo/VOD
fallbacks, DVR recording clients) now use `dispatcharr_user_agent()`,
`dispatcharr_dvr_user_agent()`, and `dispatcharr_http_headers()` instead
of ad-hoc `Dispatcharr/{version}` strings and stale `Dispatcharr/1.0`
fallbacks.
- **EPG auto-match overhaul** — matching logic moved to
`apps/channels/epg_matching.py`; Celery tasks in `tasks.py` are thin
wrappers.
- Single-channel auto-match is now asynchronous: the API returns `202
Accepted` and pushes the result over WebSocket
(`single_channel_epg_match`), so large EPG libraries no longer hit the
previous 30-second HTTP timeout.
- Progress, bulk completion, and single-channel results use
`send_websocket_update` instead of
`async_to_sync(channel_layer.group_send)`, so notifications work
reliably under gevent-patched uWSGI and Celery workers.
- Single-channel and selected-channel auto-match always run, even when
the channel already has EPG assigned; match-all (no channel IDs) still
only processes channels without EPG.
- Rematching to the same EPG no longer re-saves the channel or queues
program-parse tasks; only assignments that actually change are written
and refreshed.
- **Easier EPG search when editing a channel.** The filter in the EPG
picker now works more like a normal search box. You can type several
words at once (e.g. `sky uk`) and it finds channels where every word
appears somewhere in the name or TVG-ID, the order doesn't matter, and a
word in the name can pair with one in the ID. Accents are ignored too,
so `decale` matches `Décalé` and the other way around. — Thanks
[@&#8203;FiveBoroughs](https://redirect.github.com/FiveBoroughs)

##### Performance

- **XC live refresh releases bulk catalog data sooner during batch
processing.** After filtering the single `get_all_live_streams`
response, the full provider catalog list is dropped before batch DB
work. `process_m3u_batch_direct` (the path XC refreshes use) now runs
`gc.collect()` after each batch and clears batch slice references as
thread futures complete. Large structures are still `del`'d in the
refresh `finally` block before Celery's `task_postrun` runs
`cleanup_memory()`.
- **VOD movie/series batch matching no longer scans the full no-ID
catalog.** `process_movie_batch` and `process_series_batch` previously
loaded every `Movie`/`Series` row without TMDB or IMDB IDs on each
1000-item chunk to resolve name+year duplicates. Lookup is now scoped to
the names in the current batch via `lookup_by_name_year()`, which
reduces memory and DB time per chunk. `refresh_vod_content` and
`batch_refresh_series_episodes` are registered for Celery post-task
memory cleanup (one GC pass at task end, not per chunk).
- **EPG programme parse now streams through PostgreSQL staging instead
of holding the full catalogue in Python.** `parse_programs_for_source`
writes parsed rows into a session-scoped temp table in batches
(`_EPG_PARSE_BATCH_SIZE=2500`), then atomically swaps them into
`ProgramData` with batched `DELETE ... RETURNING` + `INSERT`
(`_EPG_SWAP_BATCH_SIZE=5000`) so Postgres never materializes the entire
guide in one statement. Peak Celery memory during large XMLTV refreshes
drops sharply compared with building a monolithic in-memory list before
bulk insert. The byte-offset programme index build is deferred until
after the swap completes so index construction no longer competes with
parse for memory and I/O. `refresh_epg_data` closes its DB connection in
`finally`, and `build_programme_index_task` is registered as a
memory-intensive Celery task. SQLite/dev installs keep an in-memory
fallback path.
- **Pooled PostgreSQL connections now rotate on a bounded lifetime.**
psycopg3 client-side cache grows on handles kept open indefinitely by
`django-db-geventpool`; recycling uWSGI workers would interrupt live
streams. A thin custom backend
(`dispatcharr.db.backends.postgresql_psycopg3`) closes and replaces pool
connections after `DATABASE_POOL_CONN_MAX_LIFETIME` seconds (default
600, env-overridable; set `0` to disable) on checkout and return while
preserving warm-pool reuse within the window. (Fixes
[#&#8203;1343](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/1343))
- **Schedules Direct refresh only fetches guide data for mapped
channels.** Schedule MD5 checks and schedule downloads now target mapped
lineup stations instead of the entire lineup, and schedule MD5 cache for
unmapped stations is pruned each refresh. Unmapped lineup entries no
longer trigger wasted schedule API calls when their MD5 changes.
- **EPG auto-match memory and throughput improvements.**
- Single-channel matching streams active EPG rows and keeps only the
best match plus the top 20 candidates in memory; ML validates at most 21
names per channel instead of embedding the full catalog.
- Strong fuzzy matches (≥75% single channel, ≥80% bulk) skip ML
entirely, avoiding a \~500MB PyTorch load when the fuzzy result is
already reliable.
- Bulk matching uses a single fuzzy pass per channel instead of scanning
the full catalog twice for best match and top candidates.
- Bulk exact `tvg_id` / Gracenote matching uses an in-memory index built
alongside the EPG catalog (`build_epg_matching_catalog()`), giving O(1)
lookups with no extra database queries.
- Bulk match apply uses batched queries (two fetches plus `bulk_update`)
instead of one `EPGData.objects.get()` per matched channel.
- EPG normalization settings are cached once per matching run, avoiding
repeated `CoreSettings` reads when normalizing thousands of names.

##### Fixed

- **Live proxy channels could remain running with no clients after
disconnect.** `stop_channel` called `log_system_event('channel_stop')`
synchronously before local cleanup and Redis key deletion; Connect
integrations or DB event writes on that path could block teardown
indefinitely while the cleanup thread kept refreshing metadata TTL for a
stale `stream_buffers` entry. Teardown now signals shutdown
(`buffer.stopping`), runs model `release_stream()` and Redis key
deletion, releases ownership, stops ffmpeg/output managers and local
buffers, and only then logs the stop event asynchronously. Metadata TTL
refresh skips channels mid-shutdown and non-owned channels; a stuck-stop
watchdog forces cleanup if `stop_channel` does not return within \~10s
(or 2× `channel_shutdown_delay`, whichever is greater). Stream buffers
ignore late `add_chunk()` writes once shutdown begins (`buffer.stopping`
set at teardown start).
- **Client reconnect during channel teardown could wedge the channel
across uWSGI workers.** The disconnect path called
`proxy_server.stop_channel()` directly, so other workers were not
notified and reconnecting clients could attach while ownership was
released on the owner worker. The cleanup thread then re-acquired
expired ownership and looped on orphaned clients while the upstream
connection stayed open. All lifecycle stops now go through coordinated
Redis teardown (`channel_stopping` flag, metadata `stopping` state, and
`CHANNEL_STOP` pubsub). New stream requests are rejected with `503
Retry-After` only during active teardown (not during the post-disconnect
shutdown delay grace period); `initialize_channel()` and ownership
re-acquisition refuse to proceed in those states; non-owner workers
clean local resources only on pubsub; and orphaned-client sweeps force
cleanup when teardown is already active. (Fixes
[#&#8203;1342](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/1342))
- **Orphaned upstream threads kept writing Redis after teardown.**
Partial cleanup could remove a channel from `stream_managers` while the
`stream-{uuid}` OS thread kept running, so orphan metadata sweeps only
deleted Redis keys and chunks/metadata were immediately recreated (bare
`total_bytes` hash with TTL -1). Stream managers now stop upstream when
ownership is lost, orphan cleanup stops local ffmpeg/stream threads even
when registry entries are gone (`_live_stream_managers` tracks managers
until the thread exits), and forced recovery stops processes before
Redis deletion instead of dropping dict entries without calling
`stop()`.
- **Rapid channel switching could leave bare `buffer:index` keys in
Redis.** `buffer.stop()` flushed a final partial chunk via `INCR` during
teardown, creating a persistent index key (TTL -1) after chunk keys
expired; under load overlapping disconnect and cleanup-thread stops
could race on `_stop_local_stream_activity`, blocking on ffmpeg stderr
join before `_clean_redis_keys` ever ran. Teardown no longer writes to
Redis from `buffer.stop()`, disconnect uses coordinated stop only (no
duplicate upstream stop), model `release_stream()` runs before Redis
keys are scanned/deleted (so `profile_connections` counters are not left
stuck), ownership is released after Redis cleanup but before the
blocking local ffmpeg stop, and a `finally` block guarantees Redis
cleanup if local teardown raises.
- **Live proxy could leak geventpool DB checkouts outside HTTP
requests.** With `django-db-geventpool`, `connection.close()` returns
handles to the per-worker pool (`MAX_CONNS=8`); without it, greenlets
and OS threads keep connections checked out until the pool blocks on
`pool.get()`. Proxy paths that touch the ORM outside Django's request
cycle now call `close_old_connections()` after work completes:
`_clean_redis_keys()`, profile lookup in
`_establish_transcode_connection()` before spawning ffmpeg, and fMP4
client disconnect cleanup when releasing streams (TS disconnect relies
on `log_system_event()`). (Fixes
[#&#8203;1345](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/1345))
- **System events could block callers on Connect and plugin handlers.**
`log_system_event()` ran Connect subscriptions and plugin `"events"`
hooks synchronously after the DB write, so slow integrations could stall
live-proxy and streaming paths even when inserting the event row was
fast. Connect/plugin dispatch now runs on a separate gevent when the hub
is available (synchronously in Celery workers without a hub).
`log_system_event()` and `dispatch_event_system()` also call
`close_old_connections()` in `finally` blocks so integration work does
not leave pool slots checked out on the caller or dispatch greenlet.
- **Plugins could leak geventpool DB checkouts after UI or Connect event
runs.** Third-party plugins run inline on uWSGI greenlets (manual
actions and Connect `"events"` hooks) with no guaranteed connection
cleanup at the plugin boundary. `PluginManager.run_action()` and
`stop_plugin()` now call `close_old_connections()` in a `finally` block
so each action returns its pool slot whether the plugin succeeds or
raises.
- **Connect events repeatedly queried the full plugin catalog during
streaming.** `trigger_event()` called `list_plugins()` on every
`client_connect` / `client_disconnect`, loading all `PluginConfig` rows
and sometimes hitting plugin-repo work even when no plugin subscribed to
the event. Dispatch now walks the in-memory registry via
`iter_actions_for_event()`, returns immediately when no handlers exist,
and runs a single batched `enabled` lookup for matching plugins only.
Failed plugin actions are logged per handler instead of aborting the
rest.
- **Plugin discovery left idle Postgres backends after worker boot.**
`discover_plugins()` runs outside Django's request cycle during uWSGI
and Celery startup; it now calls `close_old_connections()` in a
`finally` block so bootstrap `PluginConfig` queries return their pool
slot instead of appearing as long-lived idle connections in
`pg_stat_activity`.
- **VOD proxy could leak geventpool DB checkouts during playback and
stats updates.** `stream_vod()` ran ORM lookups for content and M3U
profiles, then returned a long-lived `StreamingHttpResponse` without
releasing the checkout, so each movie/episode stream could hold a pool
slot for its full duration. Background VOD stats refresh
(`build_vod_stats_data()`, triggered on start/stop and by the admin
stats API) also queried movie, episode, and profile rows from daemon
threads with no cleanup. `stream_vod()` now calls
`close_old_connections()` before handing off to the streaming generator,
and `build_vod_stats_data()` releases its checkout in a `finally` block.
- **Channel shutdown delay did not reset after a reconnect within the
grace period.** `handle_client_disconnect()` used a fixed
`gevent.sleep(shutdown_delay)` from the first last-client disconnect. If
a client reconnected and disconnected again during the delay, an earlier
disconnect handler could still stop the channel on the original timer
instead of waiting the full delay from the latest disconnect. Shutdown
now polls Redis (`last_client_disconnect` timestamp and client count) so
concurrent disconnect handlers and multi-worker reconnects always honour
the latest disconnect time.
- **Zombie ffmpeg could survive owner-lock expiry and orphan Redis
sweeps.** When the 30s owner lock lapsed under single-worker load,
disconnect handling treated the worker as non-owner so coordinated stop
never ran, while ffmpeg kept writing and the orphan sweeper only deleted
Redis keys (recreating them immediately). Disconnect now re-acquires
ownership when local upstream is still active, stops locally when the
last client leaves without a lock, orphan cleanup stops local ffmpeg
before Redis deletion via `_has_local_upstream_activity`, re-init stops
lingering upstream before starting a duplicate thread, and
`is_channel_teardown_active` includes channels mid-`stop_channel` on
this worker so rapid reconnect gets 503 during teardown.
- **Stale `channel_stream` Redis keys after a channel stopped could skip
connection accounting on retune.** On `dev`, `get_stream()` reused any
existing `channel_stream` / `stream_profile` assignment without
reserving a new slot. If those keys were left behind after a stop, the
next tune-in could reach the provider without incrementing Redis
counters. `get_stream()` now releases stale assignments when proxy
metadata shows the channel is inactive, then reserves fresh slots.
- **EPG auto-match reliability fixes.**
- Memory could spike to multiple GB on large EPG sources when building a
full in-memory catalog before fuzzy matching; single-channel matching
now streams rows and bounds ML work to a small candidate set.
- Wrong channel assignments from global ML similarity; ML validation now
checks the fuzzy best match (or top fuzzy candidates as a last resort)
instead of scoring the entire catalog.
- Channel form auto-match spinner could stick after errors or early task
exits; all single-channel outcomes now push a WebSocket result, and the
UI clears loading state after a 3-minute timeout.
- Bulk auto-match completion no longer calls `batch-set-epg` from the
WebSocket handler, which had been re-applying every match and queueing
redundant `parse_programs_for_tvg_id` tasks even when assignments were
unchanged.
- **Schedules Direct lineup search country dropdown.** The country list
was fetched directly from the SD API in the browser, which failed due to
CORS and silently fell back to a 14-country hardcoded list. Countries
are now included in the `GET sd-lineups` response (server-side fetch
with proper User-Agent) so the full SD country list populates correctly.
— Thanks [@&#8203;sethwv](https://redirect.github.com/sethwv)
- **Schedules Direct program poster proxy omitted User-Agent on image
requests.** The poster endpoint authenticated with SD correctly but
fetched images with only the `token` header, which violates SD's API
requirements (error 1003). Image requests now include the standard
`Dispatcharr/{version}` User-Agent via `dispatcharr_http_headers()`.
- **Schedules Direct guide data missing after mapping a channel.**
Lineup refreshes cached schedule MD5s for all stations, but
`ProgramData` was only written for mapped channels. Mapping a channel
later could leave MD5s looking unchanged so schedules were skipped and
the channel had no guide until dates rolled outside the cached window.
Refreshes now backfill fetch-window dates that lack `ProgramData`
(including newly mapped stations with stale cache), fetch program
metadata when no local `ProgramData` exists for a `programID`, and clean
up orphaned `ProgramData` on unmapped `EPGData` entries. — Thanks
[@&#8203;sethwv](https://redirect.github.com/sethwv)
- **Schedules Direct guide fetch on channel map.** Mapping a channel (or
bulk-assigning EPG) now triggers a targeted guide fetch for that
`EPGData` entry—mirroring XMLTV's `parse_programs_for_tvg_id`
flow—without waiting for the next full source refresh. Skips the fetch
when `ProgramData` already exists so additional channels sharing the
same `tvg-id` do not trigger redundant API calls. Bulk assignment of
three or more SD stations without guide data on the same source queues
one batched mapped-station fetch instead of separate per-station API
sessions. Concurrent batch and single-EPG fetches coordinate via
source-level locks with deferred retries so mappings are not dropped and
overlapping SD API sessions are avoided when possible.
- **Auto-sync numbering modes now read only the fields each mode's UI
exposes.** After the auto-sync overhaul, switching between Provider,
Next Available, and Fixed modes left stale `auto_sync_channel_start` /
`auto_sync_channel_end` values in the database while each mode's UI only
edits a subset of those fields. Sync treated the hidden values as
authoritative in every mode, which discarded valid provider numbers
(floored at an auto-computed start), capped Next Available at a stale
End, and ran range-enforcement deletes against provider- and
next-available-numbered channels. Provider mode now honors `stream_chno`
verbatim when free (Start/End bound only the fallback for numberless
streams); Next Available ignores End; overflow-delete runs in Fixed mode
only. Duplicate or already-taken provider numbers fall back to a free
slot instead of being dropped or overwriting an existing channel.
Provider mode's Start # field now drops End when it would invert the
fallback range (matching Fixed mode). (Closes
[#&#8203;1273](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/1273))
— Thanks [@&#8203;CodeBormen](https://redirect.github.com/CodeBormen)

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Renovate
Bot](https://redirect.github.com/renovatebot/renovate).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xMzAuMSIsInVwZGF0ZWRJblZlciI6IjQzLjEzMC4xIiwidGFyZ2V0QnJhbmNoIjoibWFzdGVyIiwibGFiZWxzIjpbImFwcC9kaXNwYXRjaGFyciIsImF1dG9tZXJnZSIsInJlbm92YXRlL2NvbnRhaW5lciIsInR5cGUvbWlub3IiXX0=-->
2026-06-17 00:51:13 +02:00
TrueCharts Bot 371941ac91 feat(external-service): update image alpine (#49074)
This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
| alpine | minor | `5b10f43` → `a2d49ea` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the [Dependency
Dashboard](../issues/18710) for more information.

Add the preset `:preserveSemverRanges` to your config if you don't want
to pin your dependencies.

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

👻 **Immortal**: This PR will be recreated if closed unmerged. Get
[config
help](https://redirect.github.com/renovatebot/renovate/discussions) if
that's undesired.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Renovate
Bot](https://redirect.github.com/renovatebot/renovate).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xMzAuMSIsInVwZGF0ZWRJblZlciI6IjQzLjEzMC4xIiwidGFyZ2V0QnJhbmNoIjoibWFzdGVyIiwibGFiZWxzIjpbImFwcC9leHRlcm5hbC1zZXJ2aWNlIiwiYXV0b21lcmdlIiwicmVub3ZhdGUvY29udGFpbmVyIiwidHlwZS9taW5vciJdfQ==-->
2026-06-13 09:12:55 +02:00
TrueCharts Bot 9394df1989 feat(helm-deps): update chart common 29.3.4 → 29.5.3 (#48994)
This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [common](https://truecharts.org/charts/library/common)
([source](https://ghcr.io/cloudnative-pg/postgis)) | minor | `29.3.4` →
`29.5.3` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the [Dependency
Dashboard](../issues/18710) for more information.

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Renovate
Bot](https://redirect.github.com/renovatebot/renovate).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xMzAuMSIsInVwZGF0ZWRJblZlciI6IjQzLjEzMC4xIiwidGFyZ2V0QnJhbmNoIjoibWFzdGVyIiwibGFiZWxzIjpbImFwcC96d2F2ZWpzMm1xdHQiLCJhdXRvbWVyZ2UiLCJyZW5vdmF0ZS9jb250YWluZXIiLCJ0eXBlL21pbm9yIl19-->

Signed-off-by: Alfred Göppel <43101280+alfi0812@users.noreply.github.com>
Co-authored-by: Alfred Göppel <43101280+alfi0812@users.noreply.github.com>
2026-06-11 05:24:51 +02:00
TrueCharts Bot 645e4dc12a feat(dispatcharr): update image docker.io/dispatcharr/dispatcharr 0.25.1 → 0.26.0 (#48862)
This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
|
[docker.io/dispatcharr/dispatcharr](https://redirect.github.com/Dispatcharr/Dispatcharr)
| minor | `2bdcfd9` → `9275b0c` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the [Dependency
Dashboard](../issues/18710) for more information.

Add the preset `:preserveSemverRanges` to your config if you don't want
to pin your dependencies.

---

### Release Notes

<details>
<summary>Dispatcharr/Dispatcharr
(docker.io/dispatcharr/dispatcharr)</summary>

###
[`v0.26.0`](https://redirect.github.com/Dispatcharr/Dispatcharr/blob/HEAD/CHANGELOG.md#0260---2026-06-07)

[Compare
Source](https://redirect.github.com/Dispatcharr/Dispatcharr/compare/v0.25.1...v0.26.0)

##### Added

- **EPG logo auto-apply for XMLTV and Schedules Direct.** Channel logos
are applied from `EPGData.icon_url` through a shared helper used by bulk
"Set Logos from EPG", optional per-source auto-apply on refresh
(`auto_apply_epg_logos` in source `custom_properties`), and a new
`epg_source_id` option on the set-logos-from-epg API. Large libraries
are processed in 500-channel chunks without loading every mapped row
into memory.
- **Schedules Direct EPG integration.** Dispatcharr now supports
Schedules Direct as a first-class EPG source type alongside XMLTV and
dummy EPG, with credential auth, lineup management, and guide refresh
through the existing EPG pipeline and WebSocket progress. (Closes
[#&#8203;1246](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/1246))
— Thanks [@&#8203;Shokkstokk](https://redirect.github.com/Shokkstokk)
- Lineup manager in EPG source settings: search by postal code,
add/remove up to four active lineups, with SD's six-adds-per-24-hours
limit and midnight-UTC reset surfaced in the UI.
- MD5 delta refresh skips unchanged schedule and program downloads; a
two-hour minimum interval between full refreshes is enforced (bypassable
via force refresh).
- Station logos in dark, light, gray, or white variants (`logo_style`),
stored in `EPGData.icon_url` alongside XMLTV channel icons.
- Optional program poster fetch (off by default): configurable style
preference (SD Recommended via Gracenote's `primary` flag, or
portrait/landscape banner/iconic variants with fallbacks), served
through a backend proxy cached by nginx on first view.
- XMLTV-compatible cast output (`role` for character names, `guest` for
guest stars).
- Lightweight stations-only fetch on source creation so EPG entries
exist for auto-matching before the first full schedule pull.
- **Live EPG program preview in the channel create/edit modal.** When
selecting an EPG channel in the channel form, a "Current Program" card
appears showing the currently-airing program title, description, and a
progress bar. The backend builds a byte-offset index over the raw XMLTV
file after each EPG refresh so lookups seek directly to the relevant
file positions rather than parsing the full file - returning results in
1-10ms regardless of source size. If the index has not yet been built
for a source, the API dispatches an async background build and the
frontend retries up to 20 times over a 3-minute window. The
`ProgramPreview` component was extracted into a shared component reused
by the Stats page. The `programme_index` field on `EPGSource` is
excluded from all API list responses to avoid returning multi-MB blobs.
— Thanks
[@&#8203;FiveBoroughs](https://redirect.github.com/FiveBoroughs)
- **Public IP display in the sidebar is now blurred by default and
reveals on click.** Prevents accidental exposure in screenshots and
screen shares. A new toggle in Settings > System Settings lets users
disable IP and geolocation fetching entirely. A
`DISPATCHARR_ENABLE_IP_LOOKUP` environment variable provides a
container-level override; when set to `false` the toggle is hidden from
the UI and cannot be changed. (Closes
[#&#8203;1302](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/1302))
— Thanks [@&#8203;sethwv](https://redirect.github.com/sethwv)
- **Configurable per-page count and sticky pagination footer in Plugin
Browse.** The pagination controls now live in a fixed footer bar at the
bottom of the page. A page-size selector (9 / 18 / 27 / 36) sits
alongside the pagination widget and an item range readout (`X to Y of
Z`). The selected page size is persisted in `localStorage` so it
survives page navigations. — Thanks
[@&#8203;sethwv](https://redirect.github.com/sethwv)
- **VOD basic sync now stores additional metadata from the provider's
stream list.** When a provider includes `director`, `cast`,
`release_date`, or `trailer`/`youtube_trailer` in its `get_vod_streams`
response, Dispatcharr now captures and stores those fields in
`custom_properties` during the initial sync pass. Previously, this data
was discarded and only populated if an advanced per-movie refresh was
triggered. - Thanks
[@&#8203;nemesbak](https://redirect.github.com/nemesbak)

##### Changed

- **`send_websocket_update` now detects Celery worker context when
gevent is monkey-patched.** EPG refresh progress (including Schedules
Direct) uses the shared `send_epg_update` helper instead of a duplicate
synchronous Redis sender. In Celery prefork workers that inherit gevent
patching, WebSocket messages are delivered synchronously via the
existing `_gevent_ws_send` Redis path; uWSGI continues to use
`gevent.spawn` as before.
- **Schedules Direct EPG delete confirmation shows username only.** The
delete dialog no longer surfaces password or API key fields for
credential-based SD sources.
- **Channel edit form reorganized into three semantic columns.** Fields
are now grouped as Identity (name, number, group, logo), Guide Data
(TVG-ID, Gracenote StationId, EPG picker, current program preview), and
Behavior/Access (stream profile, user level, mature content, hidden).
The EPG section having its own column gives the `ProgramPreview` card
enough width to truncate long titles correctly rather than expanding the
column.
- **IP lookup result delivered via WebSocket push.** When the background
lookup completes, an `ip_lookup_complete` event is pushed to all
connected clients so the sidebar IP field populates without polling. A
`Skeleton` placeholder is shown while the lookup is in progress.
- **`get_host_and_port` and `build_absolute_uri_with_port` moved from
`apps/output/views.py` to `core/utils.py`.** Both helpers have no
dependencies on anything in `apps/output` and are now used in
`apps/channels/serializers.py` as well. Moving them to `core/utils`
eliminates the need for a local import inside `LogoSerializer` and makes
them available to the rest of the codebase without circular-import risk.
`LogoSerializer.get_cache_url()` was also updated to use
`build_absolute_uri_with_port` instead of
`request.build_absolute_uri()`, so logo cache URLs now correctly include
non-standard ports (fixing port-stripping for logo URLs behind reverse
proxies, matching the existing fix applied to M3U and EPG URLs).
- **nginx logo/poster proxy cache moved to `/data/logo_cache`.**
`proxy_cache_path` now uses the persistent data volume instead of
`/app/logo_cache`, and the init script ensures the directory exists with
correct ownership on container start.
- **`debian_install.sh` switched from Gunicorn to uWSGI with gevent
workers.** The Debian/LXC bare-metal installer now deploys Dispatcharr
under the same uWSGI + gevent stack used by the Docker image,
eliminating a class of compatibility differences between the two
deployment paths. The installer writes a `uwsgi-debian.ini` next to the
app and manages uWSGI via a systemd service. The nginx site config now
uses `uwsgi_pass` + `include uwsgi_params` instead of `proxy_pass`,
which correctly populates `SERVER_PORT` in the WSGI environ so M3U
playlist URLs include the configured port number (fixing the
port-stripping bug from
[#&#8203;1267](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/1267)
for bare-metal installs). Python 3.13 is now provisioned through uv's
managed runtime so the install works on Debian 12 and Ubuntu 24.04 LTS
regardless of the system Python version.
- **`get_vod_streams` XC API response was missing metadata fields
available from basic sync.** When a provider includes `director`,
`cast`, `release_date`, `plot`, `genre`, or `year` in its
`get_vod_streams` response, Dispatcharr stores those fields during the
basic sync pass but was not including them in its own `get_vod_streams`
XC output. The endpoint now outputs all six fields. The `trailer` key in
the response also mapped to the wrong internal key (`trailer` instead of
`youtube_trailer`) following the storage key rename, so the trailer
field was always empty until an advanced refresh ran. Both issues are
corrected. Users with existing libraries should trigger a VOD provider
refresh to populate the missing fields. (Fixes
[#&#8203;1228](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/1228))
- **Docker base image now uses a multi-stage build.** The builder stage
installs compilers and dev headers (`gcc`, `g++`, `gfortran`,
`build-essential`, `libopenblas-dev`, `libpcre3-dev`, `python3.13-dev`,
`ninja-build`) to create the virtual environment and compile the legacy
NumPy wheel (cpu-baseline=none for old hardware). The final runtime
image starts from the same ffmpeg base, copies only the prebuilt venv
and wheel from the builder, and installs only the runtime libraries
needed at runtime - eliminating compiler binaries from production
containers and reducing final image size. — Thanks
[@&#8203;kensac](https://redirect.github.com/kensac)
- **`libpq-dev` removed from both build and runtime stages.** The
project uses `psycopg[binary]` (psycopg3), which bundles its own
statically linked copy of libpq inside the wheel. No system libpq
headers or shared library are needed at build or runtime.
- **Database driver upgraded from `psycopg2` to `psycopg3`
(`psycopg[binary]`).** psycopg3 rewrote its network I/O layer in Python,
so `gevent`'s `monkey.patch_all()` makes it gevent-cooperative without
any additional patching. The `psycogreen` dependency and its
driver-patching block in `gevent_patch.py` have been removed. Django's
native psycopg3 connection pool is explicitly disabled (`pool: False`)
so `django-db-geventpool` remains in sole control of connection
lifecycle. The `dropdb` management command was updated to the `psycopg`
API (`psycopg.connect`, `psycopg.sql`).
- **Frontend unit tests extended to additional form components and the
Guide page.** `ProgramRecordingModal`, `Recording`,
`RecordingDetailsModal`, `RecurringRuleModal`, `ScheduleInput`,
`SeriesRecordingModal`, `SeriesRuleEditorModal`, `Stream`,
`StreamProfile`, `SuperuserForm`, `User`, `UserAgent`, and
`VODCategoryFilter` now have Vitest + Testing Library test suites.
Business logic was extracted from these components into corresponding
utility modules and is exercised through the new tests. The Guide page
was similarly refactored with extracted utils, and `dateTimeUtils.js`
was extended to support the new test coverage. — Thanks
[@&#8203;nick4810](https://redirect.github.com/nick4810)
- **Official plugin repository manifest URL moved to GitHub Pages.** The
default `OFFICIAL_REPO_URL` now points to
`https://dispatcharr.github.io/Plugins/manifest.json` instead of the raw
GitHub content URL. GitHub Pages avoids opaque caching on raw content
that could serve stale manifests without indication. A data migration
updates existing `PluginRepo` rows marked `is_official=True` in place;
fresh installs pick up the new URL from the model default after the seed
migration runs. — Thanks
[@&#8203;sethwv](https://redirect.github.com/sethwv)

##### Performance

- **M3U and EPG channel logo URLs no longer call
`build_absolute_uri_with_port()` per channel.** The host/port/scheme
base URL and logo path prefix/suffix are computed once per request; each
row appends only the logo ID. M3U proxy stream URLs use the same pattern
for channel UUIDs.
- **`get_vod_streams` and `get_series` XC API response times reduced
significantly for large libraries.** Both endpoints previously loaded
all active relations per title (one row per account that carries the
same movie/series), then discarded duplicates in Python. With large
libraries across multiple active accounts this fetched a multiple of N
rows. Both queries now use a single `DISTINCT ON (movie_id/series_id)`
pass over the relation table ordered by account priority, returning
exactly one row per title. Logo URL construction (`reverse()` +
`build_absolute_uri_with_port()`) is also computed once and reused via
prefix/suffix string concatenation, matching the existing pattern in
`get_live_streams`. The `m3u_account` JOIN in `get_series` was also
removed (it was fetched but never read in the loop). Additionally,
`get_series` previously had no `order_by` on `M3USeriesRelation`, so
output order was non-deterministic (physical storage order). Both
endpoints now sort alphabetically by title. Observed improvements: 18s →
12s for 48k movies; 9.5s → 3.5s for 11k series.
- **Database connections are now managed by a persistent per-worker
pool.** Previously each request opened a fresh TCP connection to
PostgreSQL, paid a full authentication handshake, and closed the
connection at request end. `django-db-geventpool` now maintains a pool
of warm connections per uWSGI worker; requests borrow a connection and
return it when done, eliminating connection-setup overhead on every
request. Pool size is bounded (`MAX_CONNS=8` per worker, `REUSE_CONNS=3`
warm connections kept idle) to stay comfortably within PostgreSQL's
default `max_connections=100` across all uWSGI workers, Celery workers,
and Daphne. — Thanks
[@&#8203;JCBird1012](https://redirect.github.com/JCBird1012)
- **Reduced Redis round-trips on the Stats page channel status
endpoint.** `get_basic_channel_info` was making up to 6 individual
`HGET` calls per connected client plus a redundant `HGET` for
`TOTAL_BYTES` (already present in the preceding `HGETALL` result).
Client metadata is now fetched with a single `HMGET` per client, and
`TOTAL_BYTES` is read from the already-fetched hash. Under load with
many active streams this significantly reduces the time each uWSGI
worker holds the GIL servicing the stats endpoint, reducing the chance
of concurrent requests from other pages timing out with a 503. The same
`HGET`-to-`HMGET` consolidation was applied to `stream_ts` and
`get_user_active_connections`. The Stats page frontend was also fixed to
fire the initial fetch only once on mount (previously two `useEffect`
hooks both triggered an immediate fetch on load). — Thanks
[@&#8203;JCBird1012](https://redirect.github.com/JCBird1012)

##### Security

- **M3U endpoint no longer reflects POST body content in error
responses.** The error message for disallowed POST requests previously
echoed the raw request body back to the caller in a `text/html`
response, which could be used for reflected XSS. The body is no longer
included in the response. - Thanks
[@&#8203;sebastiondev](https://redirect.github.com/sebastiondev)
- Updated frontend npm dependencies to resolve 5 audit vulnerabilities
(2 moderate, 2 high, 1 critical):
- Updated `react-router` and `react-router-dom` 7.13.0 → 7.17.0,
resolving **high** unauthenticated RCE via turbo-stream TYPE\_ERROR
deserialization
([GHSA-49rj-9fvp-4h2h](https://redirect.github.com/advisories/GHSA-49rj-9fvp-4h2h)),
**high** open redirect via protocol-relative `//` paths
([GHSA-2j2x-hqr9-3h42](https://redirect.github.com/advisories/GHSA-2j2x-hqr9-3h42)),
**high** XSS in unstable RSC redirect handling via `javascript:` targets
([GHSA-8646-j5j9-6r62](https://redirect.github.com/advisories/GHSA-8646-j5j9-6r62)),
**high** stored XSS via unescaped `Location` header in prerendered
redirect HTML
([GHSA-f22v-gfqf-p8f3](https://redirect.github.com/advisories/GHSA-f22v-gfqf-p8f3)),
**high** DoS via unbounded path expansion in the `__manifest` endpoint
([GHSA-8x6r-g9mw-2r78](https://redirect.github.com/advisories/GHSA-8x6r-g9mw-2r78)),
and **high** DoS via reflected user input in single-fetch
([GHSA-rxv8-25v2-qmq8](https://redirect.github.com/advisories/GHSA-rxv8-25v2-qmq8))
- Updated `vitest` 3.2.4 → 4.1.8, resolving **critical** arbitrary file
read and execution when the Vitest UI server is listening
([GHSA-5xrq-8626-4rwp](https://redirect.github.com/advisories/GHSA-5xrq-8626-4rwp))
- Updated `brace-expansion` 5.0.5 → 5.0.6, resolving **moderate** DoS
when large numeric ranges defeat the documented `max` limit
([GHSA-jxxr-4gwj-5jf2](https://redirect.github.com/advisories/GHSA-jxxr-4gwj-5jf2))
- Updated `ws` 8.19.0 → 8.21.0, resolving **moderate** uninitialized
memory disclosure
([GHSA-58qx-3vcg-4xpx](https://redirect.github.com/advisories/GHSA-58qx-3vcg-4xpx))

##### Fixed

- **HDHR lineup and discovery URLs now use the shared port-aware URI
builder.** `lineup.json`, `discover.json`, and `device.xml` previously
called Django's `request.build_absolute_uri()`, which drops non-standard
ports behind reverse proxies and on dev installs. They now use
`build_absolute_uri_with_port()` from `core/utils.py`, matching M3U,
EPG, XC, and logo cache URLs.
- **EPG channel list did not refresh after a source finished parsing
channels.** The `epg_refresh` WebSocket handler closed over a stale
`epgs` snapshot from when the socket connected. When a newly created
source was not found in that snapshot, the handler updated progress and
returned early without calling `fetchEPGData()`, so the channel picker
and EPG assignment UI stayed empty until a full page reload. The handler
now reads the current store via `getState()` and still calls
`fetchEPGData()` when `parsing_channels` completes.
- **DVR recordings no longer stop immediately when FFmpeg exits
mid-stream.** If FFmpeg crashes, stalls, or loses the source before the
scheduled end time, `run_recording` now restarts it in-process instead
of going straight to HLS→MKV concat and marking the recording complete.
Each restart reuses the same HLS working directory and continues segment
numbering (`append_list`, `-start_number`) so the final MKV is a single
continuous file. Retries are bounded by a per-outage time window
matching live-proxy client tolerance (`STREAM_TIMEOUT` +
`FAILOVER_GRACE_PERIOD`, default 80s); the window resets whenever new
segments resume, so a long recording can survive multiple separate
outages. Reconnect pacing between attempts follows the same `min(0.25 ×
attempt, 3s)` backoff used by the live-proxy `StreamManager`. Input
demuxing also uses `-err_detect ignore_err` to tolerate minor TS
corruption without aborting the process. If the outage window expires
without recovery, the recording is saved as `interrupted` with
`ffmpeg_outage_window_exhausted` rather than falsely reported as
`completed`. (Closes
[#&#8203;1170](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/1170))
- **DVR HLS→MKV concat now tolerates timestamp splices and corrupt
segments.** The finalize step (and startup recovery concat) previously
used a bare `ffmpeg -f concat -c copy`, which could fail on truncated
tail segments or PTS discontinuities from FFmpeg restarts mid-recording.
Concat now uses a shared `_dvr_build_hls_concat_cmd` helper with
`-fflags +genpts+igndts+discardcorrupt`, `-err_detect ignore_err`, and
`-avoid_negative_ts make_zero`; the existing MP4-intermediate fallback
path uses the same tolerant input flags.
- **DVR FFmpeg restart no longer skips HLS segment numbers.** On
restart, `-start_number` was set to the existing segment count while
`append_list` also incremented the internal sequence for each segment
reloaded from `index.m3u8`, so the first post-restart segment could land
at roughly double the expected index (e.g. `seg_00028.ts` after 14
segments). Restarts now pass `-start_number 0` when the playlist already
lists segments and let `append_list` continue numbering; loose `.ts`
files without a playlist still seed from the highest filename index + 1.
- **Null TS keepalive packets during channel initialization caused Emby
(and Jellyfin) to force a deinterlace transcode or fail playback
entirely.** While waiting for a channel to become ready, the generator
sent a null TS packet (PID `0x1FFF`) every 0.5 s to keep the HTTP
connection alive. Emby probes the initial bytes of the response via
ffprobe; receiving a null packet instead of real stream data produced
incorrect codec metadata and triggered a forced transcode. The
null-packet yield has been replaced with a plain `gevent.sleep(0.1)`.
(Fixes
[#&#8203;1280](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/1280))
- **VOD proxy stream URLs survive M3U import refresh cycles.** When
`process_movie_batch` / `process_series_batch` create duplicate content
records during a refresh, existing `M3UMovieRelation` /
`M3UEpisodeRelation` rows are repointed at the new records, orphaning
the old UUIDs. External players (Emby, Jellyfin, ChannelsDVR) that
cached `.strm` URLs with the dead UUID would then 404 even though the
same request carried a stable `stream_id` that maps to an active
relation. The proxy now falls back to stream\_id resolution when the
UUID lookup misses, using strictest-match-first logic (account-scoped
relation preferred, then highest-priority account). A
`[STREAMID-FALLBACK]` WARNING log keeps the underlying import churn. —
Thanks [@&#8203;R3XCHRIS](https://redirect.github.com/R3XCHRIS)
- **IP lookup no longer blocks the settings page load.** The
`environment` endpoint previously made up to three sequential HTTP calls
(ipify, ipapi.co, ip-api.com) with 5s timeouts each, blocking the page
for up to 15s if any were unreachable. The lookups now run in a
background thread on first request and results are cached in Redis for 1
hour, so the endpoint returns immediately on every call. — Thanks
[@&#8203;sethwv](https://redirect.github.com/sethwv)
- **Authenticated users were not identified in VOD connection cards and
stream events when streaming via the web player.** The VOD proxy now
accepts a JWT via a `?token=` query parameter so browser `<video>`
elements (which cannot send `Authorization` headers) can authenticate.
The token is read on the initial request, the resolved user ID is stored
in Redis, then retrieved on the actual streaming request after the
session redirect. Previously the redirect discarded auth context and all
JWT-authenticated VOD sessions were tracked as anonymous. (Fixes
[#&#8203;1224](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/1224))
- **Deleting the last playlist (or any playlist) crashed the entire
UI.** `removePlaylists()` in the playlists store filtered the
`playlists` array but never removed the corresponding entries from the
`profiles` map. After deletion, components reading `profiles[deletedId]`
received `undefined` and crashed with `undefined is not an object
(evaluating 'P[R].name')`, replacing the entire page with an error
screen. The profiles map is now cleaned up atomically in the same state
update as the playlists array. (Fixes
[#&#8203;1269](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/1269))
- Thanks [@&#8203;nemesbak](https://redirect.github.com/nemesbak)
- **Switching providers in the VOD or Series detail modal had no
effect.** The `provider-info` endpoints for movies and series always
fetched data from the highest-priority provider, ignoring the
`relation_id` the frontend sent when the user selected a different
provider from the dropdown. The endpoints now accept an optional
`relation_id` query parameter and fetch from that specific relation. The
VOD modal also now shows the "Loading additional details..." indicator
while the provider switch is in flight, matching the existing behaviour
in the Series modal. (Fixes
[#&#8203;1285](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/1285))
- Thanks [@&#8203;nemesbak](https://redirect.github.com/nemesbak)
- **Per-channel stream profile override was ignored during streaming.**
`Channel.get_stream_profile()` read `self.stream_profile` directly,
bypassing any `ChannelOverride.stream_profile` set by the user on
auto-synced channels. The method now resolves through
`effective_stream_profile_obj`, which checks the channel's override
record first and falls back to the channel's own field. Channels without
an override continue to behave identically to before. (Fixes
[#&#8203;1268](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/1268))
- Thanks [@&#8203;nemesbak](https://redirect.github.com/nemesbak)
- **Web-player output profile was ignored for live streams started
outside the Channels page.** `getShowVideoUrl` in
`RecordingCardUtils.js` returned a raw proxy URL without calling
`buildLiveStreamUrl`, so the output profile preference stored in
`localStorage` was not applied when launching a stream from the TV
Guide, Program Detail modal, DVR page, Recording Details modal, or
Recording Card. Only the Channels page (StreamsTable) was calling
`buildLiveStreamUrl` correctly. One additional import and one changed
return value fix all affected entry points. (Fixes
[#&#8203;1304](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/1304))
- Thanks [@&#8203;nemesbak](https://redirect.github.com/nemesbak)
- **Plugins with available updates were not sorting to the top of the
Plugin Browse list.** The sort weight function previously treated
`update_available` plugins the same as any other installed plugin,
leaving them buried. They now receive the highest sort priority (below
the search/filter results header) so users can spot pending updates
immediately. — Thanks
[@&#8203;sethwv](https://redirect.github.com/sethwv)
- **Disabled state on the size-labeled install button rendered with a
warm tint instead of appearing clearly disabled.** The CSS filter was
`brightness(0.65) saturate(0.7)`, which left a faint color cast. It is
now `grayscale(1) brightness(0.55)`, matching the standard disabled
appearance. — Thanks
[@&#8203;sethwv](https://redirect.github.com/sethwv)
- **Restoring a backup from an older version left the database with
missing schema.** The restore task ran `pg_restore` which replaced the
entire database (including the `django_migrations` table) but did not
run migrations afterward. If the backup predated a schema migration, the
restored database was missing tables and columns added by those
migrations, causing 500 errors on every API call. `migrate --noinput`
now runs automatically after every restore. The success notification
also now recommends a restart to clear stale service state.
- **Cast and actors lists were silently truncated to the first name.**
When a provider returns `cast` or `actors` as a JSON array, the helper
used during both movie and series basic sync and movie advanced refresh
only extracted the first element. All names in the array are now joined
into a comma-separated string. Providers that return cast as a plain
string are unaffected.
- **Channel Group Override interactions in compact numbering and
override display.** Two related bugs surfaced when Channel Group
Override was used with auto-synced channels. First, compact numbering
silently failed: with an override on the source
`ChannelGroupM3UAccount`, sync stores channels under the override target
group's id (not the source group id), so hide/unhide/repack operations
all missed their channels and slot accounting broke silently (hidden
channels kept their numbers, unhides got none, repack saw zero
channels). The compact paths now include an override-aware fallback to
match channels under both source and override-target group ids. Second,
the clear-override reset button disappeared when an override's stored
value coincidentally matched the provider value. The frontend
`isFormFieldOverridden` function was value-based only; it now also
checks for a persisted override row regardless of value, so the reset
button remains available to clear any override. (Fixes
[#&#8203;1263](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/1263),
Fixes
[#&#8203;1276](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/1276))
— Thanks [@&#8203;CodeBormen](https://redirect.github.com/CodeBormen)
- **Compact numbering repack is now idempotent.** Auto-synced channel
numbers reshuffled within their configured range on every sync, even
when the provider returned no changes. The compact repack queried
channels with no `ORDER BY`, so packing followed PostgreSQL's physical
row order, which drifts after each repack's UPDATEs and autovacuum. The
channel query now uses `.order_by("id")` (creation order tracks provider
stream order for the default "provider" sort), and explicit name /
tvg\_id / updated\_at sorts carry `c.id` as a secondary tiebreaker so
equal values keep a stable relative order instead of churning. (Fixes
[#&#8203;1321](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/1321))
— Thanks [@&#8203;CodeBormen](https://redirect.github.com/CodeBormen)

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Renovate
Bot](https://redirect.github.com/renovatebot/renovate).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xMzAuMSIsInVwZGF0ZWRJblZlciI6IjQzLjEzMC4xIiwidGFyZ2V0QnJhbmNoIjoibWFzdGVyIiwibGFiZWxzIjpbImFwcC9kaXNwYXRjaGFyciIsImF1dG9tZXJnZSIsInJlbm92YXRlL2NvbnRhaW5lciIsInR5cGUvbWlub3IiXX0=-->
2026-06-08 02:21:30 +02:00
TrueCharts Bot 18182ff6a4 fix(dispatcharr): update image docker.io/dispatcharr/dispatcharr 0.25.0 → 0.25.1 (#48479)
This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
|
[docker.io/dispatcharr/dispatcharr](https://redirect.github.com/Dispatcharr/Dispatcharr)
| patch | `9f6a7e5` → `2bdcfd9` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the [Dependency
Dashboard](../issues/18710) for more information.

Add the preset `:preserveSemverRanges` to your config if you don't want
to pin your dependencies.

---

### Release Notes

<details>
<summary>Dispatcharr/Dispatcharr
(docker.io/dispatcharr/dispatcharr)</summary>

###
[`v0.25.1`](https://redirect.github.com/Dispatcharr/Dispatcharr/blob/HEAD/CHANGELOG.md#0251---2026-05-23)

[Compare
Source](https://redirect.github.com/Dispatcharr/Dispatcharr/compare/v0.25.0...v0.25.1)

##### Fixed

- **`SynchronousOnlyOperation` and permanent loading state in series
rules save.** `_evaluate_series_rules_locked` and
`reschedule_upcoming_recordings_for_offset_change_impl` called
`async_to_sync(channel_layer.group_send)` directly from WSGI views. In a
uWSGI/gevent worker this has two effects: (1) it sets Python 3.10+'s
C-level OS-thread-local running-loop flag, causing Django's
`@async_unsafe` guard to raise `SynchronousOnlyOperation` on any ORM
call made by another greenlet on the same thread; (2) the asyncio event
loop it creates cannot make progress because the gevent hub is blocked
waiting for the request greenlet to complete, so the request hangs and
the frontend spinner never clears. Both functions now use
`send_websocket_update` - the same gevent-aware helper used elsewhere -
which takes a direct Redis path (no asyncio) in gevent workers. (Fixes
[#&#8203;1260](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/1260))
- **Migration 0037 fails on PostgreSQL when channels have been orphaned
from their M3U account.** Channels created by auto-sync retain
`auto_created=True` but get `auto_created_by=NULL` when the originating
M3U account is deleted (`on_delete=SET_NULL`). The data migration step
that re-attributes or demotes those channels updates the FK column via
ORM `.save()` calls, which queues deferred constraint trigger events in
PostgreSQL. The subsequent `ALTER TABLE ... DROP NOT NULL` on the same
table then fails with `ObjectInUse: cannot ALTER TABLE because it has
pending trigger events`. Fixed by issuing `SET CONSTRAINTS ALL
IMMEDIATE` at the end of the data migration function to flush those
pending events before the DDL runs. (Fixes
[#&#8203;1259](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/1259))
- **XC stream URLs with no file extension now respect output format
defaults.** `stream_xc` previously treated any extension other than
`.mp4` as a forced `mpegts` request, including the empty-extension URLs
that XC-compatible M3U playlists produce via `get.php`. Requests with no
extension now pass `force_format=None` so the standard resolution chain
(request param, user default, server default) applies correctly.
- **XC M3U stream URLs now carry output profile and output format
parameters.** The `get.php` M3U playlist previously emitted bare
`/live/user/pass/id` URLs with no query string, causing per-user and
server-wide output profile and output format settings to be silently
ignored for XC clients. When `output_profile` or `output_format`
parameters are present on the playlist request, they are now appended to
every stream URL in the playlist so the proxy honours them on playback.

##### Performance

- **M3U playlist URL building moved outside the channel loop.**
`generate_m3u` previously rebuilt the query-string suffix (and for XC
requests, called `build_absolute_uri_with_port`) on every channel
iteration even though both inputs are request-level constants. The XC
base URL and both query-string suffixes (`xc_qs_suffix`,
`proxy_qs_suffix`) are now computed once before the channel loop;
per-channel URL assembly is a single f-string interpolation.

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Renovate
Bot](https://redirect.github.com/renovatebot/renovate).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xMzAuMSIsInVwZGF0ZWRJblZlciI6IjQzLjEzMC4xIiwidGFyZ2V0QnJhbmNoIjoibWFzdGVyIiwibGFiZWxzIjpbImFwcC9kaXNwYXRjaGFyciIsImF1dG9tZXJnZSIsInJlbm92YXRlL2NvbnRhaW5lciIsInR5cGUvcGF0Y2giXX0=-->
2026-05-23 21:54:01 +02:00
TrueCharts Bot e3c064bcfa feat(dispatcharr): update image docker.io/dispatcharr/dispatcharr 0.24.0 → 0.25.0 (#48428)
This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
|
[docker.io/dispatcharr/dispatcharr](https://redirect.github.com/Dispatcharr/Dispatcharr)
| minor | `c9920ec` → `9f6a7e5` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the [Dependency
Dashboard](../issues/18710) for more information.

Add the preset `:preserveSemverRanges` to your config if you don't want
to pin your dependencies.

---

### Release Notes

<details>
<summary>Dispatcharr/Dispatcharr
(docker.io/dispatcharr/dispatcharr)</summary>

###
[`v0.25.0`](https://redirect.github.com/Dispatcharr/Dispatcharr/blob/HEAD/CHANGELOG.md#0250---2026-05-21)

[Compare
Source](https://redirect.github.com/Dispatcharr/Dispatcharr/compare/v0.24.0...v0.25.0)

##### Security

- Updated `Django` 6.0.4 → 6.0.5, resolving the following CVEs:
- **CVE-2026-6907**: Cache leak exposing sensitive information via
`cache.get_or_set()` race condition.
- **CVE-2026-35192**: Persistent session cookies retaining sensitive
information after logout.
- **CVE-2026-5766**: Improper handling of length parameter inconsistency
in multipart form parsing.

##### Added

- **About modal.** A `?` button in the sidebar footer opens an About
dialog showing the current version, links to Documentation, Discord,
GitHub, and Open Collective, a contributors acknowledgment, and a
memorial note for Jesse Mann. The button is visible in both expanded and
collapsed sidebar states.
- **Comskip mode setting.** DVR Settings now includes a "Comskip mode"
option:
- **Cut** (default): FFmpeg permanently removes commercial segments from
the recording file in place. The EDL file is deleted after a successful
cut.
- **Mark**: comskip analysis runs as normal but the recording file is
left untouched. The EDL file is kept alongside the recording so players
that support EDL-based commercial skipping (e.g. Kodi) can use it. The
recording's `custom_properties` record the EDL filename, commercial
count, and mode so the UI can surface this.
- **Comskip hardware acceleration setting.** DVR Settings now includes a
"Hardware acceleration" option that passes a hardware-decode flag to the
comskip binary, reducing CPU load during commercial detection on capable
hosts:
  - **None** (default): software decode.
- **NVIDIA NVDEC (`--cuvid`)**: requires the NVIDIA container toolkit
and a supported GPU inside the container.
- **Intel Quick Sync (`--qsv`)**: requires an Intel iGPU or ARC GPU with
the i915 driver exposed to the container.
- **HDHR output profile URL support.** HDHomeRun lineup URLs now support
an `output_profile` path segment so HDHR clients (Plex, Channels DVR,
Emby, etc.) can request a specific transcode profile without any
query-parameter support. URL formats accepted:
  - `/hdhr/output_profile/<id>/lineup.json` - output profile only
- `/hdhr/<channel_profile>/output_profile/<id>/lineup.json` - channel
profile + output profile
- Bare `/hdhr/lineup.json` - existing behavior, uses the new system
default (see below)
- The profile ID is resolved against active `OutputProfile` rows; if the
ID is inactive or does not exist, a warning is logged and the stream is
served without transcoding.
- **HDHR default output profile setting.** A new "HDHR Default Output
Profile" select in Settings → Stream Settings lets admins pick an output
profile that is applied to all HDHR stream URLs that do not specify one
in the path. When cleared, streams are served as-is (pass-through).
- **fMP4 streaming support.** Dispatcharr can now serve live channels as
fragmented MP4 in addition to MPEG-TS. A dedicated `FMP4RemuxManager`
runs a single FFmpeg remux process per active channel, muxes incoming TS
data into fMP4 fragments, and stores them in a Redis-backed ring buffer.
Multiple clients requesting the same channel in fMP4 all read from the
same shared buffer, so only one FFmpeg process runs regardless of viewer
count. Clients that request MPEG-TS continue to be served as before. The
output format is selected via `?output_format=mpegts|fmp4` on the stream
URL, or by the per-user default configured by an admin on the user
account, or by the server-wide default in Stream Settings.
- **Output profiles.** Admins can define named transcode profiles under
Settings → Output Profiles. Each profile specifies an executable (e.g.
`ffmpeg`) and a parameter string that reads raw TS from `pipe:0` and
writes the transcoded output to `pipe:1`. When a client requests a
profile, one FFmpeg transcode process runs per active (channel, profile)
pair and all requesting clients share the resulting output buffer.
Common use case: a profile that converts AC3 audio to AAC for browser
and mobile clients while the native stream (AC3 intact) continues to
serve Plex/Emby/Jellyfin. Profiles are applied via
`?output_profile=<id>` on the stream URL, or by per-user and server-wide
defaults. Two locked built-in profiles are seeded on first run: **Media
Server (AC3 Audio)** (copies video, re-encodes audio to AC3 384k) and
**Web Player (AAC Audio)** (copies video, re-encodes audio to AAC 192k).
(Closes
[#&#8203;407](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/407))
- **Container format and output profile shown in Stats client rows.**
The expanded client row on the Stats page now displays the container
format (`mpegts` or `fmp4`) and, when an output profile is active, the
profile name.
- **Browser-local web player output profile setting.** A new "Web Player
Output Profile" select in Settings → UI Settings lets each browser
choose which output profile (if any) is applied when previewing streams
in the built-in floating player. The preference is saved to
`localStorage` (`dispatcharr-player-prefs`) and picked up by every
preview URL builder at click time, so a user whose account default
transcodes to AC3 for their media server can still get a
browser-compatible audio stream from the preview player without changing
their account settings.
- **DVR series rules: EPG channel is now optional.** Series recording
rules no longer require a `tvg_id`. Rules with only a title or
description filter search across all EPG channels in the 7-day horizon.
The evaluator pre-loads one channel per EPG source (lowest channel
number) in a single query and resolves the recording channel
per-program, so cross-channel searches remain efficient. Saves and the
preview endpoint now require at least one of `title`, or `description`
instead of requiring `tvg_id`. The preview response includes a `warn:
true` flag when more than 50 programs match, and the editor shows an
orange alert when this flag is set. The upsert key for rules changed
from `tvg_id` alone to `(tvg_id, title)` so multiple rules can target
the same channel.
- **DVR series rules: rich title and description matching.** Series
recording rules now accept the same boolean / quoted-phrase / regex /
whole-word semantics as the EPG Program Search API on both the title and
description fields, plus an optional pinned channel. Existing rules
continue to work unchanged (default `title_mode=exact`, no description
filter, no pinned channel). (Closes
[#&#8203;570](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/570))
- New rule fields: `title_mode` (`exact` / `contains` / `search` /
`regex`), `description`, `description_mode` (`contains` / `search` /
`regex`), and `channel_id` (optional integer to pin recordings to a
specific channel; defaults to the lowest-numbered channel for the EPG as
before).
- The boolean/quoted/regex/whole-word parser was extracted from
`apps/epg/api_views.py` into a shared `apps/epg/query_utils.py` so the
rule evaluator and the search endpoint use the same code path. No
behavioral changes to the existing `/api/epg/programs/search/` endpoint.
- The evaluator builds a single `ProgramData` queryset with
`.distinct()` and reuses the existing `(tvg_id, start_time, end_time)`
dedup key, so dropping in a description filter or a fuzzy title still
preserves the dedup, episode collapsing, and offset-adjustment behavior.
No N+1 introduced.
- **`POST /api/channels/series-rules/preview/`** returns up to 25
(configurable, max 100) upcoming programs that a candidate rule would
match within the standard 7-day evaluation horizon, without persisting
anything. Used by the new rule editor to give live feedback as the user
types.
- **`GET /api/epg/programs/search/?tvg_id=`** filter parameter for exact
`epg.tvg_id` matches.
- **Series rule editor modal** with form (title + match mode,
description + match mode, episodes mode, pinned channel) and a debounced
(500ms) preview pane backed by an `AbortController` so per-keystroke
calls don't pile up. A "Customize rule..." link in the program
record-choice modal opens the editor pre-filled with the program's
`tvg_id` and title; the series rules modal gains an "Add rule" button
and an "Edit" button per existing rule.
- **Shift+click and Ctrl+click row selection in tables.** Clicking
anywhere on a non-interactive area of a row now participates in
selection:
- **Shift+click**: extends selection from the last-clicked row to the
current row (range select), identical to shift+clicking the checkbox.
- **Ctrl+click** (Cmd+click on Mac): toggles the clicked row in or out
of the current selection without disturbing other selected rows.
- Plain clicks on action buttons, checkboxes, inputs, links, and menus
are unaffected. The expand chevron uses `stopPropagation` so expanding a
row does not also trigger selection.

##### Changed

- **Custom SVG icons extracted to `icons.jsx`.** `DiscordIcon` and
`GitHubIcon` were moved from `PluginDetailPanel.jsx` into a new shared
`frontend/src/components/icons.jsx` module so they can be reused across
components without cross-importing from an unrelated file.

- **Comskip `.ini` overhauled.** The shipped `docker/comskip.ini` was
replaced with a fully documented configuration covering all tunable
sections: Main Settings, Output, Commercial Break Timing, Black Frame
Detection, Logo Detection, Silence Detection, and Live TV. Key defaults:
`detect_method=127` (all seven detection methods, up from the comskip
default of 107), `min_commercialbreak=25` (slightly stricter floor for
US broadcast TV), `output_default=0` (suppresses the `.txt` stats file
comskip writes by default), `edl_skip_field=3` (Kodi commercial-break
action code). All values include inline source references and
plain-language explanations.

- **Comskip enable switch label updated.** The DVR settings switch was
relabeled from "Enable Comskip (remove commercials after recording)" to
"Enable Comskip (commercial detection after recording)" to remain
accurate when mark mode is selected.

- **Settings reorganization: Preferred Region and Auto-Import Mapped
Files moved to System Settings.** These two settings were previously
stored in the `stream_settings` database group and shown under Stream
Settings in the UI. They are now stored in `system_settings` and
displayed under System Settings, which better reflects that they are
server-wide behavior settings rather than stream delivery settings. A
data migration (0025) moves existing values from the old group to the
new one for all existing installs.

- **Stream Settings descriptions added.** Default User Agent, Default
Stream Profile, Default Output Format, M3U Hash Key, and HDHR Default
Output Profile all now have inline description text below their labels
explaining their purpose and effect.

- **System Settings descriptions added.** Maximum System Events,
Preferred Region, and Auto-Import Mapped Files now have inline
description text. The redundant description paragraph that duplicated
the accordion header text has been removed.

- **`get_client_ip()` in `dispatcharr/utils.py` cleaned up.** Removed
dead `.split(',')[0]` call and misleading variable name left over from a
copy-paste of the `X-Forwarded-For` pattern. `X-Real-IP` is always a
single IP (set by nginx from `$remote_addr`), so no splitting is needed.
Behavior is unchanged.

- **`ts_proxy` module refactored and renamed to `live_proxy`.** The
live-streaming proxy was reorganized into a structured package
(`apps/proxy/live_proxy/`) with explicit submodules for each stage of
the pipeline: `input/` (HTTP streamer, stream manager, TS ring buffer),
`output/ts/` (MPEG-TS client generator), `output/fmp4/` (fMP4 remux
manager, Redis buffer, client generator), and `output/profile/` (output
profile transcode manager). A dedicated `redis_keys.py` module
centralizes all Redis key name construction for the proxy. Existing
behavior for MPEG-TS clients is unchanged.

- **XC API `allowed_output_formats` now includes `mp4`.** The
`user_info` block returned by `player_api.php` and `get.php` previously
advertised only `["ts"]`. It now advertises `["ts", "mp4"]` for all
users, enabling XC-compatible clients that support fMP4 to request
`.mp4` stream URLs which are proxied as fMP4.

- **Browser preview URLs always force `mpegts` output.** The four in-app
preview buttons (Channels table, channel stream list, Streams table,
Stats client row) now append `?output_format=mpegts` (and the web player
profile, if set) to the preview URL so the built-in `mpegts.js` player
always receives a compatible stream, regardless of the user's configured
default output format.

- **Series rules modal redesign.** Rules now display a one-line summary
with the title-match mode, description filter (when present), and a
"Pinned channel" badge when a `channel_id` is configured. The previous
list rendering remains for legacy rules.

- **EPG Program Search API** (`GET /api/epg/programs/search/`): a new
endpoint for querying EPG program data with rich filtering and query
support. - Thanks
[@&#8203;northernpowerhouse](https://redirect.github.com/northernpowerhouse)
- **Text search** on title and description with AND/OR boolean operators
(case-insensitive), quoted phrase matching (`"Law and Order"` treats
*and* as literal text), parenthetical grouping (`(Newcastle OR NEW) AND
(Villa OR AST)`), whole-word mode (`title_whole_words=true`), and regex
mode (`title_regex=true`).
- **Time filters**: `airing_at` (programs live at a specific instant),
`start_after`, `start_before`, `end_after`, `end_before`.
- **Relational filters**: `channel`, `channel_id`, `stream`, `group`,
`epg_source`.
- **Field selection**: `fields=title,start_time,channels` returns only
the requested keys; channel and stream data is skipped server-side (no
wasted serialization) when not requested.
- **Pagination**: default 50 results per page, configurable up to 500
via `page_size`.
- **Access control**: results are scoped to channels the requesting user
can access. `user_level` and the per-user adult-content filter are both
enforced, matching the access model used by the M3U playlist, XC API,
and stream proxy. Admin users receive unfiltered results.
  - Full OpenAPI/Swagger documentation available.
  - Requires `IsStandardUser` permission (user level ≥ 1).

- **Per-user IP/CIDR network allowlists**. Admins can now assign IP
address and CIDR range restrictions to individual user accounts via the
API & XC tab on the user edit form. When a user has one or more allowed
ranges configured, requests from IPs outside that list are rejected with
`403 Forbidden` regardless of the global network access policy; if no
ranges are configured, the user inherits global settings unchanged. The
existing `network_access_allowed()` utility is extended with an optional
`user` argument so the per-user check is enforced at all
access-controlled entry points (M3U/EPG, Streams, XC API, UI) without
duplicating IP-matching logic. Per-user restrictions are stored in
`custom_properties['allowed_networks']`; no model changes or migrations
are required. — Thanks
[@&#8203;sethwv](https://redirect.github.com/sethwv)

- **`reset_user_network` management command**: `manage.py
reset_user_network <username>` clears the per-user `allowed_networks`
restriction for the specified account, restoring it to global-policy
inheritance. Useful for recovering a user locked out by a misconfigured
allowlist. — Thanks [@&#8203;sethwv](https://redirect.github.com/sethwv)

- **Auto-sync overhaul**: comprehensive rebuild of the M3U
auto-channel-sync flow. Introduces a per-field override system,
hide-from-output flag, range-bounded auto-numbering with a re-pack
helper, multi-stream channel safety, multi-provider shared-range
merging, and an across-the-board move from per-row writes to bulk
operations. (Closes
[#&#8203;1196](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/1196))
— Thanks [@&#8203;CodeBormen](https://redirect.github.com/CodeBormen)
- **Per-field channel overrides for auto-synced channels.** A new
`ChannelOverride` table (one-to-one with `Channel`) holds user-specified
values for `name`, `channel_number`, `channel_group`, `logo`, `tvg_id`,
`tvc_guide_stationid`, `epg_data`, and `stream_profile`. Sync writes
only to `Channel.*` columns; the override row takes precedence at read
time via a new `with_effective_values()` queryset helper that coalesces
both sources at the SQL layer. Every output surface that consumes
channel data reads through this helper so overrides surface
consistently: HDHR (`/hdhr/lineup.json` and per-profile variants,
`/hdhr/discover.json`), M3U (`/output/m3u`, per-profile variants), EPG
(`/output/epg`, per-profile variants), XC API (`get_live_streams`,
`xmltv.php`), the channels list/detail endpoints, and the TV Guide
summary endpoint (`GET /api/channels/channels/summary/`). Channel API
responses now include both the raw `name`/`channel_number`/etc. fields
AND new `effective_*` annotations plus the `override` object, so
frontend consumers can show both "what the user picked" and "what the
provider sent" simultaneously.
- **Per-field reset-to-provider icon.** FK pickers (channel group, logo,
EPG, stream profile) and scalar inputs (name, channel\_number, tvg\_id,
tvc\_guide\_stationid) display a small reset icon ("Provider: <value>"
subtext + Undo2 button) when the field has an active override on an
auto-synced channel. Clicking it sets the form value back to the
provider value; the override for that field is cleared on save while
other overrides remain intact.
- **Auto-created channel source attribution.** The channel edit form
shows an "Auto-created from: <provider name> / <stream name>" label at
the top of auto-synced channels so the user can see which M3U account
and which stream produced the row.
- **Hide-from-output flag (`hidden_from_output`).** A user-toggleable
boolean that excludes a channel from HDHR, M3U, EPG, and XC output
without deleting it. Hidden channels are preserved across auto-sync
refreshes and excluded from auto-cleanup. The channels table shows an
EyeOff icon on hidden rows.
- **Channels table visibility filter and inline indicators.** The table
header has a new visibility selector ("Active Only" / "Hidden Only" /
"Show All") plus a "Has Overrides" filter that narrows to channels with
an active override row. Each row's name cell renders a yellow Pencil
icon when overrides are active (tooltip lists the overridden field
names) and an EyeOff icon when the channel is hidden.
- **Bulk hide / unhide in the bulk edit modal.** Selecting any number of
channels and toggling `hidden_from_output` in bulk edit applies the
change in a single PATCH; auto-routes through the override-aware bulk
endpoint so it works equivalently for auto-synced and manual channels.
- **Auto-sync channel ranges per group.** `ChannelGroupM3UAccount`
accepts `auto_sync_channel_start` (lower bound, inclusive) and
`auto_sync_channel_end` (upper bound, inclusive; nullable for unbounded
fill). Channels created by auto-sync are assigned numbers within the
configured range; streams that do not fit are surfaced in the failure
detail modal with a typed "RANGE\_EXHAUSTED" reason.
- **Per-group inline configuration in the M3U Live group filter.** Each
group row now exposes a Sync toggle, a Numbering Mode selector (Provider
/ Next Available / Fixed), and Start/End channel-number inputs alongside
the existing fields. The numbering modes control how each stream's
channel number is chosen during sync: Provider tries the M3U-supplied
number first then falls back to next-available; Next Available always
picks the lowest free number from 1 upward; Fixed picks sequentially
from the configured Start.
- **Per-group "Configure" modal.** A gear-icon button on each group row
opens a `GroupConfigureModal` containing the full set of advanced
options. Fields surfaced in the modal: Channel Sort Order (provider
order / name / tvg\_id / updated\_at, with reverse toggle), Compact
Numbering toggle and "Re-pack now" button, Group Override (re-route this
group's auto-created channels into a different `ChannelGroup` so
multiple provider groups merge into one logical group), Name Regex
Find/Replace pattern, Exclude Regex pattern, Force Dummy EPG, and a live
regex preview pane.
- **Live regex preview pane.** A new `GET
/api/channels/streams/regex-preview/` endpoint scans the group's streams
(capped at 5000) for the find / match / exclude patterns the user is
editing and returns up to 10 sample matches per pattern plus accurate
total counts. The frontend debounces the call (500ms) and runs it inside
the configure modal so the user sees live evidence of what their
patterns will match before saving.
- **Live overlap warning across rows.** As the user edits Start/End
values across multiple groups in the same provider, an
`AbortController`-debounced scan calls a new `GET
/api/channels/channels/numbers-in-range/` endpoint to detect (a)
cross-row range overlaps (in-memory, all group pairs) and (b) channels
already occupying the configured range that are not the expected
occupants for that group. An informational warning surfaces inline;
configurations are not blocked.
- **Compact numbering with re-pack helper.** When
`compact_numbering=True` on a group's account relation, sync packs
visible auto-sync channels sequentially into the configured range. A
"Re-pack now" button in the group config modal runs the pack manually
via a new `POST /api/m3u/accounts/{id}/repack-group/` endpoint. Hidden
channels do not occupy slots; un-hiding shifts visible numbers down to
fill the gap. Override-pinned channel numbers are respected as fixed
anchors. Single-channel hide / unhide via the post-save signal triggers
an incremental `assign_compact_numbers_for_channels` pass without
requiring a full sync.
- **Multi-stream channel safety in sync.** A user-attached second stream
on an auto-created channel no longer causes the channel to be deleted
when one of those streams disappears from the provider. The deletion
path filters out channels where at least one current stream remains
alive, and per-stream iteration mutates the same `Channel` instance so
`bulk_update` writes the merged final state.
- **Orphan channel cleanup mode (3-state, account-scoped).** Stored
under `M3UAccount.custom_properties.orphan_channel_cleanup` and surfaced
as a SegmentedControl at the top of the M3U account's group-settings
page. Three positions: `always` (default; removes every auto-channel
whose source stream has disappeared), `preserve_customized` (removes
orphans without a `ChannelOverride` row, preserves those with one), and
`never` (preserves all orphans, intended for users with manual
redundancy/failover stream setups). Hidden channels are universally
preserved regardless of mode.
- **Failure modal grouped by reason.** The auto-sync completion
notification's failure detail modal groups entries by typed reason
(`RANGE_EXHAUSTED`, `INTEGRITY_ERROR`, `OTHER`) with collapsible
sections and a per-group cap. The in-memory cap was raised from 50 to
1000 so realistic multi-provider failure sets are not truncated. The
notification's auto-close timeout extends from 4s to 12s when failures
are present so users have time to see and click into the modal.
- **Multi-provider shared-range merging.** Two M3U accounts can target
the same group with overlapping channel-number ranges. Sync seeds
`used_numbers` globally so the second provider's channels pick up where
the first left off without colliding. The overlap warning in the
group-settings UI surfaces this configuration as informational; the
merge is intentional, not a hard error.
- **Selection summary in the bulk edit modal.** The bulk edit form shows
`Selection: X auto-synced, Y manual` at the top so users can see how a
single set of changes will route between override rows and direct
writes.
- **Delete-Playlist preview.** A new `GET
/api/m3u/accounts/{id}/auto-created-channels-count/` endpoint returns
the count and up to 5 sample names of auto-created channels owned by the
account. The Delete Playlist confirmation dialog uses this to show the
user exactly how many channels will cascade away before they confirm.
- **Custom `Channel.objects` manager.** A thin manager wraps the
existing `with_effective_values()` helper so new code can call
`Channel.objects.with_effective_values(...)` directly. The module-level
helper remains the canonical implementation; existing call sites are
unchanged.

- **Frontend unit tests added for form components.** `GroupManager`,
`LiveGroupFilter`, `LoginForm`, and `Logo` now have Vitest + Testing
Library test suites covering rendering, user interactions, API
integration, and edge cases (144 tests total). Business logic that was
extracted into utility modules (`ChannelGroupUtils`,
`LiveGroupFilterUtils`, `LogoUtils`, `M3uUtils`, `M3uFilterUtils`,
`M3uGroupFilterUtils`, `M3uProfileUtils`, `AutoSyncAdvancedUtils`,
`AutoSyncBasicUtils`) is also exercised through these tests. — Thanks
[@&#8203;nick4810](https://redirect.github.com/nick4810)

- **Global Network Access settings use tag-style inputs**: the IP/CIDR
range fields in the Network Access settings panel now use tag-style chip
inputs instead of a plain text field, making it easier to add, review,
and remove individual addresses or ranges. — Thanks
[@&#8203;sethwv](https://redirect.github.com/sethwv)

- **Auto-sync overhaul** sync and channel behavior changes for the new
override system. — Thanks
[@&#8203;CodeBormen](https://redirect.github.com/CodeBormen)
- **`Channel.channel_number` is now nullable.** Auto-sync may produce
channels without an assigned number (for example, a sync run where the
configured range was exhausted before this stream could be slotted, or a
hidden channel in compact mode whose slot was released for visible
channels). Existing channels are unchanged. HDHR / M3U / EPG / XC output
endpoints filter out channels with `null` channel\_number, so a
number-less row is invisible to clients but visible in the channels page
where the user can assign one. The 0037 auto-sync overhaul migration
includes a reverse-direction backfill on the `channel_number` AlterField
step so a rollback that re-imposes NOT NULL still succeeds even if NULLs
are present.
- **M3U account delete always cascades auto-created channels.** The
delete dialog is a single confirmation showing the count of affected
channels (fetched from the new auto-created-channels-count endpoint).
Auto-created channels are removed regardless of `hidden_from_output` or
override state: override rows cascade via the one-to-one FK; hidden
status does not preserve the channel because there is no provider left
to populate it on the next sync. Manual channels are unaffected; their
non-provider streams remain intact, and only streams owned by the
deleted account are removed. The destroy response now returns
`{"deleted_channels": N}` (HTTP 200) instead of an empty 204 so the
confirmation toast can show the actual count.
- **Bulk channel edit auto-routes auto-created channels through the
override path.** When a bulk edit includes auto-synced channels, the
changed fields are written to each channel's `ChannelOverride` row
instead of the raw `Channel.*` columns, so the edit survives the next
sync. Manual channels in the same selection write directly to
`Channel.*`. A "Clear all overrides" affordance pre-clears overrides on
the selection before applying new edits in the same submit (clear runs
before the routing PATCH so a same-submit clear cannot wipe just-written
overrides).
- **Inline edits on the channels table route through the override row
for auto-synced channels.** Editing a cell directly in the table
(number, name, group, EPG, logo) now writes to `ChannelOverride.<field>`
for auto-synced rows so the edit survives the next refresh; manual rows
continue to write directly. The save mechanic and validation are
unchanged from the user's perspective.
- **Channel-number duplicates are explicitly allowed.** Sync's
`used_numbers` seed still avoids assigning the same number to two
newly-created auto channels in the same run, so accidental duplication
during sync remains impossible. Manual or override-driven duplication is
permitted; downstream client behavior on duplicates varies by client.

- **gevent cooperative multitasking enabled in all uWSGI workers.**
`gevent-early-monkey-patch = true` and `import =
dispatcharr.gevent_patch` are now set in all four uWSGI configuration
files (`uwsgi.ini`, `uwsgi.modular.ini`, `uwsgi.dev.ini`,
`uwsgi.debug.ini`). The new `dispatcharr/gevent_patch.py` module ensures
gevent's stdlib monkey-patching is applied before application code loads
(replacing blocking socket, threading, and OS primitives with
cooperative gevent equivalents) and installs psycogreen's wait-callback
so psycopg2 database I/O yields to the gevent hub instead of blocking
the OS thread. Without these settings, any blocking psycopg2,
`requests`, or DNS call froze every greenlet on the affected worker for
the duration of the call.

- **WebSocket group sends rewritten to bypass asyncio in gevent
workers.** `gevent.monkey.patch_all()` removes `select.epoll` from the
stdlib `select` module, which breaks asyncio event loop creation in
threadpool threads. The previous `send_websocket_update` and
`_send_async` paths dispatched via
`async_to_sync(channel_layer.group_send)()` from a threadpool thread,
which failed with `AttributeError: module 'select' has no attribute
'epoll'`; the exception was caught at WARNING level, so every WebSocket
push from REST views was silently discarded. A new `_gevent_ws_send()`
in `core/utils.py` replicates the channels\_redis 4.x `group_send` wire
format directly using the synchronous Redis client - group membership
lookup from the `asgi:group:{name}` sorted set, msgpack serialization
with a 12-byte random prefix, and `ZADD` to per-channel sorted sets.
Both `send_websocket_update()` and `_send_async()` detect gevent
patching at call time and dispatch via `gevent.spawn(_gevent_ws_send)`
instead. Celery workers, which are not gevent-patched, continue to use
the `async_to_sync` path unchanged.

- Dependency updates:
  - `Django` 6.0.4 → 6.0.5 (security patch; see Security section)

##### Removed

- **`python-gnupg` dependency dropped.** GPG manifest signature
verification now calls the `gpg` binary directly via `os.posix_spawn`
(see Fixed below). The `python-gnupg` Python library was the only
consumer and has been removed from `pyproject.toml`. The `gpg` binary
itself is still required on the host (it was always required since
`python-gnupg` is just a wrapper around it).

##### Fixed

- **DVR settings form no longer flashes back to old values during
save.** The comskip mode and hardware acceleration selects briefly
showed stale values while the save was in flight because the Zustand
settings store update (triggered by the API response) fired the
`useEffect([settings])` re-hydration hook mid-save. An `isSavingRef`
guard now suppresses the reactive re-hydration while a save is in
progress; after a successful save the form is explicitly synced from the
freshly-updated store state instead.
- **`_cleanup_local_resources` skipped `ClientManager.stop()` on channel
removal.** `ProxyServer._cleanup_local_resources` deleted each channel's
`ClientManager` entry with `del`, which removed it from the dict but
gave the object no signal to terminate. The per-channel heartbeat
greenlet inside the manager continued running until it next checked its
running flag and found the channel absent from Redis. The entry is now
removed with `pop()` and `stop()` is called on the captured manager
before it is discarded, terminating the heartbeat immediately on channel
cleanup.
- **XC profile `exp_date` not updating on account refresh.**
`refresh_account_profiles` saved the freshly-fetched `custom_properties`
with `update_fields=['custom_properties']`, which excluded `exp_date`
from the SQL `UPDATE`. The model's `save()` method parses the new expiry
from `custom_properties` and assigns it to `self.exp_date`, but that
value was silently dropped because the column was not listed in
`update_fields`. Added `'exp_date'` to the `update_fields` list so both
columns are written together.
- **\~25-second transcode startup delay and worker freeze when starting
ffmpeg under gevent+uWSGI.** Enabling gevent cooperative multitasking
(see Changed above) exposed a deadlock: `fork()` hangs indefinitely in
gevent's `_before_fork` pthread\_atfork handler when called from any
thread while gevent is running - including from real OS threads.
`subprocess.Popen`, which all three ffmpeg spawn sites used, calls
`fork()` internally, stalling the uWSGI worker for \~25 seconds or
freezing it entirely and blocking all other clients on that worker.
- `input/manager.py`: replaced `subprocess.Popen` with `os.posix_spawn`
+ a minimal `_SpawnedProcess` wrapper. `os.posix_spawn` is
POSIX-specified to skip pthread\_atfork handlers entirely.
- `output/fmp4/manager.py`, `output/profile/manager.py`: replaced
`subprocess.Popen` with a new shared `posix_spawn_proc()` helper in
`live_proxy/utils.py`. The helper also sets `O_NONBLOCK` on the stdin
pipe write-end: under gevent, `threading.Thread` is monkey-patched to
greenlets, so a blocking write to a full 64 KB pipe would stall the
entire hub. `_write_all()` in both output managers now treats a `None`
return (EAGAIN on the non-blocking FD) as a cooperative wait via
`select.select()` rather than a fatal error.
- `input/http_streamer.py`: set `O_NONBLOCK` on the HTTP-to-pipe relay
write-end with an EAGAIN retry loop for the same reason.
- `core/views.py` (`stream_view`): replaced `subprocess.Popen` with
`os.posix_spawn`; also fixed a pre-existing indentation bug where the
`return StreamingHttpResponse(...)` was accidentally nested inside
`stream_generator` (making every successful response return `None` and
raise a Django error). Also corrected two `NameError` references to the
undefined `stream_id` variable in log messages.
- `apps/connect/handlers/script.py` (`ScriptHandler`): replaced
`subprocess.run` with a `_posix_run` helper that uses `os.posix_spawn` +
cooperative `select.select` reads + non-blocking `waitpid` polling.
Without this fix, any script-type connect integration configured for
events fired from a uWSGI worker (e.g. `client_connect`) would deadlock
the serving greenlet. Note: `cwd` is no longer set to the script's
directory during execution (it inherits the worker's cwd); this was a
minor convenience, not a documented guarantee.
- **`POST /api/plugins/repos/plugin-detail/` hung for up to 105 seconds
under gevent+uWSGI.** The plugin detail endpoint called GPG via
`subprocess.Popen` to verify per-plugin manifest signatures, triggering
the same `fork()` atfork deadlock described above. Replaced with a
`_gpg_run()` helper that uses `os.posix_spawn`, matching the pattern
used by the ffmpeg and script-handler fixes. `select.select()` drains
stdout/stderr cooperatively (gevent-patched) and `os.waitpid(WNOHANG)`
with `time.sleep(0.01)` reaps the child without blocking the hub.
Results are cached in Redis for 5 minutes per manifest URL so repeat
detail fetches skip GPG entirely. The cache is also invalidated
per-plugin when the owning repo's hub manifest is refreshed, so a newly
released version is visible immediately after a manual hub refresh.
- **XC server sub-path URLs now work correctly.** When a provider serves
its XC API from a sub-path (e.g.
`http://server/Pluto/gb/player_api.php`), Dispatcharr was stripping the
path entirely and hitting the root (`/player_api.php`) instead.
`_normalize_url` now preserves sub-path components and only strips any
trailing `.php` segment (covering `player_api.php`, `get.php`,
`xmltv.php`, and any future endpoint without a maintained list). The
same fix is applied to `get_transformed_credentials` in the M3U profile
transformation path. (Fixes
[#&#8203;1218](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/1218))
- **M3U filter delete confirmation showed wrong field name and had a
typo.** The confirmation dialog for deleting an M3U filter read
`filter.type` (always `undefined`) instead of `filter.filter_type`,
leaving the "Type:" line blank, and displayed "Patter:" instead of
"Pattern:". Both are corrected. — Thanks
[@&#8203;nick4810](https://redirect.github.com/nick4810)
- **M3U form FileInput expanded the modal width on long filenames.**
Uploading a local M3U file with a long name caused the `FileInput` to
expand beyond the modal's layout bounds. The input now clips overflow
with `textOverflow: ellipsis`. — Thanks
[@&#8203;nick4810](https://redirect.github.com/nick4810)
- **Login loading spinner not cleared on successful login.**
`setIsLoading(false)` was inside the `catch` block only, so a successful
login that immediately navigated away left the loading state as `true`
if the component re-mounted. Moved to a `finally` block so it always
resets. — Thanks
[@&#8203;nick4810](https://redirect.github.com/nick4810)
- **Bulk channel edit silently failed on API rejection.** The bulk-edit
submit handler in `ChannelBatch.jsx` only wrote `console.error` when the
PATCH was rejected; the user saw the spinner stop but received no
notification, leading them to assume the save succeeded. The catch block
now surfaces a red toast with the server-provided detail (or a generic
fallback), and the form stays open with the in-progress selection intact
so the user can correct and retry. — Thanks
[@&#8203;CodeBormen](https://redirect.github.com/CodeBormen)
- **Recurring rule edit modal showed a blank Channel field.** The
`RecurringRuleModal` read channel data from a Zustand store slice
(`channels`) that nothing populates - the channels page and all other
consumers switched to on-demand summary fetches in a prior release.
Opening the edit modal from the DVR page always produced an empty
Channel select. The modal now fetches channels via
`API.getChannelsSummary()` when it opens, matching the lightweight
approach used by the one-time recording form and other modals.
- **DVR section count badges appeared at the far right of the screen.**
The "Currently Recording", "Upcoming Recordings", and "Previously
Recorded" section headers wrapped the title and badge in a `Group
justify="space-between"`, which pushed the badge to the opposite edge of
the full-width container. Changed to `Group gap="xs" align="center"` so
each badge sits inline with its heading.
- **Plugin event dispatch aborted silently on first disabled plugin.**
`trigger_event` in `apps/connect/utils.py` iterated `pm.list_plugins()`
and, for disabled plugins, logged a debug message using `plugin.key` /
`plugin.name` (attribute access). Because `list_plugins()` returns
dicts, this raised `AttributeError` on the first disabled plugin
encountered. Fixed by changing the two accesses to `plugin['key']` /
`plugin['name']`. (Fixes
[#&#8203;1231](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/1231))
- Thanks [@&#8203;R3XCHRIS](https://redirect.github.com/R3XCHRIS)
- **Plugin periodic tasks silently missed the first beat tick after
every Celery worker restart.** Plugin modules live outside
`INSTALLED_APPS` so `autodiscover_tasks()` never imports them, and
worker startup skips plugin discovery via
`should_skip_initialization()`. Any plugin using module-level
`@shared_task` had its tasks unregistered with the worker until a lazy
event import warmed the module; beat fired on schedule but the worker
rejected with `Received unregistered task` and advanced `last_run_at`
anyway, hiding the miss. A `worker_ready` hook in
`dispatcharr/celery.py` now eagerly calls
`PluginManager.discover_plugins(sync_db=False)` on every worker boot so
plugin tasks are registered before beat starts firing. (Fixes
[#&#8203;1244](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/1244))
- Thanks [@&#8203;R3XCHRIS](https://redirect.github.com/R3XCHRIS)
- **Plugin monkey-patches and module-level hooks were never applied in
uWSGI workers.** Both uWSGI configs use `lazy-apps=true`, meaning each
worker boots independently and never inherits state from the master.
`should_skip_initialization()` correctly skipped one-shot startup tasks
in workers, but also blocked `discover_plugins`, so plugin modules were
never imported in any of the 4 request-serving workers. Plugins relying
on patching request handling (monkey-patches, signal registrations) were
silently inactive until a Connect event lazily triggered discovery in
that specific worker. Discovery is now run in every process that serves
requests, gated only for Celery processes (which use `worker_ready`) and
management commands that don't serve requests.
- **PostgreSQL connection pool exhausted under load in gevent workers.**
uWSGI's gevent pool runs many greenlets concurrently on a single OS
thread. With `CONN_MAX_AGE = 60`, each greenlet that touched the
database retained its own open connection for the full 60 seconds
(gevent thread-locals are greenlet-locals), rapidly exhausting
PostgreSQL's `max_connections` limit under moderate concurrency.
`DATABASE_CONN_MAX_AGE` is now `0` so connections are closed after each
request. `close_old_connections()` is also called at the top of the
long-running stream manager and cleanup watchdog loops so stale handles
accumulated across greenlet switches are released promptly.
- **Stream proxy race: cleanup watchdog could stop a channel still in
the connecting phase.** When the first viewer triggered channel
initialization, the client was registered only after the connect-wait
loop completed. The cleanup watchdog runs concurrently and stops
channels with zero connected clients after a grace period; if the grace
period elapsed during the connect-wait, the watchdog killed the channel
and left the viewer stuck. The client is now registered before the
connect-wait loop begins so the watchdog always sees at least one
client.
- **2-second "stream thread did not terminate within timeout" warning on
every channel stop.** `_close_socket` in `input/manager.py` was closing
the relay pipe read-end (`self.socket`) before killing the ffmpeg
process. The stream OS thread blocks in `select()` on that fd; on Linux,
closing an fd from another thread while a `select()` is in progress on
it does not reliably interrupt the call (POSIX allows this to be
undefined). The thread stayed blocked for the full chunk-timeout (5 s),
and `stream_thread.join(timeout=2.0)` always expired first. Fixed by
killing ffmpeg first: when ffmpeg dies its copy of the relay write-end
closes, delivering EOF to `select()` immediately. `self.socket` is
closed afterward as cleanup only.
- **Duplicate `stop_channel` calls on every channel shutdown.**
`StreamGenerator._cleanup` triggered channel shutdown via two parallel
paths: `client_manager.remove_client()` (which fires
`handle_client_disconnect` on the owning worker, the correct path) and
`_schedule_channel_shutdown_if_needed` (which independently spawned a
delayed `stop_channel` greenlet). The duplicate call was suppressed by
the `_stopping_channels` guard but produced a redundant log entry and
unnecessary greenlet on every shutdown.
`_schedule_channel_shutdown_if_needed` has been removed;
`handle_client_disconnect` is the sole shutdown trigger.
- **Concurrent greenlets could re-enter `_close_socket` during
`proc.wait()`.** `self.transcode_process` was set to `None` at the end
of the `if proc:` block rather than immediately after capturing the
reference. Under gevent, `proc.wait(timeout=0.5)` yields the hub,
allowing a second greenlet to enter `_close_socket`, find
`self.transcode_process` still set, and attempt a second kill+close.
`self.transcode_process = None` is now assigned immediately after `proc
= self.transcode_process` so concurrent callers see `None` and skip the
block.
- **Channel card "started at" tooltip jumping by 1 second on every stats
poll.** The Stats page channel card tooltip showed the channel start
time by computing `Date.now() - uptime * 1000` on every render. Because
`uptime` is a server-side elapsed-seconds value recomputed each
response, this reconstruction drifted by up to 1 second per tick. The
basic channel info path now emits `started_at` (the raw Unix timestamp
from Redis) alongside `uptime`, matching what the detailed stats path
already sent. The frontend `getStartDate` helper now accepts the stable
`started_at` timestamp directly, so the displayed wall-clock time is
fixed from the first poll and never changes.
- **Shift+click range selection in the channels table broken after row
memoization.** After `MemoizedTableRow` was introduced,
`handleShiftSelect` captured `lastClickedId` from its render-time
closure. Because the memo comparator intentionally excludes callback
function references, unselected rows retained the stale closure where
`lastClickedId === null`, so every shift+click from a previously
unchecked row fell through to a plain toggle instead of selecting the
range. Added `lastClickedIdRef` and `allRowIdsRef` alongside the
existing `selectedTableIdsRef`; `handleShiftSelect` now reads from those
refs so every row uses the current anchor ID and full ID list regardless
of which render produced the closure.
- **Selected rows in the channels table did not show the teal
highlight.** `MemoizedTableRow` applied `backgroundColor:
'#&#8203;163632'` based on `row.getIsSelected()` from TanStack Table's
API. Because `state.rowSelection` was never wired into `useReactTable`,
`row.getIsSelected()` always returned `false` and selected rows remained
unstyled. Changed to use the `isSelected` prop, which is correctly
derived from `selectedTableIdsSet` and already tracked by the memo
comparator.
- **`blur` event listener in `useTable` leaked on component unmount.**
The `useEffect` cleanup function called
`window.removeEventListener('blur', ...)` with a newly created anonymous
function literal that never matched the handler registered at setup
time, so the listener was never removed. Extracted to a named
`handleBlur` constant so setup and cleanup reference the same function.

##### Performance

- **EPG HTTP response cache replaced with `django-redis`.** The default
Django cache backend was `LocMemCache`, an in-process memory store. With
multiple uWSGI workers, each worker independently generated and cached
the full EPG XML in its own heap; with 4 workers the peak memory cost
was up to 4 times the size of the EPG document. The cache backend is now
`django_redis.cache.RedisCache`, backed by the same Redis instance used
by `channels_redis`, so a single cached EPG copy is shared across all
workers.
- **`AutoSyncAdvanced` and `LogoForm` are now lazy-loaded in the M3U
group filter.** Both components are large and only needed when the user
opens the gear modal or logo upload modal. Wrapping them in `React.lazy`
+ `Suspense` removes them from the initial bundle and defers their
parse/execute cost until first use. — Thanks
[@&#8203;nick4810](https://redirect.github.com/nick4810)
- **Auto-sync at scale**: the new override-aware sync flow is more
capable than the prior path but the implementation choices below keep it
viable on libraries with thousands of channels. — Thanks
[@&#8203;CodeBormen](https://redirect.github.com/CodeBormen)
- **Bulk writes throughout `sync_auto_channels`.** Per-row
`Channel.objects.create()` and `.save()` calls were replaced with
`bulk_create()` and `bulk_update()` paths that batch the entire group's
create + update sets into single round-trips. The renumber pass collects
all dirty channels into one list and flushes with a single `bulk_update`
at the end of the loop. `ChannelStream.order` writes were similarly
consolidated into a single `bulk_update`.
- **Single-pass collision detection via a global `used_numbers` set.**
Choosing a free channel number was previously an `O(N)` DB query per
stream. The new path seeds a `set()` of all reserved numbers (existing
channels + override pins) once per run; `_next_available_number()` is
`O(cluster size)` against that set. The seed deliberately excludes only
this account's visible auto-created channels (the rows about to be
reassigned), so cross-account and hidden-channel reservations are
honored without extra queries.
- **SQL-level effective-value coalescing via
`with_effective_values()`.** All channel-data consumers (HDHR, M3U, EPG,
XC, channel list / detail, TV Guide summary) read effective values
directly from a single annotated queryset that left-joins the override
row at the database layer. Prevents N+1 `ChannelOverride` lookups across
the channel scan and lets the same query serve the join.
- **EPG dispatch deduplication.** `bulk_create` and `bulk_update` bypass
`post_save`, so the post-loop step explicitly dispatches
`parse_programs_for_tvg_id.delay` once per unique `epg_data_id` actually
changed in the run, not per channel. Avoids fan-out where one EPG source
change otherwise queued thousands of redundant parse tasks.
- **Logo and EPGData lookup caching during sync.** `sync_auto_channels`
now caches Logo and EPGData lookups in a per-run dict so repeated
provider URLs / IDs across many streams do not produce duplicate
`Logo.objects.get_or_create` or `EPGData.objects.filter` calls.
- **Override-aware bulk PATCH endpoint.**
`update_channels_with_override_routing` partitions the bulk-edit
selection into auto-created (override write) vs manual (direct write)
once, then issues two bulk PATCHes instead of N single-row updates.
- **`xc_get_live_streams` response-build overhead reduced.** Previously
the function iterated the full channel queryset three times (two passes
for collision-free integer number mapping, one pass to build the
response list) and called `reverse()` and
`build_absolute_uri_with_port()` once per channel to construct logo
URLs, amounting to N URL-pattern lookups and N header-parse calls per
request. The number-mapping passes are now combined into one full
queryset pass that immediately classifies channels with integer numbers,
deferring only the fractional-number subset to a second, smaller loop.
Logo URL construction is precomputed once per request using a single
`reverse()` call; per-channel URLs are assembled with string
interpolation. `_get_default_group_id()` is also called at most once per
null-group channel instead of twice. The `get_live_streams` API endpoint
now returns a `StreamingHttpResponse` backed by a generator that
serializes one channel entry at a time instead of building the full JSON
array in memory before writing; time-to-first-byte is lower on large
channel libraries and peak worker memory is O(1) per channel rather than
O(N). (Fixes
[#&#8203;1220](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/1220))
- **Stream filter-options fast path when no filters are active.** `GET
/api/channels/streams/filter-options/` previously ran `DISTINCT` queries
across the full streams table while also mutating the underlying Django
request object to strip inapplicable filter params. With no active
filters the fast path now queries the streams table directly with two
simple `DISTINCT` aggregations, skipping the request mutation and
filterset instantiation overhead.
- **Channel list queryset drops unconditional `DISTINCT`.**
`ChannelViewSet.get_queryset()` previously appended `.distinct()`
unconditionally. `DISTINCT` is only needed when the query joins a
one-to-many table - specifically when `channel_profile_id` or
`only_stale` filters are active. The queryset now returns plain results
in the common case, eliminating sort-and-deduplicate overhead on most
channel list fetches.
- **`JsonResponse` for ID list and channel summary endpoints.** The
`stream_ids`, `channel_ids`, and channel `summary` endpoints now use
`django.http.JsonResponse` instead of DRF's `Response`, bypassing the
DRF renderer pipeline (content-type negotiation, serializer dispatch)
for responses that are already plain Python structures. Removes overhead
on every channel-table load and stream-table load.
- **Logo queryset annotates `channel_count` to eliminate N+1 in
`LogoSerializer`.** `LogoViewSet.get_queryset()` now annotates each row
with `Count('channels')`. `LogoSerializer.get_channel_count()` and
`get_is_used()` read the annotation directly instead of issuing a
separate `COUNT(*)` per logo. The `used=true` and `used=false` list
filters use the annotation for their conditions, removing the `DISTINCT`
that was previously required.
- **`ChannelProfileSerializer` reads prefetched memberships.**
`ChannelProfileViewSet.get_queryset()` now prefetches enabled
`ChannelProfileMembership` rows into `enabled_memberships`.
`ChannelProfileSerializer.get_channels()` uses the prefetched set when
available, eliminating one query per profile in any response that lists
multiple profiles.
- **Channel table selection re-render reduction.**
- Removed `isShiftKeyDown` React state from `useTable`. Setting it on
every `keydown`/`keyup` event triggered a re-render of any table
consumer (`ChannelsTable`, `CustomTableBody`, \~50 memoized row
comparisons) each time shift was pressed or released. The visual
shift-key effect is handled entirely by `document.body.classList`
manipulation, so the React state served no purpose.
- Removed the `selectedChannelIds` reactive Zustand store subscription
from `ChannelsTable`. Each checkbox click wrote to both local
`selectedTableIds` state and the store via `onRowSelectionChange`,
causing two sequential re-renders of `ChannelsTable` per click. The two
consumers of this subscription (`deleteChannel` and `ChannelBatchForm`)
now read from `table.selectedTableIds` directly.
- Removed the dead `rowSelection` useMemo that built a TanStack
row-selection map from `selectedTableIds`. The map was placed in
`tableInstance` but `state.rowSelection` was never passed to
`useReactTable`, so TanStack never consumed it. Eliminated a full
page-row iteration on every selection change.

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Renovate
Bot](https://redirect.github.com/renovatebot/renovate).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xMzAuMSIsInVwZGF0ZWRJblZlciI6IjQzLjEzMC4xIiwidGFyZ2V0QnJhbmNoIjoibWFzdGVyIiwibGFiZWxzIjpbImFwcC9kaXNwYXRjaGFyciIsImF1dG9tZXJnZSIsInJlbm92YXRlL2NvbnRhaW5lciIsInR5cGUvbWlub3IiXX0=-->
2026-05-22 02:29:53 +02:00
TrueCharts Bot 3a669055d6 feat(helm-deps): update chart common 29.2.8 → 29.3.4 (#48099)
This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [common](https://truecharts.org/charts/library/common)
([source](https://ghcr.io/cloudnative-pg/postgis)) | minor | `29.2.8` →
`29.3.4` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the [Dependency
Dashboard](../issues/18710) for more information.

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Renovate
Bot](https://redirect.github.com/renovatebot/renovate).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xMzAuMSIsInVwZGF0ZWRJblZlciI6IjQzLjEzMC4xIiwidGFyZ2V0QnJhbmNoIjoibWFzdGVyIiwibGFiZWxzIjpbImFwcC96d2F2ZWpzMm1xdHQiLCJhdXRvbWVyZ2UiLCJyZW5vdmF0ZS9jb250YWluZXIiLCJ0eXBlL21pbm9yIl19-->

Signed-off-by: Alfred Göppel <43101280+alfi0812@users.noreply.github.com>
Co-authored-by: Alfred Göppel <43101280+alfi0812@users.noreply.github.com>
2026-05-11 05:34:10 +02:00
TrueCharts Bot 04236fdeb3 feat(dispatcharr): update image docker.io/dispatcharr/dispatcharr 0.23.0 → 0.24.0 (#47828) 2026-05-03 21:00:37 +02:00
TrueCharts Bot 8ef3aa184c feat(helm-deps): update chart common 29.1.6 → 29.2.8 (#47769)
This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [common](https://truecharts.org/charts/library/common)
([source](https://ghcr.io/cloudnative-pg/postgis)) | minor | `29.1.6` →
`29.2.8` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the [Dependency
Dashboard](../issues/18710) for more information.

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Renovate
Bot](https://redirect.github.com/renovatebot/renovate).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xMzAuMSIsInVwZGF0ZWRJblZlciI6IjQzLjEzMC4xIiwidGFyZ2V0QnJhbmNoIjoibWFzdGVyIiwibGFiZWxzIjpbImFwcC96d2F2ZWpzMm1xdHQiLCJhdXRvbWVyZ2UiLCJyZW5vdmF0ZS9jb250YWluZXIiLCJ0eXBlL21pbm9yIl19-->

Signed-off-by: Alfred Göppel <43101280+alfi0812@users.noreply.github.com>
Co-authored-by: Alfred Göppel <43101280+alfi0812@users.noreply.github.com>
2026-05-01 20:17:18 +02:00
TrueCharts Bot 7be5b7277c fix(helm-deps): update chart common 29.1.0 → 29.1.6 (#47400)
This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [common](https://truecharts.org/charts/library/common)
([source](https://ghcr.io/cloudnative-pg/postgis)) | patch | `29.1.0` →
`29.1.6` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the [Dependency
Dashboard](../issues/18710) for more information.

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Renovate
Bot](https://redirect.github.com/renovatebot/renovate).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xMzAuMSIsInVwZGF0ZWRJblZlciI6IjQzLjEzMC4xIiwidGFyZ2V0QnJhbmNoIjoibWFzdGVyIiwibGFiZWxzIjpbImFwcC96d2F2ZWpzMm1xdHQiLCJhdXRvbWVyZ2UiLCJyZW5vdmF0ZS9jb250YWluZXIiLCJ0eXBlL3BhdGNoIl19-->

Signed-off-by: Alfred Göppel <43101280+alfi0812@users.noreply.github.com>
Co-authored-by: Alfred Göppel <43101280+alfi0812@users.noreply.github.com>
2026-04-22 20:58:48 +02:00
TrueCharts-Bot e0feb4afdb Commit daily changes
Signed-off-by: TrueCharts-Bot <bot@truecharts.org>
2026-04-20 00:25:55 +00:00
TrueCharts Bot ffc13416f5 feat(helm-deps): update chart common 29.0.15 → 29.1.0 (#47224)
This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
|
[common](https://truecharts.org/truetech/truecharts/charts/library/common)
([source](https://ghcr.io/cloudnative-pg/postgis)) | minor | `29.0.15` →
`29.1.0` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the [Dependency
Dashboard](../issues/18710) for more information.

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Renovate
Bot](https://redirect.github.com/renovatebot/renovate).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yOS4yIiwidXBkYXRlZEluVmVyIjoiNDMuMjkuMiIsInRhcmdldEJyYW5jaCI6Im1hc3RlciIsImxhYmVscyI6WyJhcHAvendhdmVqczJtcXR0IiwiYXV0b21lcmdlIiwicmVub3ZhdGUvY29udGFpbmVyIiwidHlwZS9taW5vciJdfQ==-->

Signed-off-by: Alfred Göppel <43101280+alfi0812@users.noreply.github.com>
Co-authored-by: Alfred Göppel <43101280+alfi0812@users.noreply.github.com>
2026-04-19 11:20:31 +02:00
TrueCharts-Bot 371d1817b4 Commit daily changes
Signed-off-by: TrueCharts-Bot <bot@truecharts.org>
2026-04-19 00:25:45 +00:00
TrueCharts Bot 4b163d1dc9 feat(dispatcharr): update image docker.io/dispatcharr/dispatcharr 0.22.1 → 0.23.0 (#47157)
This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
|
[docker.io/dispatcharr/dispatcharr](https://redirect.github.com/Dispatcharr/Dispatcharr)
| minor | `ccdfa7e` → `b731cda` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the [Dependency
Dashboard](../issues/18710) for more information.

Add the preset `:preserveSemverRanges` to your config if you don't want
to pin your dependencies.

---

### Release Notes

<details>
<summary>Dispatcharr/Dispatcharr
(docker.io/dispatcharr/dispatcharr)</summary>

###
[`v0.23.0`](https://redirect.github.com/Dispatcharr/Dispatcharr/blob/HEAD/CHANGELOG.md#0230---2026-04-17)

[Compare
Source](https://redirect.github.com/Dispatcharr/Dispatcharr/compare/v0.22.1...v0.23.0)

##### Security

- Set `DEFAULT_PERMISSION_CLASSES` to `IsAdmin` in the DRF
configuration. All viewsets and function-based views that require
non-admin or unauthenticated access were explicitly annotated: proxy
streaming endpoints (`stream_ts`, `stream_xc`, `stream_vod`, `head_vod`,
`stream_xc_movie`, `stream_xc_episode`) use
`@permission_classes([AllowAny])` (access is controlled by the
per-stream-type network allow-list inside the view body); the
`UserAgentViewSet`, `StreamProfileViewSet`, `CoreSettingsViewSet`, and
`ProxySettingsViewSet` gained `get_permissions()` methods mapping read
actions to `IsStandardUser` and write actions to `IsAdmin`; and
`AuthViewSet.logout` was updated to return `[Authenticated()]`.
- Fixed missing `network_access_allowed` checks in the VOD proxy.
`stream_vod`, `head_vod`, `stream_xc_movie`, and `stream_xc_episode`
were not checking the `STREAMS` network policy, unlike the equivalent TS
proxy endpoints.
- Explicitly marked the HDHomeRun discovery endpoints
(`DiscoverAPIView`, `LineupAPIView`, `LineupStatusAPIView`,
`HDHRDeviceXMLAPIView`) and the version endpoint with
`permission_classes = [AllowAny]` to document their intentionally public
access now that the global default is `IsAdmin`.
- Fixed path traversal vulnerability in file uploads. The M3U account
upload (`apps/m3u/api_views.py`), logo upload
(`apps/channels/api_views.py`), and backup upload
(`apps/backups/api_views.py`) all used the uploaded filename directly
without sanitization. `os.path.join()` discards all preceding components
when it encounters an absolute path segment, and `pathlib`'s `/`
operator behaves identically; a relative `../` sequence also escapes via
OS path resolution at `open()` time. All three upload paths now strip
directory components via `Path(name).name` and validate the resolved
path remains within the intended upload directory. Exploiting any of
these required admin credentials.
- Prevented users from setting `xc_password` (and other admin-managed
keys) on their own account via the `PATCH /api/accounts/users/me/`
endpoint.
- Hardened the HLS proxy `change_stream` endpoint by converting it from
a plain Django view to a DRF `@api_view` with
`@permission_classes([IsAdmin])`, ensuring the endpoint actually
enforces admin-only access. The previous decorator arrangement
(`@csrf_exempt` + `@permission_classes`) had no effect on a plain Django
view.
- Added rate limiting to the login endpoint (`POST
/api/accounts/token/`) using DRF's built-in throttling. A
`LoginRateThrottle` (3 requests/minute per IP, sliding window) is
applied to the `TokenObtainPairView`. Repeated failed attempts from the
same IP receive `429 Too Many Requests`.
- Extended rate limiting to the session-auth login alias (`POST
/api/accounts/auth/login/`). It now delegates entirely to
`TokenObtainPairView`, inheriting its throttle, network access check,
and audit logging, and returns JWT tokens instead of a session cookie
(the session-based response was unusable since `SessionAuthentication`
is not in `DEFAULT_AUTHENTICATION_CLASSES`). Both endpoints share the
same `"login"` throttle scope, so attempts across either path count
against the same per-IP limit.
- Removed `CORS_ALLOW_CREDENTIALS = True` from CORS configuration.
Dispatcharr authenticates via JWT `Authorization` headers and API keys —
not cookies — so credentials are never sent cross-origin by browsers.
The setting was also redundant: browsers reject
`Access-Control-Allow-Credentials: true` when
`Access-Control-Allow-Origin` is a wildcard (`*`), so it had no effect
in practice.
- Updated frontend npm dependencies to resolve 6 audit vulnerabilities
(6 high):
- Updated `@xmldom/xmldom` 0.8.11 → 0.8.12, resolving **high** XML
injection via unsafe CDATA serialization allowing attacker-controlled
markup insertion
([GHSA-wh4c-j3r5-mjhp](https://redirect.github.com/advisories/GHSA-wh4c-j3r5-mjhp))
- Updated `lodash` 4.17.23 → 4.18.1, resolving **high** Code Injection
via `_.template` imports key names
([GHSA-r5fr-rjxr-66jc](https://redirect.github.com/advisories/GHSA-r5fr-rjxr-66jc))
and **high** Prototype Pollution via array path bypass in `_.unset` and
`_.omit`
([GHSA-f23m-r3pf-42rh](https://redirect.github.com/advisories/GHSA-f23m-r3pf-42rh))
- Updated `vite` 7.3.1 → 7.3.2, resolving **high** Path Traversal in
optimized deps `.map` handling
([GHSA-4w7w-66w2-5vf9](https://redirect.github.com/advisories/GHSA-4w7w-66w2-5vf9)),
**high** `server.fs.deny` bypass with queries
([GHSA-v2wj-q39q-566r](https://redirect.github.com/advisories/GHSA-v2wj-q39q-566r)),
and **high** Arbitrary File Read via dev server WebSocket
([GHSA-p9ff-h696-f583](https://redirect.github.com/advisories/GHSA-p9ff-h696-f583))
- Updated `Django` 6.0.3 → 6.0.4, resolving the following CVEs:
- **CVE-2026-33033**: Potential DoS via `MultiPartParser` through
crafted multipart uploads.
- **CVE-2026-33034**: SGI requests with a missing or understated
`Content-Length` header could bypass the `DATA_UPLOAD_MAX_MEMORY_SIZE`
limit.
  - **CVE-2026-4292**: Privilege abuse in `ModelAdmin.list_editable`.
- **CVE-2026-3902**: ASGI header spoofing via underscore/hyphen
conflation.
  - **CVE-2026-4277**: Privilege abuse in `GenericInlineModelAdmin`.

##### Added

- **EPG historical data window**: the EPG XML output and XC EPG API now
support a `prev_days` URL parameter (e.g. `&prev_days=3`) to include
past programs in the EPG response. This allows third-party players that
request historical program schedules to receive the data they need. The
EPG URL builder in the Channels page exposes "Days forward" and "Days
back" controls. Per-user defaults for both values (`epg_days` /
`epg_prev_days`) can be configured in the User settings modal and are
applied automatically when no URL parameter is present. (Closes
[#&#8203;1154](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/1154))
- **Plugin Hub**: administrators can now browse, install, and update
plugins directly from remote repositories via a new Plugin Hub page in
Settings. (Closes
[#&#8203;393](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/393))
— Thanks [@&#8203;sethwv](https://redirect.github.com/sethwv)
- Install plugins directly from the hub: the release zip is downloaded,
SHA256 integrity is verified, and the plugin is installed atomically.
- Update managed plugins when a newer version is available from their
source repo. Version compatibility constraints
(`min_dispatcharr_version` / `max_dispatcharr_version`) are enforced at
install time.
- Browse available plugins from all enabled repos with name,
description, version, author, and icon.
- Plugins installed from a repo are tracked as "managed": source repo,
slug, installed version, prerelease flag, and deprecated status are all
persisted and surfaced in the UI.
- Add plugin repositories by manifest URL. The official Dispatcharr
Plugins repository is pre-configured; third-party repos are supported by
supplying an optional GPG public key.
- Manifest signatures are verified via GPG; the official repo uses a
bundled public key. Signature status is displayed per-repo.
- Preview a repository URL before adding it - validates the manifest and
reports plugin count and signature status without saving anything.
- Configurable automatic manifest refresh interval (in hours; 0 to
disable) runs as a Celery background task.

##### Removed

- Removed dead `VODConnectionManager` class
(`apps/proxy/vod_proxy/connection_manager.py`) and its associated
helpers, which had been superseded by `MultiWorkerVODConnectionManager`.
All active code already used the multi-worker implementation. Removed
the unused `VODConnectionManager` import from `vod_proxy/views.py`, the
unscheduled `cleanup_vod_connections` task from `apps/proxy/tasks.py`,
and the unscheduled `cleanup_vod_persistent_connections` task from
`core/tasks.py`.
- Removed dead VOD URL routes: `VODPlaylistView` (playlist generation),
`VODPositionView` (position tracking), and the class-based
`VODStatsView` (replaced by the existing function-based `vod_stats`
view).
- Removed dead `updateVODPosition()` API method from
`frontend/src/api.js`, which called the now-removed position tracking
endpoint.

##### Fixed

- Fixed TV Guide "Record One" always scheduling the recording on the
first channel that matched the program's `tvg_id`, rather than the
channel the user actually selected. When multiple channels share the
same EPG source, the intended channel was silently ignored. The selected
channel object is now passed explicitly through the click handler chain
to `recordOne`, bypassing the `findChannelByTvgId` fallback lookup
entirely. (Fixes
[#&#8203;1140](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/1140))
— Thanks [@&#8203;fezster](https://redirect.github.com/fezster)
- Graceful container shutdown: `docker stop` no longer results in exit
137 (SIGKILL). The entrypoint now explicitly stops all child processes —
including uWSGI workers, Celery, Daphne, and Redis, which are spawned as
uWSGI `attach-daemon` children and were previously invisible to the
signal handler. A polling loop replaces the old fixed `sleep`, exiting
as soon as all processes have stopped (up to an 8-second ceiling before
force-stopping). PostgreSQL is stopped using `pg_ctl stop -m immediate`
as a fallback rather than SIGKILL to avoid data corruption. Process
names are now recorded at startup and displayed correctly in crash
diagnostics. The unexpected-exit diagnostic block is now suppressed on
normal `docker stop` shutdowns. — Thanks
[@&#8203;Shokkstokk](https://redirect.github.com/Shokkstokk) for the
initial fix!
- Fixed two race conditions in the VOD proxy that caused the
`profile_connections` counter to go permanently negative, allowing
connections beyond the configured profile limit. (1)
`_decrement_profile_connections()` used a GET-before-DECR guard: two
concurrent decrements could both read the same positive value, both pass
the guard, and both fire, driving the counter below zero. Replaced with
an unconditional `DECR` followed by a clamp-to-zero if the result is
negative. (2) The `stream_generator` decremented `active_streams` and
then checked `has_active_streams()` in two separate Redis round-trips
without locking. A concurrent generator on another worker could read
`active_streams=0` in the window between those two calls and also
decrement the profile counter, producing a double-decrement. A new
`decrement_active_streams_and_check()` method performs both operations
under a single distributed lock, and a `profile_decremented` flag guards
all four call sites in the generator so the profile counter is only ever
decremented once per stream. (Closes
[#&#8203;1125](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/1125))
— Thanks
[@&#8203;firestaerter3](https://redirect.github.com/firestaerter3)
- Fixed a provider TCP connection leak in the VOD proxy
`stream_generator`. When a stream ended via an unhandled exception path
that reached the `finally` block without any of the three exception
handlers having run (e.g. an error raised before the first `yield`), the
`finally` block decremented counters but never called
`redis_connection.cleanup()`. The upstream `requests.Response` and
`requests.Session` were left open until garbage collection. The
`finally` block now starts a `delayed_cleanup` daemon thread (matching
the 1-second delay used by the normal-completion and `GeneratorExit`
paths) so that seeking clients have time to reconnect and increment
`active_streams` before `cleanup()` checks whether it is safe to close
the connection.
- Fixed manual stream selection from the Stats page not enforcing M3U
profile connection limits in multi-worker deployments. When a non-owning
worker handled the `change_stream` request it correctly packaged
`stream_id` and `m3u_profile_id` into the Redis pubsub message, but the
owning worker's pubsub handler only consumed `url` and `user_agent`
silently dropping both IDs before calling `stream_manager.update_url()`.
Because `update_url` only calls `update_stream_profile()` when a
`stream_id` is provided, the `profile_connections` counter was never
updated after the switch, causing subsequent capacity checks to see
incorrect counts and bypass the full-profile guard. The handler now
extracts `stream_id` and `m3u_profile_id` from the event and forwards
them to `update_url()`. The bug did not affect single-worker / dev-mode
deployments because the owning worker handles those requests directly
without pubsub.
- Fixed the `next_stream` rotation endpoint applying the same class of
bug: `get_stream_info_for_switch()` was called and returned
`m3u_profile_id`, but the result was dropped when forwarding to
`ChannelService.change_stream_url()`, so `update_stream_profile()` was
never called and `profile_connections` counters were not updated after
an automatic stream rotation.
- Fixed stream switch metadata (`url`, `user_agent`, `stream_id`,
`m3u_profile`) being written to Redis before the switch was confirmed to
succeed. If the switch failed, URL unchanged or exception during
teardown, Redis described a URL not actually in use. Metadata is now
written only after `update_url()` returns `True`; on failure the owner
writes `stream_manager.url` back as the ground truth. The non-owner no
longer pre-writes metadata at all, all needed info is carried in the
pubsub payload and written by the owner after confirmation.
- Fixed the Stats page "Active Stream" dropdown not updating when a
stream switch occurs. The card was matching the active stream by
comparing the URL stored in Redis against stream URLs from the database,
which failed silently when the stored URL was a transformed/rewritten
value that didn't substring-match the original. The dropdown now matches
by `stream_id` (the authoritative value already present in the stats
payload) and re-runs only when `stream_id` changes, so the normal
polling interval drives updates with no extra renders.
- Fixed the XC Password field in the User modal being editable by
standard users despite the backend (`PATCH /api/accounts/users/me/`)
stripping `xc_password` from `custom_properties` for non-admin users,
causing the change to silently revert on save. The field and its
generate button are now disabled with an explanatory description when
the current user is not an administrator.
- Fixed live stream hiccups caused by nginx buffering TS proxy data to
disk. The `/proxy/` location block used `proxy_buffering off` and
`proxy_read/send_timeout` directives, which are silently ignored when
the upstream is `uwsgi_pass` (a different directive family). nginx was
therefore defaulting to `uwsgi_buffering on`, spooling stream data
through temp files on disk. Replaced with the correct `uwsgi_buffering
off`, `uwsgi_read_timeout 300s`, and `uwsgi_send_timeout 300s`
directives so stream data flows directly from uWSGI to the client socket
without intermediate disk I/O.
- Fixed the logo cache endpoint (`/api/channels/logos/{id}/cache/`)
holding a uWSGI greenlet indefinitely when fetching from a slow or
dripping remote server. The previous implementation used
`StreamingHttpResponse(iter_content())` with only a per-chunk read
timeout; a server that drips data just fast enough to reset the per-read
timer could hold the greenlet open forever. Replaced with an eager read
loop enforcing a hard total-download deadline (10 s) and a size cap (5
MB). Also fixed a race condition in the existing negative-cache logic:
the failure entry for a URL was cleared immediately upon receiving HTTP
200, before the body was read. A concurrent greenlet seeing no failure
entry during a slow download that ultimately timed out would also
attempt the fetch, defeating the cache. The entry is now cleared only
after the full body has been successfully received.
- Fixed uploading a local M3U file with no expiration date set sending
the string `"null"` as the `exp_date` field in the `FormData` request,
causing a 400 validation error from the API. Null/undefined values are
now skipped when building the `FormData` body, matching the behaviour
already present in the update path.
- Fixed `PATCH /api/channels/channels/edit/bulk/` returning a 500 error
when the request body included a `streams` list. The bulk edit handler
was iterating `validated_data` directly and calling `setattr(channel,
"streams", value)`, which Django prohibits on ManyToMany fields. Also
added an `@extend_schema` decorator so the Swagger UI correctly
documents the endpoint as accepting a JSON array and shows the `streams`
field. (Fixes
[#&#8203;883](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/883))
- Fixed several incorrect or incomplete OpenAPI (`@extend_schema`)
schemas across the API:
- `POST /api/epg/import/` — request body was undocumented; now correctly
shows the `id` field. Description updated from "import" to "refresh" to
match frontend and backend terminology.
- `DELETE /api/channels/logos/bulk-delete/` — `delete_files` boolean was
missing from the documented request body.
- `POST /api/channels/channels/batch-set-epg/` — `epg_data_id` inside
each association object was not marked `allow_null`/`required=False`,
even though passing `null` is the correct way to remove an EPG link.
- `PUT /api/connect/integrations/{id}/subscriptions/set/` — endpoint had
no `@extend_schema` at all; now documents that the request body is a
JSON array of subscription objects.

##### Changed

- **Output bitrate DB persistence**: the `ffmpeg_output_bitrate` stat is
no longer written to the database on every FFmpeg stats tick
(\~2/second). Instead, a local exponential moving average (EMA, α=0.1)
accumulates readings continuously. The first 10 samples (\~5 seconds)
are discarded as warmup to avoid polluting the average with FFmpeg's
unstable ramp-up values. After warmup, the smoothed value is flushed to
the database at most once every 30 seconds, and a final flush occurs
when the stream stops but only if the EMA has been seeded (i.e. the
stream ran past warmup). Streams that stop during warmup leave the
existing database value untouched, preserving previously accurate
measurements when channel-hopping.
- Performance: `generate_m3u`, `generate_epg`, and `xc_get_live_streams`
now use `select_related('channel_group', 'logo')` (or
`select_related('logo')` for EPG) on every Channel queryset in
`apps/output/views.py`. Previously each channel in the loop triggered a
separate database query for its `logo` and `channel_group` foreign keys;
with the JOIN-based prefetch this is reduced to a single query per
request. On deployments with \~2 000 channels, `xc_get_live_streams`
response time drops from \~2.5–4 s to \~250–450 ms. (Closes
[#&#8203;1127](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/1127))
— Thanks [@&#8203;xBOBxSAGETx](https://redirect.github.com/xBOBxSAGETx)
- Performance: `generate_epg` now uses
`select_related('epg_data__epg_source')` on all EPG channel querysets,
eliminating N+1 database queries for `EPGSource` traversal per channel
(\~15 s improvement on \~2000-channel deployments; total EPG generation
time dropped from \~87 s to \~72 s in benchmarks).
- Performance: `xc_get_epg` now uses
`select_related('epg_data__epg_source')` on all three channel fetch
paths. Previously each request triggered 2 additional queries to resolve
`channel.epg_data` and `channel.epg_data.epg_source`.
- Performance: `generate_m3u` now uses `prefetch_related` for streams
when `?direct=true` is requested, eliminating N+1 stream queries (one
per channel) on that code path.
- Performance: `EPGGridAPIView` (`apps/epg/api_views.py`) now uses
`select_related('epg_data__epg_source')` on the
`channels_with_custom_dummy` queryset, eliminating 2 extra queries per
channel (for `epg_data` and `epg_source`) in the dummy EPG generation
loop.
- Performance: `generate_epg` now issues a single cross-channel
`ProgramData` bulk query. `.values()` returns plain dicts, bypassing
per-row Django model instantiation. Results are consumed in independent
5000-row keyset-paginated chunks. Combined with the `select_related`
improvements above, EPG generation time on large deployments is
significantly reduced.
- Performance: `xc_get_live_streams` no longer calls
`ChannelGroup.objects.get_or_create(name="Default Group")` once per
null-group channel; replaced with a lazy-initialised closure that
executes at most one query regardless of how many ungrouped channels are
present.
- AIO containers now connect to the internal PostgreSQL instance via a
Unix domain socket instead of TCP loopback. Users who have
`POSTGRES_HOST` explicitly set to `localhost` or `127.0.0.1` in their
compose file are automatically migrated to the socket path; any other
explicit value (external host/IP) is left untouched. — Thanks
[@&#8203;JCBird1012](https://redirect.github.com/JCBird1012)
- Improved the EPG response cache key. Previously it was based on the
raw query string and username, meaning a user default of `epg_days=7`
and an explicit `&days=7` URL parameter produced different cache entries
for identical output. The key is now built from all resolved effective
parameter values (`days`, `prev_days`, `cachedlogos`, `tvg_id_source`)
so semantically equivalent requests always share the same cache entry.
- Improved the HDHR, M3U, and EPG URL builder popovers in the Channels
table: each popover now opens with a brief intro sentence describing its
purpose. Toggle switches were refactored to use Mantine's native `label`
and `description` props (replacing the previous manual
`Group`/`Stack`/`Text` layout), giving each switch a properly styled
description line beneath its label. Switch alignment was also corrected.
Toggles now appear on the left with the label and description stacked to
the right, consistent with standard Mantine form layout.
- Redesigned the User settings modal with a tabbed layout: **Account**
(username, email, name, password), **Permissions** (user level, stream
limit, channel profiles, mature content filter - admin only), **EPG
Defaults** (days forward/back), and **API & XC** (XC password, API key
management). Fields are now logically grouped rather than split across
two ad-hoc columns.
- EPG channel scanning now automatically removes stale `EPGData`
entries. tvg-ids that were present in a previous scan but are no longer
found in the upstream source, provided they are not mapped to any
channel. This prevents unbounded database bloat over time. Entries
mapped to at least one channel are always preserved.
- Rewrote the M3U line parser as an `iter_m3u_entries` generator that
owns the full per-entry state machine. Intermediate directive lines
between `#EXTINF` and the stream URL are now handled correctly rather
than corrupting the pending entry or being silently misassigned. A
`#EXTINF` with no following URL is discarded with a warning instead of
carrying over a `url`-less entry into batch processing. Attribute keys
are normalised to lowercase during parsing (provider attribute names
remain case-insensitive end-to-end). The `#EXTINF` attribute regex is
pre-compiled at module load, and attribute lookups use O(1) `dict.get()`
instead of linear scans — approximately 10% faster parsing on large M3U
files.
- Added support for the `#EXTGRP` directive in M3U files. When a
`group-title` attribute is absent from the `#EXTINF` line, the value
from a following `#EXTGRP:` line is used as the group. An explicit
`group-title` attribute always takes priority. (Closes
[#&#8203;1088](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/1088))
- Added accumulation of `#EXTVLCOPT` directives per entry. Options are
stored as a list under `vlc_opts` inside the stream's
`custom_properties`, available for downstream use (e.g. passing
VLC-specific options to the player). This is for a planned future
enhancement and can also be utlized with the API.
- M3U stream name parsing now uses the comma text (the canonical display
title per the base `#EXTINF` spec) as the primary stream name, falling
back to `tvc-guide-title`, then `tvg-name`, rather than preferring
`tvg-name` first. Providers that use `tvg-name` as an EPG key and put
the human-readable title after the comma will now display the correct
name. Providers that duplicate the same value in both fields are
unaffected. (Fixes
[#&#8203;1081](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/1081))
- FloatingVideo player: the native video controls (timeline, play/pause,
volume) are now hidden by default when a live stream starts and only
appear when the user hovers over the player.
- Enhanced Swagger UI authorization dialog: registered a custom
`OpenApiAuthenticationExtension` for `ApiKeyAuthentication` so
drf-spectacular now generates an `ApiKeyAuth (apiKey)` entry alongside
`jwtAuth`. Both entries include descriptive text linking to the relevant
endpoints (`/api/accounts/token/`, `/api/accounts/api-keys/generate/`,
`/api/accounts/api-keys/revoke/`).
- Refactored frontend form components (`AccountInfoModal`,
`AssignChannelNumbers`, `Channel`, `ChannelBatch`, `ChannelGroup`,
`Connection`, `CronBuilder`, `DummyEPG`, and `EPG`) to extract business
logic into dedicated utility modules under `src/utils/forms/`. Each
extracted module is covered by unit tests. Mantine compound component
references (`Table.Tbody`, `Popover.Target`, `Accordion.Item`, etc.)
have been updated to use flat named imports. — Thanks
[@&#8203;nick4810](https://redirect.github.com/nick4810)
- Improved the EPG BOM fix from v0.22.1: replaced the
`lstrip(b'\xef\xbb\xbf')` / `startswith` approach with
`start.find(b'<?xml')`, which locates the XML declaration regardless of
any leading bytes BOM, whitespace, or other encoding markers without
needing to know what those bytes are.
- Dependency updates:
  - `Django` 6.0.3 → 6.0.4 (security patch; see Security section)
  - `djangorestframework` 3.16.1 → 3.17.1
  - `requests` 2.33.0 → 2.33.1
  - `gevent` 25.9.1 → 26.4.0
  - `rapidfuzz` 3.14.3 → 3.14.5
  - `sentence-transformers` 5.3.0 → 5.4.0
  - `lxml` 6.0.2 → 6.0.3
- Added `python-gnupg` for GPG signature verification of official and
third-party plugin repository manifests.

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Renovate
Bot](https://redirect.github.com/renovatebot/renovate).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yOS4yIiwidXBkYXRlZEluVmVyIjoiNDMuMjkuMiIsInRhcmdldEJyYW5jaCI6Im1hc3RlciIsImxhYmVscyI6WyJhcHAvZGlzcGF0Y2hhcnIiLCJhdXRvbWVyZ2UiLCJyZW5vdmF0ZS9jb250YWluZXIiLCJ0eXBlL21pbm9yIl19-->

Signed-off-by: Alfred Göppel <43101280+alfi0812@users.noreply.github.com>
Co-authored-by: Alfred Göppel <43101280+alfi0812@users.noreply.github.com>
2026-04-18 18:29:11 +00:00
TrueCharts Bot 9f06caf48c chore(dispatcharr): update image alpine digest to 5b10f43 (#47051) 2026-04-18 18:32:48 +02:00
TrueCharts-Bot 91642136a2 Commit daily changes
Signed-off-by: TrueCharts-Bot <bot@truecharts.org>
2026-04-18 00:24:44 +00:00
Kjeld Schouten fcbe54ccf4 more fixes 2026-04-17 22:06:12 +02:00
TrueCharts-Bot ce6c266331 Commit daily changes
Signed-off-by: TrueCharts-Bot <bot@truecharts.org>
2026-04-17 18:06:06 +00:00
TrueCharts Bot 509cd59e0a fix(helm-deps): update chart common 29.0.10 → 29.0.15 (#47022)
This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
|
[common](https://trueforge.org/truetech/truecharts/charts/library/common)
([source](https://ghcr.io/cloudnative-pg/postgis)) | patch | `29.0.10` →
`29.0.15` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the [Dependency
Dashboard](../issues/18710) for more information.

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Renovate
Bot](https://redirect.github.com/renovatebot/renovate).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yOS4yIiwidXBkYXRlZEluVmVyIjoiNDMuMjkuMiIsInRhcmdldEJyYW5jaCI6Im1hc3RlciIsImxhYmVscyI6WyJhcHAvendhdmVqczJtcXR0IiwiYXV0b21lcmdlIiwicmVub3ZhdGUvY29udGFpbmVyIiwidHlwZS9wYXRjaCJdfQ==-->

Signed-off-by: Alfred Göppel <43101280+alfi0812@users.noreply.github.com>
Co-authored-by: Alfred Göppel <43101280+alfi0812@users.noreply.github.com>
2026-04-15 13:43:44 +02:00
TrueCharts Bot 827a6dd178 fix(dispatcharr): update image docker.io/dispatcharr/dispatcharr 0.22.0 → 0.22.1 (#46845)
This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
|
[docker.io/dispatcharr/dispatcharr](https://redirect.github.com/Dispatcharr/Dispatcharr)
| patch | `06189b5` → `ccdfa7e` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the [Dependency
Dashboard](../issues/18710) for more information.

Add the preset `:preserveSemverRanges` to your config if you don't want
to pin your dependencies.

---

### Release Notes

<details>
<summary>Dispatcharr/Dispatcharr
(docker.io/dispatcharr/dispatcharr)</summary>

###
[`v0.22.1`](https://redirect.github.com/Dispatcharr/Dispatcharr/blob/HEAD/CHANGELOG.md#0221---2026-04-05)

[Compare
Source](https://redirect.github.com/Dispatcharr/Dispatcharr/compare/v0.22.0...v0.22.1)

##### Fixed

- Fixed EPG sources that emit a UTF-8 BOM (e.g. ErsatzTV, EPGShare,
WebGrab+Plus) parsing 0 channels and 0 programmes after the HTML entity
fix introduced in v0.22.0. `bytes.lstrip()` only strips ASCII
whitespace, leaving the three BOM bytes (`EF BB BF`) in place, so
`stripped.startswith(b'<?xml')` returned `False`. The function fell
through to the no-declaration branch and prepended the HTML entity
DOCTYPE block *before* the BOM and XML declaration, producing invalid
XML that lxml silently discarded under `recover=True`. Fixed by
stripping the BOM explicitly before the whitespace strip:
`start.lstrip(b'\xef\xbb\xbf').lstrip()`. BOM-free files are unaffected.
(Closes
[#&#8203;1173](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/1173))
— Thanks [@&#8203;dwot](https://redirect.github.com/dwot) for the fix!

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Renovate
Bot](https://redirect.github.com/renovatebot/renovate).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yOS4yIiwidXBkYXRlZEluVmVyIjoiNDMuMjkuMiIsInRhcmdldEJyYW5jaCI6Im1hc3RlciIsImxhYmVscyI6WyJhcHAvZGlzcGF0Y2hhcnIiLCJhdXRvbWVyZ2UiLCJyZW5vdmF0ZS9jb250YWluZXIiLCJ0eXBlL3BhdGNoIl19-->
2026-04-13 18:27:35 +02:00
TrueCharts Bot d53ffcaf79 fix(helm-deps): update chart common 29.0.0 → 29.0.10 (#46661)
This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
|
[common](https://trueforge.org/truetech/truecharts/charts/library/common)
([source](https://ghcr.io/cloudnative-pg/postgis)) | patch | `29.0.0` →
`29.0.10` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the [Dependency
Dashboard](../issues/18710) for more information.

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Renovate
Bot](https://redirect.github.com/renovatebot/renovate).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yOS4yIiwidXBkYXRlZEluVmVyIjoiNDMuMjkuMiIsInRhcmdldEJyYW5jaCI6Im1hc3RlciIsImxhYmVscyI6WyJhcHAvendhdmVqczJtcXR0IiwiYXV0b21lcmdlIiwicmVub3ZhdGUvY29udGFpbmVyIiwidHlwZS9wYXRjaCJdfQ==-->

Signed-off-by: Alfred Göppel <43101280+alfi0812@users.noreply.github.com>
Co-authored-by: Alfred Göppel <43101280+alfi0812@users.noreply.github.com>
2026-04-02 02:00:45 +02:00
TrueCharts Bot 9fb3507e24 feat(dispatcharr): update image docker.io/dispatcharr/dispatcharr 0.21.1 → 0.22.0 (#46628)
This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
|
[docker.io/dispatcharr/dispatcharr](https://redirect.github.com/Dispatcharr/Dispatcharr)
| minor | `3fe4da0` → `06189b5` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the [Dependency
Dashboard](../issues/18710) for more information.

Add the preset `:preserveSemverRanges` to your config if you don't want
to pin your dependencies.

---

### Release Notes

<details>
<summary>Dispatcharr/Dispatcharr
(docker.io/dispatcharr/dispatcharr)</summary>

###
[`v0.22.0`](https://redirect.github.com/Dispatcharr/Dispatcharr/blob/HEAD/CHANGELOG.md#0220---2026-04-01)

[Compare
Source](https://redirect.github.com/Dispatcharr/Dispatcharr/compare/v0.21.1...v0.22.0)

##### Security

- Updated `requests` 2.32.5 → 2.33.0, resolving the following CVE:
- **CVE-2026-25645** (moderate): Insecure temp file reuse in
`extract_zipped_paths()` utility function.
- Updated frontend npm dependencies to resolve 4 audit vulnerabilities
(2 moderate, 2 high):
- Updated `brace-expansion` 5.0.2 → 5.0.5, resolving **moderate**
zero-step sequence causing process hang and memory exhaustion
([GHSA-f886-m6hf-6m8v](https://redirect.github.com/advisories/GHSA-f886-m6hf-6m8v))
- Updated `flatted` 3.4.1 → 3.4.2, resolving **high** Prototype
Pollution via `parse()` in NodeJS flatted
([GHSA-rf6f-7fwh-wjgh](https://redirect.github.com/advisories/GHSA-rf6f-7fwh-wjgh))
- Updated `picomatch` 4.0.3 → 4.0.4, resolving **high** method injection
in POSIX character classes causing incorrect glob matching
([GHSA-3v7f-55p6-f55p](https://redirect.github.com/advisories/GHSA-3v7f-55p6-f55p))
and a ReDoS vulnerability via extglob quantifiers
([GHSA-c2c7-rcm5-vvqj](https://redirect.github.com/advisories/GHSA-c2c7-rcm5-vvqj))
- Updated `yaml` 1.10.2 → 1.10.3, resolving **moderate** stack overflow
via deeply nested YAML collections
([GHSA-48c2-rrv3-qjmp](https://redirect.github.com/advisories/GHSA-48c2-rrv3-qjmp))

##### Added

- Connection cards on the Stats page now show the **username** of the
connected user. For live channel connections a new User column appears
between IP Address and Connected; for VOD connections the username is
shown inline next to the IP address in the Client summary row. The
username is resolved from the user store using the `user_id` stored in
Redis client metadata. (Closes
[#&#8203;766](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/766),
Closes
[#&#8203;586](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/586))
- `ip_address` and `user_id` were not included in the client info
returned by `get_detailed_channel_info()` despite being available in the
Redis hash. Both fields are now extracted and returned. `user_id` is now
also included in the VOD stats response.
- Web UI stream preview now sends an `Authorization: Bearer` header with
each mpegts.js request, identifying the logged-in user. Live channel
previews initiated from the web UI now appear on the Stats page with the
correct username rather than as unknown user.
- `client_connect` and `client_disconnect` system events now include the
**username** of the connected user. The username is stored alongside the
client metadata in Redis and included in the event payload for
`log_system_event` calls (making it available to webhook and script
integrations).
- Donate button added to the sidebar footer. A heart icon links to the
project's Open Collective page, visible in both expanded and collapsed
states. Hovering shows a "Support Dispatcharr" tooltip. The version
string is also now clickable to copy it to the clipboard.
- User stream limits: administrators can now set a maximum number of
concurrent streams per user account. When a user reaches their limit,
the system can automatically terminate an existing stream to free a slot
based on configurable rules. Limit enforcement applies to both live
channels and VOD. (Closes
[#&#8203;544](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/544))
- Each user account has a new **Stream Limit** field (0 = unlimited)
configurable from the user edit form in Settings → Users.
- Global enforcement behaviour is configurable in Settings → User
Limits:
- **Terminate on Limit Exceeded**: automatically stop an existing stream
when the user's limit is reached (vs. rejecting the new connection).
- **Terminate Oldest**: prefer terminating the oldest stream when
freeing a slot; disable to prefer the newest.
- **Prioritize Single-Client Channels**: prefer terminating streams on
channels that only this user is watching.
- **Ignore Same-Channel Connections**: count multiple connections to the
same live channel as one stream toward the limit. Same-channel
reconnects are always allowed through. When this is enabled and a
channel must be freed, all connections to the chosen channel are
terminated together so that the unique-channel count actually decreases.
VOD is explicitly excluded from this bypass since VOD connections are
not shared upstream.
- TLS and mutual TLS (mTLS) support for Redis and PostgreSQL connections
in modular deployments. Supports encrypted connections, server
certificate verification (Redis: on/off; PostgreSQL: verify-full,
verify-ca, require), CA certificate configuration, and client
certificate authentication. Configured via environment variables in the
docker compose file. Includes startup validation for certificate paths
and TLS/URL scheme conflicts, and a read-only Connection Security panel
in System Settings. (Closes
[#&#8203;950](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/950))
— Thanks [@&#8203;CodeBormen](https://redirect.github.com/CodeBormen)
- Status filter for M3U group and VOD category filter modals: A new
**All / Enabled / Disabled** segmented control is now shown alongside
the text search input in the Live, VOD - Movies, and VOD - Series tabs
of the M3U Group Filter modal. The status filter works in combination
with the text search and also scopes the "Select Visible" / "Deselect
Visible" buttons so they only act on the currently visible subset.
(Closes
[#&#8203;312](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/312))

##### Changed

- M3U Profile form (XC accounts): added a **Simple / Advanced** mode
toggle for credential-based URL rewriting. In Simple mode users enter
just a new username and password; the search and replace patterns are
built automatically from the account's current credentials. In Advanced
mode the full regex fields are shown as before. The selected mode is
saved to `custom_properties.xcMode` and auto-detected on existing
profiles (a profile whose search pattern matches the account's current
`username/password` is recognised as Simple automatically). The Live
Regex Demonstration panel is hidden in Simple mode.
- XtreamCodes VOD endpoints (`/movie/` and `/series/`) no longer
redirect clients to a UUID-based proxy URL. Requests are now handled
directly in the proxy layer via `stream_xc_movie` and
`stream_xc_episode`, which call `stream_vod()` internally. The original
XC path is preserved for the client throughout the stream.
- `CustomTable` column layout now supports flexible (`grow`) columns
alongside fixed-width ones:
- Column definitions accept a `grow` property (boolean or number) to opt
into flex layout. A numeric value sets the flex-grow weight, allowing
relative sizing between grow columns (e.g. `grow: 2` gives a column
twice the share of spare space as `grow: 1`).
- `maxSize` is now respected on grow columns, capping how wide they
expand via `maxWidth`.
- The wrapper's `minWidth` calculation now uses `minSize` (not
TanStack's 150px default) for grow columns, preventing the table from
overflowing its container when columns would otherwise be sized larger
than available space.
- Dependency updates:
  - `requests` 2.32.5 → 2.33.0 (security patch; see Security section)
  - `celery` 5.6.2 → 5.6.3
  - `torch` 2.10.0+cpu → 2.11.0+cpu
  - `sentence-transformers` 5.2.3 → 5.3.0
  - `yt-dlp` 2026.3.13 → 2026.3.17
- Docker base image cleanup: removed `python-is-python3`, `python3-pip`,
and `streamlink` from the apt package list in `DispatcharrBase`.
`python3-pip` and `streamlink` were pulling outdated system Python
packages (e.g. `requests 2.31.0`, `cryptography 41.0.7`, `lxml 5.2.1`)
into the system Python's site-packages despite the app running entirely
in the uv-managed venv at `/dispatcharrpy`. `streamlink` is already
installed in the venv via `pyproject.toml`. `python-is-python3` is
unnecessary as `PATH` resolves bare `python` to the venv binary.
- M3U table **Max Streams** column now reflects the combined limit
across all active profiles. When a playlist has multiple active
profiles, the column displays their summed total (or ∞ if any profile is
unlimited) and a hover tooltip lists each profile's individual limit by
name. (Closes
[#&#8203;816](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/816))
- Toggling an M3U profile's active state now immediately updates the
playlist store (including the `playlists` array), so the **Max Streams**
total in the M3U table reflects the change without a page reload.
- M3U account form: **Max Streams** field changed from a plain text
input to a number input with increment/decrement controls, consistent
with other integer fields.
- M3U account form: removed unused `useMantineTheme` import and `theme`
variable.
- Moved `guideUtils.js` from `frontend/src/pages/` to
`frontend/src/utils/` to be consistent with other utility modules (e.g.
`networkUtils.js`). Updated all imports across `GuideRow.jsx`,
`HourTimeline.jsx`, `ProgramDetailModal.jsx`, `RecordingCardUtils.js`,
`Guide.jsx`, and related test files.
- Frontend cleanup: removed unused imports from `M3UGroupFilter`,
`LiveGroupFilter`, and `VODCategoryFilter` (`Yup`, `M3UProfiles`,
several unused Mantine components, dead `OptionWithTooltip` component,
duplicate lucide-react imports, and `Divider` in `VODCategoryFilter`).
No behaviour changes.
- Network Access settings: leaving a field blank no longer shows a
validation error. The default CIDR range for that field is saved
automatically and a "Defaults Restored" warning is displayed listing
which fields were reset. (Closes
[#&#8203;726](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/726))

##### Fixed

- M3U profile URL rewriting now uses the `regex` module instead of `re`
across all URL transform code paths (`url_utils.transform_url`,
`core/views.py`, `vod_proxy/_transform_url`,
`tasks.get_transformed_credentials`, and the WebSocket live-preview
handler in `consumers.py`). The `regex` module natively accepts
JavaScript/PCRE-style named capture groups (`(?<name>...)`) without any
conversion, eliminating the root cause of patterns that matched in the
frontend live preview but failed on the backend with a `re.error`. As a
further improvement, `regex` also supports variable-length lookbehind
assertions (e.g. `(?<=a+)`), which `re` rejects with an error; patterns
using these will now work correctly on the backend as well.
Replace-pattern JS tokens are still normalised before calling
`regex.sub`: `$<name>` → `\g<name>` and `$1`/`$2`/… → `\1`/`\2`/…
(Python replacement syntax). Also fixed a bug in the WebSocket preview
handler where a pattern error was incorrectly returning the search
pattern string as the preview output instead of the original URL. (Fixes
[#&#8203;1005](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/1005))
- Web UI stream preview (`FloatingVideo`) was calling
`mpegts.createPlayer()` with all `Config` options (e.g. `enableWorker`,
`liveSync`, `headers`) merged into the first `MediaDataSource` argument.
mpegts.js only reads `Config` from the optional second argument;
unrecognised fields in the first are silently ignored. As a result all
player configuration was effectively the library defaults — worker
offloading was disabled, latency management had no effect, and the
`Authorization: Bearer` header (required for user identification) was
never sent. Fixed by splitting into the correct two-argument call. Both
`liveBufferLatencyChasing` and `liveSync` have been disabled,
eliminating playback-rate fluctuations that caused audible stuttering on
live streams. SourceBuffer cleanup thresholds were also relaxed from
10s/5s to 120s/60s to prevent frequent SourceBuffer pauses.
- HTML named entities in XMLTV EPG files are now correctly preserved
during lxml parsing. Some EPG providers (particularly French and other
European sources) use HTML named entities like `&eacute;`, `&icirc;`,
`&uuml;` in channel names, program titles, and metadata. These are not
valid XML entities — lxml 6.0.2 with `recover=True` silently drops them,
causing characters to go missing (e.g., "Chaîne Télé" becomes "Chane
Tl"). This is now fixed by injecting an XML `<!DOCTYPE tv [...]>`
internal subset declaring all 252 HTML 4 named entities directly into
the byte stream that lxml reads, using a lightweight in-memory wrapper
(`_PrependStream`) with zero disk I/O. libxml2 resolves the entities
during its normal C-level parse pass — no Python-level preprocessing or
temporary files are involved. The DOCTYPE block (\~8 KB) is built once
at module load from Python's stdlib `html.entities.name2codepoint` and
reused for every parse. Files that already declare their own
`<!DOCTYPE>` are passed through unchanged. (Closes
[#&#8203;1095](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/1095))
— Thanks [@&#8203;CodeBormen](https://redirect.github.com/CodeBormen)
for helping with this!
- Duplicate recordings created when EPG sources refresh and re-evaluate
series rules (Fixes
[#&#8203;940](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/940))
— Thanks [@&#8203;CodeBormen](https://redirect.github.com/CodeBormen):
- **Program ID instability**: `parse_programs_for_source()` deletes and
recreates all `ProgramData` rows with new auto-increment IDs on every
EPG refresh. The dedup set used these IDs, so it never matched after a
refresh. Deduplication now uses a stable `(tvg_id, start_time,
end_time)` composite key sourced from
`Recording.custom_properties.program`.
- **Secondary guard using wrong times**: The DB guard compared
unadjusted program times against offset-adjusted
`Recording.start_time`/`end_time`, so it never matched when any DVR
pre/post offset was configured. It now queries
`custom_properties__program__start_time/end_time` (the original,
unadjusted program times stored at recording creation).
- **No concurrency guard**: Each EPG source refresh fired
`evaluate_series_rules.delay()` independently. Concurrent tasks loaded
the dedup set before others committed, allowing races. Evaluation is now
serialized with `acquire_task_lock` (reusing the existing EPG task
pattern). Gracefully degrades if Redis is unavailable — the primary and
secondary dedup guards still protect.
- EPG refresh tasks (`refresh_epg_data`) were being killed
mid-transaction on large EPG sources. The `soft_time_limit=1700s`
introduced in v0.21.0 raised `SoftTimeLimitExceeded`, a subclass of
`Exception`, which was swallowed by the existing `except Exception`
handler in `parse_programs_for_source`, leaving the database in a
partial state with no logged error. `soft_time_limit` has been removed
from `refresh_epg_data` and `time_limit` raised to 14400s (4 hours) as a
true last-resort ceiling; the existing `TaskLockRenewer` daemon thread
continues to renew the Redis lock every 120s for legitimately
long-running tasks.

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Renovate
Bot](https://redirect.github.com/renovatebot/renovate).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yOS4yIiwidXBkYXRlZEluVmVyIjoiNDMuMjkuMiIsInRhcmdldEJyYW5jaCI6Im1hc3RlciIsImxhYmVscyI6WyJhcHAvZGlzcGF0Y2hhcnIiLCJhdXRvbWVyZ2UiLCJyZW5vdmF0ZS9jb250YWluZXIiLCJ0eXBlL21pbm9yIl19-->
2026-04-01 05:54:35 +02:00
TrueCharts Bot d2925c98de BREAKING CHANGE(helm-deps): Update chart common 28.33.4 → 29.0.0 (#46224)
This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
|
[common](https://trueforge.org/truetech/truecharts/charts/library/common)
([source](https://ghcr.io/cloudnative-pg/postgis)) | major | `28.33.4` →
`29.0.0` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the [Dependency
Dashboard](../issues/18710) for more information.

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Renovate
Bot](https://redirect.github.com/renovatebot/renovate).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yOS4yIiwidXBkYXRlZEluVmVyIjoiNDMuMjkuMiIsInRhcmdldEJyYW5jaCI6Im1hc3RlciIsImxhYmVscyI6WyJhcHAvendhdmVqczJtcXR0IiwicmVub3ZhdGUvY29udGFpbmVyIiwidHlwZS9tYWpvciJdfQ==-->

Signed-off-by: Alfred Göppel <43101280+alfi0812@users.noreply.github.com>
Co-authored-by: Alfred Göppel <43101280+alfi0812@users.noreply.github.com>
2026-03-19 17:38:03 +01:00
TrueCharts Bot 29f02f9831 feat(dispatcharr): update image docker.io/dispatcharr/dispatcharr 0.20.2 → 0.21.1 (#46164)
This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
|
[docker.io/dispatcharr/dispatcharr](https://redirect.github.com/Dispatcharr/Dispatcharr)
| minor | `116f7f4` → `3fe4da0` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the [Dependency
Dashboard](../issues/18710) for more information.

Add the preset `:preserveSemverRanges` to your config if you don't want
to pin your dependencies.

---

### Release Notes

<details>
<summary>Dispatcharr/Dispatcharr
(docker.io/dispatcharr/dispatcharr)</summary>

###
[`v0.21.1`](https://redirect.github.com/Dispatcharr/Dispatcharr/blob/HEAD/CHANGELOG.md#0211---2026-03-18)

[Compare
Source](https://redirect.github.com/Dispatcharr/Dispatcharr/compare/v0.21.0...v0.21.1)

##### Fixed

- Docker container initialization fixes for PUID/PGID handling — Thanks
[@&#8203;CodeBormen](https://redirect.github.com/CodeBormen):
- Backups failing on previous installations where `/data/backups`
already existed: `/data/backups` was missing from the `DATA_DIRS` list
in the init script, causing the PUID/PGID ownership migration to skip
the directory and leave it with incorrect permissions.
- Container startup failure on upgrade when data directories reside on
external mounts (NFS, SMB/CIFS, FUSE): `chown` failures under `set -e`
were crashing the container, breaking setups that worked fine on the
previous image. Failures are now collected per-directory and reported as
a consolidated warning; the container continues to start and Django
reports at runtime if it cannot write a specific directory.
- Upgrading users running as UID 102 (the internal PostgreSQL system
user) instead of the expected UID 1000: the PUID/PGID auto-detect
introduced in v0.21.0 read ownership from `/data/db`, which was UID 102
in pre-PUID images, causing Django, file creation, and comskip to all
run as the wrong user. PUID/PGID now default to 1000 (matching the
original Django UID) rather than auto-detecting from data directory
ownership.

###
[`v0.21.0`](https://redirect.github.com/Dispatcharr/Dispatcharr/blob/HEAD/CHANGELOG.md#0210---2026-03-17)

[Compare
Source](https://redirect.github.com/Dispatcharr/Dispatcharr/compare/v0.20.2...v0.21.0)

##### Security

- Updated frontend npm dependencies to resolve 1 high-severity
vulnerability:
- Updated `flatted` to 3.4.1, resolving **high** unbounded recursion DoS
in the `parse()` revive phase
([GHSA-25h7-pfq9-p65f](https://redirect.github.com/advisories/GHSA-25h7-pfq9-p65f))
- Updated `Django` to 6.0.3 and `django-celery-beat` to 2.9.0, resolving
new security vulnerabilities:
- [CVE-2026-25673](https://www.cve.org/CVERecord?id=CVE-2026-25673):
Potential denial-of-service vulnerability in URLField via Unicode
normalization on Windows (March 3, 2026)
- [CVE-2026-25674](https://www.cve.org/CVERecord?id=CVE-2026-25674):
Potential incorrect permissions on newly created file system objects
(March 3, 2026)

##### Added

- Configurable sidebar navigation ordering and visibility — Thanks
[@&#8203;jcasimir](https://redirect.github.com/jcasimir)
- Sidebar nav items can be reordered via drag-and-drop in Settings → UI
Settings → Navigation.
- Individual nav items can be hidden from the sidebar using the eye
toggle. Hiding an item preserves its position in the order.
- A "Reset to Default" button restores the role-appropriate default
order and clears all hidden items.
- Order and visibility are saved per-user with optimistic updates and
automatic rollback on failure. Changes appear in the sidebar immediately
without a page reload.
- Admin users see a grouped navigation: flat items (`Channels`, `VODs`,
`M3U & EPG Manager`, `TV Guide`, `DVR`, `Stats`, `Plugins`) plus
collapsible `Integrations` (Connections, Logs) and `System` (Users, Logo
Manager, Settings) groups. The `System` group cannot be hidden.
- Non-admin users see `Channels`, `TV Guide`, and `Settings`, with the
`Settings` item not hideable.
- Unit tests for `NotificationCenter`, `NotificationCenterUtils`, and
`M3URefreshNotification` components, and for settings form components
`DvrSettingsForm`, `NetworkAccessForm`, `ProxySettingsForm`,
`StreamSettingsForm`, `SystemSettingsForm`, `UiSettingsForm`. — Thanks
[@&#8203;nick4810](https://redirect.github.com/nick4810)
- Unit tests for DVR port resolution (`build_dvr_candidates`) and
selective Redis flush behavior in modular mode. — Thanks
[@&#8203;CodeBormen](https://redirect.github.com/CodeBormen)
- Floating video player improvements
- **Title display**: The channel, stream, or VOD title is now shown in
the player header bar. Title is passed through from all preview entry
points: channel table, stream table, stream connection card, guide, DVR,
recording cards, recording details modal, VOD modal, and series modal.
- **Persistent state**: Size, position, volume level, and mute state are
now saved across sessions using a single `dispatcharr-player-prefs`
localStorage key. Size and position are restored on next open (clamped
to the current viewport); volume and mute are restored when the player
initialises.
- New Client Buffer proxy setting: new clients joining an active channel
are now positioned a configurable number of seconds behind live rather
than a fixed chunk count. The start position is determined by wall-clock
chunk receive time (stored as a Redis sorted set alongside the buffer),
so the buffer depth is consistent in seconds regardless of stream
bitrate. Setting the value to `0` starts clients at live with no buffer.
Defaults to 5 seconds. Existing chunk-count gating for the first client
connecting to a channel is unchanged. The setting is exposed in Settings
→ Proxy as "New Client Buffer (seconds)".
- Channel table filter for channels that have stale streams: A new "Has
Stale Streams" filter option in the channel table header menu highlights
and filters channels containing at least one stale stream. Channels with
stale streams are visually distinguished with an orange tint. The filter
is mutually exclusive with "Only Empty Channels". - Thanks
[@&#8203;JCBird1012](https://redirect.github.com/JCBird1012)
- "Next Highest Channel" numbering mode when creating channels from
streams: A new `Next Highest` option is available alongside `Provider`,
`Auto`, and `Custom` when creating channels from the Streams table.
Selecting it assigns channel numbers starting one above the current
highest channel number; the next available number is fetched from the
backend at selection time. (Closes
[#&#8203;1000](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/1000))
— Thanks [@&#8203;JCBird1012](https://redirect.github.com/JCBird1012)
- TV Guide program cards now display richer metadata — Thanks
[@&#8203;CodeBormen](https://redirect.github.com/CodeBormen)
- **Season/episode badges** (e.g. `S12E06`) extracted from EPG
`<episode-num>` elements, `onscreen` episode strings (e.g. `S12 E6`,
`S3E21`, `S8 E8 P2/2`), and as a last-resort fallback from description
text patterns at parse time (3-tier pipeline). (Closes
[#&#8203;1065](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/1065))
- **Episode subtitle** shown below the program title on guide cards;
falls back to the short description when no subtitle is available.
- **Status badges**: `LIVE`, `NEW`, `PREMIERE`, and `FINALE` surfaced
from EPG flags on both compact cards and the detail modal.
- **Program detail modal**: Clicking any guide program opens a modal
with full program details — poster/icon image, season/episode, subtitle,
duration, categories, cast/director/writer credits, content rating, star
ratings, production date, original air date, video quality, and external
links to IMDb and TMDB where available. Detail data is fetched from a
new `GET /api/epg/programs/{id}/` endpoint backed by the new
`ProgramDetailSerializer`. Dummy/placeholder programs skip the fetch.
- **Real-time progress bars**: Currently-airing programs show a green
progress bar on their guide card that updates every second via direct
DOM manipulation (no React re-renders).
- **Channel name tooltip**: Hovering the channel logo column shows the
channel name.
- Sort icons added to the Group and EPG column headers in the Channels
table, and to the Group column header in the Streams table. Clicking a
sort icon cycles through ascending/descending/unsorted states. EPG
sorting required a backend change (`epg_data__name` added to
`ChannelViewSet.ordering_fields`); Group sorting was already supported
by the API in both tables. (Closes
[#&#8203;854](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/854))
— Thanks [@&#8203;CodeBormen](https://redirect.github.com/CodeBormen)
- DVR enhancements — Thanks
[@&#8203;CodeBormen](https://redirect.github.com/CodeBormen)
- **Stop Recording**: A new Stop button (distinct from Cancel) cleanly
ends an in-progress recording early and keeps the partial file available
for playback. The API returns immediately; stream teardown and task
revocation happen in a background thread to prevent 504 timeouts. When
multiple recordings run simultaneously, stopping one only terminates
that recording's proxy client by ID, leaving all others unaffected.
(Closes
[#&#8203;454](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/454))
- **Extend Recording**: In-progress recordings can be extended by 15,
30, or 60 minutes without interrupting the stream.
- **Inline metadata editing**: Title and description can now be edited
directly in the recording details modal.
- **Refresh artwork button**: Manually re-run poster resolution on
demand from the recording card.
- **Multi-source poster resolution**: Added pipeline querying EPG, VOD,
TMDB, OMDb, TVMaze, and iTunes for richer recording artwork.
- **Series rules for currently-airing episodes**: Series rules now
capture currently-airing episodes in addition to future scheduled ones.
(Closes
[#&#8203;473](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/473))
- **Search and filter controls**: Added search and filter controls to
the recordings list.
- **Stream generator throttling**: Cached the `ProxyServer` singleton
reference per client and throttled Redis resource checks (1 s) and
non-owner health checks (2 s), eliminating 3+ Redis round-trips per
stream loop iteration.
- **Automatic crash recovery on worker restart**: A `worker_ready`
Celery signal now fires `recover_recordings_on_startup` automatically
when the worker starts, so recordings stuck in "recording" status are
recovered without manual intervention.
- Account expiration tracking and notifications for M3U profiles
- A new `exp_date` field on `M3UAccountProfile` stores the account
expiration date as a proper `DateTimeField`. For Xtream Codes accounts
the field is auto-synced from `custom_properties.user_info.exp_date` on
every save (supports both Unix timestamps and ISO date strings). For
non-XC M3U accounts the date can be entered manually via the account or
profile form.
- The M3U accounts table now shows an **Expiration** column displaying
the earliest expiration date across all profiles for that account
(color-coded: red = expired, orange = expiring soon, green = OK).
Hovering the cell shows a tooltip with per-profile expiration details
including inactive-profile labels.
- A daily Celery Beat task (`check_xc_account_expirations`) checks all
active profiles with an expiration date and manages system
notifications: a normal-priority warning is raised for profiles expiring
within 7 days; a high-priority alert is raised once the profile has
already expired. Warning and expired notifications use separate keys so
dismissing the 7-day warning does not suppress the expiration alert.
- Notifications are also updated immediately when a profile is saved: if
the expiration date is cleared or pushed beyond the 7-day window, any
existing warning/expired notifications are deleted; if the date falls
within the window or is already past, the matching notification is
updated in place.
- Non-XC accounts expose a `DateTimePicker` on both the M3U account form
and the profile form.

##### Changed

- Dependency updates:
- `Django` 5.2.11 → 6.0.3 (security patch + major version upgrade; see
Security section)
- `django-celery-beat` ≥2.8.1 → ≥2.9.0 (adds explicit Django 6.0
support)
- When selecting an EPG source for a channel, the EPG source dropdown
now only lists enabled (active) EPGs, sorted alphabetically.
- Channels page default splitter ratio changed from 50/50 to 60/40
(channels/streams) so all channel action buttons are visible without
scrolling on 1080p displays.
- Frontend component refactoring and cleanup — Thanks
[@&#8203;nick4810](https://redirect.github.com/nick4810)
- `FloatingVideo`, `SeriesModal`, `VODModal`, `SystemEvents`,
`M3URefreshNotification`, and `NotificationCenter` significantly reduced
in size by separating business logic into dedicated utility modules
under `utils/components/` (`FloatingVideoUtils.js`,
`SeriesModalUtils.js`, `VODModalUtils.js`,
`NotificationCenterUtils.js`).
- `FloatingVideo` resize handle elements extracted into a standalone
`ResizeHandles` sub-component.
- `YouTubeTrailerModal` extracted into a standalone component
(`components/modals/YouTubeTrailerModal.jsx`).
- `NotificationCenter` and `Sidebar` updated from Mantine dot-notation
sub-components (`Popover.Target`, `Popover.Dropdown`,
`ScrollArea.Autosize`, `AppShell.Navbar`) to Mantine v7 named imports
(`PopoverTarget`, `PopoverDropdown`, `ScrollAreaAutosize`,
`AppShellNavbar`).
- `M3URefreshNotification` now uses the centralized `showNotification()`
utility (from `notificationUtils.js`) instead of calling
`notifications.show()` directly, bringing it in line with the rest of
the app. State updates also converted to functional updater form (`prev
=> ...`) to eliminate potential stale-closure bugs.
- `SystemEvents` now imports `format` from `dateTimeUtils` for
consistent date/time formatting.
- Removed a dead `onLogout` handler in `Sidebar` that called `logout()`
and `window.location.reload()` but was never wired to any UI element.
- EPG output when no `days` parameter is specified now excludes
already-ended programs instead of returning all historical data.

##### Fixed

- Single-stream channel creation modal not opening correctly when
clicking the channel-creation button on an individual stream row in the
Streams table. — Thanks
[@&#8203;JCBird1012](https://redirect.github.com/JCBird1012)
- DVR series rule creation failing with a 500 error when the stored
`series_rules` data contained corrupted (non-dict) entries. Added type
guards on the getter, setter, and generic settings serializer to filter
invalid entries on read and write. Hardened the EPG ignore list getters
(`prefixes`, `suffixes`, `custom`) with the same pattern. Frontend
settings parse and save now validate `series_rules` with
`Array.isArray()`, matching the existing EPG field pattern, preventing
corrupted data from being round-tripped back to the database. (Fixes
[#&#8203;1059](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/1059))
— Thanks [@&#8203;CodeBormen](https://redirect.github.com/CodeBormen)
- TS proxy clients stuck indefinitely in keepalive mode when a stream
fails and never recovers (Fixes
[#&#8203;1102](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/1102),
[#&#8203;1103](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/1103))
— Thanks
[@&#8203;cmcpherson274](https://redirect.github.com/cmcpherson274) &
[@&#8203;CodeBormen](https://redirect.github.com/CodeBormen)
- **Keepalive duration cap**: Non-owner worker clients sending keepalive
packets to hold a connection open during failover can now be held at
most `MAX_KEEPALIVE_DURATION` seconds (default 300 s). If no real stream
data has been received within that window, the client is disconnected
with a warning log. The timer resets each time real data resumes, so
independent stalls do not accumulate.
- **`last_active` tracking**: `last_active` is now updated on every
keepalive packet and on every real data chunk, so clients actively
waiting during a failover are not incorrectly evicted as ghost clients
by the heartbeat thread. The heartbeat thread now only refreshes the
Redis TTL rather than updating `last_active`, ensuring the
ghost-detection check reflects true client activity rather than
heartbeat activity.
- **Buffer reset on stream transition**: A new `reset_buffer_position()`
method on `StreamBuffer` clears the in-memory write buffer and
partial-packet accumulator when switching between FFmpeg processes.
Without this, a partial 188-byte TS packet from the dying FFmpeg process
was being prepended to the first bytes from the new FFmpeg process,
producing a corrupted TS packet boundary that broke audio decoder sync
on the client side. Redis-stored chunks already consumed by clients are
unaffected.
- **`add_chunk()` locking hardened**: The lock scope in `add_chunk()`
was expanded to cover the entire partial-packet merge and write-buffer
accumulation phase, preventing a race condition between `add_chunk()`
and the new `reset_buffer_position()` call.
- uWSGI segfaults caused by mixing threading and gevent concurrency
models. The dev and debug uWSGI configs had `threads` and
`enable-threads = true` set alongside gevent, which triggers
segmentation faults particularly on ARM64/Python 3.13. Removed those
options to match the already-correct production config. — Thanks
[@&#8203;jcasimir](https://redirect.github.com/jcasimir)
- `Stream.last_seen` and `ChannelGroupM3UAccount.last_seen` model
defaults now use `django.utils.timezone.now` instead of
`datetime.datetime.now`, eliminating spurious `RuntimeWarning:
DateTimeField received a naive datetime` warnings emitted during test
database creation and on new record creation when `USE_TZ=True`.
- EPG programme parsing crash when an XMLTV source contains programme
titles exceeding 255 characters. Previously, a single oversized title
would cause the entire `bulk_create` batch to fail with a database
truncation error, silently dropping all programmes in that batch. Titles
are now truncated to 255 characters before being saved. (Fixes
[#&#8203;1039](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/1039))
- Container startup failure when `PUID`/`PGID` is set, caused by
`/data/db` ownership conflicts between the `postgres` system user (UID
102) and the configured PUID/PGID. PostgreSQL now runs as the PUID/PGID
user in AIO mode, eliminating all `chown`-to-UID-102 operations and
unifying `/data` ownership. (Fixes
[#&#8203;1078](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/1078))
— Thanks [@&#8203;CodeBormen](https://redirect.github.com/CodeBormen)
- Existing installations where PUID/PGID differs from the current
`/data/db` owner are migrated automatically on first startup; a sentinel
file prevents redundant recursive `chown` on subsequent boots.
- PUID/PGID auto-detected from existing data ownership when not
explicitly set, avoiding cross-UID `chown` failures on restricted
filesystems (NFS `root_squash`, CIFS).
- PUID/PGID validated as positive non-zero integers before any
user/group operations.
- UID collisions with the `postgres` system user (e.g. PUID=102) are now
handled gracefully.
- Ensured proper variable quoting in the /docker/ directory to guard
from inappropriate input
- Floating video player bug fixes
- **Resize stuck after releasing mouse outside window**: The `mouseup`
event is not delivered when the pointer leaves the viewport, leaving the
`mousemove` listener active indefinitely. Fixed by checking
`event.buttons === 0` at the top of `handleResizeMove`; when no button
is held the resize session is torn down immediately.
- **Drag stuck after releasing mouse outside window**: Same root cause
as the resize bug. Fixed by detecting `event.buttons === 0` in the
`onDrag` handler and dispatching a synthetic `mouseup` event so
react-draggable cleanly ends the drag session.
- **Player draggable off screen**: The player could be dragged off any
edge, making the header (and drag handle) unreachable. The player is now
fully bounded: left and top edges are clamped to `x ≥ 0` / `y ≥ 0` so
the header is always reachable, and right/bottom edges are clamped to
the viewport. Size and position are also re-clamped automatically when
the browser window is resized, with proportional scale-down if the saved
size exceeds the new viewport.
- Double error notification when saving user preferences: `API.updateMe`
was catching errors internally and displaying a notification before
re-throwing, causing callers to display a second notification for the
same failure.
- Navigation preference saves from concurrent sessions could overwrite
each other due to a double-merge race: the frontend was pre-merging
`custom_properties` before sending, then the backend merged again
against the DB value, causing the second session's write to silently
drop keys set by the first. The frontend now sends only the delta; the
backend merges authoritatively against the stored value.
- Stale nav item IDs (e.g. from a previous nav structure) are now
scrubbed from `navOrder` and `hiddenNav` on the next preference save,
preventing unbounded growth of the `custom_properties` JSON field.
- Version update notification persisting after upgrading to the notified
version (e.g. "v0.20.2 available" shown while already running v0.20.2).
Root cause: `check_for_version_update.delay()` was called from
`AppConfig.ready()`, which fires inside Celery prefork pool subprocesses
before the broker connection is established, causing the dispatch to
fail silently with no log output. Fixed by moving the startup dispatch
to the `worker_ready` signal in `celery.py` (consistent with the
existing `recover_recordings_on_startup` pattern), and deleting the
stale `version-{current_version}` notification at the top of the
production check path so it is cleared even when GitHub is unreachable.
A WebSocket update is sent immediately on deletion so the frontend badge
clears without waiting for the API response.
- VOD orphan cleanup crashing with a `ForeignKeyViolation`
(`IntegrityError`) when a concurrent refresh task created a new
`M3UMovieRelation` or `M3USeriesRelation` for a movie/series between the
orphan-detection query and the `DELETE` SQL. Both
`orphaned_movies.delete()` and `orphaned_series.delete()` are now
wrapped in `try/except IntegrityError`; affected records are skipped
with a warning and will be cleaned up on the next scheduled run.
- XC stream refresh crashing with a `null value in column "name"`
database error when a provider returns streams with a null or empty
name. Affected streams are now assigned a generated fallback name in the
format `<account name> - <stream_id>` so the refresh completes
successfully and the stream remains accessible. A warning is logged for
each affected stream.
- 504 Gateway Timeout when saving M3U group settings on slower hardware
(e.g. Synology NAS). Replaced per-row `update_or_create()` loops with
`bulk_create(update_conflicts=True)` wrapped in `transaction.atomic()`
for both `ChannelGroupM3UAccount` and `M3UVODCategoryRelation`, reducing
hundreds of individual DB round-trips to a single query per model.
(Fixes
[#&#8203;745](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/745))
— Thanks [@&#8203;nickgerrer](https://redirect.github.com/nickgerrer)
- Improved frontend table stability during M3U imports: Fixed incorrect
default `state` initialization (`[]` → `{}`) in `CustomTable` to match
TanStack Table v8's expected state object shape. Added
`autoResetPageIndex: false` and `autoResetExpanded: false` to prevent
TanStack Table from issuing internal state resets on data updates.
Memoized `processedData` in `M3UsTable` to avoid redundant sort/filter
recomputation on re-renders. - Thanks
[@&#8203;marcinolek](https://redirect.github.com/marcinolek)
- `debian_install.sh` hardened for non-UTF8 environments (common in
minimal LXC containers) - Thanks
[@&#8203;marcinolek](https://redirect.github.com/marcinolek)
- Added `setup_locales` step that installs the `locales` package,
enables `en_US.UTF-8`, regenerates locales, and exports `LANG`/`LC_ALL`
before any other work runs, preventing PostgreSQL from defaulting to
`SQL_ASCII` encoding.
- PostgreSQL database creation now explicitly passes `-E UTF8` to
`createdb`.
- `PATH` in the Celery worker, Celery Beat, and Daphne systemd service
files extended to include
`/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin`, fixing failures
where background tasks could not locate `ffmpeg` or `ffprobe`.
- `is_adult` field parsing now guards against invalid values (e.g. the
string `"None"`) that providers may send instead of a valid integer,
preventing a `ValueError` crash during M3U/XC stream refresh. A new
`parse_is_adult()` helper wraps the cast in a `try/except`, returning
`False` for anything that cannot be interpreted as `1`. (Fixes
[#&#8203;1061](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/1061))
— Thanks [@&#8203;JCBird1012](https://redirect.github.com/JCBird1012)
- M3U EXTINF attribute parsing for values containing `=` or `==` (e.g.
base64-padded `tvg-logo` URLs, catchup tokens with query strings). The
previous regex used `[^\s]+` for the key pattern, allowing `=` signs
inside a quoted value to be greedily absorbed into the next attribute's
key name, causing that attribute and all subsequent ones on the line to
be silently dropped. Changed to `[^\s=]+` so the key match always stops
at the first `=`. (Fixes
[#&#8203;1055](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/1055))
- Thanks [@&#8203;JCBird1012](https://redirect.github.com/JCBird1012)
- Celery worker memory leak during M3U/XC refresh causing 20–80 MB
growth per cycle with no reclamation (Fixes
[#&#8203;1012](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/1012),
[#&#8203;1053](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/1053))
- Thanks [@&#8203;CodeBormen](https://redirect.github.com/CodeBormen)
- Restructured `refresh_single_m3u_account()` with a `try/finally` that
guarantees `del` of large data structures runs before Celery's
`gc.collect()`, and lock release on all exit paths (success, exception,
early return)
- Re-enabled batch data cleanup in `process_m3u_batch_direct()` (was
commented out)
- Added `CELERY_WORKER_MAX_MEMORY_PER_CHILD = 512 MB` as a safety net
against pymalloc arena fragmentation
- EPG output was filtering programs using `start_time__gte=now` when the
`days` parameter was specified, which caused currently-airing programs
(started before the request time but not yet ended) to be omitted from
the XML output. This produced a gap in clients' guides immediately after
an EPG refresh, lasting until the next program started. Fixed by
changing the filter to `end_time__gte=now` so any program that has not
yet finished is included.
- TS proxy connection slot leaks and TOCTOU races in stream
initialization (Fixes
[#&#8203;947](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/947))
- Thanks [@&#8203;CodeBormen](https://redirect.github.com/CodeBormen)
- **TOCTOU race in slot reservation**: `get_stream()` previously used a
`GET`→check→`INCR` sequence, allowing concurrent requests to both read
the same count below the limit and both reserve a slot, silently
exceeding `max_streams`. Replaced with an atomic `INCR`-first pattern:
increment unconditionally, check the result, roll back with `DECR` if
over capacity. — Thanks
[@&#8203;patchy8736](https://redirect.github.com/patchy8736)
- **Leak on URL generation failure**: `generate_stream_url()` called
`get_stream()` (which `INCR`s the counter) but had no cleanup path if
subsequent DB lookups or URL construction failed. The
post-`get_stream()` block is now wrapped in a `try/except` that calls
`release_stream()` on any error.
- **Leak on retry-loop timeout**: the retry loop in `stream_ts()` called
a bare `get_stream()` on the first failure to classify the error reason.
If a slot was available, this `INCR`'d the counter and set Redis keys
that were never released when the loop timed out. A `release_stream()`
call is now issued before returning 503.
- **Leak on `initialize_channel()` failure**: when
`initialize_channel()` returned `False`, the connection slot allocated
by the preceding `get_stream()` was never released. A
`connection_allocated` flag now tracks whether this request performed
the `INCR` (fresh initialization vs. joining an existing channel), and
`release_stream()` is called guarded by that flag to prevent incorrect
decrements when attaching to an already-running channel.
- **Safety net for unexpected exceptions**: the outer `except` in
`stream_ts()` now checks `connection_allocated` and calls
`release_stream()` as a last-resort cleanup for any unhandled exception
that escapes before the channel is handed off to the stream lifecycle.
- **`release_stream()` now returns `bool`** and adds a metadata-hash
fallback: if the primary `channel_stream` / `stream_profile` Redis keys
have already been cleaned up by the proxy, it recovers `stream_id` and
`profile_id` from the channel's metadata hash and clears those fields
atomically to prevent duplicate `DECR`s on repeated calls. — Thanks
[@&#8203;patchy8736](https://redirect.github.com/patchy8736)
- **`update_stream_profile()` uses a Redis pipeline** for the
old-profile DECR + key update + new-profile INCR sequence, preventing
counter drift if the process crashes between operations.
- **`stream_generator._cleanup()`** now falls back to
`Stream.objects.get()` when the channel UUID resolves to a preview flow
rather than a normal channel, rather than silently skipping the slot
release.
- **VOD `cleanup_persistent_connection()`** fallback DECR is now
conditional: it only decrements the profile counter when the connection
tracking key had already expired by TTL (i.e., `remove_connection()`
would have skipped the DECR), preventing double-decrements when the key
is still present.
- Ghost clients and channels stuck in `INITIALIZING` state in the TS
proxy (Fixes
[#&#8203;695](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/695),
[#&#8203;669](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/669))
— Thanks [@&#8203;CodeBormen](https://redirect.github.com/CodeBormen)
- **`INITIALIZING` added to cleanup grace period monitoring**: channels
stuck in `INITIALIZING` are now surfaced by the cleanup task and torn
down, preventing indefinite hangs when stream startup fails.
- **Orphaned channel cleanup validates client SET entries**: the cleanup
task now cross-checks client SET members against actual metadata hashes;
ghost SET entries are removed and the channel is torn down cleanly when
no real clients remain.
- **Stats page self-heals**: ghost client SET entries are detected and
removed when reading channel stats, preventing stale entries from
inflating the active-client count.
- **`remove_ghost_clients()` extracted to `ClientManager`**:
ghost-detection logic is now a single authoritative helper, callable
with an optional pre-fetched `client_ids` set to eliminate a redundant
Redis `SMEMBERS` round-trip when the caller already holds the set.
- **Ownership TTL fallback**: the error-state writer now triggers via
ownership check *or* state guard fallback, so a channel stuck in a
pre-active state is correctly marked `ERROR` even when the ownership TTL
expired during retries.
- **Missing `SOURCE_BITRATE` / `FFMPEG_BITRATE` metadata constants**
added to `ChannelMetadataField`, preventing `AttributeError` on detailed
channel stats reads.
- TS proxy client stream lag recovery now only bumps clients forward
when their next required chunk has genuinely expired from Redis (TTL),
rather than unconditionally jumping if they fell more than 50 chunks
behind. Clients are repositioned to the oldest available chunk (minimum
data loss) using an atomic server-side Lua binary search, falling back
to near the buffer head if nothing is available.
- TS proxy streams dying after 30–200 seconds in multi-worker
uWSGI/Celery deployments, caused by three interrelated bugs. (Fixes
[#&#8203;992](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/992),
[#&#8203;980](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/980))
- Thanks [@&#8203;PFalko](https://redirect.github.com/PFalko)
- **Double ProxyServer instantiation**: `ProxyConfig.ready()` called
`TSProxyServer()` directly while `TSProxyConfig.ready()` also called
`TSProxyServer.get_instance()`, creating two instances per worker — each
with its own cleanup thread. The orphaned thread could not extend
ownership because it had no entries in `stream_managers`. Fixed by using
`TSProxyServer.get_instance()` in `ProxyConfig.ready()`.
- **`flushdb()` on every Redis client init**: `RedisClient.get_client()`
called `client.flushdb()` whenever `_client` was `None`. Celery
autoscale (`--autoscale=6,1`) spawning new workers mid-stream triggered
this path, nuking all Redis keys including active ownership keys, client
records, and channel metadata. Removed the `flushdb()` call entirely.
- **No recovery from expired ownership**: `get_channel_owner()` called
`redis.get()` twice inside a lambda (TOCTOU race — key could expire
between calls); `extend_ownership()` silently returned `False` on expiry
with no re-acquisition; and the non-owner cleanup path unconditionally
killed streams even when the worker held the `stream_manager`. Fixed
with a single `GET` in `get_channel_owner()`, re-acquisition via atomic
`SET NX EX` in `extend_ownership()`, and a re-acquisition attempt with
client-aware cleanup deferral in the cleanup thread.
- `get_instance()` deadlock: if `ProxyServer()` raised an exception
during singleton construction, `_instance` was left permanently as the
`_INITIALIZING` sentinel, causing all subsequent `get_instance()`
callers to spin in an infinite `gevent.sleep()` loop. Construction is
now wrapped in `try/except`; on failure `_instance` resets to `None` so
the next call can retry.
- Non-atomic ownership acquisition in `try_acquire_ownership()`:
replaced the separate `setnx()` + `expire()` calls with a single atomic
`SET NX EX`, eliminating the race window where a process crash between
the two calls could leave an ownership key with no TTL (permanent
ownership lock).
- DVR bug fixes — Thanks
[@&#8203;CodeBormen](https://redirect.github.com/CodeBormen)
- **Duplicate recording execution**:
`run_recording.apply_async(countdown=...)` exceeded Redis' default
`visibility_timeout` (3600 s) for recordings scheduled more than one
hour out, causing Redis to redeliver the task to multiple workers
simultaneously and producing corrupted output files. Replaced
`apply_async` with `ClockedSchedule` + `PeriodicTask` for
database-backed one-shot scheduling that survives restarts and upgrades
without the redelivery race. `run_recording` also now exits immediately
if the recording is already in progress, completed, or stopped.
`revoke_task()` cleans up both the `PeriodicTask` and its orphaned
`ClockedSchedule` on execution. (Fixes
[#&#8203;940](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/940),
[#&#8203;641](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/641))
- **Stream reconnection resilience**: Recordings now survive transient
network drops with automatic reconnection retrying up to 5 times and
appending to the existing file. DB operations use exponential-backoff
retry for transient database errors throughout the recording lifecycle.
- **Crash recovery pipeline**: On worker restart, recordings stuck in
"recording" status have their segments concatenated and remuxed. Remux
sanity checks reject MKV output that is less than 50% the size of a
previous MKV (duplicate-task overwrite) or less than 10% of the source
TS (corrupt first attempt); the source `.ts` is preserved for manual
recovery on all failure paths. (Fixes
[#&#8203;619](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/619),
[#&#8203;624](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/624))
- **Output file collision**: Fixed collision when multiple tasks
targeted the same filename.
- **WebSocket deadlock**: `send_websocket_update()` was deadlocking the
gevent event loop, causing one recording's WebSocket events to block all
other simultaneous recordings.
- **DVR client isolation**: Stop and Cancel operations now identify the
target client by recording ID (via `User-Agent:
Dispatcharr-DVR/recording-{id}`), ensuring only the correct proxy client
is torn down and never affecting other recordings on the same channel.
- **Accidental stream termination on delete**: `destroy()` now only
calls `_stop_dvr_clients()` for in-progress recordings, preventing
stream termination when deleting a completed recording.
- **Recording card logos**: Logos were not displaying due to a channel
summary API shape mismatch.
- **Logo fetch negative cache**: Added negative cache for failed remote
logo fetches so dead CDNs no longer block Daphne workers on repeated
requests.
- **Artwork fuzzy-match sanitisation**: Poster artwork fuzzy-matching
against external APIs (TMDB, OMDb, etc.) was producing incorrect results
for channels with names like "USA A\&E SD\*"; channel-name strings are
now sanitised before querying external sources.
- **Series modal "No upcoming episodes"**: Fixed due to a missing
`_group_count` merge and an incorrect time filter.
- **Series rule cleanup**: Deleting a series rule left orphaned
recordings and stale Guide indicators; rule deletion now cleans up all
associated recordings. Orphaned recordings with no parent rule are also
cleaned up automatically. (Fixes
[#&#8203;1041](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/1041))
- **Series rule timezone calculation**: Recurring rules silently dropped
scheduled recordings for users in UTC-negative timezones after 4 pm
local time. (Fixes
[#&#8203;1042](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/1042))
- **Recording modal TDZ crash**: Modal crashed on load in production
bundles due to a Temporal Dead Zone error — editing state was referenced
before its declaration in the minified bundle.
- **Description textarea focus loss**: The description textarea lost
focus immediately when opened because the inline editing component was
remounting on every render.
- **WebSocket-driven refresh**: Replaced all manual `fetchRecordings()`
polling calls with debounced WebSocket-driven refresh so the recordings
list stays up to date without redundant API requests.
- **comskip exit code handling**: comskip treated exit code 1 ("no
commercials found") as a fatal error, causing post-processing to fail on
clean recordings. Exit code 1 is now recognised as a successful no-op.
- **Differentiated WebSocket notification events**: `recording_stopped`,
`recording_cancelled` (in-progress cancel), and `recording_deleted` with
a `was_in_progress` flag now allow the frontend to display distinct
"Recording stopped", "Recording cancelled", and "Recording deleted"
toasts.
- **Duplicate series rule evaluation race**: Creating a series rule
fired `evaluate_series_rules.delay()` in the API view while the frontend
immediately called the synchronous evaluate endpoint, racing to create
duplicate recordings for the same program. Removed the redundant async
call from the API; the frontend's explicit evaluate call is now the sole
evaluation path.
- **Recording card S/E badge overlap**: Season/episode badges were
overlapping and metadata was hidden on the recording card.
- **Orphaned recording fallback in series modal**: When a series rule no
longer exists, the recurring rule modal now shows a "Delete Recording"
button for the orphaned recording instead of failing silently.
- Modular mode deployment hardening — Thanks
[@&#8203;CodeBormen](https://redirect.github.com/CodeBormen)
- **Postgres version check with restricted DB users**: The version check
was connecting to the hardcoded `postgres` database, which fails when
the configured user lacks access to it. Changed to use `$POSTGRES_DB` so
the check works with least-privilege database users. (Fixes
[#&#8203;1045](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/1045))
- **DVR recording broken in modular mode**: Internal TS stream URL
candidates hardcoded port `9191`, so recordings failed when
`DISPATCHARR_PORT` was set to any other value. URL construction now
reads `DISPATCHARR_PORT` from the environment via the new
`build_dvr_candidates()` helper. `DISPATCHARR_PORT` is also now
explicitly passed to the Celery container in `docker-compose.yml`.
- **Selective Redis flush in modular mode**: `wait_for_redis.py` now
performs a targeted flush in modular mode — clearing stale stream locks,
proxy metadata, and server-state keys — while preserving Celery broker
and result-backend keys. Previously either a full `flushdb()` (which
wiped Celery queues) or no flush at all was performed.
- **Redis wait stripping environment variables**: The modular-mode Redis
readiness check ran as a uWSGI `exec-pre` hook, which executes under `su
-` and strips Docker environment variables, making `DISPATCHARR_ENV` and
`REDIS_HOST` unavailable. Moved to the container entrypoint so all env
vars are present.
- **Stale environment variables after container restart**:
`/etc/profile.d/dispatcharr.sh` was only written on the first container
run; restarts with changed env vars (e.g. a rotated `POSTGRES_PASSWORD`)
retained stale values. The file is now truncated and rewritten on every
startup. `/etc/environment` entries are likewise updated rather than
skipped when a key already exists. All exported values are now quoted to
prevent breakage from special characters.
- **Celery entrypoint startup timeouts**: The JWT key wait and migration
wait loops had no timeout, leaving the Celery worker hanging
indefinitely if the web container was stuck. Each loop now times out
(120 s for JWT, 300 s for migrations) and exits with a clear diagnostic
message. The migration readiness check is also replaced from a fragile
`showmigrations | grep` pattern to `migrate --check`, which exits
cleanly on both unapplied migrations and connection errors.
- **Service startup ordering**: `depends_on` entries for `db` and
`redis` in `docker-compose.yml` upgraded from plain name-link ordering
to `condition: service_healthy`, ensuring containers wait for actual
readiness signals before starting.
- **`host.docker.internal` resolution on Linux**: Added `extra_hosts:
host.docker.internal:host-gateway` to the web service in
`docker-compose.yml` so Linux hosts resolve `host.docker.internal` the
same way Docker Desktop does on macOS/Windows.

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Renovate
Bot](https://redirect.github.com/renovatebot/renovate).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yOS4yIiwidXBkYXRlZEluVmVyIjoiNDMuMjkuMiIsInRhcmdldEJyYW5jaCI6Im1hc3RlciIsImxhYmVscyI6WyJhcHAvZGlzcGF0Y2hhcnIiLCJhdXRvbWVyZ2UiLCJyZW5vdmF0ZS9jb250YWluZXIiLCJ0eXBlL21pbm9yIl19-->
2026-03-18 04:39:40 +01:00
TrueCharts-Bot 6c96fb9c0f Commit daily changes
Signed-off-by: TrueCharts-Bot <bot@truecharts.org>
2026-03-18 00:20:43 +00:00
Alfred Göppel ec199707c1 fix(charts): fix chart.yaml annotations for all charts (#46134)
**Description**
<!--
Please include a summary of the change and which issue is fixed. Please
also include relevant motivation and context. List any dependencies that
are required for this change.
-->
⚒️ Fixes  # <!--(issue)-->

**⚙️ Type of change**

- [ ] ⚙️ Feature/App addition
- [ ] 🪛 Bugfix
- [ ] ⚠️ Breaking change (fix or feature that would cause existing
functionality to not work as expected)
- [x] 🔃 Refactor of current code
- [ ] 📜 Documentation Changes

**🧪 How Has This Been Tested?**
<!--
Please describe the tests that you ran to verify your changes. Provide
instructions so we can reproduce. Please also list any relevant details
for your test configuration
-->

**📃 Notes:**
<!-- Please enter any other relevant information here -->

**✔️ Checklist:**

- [x] ⚖️ My code follows the style guidelines of this project
- [x] 👀 I have performed a self-review of my own code
- [ ] #️⃣ I have commented my code, particularly in hard-to-understand
areas
- [ ] 📄 I have made changes to the documentation
- [ ] 🧪 I have added tests to this description that prove my fix is
effective or that my feature works
- [ ] ⬆️ I increased versions for any altered app according to semantic
versioning
- [x] I made sure the title starts with `feat(chart-name):`,
`fix(chart-name):`, `chore(chart-name):`, `docs(chart-name):` or
`fix(docs):`

** App addition**

If this PR is an app addition please make sure you have done the
following.

- [ ] 🖼️ I have added an icon in the Chart's root directory called
`icon.png`

---

_Please don't blindly check all the boxes. Read them and only check
those that apply.
Those checkboxes are there for the reviewer to see what is this all
about and
the status of this PR with a quick glance._
2026-03-17 11:40:04 +01:00
TrueCharts Bot 465db91ac6 fix(helm-deps): update chart common 28.33.3 → 28.33.4 (#45987)
This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
|
[common](https://trueforge.org/truetech/truecharts/charts/library/common)
([source](https://ghcr.io/cloudnative-pg/postgis)) | patch | `28.33.3` →
`28.33.4` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the [Dependency
Dashboard](../issues/18710) for more information.

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Renovate
Bot](https://redirect.github.com/renovatebot/renovate).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yOS4yIiwidXBkYXRlZEluVmVyIjoiNDMuMjkuMiIsInRhcmdldEJyYW5jaCI6Im1hc3RlciIsImxhYmVscyI6WyJhcHAvendhdmVqczJtcXR0IiwiYXV0b21lcmdlIiwicmVub3ZhdGUvY29udGFpbmVyIiwidHlwZS9wYXRjaCJdfQ==-->
2026-03-12 21:15:56 +01:00
TrueCharts Bot 77276072da fix(helm-deps): update chart common 28.33.0 → 28.33.3 (#45974)
This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
|
[common](https://trueforge.org/truetech/truecharts/charts/library/common)
([source](https://ghcr.io/cloudnative-pg/postgis)) | patch | `28.33.0` →
`28.33.3` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the [Dependency
Dashboard](../issues/18710) for more information.

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Renovate
Bot](https://redirect.github.com/renovatebot/renovate).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yOS4yIiwidXBkYXRlZEluVmVyIjoiNDMuMjkuMiIsInRhcmdldEJyYW5jaCI6Im1hc3RlciIsImxhYmVscyI6WyJhcHAvendhdmVqczJtcXR0IiwiYXV0b21lcmdlIiwicmVub3ZhdGUvY29udGFpbmVyIiwidHlwZS9wYXRjaCJdfQ==-->

Signed-off-by: Alfred Göppel <43101280+alfi0812@users.noreply.github.com>
Co-authored-by: Alfred Göppel <43101280+alfi0812@users.noreply.github.com>
2026-03-12 15:06:57 +01:00
TrueCharts-Bot 35a06650d9 Commit daily changes
Signed-off-by: TrueCharts-Bot <bot@truecharts.org>
2026-03-06 00:23:07 +00:00
TrueCharts Bot 5e7adcd624 fix(dispatcharr): update image docker.io/dispatcharr/dispatcharr 0.20.1 → 0.20.2 (#45753)
This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
|
[docker.io/dispatcharr/dispatcharr](https://redirect.github.com/Dispatcharr/Dispatcharr)
| patch | `db8a9d3` → `116f7f4` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the [Dependency
Dashboard](../issues/18710) for more information.

Add the preset `:preserveSemverRanges` to your config if you don't want
to pin your dependencies.

---

### Release Notes

<details>
<summary>Dispatcharr/Dispatcharr
(docker.io/dispatcharr/dispatcharr)</summary>

###
[`v0.20.2`](https://redirect.github.com/Dispatcharr/Dispatcharr/blob/HEAD/CHANGELOG.md#0202---2026-03-03)

[Compare
Source](https://redirect.github.com/Dispatcharr/Dispatcharr/compare/v0.20.1...v0.20.2)

##### Security

- Updated frontend npm dependencies to resolve 2 high-severity
vulnerabilities:
- Updated `minimatch` to ≥10.2.3, resolving **high** ReDoS via
matchOne() combinatorial backtracking with multiple non-adjacent
GLOBSTAR segments
([GHSA-7r86-cg39-jmmj](https://redirect.github.com/advisories/GHSA-7r86-cg39-jmmj))
- Updated `rollup` to ≥4.58.1, resolving **high** Arbitrary File Write
via Path Traversal
([GHSA-mw96-cpmx-2vgc](https://redirect.github.com/advisories/GHSA-mw96-cpmx-2vgc))

##### Fixed

- EPG filter regression in channel table (introduced in 0.20.0 channel
store refactor): The EPG filter dropdown was showing all EPG sources
regardless of whether they had any channels assigned, and the "No EPG"
option was never displayed. Fixed by annotating EPGSource records with a
`has_channels` flag (via a lightweight `EXISTS` subquery) so only active
EPG sources with at least one channel assigned appear as filter options.
"No EPG" now appears only when at least one channel globally has no EPG
assigned; this is determined by a second `EXISTS` query embedded
directly in the paginated channel response
(`has_unassigned_epg_channels`), avoiding any additional network
requests.
- Stale stream rows missing hover effect: Stale streams in the streams
table had no hover color change, unlike channels with no streams
assigned. Converted the inline `backgroundColor` style to a CSS class
(`stale-stream-row`) so the `:hover` rule can apply correctly. Applied
the same fix to the channel-streams sub-table, where the teal
expanded-row background caused the semi-transparent red tint to visually
mismatch; the sub-table now uses a pre-blended solid color via
`color-mix()` to match the appearance of stale rows in the main streams
table.
- Channel table onboarding shown when filter returns zero results: The
channel store refactor changed to loading only channel IDs instead of
full channel objects, leaving `Object.keys(channels).length` always `0`
and incorrectly triggering the onboarding state on any empty filter.
Fixed by checking `channelIds.length` instead.
- TV Guide scrolls to position 0 when a filter yields no results:
Applying any filter that temporarily empties the channel list (e.g.
switching directly between two channel groups, or typing a search query
that matches nothing) caused the guide to show a blank/empty view with
no programs visible. The `VariableSizeList` unmounts when
`filteredChannels` becomes empty, destroying its DOM node and resetting
`scrollLeft` to 0. On remount the scroll position was never restored
because `initialScrollComplete` was still `true`. Fixed by saving the
user's current scroll position when the channel list empties
mid-transition, then restoring it once new channels have loaded. On
first load the guide still scrolls to the current time as before.
- `debian_install.sh` regressions after `uv` migration on clean/minimal
Debian installs: fixed pip-less venv (`ensurepip`), missing `gunicorn`
for the systemd unit, and inconsistent `DJANGO_SECRET_KEY` availability
(now persisted to `.env` via `EnvironmentFile`). Docker unaffected. -
Thanks [@&#8203;marcinolek](https://redirect.github.com/marcinolek)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Renovate
Bot](https://redirect.github.com/renovatebot/renovate).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yOS4yIiwidXBkYXRlZEluVmVyIjoiNDMuMjkuMiIsInRhcmdldEJyYW5jaCI6Im1hc3RlciIsImxhYmVscyI6WyJhcHAvZGlzcGF0Y2hhcnIiLCJhdXRvbWVyZ2UiLCJyZW5vdmF0ZS9jb250YWluZXIiLCJ0eXBlL3BhdGNoIl19-->
2026-03-03 05:21:52 +01:00
TrueCharts Bot c685ddd8cf feat(helm-deps): update chart common 28.32.5 → 28.33.0 (#45682)
This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
|
[common](https://trueforge.org/truetech/truecharts/charts/library/common)
([source](https://ghcr.io/cloudnative-pg/postgis)) | minor | `28.32.5` →
`28.33.0` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the [Dependency
Dashboard](../issues/18710) for more information.

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Renovate
Bot](https://redirect.github.com/renovatebot/renovate).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yOS4yIiwidXBkYXRlZEluVmVyIjoiNDMuMjkuMiIsInRhcmdldEJyYW5jaCI6Im1hc3RlciIsImxhYmVscyI6WyJhcHAvendhdmVqczJtcXR0IiwiYXV0b21lcmdlIiwicmVub3ZhdGUvY29udGFpbmVyIiwidHlwZS9taW5vciJdfQ==-->

Signed-off-by: Alfred Göppel <43101280+alfi0812@users.noreply.github.com>
Co-authored-by: Alfred Göppel <43101280+alfi0812@users.noreply.github.com>
2026-03-01 11:04:58 +01:00
TrueCharts Bot b5bbdbc7bf fix(dispatcharr): update image docker.io/dispatcharr/dispatcharr 0.20.0 → 0.20.1 (#45599)
This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
|
[docker.io/dispatcharr/dispatcharr](https://redirect.github.com/Dispatcharr/Dispatcharr)
| patch | `f3583e5` → `db8a9d3` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the [Dependency
Dashboard](../issues/18710) for more information.

Add the preset `:preserveSemverRanges` to your config if you don't want
to pin your dependencies.

---

### Release Notes

<details>
<summary>Dispatcharr/Dispatcharr
(docker.io/dispatcharr/dispatcharr)</summary>

###
[`v0.20.1`](https://redirect.github.com/Dispatcharr/Dispatcharr/blob/HEAD/CHANGELOG.md#0201---2026-02-26)

[Compare
Source](https://redirect.github.com/Dispatcharr/Dispatcharr/compare/v0.20.0...v0.20.1)

##### Fixed

- Login form disabled after token expiry: The login button was
permanently rendered as disabled ("Logging you in...") on page load
after a session expired, preventing users from logging back in. A
regression in v0.20.0 caused `LoginForm` to check `if (user)` to detect
an already-authenticated reload, but the Zustand auth store initializes
`user` as a truthy empty object `{ username: '', email: '', user_level:
'' }`, so the loading state was set immediately on every mount. Reverted
to pre-regression behavior. (Fixes
[#&#8203;1029](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/1029))

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Renovate
Bot](https://redirect.github.com/renovatebot/renovate).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yOS4yIiwidXBkYXRlZEluVmVyIjoiNDMuMjkuMiIsInRhcmdldEJyYW5jaCI6Im1hc3RlciIsImxhYmVscyI6WyJhcHAvZGlzcGF0Y2hhcnIiLCJhdXRvbWVyZ2UiLCJyZW5vdmF0ZS9jb250YWluZXIiLCJ0eXBlL3BhdGNoIl19-->
2026-02-27 03:49:55 +01:00
TrueCharts Bot ac53c949e5 feat(dispatcharr): update image docker.io/dispatcharr/dispatcharr 0.19.0 → 0.20.0 (#45593) 2026-02-26 21:42:57 +01:00
TrueCharts Bot 772ec1b1b4 fix(helm-deps): update chart common 28.32.2 → 28.32.5 (#45514)
This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
|
[common](https://trueforge.org/truetech/truecharts/charts/library/common)
([source](https://ghcr.io/cloudnative-pg/postgis)) | patch | `28.32.2` →
`28.32.5` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the [Dependency
Dashboard](../issues/18710) for more information.

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Renovate
Bot](https://redirect.github.com/renovatebot/renovate).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yOS4yIiwidXBkYXRlZEluVmVyIjoiNDMuMjkuMiIsInRhcmdldEJyYW5jaCI6Im1hc3RlciIsImxhYmVscyI6WyJhcHAvendhdmVqczJtcXR0IiwiYXV0b21lcmdlIiwicmVub3ZhdGUvY29udGFpbmVyIiwidHlwZS9wYXRjaCJdfQ==-->

Signed-off-by: Alfred Göppel <43101280+alfi0812@users.noreply.github.com>
Co-authored-by: Alfred Göppel <43101280+alfi0812@users.noreply.github.com>
2026-02-24 21:16:35 +01:00
TrueCharts Bot d828cf193b feat(helm-deps): update chart common 28.31.19 → 28.32.2 (#45462)
This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
|
[common](https://trueforge.org/truetech/truecharts/charts/library/common)
([source](https://ghcr.io/cloudnative-pg/postgis)) | minor | `28.31.19`
→ `28.32.2` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the [Dependency
Dashboard](../issues/18710) for more information.

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Renovate
Bot](https://redirect.github.com/renovatebot/renovate).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yOS4yIiwidXBkYXRlZEluVmVyIjoiNDMuMjkuMiIsInRhcmdldEJyYW5jaCI6Im1hc3RlciIsImxhYmVscyI6WyJhcHAvendhdmVqczJtcXR0IiwiYXV0b21lcmdlIiwicmVub3ZhdGUvY29udGFpbmVyIiwidHlwZS9taW5vciJdfQ==-->

Signed-off-by: Alfred Göppel <43101280+alfi0812@users.noreply.github.com>
Co-authored-by: Alfred Göppel <43101280+alfi0812@users.noreply.github.com>
2026-02-24 05:37:48 +01:00
TrueCharts Bot 71711148d4 feat(helm-deps): update chart common 28.30.2 → 28.31.19 (#45418)
This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
|
[common](https://trueforge.org/truetech/truecharts/charts/library/common)
([source](https://ghcr.io/cloudnative-pg/postgis)) | minor | `28.30.2` →
`28.31.19` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the [Dependency
Dashboard](../issues/18710) for more information.

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Renovate
Bot](https://redirect.github.com/renovatebot/renovate).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yOS4yIiwidXBkYXRlZEluVmVyIjoiNDMuMjkuMiIsInRhcmdldEJyYW5jaCI6Im1hc3RlciIsImxhYmVscyI6WyJhcHAvendhdmVqczJtcXR0IiwiYXV0b21lcmdlIiwicmVub3ZhdGUvY29udGFpbmVyIiwidHlwZS9taW5vciJdfQ==-->

Signed-off-by: Alfred Göppel <43101280+alfi0812@users.noreply.github.com>
Co-authored-by: Alfred Göppel <43101280+alfi0812@users.noreply.github.com>
2026-02-23 05:37:11 +01:00
TrueCharts Bot 02dac97d45 fix(helm-deps): update chart common 28.30.0 → 28.30.2 (#45040)
This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
|
[common](https://trueforge.org/truetech/truecharts/charts/library/common)
([source](https://ghcr.io/cloudnative-pg/postgis)) | patch | `28.30.0` →
`28.30.2` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the Dependency
Dashboard for more information.

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Renovate
Bot](https://redirect.github.com/renovatebot/renovate).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My44LjUiLCJ1cGRhdGVkSW5WZXIiOiI0My44LjUiLCJ0YXJnZXRCcmFuY2giOiJtYXN0ZXIiLCJsYWJlbHMiOlsiYXBwL3p3YXZlanMybXF0dCIsImF1dG9tZXJnZSIsInJlbm92YXRlL2NvbnRhaW5lciIsInR5cGUvcGF0Y2giXX0=-->

Signed-off-by: Alfred Göppel <43101280+alfi0812@users.noreply.github.com>
Co-authored-by: Alfred Göppel <43101280+alfi0812@users.noreply.github.com>
2026-02-16 13:13:23 +01:00
TrueCharts Bot 507412860e feat(helm-deps): update chart common 28.29.59 → 28.30.0 (#45031)
This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
|
[common](https://trueforge.org/truetech/truecharts/charts/library/common)
([source](https://ghcr.io/cloudnative-pg/postgis)) | minor | `28.29.59`
→ `28.30.0` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the Dependency
Dashboard for more information.

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Renovate
Bot](https://redirect.github.com/renovatebot/renovate).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My44LjUiLCJ1cGRhdGVkSW5WZXIiOiI0My44LjUiLCJ0YXJnZXRCcmFuY2giOiJtYXN0ZXIiLCJsYWJlbHMiOlsiYXBwL3p3YXZlanMybXF0dCIsImF1dG9tZXJnZSIsInJlbm92YXRlL2NvbnRhaW5lciIsInR5cGUvbWlub3IiXX0=-->

Signed-off-by: Alfred Göppel <43101280+alfi0812@users.noreply.github.com>
Co-authored-by: Alfred Göppel <43101280+alfi0812@users.noreply.github.com>
2026-02-15 23:04:15 +01:00
TrueCharts-Bot fe6ee7ee60 Commit daily changes
Signed-off-by: TrueCharts-Bot <bot@truecharts.org>
2026-02-15 11:30:36 +00:00
TrueCharts Bot 3d99f5804a fix(helm-deps): update chart common 28.29.49 → 28.29.59 (#44970)
This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
|
[common](https://trueforge.org/truetech/truecharts/charts/library/common)
([source](https://ghcr.io/cloudnative-pg/postgis)) | patch | `28.29.49`
→ `28.29.59` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the Dependency
Dashboard for more information.

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Renovate
Bot](https://redirect.github.com/renovatebot/renovate).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My44LjUiLCJ1cGRhdGVkSW5WZXIiOiI0My44LjUiLCJ0YXJnZXRCcmFuY2giOiJtYXN0ZXIiLCJsYWJlbHMiOlsiYXBwL3p3YXZlanMybXF0dCIsImF1dG9tZXJnZSIsInJlbm92YXRlL2NvbnRhaW5lciIsInR5cGUvcGF0Y2giXX0=-->

Signed-off-by: Kjeld Schouten <info@kjeldschouten.nl>
Co-authored-by: Kjeld Schouten <info@kjeldschouten.nl>
2026-02-15 11:33:44 +01:00
Kjeld Schouten d05c0a6e71 port yaml-language-server logic from common2026 2026-02-14 22:35:03 +01:00
TrueCharts Bot 597c66afa6 feat(dispatcharr): update image docker.io/dispatcharr/dispatcharr 0.18.1 → 0.19.0 (#44756)
This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
|
[docker.io/dispatcharr/dispatcharr](https://redirect.github.com/Dispatcharr/Dispatcharr)
| minor | `425be85` → `8b53fbe` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the Dependency
Dashboard for more information.

Add the preset `:preserveSemverRanges` to your config if you don't want
to pin your dependencies.

---

### Release Notes

<details>
<summary>Dispatcharr/Dispatcharr
(docker.io/dispatcharr/dispatcharr)</summary>

###
[`v0.19.0`](https://redirect.github.com/Dispatcharr/Dispatcharr/blob/HEAD/CHANGELOG.md#0190---2026-02-10)

[Compare
Source](https://redirect.github.com/Dispatcharr/Dispatcharr/compare/v0.18.1...v0.19.0)

##### Added

- Add system notifications and update checks
  -Real-time notifications for system events and alerts
  -Per-user notification management and dismissal
-Update check on startup and every 24 hours to notify users of available
versions
  -Notification center UI component
  -Automatic cleanup of expired notifications
- Network Access "Reset to Defaults" button: Added a "Reset to Defaults"
button to the Network Access settings form, matching the functionality
in Proxy Settings. Users can now quickly restore recommended network
access settings with one click.
- Streams table column visibility toggle: Added column menu to Streams
table header allowing users to show/hide optional columns (TVG-ID,
Stats) based on preference, with optional columns hidden by default for
cleaner default view.
- Streams table TVG-ID column with search filter and sort: Added TVG-ID
column to streams table with search filtering and sort capability for
better stream organization. (Closes
[#&#8203;866](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/866))
- Thanks [@&#8203;CodeBormen](https://redirect.github.com/CodeBormen)
- Frontend now automatically refreshes streams and channels after a
stream rehash completes, ensuring the UI is always up-to-date following
backend merge operations.
- Frontend Unit Tests: Added comprehensive unit tests for React hooks
and Zustand stores, including:
- `useLocalStorage` hook tests with localStorage mocking and error
handling
  - `useSmartLogos` hook tests for logo loading and management
  - `useTablePreferences` hook tests for table settings persistence
  - `useAuthStore` tests for authentication flow and token management
  - `useChannelsStore` tests for channel data management
  - `useUserAgentsStore` tests for user agent CRUD operations
  - `useUsersStore` tests for user management functionality
  - `useVODLogosStore` tests for VOD logo operations
  - `useVideoStore` tests for video player state management
  - `useWarningsStore` tests for warning suppression functionality
- Code refactoring for improved readability and maintainability - Thanks
[@&#8203;nick4810](https://redirect.github.com/nick4810)
- EPG auto-matching: Added advanced options to strip prefixes, suffixes,
and custom text from channel names to assist matching; default matching
behavior and settings remain unchanged (Closes
[#&#8203;771](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/771))
- Thanks [@&#8203;CodeBormen](https://redirect.github.com/CodeBormen)
- Redis authentication support for modular deployments: Added support
for authentication when connecting to external Redis instances using
either password-only authentication (Redis <6) or username + password
authentication (Redis 6+ ACL). REDIS\_PASSWORD and REDIS\_USER
environment variables with URL encoding for special characters. (Closes
[#&#8203;937](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/937))
- Thanks [@&#8203;CodeBormen](https://redirect.github.com/CodeBormen)
- Plugin logos: if a plugin ZIP includes `logo.png`, it is surfaced in
the Plugins UI and shown next to the plugin name.
- Plugin manifests (`plugin.json`) for safe metadata discovery, plus
legacy warnings and folder-name fallbacks when a manifest is missing.
- Plugin stop hooks: Dispatcharr now calls a plugin's optional `stop()`
method (or `run("stop")` action) when disabling, deleting, or reloading
plugins to allow graceful shutdown.
- Plugin action buttons can define `button_label`, `button_variant`, and
`button_color` (e.g., Stop in red), falling back to “Run” for older
plugins.
- Plugin card metadata: plugins can specify `author` and `help_url` in
`plugin.json` to show author and docs link in the UI.
- Plugin cards can now be expanded/collapsed by clicking the header or
chevron to hide settings and actions.

##### Changed

- XtreamCodes Authentication Optimization: Reduced API calls during XC
refresh by 50% by eliminating redundant authentication step. This should
help reduce rate-limiting errors.
- App initialization efficiency: Refactored app initialization to
prevent redundant execution across multiple worker processes. Created
`dispatcharr.app_initialization` utility module with
`should_skip_initialization()` function that prevents custom
initialization tasks (backup scheduler sync, developer notifications
sync) from running during management commands, in worker processes, or
in development servers. This significantly reduces startup overhead in
multi-worker deployments (e.g., uWSGI with 10 workers now syncs the
scheduler once instead of 10 times). Applied to both `CoreConfig` and
`BackupsConfig` apps.
- M3U/EPG Network Access Defaults: Updated default network access
settings for M3U and EPG endpoints to only allow local/private networks
by default (127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16,
::1/128, fc00::/7, fe80::/10). This improves security by preventing
public internet access to these endpoints unless explicitly configured.
Other endpoints (Streams, XC API, UI) remain open by default.
- Modular deployments: Bumped modular Postgres image to 17 and added
compatibility checks (PostgreSQL version and UTF-8 database encoding)
when using external databases to prevent migration/encoding issues.
- Stream Identity Stability: Added `stream_id` (provider stream
identifier) and `stream_chno` (provider channel number) fields to Stream
model. For XC accounts, the stream hash now uses the stable `stream_id`
instead of the URL when hashing, ensuring XC streams maintain their
identity and channel associations even when account credentials or
server URLs change. Supports both XC `num` and M3U
`tvg-chno`/`channel-number` attributes.
- Swagger/OpenAPI Migration: Migrated from `drf-yasg` (OpenAPI 2.0) to
`drf-spectacular` (OpenAPI 3.0) for API documentation. This provides:
- Native Bearer token authentication support in Swagger UI - users can
now enter just the JWT token and the "Bearer " prefix is automatically
added
  - Modern OpenAPI 3.0 specification compliance
  - Better auto-generation of request/response schemas
  - Improved documentation accuracy with serializer introspection
- Switched to uv for package management: Migrated from pip to uv
(Astral's fast Python package installer) for improved dependency
resolution speed and reliability. This includes updates to Docker build
processes, installation scripts (debian\_install.sh), and project
configuration (pyproject.toml) to leverage uv's features like virtual
environment management and lockfile generation. - Thanks
[@&#8203;tobimichael96](https://redirect.github.com/tobimichael96) for
getting it started!
- Copy to Clipboard: Refactored `copyToClipboard` utility function to
include notification handling internally, eliminating duplicate
notification code across the frontend. The function now accepts optional
parameters for customizing success/failure messages while providing
consistent behavior across all copy operations.

##### Fixed

- XC EPG Logic: Fixed EPG filtering issues where short EPG requests had
no time-based filtering (returning expired programs) and regular EPG
requests used `start_time__gte` (missing the currently playing program).
Both now correctly use `end_time__gt` to show programs that haven't
ended yet, with short EPG additionally limiting results. (Fixes
[#&#8203;915](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/915))
- Automatic backups not enabled by default on new installations: Added
backups app to `INSTALLED_APPS` and implemented automatic scheduler
initialization in `BackupsConfig.ready()`. The backup scheduler now
properly syncs the periodic task on startup, ensuring automatic daily
backups are enabled and scheduled immediately on fresh database creation
without requiring manual user intervention.
- Fixed modular Docker Compose deployment and entrypoint/init scripts to
properly support `DISPATCHARR_ENV=modular`, use external
PostgreSQL/Redis services, and handle port, version, and encoding
validation (Closes
[#&#8203;324](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/324),
Fixes
[#&#8203;61](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/61),
[#&#8203;445](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/445),
[#&#8203;731](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/731))
- Thanks [@&#8203;CodeBormen](https://redirect.github.com/CodeBormen)
- Stream rehash/merge logic now guarantees unique stream\_hash and
always preserves the stream with the best channel ordering and
relationships. This prevents duplicate key errors and ensures the
correct stream is retained when merging. (Fixes
[#&#8203;892](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/892))
- Admin URL Conflict with XC Streams: Updated nginx configuration to
only redirect exact `/admin` and `/admin/` paths to login in production,
preventing interference with stream URLs that use "admin" as a username
(e.g., `/admin/password/stream_id` now properly routes to stream
handling instead of being redirected).
- EPG Channel ID XML Escaping: Fixed XML parsing errors in EPG output
when channel IDs contain special characters (&, <, >, ") by properly
escaping them in XML attributes. (Fixes
[#&#8203;765](https://redirect.github.com/Dispatcharr/Dispatcharr/issues/765))
- Thanks [@&#8203;CodeBormen](https://redirect.github.com/CodeBormen)
- Fixed NumPy baseline detection in Docker entrypoint. Now properly
detects when NumPy crashes on import due to CPU baseline incompatibility
and installs legacy NumPy version. Previously, if NumPy failed to
import, the script would skip legacy installation assuming it was
already compatible.
- Backup Scheduler Test: Fixed test to correctly validate that automatic
backups are enabled by default with a retention count of 3, matching the
actual scheduler defaults. - Thanks
[@&#8203;jcasimir](https://redirect.github.com/jcasimir)
- Hardened plugin loading to avoid executing plugin code unless the
plugin is enabled.
- Prevented plugin package names from shadowing standard library or
installed modules by namespacing plugin imports with safe aliases.
- Added safety limits to plugin ZIP imports (file count and size caps)
and sanitized plugin keys derived from uploads.
- Enforced strict boolean parsing for plugin enable/disable requests to
avoid accidental enables from truthy strings.
- Applied plugin field defaults server-side when running actions so
plugins receive expected settings even before a user saves.
- Plugin settings UI improvements: render `info`/`text` fields, support
`input_type: password`, show descriptions/placeholders, surface save
failures, and keep settings in sync after refresh.
- Disabled plugins now collapse settings/actions to match the closed
state before first enable.
- Plugin card header controls (delete/version/toggle) now stay
right-aligned even with long descriptions.
- Improved plugin logo resolution (case-insensitive paths + absolute
URLs), fixing dev UI logo loading without a Vite proxy.
- Plugin reload now hits the backend, clears module caches across
workers, and refreshes the UI so code changes apply without a full
backend restart.
- Plugin loader now supports `plugin.py` without `__init__.py`,
including folders with non-identifier names, by loading modules directly
from file paths.
- Plugin action handling stabilized: avoids registry race conditions and
only shows loading on the active action.
- Plugin enable/disable toggles now update immediately without requiring
a full page refresh.

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Renovate
Bot](https://redirect.github.com/renovatebot/renovate).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My40LjIiLCJ1cGRhdGVkSW5WZXIiOiI0My40LjIiLCJ0YXJnZXRCcmFuY2giOiJtYXN0ZXIiLCJsYWJlbHMiOlsiYXBwL2Rpc3BhdGNoYXJyIiwiYXV0b21lcmdlIiwicmVub3ZhdGUvY29udGFpbmVyIiwidHlwZS9taW5vciJdfQ==-->
2026-02-11 00:30:46 +00:00
TrueCharts-Bot bafc468832 Commit daily changes
Signed-off-by: TrueCharts-Bot <bot@truecharts.org>
2026-02-03 00:19:06 +00:00
TrueCharts Bot 32a5df0fda feat(helm-deps): update chart common to v28.29.49 (#44431)
This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
|
[common](https://trueforge.org/truetech/truecharts/charts/library/common)
([source](https://ghcr.io/cloudnative-pg/postgis)) | patch | `28.29.40`
-> `28.29.49` |
|
[common](https://trueforge.org/truetech/truecharts/charts/library/common)
([source](https://ghcr.io/cloudnative-pg/postgis)) | minor | `28.26.4`
-> `28.29.49` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the Dependency
Dashboard for more information.

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about these
updates again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Renovate
Bot](https://redirect.github.com/renovatebot/renovate).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0Mi4xNy4wIiwidXBkYXRlZEluVmVyIjoiNDIuMTcuMCIsInRhcmdldEJyYW5jaCI6Im1hc3RlciIsImxhYmVscyI6WyJhcHAvendhdmVqczJtcXR0IiwiYXV0b21lcmdlIiwicmVub3ZhdGUvY29udGFpbmVyIiwidHlwZS9taW5vciIsInR5cGUvcGF0Y2giXX0=-->

Signed-off-by: Kjeld Schouten <info@kjeldschouten.nl>
Co-authored-by: Kjeld Schouten <info@kjeldschouten.nl>
2026-02-02 22:19:33 +01:00
TrueCharts-Bot bf152c8e74 Commit daily changes
Signed-off-by: TrueCharts-Bot <bot@truecharts.org>
2026-02-01 15:24:18 +00:00