Skip to content

Add encoding jobs live command#9

Merged
CMThF merged 13 commits into
mainfrom
feat/encoding-jobs-live-status
May 6, 2026
Merged

Add encoding jobs live command#9
CMThF merged 13 commits into
mainfrom
feat/encoding-jobs-live-status

Conversation

@dweinber
Copy link
Copy Markdown
Member

@dweinber dweinber commented Apr 29, 2026

Summary

Add bitmovin encoding jobs live <id> to fetch live encoding details (encoder IP, stream key, application) from GET /v1/encoding/encodings/{id}/live.

Why

This is the single most-asked question of a running live encoding: once encoding templates start returns, the user needs the encoder's public IP to point their contribution encoder (ffmpeg, OBS, hardware encoder) at it for SRT/RTMP push. Previously this required hand-rolling a curl call against the API.

Output Examples

Default (human-readable):

Encoder IP:   192.0.2.10
Stream Key:   test
Application:  live

JSON:

{
  "streamKey": "test",
  "encoderIp": "192.0.2.10",
  "application": "live"
}

When the encoder is still queued/spinning up and the live details endpoint reports that details are not available yet, the command exits successfully and shows:

Live encoding details are not available yet. The encoder may still be queued or spinning up.
Encoder IP:   (not yet running)
Stream Key:   (unknown)
Application:  (unknown)

JSON for that state:

{
  "available": false,
  "message": "Live encoding details are not available yet. The encoder may still be queued or spinning up."
}

Tests

  • New tests cover the human-readable output, the unavailable-details fallback, the unset-encoderIp fallback, and --json mode.
  • All tests pass.
  • Verified end-to-end against the real API: returns the expected fields against a recent live encoding.

Test plan

  • CI green
  • bitmovin encoding jobs live <id> returns encoder details
  • bitmovin encoding jobs live <id> --json returns JSON
  • Queued/spinning-up live encodings print a friendly fallback instead of API error: 400

Add `bitmovin encoding jobs live <id>` to fetch live encoding details
(encoder IP, stream key, application) from
GET /v1/encoding/encodings/{id}/live. This is the single most-asked
question of a running live encoding (you need the encoder IP to point
your contribution encoder at it for SRT/RTMP push), and previously
required hand-rolling a curl call.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a new CLI subcommand to retrieve live encoding connection details (encoder IP, stream key, application) for a running Bitmovin encoding via the GET /v1/encoding/encodings/{id}/live endpoint.

Changes:

  • Introduces bitmovin encoding jobs live <id> with human-readable output and --json support.
  • Adds Vitest coverage for normal output, the “no encoder IP yet” fallback, and JSON mode.

Reviewed changes

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

File Description
src/commands/encoding/jobs/live.ts Implements the new encoding jobs live command and output formatting (human + JSON).
test/commands/encoding-job.test.ts Extends the mocked API and adds tests for the new command’s output modes and fallback behavior.

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

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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 2 out of 2 changed files in this pull request and generated 3 comments.


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

Comment thread src/commands/encoding/jobs/live.ts
Comment thread src/commands/encoding/jobs/live.ts Outdated
Comment thread test/commands/encoding-job.test.ts Outdated
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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 2 out of 2 changed files in this pull request and generated 1 comment.


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

Comment thread src/commands/encoding/jobs/live.ts Outdated
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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 2 out of 2 changed files in this pull request and generated 1 comment.


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

Comment thread src/commands/encoding/jobs/live.ts Outdated
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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 3 out of 3 changed files in this pull request and generated no new comments.


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

@dweinber dweinber requested a review from lukaskroepfl April 30, 2026 06:28
@lukaskroepfl
Copy link
Copy Markdown
Member

Useful command. A few notes:

  1. Robustness of isLiveDetailsUnavailable — is there a stable Bitmovin API errorCode (numeric) for this case? If so, please match on that instead of substring-matching developerMessage/errorCode strings. Today's heuristic relies on the wording "live" + "not" + "available" — if the API ever rewords this (e.g. collapses "not available" to "unavailable"), the fallback silently breaks and users see API error: 400 again, which is exactly what this PR set out to fix. The test mocks the same string production matches, so the regression would be invisible in CI.

  2. Bypasses this.outputData — every other command in this CLI flows through outputData (which gives you --fields/--jq filtering and the JSON-vs-table dispatch for free). Here you've handcrafted three process.stdout.write lines plus a leading message. Consider building a display object (with the (not yet running) / (unknown) fallbacks baked in) and calling outputData(display); emit the "not available yet" preamble via this.log first if available === false. That gets you bitmovin encoding jobs live <id> --fields encoderIp working consistently with the rest of the CLI, and routes the preamble to stderr automatically under --json (per base-command.ts:38–45).

  3. Use the SDK's typed return(await api.encoding.encodings.live.get(...)) as LiveDetails casts away the SDK type. PR Mask secrets in account info by default #6 set the precedent of importing the SDK type (AccountInformation). If the SDK exports a LiveEncodingDetails-style model, prefer that over the local LiveDetails interface for the same reason — when the SDK adds fields, you'll see them; when it removes one, the compiler tells you.

