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,4 +93,10 @@ public class Chapter
/// </summary>
[JsonPropertyName("mediaType")]
public string? MediaType { get; set; }
/// <summary>
/// Gets or sets the type (e.g., SCHEDULED_LIVESTREAM).
/// </summary>
[JsonPropertyName("type")]
public string? Type { get; set; }
}
@@ -0,0 +1,107 @@
using System;
using System.Collections.Generic;
using System.Text.Json.Serialization;
namespace Jellyfin.Plugin.SRFPlay.Api.Models.PlayV3;
/// <summary>
/// TV Program entry in the guide.
/// </summary>
public class PlayV3TvProgram
{
/// <summary>
/// Gets or sets the URN identifier.
/// </summary>
[JsonPropertyName("urn")]
public string? Urn { get; set; }
/// <summary>
/// Gets or sets the title.
/// </summary>
[JsonPropertyName("title")]
public string? Title { get; set; }
/// <summary>
/// Gets or sets the lead/description.
/// </summary>
[JsonPropertyName("lead")]
public string? Lead { get; set; }
/// <summary>
/// Gets or sets the description.
/// </summary>
[JsonPropertyName("description")]
public string? Description { get; set; }
/// <summary>
/// Gets or sets the image URL.
/// </summary>
[JsonPropertyName("imageUrl")]
public string? ImageUrl { get; set; }
/// <summary>
/// Gets or sets the content type (e.g., SCHEDULED_LIVESTREAM).
/// </summary>
[JsonPropertyName("type")]
public string? Type { get; set; }
/// <summary>
/// Gets or sets the media type.
/// </summary>
[JsonPropertyName("mediaType")]
public string? MediaType { get; set; }
/// <summary>
/// Gets or sets the vendor (e.g., SRF, RTS).
/// </summary>
[JsonPropertyName("vendor")]
public string? Vendor { get; set; }
/// <summary>
/// Gets or sets the start time.
/// </summary>
[JsonPropertyName("date")]
public DateTime? Date { get; set; }
/// <summary>
/// Gets or sets the valid from time (for scheduled livestreams).
/// </summary>
[JsonPropertyName("validFrom")]
public DateTime? ValidFrom { get; set; }
/// <summary>
/// Gets or sets the valid to time (for scheduled livestreams).
/// </summary>
[JsonPropertyName("validTo")]
public DateTime? ValidTo { get; set; }
/// <summary>
/// Gets or sets the duration in milliseconds.
/// </summary>
[JsonPropertyName("duration")]
public long? Duration { get; set; }
/// <summary>
/// Gets or sets the channel ID.
/// </summary>
[JsonPropertyName("channelId")]
public string? ChannelId { get; set; }
/// <summary>
/// Gets or sets the channel title.
/// </summary>
[JsonPropertyName("channelTitle")]
public string? ChannelTitle { get; set; }
/// <summary>
/// Gets or sets whether this is blocked (DRM).
/// </summary>
[JsonPropertyName("blocked")]
public bool? Blocked { get; set; }
/// <summary>
/// Gets or sets whether this is geoblocked.
/// </summary>
[JsonPropertyName("geoblocked")]
public bool? Geoblocked { get; set; }
}
@@ -0,0 +1,16 @@
using System.Collections.Generic;
using System.Text.Json.Serialization;
namespace Jellyfin.Plugin.SRFPlay.Api.Models.PlayV3;
/// <summary>
/// TV Program Guide response from Play v3 API.
/// </summary>
public class PlayV3TvProgramGuideResponse
{
/// <summary>
/// Gets the list of TV program entries.
/// </summary>
[JsonPropertyName("data")]
public IReadOnlyList<PlayV3TvProgram>? Data { get; init; }
}
@@ -6,6 +6,7 @@ using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Plugin.SRFPlay.Api.Models;
using Jellyfin.Plugin.SRFPlay.Api.Models.PlayV3;
using Jellyfin.Plugin.SRFPlay.Configuration;
using Microsoft.Extensions.Logging;
@@ -485,6 +486,58 @@ public class SRFApiClient : IDisposable
}
}
/// <summary>
/// Gets scheduled livestreams for sports or news events from the Play v3 API.
/// </summary>
/// <param name="businessUnit">The business unit (e.g., srf, rts).</param>
/// <param name="eventType">The event type (SPORT or NEWS).</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>List of scheduled livestream entries.</returns>
public async Task<System.Collections.Generic.IReadOnlyList<PlayV3TvProgram>?> GetScheduledLivestreamsAsync(
string businessUnit,
string eventType = "SPORT",
CancellationToken cancellationToken = default)
{
try
{
var baseUrl = string.Format(CultureInfo.InvariantCulture, PlayV3UrlFormat, businessUnit);
var url = $"{baseUrl}livestreams?eventType={eventType.ToUpperInvariant()}";
_logger.LogInformation("Fetching scheduled livestreams for eventType={EventType} from business unit: {BusinessUnit}", eventType, businessUnit);
var response = await _playV3HttpClient.GetAsync(url, cancellationToken).ConfigureAwait(false);
if (!response.IsSuccessStatusCode)
{
var errorContent = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false);
_logger.LogError("API returned error {StatusCode}: {Error}", response.StatusCode, errorContent);
return null;
}
var content = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false);
// The response structure is: { "data": { "scheduledLivestreams": [...] } }
var jsonDoc = JsonSerializer.Deserialize<JsonElement>(content, _jsonOptions);
if (jsonDoc.TryGetProperty("data", out var dataElement) &&
dataElement.TryGetProperty("scheduledLivestreams", out var livestreamsElement))
{
var livestreams = JsonSerializer.Deserialize<System.Collections.Generic.List<PlayV3TvProgram>>(
livestreamsElement.GetRawText(),
_jsonOptions);
_logger.LogInformation("Successfully fetched {Count} scheduled livestreams for eventType={EventType}", livestreams?.Count ?? 0, eventType);
return livestreams;
}
_logger.LogWarning("No scheduledLivestreams found in response");
return null;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error fetching scheduled livestreams for eventType={EventType} from business unit: {BusinessUnit}", eventType, businessUnit);
return null;
}
}
/// <inheritdoc/>
public void Dispose()
{