[WIP] got basic podcast RSS working

This commit is contained in:
Kieran Eglin 2024-03-23 19:02:46 -07:00
parent 67f8000f5a
commit 1413aa153a
No known key found for this signature in database
GPG key ID: 193984967FCF432D
3 changed files with 53 additions and 30 deletions

View file

@ -1,6 +1,7 @@
defmodule Pinchflat.Podcasts.RssFeedBuilder do
@datetime_format "%a, %d %b %Y %H:%M:%S %z"
# TODO: test
# TODO: only MIs that are confirmed to exist on-disk should be provided
def build(source, media_items) do
media_item_xml = Enum.map(media_items, &build_media_item_xml(source, &1))
@ -8,41 +9,20 @@ defmodule Pinchflat.Podcasts.RssFeedBuilder do
build_source_xml(source, media_item_xml)
end
defp build_media_item_xml(source, media_item) do
# NOTE: improvements for future:
# - Add <itunes:duration>
# - Serve proper images instead of the placeholders
"""
<item>
<guid isPermaLink="false">#{media_item.uuid}</guid>
<title>#{media_item.title}</title>
<link>#{media_item.original_url}</link>
<description>#{media_item.description}</description>
<pubDate>#{generate_upload_date(media_item)}</pubDate>
<enclosure
url="#{generate_media_stream_path(media_item)}"
length="#{media_item.media_size_bytes}"
type="#{determine_content_type(media_item)}">
</enclosure>
<itunes:author>#{source.custom_name}</itunes:author>
<itunes:subtitle>#{media_item.title}</itunes:subtitle>
<itunes:summary><![CDATA[#{media_item.description}]]></itunes:summary>
<itunes:image href="https://raw.githubusercontent.com/kieraneglin/pinchflat/master/priv/static/images/originals/logo.png"></itunes:image>
<itunes:explicit>false</itunes:explicit>
</item>
"""
end
defp build_source_xml(source, media_item_xml) do
# Useful: https://validator.w3.org/feed/#validate_by_input
# NOTE: improvements for future:
# Useful: resources:
# - https://validator.w3.org/feed/#validate_by_input
# - https://github.com/Podcastindex-org/podcast-namespace/blob/main/docs/1.0.md
# - https://podba.se/validate
#
# - Add real <description>
# - Add <itunes:summary>
# - Serve proper images instead of the placeholders
# - Ass <atom:link href="<LINK TO FEED>" rel="self" type="application/rss+xml" />
"""
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd">
<rss version="2.0"
xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd"
xmlns:podcast="https://podcastindex.org/namespace/1.0"
xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>#{source.custom_name}</title>
<link>#{source.original_url}</link>
@ -52,6 +32,9 @@ defmodule Pinchflat.Podcasts.RssFeedBuilder do
<language>en-us</language>
<lastBuildDate>#{Calendar.strftime(DateTime.utc_now(), @datetime_format)}</lastBuildDate>
<pubDate>#{Calendar.strftime(source.inserted_at, @datetime_format)}</pubDate>
<atom:link href="#{generate_self_link(source)}" rel="self" type="application/rss+xml" />
<podcast:locked>false</podcast:locked>
<podcast:guid>#{source.uuid}</podcast:guid>
<image>
<url>https://raw.githubusercontent.com/kieraneglin/pinchflat/master/priv/static/images/originals/logo.png</url>
<title>#{source.custom_name}</title>
@ -71,6 +54,31 @@ defmodule Pinchflat.Podcasts.RssFeedBuilder do
"""
end
defp build_media_item_xml(source, media_item) do
"""
<item>
<guid isPermaLink="false">#{media_item.uuid}</guid>
<title>#{media_item.title}</title>
<link>#{media_item.original_url}</link>
<description>#{media_item.description}</description>
<pubDate>#{generate_upload_date(media_item)}</pubDate>
<enclosure
url="#{generate_media_stream_path(media_item)}"
length="#{media_item.media_size_bytes}"
type="#{determine_content_type(media_item)}">
</enclosure>
<itunes:author>#{source.custom_name}</itunes:author>
<itunes:subtitle>#{media_item.title}</itunes:subtitle>
<itunes:summary><![CDATA[#{media_item.description}]]></itunes:summary>
<itunes:explicit>false</itunes:explicit>
</item>
"""
end
defp generate_self_link(source) do
"#{url_base()}/sources/#{source.uuid}/feed.xml"
end
defp generate_media_stream_path(media_item) do
extension = Path.extname(media_item.media_filepath)

View file

@ -7,6 +7,7 @@ defmodule PinchflatWeb.Sources.SourceController do
alias Pinchflat.Sources
alias Pinchflat.Profiles
alias Pinchflat.Sources.Source
alias Pinchflat.Podcasts.RssFeedBuilder
def index(conn, _params) do
sources = Repo.preload(Sources.list_sources(), :media_profile)
@ -58,6 +59,19 @@ defmodule PinchflatWeb.Sources.SourceController do
)
end
def feed(conn, %{"id" => uuid}) do
# TODO: change this to UUID
source = Repo.get_by!(Source, id: uuid)
media_items = Media.list_downloaded_media_items_for(source, limit: 100)
xml = RssFeedBuilder.build(source, media_items)
conn
|> put_resp_content_type("application/rss+xml")
|> put_resp_header("content-disposition", "inline")
|> send_resp(200, xml)
end
def edit(conn, %{"id" => id}) do
source = Sources.get_source!(id)
changeset = Sources.change_source(source)

View file

@ -36,6 +36,7 @@ defmodule PinchflatWeb.Router do
# TODO: consider putting the basic auth here behind a config flag
# so people that want RSS feeds to work can enable it.
scope "/", PinchflatWeb do
get "/sources/:id/feed", Sources.SourceController, :feed
get "/media/:id/stream", MediaItems.MediaItemController, :stream
end