Add a retention timer for podcast
This commit is contained in:
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Jellyfin.Plugin.Jellypod.Models;
|
||||
using Jellyfin.Plugin.Jellypod.Services;
|
||||
using MediaBrowser.Model.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
@@ -117,7 +118,22 @@ public class PodcastUpdateTask : IScheduledTask
|
||||
}
|
||||
|
||||
processedCount++;
|
||||
progress.Report((double)processedCount / totalPodcasts * 100);
|
||||
progress.Report((double)processedCount / totalPodcasts * 90 / 100);
|
||||
}
|
||||
|
||||
// Run cleanup for expired episodes
|
||||
_logger.LogInformation("Starting episode retention cleanup");
|
||||
foreach (var podcast in podcasts)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
try
|
||||
{
|
||||
await CleanupExpiredEpisodesAsync(podcast, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Failed to cleanup expired episodes for: {Title}", podcast.Title);
|
||||
}
|
||||
}
|
||||
|
||||
progress.Report(100);
|
||||
@@ -139,4 +155,78 @@ public class PodcastUpdateTask : IScheduledTask
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cleans up episodes that exceed the retention policy.
|
||||
/// </summary>
|
||||
/// <param name="podcast">The podcast to clean up.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task representing the asynchronous operation.</returns>
|
||||
private async Task CleanupExpiredEpisodesAsync(Podcast podcast, CancellationToken cancellationToken)
|
||||
{
|
||||
var config = Plugin.Instance?.Configuration;
|
||||
|
||||
// Determine effective max age for this podcast
|
||||
int effectiveMaxAgeDays;
|
||||
if (podcast.MaxEpisodeAgeDays == -1)
|
||||
{
|
||||
// Per-podcast override: unlimited
|
||||
return;
|
||||
}
|
||||
else if (podcast.MaxEpisodeAgeDays > 0)
|
||||
{
|
||||
// Per-podcast specific value
|
||||
effectiveMaxAgeDays = podcast.MaxEpisodeAgeDays;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use global setting (podcast.MaxEpisodeAgeDays == 0)
|
||||
effectiveMaxAgeDays = config?.MaxEpisodeAgeDays ?? 0;
|
||||
}
|
||||
|
||||
// 0 means unlimited
|
||||
if (effectiveMaxAgeDays <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var cutoffDate = DateTime.UtcNow.AddDays(-effectiveMaxAgeDays);
|
||||
var expiredEpisodes = podcast.Episodes
|
||||
.Where(e => e.Status == EpisodeStatus.Downloaded
|
||||
&& e.DownloadedDate.HasValue
|
||||
&& e.DownloadedDate.Value < cutoffDate)
|
||||
.ToList();
|
||||
|
||||
if (expiredEpisodes.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_logger.LogInformation(
|
||||
"Found {Count} expired episodes for {Podcast} (older than {Days} days)",
|
||||
expiredEpisodes.Count,
|
||||
podcast.Title,
|
||||
effectiveMaxAgeDays);
|
||||
|
||||
foreach (var episode in expiredEpisodes)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
try
|
||||
{
|
||||
await _downloadService.DeleteEpisodeFileAsync(episode).ConfigureAwait(false);
|
||||
_logger.LogDebug(
|
||||
"Deleted expired episode: {Title} (downloaded {Date})",
|
||||
episode.Title,
|
||||
episode.DownloadedDate);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Failed to delete expired episode: {Title}", episode.Title);
|
||||
}
|
||||
}
|
||||
|
||||
// Save changes to storage
|
||||
await _storageService.UpdatePodcastAsync(podcast).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user