mirror of
https://github.com/svemagie/indiekit-endpoint-youtube.git
synced 2026-04-02 15:54:59 +02:00
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:
66
CLAUDE.md
66
CLAUDE.md
@@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
|||||||
|
|
||||||
## Project Overview
|
## 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
|
## 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
|
- Exports a `YouTubeEndpoint` class that Indiekit loads as a plugin
|
||||||
- Registers protected routes (admin dashboard) and public routes (JSON API)
|
- Registers protected routes (admin dashboard) and public routes (JSON API)
|
||||||
- Stores configuration in `Indiekit.config.application.youtubeConfig` for controller access
|
- 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`):
|
**YouTube API Client** (`lib/youtube-client.js`):
|
||||||
- Handles all YouTube Data API v3 interactions
|
- Handles all YouTube Data API v3 interactions
|
||||||
- Implements in-memory caching with configurable TTL
|
- Implements in-memory caching with configurable TTL
|
||||||
- Uses uploads playlist method for quota efficiency (2 units) instead of search (100 units)
|
- 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
|
- 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/`):
|
**Controllers** (`lib/controllers/`):
|
||||||
- `dashboard.js` - Admin page rendering, cache refresh
|
- `dashboard.js` - Admin page rendering, multi-channel display, cache refresh via POST
|
||||||
- `videos.js` - `/api/videos` JSON endpoint
|
- `videos.js` - `/api/videos` JSON endpoint (supports multi-channel aggregation)
|
||||||
- `channel.js` - `/api/channel` JSON endpoint
|
- `channel.js` - `/api/channel` JSON endpoint (returns array for multi-channel mode)
|
||||||
- `live.js` - `/api/live` JSON endpoint with efficient vs full search modes
|
- `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/Templates**:
|
||||||
- `views/youtube.njk` - Admin dashboard template (Nunjucks)
|
- `views/youtube.njk` - Admin dashboard template with multi-channel support
|
||||||
- `includes/@indiekit-endpoint-youtube-widget.njk` - Widget component
|
- `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
|
## 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
|
- `channels.list`, `playlistItems.list`, `videos.list`: 1 unit each
|
||||||
- `search.list`: 100 units (used only for full live status check with `?full=true`)
|
- `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
|
## Workspace Context
|
||||||
|
|
||||||
|
|||||||
147
README.md
147
README.md
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
YouTube channel endpoint for [Indiekit](https://getindiekit.com/).
|
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
|
## Installation
|
||||||
|
|
||||||
@@ -17,7 +17,8 @@ npm install @rmdes/indiekit-endpoint-youtube
|
|||||||
|
|
||||||
## Features
|
## 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)
|
- **Live Status** - Shows when channel is live streaming (with animated badge)
|
||||||
- **Upcoming Streams** - Display scheduled upcoming live streams
|
- **Upcoming Streams** - Display scheduled upcoming live streams
|
||||||
- **Latest Videos** - Grid of recent uploads with thumbnails, duration, view counts
|
- **Latest Videos** - Grid of recent uploads with thumbnails, duration, view counts
|
||||||
@@ -27,6 +28,8 @@ npm install @rmdes/indiekit-endpoint-youtube
|
|||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
|
### Single Channel
|
||||||
|
|
||||||
Add to your `indiekit.config.js`:
|
Add to your `indiekit.config.js`:
|
||||||
|
|
||||||
```javascript
|
```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
|
## Environment Variables
|
||||||
|
|
||||||
| Variable | Required | Description |
|
| Variable | Required | Description |
|
||||||
@@ -58,7 +94,7 @@ export default {
|
|||||||
| `YOUTUBE_CHANNEL_ID` | Yes* | Channel ID (starts with `UC...`) |
|
| `YOUTUBE_CHANNEL_ID` | Yes* | Channel ID (starts with `UC...`) |
|
||||||
| `YOUTUBE_CHANNEL_HANDLE` | Yes* | Channel handle (e.g., `@YourChannel`) |
|
| `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
|
### Getting a YouTube API Key
|
||||||
|
|
||||||
@@ -81,7 +117,7 @@ export default {
|
|||||||
| Route | Description |
|
| Route | Description |
|
||||||
|-------|-------------|
|
|-------|-------------|
|
||||||
| `GET /youtube/` | Dashboard with channel info, live status, latest videos |
|
| `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)
|
### Public API Routes (JSON)
|
||||||
|
|
||||||
@@ -89,7 +125,8 @@ export default {
|
|||||||
|-------|-------------|
|
|-------|-------------|
|
||||||
| `GET /youtube/api/videos` | Latest videos (supports `?limit=N`) |
|
| `GET /youtube/api/videos` | Latest videos (supports `?limit=N`) |
|
||||||
| `GET /youtube/api/channel` | Channel information |
|
| `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
|
### Example: Eleventy Integration
|
||||||
|
|
||||||
@@ -119,6 +156,7 @@ export default async function() {
|
|||||||
|
|
||||||
### GET /youtube/api/live
|
### GET /youtube/api/live
|
||||||
|
|
||||||
|
**Single channel:**
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"isLive": true,
|
"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
|
### GET /youtube/api/videos
|
||||||
|
|
||||||
|
**Single channel:**
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"videos": [
|
"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
|
## Options
|
||||||
|
|
||||||
| Option | Default | Description |
|
| Option | Default | Description |
|
||||||
|--------|---------|-------------|
|
|--------|---------|-------------|
|
||||||
| `mountPath` | `/youtube` | URL path for the endpoint |
|
| `mountPath` | `/youtube` | URL path for the endpoint |
|
||||||
| `apiKey` | - | YouTube Data API key |
|
| `apiKey` | - | YouTube Data API key |
|
||||||
| `channelId` | - | Channel ID (UC...) |
|
| `channelId` | - | Channel ID (UC...) - single channel mode |
|
||||||
| `channelHandle` | - | Channel handle (@...) |
|
| `channelHandle` | - | Channel handle (@...) - single channel mode |
|
||||||
|
| `channels` | `null` | Array of channels for multi-channel mode |
|
||||||
| `cacheTtl` | `300000` | Cache TTL in ms (5 min) |
|
| `cacheTtl` | `300000` | Cache TTL in ms (5 min) |
|
||||||
| `liveCacheTtl` | `60000` | Live status cache TTL in ms (1 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
|
## 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 videos | 2 units | Uses uploads playlist (not search) |
|
||||||
| Get channel | 1 unit | Cached for 24 hours |
|
| Get channel | 1 unit | Cached for 24 hours |
|
||||||
| Check live status | 2 units | Checks recent videos (efficient) |
|
| Check live status (efficient) | 2 units | Checks recent videos |
|
||||||
| Full live search | 100 units | Only when explicitly requested |
|
| 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
|
## Requirements
|
||||||
|
|
||||||
- Indiekit >= 1.0.0-beta.25
|
- Indiekit >= 1.0.0-beta.25
|
||||||
- YouTube Data API v3 enabled
|
- YouTube Data API v3 enabled
|
||||||
- Valid API key with YouTube Data API access
|
- Valid API key with YouTube Data API access
|
||||||
|
- Node.js >= 20
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user