Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
d144070
Add livestream service
mrdarrengriffin Jul 9, 2026
5ff8c27
Potential fix for pull request finding
mrdarrengriffin Jul 9, 2026
dfb64ca
Potential fix for pull request finding
mrdarrengriffin Jul 9, 2026
519a220
Potential fix for pull request finding
mrdarrengriffin Jul 9, 2026
290fc9a
Potential fix for pull request finding
mrdarrengriffin Jul 9, 2026
5c9b8d4
Potential fix for pull request finding
mrdarrengriffin Jul 9, 2026
e4bbc26
Switch implementation
mrdarrengriffin Jul 9, 2026
d12e059
Potential fix for pull request finding
mrdarrengriffin Jul 9, 2026
3bee330
Potential fix for pull request finding
mrdarrengriffin Jul 9, 2026
9cfc4f4
Refactor YouTube feed URL usage to utilize a centralized feedUrl func…
mrdarrengriffin Jul 9, 2026
92b2eb2
Enhance LivestreamService with discovery and reconcile state manageme…
mrdarrengriffin Jul 9, 2026
0284ebb
Refactor error handling in LivestreamService, PubSubController, and P…
mrdarrengriffin Jul 9, 2026
0b463b5
Enhance LivestreamService error handling and ensure deterministic sta…
mrdarrengriffin Jul 9, 2026
6219cb1
Update environment configuration and enhance PubSub handling; require…
mrdarrengriffin Jul 9, 2026
382b0dc
Potential fix for pull request finding
mrdarrengriffin Jul 14, 2026
1d584a1
Potential fix for pull request finding
mrdarrengriffin Jul 14, 2026
14beac1
Potential fix for pull request finding
mrdarrengriffin Jul 14, 2026
cf5c20e
Potential fix for pull request finding
mrdarrengriffin Jul 14, 2026
495aeba
Potential fix for pull request finding
mrdarrengriffin Jul 15, 2026
704f106
Potential fix for pull request finding
mrdarrengriffin Jul 15, 2026
db0f8c0
Potential fix for pull request finding
mrdarrengriffin Jul 15, 2026
612f960
Potential fix for pull request finding
mrdarrengriffin Jul 15, 2026
849eb22
Potential fix for pull request finding
mrdarrengriffin Jul 15, 2026
baab315
Potential fix for pull request finding
mrdarrengriffin Jul 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@
"name": "Open Home Foundation - API",
"image": "mcr.microsoft.com/devcontainers/typescript-node:24-bookworm",
"forwardPorts": [
443
3000
],
"portsAttributes": {
"443": {
"label": "HTTPS server"
"3000": {
"label": "web-api (HTTP)"
}
},
"customizations": {
Expand All @@ -31,4 +31,4 @@
},
"postCreateCommand": "pnpm install",
"remoteUser": "node"
}
}
18 changes: 18 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# YouTube Data API v3 key (https://console.cloud.google.com/apis/credentials)
YOUTUBE_API_KEY=

# Publicly reachable base URL of this service, used as the WebSub callback
# target for YouTube push notifications (e.g. https://api.openhomefoundation.org).
# Leave blank in local dev without a public tunnel; status will still update via
# the periodic reconcile.
PUBLIC_BASE_URL=

# Shared secret for verifying WebSub push payloads (HMAC signature). Required
# for push notifications: without it the service skips subscriptions and the
# /pubsub webhook rejects all incoming notifications (403). Generate one with
# `openssl rand -hex 32`.
PUBSUB_SECRET=
Comment thread
Copilot marked this conversation as resolved.

# Optional token echoed by the hub during subscription verification.
# If set, /pubsub GET requires hub.verify_token to match this value.
PUBSUB_VERIFY_TOKEN=
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ node_modules/
dist/
*.log
.DS_Store
.env
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
},
"dependencies": {
"@nestjs/common": "^11.1.19",
"@nestjs/config": "^4.0.4",
"@nestjs/core": "^11.1.19",
"@nestjs/platform-express": "^11.1.19",
"@nestjs/platform-socket.io": "^11.1.19",
Expand Down
37 changes: 37 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 7 additions & 1 deletion src/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { AppController } from './app.controller';
import { AppGateway } from './app.gateway';
import { HealthModule } from './health';
import { getVersionInfo } from './health/version';
import { LivestreamModule } from './livestream';

@Module({
imports: [HealthModule.register({ version: getVersionInfo() })],
imports: [
ConfigModule.forRoot({ isGlobal: true }),
HealthModule.register({ version: getVersionInfo() }),
LivestreamModule,
],
controllers: [AppController],
providers: [AppGateway],
})
Expand Down
4 changes: 4 additions & 0 deletions src/livestream/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export * from './livestream.channels';
export * from './livestream.module';
export * from './livestream.service';
export * from './pubsub.service';
36 changes: 36 additions & 0 deletions src/livestream/livestream.channels.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
export interface Channel {
/** URL-safe identifier used in the API path, e.g. "home-assistant". */
slug: string;
/** Human-friendly display name. */
name: string;
/** YouTube handle without the leading "@". */
handle: string;
}

/**
* Open Home Foundation project channels tracked for livestreams.
* Add a new project by appending an entry here — no other code changes needed.
*/
export const CHANNELS: readonly Channel[] = [
{ slug: 'home-assistant', name: 'Home Assistant', handle: 'home_assistant' },
{ slug: 'esphome', name: 'ESPHome', handle: 'esphomeio' },
{
slug: 'open-home-foundation',
name: 'Open Home Foundation',
handle: 'OpenHomeFndn',
},
{
slug: 'music-assistant',
name: 'Music Assistant',
handle: 'musicassistantio',
},
];

/**
* Canonical YouTube channel RSS feed URL, used both as the discovery source and
* as the WebSub (PubSubHubbub) subscription topic — keep a single form so the
* two code paths cannot drift. This is the URL documented for the hub topic:
* https://developers.google.com/youtube/v3/guides/push_notifications
*/
export const feedUrl = (channelId: string): string =>
`https://www.youtube.com/feeds/videos.xml?channel_id=${channelId}`;
18 changes: 18 additions & 0 deletions src/livestream/livestream.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Controller, Get, Param } from '@nestjs/common';

import { LivestreamInfo, LivestreamService } from './livestream.service';

@Controller('livestream')
export class LivestreamController {
constructor(private readonly livestream: LivestreamService) {}

@Get()
getAll(): LivestreamInfo[] {
return this.livestream.getAll();
}

@Get(':slug')
getStatus(@Param('slug') slug: string): LivestreamInfo {
return this.livestream.getStatus(slug);
}
}
12 changes: 12 additions & 0 deletions src/livestream/livestream.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Module } from '@nestjs/common';

import { LivestreamController } from './livestream.controller';
import { LivestreamService } from './livestream.service';
import { PubSubController } from './pubsub.controller';
import { PubSubService } from './pubsub.service';

@Module({
controllers: [LivestreamController, PubSubController],
providers: [LivestreamService, PubSubService],
})
export class LivestreamModule {}
Loading