Duncan Tourolle 7c76402a4a
Some checks failed
🏗️ Build Plugin / build (push) Failing after 9s
🧪 Test Plugin / test (push) Successful in 1m28s
🚀 Release Plugin / build-and-release (push) Failing after 5s
Clean up dead code, consolidate duplication, fix redundancies
Remove 9 dead methods, 6 unused constants, and redundant
ReaderWriterLockSlim from MetadataCache. Consolidate repeated
patterns into HasChapters, IsPlayable, and ToLowerString helpers.
Extract shared API methods in SRFApiClient. Move variant manifest
rewriting from controller to StreamProxyService. Make Auto quality
distinct from HD. Update README architecture section.
2026-02-28 11:34:45 +01:00

167 lines
5.6 KiB
C#

using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Plugin.SRFPlay.Services.Interfaces;
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 IMediaCompositionFetcher _compositionFetcher;
/// <summary>
/// Initializes a new instance of the <see cref="SRFEpisodeProvider"/> class.
/// </summary>
/// <param name="logger">The logger.</param>
/// <param name="compositionFetcher">The media composition fetcher.</param>
public SRFEpisodeProvider(
ILogger<SRFEpisodeProvider> logger,
IMediaCompositionFetcher compositionFetcher)
{
_logger = logger;
_compositionFetcher = compositionFetcher;
}
/// <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 mediaComposition = await _compositionFetcher.GetMediaCompositionAsync(urn, cancellationToken).ConfigureAwait(false);
if (mediaComposition?.HasChapters == true)
{
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 mediaComposition = await _compositionFetcher.GetMediaCompositionAsync(urn, cancellationToken).ConfigureAwait(false);
if (mediaComposition?.HasChapters != true)
{
_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");
}
}