docs: update CLAUDE.md and README.md with multi-channel support

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Ricardo
2026-02-13 18:23:26 +01:00
parent 4119391ef3
commit 58ea798106
2 changed files with 194 additions and 19 deletions

View File

@@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
## Project Overview
This is an Indiekit plugin that adds a YouTube channel endpoint. It displays latest videos and live streaming status from a YouTube channel, with both an admin dashboard and public JSON API endpoints.
This is an Indiekit plugin that adds a YouTube channel endpoint. It displays latest videos and live streaming status from YouTube channels (supports both single and multiple channels), with an admin dashboard and public JSON API endpoints.
## Development
@@ -18,22 +18,61 @@ No test suite is configured. Testing requires a running Indiekit instance with v
- Exports a `YouTubeEndpoint` class that Indiekit loads as a plugin
- Registers protected routes (admin dashboard) and public routes (JSON API)
- Stores configuration in `Indiekit.config.application.youtubeConfig` for controller access
- Registers navigation items and shortcuts in Indiekit's UI
- Supports both single channel (backward compatible) and multi-channel mode
**YouTube API Client** (`lib/youtube-client.js`):
- Handles all YouTube Data API v3 interactions
- Implements in-memory caching with configurable TTL
- Uses uploads playlist method for quota efficiency (2 units) instead of search (100 units)
- Channel info cached for 24 hours; videos cached per `cacheTtl` option
- Two live status methods: `getLiveStatus()` (expensive, 100 units) and `getLiveStatusEfficient()` (cheap, 2 units)
- Channel resolution: accepts either `channelId` (UC...) or `channelHandle` (@username)
- `channelHandle` is resolved via `forHandle` API parameter (removes @ prefix automatically)
**Controllers** (`lib/controllers/`):
- `dashboard.js` - Admin page rendering, cache refresh
- `videos.js` - `/api/videos` JSON endpoint
- `channel.js` - `/api/channel` JSON endpoint
- `live.js` - `/api/live` JSON endpoint with efficient vs full search modes
- `dashboard.js` - Admin page rendering, multi-channel display, cache refresh via POST
- `videos.js` - `/api/videos` JSON endpoint (supports multi-channel aggregation)
- `channel.js` - `/api/channel` JSON endpoint (returns array for multi-channel mode)
- `live.js` - `/api/live` JSON endpoint with efficient vs full search modes (`?full=true`)
- All controllers support both single-channel (backward compatible) and multi-channel modes
- Multi-channel responses include both aggregated data and per-channel breakdowns
**Views/Templates**:
- `views/youtube.njk` - Admin dashboard template (Nunjucks)
- `includes/@indiekit-endpoint-youtube-widget.njk` - Widget component
- `views/youtube.njk` - Admin dashboard template with multi-channel support
- `includes/@indiekit-endpoint-youtube-widget.njk` - Homepage widget for Indiekit admin
- `includes/@indiekit-endpoint-youtube-live.njk` - Reusable live status partial
- `includes/@indiekit-endpoint-youtube-videos.njk` - Reusable video list partial (compact)
**Locales**:
- `locales/en.json` - English translations for all UI strings
## Configuration Modes
### Single Channel (Backward Compatible)
```javascript
{
channelId: "UC...",
// OR
channelHandle: "@username"
}
```
### Multi-Channel
```javascript
{
channels: [
{ id: "UC...", name: "Channel 1" },
{ handle: "@username", name: "Channel 2" }
]
}
```
## API Response Shapes
**Single channel mode**: Flat responses (e.g., `{ videos: [...] }`)
**Multi-channel mode**: Includes both aggregated flat data (for backward compat) and per-channel breakdowns (e.g., `{ videos: [...], videosByChannel: {...} }`)
## API Quota Considerations
@@ -41,7 +80,18 @@ YouTube Data API has a 10,000 units/day default quota:
- `channels.list`, `playlistItems.list`, `videos.list`: 1 unit each
- `search.list`: 100 units (used only for full live status check with `?full=true`)
The plugin uses the playlist method by default for quota efficiency.
The plugin uses the playlist method by default for quota efficiency. With default caching (5 min), single channel uses ~600 units/day. Multi-channel multiplies by number of channels.
## Routes
**Protected (require auth)**:
- `GET /youtube/` - Dashboard
- `POST /youtube/refresh` - Clear cache and refetch data (returns JSON)
**Public JSON API**:
- `GET /youtube/api/videos?limit=N` - Latest videos
- `GET /youtube/api/channel` - Channel info
- `GET /youtube/api/live?full=true` - Live status (efficient by default, add `?full=true` for search API)
## Workspace Context

147
README.md
View File

