Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
153 changes: 152 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,20 @@ mux webhooks trigger video.asset.created --forward-to http://localhost:3000/api/

Run `mux webhooks trigger <invalid-type>` to see all supported event types.

### Webhook Management

Configure the webhooks that Mux delivers events to for the current environment.

```bash
mux webhooks create --address https://example.com/api/webhooks/mux
mux webhooks list
mux webhooks get <webhook-id>
mux webhooks update <webhook-id> [--address <url>] [--enable | --disable]
mux webhooks delete <webhook-id> [--force]
```

The signing secret is printed when a webhook is created; store it securely and use it to verify webhook signatures.

## Commands

<details open>
Expand Down Expand Up @@ -350,10 +364,14 @@ Update metadata fields on a video asset. At least one field flag must be provide
- `--creator-id <string>` - Set `meta.creator_id` (max 128 characters)
- `--external-id <string>` - Set `meta.external_id` (max 128 characters)
- `--passthrough <string>` - Set `passthrough` (max 255 characters)
- `--thumbnail-time <seconds>` - Set the asset's default thumbnail time
- `--clear-thumbnail-time` - Reset the default thumbnail time

```bash
mux assets update abc123xyz --title "My Video" --creator-id "user-42"
mux assets update abc123xyz --title "" # clear a field
mux assets update abc123xyz --thumbnail-time 12.5
mux assets update abc123xyz --clear-thumbnail-time
```

#### `mux assets delete <asset-id>`
Expand Down Expand Up @@ -413,6 +431,16 @@ mux assets static-renditions delete <asset-id> <rendition-id> [--force]

**Resolution options:** `highest`, `audio-only`, `2160p`, `1440p`, `1080p`, `720p`, `540p`, `480p`, `360p`, `270p`

#### Shots Management

Shot detection data segments a video into its individual shots. Generated data is reused by Robots jobs.

```bash
mux assets shots get <asset-id>
mux assets shots generate <asset-id>
mux assets shots delete <asset-id> [--force]
```

#### Track Management

Manage text and audio tracks (subtitles, captions, audio) on video assets.
Expand All @@ -434,6 +462,16 @@ Add a text or audio track to an asset.
mux assets tracks create abc123xyz --url https://example.com/subs.vtt --type text --language-code en --text-type subtitles
```

##### `mux assets tracks update <asset-id> <track-id>`

Update a text track (subtitles/captions) on an asset. At least one field flag must be provided. The API rejects non-text tracks.

**Options:** `--name <name>`, `--language-code <code>`, `--closed-captions <true|false>`, `--passthrough <string>`

```bash
mux assets tracks update abc123xyz track456 --name "English (SDH)" --closed-captions true
```

##### `mux assets tracks delete <asset-id> <track-id>`

Delete a track from an asset. Supports `--force`.
Expand Down Expand Up @@ -861,6 +899,8 @@ mux robots cancel <job-id>

**List options:** `--workflow`, `--status`, `--asset-id`, `--limit`, `--page`, `--compact`

**Workflow values:** `summarize`, `moderate`, `generate-chapters`, `ask-questions`, `find-key-moments`, `translate-captions`, `edit-captions`, `find-best-thumbnails`, `find-scenes`, `generate-engagement-insights`, `generate-premium-captions`, `translate-audio`

#### `mux robots summarize <asset-id>`

Generate a title, description, and tags for a video.
Expand Down Expand Up @@ -948,6 +988,104 @@ Translate captions on a video to another language.
mux robots translate-captions abc123 --track-id track456 --to-language-code es
```

#### `mux robots translate-audio <asset-id>`

Translate a video's audio track to another language.

**Options:**
- `--to-language-code <code>` - BCP 47 code for the translated audio (required)
- `--no-upload` - Do not upload the translated audio track to Mux
- `--passthrough <string>` - Arbitrary metadata (max 255 chars)

```bash
mux robots translate-audio abc123 --to-language-code es
```

#### `mux robots edit-captions <asset-id>`

Edit a caption track with find/replace rules or automatic profanity censoring.

**Options:**
- `--track-id <id>` - Caption track ID to edit (required)
- `--replace <find=replace>` - Find/replace rule (repeatable)
- `--case-sensitive` - Make `--replace` rules match case-sensitively
- `--censor-profanity` - Automatically censor profanity
- `--censor-mode <mode>` - `blank`, `remove`, or `mask` (implies `--censor-profanity`)
- `--delete-original-track` - Delete the original track after editing
- `--track-name-suffix <suffix>` - Suffix appended to the edited track name
- `--no-upload` - Do not upload the edited VTT to Mux
- `--passthrough <string>` - Arbitrary metadata (max 255 chars)

```bash
mux robots edit-captions abc123 --track-id track456 --replace "Muks=Mux" --censor-profanity
```

#### `mux robots generate-premium-captions <asset-id>`

Generate high-accuracy premium captions for a video.

**Options:**
- `--language-code <code>` - BCP 47 code of the spoken audio (auto-detected when omitted)
- `--include-speakers` - Include speaker labels
- `--include-words` - Include word-level timing
- `--phrase <phrase>` - Domain-specific phrase to boost recognition (repeatable)
- `--replace-existing` - Replace an existing generated caption track in the same language
- `--track-name <name>` - Name for the generated caption track
- `--no-upload` - Do not upload the generated captions to Mux
- `--passthrough <string>` - Arbitrary metadata (max 255 chars)

```bash
mux robots generate-premium-captions abc123 --language-code en --include-speakers
```

#### `mux robots find-best-thumbnails <asset-id>`

Find the best thumbnail candidates for a video.

**Options:**
- `--max-thumbnails <n>` - Maximum number of candidates to return
- `--update-asset-thumbnail` - Set the asset's default thumbnail time to the top candidate
- `--audience <text>` - Intended audience used as scoring guidance
- `--campaign-style <text>` - Campaign/channel thumbnail style to prefer
- `--looking-for <text>` - What to look for in candidate thumbnails
- `--start-time <seconds>` / `--end-time <seconds>` - Execution window on the asset timeline
- `--passthrough <string>` - Arbitrary metadata (max 255 chars)

```bash
mux robots find-best-thumbnails abc123 --max-thumbnails 3 --update-asset-thumbnail
```

#### `mux robots find-scenes <asset-id>`

Detect and describe the scenes in a video.

**Options:**
- `--language-code <code>` - BCP 47 code of the caption track to analyze
- `--min-scene-duration-ms <ms>` - Minimum scene duration
- `--min-scenes <n>` - Minimum number of scenes
- `--audience <text>` - Intended audience used as model guidance
- `--brand-term <term>` - Preferred brand or domain term (repeatable)
- `--narration-detail <detail>` - `concise`, `balanced`, or `detailed`
- `--start-time <seconds>` / `--end-time <seconds>` - Execution window on the asset timeline
- `--passthrough <string>` - Arbitrary metadata (max 255 chars)

```bash
mux robots find-scenes abc123 --min-scenes 5 --narration-detail detailed
```

#### `mux robots generate-engagement-insights <asset-id>`

Analyze a video's engagement data and generate insights.

**Options:**
- `--passthrough <string>` - Arbitrary metadata (max 255 chars)

```bash
mux robots generate-engagement-insights abc123 --wait
```

All job creation commands also support `--file <path>` (JSON config with the full parameters object), `--wait` (poll until the job completes), and `--json`.

</details>

<details>
Expand Down Expand Up @@ -1032,6 +1170,18 @@ mux dimensions list # list available dimensio
mux dimensions values <dimension-id> [--timeframe "24:hours"]
```

#### Engagement

Engagement analytics for a specific video, addressed by exactly one of `--asset-id`, `--playback-id`, or `--video-id`.

```bash
# Viewership across the video timeline
mux engagement heatmap --asset-id <id> [--timeframe "7:days"]

# Most-watched moments
mux engagement hotspots --asset-id <id> [--limit 5] [--order-direction desc]
```

#### Errors

```bash
Expand Down Expand Up @@ -1180,9 +1330,10 @@ src/
│ ├── incidents/ # Mux Data: incident tracking
│ ├── annotations/ # Mux Data: annotation management
│ ├── dimensions/ # Mux Data: dimension queries
│ ├── engagement/ # Mux Data: engagement analytics
│ ├── errors/ # Mux Data: error analytics
│ ├── exports/ # Mux Data: export files
│ ├── webhooks/ # Webhook listening & replay
│ ├── webhooks/ # Webhook config, listening & replay
│ │ ├── events/ # Event storage & replay
│ │ └── listen.ts # Real-time SSE listener
│ ├── env/ # Environment management
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
"@cliffy/ansi": "jsr:1.0.0-rc.8",
"@cliffy/command": "jsr:1.0.0-rc.8",
"@cliffy/prompt": "npm:@jsr/cliffy__prompt@1.0.0-rc.8",
"@mux/mux-node": "^14.0.0",
"@mux/ts": "15.1.0",
"@opentui/core": "^0.1.50",
"@opentui/react": "^0.1.50",
"@std/path": "npm:@jsr/std__path@^1.1.3",
Expand Down
12 changes: 6 additions & 6 deletions pnpm-lock.yaml

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

