Duncan Tourolle 4679b77d1a
Some checks failed
🏗️ Build Plugin / call (push) Failing after 0s
📝 Create/Update Release Draft & Release Bump PR / call (push) Failing after 0s
🔬 Run CodeQL / call (push) Failing after 0s
🧪 Test Plugin / call (push) Failing after 0s
First POC with podcasts library
2025-12-13 23:57:58 +01:00

143 lines
5.1 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Plugin.Jellypod.Services;
using MediaBrowser.Model.Tasks;
using Microsoft.Extensions.Logging;
namespace Jellyfin.Plugin.Jellypod.ScheduledTasks;
/// <summary>
/// Scheduled task that updates all podcast feeds and downloads new episodes.
/// </summary>
public class PodcastUpdateTask : IScheduledTask
{
private readonly ILogger<PodcastUpdateTask> _logger;
private readonly IRssFeedService _rssFeedService;
private readonly IPodcastStorageService _storageService;
private readonly IPodcastDownloadService _downloadService;
/// <summary>
/// Initializes a new instance of the <see cref="PodcastUpdateTask"/> class.
/// </summary>
/// <param name="logger">Logger instance.</param>
/// <param name="rssFeedService">RSS feed service.</param>
/// <param name="storageService">Storage service.</param>
/// <param name="downloadService">Download service.</param>
public PodcastUpdateTask(
ILogger<PodcastUpdateTask> logger,
IRssFeedService rssFeedService,
IPodcastStorageService storageService,
IPodcastDownloadService downloadService)
{
_logger = logger;
_rssFeedService = rssFeedService;
_storageService = storageService;
_downloadService = downloadService;
}
/// <inheritdoc />
public string Name => "Update Podcast Feeds";
/// <inheritdoc />
public string Key => "JellypodUpdateFeeds";
/// <inheritdoc />
public string Description => "Checks all subscribed podcasts for new episodes and downloads them.";
/// <inheritdoc />
public string Category => "Jellypod";
/// <inheritdoc />
public async Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken)
{
_logger.LogInformation("Starting podcast feed update task");
var podcasts = await _storageService.GetAllPodcastsAsync().ConfigureAwait(false);
var totalPodcasts = podcasts.Count;
var processedCount = 0;
foreach (var podcast in podcasts)
{
cancellationToken.ThrowIfCancellationRequested();
try
{
_logger.LogDebug("Updating podcast: {Title}", podcast.Title);
var updatedPodcast = await _rssFeedService.FetchPodcastAsync(podcast.FeedUrl, cancellationToken).ConfigureAwait(false);
if (updatedPodcast != null)
{
// Find new episodes (by GUID)
var existingGuids = podcast.Episodes
.Select(e => e.EpisodeGuid)
.Where(g => !string.IsNullOrEmpty(g))
.ToHashSet(StringComparer.Ordinal);
var newEpisodes = updatedPodcast.Episodes
.Where(e => !string.IsNullOrEmpty(e.EpisodeGuid) && !existingGuids.Contains(e.EpisodeGuid))
.ToList();
if (newEpisodes.Count > 0)
{
_logger.LogInformation("Found {Count} new episodes for {Title}", newEpisodes.Count, podcast.Title);
// Add new episodes to podcast
foreach (var episode in newEpisodes)
{
episode.PodcastId = podcast.Id;
podcast.Episodes.Insert(0, episode);
}
podcast.LastUpdated = DateTime.UtcNow;
// Auto-download if enabled
var config = Plugin.Instance?.Configuration;
if (config?.GlobalAutoDownloadEnabled == true && podcast.AutoDownloadEnabled)
{
foreach (var episode in newEpisodes)
{
await _downloadService.QueueDownloadAsync(podcast, episode).ConfigureAwait(false);
}
}
await _storageService.UpdatePodcastAsync(podcast).ConfigureAwait(false);
}
else
{
_logger.LogDebug("No new episodes for {Title}", podcast.Title);
}
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to update podcast: {Title}", podcast.Title);
}
processedCount++;
progress.Report((double)processedCount / totalPodcasts * 100);
}
progress.Report(100);
_logger.LogInformation("Podcast feed update task completed");
}
/// <inheritdoc />
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
{
var config = Plugin.Instance?.Configuration;
var intervalHours = config?.UpdateIntervalHours ?? 6;
return new[]
{
new TaskTriggerInfo
{
Type = TaskTriggerInfo.TriggerInterval,
IntervalTicks = TimeSpan.FromHours(intervalHours).Ticks
}
};
}
}