@@ -5,7 +5,7 @@
YouTube channel endpoint for [Indiekit](https://getindiekit.com/).
Display latest videos and live streaming status from any YouTube channel on your IndieWeb site.
Display latest videos and live streaming status from any YouTube channel (or multiple channels) on your IndieWeb site.
## Installation
@@ -17,7 +17,8 @@ npm install @rmdes/indiekit-endpoint-youtube
## Features
- **Admin Dashboard** - Overview of channel with latest videos in Indiekit's admin UI
- **Single or Multi-Channel** - Monitor one channel or aggregate multiple channels
- **Admin Dashboard** - Overview of channel(s) with latest videos in Indiekit's admin UI
- **Live Status** - Shows when channel is live streaming (with animated badge)
- **Upcoming Streams** - Display scheduled upcoming live streams
- **Latest Videos** - Grid of recent uploads with thumbnails, duration, view counts
@@ -27,6 +28,8 @@ npm install @rmdes/indiekit-endpoint-youtube
## Configuration
### Single Channel
Add to your `indiekit.config.js`:
```javascript
@@ -50,6 +53,39 @@ export default {
};
```
### Multiple Channels
Monitor multiple YouTube channels simultaneously:
```javascript
import YouTubeEndpoint from "@rmdes/indiekit-endpoint-youtube";
export default {
plugins: [
new YouTubeEndpoint({
mountPath: "/youtube",
apiKey: process.env.YOUTUBE_API_KEY,
channels: [
{ id: "UC...", name: "Main Channel" },
{ handle: "@SecondChannel", name: "Second Channel" },
{ id: "UC...", name: "Third Channel" },
],
cacheTtl: 300_000,
liveCacheTtl: 60_000,
limits: {
videos: 10,
},
}),
],
};
```
In multi-channel mode:
- Dashboard shows all channels with separate sections
- API endpoints aggregate data from all channels
- Videos are sorted by date across all channels
- Live status shows any channel that is currently live
## Environment Variables
| Variable | Required | Description |
@@ -58,7 +94,7 @@ export default {
| `YOUTUBE_CHANNEL_ID` | Yes* | Channel ID (starts with `UC...`) |
| `YOUTUBE_CHANNEL_HANDLE` | Yes* | Channel handle (e.g., `@YourChannel`) |
*Either `channelId` or `channelHandle` is required.
*Either `channelId` or `channelHandle` is required for single-channel mode. In multi-channel mode, use the `channels` array instead.
### Getting a YouTube API Key
@@ -81,7 +117,7 @@ export default {
| Route | Description |
|-------|-------------|
| `GET /youtube/` | Dashboard with channel info, live status, latest videos |
| `POST /youtube/refresh` | Clear cache and refresh data |
| `POST /youtube/refresh` | Clear cache and refresh data (returns JSON) |
### Public API Routes (JSON)
@@ -89,7 +125,8 @@ export default {
|-------|-------------|
| `GET /youtube/api/videos` | Latest videos (supports `?limit=N`) |
| `GET /youtube/api/channel` | Channel information |
| `GET /youtube/api/live` | Live streaming status |
| `GET /youtube/api/live` | Live streaming status (efficient by default) |
| `GET /youtube/api/live?full=true` | Live status using search API (more accurate, costs more quota) |
### Example: Eleventy Integration
@@ -119,6 +156,7 @@ export default async function() {
### GET /youtube/api/live
**Single channel:**
```json
{
"isLive": true,
@@ -133,8 +171,34 @@ export default async function() {
}
```
**Multi-channel:**
```json
{
"isLive": true,
"isUpcoming": false,
"stream": {
"videoId": "abc123",
"title": "Live Stream Title"
},
"liveStatuses": [
{
"channelConfigName": "Main Channel",
"isLive": true,
"stream": { "videoId": "abc123" }
},
{
"channelConfigName": "Second Channel",
"isLive": false,
"stream": null
}
],
"cached": true
}
```
### GET /youtube/api/videos
**Single channel:**
```json
{
"videos": [
@@ -155,17 +219,75 @@ export default async function() {
}
```
**Multi-channel:**
```json
{
"videos": [],
"videosByChannel": {
"Main Channel": [],
"Second Channel": []
},
"count": 20,
"cached": true
}
```
### GET /youtube/api/channel
**Single channel:**
```json
{
"channel": {
"id": "UC...",
"title": "Channel Name",
"description": "Channel description",
"thumbnail": "https://...",
"subscriberCount": 12345,
"videoCount": 100,
"viewCount": 999999
},
"cached": true
}
```
**Multi-channel:**
```json
{
"channels": [
{ "id": "UC...", "title": "Channel 1", "configName": "Main Channel" },
{ "id": "UC...", "title": "Channel 2", "configName": "Second Channel" }
],
"channel": {},
"cached": true
}
```
## Options
| Option | Default | Description |
|--------|---------|-------------|
| `mountPath` | `/youtube` | URL path for the endpoint |
| `apiKey` | - | YouTube Data API key |
| `channelId` | - | Channel ID (UC...) |
| `channelHandle` | - | Channel handle (@...) |
| `channelId` | - | Channel ID (UC...) - single channel mode |
| `channelHandle` | - | Channel handle (@...) - single channel mode |
| `channels` | `null` | Array of channels for multi-channel mode |
| `cacheTtl` | `300000` | Cache TTL in ms (5 min) |
| `liveCacheTtl` | `60000` | Live status cache TTL in ms (1 min) |
| `limits.videos` | `10` | Number of videos to fetch |
| `limits.videos` | `10` | Number of videos to fetch per channel |
### Channels Array Format
For multi-channel mode, the `channels` option accepts an array of objects:
```javascript
channels: [
{ id: "UC...", name: "Display Name" }, // Using channel ID
{ handle: "@username", name: "Display Name" }, // Using handle
{ id: "UC..." } // Name defaults to channel title
]
```
Either `id` or `handle` is required. The `name` field is optional and used for display purposes.
## Quota Efficiency
@@ -175,16 +297,19 @@ YouTube Data API has a daily quota (10,000 units by default). This plugin is opt
|-----------|------------|--------|
| Get videos | 2 units | Uses uploads playlist (not search) |
| Get channel | 1 unit | Cached for 24 hours |
| Check live status | 2 units | Checks recent videos (efficient) |
| Full live search | 100 units | Only when explicitly requested |
| Check live status (efficient) | 2 units | Checks recent videos |
| Check live status (full) | 100 units | Only when explicitly requested |
With default settings (5-min cache), you'll use ~600 units/day for video checks.
**Single channel:** With default settings (5-min cache), ~600 units/day.
**Multi-channel:** Quota usage scales linearly. 3 channels = ~1,800 units/day.
## Requirements
- Indiekit >= 1.0.0-beta.25
- YouTube Data API v3 enabled
- Valid API key with YouTube Data API access
- Node.js >= 20
## License