@chunkify/cli
v0.9.0
Published
Chunkify CLI for video transcoding and local webhook forwarding
Downloads
127
Readme
The Chunkify CLI brings super-fast video transcoding to your terminal. With a single command, you can upload local files, transcode videos using Chunkify's parallel technology, and download the processed files to your local disk.
For local development, the Chunkify CLI provides a convenient command to forward webhook notifications to your local application URL.
Useful links:
Table of Contents
- Prerequisites
- Installation
- Authentication
- Quick Start with Chunkify
- Transcoding Parameters
- JSON Output
- CLI Profiles
- Direct API commands
- Chunkify API Integration
Prerequisites
You need to have a Chunkify account to use the CLI. If you don't have one, you can sign up for a free trial at chunkify.dev.
Installation
npm and npx
With Node.js 22 or later, run the CLI without a global install:
npx @chunkify/cli@latest --helpOr install the chunkify command globally:
npm install -g @chunkify/cli
chunkify versionThe npm package supports macOS and Linux on x64 and ARM64, and Windows on x64. It includes the native binaries, so installation does not need Go, curl, or npm install scripts.
Use npx @chunkify/cli@latest in place of chunkify in the examples below. To pin a version, use npx @chunkify/cli@<version> with a version published to npm.
Update a global install with npm install -g @chunkify/cli@latest. For a project dependency, use npm install --save-dev @chunkify/cli and run it with npx chunkify.
Shell installer
On macOS and Linux, install the latest version without Node.js:
curl -fsSL https://cli.chunkify.sh | bashAuthentication
After the installation, the first step is to set your project token:
chunkify config token <sk_project_token>[!TIP] You will find the project token in your project settings page under the Project access token section. It's best to create a new token for the CLI.
Another way to authenticate is to set the environment variable CHUNKIFY_TOKEN:
export CHUNKIFY_TOKEN=sk_project_tokenIf you have multiple projects that you want to use with the CLI, simply use the --profile flag to use a different project token. See CLI Profiles for more details.
Quick Start with Chunkify
You can use the Chunkify CLI to transcode a local video, an HTTP URL, an existing source ID, or an object in connected storage using store://.
Transcode a Video
chunkify -i video.mp4 -o video_1080p.mp4 -f mp4_h264 -s 1920x1080 --crf 21It will upload the video to Chunkify, transcode it to MP4 H.264, and download it to your local disk.
For local files, the CLI creates an upload session, transfers the file, and calls the completion endpoint before looking up the source. Temporary completion failures are retried without uploading the file again. Both requests must finish before the session expires. See the video upload guide.
Save a storage connection for the CLI to use for both uploads and job outputs:
chunkify config storage-id stor_aws_example
chunkify -i video.mp4 -o output.mp4The configured ID takes precedence over both --upload-storage-id and --output-storage-id. For external storage, the CLI generates chunkify-cli/sources/<execution-id>/<input-filename> for uploads and chunkify-cli/jobs/<execution-id>/<output-filename> for outputs. When no local output filename is supplied, it uses output with the format's extension. Explicit --upload-storage-path and --output-storage-path values are preserved. Chunkify-managed storage, identified by stor_chunkify_*, uses API-generated paths and rejects explicit path flags for the corresponding operation.
--storage-path remains a deprecated alias for --output-storage-path. Existing commands still work and print a deprecation warning.
Without a configured storage ID, the CLI sends the IDs and paths supplied on the command line. Omitted IDs use the project's default storage. If that storage is external, you must supply --upload-storage-path for a local upload and --output-storage-path for job outputs. Upload paths are exact bucket keys; output paths are relative to the storage connection's base_prefix.
Selecting upload storage through config or flags forces a new upload, even if the video was uploaded before. Generated paths use an execution ID to avoid collisions. An explicit path can overwrite an existing object.
By default, the number of transcoders and their type will be selected automatically according to the input and output specifications.
To define them yourself, use --transcoders and --vcpu like this:
chunkify -i video.mp4 \
-o video_720p.mp4 \
-f mp4_h264 \
-s 1280x720 \
--crf 24 \
--transcoders 10 \
--vcpu 8[!TIP] When transcoding the same local video multiple times, we use the source already created on Chunkify so you won't need to upload the video more than once.
You can also transcode a video that is publicly available via HTTP:
chunkify -i https://cdn/video.mp4 -o video_1080p.mp4 -f mp4_h264 -s 1920x1080 --crf 21If a video has already been uploaded to Chunkify, you can simply use the source ID as the input:
chunkify -i src_33aoGbF6fyY49qUVebIeNaxZJ34 \
-o video_av1_1080p.mp4 \
-f mp4_av1 \
-s 1920x1080 \
--crf 34 \
--preset 7[!TIP] If
--formatis omitted but--outputis set, we will match the file extension to the appropriate format:
.mp4→mp4_h264.webm→webm_vp9.m3u8→hls_h264.jpg→jpg
Sometimes, it's better to know what the input specifications are before transcoding. Use --input without setting --format, and it will only upload or make available the source video:
chunkify -i chunkify-animation-logo.mp4
██ ▗▄▄▖▗▖ ▗▖▗▖ ▗▖▗▖ ▗▖▗▖ ▗▖▗▄▄▄▖▗▄▄▄▖▗▖ ▗▖
██ ▐▌ ▐▌▄▐▌▐▌ ▐▌▐▛▚▖▐▌▐▌▗▞▘ █ ▐▌▗▖ ▝▚▞▘
██ ▝▚▄▄▖▐▌ ▐▌▝▚▄▞▘▐▌ ▐▌▐▌ ▐▌▗▄█▄▖▐▌ ▐▌
Chunkify CLI version: dev
https://chunkify.dev
────────────────────────────────────────────────
▮ Source: chunkify-animation-logo.mp4
Duration: 00:03 Size: 61KB Video: h264, 400x400, 149KB/s, 24.00fps
────────────────────────────────────────────────
Source ID: src_33dLly8jh7bQxVJ5L9LeMG3FAVcNow you can perfectly adapt your transcoding settings to your needs with a second command by either setting --input to the source ID or the same local file (if uploaded from disk).
Read from connected storage
Use store:// to read an existing object directly from external storage. This creates a source without uploading or copying the file:
chunkify -i store://videos/input.mp4 --source-storage-id stor_aws_exampleTo transcode and download the result using a saved storage connection:
chunkify config storage-id stor_aws_example
chunkify -i store://videos/input.mp4 -o output.mp4For source inputs, storage selection uses --source-storage-id first, then config storage-id, then the project's default storage. An explicit source storage ID takes precedence over config because it identifies the bucket containing the existing object. Output storage continues to follow the output rules above.
Everything after store:// is the exact object key, including any leading slash. The CLI does not add base_prefix, decode URL escapes, or normalize the path. Quote the input if the key contains spaces or shell characters, for example -i 'store://videos/my clip.mp4'. Keys must be 1 to 1024 UTF-8 bytes.
The source storage must be external; stor_chunkify_* cannot be used for this input mode. The object must remain available while Chunkify processes it. --source-storage-id is only valid with store://, and upload storage flags cannot be combined with store://.
Per-title encoding
Use --per-title to let Chunkify select video quality and bitrate settings for your source and output resolution. It supports MP4, WebM, and HLS video formats and is disabled by default.
To encode a 1080p H.264 video with per-title optimization:
chunkify -i video.mp4 \
-o video_1080p.mp4 \
-f mp4_h264 \
-s 1920x1080 \
--per-titleFor an HLS rendition, you can let Chunkify choose the video bitrate while setting the audio bitrate yourself:
chunkify -i video.mp4 \
-o video_1080p.m3u8 \
-f hls_h264 \
-s 1920x1080 \
-g 120 \
--x264keyint 120 \
--ab 128k \
--per-titleDo not combine --per-title with --crf, --vb, --maxrate, or --bufsize. HLS outputs do not require a manual bitrate when --per-title is enabled. JPG output does not support per-title encoding.
HLS Packaging
Chunkify supports 3 HLS formats: hls_h264, hls_h265, and hls_av1.
[!WARNING] Keyframes must be aligned for all renditions, so you must use the same values for
--gop,--x264keyint(H.264), and--x265keyint(H.265). Forhls/av1, only--gopis necessary.
chunkify -i video.mp4 \
-o video_540p.m3u8 \
-f hls_h264 \
-s 540x0 \
-g 120 \
--x264keyint 120 \
--vb 800k \
--ab 128kOnce the video is transcoded, the CLI will return a summary including the HLS Manifest ID, which we will use for the next command:
chunkify -i video.mp4 \
-o video_720p.m3u8 \
-f hls_h264 \
-s 720x0 \
-g 120 \
--x264keyint 120 \
--vb 1200k \
--ab 128k \
--hls-manifest-id hls_33atK0NkjF3lz6qUNi3GLwYdi0m[!NOTE] The video bitrate and/or audio bitrate are mandatory for HLS output unless
--per-titleis enabled.
Now we have 2 renditions that belong to the same manifest:
manifest.m3u8
video_540p.mp4
video_540p.m3u8
video_720p.mp4
video_720p.m3u8Generate Thumbnails
To generate thumbnails every 10 seconds:
chunkify -i video.mp4 -o thumbnails.jpg -f jpg -s 320x0 --interval 10If many thumbnails are required, it's recommended to generate a sprite image instead of multiple individual images. A sprite image is a single image containing many thumbnails arranged in a grid, which is more efficient when there are hundreds of them to download for displaying a preview.
chunkify -i video.mp4 -o sprite.jpg -f jpg -s 160x0 --interval 5 --sprite[!NOTE] For all JPG outputs, an
images.vttfile is generated, which can be loaded by an HTML5 player to display a mini preview when hovering over the player progress bar
The VTT filename is always images.vtt. Here is how it looks:
WEBVTT
00:00:00.000 --> 00:00:05.000
sprite-00000.jpg#xywh=0,0,160,160
00:00:05.000 --> 00:00:10.000
sprite-00000.jpg#xywh=160,0,160,160
00:00:10.000 --> 00:00:15.000
sprite-00000.jpg#xywh=320,0,160,160Transcoding Parameters
| Flag | Type | Description |
|------|------|-------------|
| -i, --input | string | Local file, HTTP URL, source ID (src_*), or store://object-key |
| --source-storage-id | string | External storage for store:// input; takes precedence over config storage-id |
| -o, --output | string | Output file path |
| -f, --format | string | mp4_h264, mp4_h265, mp4_av1, webm_vp9, hls_h264, hls_h265, hls_av1, jpg |
| --transcoders | int | Number of transcoders to use |
| --vcpu | int | vCPU per transcoder (4, 8, or 16) |
| --upload-storage-id | string | Storage for local uploads; overridden by config storage-id |
| --output-storage-id | string | Storage for job outputs; overridden by config storage-id |
| --upload-storage-path | string | Exact upload object key; generated for external storage when config storage-id is set |
| --output-storage-path | string | Output path relative to base_prefix; generated for external storage when config storage-id is set |
Video Settings
| Flag | Type | Description | Value |
|------|------|-------------|-------|
| -s, --resolution | string | Set resolution wxh | 0-8192x0-8192 |
| -r, --framerate | float | Set frame rate | 15-120 |
| -g, --gop | int | Set group of pictures size | 1-300 |
| --vb | int | Set video bitrate in bits per second | 100000-50000000. You can also use units like 2000K or 2M |
| --maxrate | string | Set maximum bitrate in bits per second | 100000-50000000. You can also use units like 2000K or 2M |
| --bufsize | string | Set buffer size in bits | 100000-50000000. You can also use units like 2000K or 2M |
| --pixfmt | string | Set pixel format | yuv410p, yuv411p, yuv420p, yuv422p, yuv440p, yuv444p, yuvJ411p, yuvJ420p, yuvJ422p, yuvJ440p, yuvJ444p, yuv420p10le, yuv422p10le, yuv440p10le, yuv444p10le, yuv420p12le, yuv422p12le, yuv440p12le, yuv444p12le, yuv420p10be, yuv422p10be, yuv440p10be, yuv444p10be, yuv420p12be, yuv422p12be, yuv444p12be |
| --vn | bool | Disable video |
| --per-title | bool | Automatically select video rate-control settings for each source | Disabled by default |
See Per-title encoding for examples and compatible settings.
Audio Settings
| Flag | Type | Description | Value |
|------|------|-------------|-------|
| --ab | int | Set audio bitrate in bits per second | 32000-512000. You can also use units like 128K |
| --channels | int | Set number of audio channels | 1, 2, 5, 7 |
| --an | bool | Disable audio |
H.264/H.265/AV1 Settings
| Flag | Type | Description | Value |
|------|------|-------------|-------|
| --crf | int | Set constant rate factor | H.264/H.265: 16-35, AV1: 16-63, VP9: 15-35 |
| --preset | string | Set encoding preset | H.264/H.265: ultrafast, superfast, veryfast, faster, fast, medium, AV1: 6-13 |
| --profilev | string | Set video profile | H.264: baseline, main, high, high10, high422, high444, H.265/AV1: main, main10, mainstillpicture |
| --level | int | Set encoding level | H.264: 10, 11, 12, 13, 20, 21, 22, 30, 31, 32, 40, 41, 42, 50, 51, H.265: 30, 31, 41, AV1: 30, 31, 41 |
| --x264keyint | int | H.264 - Set x264 keyframe interval | 1-300 |
| --x265keyint | int | H.265 - Set x265 keyframe interval | 1-300 |
VP9 Settings
| Flag | Type | Description | Value |
|------|------|-------------|-------|
| --quality | string | Set VP9 quality | good, best, realtime |
| --cpu-used | string | Set VP9 CPU usage | 0-8 |
HLS Settings
| Flag | Type | Description | Value |
|------|------|-------------|-------|
| --hls-manifest-id | string | Set HLS manifest ID |
| --hls-time | int | Set HLS segment duration in seconds | 1-10 |
| --hls-segment-type | string | Set HLS segment type | mpegts, fmp4 |
| --hls-enc | bool | Enable HLS encryption |
| --hls-enc-key | string | Set HLS encryption key |
| --hls-enc-key-url | string | Set HLS encryption key URL |
| --hls-enc-iv | string | Set HLS encryption IV |
JPG Settings
| Flag | Type | Description | Value |
|------|------|-------------|-------|
| --interval | int | Set frame extraction interval in seconds | 1-60 |
| --sprite | bool | Generate sprite sheet instead of multiple JPG files |
JSON Output
It's possible to output the progress in JSON format by passing the --json flag.
chunkify -i video.mp4 -o video_1080p.mp4 -s 1920x1080 --crf 21 --json{"status":"Queued","progress":0,"fps":0,"speed":"0.0x","out_time":0,"eta":""}
{"status":"Queued","progress":0,"fps":0,"speed":"0.0x","out_time":3,"eta":""}
{"status":"Ingesting","progress":20,"fps":0,"speed":"0.0x","out_time":3,"eta":""}
{"status":"Transcoding","progress":40,"fps":100,"speed":"5x","out_time":3,"eta":""}
{"status":"Transcoding","progress":70,"fps":100,"speed":"5x","out_time":3,"eta":""}
{"status":"Merging","progress":90,"fps":12,"speed":"1.2x","out_time":3,"eta":""}
{"status":"Merging","progress":100,"fps":12,"speed":"1.2x","out_time":3,"eta":""}
{"status":"Downloading","progress":100,"fps":0,"speed":"105MB/s","out_time":0,"eta":"0s"}
{"status":"Completed","progress":0,"fps":0,"speed":"","out_time":0,"eta":""}CLI Profiles
Storage configuration is saved per profile, like the project token:
chunkify config storage-id stor_aws_example --profile testing
chunkify config storage-id --profile testing # Show the saved ID
chunkify -i video.mp4 -o output.mp4 --profile testing
chunkify config storage-id "" --profile testing # Clear the saved IDThis setting selects storage for CLI requests; it does not follow later changes to the project's default storage in the dashboard. Clear it to use the storage ID flags or the API default again.
You may have multiple projects and want to use different project tokens for different tasks, or simply to differentiate between different environments.
The CLI provides a global --profile flag to use different project tokens.
First, let's save a new token for the testing profile:
chunkify config token sk_project_token --profile testingNow you can use this profile with the --profile flag for transcoding:
chunkify -i video.mp4 -o video_1080p.mp4 -s 1920x1080 --crf 21 --profile testing[!NOTE] If no profile given, the CLI will use the default one
Direct API commands
Use chunkify api <resource> <action> to call the Chunkify API. These commands print JSON to standard output by default, with no banner or update notice. The CLI reads the production OpenAPI definition to discover resources and actions, and caches it for one hour. If the docs site is unavailable, the last cached definition is used. Use chunkify api --help to list resources and chunkify api projects --help to list a resource's actions.
Command names follow OpenAPI operationId values: updateProject becomes projects update, and getJobFiles becomes job-files list because its route returns a collection. New action verbs follow the same rule.
chunkify api jobs list --query limit=10 | jq '.data'
chunkify api jobs get job_123
chunkify api projects update my-project --data '{"name":"New name"}'
chunkify api projects update my-project --data '{"storage_id":"stor_aws_example"}'
chunkify api storages create --data @storage.jsonUse --data @- to read a JSON body from standard input. Repeat --query key=value for multiple query parameters. Path IDs follow the action name, as shown by each command's --help output. The CLI prints the API's JSON response unchanged. If the API returns no body, it prints {}. Non-JSON responses, such as an asset preview, are printed as {"encoding":"base64","data":"..."}. Errors are JSON on standard error and cause a nonzero exit status.
Project resources use the project token configured with chunkify config token or CHUNKIFY_TOKEN. Projects and access tokens use a team token:
chunkify config team-token <sk_team_token>
# Or set CHUNKIFY_TEAM_TOKEN in the environment.
chunkify api projects listBoth tokens can be saved under the same --profile. Team commands require only a team token; project commands require only a project token. The CLI stores configured tokens in the system keyring and masks them when showing config values.
The request URL comes from chunkify config endpoint for the selected profile, or CHUNKIFY_ENDPOINT when set. The default is https://api.chunkify.dev/v1. The URL inside the OpenAPI document does not change where requests go.
To use another OpenAPI definition for command discovery, save its URL in the same profile:
chunkify config endpoint https://staging-api.example.com/v1 --profile staging
chunkify config openapi-url https://staging-api.example.com/openapi.json --profile staging
chunkify api projects list --profile stagingCHUNKIFY_OPENAPI_URL overrides the saved OpenAPI URL. Without either setting, the CLI uses the production definition. Clear a saved URL with chunkify config openapi-url "" --profile staging.
Chunkify API Integration
Receiving Webhook Notifications Locally
When integrating Chunkify into your app, you must rely on webhooks to receive events when a job is completed or when an upload is created. We have added the listen command to forward webhooks to your local server URL, which is normally not available publicly.
[!NOTE] First, you need to retrieve your webhook secret in your project settings page under the Webhooks section.
Start forwarding webhooks to your local server:
chunkify listen \
--forward-to http://localhost:3000/webhooks/chunkify \
--webhook-secret <secret-key>
██ ▗▄▄▖▗▖ ▗▖▗▖ ▗▖▗▖ ▗▖▗▖ ▗▖▗▄▄▄▖▗▄▄▄▖▗▖ ▗▖
██ ▐▌ ▐▌▄▐▌▐▌ ▐▌▐▛▚▖▐▌▐▌▗▞▘ █ ▐▌▗▖ ▝▚▞▘
██ ▝▚▄▄▖▐▌ ▐▌▝▚▄▞▘▐▌ ▐▌▐▌ ▐▌▗▄█▄▖▐▌ ▐▌
Chunkify CLI version: dev
https://chunkify.dev
────────────────────────────────────────────────
[mac.home] Start forwarding to http://localhost:3000/webhooks/chunkify
Events:
- job.completed
- job.failed
- job.cancelled
- upload.completed
- upload.failed
- upload.expired
────────────────────────────────────────────────
[200 OK] notf_33f3pVlO3782tPF9CkioGK1IKTu job.completed (job_33f3ocg9Vg0o0gDgg5JpCCR3DzX)
[200 OK] notf_33f3tiGWDw78SLHefdswGaL7UpB job.completed (job_33f3siy9JhMrlsIY69q2gHqL3bh)By default, it will forward all events, but you can specify the ones you are interested in:
chunkify listen \
--forward-to http://localhost:3000/webhooks/chunkify \
--webhook-secret <secret-key> \
--events job.completed,job.failed,job.cancelledWhat chunkify listen does under the hood:
- Creates a temporary webhook in your project
- Forwards all notifications to your local server
- Signs requests with the webhook secret key
- Cleans up the webhook when you exit
Development
Prerequisites
- Go 1.x or higher
npm packaging
Node.js 22 or later and Go 1.23 or later are required to build the npm package. There are no npm dependencies to install.
npm test
npm run test:install
npm packnpm pack builds all five platform binaries from this checkout. npm run test:install packs the CLI, installs it into a temporary global prefix with install scripts disabled, and runs it through both the global command and npm exec, the command behind npx. CI runs these checks on macOS, Linux, and Windows.
Release Please updates package.json alongside the Go release version. The release workflow attaches chunkify-cli-<version>.tgz to each GitHub release. Each npm package contains the matching CLI version.
Before the first npm release, a maintainer must have publishing access to the @chunkify npm scope. Download the .tgz from the intended GitHub release, sign in with npm login, and publish that file with npm publish ./chunkify-cli-<version>.tgz --access public.
Then configure an npm trusted publisher in the package settings for GitHub owner chunkifydev, repository cli, and workflow release.yml. Allow direct publishing with npm publish. Set the GitHub repository Actions variable NPM_PUBLISH_ENABLED to true to publish subsequent releases automatically. Publishing uses GitHub's identity token and does not require a stored npm token. Until enabled, the workflow only uploads the npm tarball to the GitHub release.
If npm publishing fails after a GitHub release, rerun the failed npm job. To publish an already-created tarball manually, download it from that release and use the same npm publish command above.
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
MIT
