Added sports livestreams

This commit is contained in:
2025-11-14 21:20:41 +01:00
parent d830f8ae5f
commit ce6c435a92
8 changed files with 476 additions and 9 deletions
@@ -93,6 +93,7 @@ public class SRFImageProvider : IRemoteImageProvider, IHasOrder
var chapter = mediaComposition.ChapterList[0];
if (!string.IsNullOrEmpty(chapter.ImageUrl))
{
_logger.LogDebug("URN {Urn}: Adding chapter image: {ImageUrl}", urn, chapter.ImageUrl);
list.Add(new RemoteImageInfo
{
Url = chapter.ImageUrl,
@@ -107,6 +108,10 @@ public class SRFImageProvider : IRemoteImageProvider, IHasOrder
ProviderName = Name
});
}
else
{
_logger.LogDebug("URN {Urn}: No chapter image available", urn);
}
}
// Extract images from show
@@ -114,6 +119,7 @@ public class SRFImageProvider : IRemoteImageProvider, IHasOrder
{
if (!string.IsNullOrEmpty(mediaComposition.Show.ImageUrl))
{
_logger.LogDebug("URN {Urn}: Adding show image: {ImageUrl}", urn, mediaComposition.Show.ImageUrl);
list.Add(new RemoteImageInfo
{
Url = mediaComposition.Show.ImageUrl,
@@ -124,6 +130,7 @@ public class SRFImageProvider : IRemoteImageProvider, IHasOrder
if (!string.IsNullOrEmpty(mediaComposition.Show.BannerImageUrl))
{
_logger.LogDebug("URN {Urn}: Adding show banner: {BannerUrl}", urn, mediaComposition.Show.BannerImageUrl);
list.Add(new RemoteImageInfo
{
Url = mediaComposition.Show.BannerImageUrl,
@@ -133,7 +140,7 @@ public class SRFImageProvider : IRemoteImageProvider, IHasOrder
}
}
_logger.LogDebug("Found {Count} images for URN: {Urn}", list.Count, urn);
_logger.LogInformation("URN {Urn}: Found {Count} images for '{ItemName}'", urn, list.Count, item.Name);
}
catch (Exception ex)
{
@@ -72,8 +72,14 @@ public class SRFMediaProvider : IMediaSourceProvider
return Task.FromResult<IEnumerable<MediaSourceInfo>>(sources);
}
// For scheduled livestreams, use shorter cache TTL (5 minutes) so they refresh when they go live
// For regular content, use configured cache duration
var cacheDuration = urn.Contains("scheduled_livestream", StringComparison.OrdinalIgnoreCase)
? 5
: config.CacheDurationMinutes;
// Try cache first
var mediaComposition = _metadataCache.GetMediaComposition(urn, config.CacheDurationMinutes);
var mediaComposition = _metadataCache.GetMediaComposition(urn, cacheDuration);
// If not in cache, fetch from API
if (mediaComposition == null)
@@ -113,12 +119,72 @@ public class SRFMediaProvider : IMediaSourceProvider
// Get stream URL based on quality preference
var streamUrl = _streamResolver.GetStreamUrl(chapter, config.QualityPreference);
if (string.IsNullOrEmpty(streamUrl))
// Check if this is an upcoming livestream that hasn't started yet
var isUpcomingLivestream = chapter.Type == "SCHEDULED_LIVESTREAM" && string.IsNullOrEmpty(streamUrl);
if (string.IsNullOrEmpty(streamUrl) && !isUpcomingLivestream)
{
_logger.LogWarning("Could not resolve stream URL for URN: {Urn}", urn);
return Task.FromResult<IEnumerable<MediaSourceInfo>>(sources);
}
// For upcoming livestreams, check if the event has started
if (isUpcomingLivestream)
{
if (chapter.ValidFrom != null)
{
var eventStart = chapter.ValidFrom.Value.ToUniversalTime();
var now = DateTime.UtcNow;
if (eventStart > now)
{
_logger.LogInformation(
"URN {Urn}: Livestream '{Title}' hasn't started yet (starts at {ValidFrom}). User should refresh when live.",
urn,
chapter.Title,
chapter.ValidFrom);
// Return empty sources - event not yet playable
return Task.FromResult<IEnumerable<MediaSourceInfo>>(sources);
}
}
// Event should be live now - re-fetch media composition without cache
using var freshApiClient = new SRFApiClient(_loggerFactory);
var freshMediaComposition = freshApiClient.GetMediaCompositionByUrnAsync(urn, cancellationToken).GetAwaiter().GetResult();
if (freshMediaComposition?.ChapterList != null && freshMediaComposition.ChapterList.Count > 0)
{
var freshChapter = freshMediaComposition.ChapterList[0];
streamUrl = _streamResolver.GetStreamUrl(freshChapter, config.QualityPreference);
if (!string.IsNullOrEmpty(streamUrl))
{
// Update cache with fresh data
_metadataCache.SetMediaComposition(urn, freshMediaComposition);
chapter = freshChapter;
_logger.LogInformation("URN {Urn}: Livestream is now live, got fresh stream URL", urn);
}
else
{
_logger.LogWarning("URN {Urn}: Livestream should be live but still no stream URL available", urn);
return Task.FromResult<IEnumerable<MediaSourceInfo>>(sources);
}
}
else
{
_logger.LogWarning("URN {Urn}: Failed to fetch fresh media composition for livestream", urn);
return Task.FromResult<IEnumerable<MediaSourceInfo>>(sources);
}
}
// Authenticate the stream URL (required for all SRF streams, especially livestreams)
if (!string.IsNullOrEmpty(streamUrl))
{
streamUrl = _streamResolver.GetAuthenticatedStreamUrlAsync(streamUrl, cancellationToken).GetAwaiter().GetResult();
_logger.LogDebug("Authenticated stream URL for URN: {Urn}", urn);
}
// Create media source
var mediaSource = new MediaSourceInfo
{