mirror of
https://github.com/profullstack/logicsrc.git
synced 2026-08-14 06:47: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
76
plugins/feed-discovery/src/providers/podcastindex.ts
Normal file
76
plugins/feed-discovery/src/providers/podcastindex.ts
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
import { createHash } from "node:crypto";
|
||||
import type { DiscoveredFeed, FeedDiscoveryConfig, FeedDiscoveryProvider, FeedDiscoveryQuery } from "../types.js";
|
||||
|
||||
interface PodcastIndexFeed {
|
||||
title?: string;
|
||||
description?: string;
|
||||
url?: string;
|
||||
link?: string;
|
||||
image?: string;
|
||||
language?: string;
|
||||
newestItemPublishTime?: number;
|
||||
categories?: Record<string, string>;
|
||||
}
|
||||
|
||||
export class PodcastIndexProvider implements FeedDiscoveryProvider {
|
||||
id = "podcastindex";
|
||||
name = "PodcastIndex";
|
||||
enabledByDefault = false;
|
||||
requiresApiKey = true;
|
||||
|
||||
constructor(private readonly config: FeedDiscoveryConfig) {}
|
||||
|
||||
async search(query: FeedDiscoveryQuery) {
|
||||
if (!this.config.podcastIndexApiKey || !this.config.podcastIndexApiSecret) {
|
||||
return [];
|
||||
}
|
||||
if (query.type && query.type !== "all" && query.type !== "podcast") {
|
||||
return [];
|
||||
}
|
||||
|
||||
const authDate = Math.floor(Date.now() / 1000).toString();
|
||||
const auth = createHash("sha1")
|
||||
.update(this.config.podcastIndexApiKey + this.config.podcastIndexApiSecret + authDate)
|
||||
.digest("hex");
|
||||
|
||||
const url = new URL("https://api.podcastindex.org/api/1.0/search/byterm");
|
||||
url.searchParams.set("q", query.q);
|
||||
url.searchParams.set("max", String(Math.min(query.limit ?? 25, 100)));
|
||||
|
||||
const controller = new AbortController();
|
||||
const timeout = setTimeout(() => controller.abort(), this.config.requestTimeoutMs);
|
||||
try {
|
||||
const response = await fetch(url, {
|
||||
signal: controller.signal,
|
||||
headers: {
|
||||
"user-agent": this.config.userAgent,
|
||||
"X-Auth-Date": authDate,
|
||||
"X-Auth-Key": this.config.podcastIndexApiKey,
|
||||
Authorization: auth
|
||||
}
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error(`PodcastIndex HTTP ${response.status}`);
|
||||
}
|
||||
const body = await response.json() as { feeds?: PodcastIndexFeed[] };
|
||||
return (body.feeds ?? [])
|
||||
.filter((feed) => feed.url)
|
||||
.map((feed): DiscoveredFeed => ({
|
||||
title: feed.title ?? feed.url ?? "Untitled podcast",
|
||||
description: feed.description,
|
||||
homepageUrl: feed.link,
|
||||
feedUrl: feed.url as string,
|
||||
kind: "podcast",
|
||||
provider: this.id,
|
||||
language: feed.language,
|
||||
imageUrl: feed.image,
|
||||
lastPublishedAt: feed.newestItemPublishTime ? new Date(feed.newestItemPublishTime * 1000).toISOString() : undefined,
|
||||
score: 0,
|
||||
confidence: 0.9,
|
||||
tags: [...Object.values(feed.categories ?? {}), "podcast"]
|
||||
}));
|
||||
} finally {
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue