Skip to content

Commit

Permalink
Parse and display media from Mastodon RSS
Browse files Browse the repository at this point in the history
  • Loading branch information
dam5s committed Feb 15, 2024
1 parent 72b6d28 commit 7fb6654
Show file tree
Hide file tree
Showing 10 changed files with 96 additions and 22 deletions.
11 changes: 10 additions & 1 deletion Damo.Io.Server/src/Article.fs
Original file line number Diff line number Diff line change
@@ -1,11 +1,20 @@
module DamoIoServer.Article

open FeedsProcessing.Article
open Time
open DamoIoServer.Source

type Article =
type MediaRecord = { Url: string; Description: string }

module MediaRecord =
let ofMedia (media: Media) =
{ Url = media.Url
Description = media.Description }

type ArticleRecord =
{ Title: string option
Link: string option
Content: string
Media: MediaRecord option
Date: Posix option
Source: Source }
3 changes: 1 addition & 2 deletions Damo.Io.Server/src/ArticleListTemplate.fs
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,9 @@ let private sourceLink selectedSources source =

li [] [ a attrs [ str (Source.toString source) ] ]

let render (articles: Article list) (sources: Source list) : XmlNode =
let render (articles: ArticleRecord list) (sources: Source list) : XmlNode =
let sourceLinks = Source.all |> List.map (sourceLink sources)
let articleList = articles |> List.map ArticleTemplate.render

let logo = h1 [] [ str "damo.io" ]
let menu = ul [ _class "main-menu" ] sourceLinks

Expand Down
20 changes: 15 additions & 5 deletions Damo.Io.Server/src/ArticleTemplate.fs
Original file line number Diff line number Diff line change
Expand Up @@ -31,28 +31,34 @@ let private dateToString posix =

open Giraffe.ViewEngine

let private articleTitle (article: Article) title =
let private articleTitle (article: ArticleRecord) title =
match article.Link with
| Some link -> h1 [] [ a [ _href link ] [ str title ] ]
| None -> h1 [] [ str title ]

let private articleDate (date: Posix) =
h2 [ _class "date" ] [ str (dateToString date) ]

let private articleHeader (article: Article) =
let private articleHeader (article: ArticleRecord) =
header
[]
(List.choose
id
[ Option.map (articleTitle article) article.Title
Option.map articleDate article.Date ])

let private trySourceLink (article: Article) : XmlNode option =
let private trySourceLink (article: ArticleRecord) : XmlNode option =
match (article.Title, article.Link) with
| None, Some url -> Some(nav [] [ a [ _href url; _target "_blank" ] [ str "Source" ] ])
| _, _ -> None

let render (article: Article) : XmlNode =
let private renderMedia (media: MediaRecord) : XmlNode =
figure
[]
[ img [ _src media.Url; _alt media.Description ]
figcaption [] [ str media.Description ] ]

