Podcast roll endpoint for Indiekit. Aggregates podcast episodes from FreshRSS, displays on frontend with OPML sidebar.
npm install @rmdes/indiekit-endpoint-podrollPodcast roll endpoint for Indiekit. Aggregates podcast episodes from a FreshRSS instance and provides JSON APIs for displaying a podroll page with episode listings and OPML sidebar.
- Syncs podcast episodes from FreshRSS greader API
- Syncs podcast sources from OPML export
- Caches data in MongoDB for fast API responses
- Background sync at configurable intervals
- Public JSON APIs for frontend consumption
- Admin dashboard for manual sync and status
``bash`
npm install @rmdes/indiekit-endpoint-podroll
Add to your Indiekit config:
`javascript
import PodrollEndpoint from "@rmdes/indiekit-endpoint-podroll";
export default {
plugins: [
new PodrollEndpoint({
episodesUrl: "https://your-freshrss.example/api/query.php?user=USER&t=TOKEN&f=greader",
opmlUrl: "https://your-freshrss.example/api/query.php?user=USER&t=TOKEN&f=opml",
syncInterval: 900000, // 15 minutes (default)
maxEpisodes: 100, // Maximum episodes to cache (default)
}),
],
};
`
| Endpoint | Description |
|----------|-------------|
| GET /podrollapi/api/episodes | List episodes. Params: limit, offset, source |GET /podrollapi/api/episodes/:id
| | Get single episode |GET /podrollapi/api/sources
| | List podcast sources from OPML. Params: category |GET /podrollapi/api/status
| | Sync status and counts |
| Endpoint | Description |
|----------|-------------|
| GET /podrollapi/ | Admin dashboard |POST /podrollapi/sync
| | Trigger manual sync |POST /podrollapi/clear-resync
| | Clear cache and re-sync |
` Episode description HTMLjson`
{
"items": [
{
"id": "unique-episode-id",
"title": "Episode Title",
"url": "https://podcast.example/episode",
"published": "2026-01-31T12:00:00.000Z",
"content": "
"author": "Author Name",
"enclosure": {
"url": "https://cdn.example/episode.mp3",
"type": "audio/mpeg",
"length": 12345678
},
"podcast": {
"title": "Podcast Name",
"url": "https://podcast.example",
"feedUrl": "https://podcast.example/feed.xml"
}
}
],
"total": 100,
"limit": 50,
"offset": 0,
"hasMore": true
}
`json`
{
"items": [
{
"title": "Podcast Name",
"xmlUrl": "https://podcast.example/feed.xml",
"htmlUrl": "https://podcast.example",
"category": "Technology"
}
],
"total": 70,
"categories": ["Technology", "Culture", "Politics"]
}
The APIs are designed for client-side fetching. Example with vanilla JavaScript:
`javascript
// Fetch episodes
const response = await fetch('/podrollapi/api/episodes?limit=20');
const { items, hasMore } = await response.json();
// Fetch sources for sidebar
const sourcesResponse = await fetch('/podrollapi/api/sources');
const { items: sources } = await sourcesResponse.json();
``
MIT