mirror of
https://github.com/hmaxnl/SharpRSS.git
synced 2025-01-18 21:04:21 +01:00
Implenting rssservice
This commit is contained in:
parent
f83b4451a7
commit
29f9abd5de
|
@ -4,12 +4,7 @@ namespace SharpRss.Models
|
||||||
{
|
{
|
||||||
public class FeedItemModel
|
public class FeedItemModel
|
||||||
{
|
{
|
||||||
public FeedItemModel()
|
public string Id { get; set; } = string.Empty;
|
||||||
{
|
|
||||||
Id = Guid.NewGuid().ToString();
|
|
||||||
LastUpdated = DateTime.Now;
|
|
||||||
}
|
|
||||||
public string Id { get; set; }
|
|
||||||
public string? FeedId { get; set; }
|
public string? FeedId { get; set; }
|
||||||
public bool Read { get; set; }
|
public bool Read { get; set; }
|
||||||
public string Type { get; set; } = string.Empty;
|
public string Type { get; set; } = string.Empty;
|
||||||
|
|
|
@ -4,20 +4,23 @@ namespace SharpRss.Models
|
||||||
{
|
{
|
||||||
public class FeedModel
|
public class FeedModel
|
||||||
{
|
{
|
||||||
|
public FeedModel() { }
|
||||||
public FeedModel(string rssUrl)
|
public FeedModel(string rssUrl)
|
||||||
{
|
{
|
||||||
Id = Guid.NewGuid().ToString();
|
|
||||||
Url = rssUrl;
|
Url = rssUrl;
|
||||||
|
Id = Guid.NewGuid().ToString();
|
||||||
|
Copyright = "EMPTY";
|
||||||
|
ImageUrl = "EMPTY";
|
||||||
}
|
}
|
||||||
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? GroupId { get; set; }
|
public string? GroupId { get; set; }
|
||||||
public string FeedType { get; set; } = string.Empty;
|
public string FeedType { 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; } = "EMPTY";
|
||||||
public DateTimeOffset DateAdded { get; set; }
|
public DateTimeOffset DateAdded { get; set; }
|
||||||
public DateTimeOffset LastUpdated { get; set; }
|
public DateTimeOffset LastUpdated { get; set; }
|
||||||
public string ImageUrl { get; set; } = string.Empty;
|
public string ImageUrl { get; set; } = "EMPTY";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,8 +23,8 @@ namespace SharpRss.Services
|
||||||
private readonly string _feedTable = "feed_data";
|
private readonly string _feedTable = "feed_data";
|
||||||
private readonly string _feedItemTable = "feed_item_data";
|
private readonly string _feedItemTable = "feed_item_data";
|
||||||
|
|
||||||
// Group
|
// Groups
|
||||||
public async Task<HashSet<GroupModel?>> GetGroupsAsync(string? groupName = null)
|
public async Task<HashSet<GroupModel>> GetGroupsAsync(string? groupName = null)
|
||||||
{
|
{
|
||||||
_sqlConn.Open();
|
_sqlConn.Open();
|
||||||
using SqliteCommand cmd = new SqliteCommand(groupName != null ? $"SELECT * FROM {_groupTable} WHERE name=@name;" : $"SELECT * FROM {_groupTable}", _sqlConn)
|
using SqliteCommand cmd = new SqliteCommand(groupName != null ? $"SELECT * FROM {_groupTable} WHERE name=@name;" : $"SELECT * FROM {_groupTable}", _sqlConn)
|
||||||
|
@ -35,7 +35,7 @@ namespace SharpRss.Services
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
await using SqliteDataReader reader = await cmd.ExecuteReaderAsync();
|
await using SqliteDataReader reader = await cmd.ExecuteReaderAsync();
|
||||||
HashSet<GroupModel?> groups = new HashSet<GroupModel?>();
|
HashSet<GroupModel> groups = new HashSet<GroupModel>();
|
||||||
while (reader.Read())
|
while (reader.Read())
|
||||||
{
|
{
|
||||||
groups.Add(new GroupModel()
|
groups.Add(new GroupModel()
|
||||||
|
@ -51,7 +51,7 @@ namespace SharpRss.Services
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a group if not exists else will update the group.
|
/// Creates a group if not exists then update the group.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="groupModel"></param>
|
/// <param name="groupModel"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
|
@ -94,20 +94,35 @@ namespace SharpRss.Services
|
||||||
_sqlConn.Close();
|
_sqlConn.Close();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
// Feed
|
// Feeds
|
||||||
public async Task<HashSet<FeedModel>> GetFeedsAsync(string? feedName = null)
|
public async Task<HashSet<FeedModel>> GetFeedsAsync(GroupModel? group = null)
|
||||||
{
|
{
|
||||||
HashSet<FeedModel> feeds = new HashSet<FeedModel>();
|
HashSet<FeedModel> feeds = new HashSet<FeedModel>();
|
||||||
_sqlConn.Open();
|
_sqlConn.Open();
|
||||||
using SqliteCommand cmd = new SqliteCommand(feedName != null ? $"SELECT * FROM {_feedTable} WHERE name=@name" : $"SELECT * FROM {_feedTable}", _sqlConn)
|
using SqliteCommand cmd = new SqliteCommand(group != null ? $"SELECT * FROM {_feedTable} WHERE group_id=@groupid" : $"SELECT * FROM {_feedTable}", _sqlConn)
|
||||||
{
|
{
|
||||||
Parameters =
|
Parameters =
|
||||||
{
|
{
|
||||||
new SqliteParameter("name", feedName)
|
new SqliteParameter("groupId", group?.Id == string.Empty ? null : group?.Id)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
int affected = await cmd.ExecuteNonQueryAsync();
|
await using SqliteDataReader reader = await cmd.ExecuteReaderAsync();
|
||||||
Log.Verbose("{FeedAmount} feeds found!", affected);
|
while (reader.Read())
|
||||||
|
{
|
||||||
|
feeds.Add(new FeedModel()
|
||||||
|
{
|
||||||
|
Id = reader["id"].ToString(),
|
||||||
|
Url = reader["url"].ToString(),
|
||||||
|
GroupId = reader["group_id"].ToString(),
|
||||||
|
FeedType = reader["feed_type"].ToString(),
|
||||||
|
Description = reader["description"].ToString(),
|
||||||
|
Language = reader["language"].ToString(),
|
||||||
|
Copyright = reader["copyright"].ToString(),
|
||||||
|
DateAdded = 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),
|
||||||
|
ImageUrl = reader["image_url"].ToString()
|
||||||
|
});
|
||||||
|
}
|
||||||
_sqlConn.Close();
|
_sqlConn.Close();
|
||||||
return feeds;
|
return feeds;
|
||||||
}
|
}
|
||||||
|
@ -116,8 +131,7 @@ namespace SharpRss.Services
|
||||||
{
|
{
|
||||||
bool result = false;
|
bool result = false;
|
||||||
_sqlConn.Open();
|
_sqlConn.Open();
|
||||||
using SqliteCommand cmd = new SqliteCommand($"INSERT OR REPLACE INTO {_feedTable} (id, url, group_id, feed_type, description, language, copyright, date_added, last_updated, image_url)" +
|
using SqliteCommand cmd = new SqliteCommand($"INSERT OR REPLACE INTO {_feedTable} (id, url, group_id, feed_type, description, language, copyright, date_added, last_updated, image_url) VALUES (IFNULL((SELECT id FROM {_feedTable} WHERE url=@url), @id), @url, @groupId, @feedType, @description, @language, @copyright, @dateAdded, @lastUpdated, @imageUrl)", _sqlConn)
|
||||||
$"VALUES (IFNULL((SELECT id FROM {_feedTable} WHERE url=@url), @id), @url, @groupId, @feedType, @description, @language, @copyright, @dateAdded, @lastUpdated, @imageUrl)", _sqlConn)
|
|
||||||
{
|
{
|
||||||
Parameters =
|
Parameters =
|
||||||
{
|
{
|
||||||
|
@ -127,10 +141,10 @@ namespace SharpRss.Services
|
||||||
new SqliteParameter("feedType", feedModel.FeedType),
|
new SqliteParameter("feedType", feedModel.FeedType),
|
||||||
new SqliteParameter("description", feedModel.Description),
|
new SqliteParameter("description", feedModel.Description),
|
||||||
new SqliteParameter("language", feedModel.Language),
|
new SqliteParameter("language", feedModel.Language),
|
||||||
new SqliteParameter("copyright", feedModel.Copyright),
|
new SqliteParameter("copyright", "EMPTY"),
|
||||||
new SqliteParameter("dateAdded", feedModel.DateAdded.ToUnixTimeMilliseconds()),
|
new SqliteParameter("dateAdded", feedModel.DateAdded.ToUnixTimeMilliseconds()),
|
||||||
new SqliteParameter("lastUpdated", feedModel.LastUpdated.ToUnixTimeMilliseconds()),
|
new SqliteParameter("lastUpdated", feedModel.LastUpdated.ToUnixTimeMilliseconds()),
|
||||||
new SqliteParameter("imageUrl", feedModel.ImageUrl)
|
new SqliteParameter("imageUrl", "EMPTY")
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
int affected = await cmd.ExecuteNonQueryAsync();
|
int affected = await cmd.ExecuteNonQueryAsync();
|
||||||
|
@ -156,7 +170,7 @@ namespace SharpRss.Services
|
||||||
_sqlConn.Close();
|
_sqlConn.Close();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
// Feed item
|
// Feed items
|
||||||
public async Task<HashSet<FeedItemModel>> GetFeedItemsAsync()
|
public async Task<HashSet<FeedItemModel>> GetFeedItemsAsync()
|
||||||
{
|
{
|
||||||
HashSet<FeedItemModel> feeditems = new HashSet<FeedItemModel> ();
|
HashSet<FeedItemModel> feeditems = new HashSet<FeedItemModel> ();
|
||||||
|
|
|
@ -3,24 +3,95 @@ using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using CodeHollow.FeedReader;
|
using CodeHollow.FeedReader;
|
||||||
|
using Serilog;
|
||||||
using SharpRss.Models;
|
using SharpRss.Models;
|
||||||
using ToolQit.Extensions;
|
using ToolQit.Extensions;
|
||||||
|
|
||||||
namespace SharpRss.Services
|
namespace SharpRss.Services
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Managing RSS feeds and categories.
|
/// Managing RSS feeds and groups.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class RssService : IDisposable
|
public class RssService : IDisposable
|
||||||
{
|
{
|
||||||
public RssService()
|
public RssService()
|
||||||
{
|
{
|
||||||
SetupTestCategoriesAndFeedsAsync();
|
SetupTestCategoriesAndFeedsAsync();
|
||||||
}
|
}
|
||||||
private readonly DatabaseService _dbService = new DatabaseService();
|
private readonly DatabaseService _dbService = new DatabaseService();
|
||||||
|
|
||||||
|
|
||||||
|
public async Task<bool> CreateGroupAsync(GroupModel group) => await _dbService.SetGroupAsync(group);
|
||||||
|
public async Task<HashSet<GroupModel>> GetGroupsAsync() => await _dbService.GetGroupsAsync();
|
||||||
|
|
||||||
|
public async Task<bool> AddFeed(string rssUrl, GroupModel? group = null)
|
||||||
|
{
|
||||||
|
bool result = false;
|
||||||
|
Feed fetched = await FetchFeed(rssUrl);
|
||||||
|
if (fetched == null)
|
||||||
|
return result;
|
||||||
|
FeedModel feedModel = new FeedModel(rssUrl)
|
||||||
|
{
|
||||||
|
GroupId = group?.Id,
|
||||||
|
FeedType = fetched.Type.ToString(),
|
||||||
|
Description = fetched.Description,
|
||||||
|
Language = fetched.Language,
|
||||||
|
Copyright = fetched.Copyright,
|
||||||
|
DateAdded = DateTimeOffset.Now,
|
||||||
|
LastUpdated = DateTimeOffset.Now,
|
||||||
|
ImageUrl = fetched.ImageUrl,
|
||||||
|
};
|
||||||
|
result = await _dbService.SetFeedAsync(feedModel);
|
||||||
|
if (!result)
|
||||||
|
return result;
|
||||||
|
if (await AddFeedItems(fetched.Items, feedModel) == 0)
|
||||||
|
Log.Warning($"No feed items added to feed: {feedModel.Url}");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
public async Task<HashSet<FeedModel>> GetFeedsAsync(GroupModel? group = null) => await _dbService.GetFeedsAsync(group);
|
||||||
|
public async Task<HashSet<FeedModel>> GetUngroupedFeedsAsync() => await _dbService.GetFeedsAsync(new GroupModel() { Id = "" });
|
||||||
|
private async Task<int> AddFeedItems(IList<FeedItem> items, FeedModel feedModel)
|
||||||
|
{
|
||||||
|
int result = 0;
|
||||||
|
if (!items.Any())
|
||||||
|
return result;
|
||||||
|
HashSet<FeedItemModel> itemModels = new HashSet<FeedItemModel>();
|
||||||
|
foreach (FeedItem item in items)
|
||||||
|
{
|
||||||
|
itemModels.Add(new FeedItemModel()
|
||||||
|
{
|
||||||
|
Id = item.Id,
|
||||||
|
FeedId = feedModel.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
|
||||||
|
});
|
||||||
|
}
|
||||||
|
result = await _dbService.SetFeedItemsAsync(itemModels);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
private async Task<Feed> FetchFeed(string url)
|
||||||
|
{
|
||||||
|
var urls = await FeedReader.ParseFeedUrlsAsStringAsync(url);
|
||||||
|
string feedurl = url;
|
||||||
|
if (urls.Any())
|
||||||
|
feedurl = urls.First();
|
||||||
|
return await FeedReader.ReadAsync(feedurl);
|
||||||
|
}
|
||||||
private async void SetupTestCategoriesAndFeedsAsync()
|
private async void SetupTestCategoriesAndFeedsAsync()
|
||||||
{
|
{
|
||||||
|
var groups = await GetGroupsAsync();
|
||||||
|
GroupModel testGroup = groups.Single(x => x.Name == "Test");
|
||||||
|
var res = await AddFeed("http://fedoramagazine.org/feed/", testGroup);
|
||||||
|
res = await AddFeed("https://www.nasa.gov/rss/dyn/breaking_news.rss", testGroup);
|
||||||
|
res = await AddFeed("https://journals.plos.org/plosone/feed/atom", testGroup);
|
||||||
|
res = await AddFeed("https://itsfoss.com/feed", testGroup);
|
||||||
|
|
||||||
/*bool result = await _dbService.SetGroupAsync(new GroupModel() { Name = "News" });
|
/*bool result = await _dbService.SetGroupAsync(new GroupModel() { Name = "News" });
|
||||||
result = await _dbService.SetGroupAsync(new GroupModel() { Name = "Tech" });
|
result = await _dbService.SetGroupAsync(new GroupModel() { Name = "Tech" });
|
||||||
result = await _dbService.SetGroupAsync(new GroupModel() { Name = "Science" });
|
result = await _dbService.SetGroupAsync(new GroupModel() { Name = "Science" });
|
||||||
|
|
2
ToolQit
2
ToolQit
|
@ -1 +1 @@
|
||||||
Subproject commit af3abae0445b30c3ef58579a18e6e179eaea5986
|
Subproject commit 0f3a3fb0f9145aad31a44eb25159d0f2a5a1c7fd
|
|
@ -9,21 +9,17 @@ namespace WebSharpRSS.Models
|
||||||
{
|
{
|
||||||
_service = rssService;
|
_service = rssService;
|
||||||
CategoryModel = catModel;
|
CategoryModel = catModel;
|
||||||
Initialize();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public TreeItemData(FeedModel feedModel, RssService rssService)
|
public TreeItemData(FeedModel feedModel, RssService rssService)
|
||||||
{
|
{
|
||||||
_service = rssService;
|
_service = rssService;
|
||||||
FeedModel = feedModel;
|
FeedModel = feedModel;
|
||||||
|
|
||||||
Initialize();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly RssService _service;
|
private readonly RssService _service;
|
||||||
public readonly GroupModel? CategoryModel;
|
public readonly GroupModel? CategoryModel;
|
||||||
public readonly FeedModel? FeedModel;
|
public readonly FeedModel? FeedModel;
|
||||||
//private HashSet<FeedModel> _feedModels;
|
|
||||||
|
|
||||||
public string Title { get; set; } = string.Empty;
|
public string Title { get; set; } = string.Empty;
|
||||||
public bool IsSelected { get; set; }
|
public bool IsSelected { get; set; }
|
||||||
|
@ -33,57 +29,5 @@ namespace WebSharpRSS.Models
|
||||||
// Category
|
// Category
|
||||||
public bool HasChild { get; set; }
|
public bool HasChild { get; set; }
|
||||||
public bool IsExpanded { get; set; }
|
public bool IsExpanded { get; set; }
|
||||||
//public HashSet<TreeItemData>? Feeds { get; set; }
|
|
||||||
// Feed
|
|
||||||
//public Feed? Feed { get; private set; }
|
|
||||||
/*public int FeeditemCount
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (CategoryModel != null && Feeds != null)
|
|
||||||
{
|
|
||||||
int feedsCount = 0;
|
|
||||||
foreach (var item in Feeds)
|
|
||||||
{
|
|
||||||
if (item.Feed != null)
|
|
||||||
feedsCount += item.FeeditemCount;
|
|
||||||
}
|
|
||||||
return feedsCount;
|
|
||||||
}
|
|
||||||
else if (Feed != null)
|
|
||||||
return Feed.Items.Count;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
|
|
||||||
private async void Initialize()
|
|
||||||
{
|
|
||||||
/*if (CategoryModel != null)
|
|
||||||
{
|
|
||||||
Title = CategoryModel.Name;
|
|
||||||
Icon = Icons.Material.Filled.Category;
|
|
||||||
/*_feedModels = await _service.GetFeedsAsync(CategoryModel);
|
|
||||||
if (_feedModels.Any())
|
|
||||||
Feeds = _feedModels.Select(x => new TreeItemData(x, _service)).OrderBy(x => x.Title).ToHashSet();#1#
|
|
||||||
}
|
|
||||||
|
|
||||||
if (FeedModel != null)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
//Feed = await _service.GetFeedAsync(FeedModel.FeedUrl);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Log.Error(e, "Error fetching feed: {FeedUrl}", FeedModel.FeedUrl);
|
|
||||||
Feed = null;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Icon = Icons.Material.Filled.RssFeed;
|
|
||||||
Title = Feed.Title;
|
|
||||||
string faviconAddress = Feed.Link.Remove(Feed.Link.IndexOf("http", StringComparison.Ordinal), Feed.Link.IndexOf("://", StringComparison.Ordinal) + 3);
|
|
||||||
FaviconUrl = string.Format(Caretaker.Settings["Paths"].GetString("FaviconResolveUrl"), faviconAddress);
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -9,9 +9,9 @@
|
||||||
@inject FeedStateContainer _stateContainer;
|
@inject FeedStateContainer _stateContainer;
|
||||||
@inject RssService _rssService
|
@inject RssService _rssService
|
||||||
|
|
||||||
<MudStack Spacing="2">
|
@*<MudStack Spacing="2">
|
||||||
@*<MudTreeView Items="_guideItems" @bind-SelectedValue="SelectedItem" Hover="true">
|
<MudTreeView Items="_guideItems" @bind-SelectedValue="SelectedItem" Hover="true">
|
||||||
$1$<ItemTemplate>
|
<ItemTemplate>
|
||||||
<MudTreeViewItem @bind-Expanded="@context.IsExpanded" Items="@context.Feeds" Value="@context" CanExpand="@context.HasChild" @onclick="ItemClicked">
|
<MudTreeViewItem @bind-Expanded="@context.IsExpanded" Items="@context.Feeds" Value="@context" CanExpand="@context.HasChild" @onclick="ItemClicked">
|
||||||
<Content>
|
<Content>
|
||||||
<div style="display: grid; grid-template-columns: 1fr auto; align-items: center; width: 100%">
|
<div style="display: grid; grid-template-columns: 1fr auto; align-items: center; width: 100%">
|
||||||
|
@ -33,13 +33,13 @@
|
||||||
</div>
|
</div>
|
||||||
</Content>
|
</Content>
|
||||||
</MudTreeViewItem>
|
</MudTreeViewItem>
|
||||||
</ItemTemplate>#1#
|
</ItemTemplate>
|
||||||
</MudTreeView>*@
|
</MudTreeView>
|
||||||
</MudStack>
|
</MudStack>*@
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private MudTheme Theme = new MudTheme();
|
private MudTheme Theme = new MudTheme();
|
||||||
//private readonly HashSet<TreeItemData> _guideItems = new HashSet<TreeItemData>();
|
private readonly HashSet<TreeItemData> _guideItems = new HashSet<TreeItemData>();
|
||||||
/*private TreeItemData? _selectedItem;
|
/*private TreeItemData? _selectedItem;
|
||||||
private TreeItemData? SelectedItem
|
private TreeItemData? SelectedItem
|
||||||
{
|
{
|
||||||
|
|
Binary file not shown.
Loading…
Reference in New Issue
Block a user