diff --git a/.gitignore b/.gitignore
index a9d1153..d195c27 100644
--- a/.gitignore
+++ b/.gitignore
@@ -540,6 +540,7 @@ FodyWeavers.xsd
# Data files
.DataFiles/
+_DataFiles/
# Environment settings files
appsettings.*.json
diff --git a/PodcastRewind/Models/FeedRewindData.cs b/PodcastRewind/Models/FeedRewindData.cs
index 00bb2e9..4576887 100644
--- a/PodcastRewind/Models/FeedRewindData.cs
+++ b/PodcastRewind/Models/FeedRewindData.cs
@@ -51,7 +51,9 @@ private void LoadRewoundFeed()
RewoundEntries = originalFeed.Items.OrderBy(item => item.PublishDate).ToList();
var feedItemsCount = RewoundEntries.Count;
var keyIndex = RewoundEntries.FindIndex(item => item.Id == feedRewindInfo.KeyEntryId);
- var dateOfFirstEntry = feedRewindInfo.DateOfKeyEntry.AddDays(-feedRewindInfo.Interval * (keyIndex));
+ var dateOfFirstEntry = DateTime.SpecifyKind(
+ feedRewindInfo.DateOfKeyEntry.AddDays(-feedRewindInfo.Interval * keyIndex),
+ DateTimeKind.Unspecified);
for (var i = 0; i < feedItemsCount; i++)
{
@@ -88,16 +90,19 @@ private void LoadRewoundFeed()
};
RewoundFeed.Description = new TextSyndicationContent(newDescription, descriptionType);
- RewoundFeed.Items = RewoundEntries.Where(item => item.PublishDate <= DateTimeOffset.Now)
- .OrderByDescending(item => item.PublishDate);
- MostRecentRewoundFeedEntryDate = RewoundFeed.Items.First().PublishDate;
+ RewoundFeed.Items = RewoundEntries
+ .Where(item => item.PublishDate <= DateTimeOffset.Now)
+ .OrderByDescending(item => item.PublishDate).ToList();
+ if (RewoundFeed.Items.Any())
+ MostRecentRewoundFeedEntryDate = RewoundFeed.Items.First().PublishDate;
}
private void LoadScheduledFeed()
{
if (string.IsNullOrEmpty(feedRewindInfo.FeedUrl)) return;
ScheduledFeed = originalFeed.Clone(true);
- ScheduledFeed.Items = RewoundEntries.Where(item => item.PublishDate > DateTimeOffset.Now)
+ ScheduledFeed.Items = RewoundEntries
+ .Where(item => item.PublishDate > DateTimeOffset.Now)
.OrderBy(item => item.PublishDate);
}
diff --git a/PodcastRewind/PodcastRewind.csproj b/PodcastRewind/PodcastRewind.csproj
index 65b01e4..33dccc2 100644
--- a/PodcastRewind/PodcastRewind.csproj
+++ b/PodcastRewind/PodcastRewind.csproj
@@ -4,7 +4,7 @@
net8.0
enable
enable
- 0.5.0
+ 0.5.1
diff --git a/PodcastRewind/Services/FeedRewindInfoRepository.cs b/PodcastRewind/Services/FeedRewindInfoRepository.cs
index 1b9db81..ee2d603 100644
--- a/PodcastRewind/Services/FeedRewindInfoRepository.cs
+++ b/PodcastRewind/Services/FeedRewindInfoRepository.cs
@@ -34,19 +34,14 @@ public async Task SaveAsync(CreateFeedRewindInfoDto create)
Interval = create.Interval,
};
- var filePath = Path.Combine(_dataFilesDirectory, string.Concat(id.ToString(), ".json"));
- Directory.CreateDirectory(_dataFilesDirectory);
- await using var stream = File.Create(filePath);
- await JsonSerializer.SerializeAsync(stream, feedRewind);
-
- cache.Set(id, feedRewind, CacheEntryOptions);
+ await SaveFeedRewindInfoToFileAsync(id, feedRewind);
return id;
}
public async Task UpdateAsync(EditFeedRewindInfoDto edit)
{
- var original = await GetAsync(edit.Id);
- if (original is null) throw new ArgumentException($"Item {edit.Id} does not exist.", nameof(edit));
+ var original = await GetAsync(edit.Id)
+ ?? throw new ArgumentException($"Item {edit.Id} does not exist.", nameof(edit));
var feedRewind = new FeedRewindInfo
{
@@ -58,25 +53,37 @@ public async Task UpdateAsync(EditFeedRewindInfoDto edit)
CreatedOn = original.CreatedOn,
};
- var filePath = Path.Combine(_dataFilesDirectory, string.Concat(feedRewind.Id.ToString(), ".json"));
+ await SaveFeedRewindInfoToFileAsync(edit.Id, feedRewind);
+ }
+
+ public async Task GetAsync(Guid id)
+ {
+ if (cache.TryGetValue(id, out FeedRewindInfo? info)) return info;
+ info = await LoadFeedRewindInfoFromFileAsync(id);
+ if (info != null) cache.Set(id, info, CacheEntryOptions);
+ return info;
+ }
+
+ private async Task SaveFeedRewindInfoToFileAsync(Guid id, FeedRewindInfo feedRewind)
+ {
+ var filePath = Path.Combine(_dataFilesDirectory, string.Concat(id.ToString(), ".json"));
+ Directory.CreateDirectory(_dataFilesDirectory);
await using var stream = File.Create(filePath);
await JsonSerializer.SerializeAsync(stream, feedRewind);
- cache.Set(edit.Id, feedRewind, CacheEntryOptions);
+ cache.Set(id, feedRewind, CacheEntryOptions);
}
- public Task GetAsync(Guid id) =>
- cache.GetOrCreateAsync(id, async entry =>
+ private async Task LoadFeedRewindInfoFromFileAsync(Guid id)
+ {
+ var filePath = Path.Combine(_dataFilesDirectory, string.Concat(id.ToString(), ".json"));
+ try
{
- entry.SetOptions(CacheEntryOptions);
- var filePath = Path.Combine(_dataFilesDirectory, string.Concat(id.ToString(), ".json"));
- try
- {
- await using var stream = File.OpenRead(filePath);
- return await JsonSerializer.DeserializeAsync(stream);
- }
- catch (Exception e) when (e is FileNotFoundException or DirectoryNotFoundException)
- {
- return null;
- }
- });
+ await using var stream = File.OpenRead(filePath);
+ return await JsonSerializer.DeserializeAsync(stream);
+ }
+ catch (Exception e) when (e is FileNotFoundException or DirectoryNotFoundException)
+ {
+ return null;
+ }
+ }
}
diff --git a/PodcastRewind/Services/SyndicationFeedService.cs b/PodcastRewind/Services/SyndicationFeedService.cs
index 189943d..ec9dbce 100644
--- a/PodcastRewind/Services/SyndicationFeedService.cs
+++ b/PodcastRewind/Services/SyndicationFeedService.cs
@@ -1,6 +1,7 @@
using System.ServiceModel.Syndication;
using System.Xml;
using Microsoft.Extensions.Caching.Memory;
+using Sentry;
namespace PodcastRewind.Services;
@@ -12,21 +13,30 @@ public interface ISyndicationFeedService
public class SyndicationFeedService(IHttpClientFactory httpClientFactory, IMemoryCache cache)
: ISyndicationFeedService
{
- public Task GetSyndicationFeedAsync(string url) =>
- !Uri.IsWellFormedUriString(url, UriKind.Absolute)
- ? Task.FromResult(null)
- : cache.GetOrCreateAsync(url, entry =>
- {
- entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(20);
- return GetRemoteSyndicationFeedAsync(url);
- });
+ public async Task GetSyndicationFeedAsync(string url)
+ {
+ if (!Uri.IsWellFormedUriString(url, UriKind.Absolute)) return null;
+ if (cache.TryGetValue(url, out SyndicationFeed? feed)) return feed;
+ feed = await GetRemoteSyndicationFeedAsync(url);
+ if (feed != null)
+ cache.Set(url, feed, new MemoryCacheEntryOptions().SetAbsoluteExpiration(TimeSpan.FromHours(20)));
+ return feed;
+ }
- private async Task GetRemoteSyndicationFeedAsync(string url)
+ private async Task GetRemoteSyndicationFeedAsync(string url)
{
var client = httpClientFactory.CreateClient("Polly");
client.DefaultRequestHeaders.Add("user-agent", "PodcastRewind/1.0");
- await using var stream = await client.GetStreamAsync(url);
- using var xmlReader = XmlReader.Create(stream);
- return SyndicationFeed.Load(xmlReader);
+ try
+ {
+ await using var stream = await client.GetStreamAsync(url);
+ using var xmlReader = XmlReader.Create(stream);
+ return SyndicationFeed.Load(xmlReader);
+ }
+ catch (HttpRequestException e)
+ {
+ SentrySdk.CaptureException(e);
+ return null;
+ }
}
}
diff --git a/PodcastRewind/appsettings.json b/PodcastRewind/appsettings.json
index 6055fd0..fc7aac9 100644
--- a/PodcastRewind/appsettings.json
+++ b/PodcastRewind/appsettings.json
@@ -1,5 +1,5 @@
{
- "DataFilesDirectory": "../.DataFiles",
+ "DataFilesDirectory": "_DataFiles",
"UseTestData": false,
"Sentry": {
"Environment": "production",