CheckMyFeed
Checks a podcast feed (a raw XML string) against RSS 2.0, the Apple Podcasts
requirements and the Podcasting 2.0 namespace, and reports what falls short.
Every rule has an id, a severity (:error, :warning or :info), an
explanation, a link to the specification it comes from and the rule set
(standard) it belongs to.
Podcasting 2.0 is optional as a whole: only the podcast: tags a feed uses are
checked, and a feed that doesn't use the namespace gets no findings from it.
The library never fetches anything and never fixes up the feed before looking at it: what it sees is exactly what a podcast app would get. It only checks what the feed text itself shows, so it does not download artwork or media to verify pixel sizes, file types or loudness.
Installation
def deps do
[
{:check_my_feed, "~> 0.5.0"}
]
end
Usage
{:ok, report} = CheckMyFeed.check(xml)
for finding <- report.findings do
standard = CheckMyFeed.standard_name(finding.standard)
IO.puts("[#{finding.severity}] #{standard} #{finding.rule_id}: #{finding.detail}")
end
# rules that were checked and passed
Enum.map(report.passed, & &1.id)
# rules that had nothing to check, e.g. the enclosure rules on a feed
# without episodes, or the rule for an optional tag the feed doesn't use
Enum.map(report.not_applicable, & &1.id)
CheckMyFeed.check/1 returns {:error, {:invalid_xml, reason}} if the
document cannot be parsed at all.
Probing what the feed points to
Some requirements can't be seen in the feed text: artwork size, file type,
colorspace or transparency, whether the media files are reachable with the
length and type the feed states, and the audio itself (format, bit rate,
sample rate, loudness, true peak). The library still downloads nothing: you
take the measurements and CheckMyFeed.check_probes/1 judges them, with the
same kind of report (Apple artwork and audio rules, RSS 2.0 enclosure rules).
Only the rules for the keys you pass (:artwork, :enclosures, :audio)
run.
{:ok, report} =
CheckMyFeed.check_probes(%{
artwork: [
%{label: "Artwork", url: url,
result: {:ok, %{format: "JPEG", width: 3000, height: 3000,
colorspace: "sRGB", alpha: false, dpi: 72.0}}}
],
enclosures: [
%{label: "Episode 1", url: media_url, feed_length: "123", feed_type: "audio/mpeg",
result: {:ok, %{status: 200, length: 123, type: "audio/mpeg"}}}
],
audio: [
%{label: "Episode 1", url: media_url,
result: {:ok, %{codec: "aac", container: "mp4", sample_rate: 44_100, channels: 2,
bit_rate: 128_000, loudness: -16.2, true_peak: -1.4, seconds: 62.0}}}
]
})
License
AGPL-3.0-or-later, see LICENSE.