motion-intelligence
v1.0.0
Published
Official Node.js SDK for the Motion Intelligence video processing API
Downloads
132
Maintainers
Readme
motion-intelligence
Official Node.js / TypeScript SDK for the Motion Intelligence video processing API.
Submit videos, track jobs, and retrieve results — with full TypeScript types, automatic retries, and a clean async/await interface.
Installation
npm install motion-intelligence
# or
yarn add motion-intelligence
# or
pnpm add motion-intelligenceRequires Node.js ≥ 18.
Quick Start
import { MotionClient } from "motion-intelligence";
const client = new MotionClient({
apiKey: process.env.MOTION_API_KEY!,
});
// Submit + wait for completion in one call
const job = await client.processVideo({
modelName: "motion-v1",
task: "Remove camera shake",
video: "/absolute/path/to/clip.mp4",
});
console.log(job.outputVideoUrl); // pre-signed URL valid for 1 hourAPI Reference
new MotionClient(options)
| Option | Type | Default | Description |
|----------------|----------|-----------------------------------------|-----------------------------------------------------------|
| apiKey | string | required | Your Motion Intelligence API key. |
| baseUrl | string | "https://api.motionintelligence.ai" | Override the API base URL (useful for staging/local dev). |
| timeoutMs | number | 120_000 (2 min) | Per-request timeout in milliseconds. |
| maxRetries | number | 3 | Number of automatic retries on transient errors. |
| retryDelayMs | number | 500 | Base back-off delay in milliseconds (doubles each retry). |
client.submitJob(options) → Promise<SubmitJobResponse>
Submit a video processing job and receive a jobId immediately. The job runs asynchronously — use getJob or waitForJob to track progress.
const { jobId } = await client.submitJob({
modelName: "motion-v1",
task: "Slow motion 2x",
video: videoBuffer, // Buffer, Uint8Array, Blob, or file path string
filename: "my-clip.mp4", // optional — used for MIME detection
});SubmitJobOptions
| Field | Type | Description |
|-------------|-----------------------------------------------|--------------------------------------------------------------|
| modelName | string | Name of the Motion model ("motion-v1", "motion-pro", …). |
| task | string | Natural-language processing instruction. |
| video | Buffer \| Uint8Array \| Blob \| string | Video to process. String = absolute file path (Node only). |
| filename | string (optional) | Override filename sent in the multipart upload. |
| mimeType | string (optional) | Override MIME type (auto-detected from filename otherwise). |
SubmitJobResponse
{
jobId: string;
status: "pending";
modelName: string;
task: string;
totalJobsSubmitted: number;
message: string;
}client.getJob(jobId) → Promise<Job>
Fetch the latest status of a single job.
const job = await client.getJob("3f7e2a1b-...");
console.log(job.status); // "pending" | "processing" | "completed" | "failed"
console.log(job.outputVideoUrl); // string | nullJob
{
jobId: string;
status: "pending" | "processing" | "completed" | "failed";
modelName: string;
task: string;
outputVideoUrl: string | null; // pre-signed S3 URL (1h), completed only
errorMessage: string | null; // failed only
createdAt: Date;
updatedAt: Date;
}client.listJobs(limit?, offset?) → Promise<JobListResponse>
List all jobs for the authenticated user with optional pagination.
const { jobs, total } = await client.listJobs(10, 0);
jobs.forEach((j) => console.log(j.jobId, j.status));client.waitForJob(jobId, options?) → Promise<Job>
Poll getJob in a loop until the job reaches completed or failed.
const job = await client.waitForJob("3f7e2a1b-...", {
pollIntervalMs: 5_000, // poll every 5 seconds (default: 3s)
timeoutMs: 300_000, // give up after 5 minutes (default: 10 min)
onPoll: (j) => console.log("status:", j.status),
});WaitForJobOptions
| Option | Type | Default | Description |
|-----------------|-----------------------------|------------------|-----------------------------------------------|
| pollIntervalMs| number | 3_000 | Milliseconds between status polls. |
| timeoutMs | number | 600_000 (10min)| Throw MotionTimeoutError after this. |
| onPoll | (job: Job) => void | — | Called on every poll tick (progress logging). |
client.processVideo(submitOptions, waitOptions?) → Promise<Job>
Convenience wrapper: submits the job and waits for completion in one call.
const job = await client.processVideo(
{ modelName: "motion-v1", task: "Colour grade", video: buf },
{ timeoutMs: 120_000 }
);Error Handling
All errors extend MotionError. Use instanceof to handle them specifically:
import {
MotionClient,
MotionAuthError,
MotionJobFailedError,
MotionTimeoutError,
MotionAPIError,
} from "motion-intelligence";
try {
const job = await client.processVideo({ modelName: "motion-v1", task: "…", video: buf });
} catch (err) {
if (err instanceof MotionAuthError) {
console.error("Invalid API key:", err.message);
} else if (err instanceof MotionJobFailedError) {
console.error("Job failed:", err.errorMessage);
} else if (err instanceof MotionTimeoutError) {
console.error("Timed out, check job manually:", err.jobId);
} else if (err instanceof MotionAPIError) {
console.error(`API error ${err.status}:`, err.body);
} else {
throw err;
}
}| Error class | When thrown |
|-------------------------|----------------------------------------------------------|
| MotionAPIError | Base class — any non-2xx API response. |
| MotionAuthError | 401/403 — missing or invalid API key. |
| MotionNotFoundError | 404 — job not found. |
| MotionRateLimitError | 429 — rate limit hit (after retries exhausted). |
| MotionServerError | 5xx — server error (after retries exhausted). |
| MotionTimeoutError | waitForJob exceeded its timeout. |
| MotionJobFailedError | Job status transitioned to failed. |
Video Sources
The video parameter accepts four forms:
// 1. Absolute file path (Node.js only)
video: "/home/user/videos/clip.mp4"
// 2. Buffer
import { readFileSync } from "fs";
video: readFileSync("/home/user/videos/clip.mp4")
// 3. Uint8Array
video: new Uint8Array(bytes)
// 4. Blob (browser or Node ≥ 20)
video: new Blob([bytes], { type: "video/mp4" })Supported MIME types: video/mp4, video/quicktime, video/x-msvideo, video/x-matroska, video/webm, video/mpeg.
Environment Variables
| Variable | Description |
|-----------------------------|-------------------------------------------|
| MOTION_API_KEY | Your Motion Intelligence API key. |
| MOTION_BASE_URL (optional)| Override the default API base URL. |
const client = new MotionClient({
apiKey: process.env.MOTION_API_KEY!,
baseUrl: process.env.MOTION_BASE_URL, // optional override
});Full Example — Express Upload Endpoint
import express from "express";
import multer from "multer";
import { MotionClient, MotionJobFailedError } from "motion-intelligence";
const app = express();
const upload = multer({ storage: multer.memoryStorage() });
const motion = new MotionClient({ apiKey: process.env.MOTION_API_KEY! });
app.post("/process", upload.single("video"), async (req, res) => {
if (!req.file) return res.status(400).json({ error: "No video uploaded" });
try {
const { jobId } = await motion.submitJob({
modelName: req.body.model_name ?? "motion-v1",
task: req.body.task ?? "Remove camera shake",
video: req.file.buffer,
filename: req.file.originalname,
mimeType: req.file.mimetype,
});
res.json({ jobId, message: "Job submitted" });
} catch (err) {
res.status(500).json({ error: String(err) });
}
});
app.get("/process/:jobId", async (req, res) => {
try {
const job = await motion.getJob(req.params.jobId);
res.json(job);
} catch (err) {
res.status(500).json({ error: String(err) });
}
});
app.listen(3000);Building from Source
git clone https://github.com/motion-intelligence/motion-intelligence-node
cd motion-intelligence-node
npm install
npm run buildLicense
MIT © Motion Intelligence
