diff --git a/SharpRss/DbAccess.cs b/SharpRss/DbAccess.cs index 6d8c1d6..a2db4f9 100644 --- a/SharpRss/DbAccess.cs +++ b/SharpRss/DbAccess.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Data.Common; using System.IO; using System.Linq; -using System.Security.Policy; using System.Threading.Tasks; using Dapper; using Microsoft.Data.Sqlite; @@ -14,17 +13,17 @@ namespace SharpRss { internal static class DbAccess { - static DbAccess() - { - Initialize(); - } private static readonly string ConnectionString = $"Data Source={Path.Combine(Environment.CurrentDirectory, "sharp_rss.sqlite")};"; private static bool _isInitialized; public static async Task SetSyndicationAsync(SyndicationContainer synContainer) { if (synContainer.Category != null) - await SetCategoryAsync(synContainer.Category); + { + CategoryModel? catModel = await SetCategoryAsync(synContainer.Category); + if (catModel != null) + synContainer.FeedModel.CategoryId = catModel.Id; + } if (synContainer.FeedModel != null) await SetFeedAsync(synContainer.FeedModel); if (synContainer.FeedItems != null && synContainer.FeedItems.Any()) @@ -51,14 +50,18 @@ namespace SharpRss } return categories; } - public static async Task SetCategoryAsync(CategoryModel category) + public static async Task SetCategoryAsync(CategoryModel category) { - if (category == null) return false; + CategoryModel? modelReturn = null; + if (category == null) return modelReturn; await using SqliteConnection dbc = new SqliteConnection(ConnectionString); dbc.Open(); int affected = await dbc.ExecuteAsync("INSERT OR REPLACE INTO category (id, hex_color, icon, name) VALUES (IFNULL((SELECT id FROM category WHERE name=@Name), @Id), @HexColor, @Icon, @Name)", new { category.Id, category.HexColor, category.Icon, category.Name }); - return affected > 0; + if (affected <= 0) return modelReturn; + var catModel = await GetCategoriesAsync(); + modelReturn = catModel.Where(x => x.Name == category.Name).ToHashSet().FirstOrDefault() ?? null; + return modelReturn; } public static async Task DeleteCategory(CategoryModel category) { @@ -73,7 +76,7 @@ namespace SharpRss { await using SqliteConnection dbc = new SqliteConnection(ConnectionString); dbc.Open(); - await using SqliteCommand cmd = new SqliteCommand(@"INSERT OR REPLACE INTO feed + /*await using SqliteCommand cmd = new SqliteCommand(@"INSERT OR REPLACE INTO feed (url, title, category_id, @@ -116,8 +119,33 @@ namespace SharpRss new SqliteParameter("ImageUrl", feed.ImageUrl ?? string.Empty) } }; - int exec = await cmd.ExecuteNonQueryAsync(); - /*int affected = await dbc.ExecuteAsync("INSERT OR REPLACE INTO feed (url, title, category_id, feed_type, feed_version, description, language, copyright, publication_date, last_updated, categories, image_url) VALUES (@Url, @Title, @CategoryId, @FeedType, @FeedVersion, @Description, @language, @Copyright, @PublicationDate, @LastUpdated, @Categories, @ImageUrl)", + int exec = await cmd.ExecuteNonQueryAsync();*/ + int affected = await dbc.ExecuteAsync(@"INSERT OR REPLACE INTO feed + (url, + title, + category_id, + feed_type, + feed_version, + description, + language, + copyright, + publication_date, + last_updated, + categories, + image_url) + VALUES ( + @Url, + @Title, + @CategoryId, + @FeedType, + @FeedVersion, + @Description, + @Language, + @Copyright, + @PublicationDate, + @LastUpdated, + @Categories, + @ImageUrl)", new { Url = feed.OriginalUrl ?? string.Empty, @@ -130,18 +158,18 @@ namespace SharpRss Copyright = feed.Copyright ?? string.Empty, PublicationDate = feed.PublicationDate?.ToUnixTimeMilliseconds() ?? 0, LastUpdated = feed.LastUpdated?.ToUnixTimeMilliseconds() ?? 0, - Categories = feed.Categories.Any() ? string.Join(',', feed.Categories) : string.Empty, + Categories = feed.Categories != null && feed.Categories.Any() ? string.Join(',', feed.Categories) : string.Empty, ImageUrl = feed.ImageUrl ?? string.Empty }); if (affected == 0) - Log.Warning("Failed to add feed: {FeedUrl}", feed.OriginalUrl);*/ + Log.Warning("Failed to add feed: {FeedUrl}", feed.OriginalUrl); } public static async Task> GetFeedsAsync(string[]? categoryIds = null) { await using SqliteConnection dbc = new SqliteConnection(ConnectionString); dbc.Open(); HashSet feeds = new HashSet(); - await using DbDataReader reader = await dbc.ExecuteReaderAsync(categoryIds == null ? "SELECT * FROM feed" : "SELECT * FROM feed WHERE category_id IN(@CatIds)", new { CatIds = categoryIds }); + await using DbDataReader reader = await dbc.ExecuteReaderAsync(categoryIds == null ? "SELECT * FROM feed WHERE category_id == ''" : "SELECT * FROM feed WHERE category_id IN(@CatIds)", new { CatIds = categoryIds }); while (await reader.ReadAsync()) { FeedModel feedModel = new FeedModel() @@ -172,7 +200,7 @@ namespace SharpRss await using SqliteTransaction dbTransaction = dbc.BeginTransaction(); foreach (FeedItemModel item in items) { - await using SqliteCommand cmd = new SqliteCommand(@"INSERT OR REPLACE INTO feed_item (id, feed_url, read, title, description, link, last_updated, publishing_date, authors, categories, content) + /*await using SqliteCommand cmd = new SqliteCommand(@"INSERT OR REPLACE INTO feed_item (id, feed_url, read, title, description, link, last_updated, publishing_date, authors, categories, content) VALUES (@Id, @FeedUrl, @Read, @Title, @Description, @Link, @LastUpdated, @PublishingDate, @Authors, @Categories, @Content)", dbc, dbTransaction) { Parameters = @@ -190,8 +218,8 @@ namespace SharpRss new SqliteParameter("Content", item.Content ?? string.Empty) } }; - int exec = await cmd.ExecuteNonQueryAsync(); - /*int affected = await dbc.ExecuteAsync(@"INSERT OR REPLACE INTO feed_item (id, feed_url, read, title, description, link, last_updated, publishing_date, authors, categories, content) + int exec = await cmd.ExecuteNonQueryAsync();*/ + int affected = await dbc.ExecuteAsync(@"INSERT OR REPLACE INTO feed_item (id, feed_url, read, title, description, link, last_updated, publishing_date, authors, categories, content) VALUES (@Id, @FeedUrl, @Read, @Title, @Description, @Link, @LastUpdated, @PublishingDate, @Authors, @Categories, @Content)", transaction: dbTransaction, param: new @@ -204,11 +232,11 @@ namespace SharpRss Link = item.Link ?? string.Empty, LastUpdated = item.LastUpdated?.ToUnixTimeMilliseconds() ?? 0, PublishingDate = item.PublishingDate?.ToUnixTimeMilliseconds() ?? 0, - Authors = item.Authors.Any() ? string.Join(',', item.Authors) : string.Empty, - Categories = item.Categories.Any() ? string.Join(',', item.Categories) : string.Empty, + Authors = item.Authors != null && item.Authors.Any() ? string.Join(',', item.Authors) : string.Empty, + Categories = item.Categories != null && item.Categories.Any() ? string.Join(',', item.Categories) : string.Empty, Content = item.Content ?? string.Empty }); - totalAffected += affected;*/ + totalAffected += affected; } dbTransaction.Commit(); } @@ -239,7 +267,7 @@ namespace SharpRss return items; } - private static async void Initialize() + public static async void Initialize() { if (_isInitialized) return; Log.Verbose("Checking database..."); diff --git a/SharpRss/Services/RssService.cs b/SharpRss/Services/RssService.cs index e45f760..0fc0f94 100644 --- a/SharpRss/Services/RssService.cs +++ b/SharpRss/Services/RssService.cs @@ -79,42 +79,7 @@ namespace SharpRss.Services var items = await DbAccess.GetFeedItemsAsync(feedIds); return items; } - - /*private async Task AddFeedItems(IList items, FeedModel feedModel) - { - int result = 0; - if (!items.Any()) - return result; - HashSet itemModels = new HashSet(); - // TODO: implement!!! - result = await DbAccess.SetFeedItemsAsync(itemModels); - return result; - }*/ - - /*private IList ConstructFeedItems(IList items, FeedModel feed) - { - IList itemModels = new List(); - if (!items.Any()) - return itemModels; - foreach (FeedItem item in items) - { - itemModels.Add(new FeedItemModel() - { - Id = item.Id, - FeedId = feed.Id, - Title = item.Title, - Description = item.Description, - Link = item.Link, - LastUpdated = DateTimeOffset.Now, - PublishingDate = item.PublishingDate != null ? new DateTimeOffset((DateTime)item.PublishingDate) : null, - Author = item.Author, - Categories = item.Categories.ToArray(), - Content = item.Content - }); - } - return itemModels; - }*/ - + private GenericSyndicationFeed? CreateFeed(string url) { Uri feedUri = new Uri(url); @@ -129,9 +94,7 @@ namespace SharpRss.Services } private async void SetupTestGroupsAndFeedsAsync() { - /*CategoryModel newModel = new CategoryModel() { Name = "Test" }; - bool added = await DbAccess.SetCategoryAsync(newModel);*/ - await AddSubscriptionAsync("https://en.wikipedia.org/w/api.php?hidebots=1&hidecategorization=1&hideWikibase=1&urlversion=1&days=7&limit=50&action=feedrecentchanges&feedformat=atom"); + /*await AddSubscriptionAsync("https://www.nu.nl/rss/Algemeen");*/ //TODO: Make multiple adding of feed to a transaction, now throws an exception. /*var groupRes = await CreateGroupAsync(new GroupModel() { Name = "Test" }); groupRes = await CreateGroupAsync(new GroupModel() { Name = "News" }); diff --git a/SharpRss/SyndicationManager.cs b/SharpRss/SyndicationManager.cs index 09ebbcb..d24a882 100644 --- a/SharpRss/SyndicationManager.cs +++ b/SharpRss/SyndicationManager.cs @@ -61,62 +61,63 @@ namespace SharpRss { case SyndicationContentFormat.Rss: RssFeed rssFeed = (RssFeed)container.SyndicationFeed.Resource; - container.FeedModel.OriginalUrl = rssFeed.Channel.SelfLink.ToString(); - container.FeedModel.Title = rssFeed.Channel.Title; - container.FeedModel.FeedType = rssFeed.Format.ToString(); - container.FeedModel.FeedVersion = rssFeed.Version.ToString(); - container.FeedModel.Description = rssFeed.Channel.Description; - container.FeedModel.Language = rssFeed.Channel.Language?.ToString(); - container.FeedModel.Copyright = rssFeed.Channel.Copyright; - container.FeedModel.PublicationDate = rssFeed.Channel.LastBuildDate.Ticks <= 0 ? DateTimeOffset.MinValue : new DateTimeOffset(rssFeed.Channel.LastBuildDate); - container.FeedModel.Categories = rssFeed.Channel.Categories.Select(x => x.Value).ToArray(); + if (rssFeed.Channel == null) break; + container.FeedModel.OriginalUrl = rssFeed.Channel.SelfLink?.ToString() ?? string.Empty; + container.FeedModel.Title = rssFeed.Channel.Title ?? string.Empty; + container.FeedModel.FeedType = rssFeed.Format.ToString() ?? string.Empty; + container.FeedModel.FeedVersion = rssFeed.Version?.ToString() ?? string.Empty; + container.FeedModel.Description = rssFeed.Channel.Description ?? string.Empty; + container.FeedModel.Language = rssFeed.Channel.Language?.ToString() ?? string.Empty; + container.FeedModel.Copyright = rssFeed.Channel.Copyright ?? string.Empty; + container.FeedModel.PublicationDate = rssFeed.Channel.LastBuildDate is not { Ticks: > 0 } ? DateTimeOffset.MinValue : new DateTimeOffset(rssFeed.Channel.LastBuildDate); + container.FeedModel.Categories = rssFeed.Channel.Categories?.Select(x => x.Value).ToArray() ?? Array.Empty(); container.FeedModel.ImageUrl = rssFeed.Channel.Image?.Url.ToString() ?? string.Empty; foreach (var rssItem in rssFeed.Channel.Items) { FeedItemModel itemModel = new FeedItemModel() { - Id = rssItem.Link.ToString(), - FeedUrl = container.FeedModel.OriginalUrl, - Type = container.FeedModel.FeedType, - Title = rssItem.Title, - Description = rssItem.Description, - Link = rssItem.Link.ToString(), - PublishingDate = rssItem.PublicationDate.Ticks <= 0 ? DateTimeOffset.MinValue : new DateTimeOffset(rssItem.PublicationDate), - Authors = new []{ rssItem.Author }, - Categories = rssItem.Categories.Select(x => x.Value).ToArray(), - Content = rssItem.Extensions.Where(x => x is SiteSummaryContentSyndicationExtension).Select(x => (x as SiteSummaryContentSyndicationExtension)?.Context.Encoded).First(), - CommentsUrl = rssItem.Extensions.Where(x => x is WellFormedWebCommentsSyndicationExtension).Select(x => (x as WellFormedWebCommentsSyndicationExtension)?.Context.CommentsFeed.ToString()).First() + Id = rssItem.Link?.ToString() ?? string.Empty, + FeedUrl = container.FeedModel.OriginalUrl ?? string.Empty, + Type = container.FeedModel.FeedType ?? string.Empty, + Title = rssItem.Title ?? string.Empty, + Description = rssItem.Description ?? string.Empty, + Link = rssItem.Link?.ToString() ?? string.Empty, + PublishingDate = rssItem.PublicationDate is not { Ticks: <= 0 } ? DateTimeOffset.MinValue : new DateTimeOffset(rssItem.PublicationDate), + Authors = rssItem.Author != null ? new []{ rssItem.Author } : Array.Empty(), + Categories = rssItem.Categories?.Select(x => x.Value).ToArray() ?? Array.Empty(), + Content = rssItem.Extensions?.Where(x => x is SiteSummaryContentSyndicationExtension).Select(x => (x as SiteSummaryContentSyndicationExtension)?.Context.Encoded).FirstOrDefault() ?? string.Empty, + CommentsUrl = rssItem.Extensions?.Where(x => x is WellFormedWebCommentsSyndicationExtension).Select(x => (x as WellFormedWebCommentsSyndicationExtension)?.Context.CommentsFeed.ToString()).FirstOrDefault() ?? string.Empty }; container.FeedItems.Add(itemModel); } break; case SyndicationContentFormat.Atom: AtomFeed atomFeed = (AtomFeed)container.SyndicationFeed.Resource; - container.FeedModel.OriginalUrl = atomFeed.Id.Uri.ToString(); - container.FeedModel.Title = atomFeed.Title.Content; - container.FeedModel.FeedType = atomFeed.Format.ToString(); - container.FeedModel.FeedVersion = atomFeed.Version?.ToString(); - container.FeedModel.Description = atomFeed.Subtitle?.Content; - container.FeedModel.Language = atomFeed.Language?.ToString(); - container.FeedModel.Copyright = atomFeed.Rights?.Content; - container.FeedModel.PublicationDate = new DateTimeOffset(atomFeed.UpdatedOn); - container.FeedModel.Categories = atomFeed.Categories?.Select(x => x.Label).ToArray(); + container.FeedModel.OriginalUrl = atomFeed.Id?.Uri.ToString() ?? string.Empty; + container.FeedModel.Title = atomFeed.Title?.Content ?? string.Empty; + container.FeedModel.FeedType = atomFeed.Format.ToString() ?? string.Empty; + container.FeedModel.FeedVersion = atomFeed.Version?.ToString() ?? string.Empty; + container.FeedModel.Description = atomFeed.Subtitle?.Content ?? string.Empty; + container.FeedModel.Language = atomFeed.Language?.ToString() ?? string.Empty; + container.FeedModel.Copyright = atomFeed.Rights?.Content ?? string.Empty; + container.FeedModel.PublicationDate = atomFeed.UpdatedOn != null ? new DateTimeOffset(atomFeed.UpdatedOn) : DateTimeOffset.MinValue; + container.FeedModel.Categories = atomFeed.Categories?.Select(x => x.Label).ToArray() ?? Array.Empty(); container.FeedModel.ImageUrl = atomFeed.Icon?.Uri.ToString() ?? string.Empty; foreach (var entry in atomFeed.Entries) { FeedItemModel itemModel = new FeedItemModel() { - Id = entry.Id.Uri.ToString(), - FeedUrl = container.FeedModel.OriginalUrl, - Type = container.FeedModel.FeedType, - Title = entry.Title.Content, - Description = entry.Summary.Content, - Link = entry.Id.Uri.ToString(), - LastUpdated = entry.UpdatedOn.Ticks <= 0 ? DateTimeOffset.MinValue : new DateTimeOffset(entry.UpdatedOn), - PublishingDate = entry.PublishedOn.Ticks <= 0 ? DateTimeOffset.MinValue : new DateTimeOffset(entry.PublishedOn), - Authors = entry.Authors.Select(auth => auth.Name).ToArray(), - Categories = entry.Categories.Select(cat => cat.Label).ToArray(), - Content = entry.Content?.Content + Id = entry.Id?.Uri.ToString() ?? string.Empty, + FeedUrl = container.FeedModel?.OriginalUrl ?? string.Empty, + Type = container.FeedModel?.FeedType ?? string.Empty, + Title = entry.Title?.Content ?? string.Empty, + Description = entry.Summary?.Content ?? string.Empty, + Link = entry.Id?.Uri.ToString() ?? string.Empty, + LastUpdated = entry.UpdatedOn is not { Ticks: <= 0 } ? DateTimeOffset.MinValue : new DateTimeOffset(entry.UpdatedOn), + PublishingDate = entry.PublishedOn is not { Ticks: <= 0 } ? DateTimeOffset.MinValue : new DateTimeOffset(entry.PublishedOn), + Authors = entry.Authors?.Select(auth => auth.Name).ToArray() ?? Array.Empty(), + Categories = entry.Categories?.Select(cat => cat.Label).ToArray() ?? Array.Empty(), + Content = entry.Content?.Content ?? string.Empty }; container.FeedItems.Add(itemModel); } @@ -128,5 +129,10 @@ namespace SharpRss container.Fetched = true; return container; } + + public static void Init() + { + DbAccess.Initialize(); + } } } \ No newline at end of file diff --git a/WebSharpRSS/Bootstrapper.cs b/WebSharpRSS/Bootstrapper.cs index 6b7f094..4b27b27 100644 --- a/WebSharpRSS/Bootstrapper.cs +++ b/WebSharpRSS/Bootstrapper.cs @@ -19,7 +19,7 @@ namespace WebSharpRSS Caretaker.Settings.SetAppDefaultSettings(); SetupLogging(); Log.Information("Starting SharpRSS..."); - //DbAccess_Old.Initialize(); + SyndicationManager.Init(); _bootstrapped = true; } diff --git a/WebSharpRSS/sharp_rss.sqlite b/WebSharpRSS/sharp_rss.sqlite index 23ccd7e..d1deae3 100644 Binary files a/WebSharpRSS/sharp_rss.sqlite and b/WebSharpRSS/sharp_rss.sqlite differ