tokens refresh on media start
This commit is contained in:
parent
d48b515898
commit
8e86db100a
@ -61,7 +61,7 @@ public class SRFPlayChannel : IChannel, IHasCacheKey
|
|||||||
public string Description => "Swiss Radio and Television video-on-demand content";
|
public string Description => "Swiss Radio and Television video-on-demand content";
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string DataVersion => "1.0";
|
public string DataVersion => "1.1";
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string HomePageUrl => "https://www.srf.ch/play";
|
public string HomePageUrl => "https://www.srf.ch/play";
|
||||||
@ -433,36 +433,34 @@ public class SRFPlayChannel : IChannel, IHasCacheKey
|
|||||||
// Generate deterministic GUID from URN
|
// Generate deterministic GUID from URN
|
||||||
var itemId = UrnToGuid(urn);
|
var itemId = UrnToGuid(urn);
|
||||||
|
|
||||||
// Get stream URL and authenticate it
|
// Get stream URL (unauthenticated - token will be added at playback time by SRFMediaProvider)
|
||||||
var streamUrl = _streamResolver.GetStreamUrl(chapter, config.QualityPreference);
|
var streamUrl = _streamResolver.GetStreamUrl(chapter, config.QualityPreference);
|
||||||
|
|
||||||
// For scheduled livestreams that haven't started, streamUrl might be null
|
// Skip scheduled livestreams that haven't started yet (no stream URL available)
|
||||||
var isUpcomingLivestream = chapter.Type == "SCHEDULED_LIVESTREAM" && string.IsNullOrEmpty(streamUrl);
|
if (chapter.Type == "SCHEDULED_LIVESTREAM" && string.IsNullOrEmpty(streamUrl))
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(streamUrl))
|
|
||||||
{
|
{
|
||||||
// Authenticate the stream URL (required for all SRF streams, especially livestreams)
|
_logger.LogDebug(
|
||||||
streamUrl = await _streamResolver.GetAuthenticatedStreamUrlAsync(streamUrl, cancellationToken).ConfigureAwait(false);
|
"URN {Urn}: Skipping upcoming livestream '{Title}' - stream not yet available (starts at {ValidFrom})",
|
||||||
}
|
|
||||||
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,
|
urn,
|
||||||
chapter.Title,
|
chapter.Title,
|
||||||
chapter.ValidFrom);
|
chapter.ValidFrom);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build overview with event time for upcoming livestreams
|
// Skip items without a valid stream URL
|
||||||
var overview = chapter.Description ?? chapter.Lead;
|
if (string.IsNullOrEmpty(streamUrl))
|
||||||
if (isUpcomingLivestream && chapter.ValidFrom != null)
|
|
||||||
{
|
{
|
||||||
var eventStart = chapter.ValidFrom.Value.ToString("dd.MM.yyyy HH:mm", CultureInfo.InvariantCulture);
|
_logger.LogWarning(
|
||||||
overview = $"[Upcoming Event - Starts at {eventStart}]\n\n{overview}";
|
"URN {Urn}: Skipping '{Title}' - no valid stream URL available",
|
||||||
|
urn,
|
||||||
|
chapter.Title);
|
||||||
|
noStreamCount++;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Build overview
|
||||||
|
var overview = chapter.Description ?? chapter.Lead;
|
||||||
|
|
||||||
// Get image URL - prefer chapter image, fall back to show image if available
|
// Get image URL - prefer chapter image, fall back to show image if available
|
||||||
var imageUrl = chapter.ImageUrl;
|
var imageUrl = chapter.ImageUrl;
|
||||||
if (string.IsNullOrEmpty(imageUrl) && mediaComposition.Show != null)
|
if (string.IsNullOrEmpty(imageUrl) && mediaComposition.Show != null)
|
||||||
@ -476,9 +474,10 @@ public class SRFPlayChannel : IChannel, IHasCacheKey
|
|||||||
_logger.LogWarning("URN {Urn}: No image URL available for '{Title}'", urn, chapter.Title);
|
_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
|
// Use ValidFrom for premiere date if this is a scheduled livestream, otherwise use Date
|
||||||
var premiereDate = isUpcomingLivestream ? chapter.ValidFrom?.ToUniversalTime() : chapter.Date?.ToUniversalTime();
|
var premiereDate = chapter.Type == "SCHEDULED_LIVESTREAM" ? chapter.ValidFrom?.ToUniversalTime() : chapter.Date?.ToUniversalTime();
|
||||||
|
|
||||||
|
// Store unauthenticated URL as placeholder - SRFMediaProvider will provide authenticated version at playback
|
||||||
var item = new ChannelItemInfo
|
var item = new ChannelItemInfo
|
||||||
{
|
{
|
||||||
Id = itemId,
|
Id = itemId,
|
||||||
@ -502,14 +501,14 @@ public class SRFPlayChannel : IChannel, IHasCacheKey
|
|||||||
{
|
{
|
||||||
Id = itemId,
|
Id = itemId,
|
||||||
Name = chapter.Title,
|
Name = chapter.Title,
|
||||||
Path = streamUrl,
|
Path = streamUrl, // Unauthenticated URL - placeholder only
|
||||||
Protocol = MediaBrowser.Model.MediaInfo.MediaProtocol.Http,
|
Protocol = MediaBrowser.Model.MediaInfo.MediaProtocol.Http,
|
||||||
Container = "m3u8",
|
Container = "m3u8",
|
||||||
SupportsDirectStream = true,
|
SupportsDirectPlay = false, // Disable direct play - requires auth from provider
|
||||||
SupportsDirectPlay = true,
|
SupportsDirectStream = false, // Disable direct stream - requires auth from provider
|
||||||
SupportsTranscoding = true,
|
SupportsTranscoding = false, // Force use of IMediaSourceProvider
|
||||||
IsRemote = true,
|
IsRemote = true,
|
||||||
Type = MediaBrowser.Model.Dto.MediaSourceType.Default,
|
Type = MediaBrowser.Model.Dto.MediaSourceType.Placeholder, // Mark as placeholder
|
||||||
VideoType = VideoType.VideoFile
|
VideoType = VideoType.VideoFile
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -119,37 +119,11 @@ public class SRFMediaProvider : IMediaSourceProvider
|
|||||||
// Get stream URL based on quality preference
|
// Get stream URL based on quality preference
|
||||||
var streamUrl = _streamResolver.GetStreamUrl(chapter, config.QualityPreference);
|
var streamUrl = _streamResolver.GetStreamUrl(chapter, config.QualityPreference);
|
||||||
|
|
||||||
// Check if this is an upcoming livestream that hasn't started yet
|
// For scheduled livestreams, always fetch fresh data to ensure stream URL is current
|
||||||
var isUpcomingLivestream = chapter.Type == "SCHEDULED_LIVESTREAM" && string.IsNullOrEmpty(streamUrl);
|
if (chapter.Type == "SCHEDULED_LIVESTREAM" && string.IsNullOrEmpty(streamUrl))
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(streamUrl) && !isUpcomingLivestream)
|
|
||||||
{
|
{
|
||||||
_logger.LogWarning("Could not resolve stream URL for URN: {Urn}", urn);
|
_logger.LogDebug("URN {Urn}: Scheduled livestream has no stream URL, fetching fresh data", 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);
|
using var freshApiClient = new SRFApiClient(_loggerFactory);
|
||||||
var freshMediaComposition = freshApiClient.GetMediaCompositionByUrnAsync(urn, cancellationToken).GetAwaiter().GetResult();
|
var freshMediaComposition = freshApiClient.GetMediaCompositionByUrnAsync(urn, cancellationToken).GetAwaiter().GetResult();
|
||||||
|
|
||||||
@ -163,19 +137,15 @@ public class SRFMediaProvider : IMediaSourceProvider
|
|||||||
// Update cache with fresh data
|
// Update cache with fresh data
|
||||||
_metadataCache.SetMediaComposition(urn, freshMediaComposition);
|
_metadataCache.SetMediaComposition(urn, freshMediaComposition);
|
||||||
chapter = freshChapter;
|
chapter = freshChapter;
|
||||||
_logger.LogInformation("URN {Urn}: Livestream is now live, got fresh stream URL", urn);
|
_logger.LogInformation("URN {Urn}: Got fresh stream URL for scheduled livestream", 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);
|
if (string.IsNullOrEmpty(streamUrl))
|
||||||
return Task.FromResult<IEnumerable<MediaSourceInfo>>(sources);
|
{
|
||||||
}
|
_logger.LogWarning("Could not resolve stream URL for URN: {Urn}", urn);
|
||||||
|
return Task.FromResult<IEnumerable<MediaSourceInfo>>(sources);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Authenticate the stream URL (required for all SRF streams, especially livestreams)
|
// Authenticate the stream URL (required for all SRF streams, especially livestreams)
|
||||||
@ -188,13 +158,13 @@ public class SRFMediaProvider : IMediaSourceProvider
|
|||||||
// Create media source
|
// Create media source
|
||||||
var mediaSource = new MediaSourceInfo
|
var mediaSource = new MediaSourceInfo
|
||||||
{
|
{
|
||||||
Id = urn,
|
Id = item.Id.ToString(), // Use item GUID, not URN string (required for transcoding)
|
||||||
Name = chapter.Title,
|
Name = chapter.Title,
|
||||||
Path = streamUrl,
|
Path = streamUrl,
|
||||||
Protocol = MediaProtocol.Http,
|
Protocol = MediaProtocol.Http,
|
||||||
Container = "m3u8",
|
Container = "m3u8",
|
||||||
SupportsDirectStream = true,
|
SupportsDirectStream = true,
|
||||||
SupportsDirectPlay = true,
|
SupportsDirectPlay = false, // Disabled: auth tokens don't carry over to HLS segments in browser
|
||||||
SupportsTranscoding = true,
|
SupportsTranscoding = true,
|
||||||
IsRemote = true,
|
IsRemote = true,
|
||||||
Type = MediaSourceType.Default,
|
Type = MediaSourceType.Default,
|
||||||
|
|||||||
@ -144,23 +144,6 @@ public class StreamUrlResolver : IDisposable
|
|||||||
/// <returns>True if playable content is available.</returns>
|
/// <returns>True if playable content is available.</returns>
|
||||||
public bool HasPlayableContent(Chapter chapter)
|
public bool HasPlayableContent(Chapter chapter)
|
||||||
{
|
{
|
||||||
// For scheduled livestreams that haven't started yet, resources won't exist
|
|
||||||
// but we still want to show them. The stream will become available when the event starts.
|
|
||||||
if (chapter?.Type == "SCHEDULED_LIVESTREAM")
|
|
||||||
{
|
|
||||||
var now = DateTime.UtcNow;
|
|
||||||
var hasStarted = chapter.ValidFrom == null || chapter.ValidFrom.Value.ToUniversalTime() <= now;
|
|
||||||
|
|
||||||
if (!hasStarted)
|
|
||||||
{
|
|
||||||
_logger.LogInformation(
|
|
||||||
"Scheduled livestream '{Title}' hasn't started yet (starts at {ValidFrom}), will be playable when live",
|
|
||||||
chapter.Title,
|
|
||||||
chapter.ValidFrom);
|
|
||||||
return true; // Show it, stream will be available when event starts
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (chapter?.ResourceList == null || chapter.ResourceList.Count == 0)
|
if (chapter?.ResourceList == null || chapter.ResourceList.Count == 0)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user