using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using Jellyfin.Plugin.SRFPlay.Api; using Jellyfin.Plugin.SRFPlay.Api.Models; using Jellyfin.Plugin.SRFPlay.Services.Interfaces; using Microsoft.Extensions.Logging; namespace Jellyfin.Plugin.SRFPlay.Services; /// /// Service for managing topic/category data and filtering. /// public class CategoryService : ICategoryService { private readonly ILogger _logger; private readonly ISRFApiClientFactory _apiClientFactory; private readonly TimeSpan _topicsCacheDuration = TimeSpan.FromHours(24); private Dictionary? _topicsCache; private DateTime _topicsCacheExpiry = DateTime.MinValue; /// /// Initializes a new instance of the class. /// /// The logger. /// The API client factory. public CategoryService(ILogger logger, ISRFApiClientFactory apiClientFactory) { _logger = logger; _apiClientFactory = apiClientFactory; } /// public async Task> 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 = _apiClientFactory.CreateClient(); 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(); } /// public async Task 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); } /// public async Task> GetShowsByTopicAsync( string topicId, string businessUnit, int maxResults = 50, CancellationToken cancellationToken = default) { using var apiClient = _apiClientFactory.CreateClient(); 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(); } 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; } /// public void ClearCache() { _topicsCache = null; _topicsCacheExpiry = DateTime.MinValue; _logger.LogInformation("Topics cache cleared"); } /// /// Filters shows by topic ID. /// /// The shows to filter. /// The topic ID to filter by. /// Filtered list of shows. private static IReadOnlyList FilterShowsByTopic(IReadOnlyList shows, string topicId) { if (string.IsNullOrEmpty(topicId)) { return shows; } return shows .Where(s => s.TopicList != null && s.TopicList.Contains(topicId)) .ToList(); } }