mirror of
https://github.com/Dispatcharr/Dispatcharr.git
synced 2026-07-25 02:57:57 +00:00
Adds current m3u profile to stats.
This commit is contained in:
parent
d3a7dbca10
commit
88f27d62f1
4 changed files with 102 additions and 10 deletions
|
|
@ -65,6 +65,25 @@ class ChannelStatus:
|
|||
except ValueError:
|
||||
logger.warning(f"Invalid stream_id format in Redis: {stream_id_bytes}")
|
||||
|
||||
# Add M3U profile information
|
||||
m3u_profile_id_bytes = metadata.get(ChannelMetadataField.M3U_PROFILE.encode('utf-8'))
|
||||
if m3u_profile_id_bytes:
|
||||
try:
|
||||
m3u_profile_id = int(m3u_profile_id_bytes.decode('utf-8'))
|
||||
info['m3u_profile_id'] = m3u_profile_id
|
||||
|
||||
# Look up M3U profile name from database
|
||||
try:
|
||||
from apps.m3u.models import M3UAccountProfile
|
||||
m3u_profile = M3UAccountProfile.objects.filter(id=m3u_profile_id).first()
|
||||
if m3u_profile:
|
||||
info['m3u_profile_name'] = m3u_profile.name
|
||||
logger.debug(f"Added M3U profile name '{m3u_profile.name}' for profile ID {m3u_profile_id}")
|
||||
except (ImportError, DatabaseError) as e:
|
||||
logger.warning(f"Failed to get M3U profile name for ID {m3u_profile_id}: {e}")
|
||||
except ValueError:
|
||||
logger.warning(f"Invalid m3u_profile_id format in Redis: {m3u_profile_id_bytes}")
|
||||
|
||||
# Add timing information
|
||||
state_changed_field = ChannelMetadataField.STATE_CHANGED_AT.encode('utf-8')
|
||||
if state_changed_field in metadata:
|
||||
|
|
@ -380,6 +399,25 @@ class ChannelStatus:
|
|||
# Add clients to info
|
||||
info['clients'] = clients
|
||||
|
||||
# Add M3U profile information
|
||||
m3u_profile_id_bytes = metadata.get(ChannelMetadataField.M3U_PROFILE.encode('utf-8'))
|
||||
if m3u_profile_id_bytes:
|
||||
try:
|
||||
m3u_profile_id = int(m3u_profile_id_bytes.decode('utf-8'))
|
||||
info['m3u_profile_id'] = m3u_profile_id
|
||||
|
||||
# Look up M3U profile name from database
|
||||
try:
|
||||
from apps.m3u.models import M3UAccountProfile
|
||||
m3u_profile = M3UAccountProfile.objects.filter(id=m3u_profile_id).first()
|
||||
if m3u_profile:
|
||||
info['m3u_profile_name'] = m3u_profile.name
|
||||
logger.debug(f"Added M3U profile name '{m3u_profile.name}' for profile ID {m3u_profile_id}")
|
||||
except (ImportError, DatabaseError) as e:
|
||||
logger.warning(f"Failed to get M3U profile name for ID {m3u_profile_id}: {e}")
|
||||
except ValueError:
|
||||
logger.warning(f"Invalid m3u_profile_id format in Redis: {m3u_profile_id_bytes}")
|
||||
|
||||
return info
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting channel info: {e}")
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import logging
|
|||
import time
|
||||
import json
|
||||
from django.shortcuts import get_object_or_404
|
||||
from apps.channels.models import Channel
|
||||
from apps.channels.models import Channel, Stream
|
||||
from apps.proxy.config import TSConfig as Config
|
||||
from ..server import ProxyServer
|
||||
from ..redis_keys import RedisKeys
|
||||
|
|
@ -58,7 +58,7 @@ class ChannelService:
|
|||
# Verify the stream_id was set
|
||||
stream_id_value = proxy_server.redis_client.hget(metadata_key, ChannelMetadataField.STREAM_ID)
|
||||
if stream_id_value:
|
||||
logger.info(f"Verified stream_id {stream_id_value.decode('utf-8')} is now set in Redis")
|
||||
logger.debug(f"Verified stream_id {stream_id_value.decode('utf-8')} is now set in Redis")
|
||||
else:
|
||||
logger.error(f"Failed to set stream_id {stream_id} in Redis before initialization")
|
||||
|
||||
|
|
@ -82,7 +82,7 @@ class ChannelService:
|
|||
return success
|
||||
|
||||
@staticmethod
|
||||
def change_stream_url(channel_id, new_url=None, user_agent=None, target_stream_id=None):
|
||||
def change_stream_url(channel_id, new_url=None, user_agent=None, target_stream_id=None, m3u_profile_id=None):
|
||||
"""
|
||||
Change the URL of an existing stream.
|
||||
|
||||
|
|
@ -91,6 +91,7 @@ class ChannelService:
|
|||
new_url: New stream URL (optional if target_stream_id is provided)
|
||||
user_agent: Optional user agent to update
|
||||
target_stream_id: Optional target stream ID to switch to
|
||||
m3u_profile_id: Optional M3U profile ID to update
|
||||
|
||||
Returns:
|
||||
dict: Result information including success status and diagnostics
|
||||
|
|
@ -109,6 +110,10 @@ class ChannelService:
|
|||
new_url = stream_info['url']
|
||||
user_agent = stream_info['user_agent']
|
||||
stream_id = target_stream_id
|
||||
# Extract M3U profile ID from stream info if available
|
||||
if 'm3u_profile_id' in stream_info:
|
||||
m3u_profile_id = stream_info['m3u_profile_id']
|
||||
logger.info(f"Found M3U profile ID {m3u_profile_id} for stream ID {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
|
||||
|
|
@ -163,7 +168,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, stream_id)
|
||||
ChannelService._update_channel_metadata(channel_id, new_url, user_agent, stream_id, m3u_profile_id)
|
||||
result['metadata_updated'] = True
|
||||
except Exception as e:
|
||||
logger.error(f"Error updating Redis metadata: {e}", exc_info=True)
|
||||
|
|
@ -188,7 +193,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, stream_id)
|
||||
ChannelService._publish_stream_switch_event(channel_id, new_url, user_agent, stream_id, m3u_profile_id)
|
||||
result.update({
|
||||
'direct_update': False,
|
||||
'event_published': True,
|
||||
|
|
@ -413,7 +418,7 @@ class ChannelService:
|
|||
# Helper methods for Redis operations
|
||||
|
||||
@staticmethod
|
||||
def _update_channel_metadata(channel_id, url, user_agent=None, stream_id=None):
|
||||
def _update_channel_metadata(channel_id, url, user_agent=None, stream_id=None, m3u_profile_id=None):
|
||||
"""Update channel metadata in Redis"""
|
||||
proxy_server = ProxyServer.get_instance()
|
||||
|
||||
|
|
@ -432,7 +437,8 @@ class ChannelService:
|
|||
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}")
|
||||
if m3u_profile_id:
|
||||
metadata[ChannelMetadataField.M3U_PROFILE] = str(m3u_profile_id)
|
||||
|
||||
# Use the appropriate method based on the key type
|
||||
if key_type == 'hash':
|
||||
|
|
@ -448,11 +454,11 @@ class ChannelService:
|
|||
switch_key = RedisKeys.switch_request(channel_id)
|
||||
proxy_server.redis_client.setex(switch_key, 30, url) # 30 second TTL
|
||||
|
||||
logger.info(f"Updated metadata for channel {channel_id} in Redis")
|
||||
logger.debug(f"Updated metadata for channel {channel_id} in Redis")
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def _publish_stream_switch_event(channel_id, new_url, user_agent=None, stream_id=None):
|
||||
def _publish_stream_switch_event(channel_id, new_url, user_agent=None, stream_id=None, m3u_profile_id=None):
|
||||
"""Publish a stream switch event to Redis pubsub"""
|
||||
proxy_server = ProxyServer.get_instance()
|
||||
|
||||
|
|
@ -465,6 +471,7 @@ class ChannelService:
|
|||
"url": new_url,
|
||||
"user_agent": user_agent,
|
||||
"stream_id": stream_id,
|
||||
"m3u_profile_id": m3u_profile_id,
|
||||
"requester": proxy_server.worker_id,
|
||||
"timestamp": time.time()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -351,6 +351,7 @@ def change_stream(request, channel_id):
|
|||
# Use the info from the stream
|
||||
new_url = stream_info['url']
|
||||
user_agent = stream_info['user_agent']
|
||||
m3u_profile_id = stream_info.get('m3u_profile_id')
|
||||
# Stream ID will be passed to change_stream_url later
|
||||
elif not new_url:
|
||||
return JsonResponse({'error': 'Either url or stream_id must be provided'}, status=400)
|
||||
|
|
@ -359,7 +360,7 @@ def change_stream(request, channel_id):
|
|||
|
||||
# Use the service layer instead of direct implementation
|
||||
# Pass stream_id to ensure proper connection tracking
|
||||
result = ChannelService.change_stream_url(channel_id, new_url, user_agent, stream_id)
|
||||
result = ChannelService.change_stream_url(channel_id, new_url, user_agent, stream_id, m3u_profile_id)
|
||||
|
||||
# Get the stream manager before updating URL
|
||||
stream_manager = proxy_server.stream_managers.get(channel_id)
|
||||
|
|
|
|||
|
|
@ -82,12 +82,23 @@ const ChannelCard = ({ channel, clients, stopClient, stopChannel, logos, channel
|
|||
const [availableStreams, setAvailableStreams] = useState([]);
|
||||
const [isLoadingStreams, setIsLoadingStreams] = useState(false);
|
||||
const [activeStreamId, setActiveStreamId] = useState(null);
|
||||
const [currentM3UProfile, setCurrentM3UProfile] = useState(null); // Add state for current M3U profile
|
||||
|
||||
// Safety check - if channel doesn't have required data, don't render
|
||||
if (!channel || !channel.channel_id) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Update M3U profile information when channel data changes
|
||||
useEffect(() => {
|
||||
// If the channel data includes M3U profile information, update our state
|
||||
if (channel.m3u_profile || channel.m3u_profile_name) {
|
||||
setCurrentM3UProfile({
|
||||
name: channel.m3u_profile?.name || channel.m3u_profile_name || 'Default M3U'
|
||||
});
|
||||
}
|
||||
}, [channel.m3u_profile, channel.m3u_profile_name, channel.stream_id]);
|
||||
|
||||
// Fetch available streams for this channel
|
||||
useEffect(() => {
|
||||
const fetchStreams = async () => {
|
||||
|
|
@ -110,6 +121,11 @@ const ChannelCard = ({ channel, clients, stopClient, stopChannel, logos, channel
|
|||
|
||||
if (matchingStream) {
|
||||
setActiveStreamId(matchingStream.id.toString());
|
||||
|
||||
// If the stream has M3U profile info, save it
|
||||
if (matchingStream.m3u_profile) {
|
||||
setCurrentM3UProfile(matchingStream.m3u_profile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -138,6 +154,14 @@ const ChannelCard = ({ channel, clients, stopClient, stopChannel, logos, channel
|
|||
// Update the local active stream ID immediately
|
||||
setActiveStreamId(streamId);
|
||||
|
||||
// Update M3U profile information if available in the response
|
||||
if (response && response.m3u_profile) {
|
||||
setCurrentM3UProfile(response.m3u_profile);
|
||||
} else if (selectedStream && selectedStream.m3u_profile) {
|
||||
// Fallback to the profile from the selected stream
|
||||
setCurrentM3UProfile(selectedStream.m3u_profile);
|
||||
}
|
||||
|
||||
// Show detailed notification with stream name
|
||||
notifications.show({
|
||||
title: 'Stream switching',
|
||||
|
|
@ -152,6 +176,12 @@ const ChannelCard = ({ channel, clients, stopClient, stopChannel, logos, channel
|
|||
if (channelId) {
|
||||
const updatedStreamData = await API.getChannelStreams(channelId);
|
||||
console.log("Channel streams after switch:", updatedStreamData);
|
||||
|
||||
// Update current stream information with fresh data
|
||||
const updatedStream = updatedStreamData.find(s => s.id.toString() === streamId);
|
||||
if (updatedStream && updatedStream.m3u_profile) {
|
||||
setCurrentM3UProfile(updatedStream.m3u_profile);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error checking streams after switch:", error);
|
||||
|
|
@ -305,6 +335,12 @@ const ChannelCard = ({ channel, clients, stopClient, stopChannel, logos, channel
|
|||
const avgBitrate = channel.avg_bitrate || '0 Kbps';
|
||||
const streamProfileName = channel.stream_profile?.name || 'Unknown Profile';
|
||||
|
||||
// Use currentM3UProfile if available, otherwise fall back to channel data
|
||||
const m3uProfileName = currentM3UProfile?.name ||
|
||||
channel.m3u_profile?.name ||
|
||||
channel.m3u_profile_name ||
|
||||
'Default M3U';
|
||||
|
||||
// Create select options for available streams
|
||||
const streamOptions = availableStreams.map(stream => ({
|
||||
value: stream.id.toString(),
|
||||
|
|
@ -377,6 +413,16 @@ const ChannelCard = ({ channel, clients, stopClient, stopChannel, logos, channel
|
|||
</Group>
|
||||
</Flex>
|
||||
|
||||
{/* Display M3U profile information */}
|
||||
<Flex justify="flex-end" align="center" mt={-8}>
|
||||
<Group gap={5}>
|
||||
<HardDriveUpload size="18" />
|
||||
<Tooltip label="Current M3U Profile">
|
||||
<Text size="xs">{m3uProfileName}</Text>
|
||||
</Tooltip>
|
||||
</Group>
|
||||
</Flex>
|
||||
|
||||
{/* Add stream selection dropdown */}
|
||||
{availableStreams.length > 0 && (
|
||||
<Tooltip label="Switch to another stream source">
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue