213 lines
7.4 KiB
C#
213 lines
7.4 KiB
C#
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;
|
|
|
|
/// <summary>
|
|
/// Provides metadata for SRF Play episodes.
|
|
/// </summary>
|
|
public class SRFEpisodeProvider : IRemoteMetadataProvider<Episode, EpisodeInfo>
|
|
{
|
|
private readonly ILogger<SRFEpisodeProvider> _logger;
|
|
private readonly ILoggerFactory _loggerFactory;
|
|
private readonly IHttpClientFactory _httpClientFactory;
|
|
private readonly MetadataCache _metadataCache;
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="SRFEpisodeProvider"/> class.
|
|
/// </summary>
|
|
/// <param name="loggerFactory">The logger factory.</param>
|
|
/// <param name="httpClientFactory">The HTTP client factory.</param>
|
|
/// <param name="metadataCache">The metadata cache.</param>
|
|
public SRFEpisodeProvider(
|
|
ILoggerFactory loggerFactory,
|
|
IHttpClientFactory httpClientFactory,
|
|
MetadataCache metadataCache)
|
|
{
|
|
_loggerFactory = loggerFactory;
|
|
_logger = loggerFactory.CreateLogger<SRFEpisodeProvider>();
|
|
_httpClientFactory = httpClientFactory;
|
|
_metadataCache = metadataCache;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public string Name => "SRF Play";
|
|
|
|
/// <inheritdoc />
|
|
public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(EpisodeInfo searchInfo, CancellationToken cancellationToken)
|
|
{
|
|
var results = new List<RemoteSearchResult>();
|
|
|
|
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<string, string>
|
|
{
|
|
{ "SRF", chapter.Urn }
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Error searching for episode: {Name}", searchInfo.Name);
|
|
}
|
|
|
|
return results;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public async Task<MetadataResult<Episode>> GetMetadata(EpisodeInfo info, CancellationToken cancellationToken)
|
|
{
|
|
var result = new MetadataResult<Episode>();
|
|
|
|
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<string, string>
|
|
{
|
|
{ "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;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken)
|
|
{
|
|
throw new NotImplementedException("Image handling is done by SRFImageProvider");
|
|
}
|
|
}
|