mirror of
https://github.com/profullstack/logicsrc.git
synced 2026-08-14 14:57:28 +00:00
Add feed discovery plugin
This commit is contained in:
parent
e47616bf9d
commit
5cfeea6b57
37 changed files with 2432 additions and 5 deletions
26
plugins/feed-discovery/src/output/atom.ts
Normal file
26
plugins/feed-discovery/src/output/atom.ts
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
import type { FeedDiscoveryResponse } from "../types.js";
|
||||
import { escapeXml } from "./opml.js";
|
||||
|
||||
export function renderAtom(response: FeedDiscoveryResponse) {
|
||||
const updated = new Date().toISOString();
|
||||
const entries = response.results
|
||||
.map((result) => {
|
||||
const id = result.canonicalFeedUrl ?? result.feedUrl;
|
||||
return ` <entry>
|
||||
<id>${escapeXml(id)}</id>
|
||||
<title>${escapeXml(result.title)}</title>
|
||||
<link href="${escapeXml(result.homepageUrl ?? result.feedUrl)}" />
|
||||
<updated>${escapeXml(result.lastPublishedAt ?? updated)}</updated>
|
||||
<summary>${escapeXml(result.description ?? `Discovered ${result.kind} feed from ${result.provider}`)}</summary>
|
||||
</entry>`;
|
||||
})
|
||||
.join("\n");
|
||||
|
||||
return `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<feed xmlns="http://www.w3.org/2005/Atom">
|
||||
<id>logicsrc:feed-discovery:${escapeXml(response.normalizedQuery)}</id>
|
||||
<title>LogicSRC feed discovery: ${escapeXml(response.query)}</title>
|
||||
<updated>${updated}</updated>
|
||||
${entries}
|
||||
</feed>`;
|
||||
}
|
||||
23
plugins/feed-discovery/src/output/index.ts
Normal file
23
plugins/feed-discovery/src/output/index.ts
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
import type { FeedDiscoveryResponse, FeedOutputFormat } from "../types.js";
|
||||
import { renderAtom } from "./atom.js";
|
||||
import { renderJsonFeed } from "./json-feed.js";
|
||||
import { renderOpml } from "./opml.js";
|
||||
import { renderRss } from "./rss.js";
|
||||
|
||||
export function renderDiscoveryOutput(response: FeedDiscoveryResponse, format: FeedOutputFormat) {
|
||||
if (format === "opml") {
|
||||
return renderOpml(response);
|
||||
}
|
||||
if (format === "rss") {
|
||||
return renderRss(response);
|
||||
}
|
||||
if (format === "atom") {
|
||||
return renderAtom(response);
|
||||
}
|
||||
if (format === "json-feed") {
|
||||
return renderJsonFeed(response);
|
||||
}
|
||||
return JSON.stringify(response, null, 2);
|
||||
}
|
||||
|
||||
export { renderAtom, renderJsonFeed, renderOpml, renderRss };
|
||||
22
plugins/feed-discovery/src/output/json-feed.ts
Normal file
22
plugins/feed-discovery/src/output/json-feed.ts
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
import type { FeedDiscoveryResponse } from "../types.js";
|
||||
|
||||
export function renderJsonFeed(response: FeedDiscoveryResponse) {
|
||||
return JSON.stringify(
|
||||
{
|
||||
version: "https://jsonfeed.org/version/1.1",
|
||||
title: `LogicSRC feed discovery: ${response.query}`,
|
||||
home_page_url: "https://bittorrented.com/rss",
|
||||
feed_url: `https://bittorrented.com/json-feed/discover/${encodeURIComponent(response.normalizedQuery)}.json`,
|
||||
items: response.results.map((result) => ({
|
||||
id: result.canonicalFeedUrl ?? result.feedUrl,
|
||||
url: result.homepageUrl ?? result.feedUrl,
|
||||
title: result.title,
|
||||
summary: result.description ?? `Discovered ${result.kind} feed from ${result.provider}`,
|
||||
date_published: result.lastPublishedAt,
|
||||
tags: [result.kind, result.provider, ...result.tags]
|
||||
}))
|
||||
},
|
||||
null,
|
||||
2
|
||||
);
|
||||
}
|
||||
27
plugins/feed-discovery/src/output/opml.ts
Normal file
27
plugins/feed-discovery/src/output/opml.ts
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
import type { DiscoveredFeed, FeedDiscoveryResponse } from "../types.js";
|
||||
|
||||
export function renderOpml(response: FeedDiscoveryResponse) {
|
||||
const outlines = response.results.map(renderOutline).join("\n");
|
||||
return `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<opml version="2.0">
|
||||
<head>
|
||||
<title>LogicSRC feed discovery: ${escapeXml(response.query)}</title>
|
||||
</head>
|
||||
<body>
|
||||
${outlines}
|
||||
</body>
|
||||
</opml>`;
|
||||
}
|
||||
|
||||
function renderOutline(feed: DiscoveredFeed) {
|
||||
return ` <outline text="${escapeXml(feed.title)}" title="${escapeXml(feed.title)}" type="rss" xmlUrl="${escapeXml(feed.canonicalFeedUrl ?? feed.feedUrl)}"${feed.homepageUrl ? ` htmlUrl="${escapeXml(feed.homepageUrl)}"` : ""} category="${escapeXml(feed.kind)}" />`;
|
||||
}
|
||||
|
||||
export function escapeXml(value: string) {
|
||||
return value
|
||||
.replaceAll("&", "&")
|
||||
.replaceAll("<", "<")
|
||||
.replaceAll(">", ">")
|
||||
.replaceAll('"', """)
|
||||
.replaceAll("'", "'");
|
||||
}
|
||||
25
plugins/feed-discovery/src/output/rss.ts
Normal file
25
plugins/feed-discovery/src/output/rss.ts
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
import RSS from "rss";
|
||||
import type { FeedDiscoveryResponse } from "../types.js";
|
||||
|
||||
export function renderRss(response: FeedDiscoveryResponse) {
|
||||
const feed = new RSS({
|
||||
title: `LogicSRC feed discovery: ${response.query}`,
|
||||
description: `Discovered feed sources for ${response.query}`,
|
||||
feed_url: `https://bittorrented.com/rss/discover/${encodeURIComponent(response.normalizedQuery)}.xml`,
|
||||
site_url: "https://bittorrented.com/rss",
|
||||
language: "en"
|
||||
});
|
||||
|
||||
for (const result of response.results) {
|
||||
feed.item({
|
||||
title: result.title,
|
||||
description: result.description ?? `Discovered ${result.kind} feed from ${result.provider}`,
|
||||
url: result.homepageUrl ?? result.feedUrl,
|
||||
guid: result.canonicalFeedUrl ?? result.feedUrl,
|
||||
categories: [result.kind, result.provider, ...result.tags],
|
||||
date: result.lastPublishedAt ? new Date(result.lastPublishedAt) : new Date()
|
||||
});
|
||||
}
|
||||
|
||||
return feed.xml({ indent: true });
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue