Fixes stream switches not honouring user selected order.

This commit is contained in:
SergeantPanda 2025-03-28 08:50:57 -05:00
parent e2d9e233da
commit 2f995b16fd
3 changed files with 42 additions and 24 deletions

View file

@ -95,6 +95,7 @@ class ChannelService:
dict: Result information including success status and diagnostics
"""
# If no direct URL is provided but a target stream is, get URL from target stream
stream_id = None
if not new_url and target_stream_id:
stream_info = get_stream_info_for_switch(channel_id, target_stream_id)
if 'error' in stream_info:
@ -104,6 +105,10 @@ class ChannelService:
}
new_url = stream_info['url']
user_agent = stream_info['user_agent']
stream_id = target_stream_id
elif target_stream_id:
# If we have both URL and target_stream_id, use the target_stream_id
stream_id = target_stream_id
# Check if channel exists
in_local_managers = channel_id in proxy_server.stream_managers
@ -155,7 +160,7 @@ class ChannelService:
# Update metadata in Redis regardless of ownership
if proxy_server.redis_client:
try:
ChannelService._update_channel_metadata(channel_id, new_url, user_agent)
ChannelService._update_channel_metadata(channel_id, new_url, user_agent, stream_id)
result['metadata_updated'] = True
except Exception as e:
logger.error(f"Error updating Redis metadata: {e}", exc_info=True)
@ -180,7 +185,7 @@ class ChannelService:
# If we're not the owner, publish an event for the owner to pick up
logger.info(f"Not the owner, requesting URL change via Redis PubSub")
if proxy_server.redis_client:
ChannelService._publish_stream_switch_event(channel_id, new_url, user_agent)
ChannelService._publish_stream_switch_event(channel_id, new_url, user_agent, stream_id)
result.update({
'direct_update': False,
'event_published': True,
@ -400,7 +405,7 @@ class ChannelService:
# Helper methods for Redis operations
@staticmethod
def _update_channel_metadata(channel_id, url, user_agent=None):
def _update_channel_metadata(channel_id, url, user_agent=None, stream_id=None):
"""Update channel metadata in Redis"""
if not proxy_server.redis_client:
return False
@ -411,23 +416,22 @@ class ChannelService:
key_type = proxy_server.redis_client.type(metadata_key).decode('utf-8')
logger.debug(f"Redis key {metadata_key} is of type: {key_type}")
# Build metadata update dict
metadata = {ChannelMetadataField.URL: url}
if user_agent:
metadata[ChannelMetadataField.USER_AGENT] = user_agent
if stream_id:
metadata[ChannelMetadataField.STREAM_ID] = str(stream_id)
logger.info(f"Updating stream ID to {stream_id} in Redis for channel {channel_id}")
# Use the appropriate method based on the key type
if key_type == 'hash':
proxy_server.redis_client.hset(metadata_key, ChannelMetadataField.URL, url)
if user_agent:
proxy_server.redis_client.hset(metadata_key, ChannelMetadataField.USER_AGENT, user_agent)
proxy_server.redis_client.hset(metadata_key, mapping=metadata)
elif key_type == 'none': # Key doesn't exist yet
# Create new hash with all required fields
metadata = {ChannelMetadataField.URL: url}
if user_agent:
metadata[ChannelMetadataField.USER_AGENT] = user_agent
proxy_server.redis_client.hset(metadata_key, mapping=metadata)
else:
# If key exists with wrong type, delete it and recreate
proxy_server.redis_client.delete(metadata_key)
metadata = {ChannelMetadataField.URL: url}
if user_agent:
metadata[ChannelMetadataField.USER_AGENT] = user_agent
proxy_server.redis_client.hset(metadata_key, mapping=metadata)
# Set switch request flag to ensure all workers see it
@ -438,7 +442,7 @@ class ChannelService:
return True
@staticmethod
def _publish_stream_switch_event(channel_id, new_url, user_agent=None):
def _publish_stream_switch_event(channel_id, new_url, user_agent=None, stream_id=None):
"""Publish a stream switch event to Redis pubsub"""
if not proxy_server.redis_client:
return False
@ -448,6 +452,7 @@ class ChannelService:
"channel_id": channel_id,
"url": new_url,
"user_agent": user_agent,
"stream_id": stream_id,
"requester": proxy_server.worker_id,
"timestamp": time.time()
}

View file

@ -194,8 +194,8 @@ def get_alternate_streams(channel_id: str, current_stream_id: Optional[int] = No
logger.debug(f"Looking for alternate streams for channel {channel_id}, current stream ID: {current_stream_id}")
# Get all assigned streams for this channel
streams = channel.streams.all()
# Get all assigned streams for this channel using the correct ordering from the channelstream table
streams = channel.streams.all().order_by('channelstream__order')
logger.debug(f"Channel {channel_id} has {streams.count()} total assigned streams")
if not streams.exists():
@ -204,7 +204,7 @@ def get_alternate_streams(channel_id: str, current_stream_id: Optional[int] = No
alternate_streams = []
# Process each stream
# Process each stream in the user-defined order
for stream in streams:
# Log each stream we're checking
logger.debug(f"Checking stream ID {stream.id} ({stream.name}) for channel {channel_id}")
@ -215,8 +215,6 @@ def get_alternate_streams(channel_id: str, current_stream_id: Optional[int] = No
continue
# Find compatible profiles for this stream
# FIX: Looking at the error message, M3UAccountProfile doesn't have a 'stream' field
# We need to find which field relates M3UAccountProfile to Stream
try:
# Check if we can find profiles via m3u_account
profiles = M3UAccountProfile.objects.filter(m3u_account=stream.m3u_account)

View file

@ -346,13 +346,27 @@ def next_stream(request, channel_id):
try:
logger.info(f"Request to switch to next stream for channel {channel_id} received")
# Check if the channel exists
channel = get_stream_object(channel_id)
# First check if channel is active in Redis
current_stream_id = None
profile_id = None
# Get current stream info to know which one we're currently using
current_stream_id, profile_id = channel.get_stream()
if proxy_server.redis_client:
metadata_key = RedisKeys.channel_metadata(channel_id)
if proxy_server.redis_client.exists(metadata_key):
# Get current stream ID from Redis
stream_id_bytes = proxy_server.redis_client.hget(metadata_key, ChannelMetadataField.STREAM_ID)
if stream_id_bytes:
current_stream_id = int(stream_id_bytes.decode('utf-8'))
logger.info(f"Found current stream ID {current_stream_id} in Redis for channel {channel_id}")
# Get M3U profile from Redis if available
profile_id_bytes = proxy_server.redis_client.hget(metadata_key, ChannelMetadataField.M3U_PROFILE)
if profile_id_bytes:
profile_id = int(profile_id_bytes.decode('utf-8'))
logger.info(f"Found M3U profile ID {profile_id} in Redis for channel {channel_id}")
if not current_stream_id:
# Channel is not running
return JsonResponse({'error': 'No current stream found for channel'}, status=404)
# Get alternate streams excluding the current one
@ -382,7 +396,8 @@ def next_stream(request, channel_id):
result = ChannelService.change_stream_url(
channel_id,
stream_info['url'],
stream_info['user_agent']
stream_info['user_agent'],
next_stream_id # Pass the stream_id to be stored in Redis
)
if result.get('status') == 'error':