Added sports livestreams
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
@@ -145,6 +146,15 @@ public class SRFPlayChannel : IChannel, IHasCacheKey
|
||||
ImageUrl = null
|
||||
});
|
||||
|
||||
items.Add(new ChannelItemInfo
|
||||
{
|
||||
Id = "live_sports",
|
||||
Name = "Live Sports & Events",
|
||||
Type = ChannelItemType.Folder,
|
||||
FolderType = ChannelFolderType.Container,
|
||||
ImageUrl = null
|
||||
});
|
||||
|
||||
// Add category folders if enabled and CategoryService is available
|
||||
if (config?.EnableCategoryFolders == true && _categoryService != null)
|
||||
{
|
||||
@@ -201,6 +211,54 @@ public class SRFPlayChannel : IChannel, IHasCacheKey
|
||||
items = await ConvertUrnsToChannelItems(urns, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
// Live Sports & Events
|
||||
else if (query.FolderId == "live_sports")
|
||||
{
|
||||
try
|
||||
{
|
||||
var businessUnit = config?.BusinessUnit.ToString().ToLowerInvariant() ?? "srf";
|
||||
|
||||
using var apiClient = new Api.SRFApiClient(_loggerFactory);
|
||||
var scheduledLivestreams = await apiClient.GetScheduledLivestreamsAsync(businessUnit, "SPORT", cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (scheduledLivestreams != null)
|
||||
{
|
||||
// Filter for upcoming/current events (within next 7 days) that have URNs
|
||||
var now = DateTime.UtcNow;
|
||||
var weekFromNow = now.AddDays(7);
|
||||
|
||||
var upcomingEvents = scheduledLivestreams
|
||||
.Where(p => p.Urn != null &&
|
||||
!string.IsNullOrEmpty(p.Title) &&
|
||||
p.ValidFrom != null &&
|
||||
p.ValidFrom.Value.ToUniversalTime() <= weekFromNow)
|
||||
.OrderBy(p => p.ValidFrom)
|
||||
.ToList();
|
||||
|
||||
_logger.LogInformation("Found {Count} scheduled live sports events", upcomingEvents.Count);
|
||||
|
||||
var urns = upcomingEvents.Select(e => e.Urn!).ToList();
|
||||
items = await ConvertUrnsToChannelItems(urns, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
// Enhance items with scheduled time information
|
||||
foreach (var item in items)
|
||||
{
|
||||
var matchingEvent = upcomingEvents.FirstOrDefault(e => item.ProviderIds.ContainsKey("SRF") && item.ProviderIds["SRF"] == e.Urn);
|
||||
if (matchingEvent?.ValidFrom != null)
|
||||
{
|
||||
var eventTime = matchingEvent.ValidFrom.Value;
|
||||
item.Name = $"[{eventTime:dd.MM HH:mm}] {matchingEvent.Title}";
|
||||
item.PremiereDate = eventTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Failed to load live sports events");
|
||||
}
|
||||
}
|
||||
|
||||
// Category folder - show videos for this category
|
||||
else if (query.FolderId?.StartsWith("category_", StringComparison.Ordinal) == true)
|
||||
{
|
||||
@@ -316,7 +374,8 @@ public class SRFPlayChannel : IChannel, IHasCacheKey
|
||||
var enabledTopics = config?.EnabledTopics != null && config.EnabledTopics.Count > 0
|
||||
? string.Join(",", config.EnabledTopics)
|
||||
: "all";
|
||||
return $"{config?.BusinessUnit}_{config?.EnableLatestContent}_{config?.EnableTrendingContent}_{config?.EnableCategoryFolders}_{enabledTopics}";
|
||||
var date = DateTime.Now.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture);
|
||||
return $"{config?.BusinessUnit}_{config?.EnableLatestContent}_{config?.EnableTrendingContent}_{config?.EnableCategoryFolders}_{enabledTopics}_{date}";
|
||||
}
|
||||
|
||||
private async Task<List<ChannelItemInfo>> ConvertUrnsToChannelItems(List<string> urns, CancellationToken cancellationToken)
|
||||
@@ -373,17 +432,63 @@ public class SRFPlayChannel : IChannel, IHasCacheKey
|
||||
// Generate deterministic GUID from URN
|
||||
var itemId = UrnToGuid(urn);
|
||||
|
||||
// Get stream URL and authenticate it
|
||||
var streamUrl = _streamResolver.GetStreamUrl(chapter, config.QualityPreference);
|
||||
|
||||
// For scheduled livestreams that haven't started, streamUrl might be null
|
||||
var isUpcomingLivestream = chapter.Type == "SCHEDULED_LIVESTREAM" && string.IsNullOrEmpty(streamUrl);
|
||||
|
||||
if (!string.IsNullOrEmpty(streamUrl))
|
||||
{
|
||||
// Authenticate the stream URL (required for all SRF streams, especially livestreams)
|
||||
streamUrl = await _streamResolver.GetAuthenticatedStreamUrlAsync(streamUrl, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
else if (isUpcomingLivestream)
|
||||
{
|
||||
// Use a placeholder for upcoming events
|
||||
streamUrl = "http://placeholder.local/upcoming.m3u8";
|
||||
_logger.LogInformation(
|
||||
"URN {Urn}: Upcoming livestream '{Title}' - stream will be available at {ValidFrom}",
|
||||
urn,
|
||||
chapter.Title,
|
||||
chapter.ValidFrom);
|
||||
}
|
||||
|
||||
// Build overview with event time for upcoming livestreams
|
||||
var overview = chapter.Description ?? chapter.Lead;
|
||||
if (isUpcomingLivestream && chapter.ValidFrom != null)
|
||||
{
|
||||
var eventStart = chapter.ValidFrom.Value.ToString("dd.MM.yyyy HH:mm", CultureInfo.InvariantCulture);
|
||||
overview = $"[Upcoming Event - Starts at {eventStart}]\n\n{overview}";
|
||||
}
|
||||
|
||||
// Get image URL - prefer chapter image, fall back to show image if available
|
||||
var imageUrl = chapter.ImageUrl;
|
||||
if (string.IsNullOrEmpty(imageUrl) && mediaComposition.Show != null)
|
||||
{
|
||||
imageUrl = mediaComposition.Show.ImageUrl;
|
||||
_logger.LogDebug("URN {Urn}: Using show image as fallback", urn);
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(imageUrl))
|
||||
{
|
||||
_logger.LogWarning("URN {Urn}: No image URL available for '{Title}'", urn, chapter.Title);
|
||||
}
|
||||
|
||||
// Use ValidFrom for premiere date if this is an upcoming livestream, otherwise use Date
|
||||
var premiereDate = isUpcomingLivestream ? chapter.ValidFrom?.ToUniversalTime() : chapter.Date?.ToUniversalTime();
|
||||
|
||||
var item = new ChannelItemInfo
|
||||
{
|
||||
Id = itemId,
|
||||
Name = chapter.Title,
|
||||
Overview = chapter.Description ?? chapter.Lead,
|
||||
ImageUrl = chapter.ImageUrl,
|
||||
Overview = overview,
|
||||
ImageUrl = imageUrl,
|
||||
Type = ChannelItemType.Media,
|
||||
ContentType = ChannelMediaContentType.Episode,
|
||||
MediaType = ChannelMediaType.Video,
|
||||
DateCreated = chapter.Date?.ToUniversalTime(),
|
||||
PremiereDate = chapter.Date?.ToUniversalTime(),
|
||||
PremiereDate = premiereDate,
|
||||
ProductionYear = chapter.Date?.Year,
|
||||
RunTimeTicks = chapter.Duration > 0 ? TimeSpan.FromMilliseconds(chapter.Duration).Ticks : null,
|
||||
ProviderIds = new Dictionary<string, string>
|
||||
@@ -396,7 +501,7 @@ public class SRFPlayChannel : IChannel, IHasCacheKey
|
||||
{
|
||||
Id = itemId,
|
||||
Name = chapter.Title,
|
||||
Path = _streamResolver.GetStreamUrl(chapter, config.QualityPreference),
|
||||
Path = streamUrl,
|
||||
Protocol = MediaBrowser.Model.MediaInfo.MediaProtocol.Http,
|
||||
Container = "m3u8",
|
||||
SupportsDirectStream = true,
|
||||
|
||||
Reference in New Issue
Block a user