mirror of
https://github.com/Dispatcharr/Dispatcharr.git
synced 2026-07-17 16:50:53 +00:00
Enhancement: Update user connection reporting in xc_get_info to reflect active connections and max connections based on user stream limits. (Fixes #990)
Some checks are pending
CI Pipeline / prepare (push) Waiting to run
CI Pipeline / docker (amd64, ubuntu-24.04) (push) Blocked by required conditions
CI Pipeline / docker (arm64, ubuntu-24.04-arm) (push) Blocked by required conditions
CI Pipeline / create-manifest (push) Blocked by required conditions
Build and Push Multi-Arch Docker Image / build-and-push (push) Waiting to run
Frontend Tests / test (push) Waiting to run
Some checks are pending
CI Pipeline / prepare (push) Waiting to run
CI Pipeline / docker (amd64, ubuntu-24.04) (push) Blocked by required conditions
CI Pipeline / docker (arm64, ubuntu-24.04-arm) (push) Blocked by required conditions
CI Pipeline / create-manifest (push) Blocked by required conditions
Build and Push Multi-Arch Docker Image / build-and-push (push) Waiting to run
Frontend Tests / test (push) Waiting to run
This commit is contained in:
parent
01e356c156
commit
37922a44ab
3 changed files with 17 additions and 3 deletions
|
|
@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
### Fixed
|
||||
|
||||
- **Xtream Codes `player_api.php` missing `active_cons` and reporting wrong `max_connections`**. The `user_info` block returned by the XC API did not include the `active_cons` field, which Enigma2 clients (XStreamity, XKlass) read unconditionally and crash with `KeyError: 'active_cons'` when it is absent. `max_connections` was also hardcoded to the system-wide tuner count for every user, ignoring per-user `stream_limit` configuration. `xc_get_info` now reports `max_connections` as the user's `stream_limit` when set, falling back to the system tuner count for unlimited users; `active_cons` is the user's own active connection count when they have a per-user limit, or the system-wide active connection count when they do not (so unlimited clients can still see how much of the global tuner pool is in use). The existing `get_user_active_connections` helper was generalized to accept `user_id=None` for the system-wide query rather than duplicating its Redis scan logic. (Fixes #990)
|
||||
- **Plugin discovery re-running on every connect event**. The `PluginManager` cache hit check used `if self._registry and ...`, which always evaluated false when zero plugins were installed because an empty dict is falsy. Every Connect event (`recording_start`, `client_connect`, `channel_start`, etc.) was therefore triggering a full filesystem walk of `/data/plugins` and emitting `Discovering plugins (no DB sync) in /data/plugins` / `Discovered 0 plugin(s)` log lines. Tracked separately via a new `_discovery_completed` flag so the cache short-circuits subsequent calls regardless of registry contents, dropping discovery to once per worker process lifetime.
|
||||
- **Empty show/season folders left behind after deleting a recording**. Deleting a recording removes the MKV (or HLS working directory if still in progress) but previously left the parent show / season directories on disk even when they no longer contained any other recordings. After file cleanup, `RecordingViewSet.destroy` now walks up from the deleted file's parent directory and removes any now-empty directories, stopping at the `/data/recordings` library root so the root itself is never touched.
|
||||
- **Premature DVR concat on graceful Celery worker shutdown**. A `worker_shutting_down` signal handler in `apps/channels/tasks.py` now sets a module-level `_DVR_SHUTTING_DOWN` flag. The `run_recording` loop checks this flag after FFmpeg exits and, if the recording's `end_time` has not yet passed, skips concatenation and persists `status="interrupted"` with `interrupted_reason="server_shutdown"`. The HLS working directory is preserved across the restart so the recovery path on the next worker startup can resume segment numbering from where it left off rather than truncating the show.
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import logging
|
|||
from django.db.models.functions import Lower
|
||||
import os
|
||||
from apps.m3u.utils import calculate_tuner_count
|
||||
from apps.proxy.utils import get_user_active_connections
|
||||
import regex
|
||||
from core.utils import log_system_event
|
||||
import hashlib
|
||||
|
|
@ -1945,6 +1946,13 @@ def xc_get_info(request, full=False):
|
|||
hostname = raw_host
|
||||
port = "443" if request.is_secure() else "80"
|
||||
|
||||
if user.stream_limit and user.stream_limit > 0:
|
||||
active_cons = len(get_user_active_connections(user.id))
|
||||
max_connections = user.stream_limit
|
||||
else:
|
||||
active_cons = len(get_user_active_connections(None))
|
||||
max_connections = calculate_tuner_count(minimum=1, unlimited_default=50)
|
||||
|
||||
info = {
|
||||
"user_info": {
|
||||
"username": request.GET.get("username"),
|
||||
|
|
@ -1953,7 +1961,8 @@ def xc_get_info(request, full=False):
|
|||
"auth": 1,
|
||||
"status": "Active",
|
||||
"exp_date": str(int(time.time()) + (90 * 24 * 60 * 60)),
|
||||
"max_connections": str(calculate_tuner_count(minimum=1, unlimited_default=50)),
|
||||
"active_cons": str(active_cons),
|
||||
"max_connections": str(max_connections),
|
||||
"allowed_output_formats": [
|
||||
"ts",
|
||||
],
|
||||
|
|
|
|||
|
|
@ -83,6 +83,10 @@ def attempt_stream_termination(user_id, requesting_client_id, active_connections
|
|||
return False
|
||||
|
||||
def get_user_active_connections(user_id):
|
||||
"""Return active stream connections for a single user.
|
||||
|
||||
Pass `user_id=None` to return all active connections across the system.
|
||||
"""
|
||||
redis_client = RedisClient.get_client()
|
||||
connections = []
|
||||
|
||||
|
|
@ -101,7 +105,7 @@ def get_user_active_connections(user_id):
|
|||
logger.debug(f"[stream limits] channel_id = {channel_id}")
|
||||
logger.debug(f"[stream limits] client_id = {client_id}")
|
||||
|
||||
if client_user_id and int(client_user_id) == user_id:
|
||||
if user_id is None or (client_user_id and int(client_user_id) == user_id):
|
||||
try:
|
||||
logger.debug(f"[stream limits] Found LIVE connection for user {user_id} on channel {channel_id} with client ID {client_id}")
|
||||
connected_at = float(connected_at) if connected_at else 0
|
||||
|
|
@ -127,7 +131,7 @@ def get_user_active_connections(user_id):
|
|||
logger.debug(f"[stream limits] user_id = {user_id}")
|
||||
logger.debug(f"[stream limits] client_id = {client_id}")
|
||||
|
||||
if client_user_id and int(client_user_id) == user_id:
|
||||
if user_id is None or (client_user_id and int(client_user_id) == user_id):
|
||||
try:
|
||||
logger.debug(f"[stream limits] Found VOD connection for user {user_id} on content {content_uuid} with client ID {client_id}")
|
||||
connected_at = float(connected_at) if connected_at else 0
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue