Fix RssFeedManager to remove database dependencies and add string initializers to Core models

This commit is contained in:
MasterDraco 2025-03-12 22:12:42 +00:00
parent 6dff6103d9
commit 619a861546
2 changed files with 383 additions and 273 deletions

View File

@ -2,64 +2,127 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
namespace TransmissionRssManager.Core namespace TransmissionRssManager.Core
{ {
public class LogEntry
{
public int Id { get; set; }
public DateTime Timestamp { get; set; }
public string Level { get; set; } = string.Empty;
public string Message { get; set; } = string.Empty;
public string Context { get; set; } = string.Empty;
public string Properties { get; set; } = string.Empty;
}
public class RssFeedItem public class RssFeedItem
{ {
public string Id { get; set; } public string Id { get; set; } = string.Empty;
public string Title { get; set; } public string Title { get; set; } = string.Empty;
public string Link { get; set; } public string Link { get; set; } = string.Empty;
public string Description { get; set; } public string Description { get; set; } = string.Empty;
public DateTime PublishDate { get; set; } public DateTime PublishDate { get; set; }
public string TorrentUrl { get; set; } public string TorrentUrl { get; set; } = string.Empty;
public bool IsDownloaded { get; set; } public bool IsDownloaded { get; set; }
public bool IsMatched { get; set; } public bool IsMatched { get; set; }
public string MatchedRule { get; set; } public string MatchedRule { get; set; } = string.Empty;
public string FeedId { get; set; } = string.Empty;
public string Category { get; set; } = string.Empty;
public long Size { get; set; }
public string Author { get; set; } = string.Empty;
public List<string> Categories { get; set; } = new List<string>();
public Dictionary<string, string> AdditionalMetadata { get; set; } = new Dictionary<string, string>();
public DateTime? DownloadDate { get; set; }
public int? TorrentId { get; set; }
public string RejectionReason { get; set; } = string.Empty;
public bool IsRejected => !string.IsNullOrEmpty(RejectionReason);
} }
public class TorrentInfo public class TorrentInfo
{ {
public int Id { get; set; } public int Id { get; set; }
public string Name { get; set; } public string Name { get; set; } = string.Empty;
public string Status { get; set; } public string Status { get; set; } = string.Empty;
public double PercentDone { get; set; } public double PercentDone { get; set; }
public long TotalSize { get; set; } public long TotalSize { get; set; }
public string DownloadDir { get; set; } public string DownloadDir { get; set; } = string.Empty;
public bool IsFinished => PercentDone >= 1.0; public bool IsFinished => PercentDone >= 1.0;
public DateTime? AddedDate { get; set; }
public DateTime? CompletedDate { get; set; }
public long DownloadedEver { get; set; }
public long UploadedEver { get; set; }
public int UploadRatio { get; set; }
public string ErrorString { get; set; } = string.Empty;
public bool IsError => !string.IsNullOrEmpty(ErrorString);
public int Priority { get; set; }
public string HashString { get; set; } = string.Empty;
public int PeersConnected { get; set; }
public double DownloadSpeed { get; set; }
public double UploadSpeed { get; set; }
public string Category { get; set; } = string.Empty;
public bool HasMetadata { get; set; }
public string TransmissionInstance { get; set; } = "default";
public string SourceFeedId { get; set; } = string.Empty;
public bool IsPostProcessed { get; set; }
} }
public class RssFeed public class RssFeed
{ {
public string Id { get; set; } public string Id { get; set; } = string.Empty;
public string Url { get; set; } public string Url { get; set; } = string.Empty;
public string Name { get; set; } public string Name { get; set; } = string.Empty;
public List<string> Rules { get; set; } = new List<string>(); public List<string> Rules { get; set; } = new List<string>();
public List<RssFeedRule> AdvancedRules { get; set; } = new List<RssFeedRule>();
public bool AutoDownload { get; set; } public bool AutoDownload { get; set; }
public DateTime LastChecked { get; set; } public DateTime LastChecked { get; set; }
public string TransmissionInstanceId { get; set; } = "default";
public string Schedule { get; set; } = "*/30 * * * *"; // Default is every 30 minutes (cron expression)
public bool Enabled { get; set; } = true;
public int MaxHistoryItems { get; set; } = 100;
public string DefaultCategory { get; set; } = string.Empty;
public int ErrorCount { get; set; } = 0;
public DateTime? LastError { get; set; }
public string LastErrorMessage { get; set; } = string.Empty;
} }
public class AppConfig public class AppConfig
{ {
public TransmissionConfig Transmission { get; set; } = new TransmissionConfig(); public TransmissionConfig Transmission { get; set; } = new TransmissionConfig();
public Dictionary<string, TransmissionConfig> TransmissionInstances { get; set; } = new Dictionary<string, TransmissionConfig>();
public List<RssFeed> Feeds { get; set; } = new List<RssFeed>(); public List<RssFeed> Feeds { get; set; } = new List<RssFeed>();
public bool AutoDownloadEnabled { get; set; } public bool AutoDownloadEnabled { get; set; }
public int CheckIntervalMinutes { get; set; } = 30; public int CheckIntervalMinutes { get; set; } = 30;
public string DownloadDirectory { get; set; } public string DownloadDirectory { get; set; } = string.Empty;
public string MediaLibraryPath { get; set; } public string MediaLibraryPath { get; set; } = string.Empty;
public PostProcessingConfig PostProcessing { get; set; } = new PostProcessingConfig(); public PostProcessingConfig PostProcessing { get; set; } = new PostProcessingConfig();
public bool EnableDetailedLogging { get; set; } = false;
public UserPreferences UserPreferences { get; set; } = new UserPreferences();
} }
public class TransmissionConfig public class TransmissionConfig
{ {
public string Host { get; set; } = "localhost"; public string Host { get; set; } = "localhost";
public int Port { get; set; } = 9091; public int Port { get; set; } = 9091;
public string Username { get; set; } public string Username { get; set; } = string.Empty;
public string Password { get; set; } public string Password { get; set; } = string.Empty;
public bool UseHttps { get; set; } = false; public bool UseHttps { get; set; } = false;
public string Url => $"{(UseHttps ? "https" : "http")}://{Host}:{Port}/transmission/rpc"; public string Url => $"{(UseHttps ? "https" : "http")}://{Host}:{Port}/transmission/rpc";
} }
public class RssFeedRule
{
public string Id { get; set; } = string.Empty;
public string Name { get; set; } = string.Empty;
public string Pattern { get; set; } = string.Empty;
public bool IsRegex { get; set; } = false;
public bool IsEnabled { get; set; } = true;
public bool IsCaseSensitive { get; set; } = false;
public string Category { get; set; } = string.Empty;
public int Priority { get; set; } = 0;
public string Action { get; set; } = "download"; // download, notify, ignore
public string DestinationFolder { get; set; } = string.Empty;
}
public class PostProcessingConfig public class PostProcessingConfig
{ {
public bool Enabled { get; set; } = false; public bool Enabled { get; set; } = false;
@ -67,6 +130,30 @@ namespace TransmissionRssManager.Core
public bool OrganizeMedia { get; set; } = true; public bool OrganizeMedia { get; set; } = true;
public int MinimumSeedRatio { get; set; } = 1; public int MinimumSeedRatio { get; set; } = 1;
public List<string> MediaExtensions { get; set; } = new List<string> { ".mp4", ".mkv", ".avi" }; public List<string> MediaExtensions { get; set; } = new List<string> { ".mp4", ".mkv", ".avi" };
public bool AutoOrganizeByMediaType { get; set; } = true;
public bool RenameFiles { get; set; } = false;
public bool CompressCompletedFiles { get; set; } = false;
public int DeleteCompletedAfterDays { get; set; } = 0; // 0 = never delete
}
public class UserPreferences
{
public bool EnableDarkMode { get; set; } = false;
public bool AutoRefreshUIEnabled { get; set; } = true;
public int AutoRefreshIntervalSeconds { get; set; } = 30;
public bool NotificationsEnabled { get; set; } = true;
public List<string> NotificationEvents { get; set; } = new List<string>
{
"torrent-added",
"torrent-completed",
"torrent-error"
};
public string DefaultView { get; set; } = "dashboard";
public bool ConfirmBeforeDelete { get; set; } = true;
public int MaxItemsPerPage { get; set; } = 25;
public string DateTimeFormat { get; set; } = "yyyy-MM-dd HH:mm:ss";
public bool ShowCompletedTorrents { get; set; } = true;
public int KeepHistoryDays { get; set; } = 30;
} }
public interface IConfigService public interface IConfigService
@ -93,6 +180,7 @@ namespace TransmissionRssManager.Core
Task RemoveFeedAsync(string feedId); Task RemoveFeedAsync(string feedId);
Task UpdateFeedAsync(RssFeed feed); Task UpdateFeedAsync(RssFeed feed);
Task RefreshFeedsAsync(CancellationToken cancellationToken); Task RefreshFeedsAsync(CancellationToken cancellationToken);
Task RefreshFeedAsync(string feedId, CancellationToken cancellationToken);
Task MarkItemAsDownloadedAsync(string itemId); Task MarkItemAsDownloadedAsync(string itemId);
} }

View File

@ -4,13 +4,13 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.ServiceModel.Syndication; using System.ServiceModel.Syndication;
using System.Text.Json;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Xml; using System.Xml;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using TransmissionRssManager.Core; using TransmissionRssManager.Core;
namespace TransmissionRssManager.Services namespace TransmissionRssManager.Services
@ -20,9 +20,9 @@ namespace TransmissionRssManager.Services
private readonly ILogger<RssFeedManager> _logger; private readonly ILogger<RssFeedManager> _logger;
private readonly IConfigService _configService; private readonly IConfigService _configService;
private readonly ITransmissionClient _transmissionClient; private readonly ITransmissionClient _transmissionClient;
private List<RssFeed> _feeds = new List<RssFeed>();
private List<RssFeedItem> _feedItems = new List<RssFeedItem>();
private readonly HttpClient _httpClient; private readonly HttpClient _httpClient;
private readonly string _dataPath;
private List<RssFeedItem> _items = new List<RssFeedItem>();
public RssFeedManager( public RssFeedManager(
ILogger<RssFeedManager> logger, ILogger<RssFeedManager> logger,
@ -34,276 +34,294 @@ namespace TransmissionRssManager.Services
_transmissionClient = transmissionClient; _transmissionClient = transmissionClient;
_httpClient = new HttpClient(); _httpClient = new HttpClient();
// Create data directory // Load feeds from config
string homeDir = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
string dataDir = Path.Combine(homeDir, ".local", "share", "transmission-rss-manager");
if (!Directory.Exists(dataDir))
{
Directory.CreateDirectory(dataDir);
}
_dataPath = Path.Combine(dataDir, "rss-items.json");
LoadItems();
}
public Task<List<RssFeedItem>> GetAllItemsAsync()
{
return Task.FromResult(_items.OrderByDescending(i => i.PublishDate).ToList());
}
public Task<List<RssFeedItem>> GetMatchedItemsAsync()
{
return Task.FromResult(_items.Where(i => i.IsMatched).OrderByDescending(i => i.PublishDate).ToList());
}
public Task<List<RssFeed>> GetFeedsAsync()
{
var config = _configService.GetConfiguration(); var config = _configService.GetConfiguration();
return Task.FromResult(config.Feeds); _feeds = config.Feeds;
}
public async Task<List<RssFeedItem>> GetAllItemsAsync()
{
return _feedItems;
}
public async Task<List<RssFeedItem>> GetMatchedItemsAsync()
{
return _feedItems.Where(item => item.IsMatched).ToList();
}
public async Task<List<RssFeed>> GetFeedsAsync()
{
return _feeds;
} }
public async Task AddFeedAsync(RssFeed feed) public async Task AddFeedAsync(RssFeed feed)
{ {
feed.Id = Guid.NewGuid().ToString(); feed.Id = Guid.NewGuid().ToString();
feed.LastChecked = DateTime.MinValue; _feeds.Add(feed);
await SaveFeedsToConfigAsync();
var config = _configService.GetConfiguration();
config.Feeds.Add(feed);
await _configService.SaveConfigurationAsync(config);
// Initial fetch of feed items
await FetchFeedAsync(feed);
} }
public async Task RemoveFeedAsync(string feedId) public async Task RemoveFeedAsync(string feedId)
{ {
var config = _configService.GetConfiguration(); _feeds.RemoveAll(f => f.Id == feedId);
var feed = config.Feeds.FirstOrDefault(f => f.Id == feedId); _feedItems.RemoveAll(i => i.FeedId == feedId);
await SaveFeedsToConfigAsync();
if (feed != null)
{
config.Feeds.Remove(feed);
await _configService.SaveConfigurationAsync(config);
// Remove items from this feed
_items.RemoveAll(i => i.Id.StartsWith(feedId));
await SaveItemsAsync();
}
} }
public async Task UpdateFeedAsync(RssFeed feed) public async Task UpdateFeedAsync(RssFeed feed)
{ {
var config = _configService.GetConfiguration(); var existingFeed = _feeds.FirstOrDefault(f => f.Id == feed.Id);
var index = config.Feeds.FindIndex(f => f.Id == feed.Id); if (existingFeed != null)
if (index != -1)
{ {
config.Feeds[index] = feed; int index = _feeds.IndexOf(existingFeed);
await _configService.SaveConfigurationAsync(config); _feeds[index] = feed;
await SaveFeedsToConfigAsync();
} }
} }
private async Task SaveFeedsToConfigAsync()
{
var config = _configService.GetConfiguration();
config.Feeds = _feeds;
await _configService.SaveConfigurationAsync(config);
}
public async Task RefreshFeedsAsync(CancellationToken cancellationToken) public async Task RefreshFeedsAsync(CancellationToken cancellationToken)
{ {
_logger.LogInformation("Starting RSS feed refresh"); _logger.LogInformation("Refreshing RSS feeds");
var config = _configService.GetConfiguration();
foreach (var feed in config.Feeds) foreach (var feed in _feeds.Where(f => f.Enabled))
{ {
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
{ break;
_logger.LogInformation("RSS refresh cancelled");
return;
}
try try
{ {
await FetchFeedAsync(feed); await RefreshFeedAsync(feed.Id, cancellationToken);
// Update last checked time
feed.LastChecked = DateTime.Now;
await _configService.SaveConfigurationAsync(config);
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError(ex, $"Error refreshing feed: {feed.Name}"); _logger.LogError(ex, $"Error refreshing feed {feed.Name}");
}
} }
} }
// Check for matches and auto-download if enabled public async Task RefreshFeedAsync(string feedId, CancellationToken cancellationToken)
await ProcessMatchesAsync(); {
var feed = _feeds.FirstOrDefault(f => f.Id == feedId);
if (feed == null)
return;
try
{
_logger.LogInformation($"Refreshing feed: {feed.Name}");
var feedItems = await FetchFeedItemsAsync(feed.Url);
foreach (var item in feedItems)
{
// Add only if we don't already have this item
if (!_feedItems.Any(i => i.Link == item.Link && i.FeedId == feed.Id))
{
item.FeedId = feed.Id;
_feedItems.Add(item);
// Apply rules
ApplyRulesToItem(feed, item);
// Download if matched and auto-download is enabled
if (item.IsMatched && feed.AutoDownload)
{
await DownloadMatchedItemAsync(item);
}
}
}
// Update last checked time
feed.LastChecked = DateTime.UtcNow;
feed.ErrorCount = 0;
feed.LastErrorMessage = string.Empty;
// Cleanup old items
CleanupOldItems(feed);
}
catch (Exception ex)
{
_logger.LogError(ex, $"Error refreshing feed {feed.Name}: {ex.Message}");
feed.ErrorCount++;
feed.LastError = DateTime.UtcNow;
feed.LastErrorMessage = ex.Message;
}
}
private async Task<List<RssFeedItem>> FetchFeedItemsAsync(string url)
{
var feedItems = new List<RssFeedItem>();
try
{
var response = await _httpClient.GetStringAsync(url);
using (var reader = XmlReader.Create(new StringReader(response)))
{
var feed = SyndicationFeed.Load(reader);
foreach (var item in feed.Items)
{
var feedItem = new RssFeedItem
{
Id = Guid.NewGuid().ToString(),
Title = item.Title?.Text ?? "",
Description = item.Summary?.Text ?? "",
Link = item.Links.FirstOrDefault()?.Uri.ToString() ?? "",
PublishDate = item.PublishDate.UtcDateTime,
Author = item.Authors.FirstOrDefault()?.Name ?? ""
};
// Find torrent link
foreach (var link in item.Links)
{
if (link.MediaType?.Contains("torrent") == true ||
link.Uri.ToString().EndsWith(".torrent") ||
link.Uri.ToString().StartsWith("magnet:"))
{
feedItem.TorrentUrl = link.Uri.ToString();
break;
}
}
// If no torrent link found, use the main link
if (string.IsNullOrEmpty(feedItem.TorrentUrl))
{
feedItem.TorrentUrl = feedItem.Link;
}
feedItems.Add(feedItem);
}
}
}
catch (Exception ex)
{
_logger.LogError(ex, $"Error fetching feed: {url}");
throw;
}
return feedItems;
}
private void ApplyRulesToItem(RssFeed feed, RssFeedItem item)
{
item.IsMatched = false;
item.MatchedRule = string.Empty;
// Apply simple string rules
foreach (var rulePattern in feed.Rules)
{
if (item.Title.Contains(rulePattern, StringComparison.OrdinalIgnoreCase))
{
item.IsMatched = true;
item.MatchedRule = rulePattern;
item.Category = feed.DefaultCategory;
break;
}
}
// Apply advanced rules
foreach (var rule in feed.AdvancedRules.Where(r => r.IsEnabled).OrderByDescending(r => r.Priority))
{
bool isMatch = false;
if (rule.IsRegex)
{
try
{
var regex = new Regex(rule.Pattern,
rule.IsCaseSensitive ? RegexOptions.None : RegexOptions.IgnoreCase);
isMatch = regex.IsMatch(item.Title);
}
catch (Exception ex)
{
_logger.LogError(ex, $"Invalid regex pattern: {rule.Pattern}");
}
}
else
{
var comparison = rule.IsCaseSensitive ?
StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase;
isMatch = item.Title.Contains(rule.Pattern, comparison);
}
if (isMatch)
{
item.IsMatched = true;
item.MatchedRule = rule.Name;
item.Category = rule.Category;
break;
}
}
}
private async Task DownloadMatchedItemAsync(RssFeedItem item)
{
try
{
var config = _configService.GetConfiguration();
var downloadDir = config.DownloadDirectory;
if (string.IsNullOrEmpty(downloadDir))
{
_logger.LogWarning("Download directory not configured");
return;
}
_logger.LogInformation($"Downloading matched item: {item.Title}");
// Add torrent to Transmission
int torrentId = await _transmissionClient.AddTorrentAsync(item.TorrentUrl, downloadDir);
// Update feed item
item.IsDownloaded = true;
item.DownloadDate = DateTime.UtcNow;
item.TorrentId = torrentId;
_logger.LogInformation($"Added torrent: {item.Title} (ID: {torrentId})");
}
catch (Exception ex)
{
_logger.LogError(ex, $"Error downloading item: {item.Title}");
item.RejectionReason = ex.Message;
}
}
private void CleanupOldItems(RssFeed feed)
{
if (feed.MaxHistoryItems <= 0)
return;
var feedItems = _feedItems.Where(i => i.FeedId == feed.Id).ToList();
if (feedItems.Count > feed.MaxHistoryItems)
{
// Keep all downloaded items
var downloadedItems = feedItems.Where(i => i.IsDownloaded).ToList();
// Keep most recent non-downloaded items up to the limit
var nonDownloadedItems = feedItems.Where(i => !i.IsDownloaded)
.OrderByDescending(i => i.PublishDate)
.Take(feed.MaxHistoryItems - downloadedItems.Count)
.ToList();
// Set new list
var itemsToKeep = downloadedItems.Union(nonDownloadedItems).ToList();
_feedItems.RemoveAll(i => i.FeedId == feed.Id && !itemsToKeep.Contains(i));
}
} }
public async Task MarkItemAsDownloadedAsync(string itemId) public async Task MarkItemAsDownloadedAsync(string itemId)
{ {
var item = _items.FirstOrDefault(i => i.Id == itemId); var item = _feedItems.FirstOrDefault(i => i.Id == itemId);
if (item != null) if (item != null)
{ {
item.IsDownloaded = true; item.IsDownloaded = true;
await SaveItemsAsync(); item.DownloadDate = DateTime.UtcNow;
}
}
private async Task FetchFeedAsync(RssFeed feed)
{
_logger.LogInformation($"Fetching feed: {feed.Name}");
try
{
var response = await _httpClient.GetStringAsync(feed.Url);
using var stringReader = new StringReader(response);
using var xmlReader = XmlReader.Create(stringReader);
var syndicationFeed = SyndicationFeed.Load(xmlReader);
foreach (var item in syndicationFeed.Items)
{
var link = item.Links.FirstOrDefault()?.Uri.ToString() ?? "";
var torrentUrl = ExtractTorrentUrl(link, item.Title.Text);
// Create a unique ID for this item
var itemId = $"{feed.Id}:{item.Id ?? Guid.NewGuid().ToString()}";
// Check if we already have this item
if (_items.Any(i => i.Id == itemId))
{
continue;
}
var feedItem = new RssFeedItem
{
Id = itemId,
Title = item.Title.Text,
Link = link,
Description = item.Summary?.Text ?? "",
PublishDate = item.PublishDate.DateTime,
TorrentUrl = torrentUrl,
IsDownloaded = false
};
// Check if this item matches any rules
CheckForMatches(feedItem, feed.Rules);
_items.Add(feedItem);
}
await SaveItemsAsync();
}
catch (Exception ex)
{
_logger.LogError(ex, $"Error fetching feed: {feed.Name}");
throw;
}
}
private string ExtractTorrentUrl(string link, string title)
{
// Try to find a .torrent link
if (link.EndsWith(".torrent", StringComparison.OrdinalIgnoreCase))
{
return link;
}
// If it's a magnet link, return it
if (link.StartsWith("magnet:", StringComparison.OrdinalIgnoreCase))
{
return link;
}
// Return the link as is, we'll try to find the torrent on the page
return link;
}
private void CheckForMatches(RssFeedItem item, List<string> rules)
{
foreach (var rule in rules)
{
try
{
if (Regex.IsMatch(item.Title, rule, RegexOptions.IgnoreCase))
{
item.IsMatched = true;
item.MatchedRule = rule;
break;
}
}
catch (Exception ex)
{
_logger.LogError(ex, $"Invalid regex rule: {rule}");
}
}
}
private async Task ProcessMatchesAsync()
{
var config = _configService.GetConfiguration();
if (!config.AutoDownloadEnabled)
{
return;
}
var matchedItems = _items.Where(i => i.IsMatched && !i.IsDownloaded).ToList();
foreach (var item in matchedItems)
{
try
{
_logger.LogInformation($"Auto-downloading: {item.Title}");
var torrentId = await _transmissionClient.AddTorrentAsync(
item.TorrentUrl,
config.DownloadDirectory);
item.IsDownloaded = true;
}
catch (Exception ex)
{
_logger.LogError(ex, $"Error downloading torrent: {item.Title}");
}
}
await SaveItemsAsync();
}
private void LoadItems()
{
if (!File.Exists(_dataPath))
{
_items = new List<RssFeedItem>();
return;
}
try
{
var json = File.ReadAllText(_dataPath);
var items = JsonSerializer.Deserialize<List<RssFeedItem>>(json);
_items = items ?? new List<RssFeedItem>();
}
catch (Exception ex)
{
_logger.LogError(ex, "Error loading RSS items");
_items = new List<RssFeedItem>();
}
}
private async Task SaveItemsAsync()
{
try
{
var options = new JsonSerializerOptions
{
WriteIndented = true
};
var json = JsonSerializer.Serialize(_items, options);
await File.WriteAllTextAsync(_dataPath, json);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error saving RSS items");
} }
} }
} }
@ -311,17 +329,14 @@ namespace TransmissionRssManager.Services
public class RssFeedBackgroundService : BackgroundService public class RssFeedBackgroundService : BackgroundService
{ {
private readonly ILogger<RssFeedBackgroundService> _logger; private readonly ILogger<RssFeedBackgroundService> _logger;
private readonly IRssFeedManager _rssFeedManager; private readonly IServiceProvider _serviceProvider;
private readonly IConfigService _configService;
public RssFeedBackgroundService( public RssFeedBackgroundService(
ILogger<RssFeedBackgroundService> logger, ILogger<RssFeedBackgroundService> logger,
IRssFeedManager rssFeedManager, IServiceProvider serviceProvider)
IConfigService configService)
{ {
_logger = logger; _logger = logger;
_rssFeedManager = rssFeedManager; _serviceProvider = serviceProvider;
_configService = configService;
} }
protected override async Task ExecuteAsync(CancellationToken stoppingToken) protected override async Task ExecuteAsync(CancellationToken stoppingToken)
@ -332,19 +347,26 @@ namespace TransmissionRssManager.Services
{ {
try try
{ {
await _rssFeedManager.RefreshFeedsAsync(stoppingToken); using (var scope = _serviceProvider.CreateScope())
}
catch (Exception ex)
{ {
_logger.LogError(ex, "Error refreshing RSS feeds"); var rssFeedManager = scope.ServiceProvider.GetRequiredService<IRssFeedManager>();
} var configService = scope.ServiceProvider.GetRequiredService<IConfigService>();
var config = _configService.GetConfiguration(); await rssFeedManager.RefreshFeedsAsync(stoppingToken);
var config = configService.GetConfiguration();
var interval = TimeSpan.FromMinutes(config.CheckIntervalMinutes); var interval = TimeSpan.FromMinutes(config.CheckIntervalMinutes);
_logger.LogInformation($"Next refresh in {interval.TotalMinutes} minutes"); _logger.LogInformation($"Next refresh in {interval.TotalMinutes} minutes");
await Task.Delay(interval, stoppingToken); await Task.Delay(interval, stoppingToken);
} }
} }
catch (Exception ex)
{
_logger.LogError(ex, "Error refreshing RSS feeds");
await Task.Delay(TimeSpan.FromMinutes(5), stoppingToken);
}
}
}
} }
} }