Skip to main content

Channel Status

The channel status system tracks which streaming channels are currently online. It is a generic utility that any feature can consume — currently used by the Spotify worker to avoid unnecessary API requests and event storage when offline.

Architecture

PostgreSQL (source of truth) + Redis (cache + pub/sub)

Database

The channel_status table in PostgreSQL stores the current state per account per platform:

channel_status (
account_id UUID, -- FK → accounts
platform VARCHAR(50), -- "twitch", "youtube", "kick", "trovo"
broadcast_id TEXT, -- per-broadcast identifier (multi-stream)
is_online BOOLEAN,
broadcast_status TEXT, -- "live", "upcoming", or NULL
stream_title TEXT,
category TEXT,
viewer_count INTEGER,
like_count INTEGER, -- YouTube only
total_views BIGINT, -- YouTube only
started_at TIMESTAMPTZ,
scheduled_start TIMESTAMPTZ, -- upcoming broadcast start time
live_chat_id TEXT, -- YouTube live chat ID
updated_at TIMESTAMPTZ,
PRIMARY KEY (account_id, platform, broadcast_id)
)

A partial index on is_online = TRUE enables fast lookups.

Redis

Three Redis structures per account:

KeyTypePurpose
lumio:channel_status:{account_id}:{platform}String (JSON)Cached status per platform (24h safety TTL)
lumio:channel_online:{account_id}SETSet of online platform names (SADD/SREM/SCARD)
lumio:channel_status:{account_id}Pub/SubStatus change notifications

The online set uses SADD/SREM for idempotency — duplicate events have no effect.

Manual Connect

KeyTypePurpose
lumio:spotify_manual:{account_id}StringManual override, TTL 30 minutes

Rust API

The channel status service (apps/api/src/services/channel_status.rs) exposes a platform-agnostic interface:

// Core operations
services::channel_status::set_online(pool, redis, account_id, platform, metadata) -> Result<()>
services::channel_status::set_offline(pool, redis, account_id, platform) -> Result<()>

// Queries (via db::channel_status)
db::channel_status::is_any_online(pool, account_id) -> Result<bool>
db::channel_status::get_status(pool, account_id) -> Result<Vec<ChannelStatus>>

Integration with Platform Adapters

Platform adapters call the channel status module directly when detecting stream status changes:

// In the Twitch EventSub worker:
"stream.online" => {
channel_status::set_online(&db, &redis, account_id, "twitch", metadata).await?;
// ... then proceed with normal event processing
}
"stream.offline" => {
channel_status::set_offline(&db, &redis, account_id, "twitch").await?;
// ... then proceed with normal event processing
}

The same pattern applies to YouTube, Kick, and Trovo adapters.

Worker Lifecycle

A channel_status_relay background worker subscribes to lumio:channel_status:* via PSUBSCRIBE and reacts to status changes by starting/stopping the Spotify worker through the WorkerManager.

A separate 60-second poll task checks for expired manual connect keys.

Status change → channel_status_relay → WorkerManager → start/stop Spotify worker

Decision logic:

SCARD(online_set) > 0 OR manual_key exists?
├── YES + Worker not running → start_spotify_worker()
├── YES + Worker running → no-op
├── NO + Worker running → stop_spotify_worker()
└── NO + Worker not running → no-op

Server Start Recovery

On startup, the API server:

  1. Reads channel_status from PostgreSQL for all accounts
  2. Rebuilds Redis keys (cache + online sets)
  3. Starts Spotify workers only for accounts with at least one online channel

GraphQL API

type ChannelStatus {
platform: String!
broadcastId: String!
isOnline: Boolean!
broadcastStatus: String
streamTitle: String
category: String
viewerCount: Int
likeCount: Int
totalViews: Int
startedAt: DateTime
scheduledStart: DateTime
liveChatId: String
updatedAt: DateTime!
}

type SpotifyManualStatus {
active: Boolean!
remainingSeconds: Int
}

type Query {
channelStatus: [ChannelStatus!]!
spotifyManualStatus: SpotifyManualStatus!
}

type Mutation {
startSpotifyManual: SpotifyManualStatus!
stopSpotifyManual: Boolean!
}

All queries/mutations use account_id from AuthContext.

REST API

MethodPathDescription
GET/v1/channel-statusStatus for authenticated account
GET/v1/spotify/manual-connectManual connect status + remaining time
POST/v1/spotify/manual-connectStart manual connect (30min TTL)
DELETE/v1/spotify/manual-connectStop manual connect

All routes support popout token auth.

WebSocket Events

Status changes are broadcast (not persisted) on lumio:events:{account_id}:

  • channel:online{ platform, stream_title?, game_name?, viewer_count?, started_at }
  • channel:offline{ platform }

Adding New Consumers

To use channel status in a new feature:

  1. Import the channel status module
  2. Call is_any_online(redis, account_id) for a fast boolean check
  3. Or subscribe to lumio:channel_status:{account_id} for reactive updates
  4. No changes needed to the channel status module itself