Merge branch 'dev' of https://github.com/Dispatcharr/Dispatcharr into pr/sethwv/1199

This commit is contained in:
SergeantPanda 2026-04-17 14:56:25 -05:00
commit 7a506e01ac
4 changed files with 32 additions and 22 deletions

View file

@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- **Plugin file sizes**: the plugin hub now displays the download size of a plugin directly on the Install / Update / Downgrade / Overwrite buttons (e.g. `Install 142 KB`) when the repository manifest includes size data. The size is also shown in the version detail panel. Falls back gracefully to a plain button when no size is provided. A `formatKB` utility was added to convert raw KB values to human-readable strings (KB/MB). A "Publish Your Plugin" button linking to the contributing guide was added to the plugin store toolbar. — Thanks [@sethwv](https://github.com/sethwv)
### Performance
- Eliminated repeated DB queries in the `ts_proxy` hot path. `StreamManager`, `StreamGenerator`, and `ProxyServer` were each calling `Channel.objects.get(uuid=...)` on every retry, reconnect, failover, and buffering event solely to retrieve `channel.name` for log events. `StreamManager` and `StreamGenerator` now fetch the channel name once at construction via a lightweight `values_list` query and store it as `self.channel_name`. `ProxyServer` caches the name in a `_channel_names` dict keyed by channel ID at channel-start time and pops it at channel-stop time. (Fixes #1138)
## [0.23.0] - 2026-04-17
### Security

View file

@ -66,6 +66,7 @@ class ProxyServer:
self.stream_managers = {}
self.stream_buffers = {}
self.client_managers = {}
self._channel_names = {}
# Generate a unique worker ID
import socket
@ -672,7 +673,9 @@ class ProxyServer:
# Log channel start event
try:
channel_obj = Channel.objects.get(uuid=channel_id)
_name = Channel.objects.filter(uuid=channel_id).values_list('name', flat=True).first()
channel_name = _name if _name else str(channel_id)
self._channel_names[channel_id] = channel_name
# Get stream name if stream_id is available
stream_name = None
@ -686,7 +689,7 @@ class ProxyServer:
log_system_event(
'channel_start',
channel_id=channel_id,
channel_name=channel_obj.name,
channel_name=channel_name,
stream_name=stream_name,
stream_id=channel_stream_id
)
@ -941,7 +944,7 @@ class ProxyServer:
# Log channel stop event (after cleanup, before releasing ownership section ends)
try:
channel_obj = Channel.objects.get(uuid=channel_id)
channel_name = self._channel_names.pop(channel_id, None) or str(channel_id)
# Calculate runtime and get total bytes from metadata
runtime = None
@ -967,7 +970,7 @@ class ProxyServer:
log_system_event(
'channel_stop',
channel_id=channel_id,
channel_name=channel_obj.name,
channel_name=channel_name,
runtime=runtime,
total_bytes=total_bytes
)

View file

@ -43,6 +43,12 @@ class StreamGenerator:
self.client_user_agent = client_user_agent
self.channel_initializing = channel_initializing
self.user = user
# Cache channel name once to avoid repeated DB queries for logging
try:
_name = Channel.objects.filter(uuid=channel_id).values_list('name', flat=True).first()
self.channel_name = _name if _name else str(channel_id)
except Exception:
self.channel_name = str(channel_id)
# Performance and state tracking
self.stream_start_time = time.time()
@ -107,11 +113,10 @@ class StreamGenerator:
# Log client connect event
try:
channel_obj = Channel.objects.get(uuid=self.channel_id)
log_system_event(
'client_connect',
channel_id=self.channel_id,
channel_name=channel_obj.name,
channel_name=self.channel_name,
client_ip=self.client_ip,
client_id=self.client_id,
user_agent=self.client_user_agent[:100] if self.client_user_agent else None,
@ -587,11 +592,10 @@ class StreamGenerator:
# Log client disconnect event
try:
channel_obj = Channel.objects.get(uuid=self.channel_id)
log_system_event(
'client_disconnect',
channel_id=self.channel_id,
channel_name=channel_obj.name,
channel_name=self.channel_name,
client_ip=self.client_ip,
client_id=self.client_id,
user_agent=self.client_user_agent[:100] if self.client_user_agent else None,

View file

@ -32,6 +32,12 @@ class StreamManager:
def __init__(self, channel_id, url, buffer, user_agent=None, transcode=False, stream_id=None, worker_id=None):
# Basic properties
self.channel_id = channel_id
# Cache channel name once to avoid repeated DB queries in hot retry/reconnect loops
try:
_name = Channel.objects.filter(uuid=channel_id).values_list('name', flat=True).first()
self.channel_name = _name if _name else str(channel_id)
except Exception:
self.channel_name = str(channel_id)
self.url = url
self.buffer = buffer
self.running = True
@ -274,11 +280,10 @@ class StreamManager:
# Log reconnection event if this is a retry (not first attempt)
if self.retry_count > 0:
try:
channel_obj = Channel.objects.get(uuid=self.channel_id)
log_system_event(
'channel_reconnect',
channel_id=self.channel_id,
channel_name=channel_obj.name,
channel_name=self.channel_name,
attempt=self.retry_count + 1,
max_attempts=self.max_retries
)
@ -317,11 +322,10 @@ class StreamManager:
# Log connection error event
try:
channel_obj = Channel.objects.get(uuid=self.channel_id)
log_system_event(
'channel_error',
channel_id=self.channel_id,
channel_name=channel_obj.name,
channel_name=self.channel_name,
error_type='connection_failed',
url=self.url[:100] if self.url else None,
attempts=self.max_retries
@ -344,11 +348,10 @@ class StreamManager:
# Log connection error event with exception details
try:
channel_obj = Channel.objects.get(uuid=self.channel_id)
log_system_event(
'channel_error',
channel_id=self.channel_id,
channel_name=channel_obj.name,
channel_name=self.channel_name,
error_type='connection_exception',
error_message=str(e)[:200],
url=self.url[:100] if self.url else None,
@ -826,11 +829,10 @@ class StreamManager:
# Log failover event
try:
channel_obj = Channel.objects.get(uuid=self.channel_id)
log_system_event(
'channel_failover',
channel_id=self.channel_id,
channel_name=channel_obj.name,
channel_name=self.channel_name,
reason='buffering_timeout',
duration=buffering_duration
)
@ -846,11 +848,10 @@ class StreamManager:
# Log system event for buffering
try:
channel_obj = Channel.objects.get(uuid=self.channel_id)
log_system_event(
'channel_buffering',
channel_id=self.channel_id,
channel_name=channel_obj.name,
channel_name=self.channel_name,
speed=ffmpeg_speed
)
except Exception as e:
@ -1172,11 +1173,10 @@ class StreamManager:
# Log stream switch event
try:
channel_obj = Channel.objects.get(uuid=self.channel_id)
log_system_event(
'stream_switch',
channel_id=self.channel_id,
channel_name=channel_obj.name,
channel_name=self.channel_name,
new_url=new_url[:100] if new_url else None,
stream_id=stream_id
)
@ -1304,11 +1304,10 @@ class StreamManager:
# Log reconnection event
try:
channel_obj = Channel.objects.get(uuid=self.channel_id)
log_system_event(
'channel_reconnect',
channel_id=self.channel_id,
channel_name=channel_obj.name,
channel_name=self.channel_name,
reason='health_monitor'
)
except Exception as e: