Skip to content

feat: add livestream REST endpoints#32

Open
mrdarrengriffin wants to merge 14 commits into
mainfrom
youtube-api
Open

feat: add livestream REST endpoints#32
mrdarrengriffin wants to merge 14 commits into
mainfrom
youtube-api

Conversation

@mrdarrengriffin

Copy link
Copy Markdown
Member

Adds a livestream endpoint that reports the YouTube livestream status for
Open Home Foundation project channels (Home Assistant, ESPHome, Open Home
Foundation, Music Assistant). Websites can call this to show upcoming, live, or
recently-ended streams.

Endpoints

  • GET /livestream — status for all configured channels
  • GET /livestream/:slug — status for a single channel (e.g. home-assistant);
    returns 404 for unknown slugs

Each entry returns a status of live, upcoming, past (within 24h of
ending), or none, plus title, url, and (for upcoming) startTime.

Notes

  • Channels are config-driven in src/livestream/livestream.channels.ts
    adding a project is a one-line change.
  • Results are cached per channel (5 min TTL) with in-flight de-duplication;
    a failing channel falls back to stale data and doesn't affect the others.
  • Requires a YOUTUBE_API_KEY (YouTube Data API v3), loaded from .env via
    @nestjs/config. See .env.example.
  • Devcontainer now forwards port 3000 (the app's default) instead of 443.

Copilot AI review requested due to automatic review settings July 9, 2026 12:05
@mrdarrengriffin mrdarrengriffin marked this pull request as draft July 9, 2026 12:06

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a new NestJS livestream feature area that exposes REST endpoints for querying YouTube livestream state for a configured set of Open Home Foundation project channels, backed by YouTube Data API v3 and cached per channel.

Changes:

  • Add GET /livestream and GET /livestream/:slug endpoints via a new controller/module/service.
  • Implement YouTube API integration with per-channel caching and in-flight refresh de-duplication.
  • Wire up global config loading (@nestjs/config) and add developer/supporting files (.env.example, devcontainer port forwarding, .env gitignore).

Reviewed changes

Copilot reviewed 9 out of 11 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/livestream/livestream.service.ts Implements YouTube API lookups, caching, and status derivation logic for channels.
src/livestream/livestream.controller.ts Adds REST endpoints for listing all channel statuses and querying by slug.
src/livestream/livestream.module.ts Registers the livestream controller/service in a dedicated Nest module.
src/livestream/livestream.channels.ts Defines the config-driven list of tracked channels (slug/name/handle).
src/livestream/index.ts Exposes livestream module/service/channels via barrel exports.
src/app.module.ts Enables global config loading and registers LivestreamModule.
package.json Adds @nestjs/config dependency.
pnpm-lock.yaml Locks @nestjs/config and its transitive dependencies (dotenv, dotenv-expand, etc.).
.gitignore Ensures local .env files are not committed.
.env.example Documents the required YOUTUBE_API_KEY environment variable.
.devcontainer.json Updates forwarded port to 3000 to match the app’s default HTTP port.
Files not reviewed (1)
  • pnpm-lock.yaml: Generated file

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/livestream/livestream.service.ts Outdated
Comment thread src/livestream/livestream.service.ts Outdated
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@djwmarcx djwmarcx changed the title Add livestream REST endpoints feat: add livestream REST endpoints Jul 9, 2026
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@mrdarrengriffin mrdarrengriffin marked this pull request as ready for review July 9, 2026 12:13
Copilot AI review requested due to automatic review settings July 9, 2026 12:13
@mrdarrengriffin mrdarrengriffin requested a review from djwmarcx July 9, 2026 12:14

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 11 changed files in this pull request and generated 3 comments.

Files not reviewed (1)
  • pnpm-lock.yaml: Generated file

Comment thread src/livestream/livestream.service.ts Outdated
Comment thread src/livestream/livestream.service.ts Outdated
Comment thread src/livestream/livestream.service.ts Outdated
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 9, 2026 12:20
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 11 changed files in this pull request and generated no new comments.

Files not reviewed (1)
  • pnpm-lock.yaml: Generated file

@djwmarcx djwmarcx left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This solution requires a lot of quota. More than we have available (35x).
We need to come up with a different approach.

What about polling (for each channel):

https://www.youtube.com/feeds/videos.xml?channel_id=UCbX3YkedQunLt7EQAdVxh7w

Plus 1 call to extract the necessary data using the actual API (if really needed)

Copilot AI review requested due to automatic review settings July 9, 2026 14:06
@mrdarrengriffin

Copy link
Copy Markdown
Member Author

@djwmarcx

Switched from search polling to push + adaptive polling

Reworked the livestream feature to cut YouTube API quota while keeping updates near-instant.

Why: search.list costs 100 units/call — polling four channels burns the 10k/day budget fast.

Now:

  • WebSub push for instant "new/updated video" signals (0 quota).
  • videos.list (1 unit) to classify live details.
  • RSS discovery every 5 min (free) to catch scheduled streams push doesn't signal.
  • Adaptive polling: videos.list every 10s while live/imminent, ~60s otherwise, 0 when idle — catches go-live/ended in ~10s.

Reads are served from memory, so endpoints cost no quota per request.

API:

  • GET /livestream — all channels
  • GET /livestream/:slug — one channel

Returns status (live/upcoming/past/none) + title, url, startTime. Channels are config-driven (one-line to add one).

Quota: ~1,080 units for a 3h live event + ~1,150/day discovery — well under 10k.

Deploy: needs YOUTUBE_API_KEY; set PUBLIC_BASE_URL to enable push and PUBSUB_SECRET to verify payloads (see .env.example).

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 12 out of 14 changed files in this pull request and generated 4 comments.

Files not reviewed (1)
  • pnpm-lock.yaml: Generated file

Comment on lines +66 to +81
private reconcileTimer?: ReturnType<typeof setInterval>;
private discoveryTimer?: ReturnType<typeof setInterval>;
private tickCount = 0;

constructor(private readonly config: ConfigService) {}

async onModuleInit(): Promise<void> {
// Seed initial state from each channel's (free) RSS feed so we are not
// blank until the first push arrives.
await this.discovery();
this.discoveryTimer = setInterval(
() => void this.discovery(),
DISCOVERY_INTERVAL_MS,
);
this.reconcileTimer = setInterval(() => void this.tick(), RECONCILE_TICK_MS);
}
Comment thread src/livestream/pubsub.service.ts
Comment thread src/livestream/pubsub.controller.ts Outdated
Comment thread src/livestream/livestream.service.ts Outdated
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 9, 2026 14:14
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 12 out of 14 changed files in this pull request and generated 4 comments.

Files not reviewed (1)
  • pnpm-lock.yaml: Generated file

Comment thread src/livestream/pubsub.controller.ts Outdated
Comment thread src/livestream/pubsub.service.ts Outdated
Comment on lines +44 to +48
void this.subscribeAll(baseUrl);
this.renewTimer = setInterval(
() => void this.subscribeAll(baseUrl),
RENEW_INTERVAL_MS,
);
Comment thread src/livestream/livestream.service.ts
Comment thread src/livestream/pubsub.controller.ts
Copilot AI review requested due to automatic review settings July 9, 2026 14:24

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 12 out of 14 changed files in this pull request and generated 6 comments.

Files not reviewed (1)
  • pnpm-lock.yaml: Generated file

Comment thread src/livestream/pubsub.service.ts
Comment thread src/livestream/livestream.service.ts Outdated
Comment thread src/livestream/livestream.service.ts
Comment thread src/livestream/pubsub.controller.ts
Comment thread src/livestream/livestream.service.ts
Comment thread src/livestream/livestream.service.ts
…nt; add pruning of expired streams. Update PubSubController to limit processed video IDs and improve signature verification. Refactor PubSubService to handle subscription errors gracefully.
Copilot AI review requested due to automatic review settings July 9, 2026 15:00

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 12 out of 14 changed files in this pull request and generated 3 comments.

Files not reviewed (1)
  • pnpm-lock.yaml: Generated file

Comment thread src/main.ts Outdated
Comment thread src/livestream/pubsub.controller.ts Outdated
Comment thread src/livestream/livestream.service.ts
…ubSubService to use stackOf function for improved logging of error details.
Copilot AI review requested due to automatic review settings July 9, 2026 15:07
…te for channels; update PubSubController to enforce PUBSUB_SECRET validation and improve signature verification. Refactor main.ts to ensure proper raw body parsing for YouTube WebSub pushes.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 12 out of 14 changed files in this pull request and generated 2 comments.

Files not reviewed (1)
  • pnpm-lock.yaml: Generated file

Comment thread src/livestream/pubsub.controller.ts Outdated
Comment thread src/main.ts Outdated
Copilot AI review requested due to automatic review settings July 9, 2026 15:13

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 12 out of 14 changed files in this pull request and generated 4 comments.

Files not reviewed (1)
  • pnpm-lock.yaml: Generated file

Comment thread src/livestream/pubsub.service.ts
Comment thread .env.example Outdated
Comment thread src/livestream/pubsub.controller.ts
Comment thread src/livestream/pubsub.controller.ts Outdated
… PUBSUB_SECRET for push notifications, improve error handling in PubSubController, and adjust raw body parsing in main.ts for HMAC verification.
Copilot AI review requested due to automatic review settings July 9, 2026 16:07

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 12 out of 14 changed files in this pull request and generated 4 comments.

Files not reviewed (1)
  • pnpm-lock.yaml: Generated file

Comment on lines +286 to +293
const active: string[] = [];
for (const videos of this.tracked.values()) {
for (const v of videos.values()) {
if (v.status === 'live' || v.status === 'upcoming') {
active.push(v.videoId);
}
}
}
Comment on lines +82 to +84
// Seed initial state from each channel's (free) RSS feed so we are not
// blank until the first push arrives.
await this.discovery();
Comment on lines +44 to +53
verify(@Query() query: Record<string, string>): string {
const challenge = query['hub.challenge'];
if (!challenge) {
throw new BadRequestException('Missing hub.challenge');
}
this.logger.log(
`Verified ${query['hub.mode']} for ${query['hub.topic']}`,
);
return challenge;
}
Comment on lines +95 to +104
const params = new URLSearchParams({
'hub.mode': 'subscribe',
'hub.topic': topic,
'hub.callback': callback,
'hub.verify': 'async',
'hub.lease_seconds': String(LEASE_SECONDS),
});
if (secret) {
params.set('hub.secret', secret);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants