207 lines
7.2 KiB
C#
207 lines
7.2 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
using System.Linq;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using Jellyfin.Plugin.SRFPlay.Api;
|
|
using Jellyfin.Plugin.SRFPlay.Api.Models;
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
namespace Jellyfin.Plugin.SRFPlay.Services;
|
|
|
|
/// <summary>
|
|
/// Service for managing topic/category data and filtering.
|
|
/// </summary>
|
|
public class CategoryService
|
|
{
|
|
private readonly ILogger _logger;
|
|
private readonly ILoggerFactory _loggerFactory;
|
|
private readonly TimeSpan _topicsCacheDuration = TimeSpan.FromHours(24);
|
|
private Dictionary<string, PlayV3Topic>? _topicsCache;
|
|
private DateTime _topicsCacheExpiry = DateTime.MinValue;
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="CategoryService"/> class.
|
|
/// </summary>
|
|
/// <param name="loggerFactory">The logger factory.</param>
|
|
public CategoryService(ILoggerFactory loggerFactory)
|
|
{
|
|
_loggerFactory = loggerFactory;
|
|
_logger = loggerFactory.CreateLogger<CategoryService>();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets all topics for a business unit.
|
|
/// </summary>
|
|
/// <param name="businessUnit">The business unit.</param>
|
|
/// <param name="cancellationToken">The cancellation token.</param>
|
|
/// <returns>List of topics.</returns>
|
|
public async Task<List<PlayV3Topic>> GetTopicsAsync(string businessUnit, CancellationToken cancellationToken = default)
|
|
{
|
|
// Return cached topics if still valid
|
|
if (_topicsCache != null && DateTime.UtcNow < _topicsCacheExpiry)
|
|
{
|
|
_logger.LogDebug("Returning cached topics for business unit: {BusinessUnit}", businessUnit);
|
|
return _topicsCache.Values.ToList();
|
|
}
|
|
|
|
_logger.LogInformation("Fetching topics for business unit: {BusinessUnit}", businessUnit);
|
|
using var apiClient = new SRFApiClient(_loggerFactory);
|
|
var topics = await apiClient.GetAllTopicsAsync(businessUnit, cancellationToken).ConfigureAwait(false);
|
|
|
|
if (topics != null && topics.Count > 0)
|
|
{
|
|
// Cache topics by ID for quick lookups
|
|
_topicsCache = topics
|
|
.Where(t => !string.IsNullOrEmpty(t.Id))
|
|
.ToDictionary(t => t.Id!, t => t);
|
|
_topicsCacheExpiry = DateTime.UtcNow.Add(_topicsCacheDuration);
|
|
|
|
_logger.LogInformation("Cached {Count} topics for business unit: {BusinessUnit}", _topicsCache.Count, businessUnit);
|
|
}
|
|
|
|
return topics ?? new List<PlayV3Topic>();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a topic by ID.
|
|
/// </summary>
|
|
/// <param name="topicId">The topic ID.</param>
|
|
/// <param name="businessUnit">The business unit.</param>
|
|
/// <param name="cancellationToken">The cancellation token.</param>
|
|
/// <returns>The topic, or null if not found.</returns>
|
|
public async Task<PlayV3Topic?> GetTopicByIdAsync(string topicId, string businessUnit, CancellationToken cancellationToken = default)
|
|
{
|
|
// Ensure topics are loaded
|
|
if (_topicsCache == null || DateTime.UtcNow >= _topicsCacheExpiry)
|
|
{
|
|
await GetTopicsAsync(businessUnit, cancellationToken).ConfigureAwait(false);
|
|
}
|
|
|
|
return _topicsCache?.GetValueOrDefault(topicId);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Filters shows by topic ID.
|
|
/// </summary>
|
|
/// <param name="shows">The shows to filter.</param>
|
|
/// <param name="topicId">The topic ID to filter by.</param>
|
|
/// <returns>Filtered list of shows.</returns>
|
|
public IReadOnlyList<PlayV3Show> FilterShowsByTopic(IReadOnlyList<PlayV3Show> shows, string topicId)
|
|
{
|
|
if (string.IsNullOrEmpty(topicId))
|
|
{
|
|
return shows;
|
|
}
|
|
|
|
return shows
|
|
.Where(s => s.TopicList != null && s.TopicList.Contains(topicId))
|
|
.ToList();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Groups shows by their topics.
|
|
/// </summary>
|
|
/// <param name="shows">The shows to group.</param>
|
|
/// <returns>Dictionary mapping topic IDs to shows.</returns>
|
|
public IReadOnlyDictionary<string, List<PlayV3Show>> GroupShowsByTopics(IReadOnlyList<PlayV3Show> shows)
|
|
{
|
|
var groupedShows = new Dictionary<string, List<PlayV3Show>>();
|
|
|
|
foreach (var show in shows)
|
|
{
|
|
if (show.TopicList == null || show.TopicList.Count == 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
foreach (var topicId in show.TopicList)
|
|
{
|
|
if (!groupedShows.TryGetValue(topicId, out var showList))
|
|
{
|
|
showList = new List<PlayV3Show>();
|
|
groupedShows[topicId] = showList;
|
|
}
|
|
|
|
showList.Add(show);
|
|
}
|
|
}
|
|
|
|
return groupedShows;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets shows for a specific topic, sorted by number of episodes.
|
|
/// </summary>
|
|
/// <param name="topicId">The topic ID.</param>
|
|
/// <param name="businessUnit">The business unit.</param>
|
|
/// <param name="maxResults">Maximum number of results to return.</param>
|
|
/// <param name="cancellationToken">The cancellation token.</param>
|
|
/// <returns>List of shows for the topic.</returns>
|
|
public async Task<List<PlayV3Show>> GetShowsByTopicAsync(
|
|
string topicId,
|
|
string businessUnit,
|
|
int maxResults = 50,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
using var apiClient = new SRFApiClient(_loggerFactory);
|
|
var allShows = await apiClient.GetAllShowsAsync(businessUnit, cancellationToken).ConfigureAwait(false);
|
|
|
|
if (allShows == null || allShows.Count == 0)
|
|
{
|
|
_logger.LogWarning("No shows available for business unit: {BusinessUnit}", businessUnit);
|
|
return new List<PlayV3Show>();
|
|
}
|
|
|
|
var filteredShows = FilterShowsByTopic(allShows, topicId)
|
|
.Where(s => s.NumberOfEpisodes > 0)
|
|
.OrderByDescending(s => s.NumberOfEpisodes)
|
|
.Take(maxResults)
|
|
.ToList();
|
|
|
|
_logger.LogDebug("Found {Count} shows for topic {TopicId}", filteredShows.Count, topicId);
|
|
return filteredShows;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets video count for each topic.
|
|
/// </summary>
|
|
/// <param name="shows">The shows to analyze.</param>
|
|
/// <returns>Dictionary mapping topic IDs to video counts.</returns>
|
|
public IReadOnlyDictionary<string, int> GetVideoCountByTopic(IReadOnlyList<PlayV3Show> shows)
|
|
{
|
|
var topicCounts = new Dictionary<string, int>();
|
|
|
|
foreach (var show in shows)
|
|
{
|
|
if (show.TopicList == null || show.TopicList.Count == 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
foreach (var topicId in show.TopicList)
|
|
{
|
|
if (!topicCounts.TryGetValue(topicId, out var count))
|
|
{
|
|
count = 0;
|
|
}
|
|
|
|
topicCounts[topicId] = count + show.NumberOfEpisodes;
|
|
}
|
|
}
|
|
|
|
return topicCounts;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Clears the topics cache.
|
|
/// </summary>
|
|
public void ClearCache()
|
|
{
|
|
_topicsCache = null;
|
|
_topicsCacheExpiry = DateTime.MinValue;
|
|
_logger.LogInformation("Topics cache cleared");
|
|
}
|
|
}
|