Working on syndication manager, handling feed fetching

This commit is contained in:
Max 2023-06-04 02:16:47 +02:00
parent cb13ef54c1
commit 86337f92a2
10 changed files with 206 additions and 74 deletions

View File

@ -4,8 +4,6 @@ using System.Data;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Transactions;
using Argotic.Syndication;
using Dapper; using Dapper;
using Microsoft.Data.Sqlite; using Microsoft.Data.Sqlite;
using Serilog; using Serilog;
@ -13,7 +11,7 @@ using SharpRss.Models;
namespace SharpRss namespace SharpRss
{ {
public static class DbAccess public static class DbAccess_Old
{ {
//TODO: Rename group => category. //TODO: Rename group => category.
//TODO: Reworking feed => model/db implementation. //TODO: Reworking feed => model/db implementation.
@ -149,17 +147,16 @@ namespace SharpRss
{ {
Parameters = Parameters =
{ {
new SqliteParameter("url", feedModel.Url ?? string.Empty), new SqliteParameter("url", feedModel.OriginalUrl ?? string.Empty),
new SqliteParameter("title", feedModel.Title ?? string.Empty), new SqliteParameter("title", feedModel.Title ?? string.Empty),
new SqliteParameter("groupId", feedModel.GroupId ?? string.Empty), new SqliteParameter("groupId", feedModel.GroupId ?? string.Empty),
new SqliteParameter("feedType", feedModel.FeedType ?? string.Empty), new SqliteParameter("feedType", feedModel.FeedType ?? string.Empty),
new SqliteParameter("description", feedModel.Description ?? string.Empty), new SqliteParameter("description", feedModel.Description ?? string.Empty),
new SqliteParameter("language", feedModel.Language ?? string.Empty), new SqliteParameter("language", feedModel.Language ?? string.Empty),
new SqliteParameter("copyright", feedModel.Copyright ?? string.Empty), new SqliteParameter("copyright", feedModel.Copyright ?? string.Empty),
new SqliteParameter("dateAdded", feedModel.DateAdded?.ToUnixTimeMilliseconds() ?? 0), new SqliteParameter("dateAdded", feedModel.PublicationDate?.ToUnixTimeMilliseconds() ?? 0),
new SqliteParameter("lastUpdated", feedModel.LastUpdated?.ToUnixTimeMilliseconds() ?? 0), new SqliteParameter("lastUpdated", feedModel.LastUpdated?.ToUnixTimeMilliseconds() ?? 0),
new SqliteParameter("imageUrl", feedModel.ImageUrl ?? string.Empty), new SqliteParameter("imageUrl", feedModel.ImageUrl ?? string.Empty)
new SqliteParameter("originalDoc", feedModel.OriginalDocument ?? string.Empty)
} }
}; };
try try
@ -230,8 +227,8 @@ namespace SharpRss
HashSet<FeedItemModel> feedItems = new HashSet<FeedItemModel>(); HashSet<FeedItemModel> feedItems = new HashSet<FeedItemModel>();
foreach (var dbFeed in dbFeeds) foreach (var dbFeed in dbFeeds)
{ {
GenericSyndicationFeed syndication = new GenericSyndicationFeed(); /*GenericSyndicationFeed syndication = new GenericSyndicationFeed();
syndication.Load(dbFeed.OriginalDocument); syndication.Load(dbFeed.OriginalDocument);*/
//TODO: Get items and add to db //TODO: Get items and add to db
} }
} }
@ -245,7 +242,7 @@ namespace SharpRss
{ {
Parameters = Parameters =
{ {
new SqliteParameter("Url", feedModel.Url) new SqliteParameter("Url", feedModel.OriginalUrl)
} }
}; };
int affected = await cmd.ExecuteNonQueryAsync(); int affected = await cmd.ExecuteNonQueryAsync();
@ -280,14 +277,14 @@ namespace SharpRss
DateTimeOffset.FromUnixTimeMilliseconds(long.Parse(reader["last_updated"].ToString())), DateTimeOffset.FromUnixTimeMilliseconds(long.Parse(reader["last_updated"].ToString())),
PublishingDate = PublishingDate =
DateTimeOffset.FromUnixTimeMilliseconds(long.Parse(reader["publishing_date"].ToString())), DateTimeOffset.FromUnixTimeMilliseconds(long.Parse(reader["publishing_date"].ToString())),
Author = reader["author"].ToString(), Authors = reader["authors"].ToString().ToString().Split(','),
Categories = reader["categories"].ToString().Split(','), Categories = reader["categories"].ToString().Split(','),
Content = reader["content"].ToString() Content = reader["content"].ToString()
}; };
if (feedItemModel is { FeedId: { } }) if (feedItemModel is { FeedId: { } })
{ {
FeedModel? feedModel = await GetFeedAsync(feedItemModel.FeedUrl); FeedModel? feedModel = await GetFeedAsync(feedItemModel.FeedUrl);
feedItemModel.Feed = feedModel; //feedItemModel.Feed = feedModel;
} }
feedItems.Add(feedItemModel); feedItems.Add(feedItemModel);
} }
@ -300,8 +297,8 @@ namespace SharpRss
await using SqliteConnection dbc = new SqliteConnection(ConnectionString); await using SqliteConnection dbc = new SqliteConnection(ConnectionString);
dbc.Open(); dbc.Open();
await using SqliteTransaction transaction = dbc.BeginTransaction(); await using SqliteTransaction transaction = dbc.BeginTransaction();
await using SqliteCommand cmd = new SqliteCommand($"INSERT OR REPLACE INTO {FeedItemTable} (id, feed_id, read, title, description, link, last_updated, publishing_date, author, categories, content)" + await using SqliteCommand cmd = new SqliteCommand($"INSERT OR REPLACE INTO {FeedItemTable} (id, feed_url, read, title, description, link, last_updated, publishing_date, authors, categories, content)" +
$"VALUES (IFNULL((SELECT id FROM {FeedItemTable} WHERE link=@link), @id), @feedId, @read, @title, @description, @link, @lastUpdated, @publishingDate, @author, @categories, @content)", dbc) $"VALUES (IFNULL((SELECT id FROM {FeedItemTable} WHERE link=@link), @id), @feedUrl, @read, @title, @description, @link, @lastUpdated, @publishingDate, @authors, @categories, @content)", dbc)
{ {
Transaction = transaction Transaction = transaction
}; };
@ -309,7 +306,7 @@ namespace SharpRss
{ {
cmd.Parameters.Clear(); cmd.Parameters.Clear();
cmd.Parameters.Add(new SqliteParameter("id", item.Id ?? string.Empty)); cmd.Parameters.Add(new SqliteParameter("id", item.Id ?? string.Empty));
cmd.Parameters.Add(new SqliteParameter("feedId", item.FeedId ?? string.Empty)); cmd.Parameters.Add(new SqliteParameter("feedUrl", item.FeedUrl ?? string.Empty));
cmd.Parameters.Add(new SqliteParameter("read", item.Read ? 1 : 0)); cmd.Parameters.Add(new SqliteParameter("read", item.Read ? 1 : 0));
cmd.Parameters.Add(new SqliteParameter("type", item.Type ?? string.Empty)); cmd.Parameters.Add(new SqliteParameter("type", item.Type ?? string.Empty));
cmd.Parameters.Add(new SqliteParameter("title", item.Title ?? string.Empty)); cmd.Parameters.Add(new SqliteParameter("title", item.Title ?? string.Empty));
@ -317,7 +314,7 @@ namespace SharpRss
cmd.Parameters.Add(new SqliteParameter("link", item.Link ?? string.Empty)); cmd.Parameters.Add(new SqliteParameter("link", item.Link ?? string.Empty));
cmd.Parameters.Add(new SqliteParameter("lastUpdated", item.LastUpdated?.ToUnixTimeMilliseconds())); cmd.Parameters.Add(new SqliteParameter("lastUpdated", item.LastUpdated?.ToUnixTimeMilliseconds()));
cmd.Parameters.Add(new SqliteParameter("publishingDate", item.PublishingDate?.ToUnixTimeMilliseconds() ?? 0)); cmd.Parameters.Add(new SqliteParameter("publishingDate", item.PublishingDate?.ToUnixTimeMilliseconds() ?? 0));
cmd.Parameters.Add(new SqliteParameter("author", item.Author ?? string.Empty)); cmd.Parameters.Add(new SqliteParameter("authors", item.Authors != null ? string.Join(',', item.Authors) : string.Empty));
cmd.Parameters.Add(new SqliteParameter("categories", item.Categories != null ? string.Join(',', item.Categories) : string.Empty)); cmd.Parameters.Add(new SqliteParameter("categories", item.Categories != null ? string.Join(',', item.Categories) : string.Empty));
cmd.Parameters.Add(new SqliteParameter("content", item.Content ?? string.Empty)); cmd.Parameters.Add(new SqliteParameter("content", item.Content ?? string.Empty));
if (dbc.State != ConnectionState.Open) if (dbc.State != ConnectionState.Open)
@ -372,17 +369,16 @@ namespace SharpRss
{ {
FeedModel fetchedFeed = new FeedModel() FeedModel fetchedFeed = new FeedModel()
{ {
Url = reader["url"].ToString(), OriginalUrl = reader["url"].ToString(),
Title = reader["title"].ToString(), Title = reader["title"].ToString(),
GroupId = reader["group_id"].ToString(), GroupId = reader["group_id"].ToString(),
FeedType = reader["feed_type"].ToString(), FeedType = reader["feed_type"].ToString(),
Description = reader["description"].ToString(), Description = reader["description"].ToString(),
Language = reader["language"].ToString(), Language = reader["language"].ToString(),
Copyright = reader["copyright"].ToString(), Copyright = reader["copyright"].ToString(),
DateAdded = DateTimeOffset.FromUnixTimeMilliseconds(long.TryParse(reader["date_added"].ToString(), out long parsedVal) ? parsedVal : 0), PublicationDate = DateTimeOffset.FromUnixTimeMilliseconds(long.TryParse(reader["date_added"].ToString(), out long parsedVal) ? parsedVal : 0),
LastUpdated = DateTimeOffset.FromUnixTimeMilliseconds(long.TryParse(reader["last_updated"].ToString(), out long lastUpdated) ? lastUpdated : 0), LastUpdated = DateTimeOffset.FromUnixTimeMilliseconds(long.TryParse(reader["last_updated"].ToString(), out long lastUpdated) ? lastUpdated : 0),
ImageUrl = reader["image_url"].ToString(), ImageUrl = reader["image_url"].ToString()
OriginalDocument = reader["original_document"].ToString()
}; };
//TODO: Set group on insert //TODO: Set group on insert
/*var groupFetch = await GetGroupsAsync(fetchedFeed.GroupId); /*var groupFetch = await GetGroupsAsync(fetchedFeed.GroupId);
@ -410,7 +406,7 @@ namespace SharpRss
if (queryResponse.Any()) failed.Add("feed"); if (queryResponse.Any()) failed.Add("feed");
Log.Verbose("Checking table: {Table}", "feed_item"); Log.Verbose("Checking table: {Table}", "feed_item");
queryResponse = await dbc.QueryAsync($"CREATE TABLE IF NOT EXISTS feed_item (id STRING PRIMARY KEY, feed_id STRING, read INT, title STRING, description STRING, link STRING, last_updated INT, publishing_date INT, author STRING, categories STRING, content STRING)"); queryResponse = await dbc.QueryAsync($"CREATE TABLE IF NOT EXISTS feed_item (id STRING PRIMARY KEY, feed_url STRING, read INT, title STRING, description STRING, link STRING, last_updated INT, publishing_date INT, authors STRING, categories STRING, content STRING)");
if (queryResponse.Any()) failed.Add("feed_item"); if (queryResponse.Any()) failed.Add("feed_item");
if (failed.Any()) if (failed.Any())

View File

@ -4,7 +4,6 @@ namespace SharpRss.Models
{ {
public class FeedItemModel public class FeedItemModel
{ {
public FeedModel? Feed { get; set; }
public string? Id { get; set; } = string.Empty; public string? Id { get; set; } = string.Empty;
// FeedId will be removed // FeedId will be removed
public string? FeedId { get; set; } = string.Empty; public string? FeedId { get; set; } = string.Empty;
@ -16,9 +15,10 @@ namespace SharpRss.Models
public string? Link { get; set; } = string.Empty; public string? Link { get; set; } = string.Empty;
public DateTimeOffset? LastUpdated { get; set; } public DateTimeOffset? LastUpdated { get; set; }
public DateTimeOffset? PublishingDate { get; set; } public DateTimeOffset? PublishingDate { get; set; }
public string? Author { get; set; } = string.Empty; public string[]? Authors { get; set; }
public string[]? Categories { get; set; } public string[]? Categories { get; set; }
public string? Content { get; set; } = string.Empty; public string? Content { get; set; } = string.Empty;
public string? HexColor => Feed?.Group?.HexColor; public string? CommentsUrl { get; set; } = string.Empty;
public string? HexColor { get; set; } = string.Empty;
} }
} }

View File

@ -1,23 +1,37 @@
using System; using System;
using ToolQit;
using ToolQit.Extensions;
namespace SharpRss.Models namespace SharpRss.Models
{ {
public class FeedModel public class FeedModel
{ {
public FeedModel() public CategoryModel? Category { get; set; }
{ public string OriginalUrl { get; set; } = string.Empty;
}
public CategoryModel? Group { get; set; }
public string Url { get; set; }
public string? Title { get; set; } = string.Empty; public string? Title { get; set; } = string.Empty;
public string? GroupId { get; set; } = string.Empty; public string? GroupId { get; set; } = string.Empty;
public string? FeedType { get; set; } = string.Empty; public string? FeedType { get; set; } = string.Empty;
public string? FeedVersion { get; set; } = string.Empty;
public string? Description { get; set; } = string.Empty; public string? Description { get; set; } = string.Empty;
public string? Language { get; set; } = string.Empty; public string? Language { get; set; } = string.Empty;
public string? Copyright { get; set; } = string.Empty; public string? Copyright { get; set; } = string.Empty;
public DateTimeOffset? DateAdded { get; set; } public DateTimeOffset? PublicationDate { get; set; }
public DateTimeOffset? LastUpdated { get; set; } public DateTimeOffset? LastUpdated { get; set; } = DateTimeOffset.Now;
public string? ImageUrl { get; set; } = string.Empty; public string[]? Categories { get; set; }
public string? OriginalDocument { get; set; } = string.Empty; private string _imageUrl = string.Empty;
public string ImageUrl
{
get
{
if (_imageUrl.IsNullEmptyWhiteSpace())
_imageUrl = string.Format(Caretaker.Settings["Paths"].GetString("FaviconResolveUrl"), new Uri(OriginalUrl).Host);
return _imageUrl;
}
set
{
if (!value.IsNullEmptyWhiteSpace())
_imageUrl = value;
}
}
} }
} }

View File

@ -1,11 +1,8 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel.Design;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Argotic.Common; using Argotic.Common;
using Argotic.Syndication; using Argotic.Syndication;
using Microsoft.Data.Sqlite;
using Serilog; using Serilog;
using SharpRss.Models; using SharpRss.Models;
@ -21,33 +18,33 @@ namespace SharpRss.Services
SetupTestGroupsAndFeedsAsync(); SetupTestGroupsAndFeedsAsync();
} }
public async Task<HashSet<object>> GetGroupsFeedsAsync() public async Task<HashSet<object>> GetCategoriesFeedsAsync()
{ {
HashSet<object> items = new HashSet<object>(); HashSet<object> items = new HashSet<object>();
items.UnionWith(await GetCategoriesAsync()); items.UnionWith(await GetCategoriesAsync());
items.UnionWith(await GetUngroupedFeedsAsync()); items.UnionWith(await GetUngroupedFeedsAsync());
return items; return items;
} }
public async Task<bool> CreateGroupAsync(CategoryModel group) => await DbAccess.SetCategoryAsync(group); public async Task<bool> CreateGroupAsync(CategoryModel group) => await DbAccess_Old.SetCategoryAsync(group);
public async Task<HashSet<CategoryModel>> GetCategoriesAsync() => await DbAccess.GetCategoriesAsync(); public async Task<HashSet<CategoryModel>> GetCategoriesAsync() => await DbAccess_Old.GetCategoriesAsync();
//TODO: Rework this! //TODO: Rework this!
// Subscribe to a feed. // Subscribe to a feed.
public async Task<bool> AddSubscriptionAsync(string url, CategoryModel? group = null) public async Task<bool> AddSubscriptionAsync(string url, CategoryModel? group = null)
{ {
// Check for valid feed url /*if (!SyndicationManager.GetFeed(url, out GenericSyndicationFeed? genFeed)) return false;*/
bool validate = SyndicationDiscoveryUtility.UriExists(new Uri(url)); var feed = SyndicationManager.CreateSyndication(url);
if (!validate) return false;
// Check if feed exists in db // Check if feed exists in db
FeedModel? fModel = await DbAccess.GetFeedAsync(url); FeedModel? fModel = await DbAccess_Old.GetFeedAsync(url);
// If not exists fetch feed & add. // If not exists fetch feed & add.
if (fModel == null) if (fModel == null)
{ {
GenericSyndicationFeed genFeed = GenericSyndicationFeed.Create(new Uri(url)); /*if (!SyndicationManager.TryGetGenericFeed(url, out GenericSyndicationFeed? genFeed)) return false;*/
/*if (genFeed == null) return false;
fModel = FromResource(genFeed.Resource); fModel = FromResource(genFeed.Resource);
fModel.GroupId = group?.Id; fModel.GroupId = group?.Id;*/
// Add feed // Add feed
FeedModel? dbFeed = await DbAccess.SetFeedAsync(fModel); //FeedModel? dbFeed = await DbAccess.SetFeedAsync(fModel);
// Update/fetch items // Update/fetch items
//await DbAccess.FetchFeedItemsAsync(new string[] { fModel.Url }); //await DbAccess.FetchFeedItemsAsync(new string[] { fModel.Url });
} }
@ -64,7 +61,7 @@ namespace SharpRss.Services
model.Title = rssFeed.Channel.Title; model.Title = rssFeed.Channel.Title;
model.Description = rssFeed.Channel.Description; model.Description = rssFeed.Channel.Description;
model.Copyright = rssFeed.Channel.Copyright; model.Copyright = rssFeed.Channel.Copyright;
model.Url = rssFeed.Channel.SelfLink.ToString(); model.OriginalUrl = rssFeed.Channel.SelfLink.ToString();
model.ImageUrl = rssFeed.Channel.Image?.Url.ToString(); model.ImageUrl = rssFeed.Channel.Image?.Url.ToString();
model.Language = rssFeed.Channel.Language?.ToString(); model.Language = rssFeed.Channel.Language?.ToString();
break; break;
@ -84,13 +81,13 @@ namespace SharpRss.Services
var feeds = await GetFeedsAsync(); var feeds = await GetFeedsAsync();
} }
public async Task<HashSet<FeedModel>> GetFeedsAsync(string? groupId = null) => await DbAccess.GetFeedsAsync(groupId); public async Task<HashSet<FeedModel>> GetFeedsAsync(string? groupId = null) => await DbAccess_Old.GetFeedsAsync(groupId);
public async Task<HashSet<FeedModel>> GetUngroupedFeedsAsync() => await DbAccess.GetFeedsAsync(""); public async Task<HashSet<FeedModel>> GetUngroupedFeedsAsync() => await DbAccess_Old.GetFeedsAsync("");
public async Task<HashSet<FeedItemModel>> GetFeedItemsAsync(string feedId, string? groupId = null) => await GetFeedItemsFromFeedsAsync(new[] { feedId }, groupId); public async Task<HashSet<FeedItemModel>> GetFeedItemsAsync(string feedId, string? groupId = null) => await GetFeedItemsFromFeedsAsync(new[] { feedId }, groupId);
public async Task<HashSet<FeedItemModel>> GetFeedItemsFromFeedsAsync(string[] feedIds, string? groupId = null) public async Task<HashSet<FeedItemModel>> GetFeedItemsFromFeedsAsync(string[] feedIds, string? groupId = null)
{ {
var items = await DbAccess.GetFeedItemsAsync(feedIds); var items = await DbAccess_Old.GetFeedItemsAsync(feedIds);
return items; return items;
} }
@ -143,6 +140,7 @@ namespace SharpRss.Services
} }
private async void SetupTestGroupsAndFeedsAsync() private async void SetupTestGroupsAndFeedsAsync()
{ {
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");
//TODO: Make multiple adding of feed to a transaction, now throws an exception. //TODO: Make multiple adding of feed to a transaction, now throws an exception.
/*var groupRes = await CreateGroupAsync(new GroupModel() { Name = "Test" }); /*var groupRes = await CreateGroupAsync(new GroupModel() { Name = "Test" });
groupRes = await CreateGroupAsync(new GroupModel() { Name = "News" }); groupRes = await CreateGroupAsync(new GroupModel() { Name = "News" });
@ -165,8 +163,8 @@ namespace SharpRss.Services
throw; throw;
}*/ }*/
/*var groups = await GetGroupsAsync(); /*var groups = await GetGroupsAsync();
CategoryModel testGroup = groups.Single(x => x.Name == "News"); CategoryModel testGroup = groups.Single(x => x.Name == "News");*/
await AddSubscriptionAsync("https://www.nu.nl/rss/Algemeen", testGroup);*/
/*await AddFeedsAsync(new[] /*await AddFeedsAsync(new[]
{ {
"https://www.nu.nl/rss/Algemeen", "https://www.nu.nl/rss/Algemeen",

View File

@ -1,16 +1,133 @@
using System; using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Argotic.Common;
using Argotic.Extensions.Core;
using Argotic.Syndication; using Argotic.Syndication;
using Serilog;
using SharpRss.Models;
namespace SharpRss namespace SharpRss
{ {
/// <summary>
/// Struct that contains the necessary objects for adding/fetching feeds and items
/// </summary>
public struct SyndicationContainer
{
public GenericSyndicationFeed SyndicationFeed { get; set; }
public CategoryModel Category { get; set; }
public FeedModel FeedModel { get; set; }
public HashSet<FeedItemModel> FeedItems { get; set; }
}
public static class SyndicationManager public static class SyndicationManager
{ {
public static bool TryGetFeed(string feedUrl, out GenericSyndicationFeed? feed) public static SyndicationContainer CreateSyndication(string feedUrl)
{ {
feed = null; GenericSyndicationFeed? syndicationFeed = null;
Uri feedUri = new Uri(feedUrl); Uri feedUri = new Uri(feedUrl);
feed = GenericSyndicationFeed.Create(feedUri); try
return false; {
Log.Debug("Fetching feed: {FeedUri}", feedUri.ToString());
syndicationFeed = GenericSyndicationFeed.Create(feedUri);
}
catch (Exception e)
{
Log.Error(e,"Could not get feed: {FeedUrl}", feedUrl);
}
return ConstructSyndicationContainer(syndicationFeed);
}
public static Stream FeedToStream(GenericSyndicationFeed syndicationFeed)
{
MemoryStream memStream = new MemoryStream();
syndicationFeed.Resource.Save(memStream);
if (memStream.Length <= 0)
Log.Warning("Failed to serialize {FeedType} feed: {FeedUri}", syndicationFeed.Format.ToString(), syndicationFeed.Title);
memStream.Position = 0;
return memStream;
}
private static SyndicationContainer ConstructSyndicationContainer(GenericSyndicationFeed? syndicationFeed)
{
SyndicationContainer container = new SyndicationContainer();
if (syndicationFeed == null)
{
Log.Error("Could not construct syndication container!");
return container;
}
container.SyndicationFeed = syndicationFeed;
container.FeedModel = new FeedModel();
container.FeedItems = new HashSet<FeedItemModel>();
switch (syndicationFeed.Resource.Format)
{
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();
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()
};
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.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
};
container.FeedItems.Add(itemModel);
}
break;
default:
Log.Warning("Feed implementation missing!");
break;
}
return container;
} }
} }
} }

View File

@ -2,32 +2,45 @@ using System;
using System.IO; using System.IO;
using Serilog; using Serilog;
using Serilog.Formatting.Json; using Serilog.Formatting.Json;
using Serilog.Sinks.SystemConsole.Themes; using SharpRss;
using SharpRss.Models;
using ToolQit; using ToolQit;
using ToolQit.Containers; using ToolQit.Containers;
using WebSharpRSS.Models;
namespace WebSharpRSS namespace WebSharpRSS
{ {
public static class Bootstrapper public static class Bootstrapper
{ {
public static void SetAppDefaultSettings(this DataContainer dataCon) private static bool _defaultsSet;
private static bool _bootstrapped;
public static void Bootstrap()
{
if (_bootstrapped) return;
Caretaker.Settings.SetAppDefaultSettings();
SetupLogging();
Log.Information("Starting SharpRSS...");
DbAccess_Old.Initialize();
_bootstrapped = true;
}
private static void SetAppDefaultSettings(this DataContainer dataCon)
{ {
var paths = dataCon["Paths"]; var paths = dataCon["Paths"];
//paths.Set("FaviconResolveUrl", "https://icons.duckduckgo.com/ip3/{0}.ico", false); //paths.Set("FaviconResolveUrl", "https://icons.duckduckgo.com/ip3/{0}.ico", false);
paths.Set("FaviconResolveUrl", "http://www.google.com/s2/favicons?domain={0}", false); paths.Set("FaviconResolveUrl", "http://www.google.com/s2/favicons?domain={0}", false);
paths.Set("LogPath", Path.Combine(Environment.CurrentDirectory, "logs", "log_.json"), false); paths.Set("LogPath", Path.Combine(Environment.CurrentDirectory, "logs", "log_.json"), false);
_defaultsSet = true;
} }
private static LoggerConfiguration? _configuration; private static LoggerConfiguration? _configuration;
public static void SetupLogging() private static void SetupLogging()
{ {
if (!_defaultsSet) throw new Exception("Bootstrapper defaults are not initialized!");
if (_configuration != null) return; if (_configuration != null) return;
_configuration = new LoggerConfiguration() _configuration = new LoggerConfiguration()
.WriteTo.Console() .WriteTo.Console()
.WriteTo.File(new JsonFormatter(), Caretaker.Settings["Paths"].GetString("LogPath"), rollingInterval: RollingInterval.Day) .WriteTo.File(new JsonFormatter(), Caretaker.Settings["Paths"].GetString("LogPath"), rollingInterval: RollingInterval.Day)
.MinimumLevel.Verbose(); .MinimumLevel.Verbose(); // ONLY FOR DEBUGGING!!!
Log.Logger = _configuration.CreateLogger(); Log.Logger = _configuration.CreateLogger();
} }

View File

@ -20,8 +20,8 @@ namespace WebSharpRSS.Models
{ {
FeedModel = feedModel; FeedModel = feedModel;
Title = feedModel.Title ?? string.Empty; Title = feedModel.Title ?? string.Empty;
if (FeedModel.Url == null) return; if (FeedModel.OriginalUrl == null) return;
FaviconUrl = string.Format(Caretaker.Settings["Paths"].GetString("FaviconResolveUrl"), new Uri(FeedModel.Url).Host); FaviconUrl = string.Format(Caretaker.Settings["Paths"].GetString("FaviconResolveUrl"), new Uri(FeedModel.OriginalUrl).Host);
} }
public readonly CategoryModel? GroupModel; public readonly CategoryModel? GroupModel;
public readonly FeedModel? FeedModel; public readonly FeedModel? FeedModel;

View File

@ -57,7 +57,7 @@
else if (Gid != null) else if (Gid != null)
{ {
var feeds = await _rssService.GetFeedsAsync(Gid); var feeds = await _rssService.GetFeedsAsync(Gid);
var feedIds = feeds.Select(x => x.Url); var feedIds = feeds.Select(x => x.OriginalUrl);
var feedItems = await _rssService.GetFeedItemsFromFeedsAsync(feedIds.ToArray()); var feedItems = await _rssService.GetFeedItemsFromFeedsAsync(feedIds.ToArray());
items = feedItems.Select(x => FeedItemData.FromModel(x)).OrderBy(x => x.PublishingDate).Reverse().ToHashSet(); items = feedItems.Select(x => FeedItemData.FromModel(x)).OrderBy(x => x.PublishingDate).Reverse().ToHashSet();
} }

View File

@ -3,17 +3,11 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
using MudBlazor; using MudBlazor;
using MudBlazor.Services; using MudBlazor.Services;
using Serilog;
using SharpRss;
using SharpRss.Services; using SharpRss.Services;
using ToolQit;
using WebSharpRSS; using WebSharpRSS;
using WebSharpRSS.Models; using WebSharpRSS.Models;
Caretaker.Settings.SetAppDefaultSettings(); Bootstrapper.Bootstrap();
Bootstrapper.SetupLogging();
Log.Information("Starting...");
DbAccess.Initialize();
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages(); builder.Services.AddRazorPages();

View File

@ -51,7 +51,7 @@
if (_selectedItem == null) return; if (_selectedItem == null) return;
if (_selectedItem.FeedModel != null) if (_selectedItem.FeedModel != null)
{ {
_navigation.NavigateTo($"/list?fid={_selectedItem.FeedModel.Url}"); _navigation.NavigateTo($"/list?fid={_selectedItem.FeedModel.OriginalUrl}");
} }
else if (_selectedItem.GroupModel != null) else if (_selectedItem.GroupModel != null)
{ {
@ -70,7 +70,7 @@
protected override async void OnInitialized() protected override async void OnInitialized()
{ {
Log.Verbose("Loading guide data..."); Log.Verbose("Loading guide data...");
HashSet<object> items = await _rssService.GetGroupsFeedsAsync(); HashSet<object> items = await _rssService.GetCategoriesFeedsAsync();
_guideItems.UnionWith(ModelToTreeItem(items)); _guideItems.UnionWith(ModelToTreeItem(items));
StateHasChanged(); StateHasChanged();