using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Plugin.SRFPlay.Api;
using Jellyfin.Plugin.SRFPlay.Services;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Providers;
using Microsoft.Extensions.Logging;
namespace Jellyfin.Plugin.SRFPlay.Providers;
///
/// Provides metadata for SRF Play episodes.
///
public class SRFEpisodeProvider : IRemoteMetadataProvider
{
private readonly ILogger _logger;
private readonly ILoggerFactory _loggerFactory;
private readonly IHttpClientFactory _httpClientFactory;
private readonly MetadataCache _metadataCache;
///
/// Initializes a new instance of the class.
///
/// The logger factory.
/// The HTTP client factory.
/// The metadata cache.
public SRFEpisodeProvider(
ILoggerFactory loggerFactory,
IHttpClientFactory httpClientFactory,
MetadataCache metadataCache)
{
_loggerFactory = loggerFactory;
_logger = loggerFactory.CreateLogger();
_httpClientFactory = httpClientFactory;
_metadataCache = metadataCache;
}
///
public string Name => "SRF Play";
///
public async Task> GetSearchResults(EpisodeInfo searchInfo, CancellationToken cancellationToken)
{
var results = new List();
try
{
// Check if we have a URN to search with
if (searchInfo.ProviderIds.TryGetValue("SRF", out var urn) && !string.IsNullOrEmpty(urn))
{
_logger.LogDebug("Searching for episode with URN: {Urn}", urn);
var config = Plugin.Instance?.Configuration;
if (config == null)
{
return results;
}
// Try cache first
var mediaComposition = _metadataCache.GetMediaComposition(urn, config.CacheDurationMinutes);
// If not in cache, fetch from API
if (mediaComposition == null)
{
using var apiClient = new SRFApiClient(_loggerFactory);
mediaComposition = await apiClient.GetMediaCompositionByUrnAsync(urn, cancellationToken).ConfigureAwait(false);
if (mediaComposition != null)
{
_metadataCache.SetMediaComposition(urn, mediaComposition);
}
}
if (mediaComposition?.ChapterList != null && mediaComposition.ChapterList.Count > 0)
{
var chapter = mediaComposition.ChapterList[0];
results.Add(new RemoteSearchResult
{
Name = chapter.Title,
Overview = chapter.Description ?? chapter.Lead,
ImageUrl = chapter.ImageUrl,
SearchProviderName = Name,
ProviderIds = new Dictionary
{
{ "SRF", chapter.Urn }
}
});
}
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Error searching for episode: {Name}", searchInfo.Name);
}
return results;
}
///
public async Task> GetMetadata(EpisodeInfo info, CancellationToken cancellationToken)
{
var result = new MetadataResult();
try
{
// Check if we have a URN
if (!info.ProviderIds.TryGetValue("SRF", out var urn) || string.IsNullOrEmpty(urn))
{
_logger.LogDebug("No SRF URN found for episode: {Name}", info.Name);
return result;
}
_logger.LogDebug("Fetching metadata for episode URN: {Urn}", urn);
var config = Plugin.Instance?.Configuration;
if (config == null)
{
return result;
}
// Try cache first
var mediaComposition = _metadataCache.GetMediaComposition(urn, config.CacheDurationMinutes);
// If not in cache, fetch from API
if (mediaComposition == null)
{
using var apiClient = new SRFApiClient(_loggerFactory);
mediaComposition = await apiClient.GetMediaCompositionByUrnAsync(urn, cancellationToken).ConfigureAwait(false);
if (mediaComposition != null)
{
_metadataCache.SetMediaComposition(urn, mediaComposition);
}
}
if (mediaComposition?.ChapterList == null || mediaComposition.ChapterList.Count == 0)
{
_logger.LogWarning("No chapter information found for URN: {Urn}", urn);
return result;
}
// Get the first chapter (main video)
var chapter = mediaComposition.ChapterList[0];
result.Item = new Episode
{
Name = chapter.Title,
Overview = chapter.Description ?? chapter.Lead,
ProviderIds = new Dictionary
{
{ "SRF", chapter.Urn }
}
};
// Set episode and season numbers if available
if (chapter.EpisodeNumber.HasValue)
{
result.Item.IndexNumber = chapter.EpisodeNumber;
}
if (chapter.SeasonNumber.HasValue)
{
result.Item.ParentIndexNumber = chapter.SeasonNumber;
}
// Set premiere date if available
if (chapter.Date.HasValue)
{
result.Item.PremiereDate = chapter.Date;
}
// Set runtime (convert from milliseconds to ticks)
if (chapter.Duration > 0)
{
result.Item.RunTimeTicks = TimeSpan.FromMilliseconds(chapter.Duration).Ticks;
}
// Set series information if available
if (mediaComposition.Show != null)
{
result.Item.SeriesName = mediaComposition.Show.Title;
// Set series provider ID on the episode
if (!string.IsNullOrEmpty(mediaComposition.Show.Urn))
{
result.Item.SetProviderId("SRF_Series", mediaComposition.Show.Urn);
}
}
result.HasMetadata = true;
_logger.LogDebug("Successfully fetched metadata for episode: {Title}", chapter.Title);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error fetching metadata for episode: {Name}", info.Name);
}
return result;
}
///
public Task GetImageResponse(string url, CancellationToken cancellationToken)
{
throw new NotImplementedException("Image handling is done by SRFImageProvider");
}
}