2 changes: 1 addition & 1 deletion src/commands/assets/create.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Command } from '@cliffy/command';
import type Mux from '@mux/mux-node';
import type Mux from '@mux/ts';
import { wantsJson } from '@/lib/context.ts';
import { handleCommandError } from '@/lib/errors.ts';
import { expandGlobPattern, uploadFile } from '@/lib/file-upload.ts';
Expand Down
2 changes: 2 additions & 0 deletions src/commands/assets/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { inputInfoCommand } from './input-info.ts';
import { listCommand } from './list.ts';
import { manageCommand } from './manage/index.ts';
import { playbackIdsCommand } from './playback-ids/index.ts';
import { shotsCommand } from './shots/index.ts';
import { staticRenditionsCommand } from './static-renditions/index.ts';
import { tracksCommand } from './tracks/index.ts';
import { updateCommand } from './update.ts';
Expand All @@ -27,5 +28,6 @@ export const assetsCommand = new Command()
.command('update-master-access', updateMasterAccessCommand)
.command('manage', manageCommand)
.command('playback-ids', playbackIdsCommand)
.command('shots', shotsCommand)
.command('static-renditions', staticRenditionsCommand)
.command('tracks', tracksCommand);
2 changes: 1 addition & 1 deletion src/commands/assets/list.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Command } from '@cliffy/command';
import type { Asset } from '@mux/mux-node/resources/video/assets';
import type { Asset } from '@mux/ts/resources/video/assets';
import { wantsJson } from '@/lib/context.ts';
import { handleCommandError } from '@/lib/errors.ts';
import {
Expand Down
4 changes: 2 additions & 2 deletions src/commands/assets/manage/AssetManageApp.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/** @jsxImportSource @opentui/react */
import type Mux from '@mux/mux-node';
import type { Asset } from '@mux/mux-node/resources/video/assets';
import type Mux from '@mux/ts';
import type { Asset } from '@mux/ts/resources/video/assets';
import { useKeyboard, useRenderer } from '@opentui/react';
import { useCallback, useEffect, useState } from 'react';
import { getCurrentEnvironment } from '../../../lib/config.ts';
Expand Down
16 changes: 16 additions & 0 deletions src/commands/assets/shots/_shared.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import type { AssetShots } from '@mux/ts/resources/video/assets';

export function printShots(assetId: string, shots: AssetShots): void {
console.log(`Shots for asset ${assetId}:`);
console.log(` Status: ${shots.status}`);
if (shots.shots_manifest_url) {
console.log(` Manifest URL: ${shots.shots_manifest_url}`);
}
if (shots.errors) {
const type = shots.errors.type ? ` (${shots.errors.type})` : '';
console.log(` Errors${type}:`);
for (const message of shots.errors.messages ?? []) {
console.log(` ${message}`);
}
}
}
49 changes: 49 additions & 0 deletions src/commands/assets/shots/delete.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { Command } from '@cliffy/command';
import { wantsJson } from '@/lib/context.ts';
import { handleCommandError } from '@/lib/errors.ts';
import { createAuthenticatedMuxClient } from '@/lib/mux.ts';
import { confirmPrompt } from '@/lib/prompt.ts';

interface DeleteOptions {
force?: boolean;
json?: boolean;
}

export const deleteCommand = new Command()
.description('Delete the shot detection data for an asset')
.arguments('<asset-id:string>')
.option('-f, --force', 'Skip confirmation prompt')
.option('--json', 'Output JSON instead of pretty format')
.action(async (options: DeleteOptions, assetId: string) => {
try {
if (!options.force) {
if (wantsJson(options)) {
throw new Error(
'Deletion requires the --force flag with --json or in agent mode',
);
}

const confirmed = await confirmPrompt({
message: `Are you sure you want to delete the shots data for asset ${assetId}?`,
default: false,
});

if (!confirmed) {
console.log('Deletion cancelled.');
return;
}
}

const mux = await createAuthenticatedMuxClient();

await mux.video.assets.deleteShots(assetId);

if (wantsJson(options)) {
console.log(JSON.stringify({ success: true, assetId }, null, 2));
} else {
console.log(`Shots data for asset ${assetId} deleted successfully`);
}
} catch (error) {
await handleCommandError(error, 'assets', 'shots delete', options);
}
});
32 changes: 32 additions & 0 deletions src/commands/assets/shots/generate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { Command } from '@cliffy/command';
import { wantsJson } from '@/lib/context.ts';
import { handleCommandError } from '@/lib/errors.ts';
import { createAuthenticatedMuxClient } from '@/lib/mux.ts';
import { printShots } from './_shared.ts';

interface GenerateOptions {
json?: boolean;
}

export const generateCommand = new Command()
.description(
'Generate shot detection data for an asset (reused by Robots jobs)',
)
.arguments('<asset-id:string>')
.option('--json', 'Output JSON instead of pretty format')
.action(async (options: GenerateOptions, assetId: string) => {
try {
const mux = await createAuthenticatedMuxClient();

const shots = await mux.video.assets.generateShots(assetId);

if (wantsJson(options)) {
console.log(JSON.stringify(shots, null, 2));
} else {
console.log(`Shot generation requested for asset ${assetId}.\n`);
printShots(assetId, shots);
}
} catch (error) {
await handleCommandError(error, 'assets', 'shots generate', options);
}
});
Loading
Loading