mirror of
https://github.com/profullstack/logicsrc.git
synced 2026-08-13 22:37:29 +00:00
Detect podcast RSS item enclosures (#50)
This commit is contained in:
parent
be530ab687
commit
aa3fdba277
2 changed files with 29 additions and 2 deletions
|
|
@ -29,6 +29,16 @@ describe("feed parsing", () => {
|
||||||
expect(result.sampleItems[0]).toMatchObject({ title: "Launch tiny products", url: "https://example.com/post" });
|
expect(result.sampleItems[0]).toMatchObject({ title: "Launch tiny products", url: "https://example.com/post" });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("detects podcast RSS feeds from item enclosures", () => {
|
||||||
|
const result = parseFeedDocument(
|
||||||
|
"https://example.com/podcast.xml",
|
||||||
|
`<?xml version="1.0"?><rss version="2.0"><channel><title>Audio Show</title><link>https://example.com</link><item><title>Episode 1</title><enclosure url="https://example.com/episode-1.mp3" type="audio/mpeg" length="12345" /></item></channel></rss>`
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result.ok).toBe(true);
|
||||||
|
expect(result.kind).toBe("podcast");
|
||||||
|
});
|
||||||
|
|
||||||
it("parses Atom and JSON Feed documents", () => {
|
it("parses Atom and JSON Feed documents", () => {
|
||||||
const atom = parseFeedDocument("https://example.com/atom.xml", `<feed><title>Atom Feed</title><entry><title>Entry</title><updated>2026-06-09T00:00:00Z</updated></entry></feed>`);
|
const atom = parseFeedDocument("https://example.com/atom.xml", `<feed><title>Atom Feed</title><entry><title>Entry</title><updated>2026-06-09T00:00:00Z</updated></entry></feed>`);
|
||||||
const json = parseFeedDocument("https://example.com/feed.json", JSON.stringify({ version: "https://jsonfeed.org/version/1.1", title: "JSON Feed", items: [{ title: "Item", url: "https://example.com/item" }] }), "application/feed+json");
|
const json = parseFeedDocument("https://example.com/feed.json", JSON.stringify({ version: "https://jsonfeed.org/version/1.1", title: "JSON Feed", items: [{ title: "Item", url: "https://example.com/item" }] }), "application/feed+json");
|
||||||
|
|
|
||||||
|
|
@ -96,7 +96,7 @@ function parseRss(feedUrl: string, rss: Record<string, unknown>): ValidationResu
|
||||||
title: title || "Untitled RSS Feed",
|
title: title || "Untitled RSS Feed",
|
||||||
description: stringValue(channel.description),
|
description: stringValue(channel.description),
|
||||||
homepageUrl: linkValue(channel.link),
|
homepageUrl: linkValue(channel.link),
|
||||||
kind: detectRssKind(channel),
|
kind: detectRssKind(channel, items),
|
||||||
language: stringValue(channel.language),
|
language: stringValue(channel.language),
|
||||||
imageUrl: imageValue(channel.image),
|
imageUrl: imageValue(channel.image),
|
||||||
lastPublishedAt: newestDate([stringValue(channel.lastBuildDate), stringValue(channel.pubDate), ...sampleItems.map((item) => item.publishedAt)]),
|
lastPublishedAt: newestDate([stringValue(channel.lastBuildDate), stringValue(channel.pubDate), ...sampleItems.map((item) => item.publishedAt)]),
|
||||||
|
|
@ -132,13 +132,30 @@ function parseAtom(feedUrl: string, feed: Record<string, unknown>): ValidationRe
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function detectRssKind(channel: Record<string, unknown>): FeedKind {
|
function detectRssKind(channel: Record<string, unknown>, items: Record<string, unknown>[]): FeedKind {
|
||||||
if (channel.itunes || channel["itunes:author"] || channel.enclosure) {
|
if (channel.itunes || channel["itunes:author"] || channel.enclosure) {
|
||||||
return "podcast";
|
return "podcast";
|
||||||
}
|
}
|
||||||
|
if (items.some(hasPodcastEnclosure)) {
|
||||||
|
return "podcast";
|
||||||
|
}
|
||||||
return "blog";
|
return "blog";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function hasPodcastEnclosure(item: Record<string, unknown>) {
|
||||||
|
return asArray(item.enclosure)
|
||||||
|
.filter(isRecord)
|
||||||
|
.some((enclosure) => {
|
||||||
|
const type = stringValue(enclosure.type)?.toLowerCase();
|
||||||
|
const url = stringValue(enclosure.url);
|
||||||
|
return (
|
||||||
|
type?.startsWith("audio/") ||
|
||||||
|
type?.startsWith("video/") ||
|
||||||
|
/\.(mp3|m4a|mp4|m4v|ogg|oga|wav|aac|flac)(?:[?#].*)?$/i.test(url ?? "")
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function imageValue(value: unknown) {
|
function imageValue(value: unknown) {
|
||||||
if (isRecord(value)) {
|
if (isRecord(value)) {
|
||||||
return stringValue(value.url);
|
return stringValue(value.url);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue