using System; using System.Collections.Generic; using System.IO; using System.Linq; using Argotic.Common; using Argotic.Extensions.Core; using Argotic.Syndication; using Serilog; using SharpRss.Models; namespace SharpRss { public struct SyndicationContainer { public GenericSyndicationFeed SyndicationFeed { get; set; } public CategoryModel Category { get; set; } public FeedModel FeedModel { get; set; } public HashSet FeedItems { get; set; } public bool Fetched; } public static class SyndicationManager { public static SyndicationContainer CreateSyndication(string feedUrl) { GenericSyndicationFeed? syndicationFeed = null; Uri feedUri = new Uri(feedUrl); try { 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(); 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; } container.Fetched = true; return container; } } }