Copy link
Copy Markdown
Member

@lukaskroepfl lukaskroepfl left a comment

Choose a reason for hiding this comment

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

All three points addressed cleanly:

  1. Error matching robustness — the new mentionsUnavailableLiveDetails regex (\b(?:unavailable|not\s+(?:yet\s+)?available)\b plus a "live + details" predicate) plus the errorCode/message normalization ([_-]+ → space, lowercase) widens the safety net considerably. Notably it now catches both LIVE_ENCODING_DETAILS_UNAVAILABLE errorCodes and "Live details … are unavailable" wording, with a regression test for the alternate-wording case. Still text-based rather than keyed on a stable numeric code, but the surface that has to break for this to silently regress is much smaller.
  2. outputData adoption — display object built with placeholders, --fields filtering test added, preamble routed via this.log (which lands on stderr under --json). ✅
  3. SDK typed returnLiveEncoding from @bitmovin/api-sdk imported, cast removed from the call site. ✅

One small thing for follow-up (not blocking): in JSON mode, queued encodings now serialize the human placeholders ("encoderIp": "(not yet running)") into the JSON. Scripts that key off data.encoderIp truthiness used to get undefined for unavailable; now they get a non-empty string. If you'd rather have null in JSON and the placeholder only in the table render, you'd need to branch on jsonMode when building output. Up to you — the unified shape is also a defensible choice.

Approving.

@CMThF
Copy link
Copy Markdown
Contributor

CMThF commented May 5, 2026

Very nice! Ty!

Copy link
Copy Markdown
Contributor

@CMThF CMThF left a comment

Choose a reason for hiding this comment

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

Dug a little deeper through it and tried it in my skills. we still have gaps for live:

- redundantRtmp: stream keys live on staticIngestPoints, exposed via live.stream_keys.list?assignedEncodingId=…, not live.get. 
- SRT: needs to walk streams.list → inputs.srt.get to surface mode/host/port/path. 

can we add those quickly?

CMThF and others added 2 commits May 5, 2026 07:55
Stream keys for redundant RTMP encodings live on static ingest
points and are not returned by live.get. Fetch them via
streamKeys.list?assignedEncodingId so all keys are surfaced.
For SRT, walk streams.list to inputs.srt.get to expose
mode/host/port/path. Output now uses streamKeys[] and
srtInputs[] for a consistent JSON shape.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Match the prior naming and have LiveDetails extend LiveEncoding so
its encoderIp/application/streamKey fields are inherited rather than
re-declared.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@lukaskroepfl
Copy link
Copy Markdown
Member

Quick note: my earlier approval was on f0fb222c (the simpler encoderIp/streamKey/application scope). The redundant-RTMP + SRT additions (b28b278d, d2c6f836) are a meaningful expansion, so my prior LGTM doesn't carry over cleanly. I'm not re-approving formally — CMThF is the active reviewer and is closer to the SDK / live encoding semantics here — but two things stood out on a skim that I'd want addressed:

  1. Promise.all on the three fetchers means any one failing fails the whole command. fetchLiveDetails has its own try/catch for the queued state, but fetchAssignedStreamKeys and fetchSrtInputs don't. So a transient 5xx, 403, or a permissions edge on live.streamKeys.list / encodings.streams.list / inputs.type.get will now bubble up and the user sees API error: ... even when live.get succeeded. Preferably: live.get success → return what's available, and degrade streamKeys / srtInputs to empty arrays with a stderr warning when their fetcher rejects. That preserves the "show me the encoder IP even if the rest is messy" UX.

  2. Output shape change is breaking for --json consumers from the previous PR head. The old shape had streamKey: "..." (singular string); the new shape removes that field and replaces it with streamKeys: [{value, ingestPointId, status}]. The backward-compat shim populates streamKeys[0].value when the API returns no assigned keys but live.streamKey is set, but anything reading data.streamKey directly will silently get undefined. The CHANGELOG entry currently describes the additive expansion but doesn't call out that the singular field is gone. Worth either calling that out explicitly under [Unreleased] / Changed (BREAKING vs the prior unreleased state), or leaving a streamKey alias on the output equal to streamKeys[0]?.value.

Neither is a blocker if intentional — but #1 is a regression I'd want to fix before merge.

CMThF and others added 3 commits May 5, 2026 09:34
Address lukaskroepfl review follow-up: JSON consumers that key off
truthiness would treat the human placeholder strings ("(not yet
running)" / "(unknown)") as real values. Branch on jsonMode so
encoderIp/application come back as null in JSON when the live
encoder has not started, while keeping the friendly placeholders
in the table render.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
A 5xx, 403, or other transient error from streamKeys.list,
streams.list, or inputs.type/.srt.get used to bubble through
Promise.all and surface as "API error: ..." even when live.get
already returned the encoder IP. Catch errors inside the
auxiliary fetchers, return empty arrays, and emit a stderr
warning describing the underlying failure. Live.get errors still
propagate (except the existing "details unavailable" 400).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Earlier iterations of the live command exposed streamKey as a
single string. The redundant-RTMP fix replaced it with
streamKeys[]. Restore a streamKey alias that mirrors
streamKeys[0]?.value so consumers reading data.streamKey keep
working, and clarify in the CHANGELOG that redundant RTMP
callers should read streamKeys[] for every per-ingest-point key.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@CMThF
Copy link
Copy Markdown
Contributor

CMThF commented May 5, 2026

I applied your feedback @lukaskroepfl

  • missing encoderIP in json branch now returns null instead of the custom string
  • Promise.all on the three fetchers: Changed that.
    • But I have to push back on this strategy for the cli: it is very unlikely to get one but not the others - and in that case the encoding is likely not botched anyway. if you don't get the stream key or the input, but only the IP, I assume something is very much off. And in such a case I would prefer to fail and not continue with a zombie missing an arm and a leg...
  • Alias: streamKey is back on the output (in JSON: streamKeys[0]?.value ?? null; in table mode: '(unknown)' placeholder when missing). LiveDetailsOutput overrides the field type to string | null.

@CMThF CMThF requested review from CMThF and Copilot May 5, 2026 08:02
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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 3 out of 3 changed files in this pull request and generated 4 comments.


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

Comment thread src/commands/encoding/jobs/live.ts Outdated
Comment thread src/commands/encoding/jobs/live.ts Outdated
Comment thread CHANGELOG.md
Comment thread test/commands/encoding-job.test.ts Outdated
Copy link
Copy Markdown
Member

@lukaskroepfl lukaskroepfl left a comment

Choose a reason for hiding this comment

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

All three points addressed cleanly:

  1. Auxiliary fetcher error handlingfetchAssignedStreamKeys and fetchSrtInputs now return FetchResult<T> and degrade to empty arrays + a this.warn(...) to stderr on failure. So a transient 5xx on stream-key listing or SRT enumeration no longer kills the command — the user still sees the encoder IP. describeApiError formats the error with httpStatusCode + developerMessage, which is the right amount of detail.
  2. streamKey backward-compat alias — singular streamKey is preserved on the output as streamKeys[0]?.value, with a clear inline comment about why and a CHANGELOG note explaining the shape evolution. Redundant-RTMP test asserts the alias points to the first key. ✅
  3. Bonusnull instead of (not yet running) placeholder strings in JSON output addresses the non-blocking note from the earlier review. JSON consumers can now do truthiness checks cleanly.

Re-approving on a80a4de4 so the review state on this PR reflects the current scope rather than the smaller f0fb222c it was on before.

- Per-input tolerance in fetchSrtInputs: a transient failure on one
  inputs.type.get/inputs.srt.get now drops only that input rather than
  collapsing every other valid SRT endpoint, with a single combined
  warn line summarising the failures.
- Add bitmovin encoding jobs live to README.md and src/commands/skill.ts
  inventories so users and AI tooling that scan the documented command
  list can find the new command.
- Replace the generic outputData() rendering for the default
  (non-JSON) output with a status.ts-style summary: friendly Encoder IP
  / Application / Stream Key labels, a Stream Keys list for redundant
  RTMP, and an SRT Inputs section for SRT encodings. Tests assert the
  exact human-readable layout.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@CMThF CMThF merged commit d02bbca into main May 6, 2026
3 checks passed
@lukaskroepfl lukaskroepfl deleted the feat/encoding-jobs-live-status branch May 6, 2026 14:08
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.

4 participants