Recipe: live channels (resolve by name, read status, start/stop safely)

Recipe: live channels (resolve by name, read status, start/stop safely)

Recipe: live channels (resolve by name, read status, start/stop safely)

💡

Prompt example

Find the live channel named "Studio A", show its status, start it and wait until it's running, then stop it.

Vibe prompts name a channel by name ("Studio A") but every lifecycle verb takes a <live-channel-id>. These helpers resolve the id, read status via the status name the platform returns, and drive the start/stop lifecycle. Reads are Class A (safe anywhere); start/stop are Class C — they allocate real, billable streaming infrastructure and have no folder anchor, so they run non-prod only and must always be paired.

Key facts

  • get_live_channels returns a plain array of channel objects (not {items}). Resolve by channel["name"].
  • A channel's status is nested: channel["status"]["description"] is the status name ("Idle", "Running", "Starting", "Stopping", "Error", …) and channel["status"]["id"] is a LiveChannelStatusLookup UUID. See reference/enums.md for the full status/type tables (ints, names, and lookup UUIDs).
  • Status verb: Python keeps get_live_channel_status internal, so derive status from the channel object (as below). JS exposes getLiveChannelStatus(id) publicly.
  • start_live_channel(id, wait_for_start=True) blocks until Running (≤1200s @20s); stop_live_channel(id, wait_for_stop=True) blocks until Idle (≤720s @2s). Pass wait=False to fire-and-poll yourself.

Python

# components: get_live_channels, get_live_channel, start_live_channel, stop_live_channel
RUNNING, IDLE = "Running", "Idle"  # LiveChannelStatus names; see reference/enums.md

def resolve_channel_id(sdk, name):
    """Resolve a live channel by exact name to its id; None if absent.

    Raises on ambiguity (two channels sharing a name) — fail-closed, never guess.
    """
    matches = [c["id"] for c in (get_live_channels(sdk) or []) if c.get("name") == name]
    unique = list(dict.fromkeys(matches))
    if len(unique) > 1:
        raise ValueError(f"Ambiguous live channel name {name!r}: {len(unique)} matches")
    return unique[0] if unique else None

def channel_status(sdk, channel_id):
    """Current status NAME ('Idle'/'Running'/...) for channel_id, or None.

    Python has no public status verb, so read it off the channel object exactly as
    the SDK's internal helper does: channel['status']['description'].
    """
    channel = get_live_channel(sdk, channel_id)
    return ((channel or {}).get("status") or {}).get("description")

def start_and_confirm(sdk, channel_id):
    """Start the channel, blocking until Running. CLASS C — non-prod only."""
    start_live_channel(sdk, channel_id, True)   # waits for Running
    assert channel_status(sdk, channel_id) == RUNNING
    return RUNNING

def stop_and_confirm(sdk, channel_id):
    """Stop the channel, blocking until Idle. Always run this after start_and_confirm."""
    stop_live_channel(sdk, channel_id, True)    # waits for Idle
    assert channel_status(sdk, channel_id) == IDLE
    return IDLE

JavaScript

// components: getLiveChannels, getLiveChannel, startLiveChannel, stopLiveChannel
const RUNNING = "Running", IDLE = "Idle"; // LiveChannelStatus names; see reference/enums.md

export async function resolveChannelId(sdk, name) {
    const matches = ((await getLiveChannels(sdk)) || [])
        .filter((c) => c.name === name).map((c) => c.id);
    const unique = [...new Set(matches)];
    if (unique.length > 1) throw new Error(`Ambiguous live channel name '${name}': ${unique.length} matches`);
    return unique.length ? unique[0] : null;
}

export async function channelStatus(sdk, channelId) {
    // JS also exposes sdk.getLiveChannelStatus(channelId) directly; this mirrors Python.
    const channel = await getLiveChannel(sdk, channelId);
    return channel?.status?.description ?? null;
}

export async function startAndConfirm(sdk, channelId) {
    // CLASS C — non-prod only.
    await startLiveChannel(sdk, channelId, true); // waits for Running
    if ((await channelStatus(sdk, channelId)) !== RUNNING) throw new Error("channel not Running");
    return RUNNING;
}

export async function stopAndConfirm(sdk, channelId) {
    await stopLiveChannel(sdk, channelId, true);  // waits for Idle
    if ((await channelStatus(sdk, channelId)) !== IDLE) throw new Error("channel not Idle");
    return IDLE;
}

Notes

  • Always pair start with stop. There is no run-root cascade for live channels, so the only cleanup is an explicit stop_*. Wrap start/stop in try/finally so a started channel is stopped even if an assertion fails.
  • Prerequisites to start. A channel generally needs an attached input (and, for some types, ≥2 schedule events) or it bounces back to Idle/Error. Inspect channel["associatedInputs"] / get_live_inputs first; read channel["statusMessage"] on failure.
  • Stale status? Call live_channel_refresh (Class B) to resync from infrastructure, then re-read get_live_channels.
  • Channel type lives at channel["type"] (a lookup); map ints/UUIDs via the LiveChannelType / LiveChannelTypeLookup tables in reference/enums.md.