let render (article: ArticleRecord) : XmlNode =
let articleHeader = articleHeader article
let articleContent = section [] [ rawText article.Content ]
let maybeSourceLink = trySourceLink article
Expand All @@ -62,6 +68,10 @@ let render (article: Article) : XmlNode =
[ yield articleHeader
yield articleContent

match article.Media with
| Some media -> yield renderMedia media
| None -> ()

match maybeSourceLink with
| Some sourceLink -> yield sourceLink
| _ -> () ]
| None -> () ]
7 changes: 4 additions & 3 deletions Damo.Io.Server/src/ArticlesRepository.fs
Original file line number Diff line number Diff line change
Expand Up @@ -40,18 +40,19 @@ let private aboutContent =
</p>
"""

let private about: Article =
let private about: ArticleRecord =
{ Title = Some "About"
Link = None
Content = aboutContent
Media = None
Date = None
Source = About }

let mutable private allRecords: Article list = []
let mutable private allRecords: ArticleRecord list = []

let findAll () = about :: allRecords

type FindAllBySources = Source list -> Article list
type FindAllBySources = Source list -> ArticleRecord list

let findAllBySources: FindAllBySources =
fun sources -> findAll () |> List.filter (fun r -> List.contains r.Source sources)
Expand Down
5 changes: 2 additions & 3 deletions Damo.Io.Server/src/FeedsProcessor.fs
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
module DamoIoServer.FeedsProcessor

open DamoIoServer.Article
open FSharp.Control
open FeedsProcessing
open FeedsProcessing.Article
open FeedsProcessing.DataGateway
open FeedsProcessing.Feeds
open FeedsProcessing.ProcessingResult
open FeedsProcessing.Xml
open Microsoft.Extensions.Logging

type ArticleRecord = DamoIoServer.Article.Article

let private articleToRecord sourceType (article: Article) : ArticleRecord =
{ Title = Article.title article
Link = Article.link article
Content = Article.content article
Media = Article.media article |> Option.map MediaRecord.ofMedia
Date = Article.date article
Source = sourceType }

Expand Down
20 changes: 17 additions & 3 deletions Damo.Io.Server/www/styles/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ pre {
background-color: var(--code-bg-color);
padding: 2.4rem;
margin-bottom: 2.4rem;
border-radius: 1rem;
border-radius: .5rem;

font-family: var(--monospace);
font-size: 1.4rem;
Expand Down Expand Up @@ -188,12 +188,26 @@ p {
em, strong {
font-weight: 700;
}

figure {
margin: 0;

img {
border-radius: .5rem;
}

figcaption {
font-size: 1.4rem;
text-align: center;
font-style: italic;
}
}

article {
background-color: var(--card-color);
padding: 2.4rem 4.8rem;
margin: 0 0 2.4rem 0;
border-radius: 3rem;
border-radius: 1rem;
box-shadow: var(--card-shadow);
}

Expand Down Expand Up @@ -230,7 +244,7 @@ article section {
article.Social {
background-image: url('data:image/svg+xml;utf8,%3Csvg%20height%3D%222500%22%20width%3D%222331%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%22-0.41%200.22%20747.62%20801.4499999999999%22%3E%3Cpath%20d%3D%22M729.94%20479.5c-10.96%2056.4-98.17%20118.12-198.34%20130.08-52.23%206.23-103.66%2011.96-158.49%209.44-89.68-4.1-160.45-21.4-160.45-21.4%200%208.73.54%2017.04%201.62%2024.81%2011.66%2088.52%2087.76%2093.82%20159.84%2096.29%2072.76%202.49%20137.55-17.94%20137.55-17.94l2.99%2065.79s-50.89%2027.32-141.55%2032.35c-50%202.75-112.07-1.26-184.37-20.39C31.94%20737.02%204.97%20569.86.85%20400.26-.41%20349.9.37%20302.42.37%20262.7.37%2089.27%20113.99%2038.44%20113.99%2038.44%20171.28%2012.12%20269.59%201.06%20371.79.22h2.52c102.19.84%20200.57%2011.9%20257.86%2038.22%200%200%20113.62%2050.83%20113.62%20224.26%200%200%201.42%20127.96-15.85%20216.8%22%20fill%3D%22%23888%22%2F%3E%3Cpath%20d%3D%22M611.77%20276.16v209.99h-83.2V282.33c0-42.97-18.07-64.77-54.23-64.77-39.98%200-60.01%2025.86-60.01%2077.02v111.57h-82.71V294.58c0-51.16-20.04-77.02-60.01-77.02-36.16%200-54.24%2021.8-54.24%2064.77v203.82h-83.19V276.16c0-42.92%2010.93-77.03%2032.88-102.26%2022.63-25.23%2052.27-38.17%2089.07-38.17%2042.57%200%2074.81%2016.37%2096.12%2049.1l20.72%2034.74%2020.73-34.74c21.31-32.73%2053.55-49.1%2096.12-49.1%2036.79%200%2066.44%2012.94%2089.07%2038.17%2021.95%2025.23%2032.88%2059.34%2032.88%20102.26z%22%20fill%3D%22%23fff%22%2F%3E%3C%2Fsvg%3E');
background-repeat: no-repeat;
background-position: 1.6rem 1.6rem;
background-position: 4.8rem 2.4rem;
background-size: 4.8rem 4.8rem;
}

Expand Down
18 changes: 17 additions & 1 deletion FeedsProcessing.Tests/src/XmlTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,24 @@ let ``with mastodon RSS`` () =

match result with
| Error _ -> Assert.Fail "Expected success"
| Ok records -> List.length records |> should equal 19
| Ok records ->
List.length records |> should equal 19

let mediaAt index =
records |> List.item index |> Article.media

let expectedMediaNoDescription =
{ Url = "https://mastodon.kleph.eu/system/media_attachments/files/000/055/839/original/4874168bf454bddb.jpg"
Description = "" }

mediaAt 17 |> should equal (Some expectedMediaNoDescription)

let expectedMediaAndDescription =
{ Url =
"https://mastodon.kleph.eu/system/media_attachments/files/108/207/041/262/751/249/original/1e8a0ccac5f59165.jpg"
Description = "Mountain landscape at sunset with bicycle on the foreground." }

mediaAt 13 |> should equal (Some expectedMediaAndDescription)

[<Test>]
let ``processFeed with slashdot RDF XML`` () =
Expand Down
11 changes: 10 additions & 1 deletion FeedsProcessing/resources/samples/rss.sample.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
<rss xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:content="http://purl.org/rss/1.0/modules/content/"
xmlns:media="http://search.yahoo.com/mrss/"
xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
<channel>
<title><![CDATA[Stories by Damien Le Berrigaud on Medium]]></title>
Expand All @@ -23,6 +25,10 @@
<pubDate>Tue, 20 Sep 2016 12:54:44 GMT</pubDate>
<atom:updated>2016-09-20T12:54:44.561Z</atom:updated>
<content:encoded><![CDATA[<p>This is the content in encoded tag</p>]]></content:encoded>
<media:content url="https://mastodon.kleph.eu/system/media_attachments/files/108/207/041/262/751/249/original/1e8a0ccac5f59165.jpg" type="image/jpeg" fileSize="503918" medium="image">
<media:rating scheme="urn:simple">nonadult</media:rating>
<media:description type="plain">Mountain landscape at sunset with bicycle on the foreground.</media:description>
</media:content>
</item>
<item>
<title><![CDATA[Second title!]]></title>
Expand All @@ -32,6 +38,9 @@
<pubDate>Tue, 20 Sep 2016 12:54:44 GMT</pubDate>
<atom:updated>2016-09-20T12:54:44.561Z</atom:updated>
<description><![CDATA[<p>This is the content in description tag</p>]]></description>
<media:content url="https://mastodon.kleph.eu/system/media_attachments/files/000/055/839/original/4874168bf454bddb.jpg" type="image/jpeg" fileSize="251429" medium="image">
<media:rating scheme="urn:simple">nonadult</media:rating>
</media:content>
</item>
<atom:link href="http://medium.superfeedr.com" rel="hub"/>
<item>
Expand Down
7 changes: 6 additions & 1 deletion FeedsProcessing/src/Article.fs
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,23 @@ and private Fields =
{ Title: string option
Link: string option
Content: string
Media: Media option
Date: Posix option }

and Media = { Url: string; Description: string }

[<RequireQualifiedAccess>]
module Article =
let title (Article fields) = fields.Title
let link (Article fields) = fields.Link
let content (Article fields) = fields.Content
let media (Article fields) = fields.Media
let date (Article fields) = fields.Date

let create title link content date =
let create title link content media date =
Article
{ Title = title |> Option.bind stringToOption
Link = stringToOption link
Content = content |> Option.bind stringToOption |> Option.defaultValue "" |> Html.sanitize
Media = media
Date = Option.map Posix.fromDateTimeOffset date }
16 changes: 14 additions & 2 deletions FeedsProcessing/src/Xml.fs
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,19 @@ module private Rss =

type private RssProvider = XmlProvider<"../FeedsProcessing/resources/samples/rss.sample.xml">

let private descriptionToString (description: RssProvider.Description) = description.Value

let private contentToMedia (content: RssProvider.Content) =
{ Url = content.Url
Description = content.Description |> Option.map descriptionToString |> Option.defaultValue "" }

let private itemToArticle (item: RssProvider.Item) =
Article.create item.Title item.Link (item.Encoded |> Option.orElse item.Description) (Some item.PubDate)
Article.create
item.Title
item.Link
(item.Encoded |> Option.orElse item.Description)
(item.Content |> Option.map contentToMedia)
(Some item.PubDate)

let private toArticles (rss: RssProvider.Rss) =
Try.value "Rss to articles" (fun _ -> rss.Channel.Items |> Seq.map itemToArticle |> Seq.toList)
Expand All @@ -66,6 +77,7 @@ module private Atom =
(Some entry.Title.Value)
(entry.Links |> Array.head |> (fun l -> l.Href))
(Some entry.Content.Value)
None
(Some entry.Published)

let private toArticles (atom: AtomProvider.Feed) =
Expand Down Expand Up @@ -94,7 +106,7 @@ module private Rdf =
type private RdfProvider = XmlProvider<"../FeedsProcessing/resources/samples/rdf.sample.xml">

let private itemToArticle (item: RdfProvider.Item) =
Article.create (Some item.Title) item.Link (Some item.Description) (Some item.Date)
Article.create (Some item.Title) item.Link (Some item.Description) None (Some item.Date)

let private toArticles (rdf: RdfProvider.Rdf) : Result<Article list, Explanation> =
Try.result
Expand Down

0 comments on commit 7fb6654

Please sign in to comment.