2023-05-29 18:02:24 +02:00
|
|
|
using System;
|
2023-06-04 02:16:47 +02:00
|
|
|
using System.Collections.Generic;
|
|
|
|
using System.IO;
|
|
|
|
using System.Linq;
|
2023-06-11 02:28:16 +02:00
|
|
|
using System.Text;
|
2023-06-04 02:16:47 +02:00
|
|
|
using Argotic.Common;
|
|
|
|
using Argotic.Extensions.Core;
|
2023-05-29 18:02:24 +02:00
|
|
|
using Argotic.Syndication;
|
2023-06-04 02:16:47 +02:00
|
|
|
using SharpRss.Models;
|
2023-08-27 19:54:36 +02:00
|
|
|
using ToolQit;
|
|
|
|
using ToolQit.Logging;
|
2023-05-29 18:02:24 +02:00
|
|
|
|
|
|
|
namespace SharpRss
|
|
|
|
{
|
2023-06-04 02:16:47 +02:00
|
|
|
public struct SyndicationContainer
|
|
|
|
{
|
|
|
|
public GenericSyndicationFeed SyndicationFeed { get; set; }
|
|
|
|
public CategoryModel Category { get; set; }
|
2023-06-16 22:53:26 +02:00
|
|
|
public SyndicationModel SyndicationModel { get; set; }
|
|
|
|
public HashSet<SyndicationItemModel> SyndicationItems { get; set; }
|
2023-06-04 19:00:46 +02:00
|
|
|
public bool Fetched;
|
2023-06-04 02:16:47 +02:00
|
|
|
}
|
2023-05-29 18:02:24 +02:00
|
|
|
public static class SyndicationManager
|
|
|
|
{
|
2023-08-27 19:54:36 +02:00
|
|
|
private static ILog _log = LogManager.CreateLogger(typeof(SyndicationManager));
|
2023-06-04 02:16:47 +02:00
|
|
|
public static SyndicationContainer CreateSyndication(string feedUrl)
|
2023-05-29 18:02:24 +02:00
|
|
|
{
|
2023-06-04 02:16:47 +02:00
|
|
|
GenericSyndicationFeed? syndicationFeed = null;
|
2023-05-29 18:02:24 +02:00
|
|
|
Uri feedUri = new Uri(feedUrl);
|
2023-06-04 02:16:47 +02:00
|
|
|
try
|
|
|
|
{
|
2023-08-27 19:54:36 +02:00
|
|
|
_log.Information("Fetching syndication: {SyndicationUri}", feedUri.ToString());
|
2023-06-04 02:16:47 +02:00
|
|
|
syndicationFeed = GenericSyndicationFeed.Create(feedUri);
|
|
|
|
}
|
|
|
|
catch (Exception e)
|
|
|
|
{
|
2023-08-27 19:54:36 +02:00
|
|
|
_log.Error(e,"Could not get syndication: {SyndicationUrl}", feedUrl);
|
2023-06-04 02:16:47 +02:00
|
|
|
}
|
2023-06-11 02:28:16 +02:00
|
|
|
return ConstructSyndicationContainer(syndicationFeed, feedUrl);
|
2023-06-04 02:16:47 +02:00
|
|
|
}
|
|
|
|
|
2023-06-16 22:53:26 +02:00
|
|
|
public static Stream SyndicationToStream(GenericSyndicationFeed syndicationFeed)
|
2023-06-04 02:16:47 +02:00
|
|
|
{
|
|
|
|
MemoryStream memStream = new MemoryStream();
|
|
|
|
syndicationFeed.Resource.Save(memStream);
|
|
|
|
if (memStream.Length <= 0)
|
2023-08-27 19:54:36 +02:00
|
|
|
_log.Warning("Failed to serialize {SyndicationType} feed: {SyndicationUri}", syndicationFeed.Format.ToString(), syndicationFeed.Title);
|
2023-06-04 02:16:47 +02:00
|
|
|
memStream.Position = 0;
|
|
|
|
return memStream;
|
|
|
|
}
|
|
|
|
|
2023-06-16 22:53:26 +02:00
|
|
|
private static SyndicationContainer ConstructSyndicationContainer(GenericSyndicationFeed? syndicationFeed, string syndicationUrl)
|
2023-06-04 02:16:47 +02:00
|
|
|
{
|
|
|
|
SyndicationContainer container = new SyndicationContainer();
|
|
|
|
if (syndicationFeed == null)
|
|
|
|
{
|
2023-08-27 19:54:36 +02:00
|
|
|
_log.Warning("Could not construct syndication container!");
|
2023-06-04 02:16:47 +02:00
|
|
|
return container;
|
|
|
|
}
|
|
|
|
container.SyndicationFeed = syndicationFeed;
|
2023-06-16 22:53:26 +02:00
|
|
|
container.SyndicationModel = new SyndicationModel();
|
|
|
|
container.SyndicationItems = new HashSet<SyndicationItemModel>();
|
2023-06-04 02:16:47 +02:00
|
|
|
switch (syndicationFeed.Resource.Format)
|
|
|
|
{
|
|
|
|
case SyndicationContentFormat.Rss:
|
|
|
|
RssFeed rssFeed = (RssFeed)container.SyndicationFeed.Resource;
|
2023-06-10 19:06:30 +02:00
|
|
|
if (rssFeed.Channel == null) break;
|
2023-06-16 22:53:26 +02:00
|
|
|
container.SyndicationModel.EncodedUrl = EncodeUrlToBase64(syndicationUrl);
|
|
|
|
container.SyndicationModel.Title = rssFeed.Channel.Title ?? string.Empty;
|
|
|
|
container.SyndicationModel.SyndicationType = rssFeed.Format.ToString() ?? string.Empty;
|
2023-06-17 14:04:26 +02:00
|
|
|
container.SyndicationModel.Version = rssFeed.Version?.ToString() ?? string.Empty;
|
2023-06-16 22:53:26 +02:00
|
|
|
container.SyndicationModel.Description = rssFeed.Channel.Description ?? string.Empty;
|
|
|
|
container.SyndicationModel.Language = rssFeed.Channel.Language?.ToString() ?? string.Empty;
|
|
|
|
container.SyndicationModel.Copyright = rssFeed.Channel.Copyright ?? string.Empty;
|
|
|
|
container.SyndicationModel.PublicationDate = rssFeed.Channel.PublicationDate is { Ticks: > 0 } ? new DateTimeOffset(rssFeed.Channel.PublicationDate) : DateTimeOffset.MinValue;
|
|
|
|
container.SyndicationModel.Categories = rssFeed.Channel.Categories?.Select(x => x.Value).ToArray() ?? Array.Empty<string>();
|
|
|
|
container.SyndicationModel.ImageUrl = rssFeed.Channel.Image?.Url.ToString() ?? string.Empty;
|
2023-06-04 02:16:47 +02:00
|
|
|
foreach (var rssItem in rssFeed.Channel.Items)
|
|
|
|
{
|
2023-06-16 22:53:26 +02:00
|
|
|
SyndicationItemModel itemModel = new SyndicationItemModel()
|
2023-06-04 02:16:47 +02:00
|
|
|
{
|
2023-06-17 14:04:26 +02:00
|
|
|
Link = rssItem.Link?.ToString() ?? string.Empty,
|
2023-06-16 22:53:26 +02:00
|
|
|
EncodedSyndicationUrl = container.SyndicationModel.EncodedUrl ?? string.Empty,
|
|
|
|
Type = container.SyndicationModel.SyndicationType ?? string.Empty,
|
2023-06-10 19:06:30 +02:00
|
|
|
Title = rssItem.Title ?? string.Empty,
|
|
|
|
Description = rssItem.Description ?? string.Empty,
|
2023-06-10 20:27:26 +02:00
|
|
|
PublishingDate = rssItem.PublicationDate is not { Ticks: <= 0 } ? new DateTimeOffset(rssItem.PublicationDate) : DateTimeOffset.MinValue,
|
2023-06-10 19:06:30 +02:00
|
|
|
Authors = rssItem.Author != null ? new []{ rssItem.Author } : Array.Empty<string>(),
|
|
|
|
Categories = rssItem.Categories?.Select(x => x.Value).ToArray() ?? Array.Empty<string>(),
|
|
|
|
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
|
2023-06-04 02:16:47 +02:00
|
|
|
};
|
2023-06-16 22:53:26 +02:00
|
|
|
container.SyndicationItems.Add(itemModel);
|
2023-06-04 02:16:47 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SyndicationContentFormat.Atom:
|
|
|
|
AtomFeed atomFeed = (AtomFeed)container.SyndicationFeed.Resource;
|
2023-06-16 22:53:26 +02:00
|
|
|
container.SyndicationModel.EncodedUrl = EncodeUrlToBase64(syndicationUrl);
|
|
|
|
container.SyndicationModel.Title = atomFeed.Title?.Content ?? string.Empty;
|
|
|
|
container.SyndicationModel.SyndicationType = atomFeed.Format.ToString() ?? string.Empty;
|
2023-06-17 14:04:26 +02:00
|
|
|
container.SyndicationModel.Version = atomFeed.Version?.ToString() ?? string.Empty;
|
2023-06-16 22:53:26 +02:00
|
|
|
container.SyndicationModel.Description = atomFeed.Subtitle?.Content ?? string.Empty;
|
|
|
|
container.SyndicationModel.Language = atomFeed.Language?.ToString() ?? string.Empty;
|
|
|
|
container.SyndicationModel.Copyright = atomFeed.Rights?.Content ?? string.Empty;
|
|
|
|
container.SyndicationModel.PublicationDate = atomFeed.UpdatedOn is { Ticks: > 0 } ? new DateTimeOffset(atomFeed.UpdatedOn) : DateTimeOffset.MinValue;
|
|
|
|
container.SyndicationModel.Categories = atomFeed.Categories?.Select(x => x.Label).ToArray() ?? Array.Empty<string>();
|
|
|
|
container.SyndicationModel.ImageUrl = atomFeed.Icon?.Uri.ToString() ?? string.Empty;
|
2023-06-04 02:16:47 +02:00
|
|
|
foreach (var entry in atomFeed.Entries)
|
|
|
|
{
|
2023-06-16 22:53:26 +02:00
|
|
|
SyndicationItemModel itemModel = new SyndicationItemModel()
|
2023-06-04 02:16:47 +02:00
|
|
|
{
|
2023-06-17 14:04:26 +02:00
|
|
|
Link = entry.Id?.Uri.ToString() ?? string.Empty,
|
2023-06-16 22:53:26 +02:00
|
|
|
EncodedSyndicationUrl = container.SyndicationModel?.EncodedUrl ?? string.Empty,
|
|
|
|
Type = container.SyndicationModel?.SyndicationType ?? string.Empty,
|
2023-06-10 19:06:30 +02:00
|
|
|
Title = entry.Title?.Content ?? string.Empty,
|
|
|
|
Description = entry.Summary?.Content ?? string.Empty,
|
2023-06-16 22:53:26 +02:00
|
|
|
ItemUpdatedDate = entry.UpdatedOn is { Ticks: > 0 } ? new DateTimeOffset(entry.UpdatedOn) : DateTimeOffset.Now,
|
2023-06-17 14:04:26 +02:00
|
|
|
PublishingDate = entry.PublishedOn is { Ticks: > 0 } ? new DateTimeOffset(entry.PublishedOn) : DateTimeOffset.MinValue,
|
2023-06-10 19:06:30 +02:00
|
|
|
Authors = entry.Authors?.Select(auth => auth.Name).ToArray() ?? Array.Empty<string>(),
|
|
|
|
Categories = entry.Categories?.Select(cat => cat.Label).ToArray() ?? Array.Empty<string>(),
|
|
|
|
Content = entry.Content?.Content ?? string.Empty
|
2023-06-04 02:16:47 +02:00
|
|
|
};
|
2023-06-16 22:53:26 +02:00
|
|
|
container.SyndicationItems.Add(itemModel);
|
2023-06-04 02:16:47 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
2023-08-27 19:54:36 +02:00
|
|
|
_log.Warning("Syndication implementation missing!");
|
2023-06-04 02:16:47 +02:00
|
|
|
break;
|
|
|
|
}
|
2023-06-04 19:00:46 +02:00
|
|
|
container.Fetched = true;
|
2023-06-04 02:16:47 +02:00
|
|
|
return container;
|
2023-05-29 18:02:24 +02:00
|
|
|
}
|
2023-06-16 22:53:26 +02:00
|
|
|
public static string EncodeUrlToBase64(string url) => Convert.ToBase64String(Encoding.UTF8.GetBytes(url));
|
|
|
|
public static string DecodeUrlFromBase64(string base64)
|
2023-06-11 02:28:16 +02:00
|
|
|
{
|
|
|
|
byte[] bytes = Convert.FromBase64String(base64);
|
|
|
|
return Encoding.Default.GetString(bytes);
|
|
|
|
}
|
2023-06-16 22:53:26 +02:00
|
|
|
public static string FormatStringArrayToString(string[] sArr) => string.Join('|', sArr);
|
|
|
|
public static string[] StringToStringArray(string stringFormat) => stringFormat.Split('|');
|
|
|
|
|
2023-06-10 19:06:30 +02:00
|
|
|
public static void Init()
|
|
|
|
{
|
|
|
|
DbAccess.Initialize();
|
|
|
|
}
|
2023-05-29 18:02:24 +02:00
|
|
|
}
|
|
|
|
}
|