此库提供了从服务端 TypeScript 或 JavaScript 访问 Mux REST API 的便捷方式。
[!NOTE] 在 2024 年 2 月,此 SDK 更新到了版本 8.0。有关升级到 8.x 的信息,请参阅 UPGRADE_8.x.md
REST API 文档可以在 docs.mux.com 找到。此库的完整 API 可以在 api.md 中找到。
npm install @mux/mux-node
此库的完整 API 可以在 api.md 中找到。
import Mux from '@mux/mux-node';
const client = new Mux({
tokenId: process.env['MUX_TOKEN_ID'], // 这是默认值,可以省略
tokenSecret: process.env['MUX_TOKEN_SECRET'], // 这是默认值,可以省略
});
const asset = await client.video.assets.create({
inputs: [{ url: 'https://storage.googleapis.com/muxdemofiles/mux-video-intro.mp4' }],
playback_policies: ['public'],
});
console.log(asset.id);
此库包括所有请求参数和响应字段的 TypeScript 定义。您可以像这样导入并使用它们:
import Mux from '@mux/mux-node';
const client = new Mux({
tokenId: process.env['MUX_TOKEN_ID'], // 这是默认值,可以省略
tokenSecret: process.env['MUX_TOKEN_SECRET'], // 这是默认值,可以省略
});
const params: Mux.Video.AssetCreateParams = {
inputs: [{ url: 'https://storage.googleapis.com/muxdemofiles/mux-video-intro.mp4' }],
playback_policies: ['public'],
};
const asset: Mux.Video.Asset = await client.video.assets.create(params);
每个方法、请求参数和响应字段的文档都可以在 docstrings 中找到,并且会在大多数现代编辑器中悬停时显示。
您可以使用任何兼容 JWT 的库,但我们已在此 SDK 中包含了一些轻量级的帮助程序,以便更容易地开始使用。
// 假设您已经在环境变量中指定了签名密钥:
// 签名令牌 ID:process.env.MUX_SIGNING_KEY
// 签名令牌密钥:process.env.MUX_PRIVATE_KEY
// 最简单的请求,默认类型为视频,有效期为 7 天。
const token = mux.jwt.signPlaybackId('some-playback-id');
// https://stream.mux.com/some-playback-id.m3u8?token=${token}
// 如果要签名缩略图
const thumbParams = { time: 14, width: 100 };
const thumbToken = mux.jwt.signPlaybackId('some-playback-id', {
type: 'thumbnail',
params: thumbParams,
});
// https://image.mux.com/some-playback-id/thumbnail.jpg?token=${token}
// 如果要签名 GIF
const gifToken = mux.jwt.signPlaybackId('some-playback-id', { type: 'gif' });
// https://image.mux.com/some-playback-id/animated.gif?token=${token}
// 这是一个故事板的例子
const storyboardToken = mux.jwt.signPlaybackId('some-playback-id', {
type: 'storyboard',
});
// https://image.mux.com/some-playback-id/storyboard.jpg?token=${token}
// 您还可以使用 `signViewerCounts` 获取一个用于请求 Mux Engagement Counts API 的令牌
// https://docs.mux.com/guides/see-how-many-people-are-watching
const statsToken = mux.jwt.signViewerCounts('some-live-stream-id', {
type: 'live_stream',
});
// https://stats.mux.com/counts?token={statsToken}
在需要多个令牌的情况下,例如使用 Mux Player 时,事情可能会变得相当复杂。例如,
const playbackToken = await mux.jwt.signPlaybackId(id, {
expiration: "1d",
type: "playback"
})
const thumbnailToken = await mux.jwt.signPlaybackId(id, {
expiration: "1d",
type: "thumbnail",
})
const storyboardToken = await mux.jwt.signPlaybackId(id, {
expiration: "1d",
type: "storyboard"
})
const drmToken = await mux.jwt.signPlaybackId(id, {
expiration: "1d",
type: "drm_license"
})
<mux-player
playback-token={playbackToken}
thumbanil-token={thumbnailToken}
storyboard-token={storyboardToken}
drm-token={drmToken}
playbackId={id}
></mux-player>
为了简化这种情况,您可以向 signPlaybackId 提供多种类型以接收多个令牌。这些令牌以 Mux Player 可以作为属性使用的格式提供:
// { "playback-token", "thumbnail-token", "storyboard-token", "drm-token" }
const tokens = await mux.jwt.signPlaybackId(id, {
expiration: "1d",
type: ["playback", "thumbnail", "storyboard", "drm_license"]
})
<mux-player
{...tokens}
playbackId={id}
></mux-player>
如果您想为单个令牌提供参数(例如,如果您希望有缩略图 time),您可以提供 [type, typeParams] 而不是 type:
const tokens = await mux.jwt.signPlaybackId(id, {
expiration: "1d",
type: ["playback", ["thumbnail", { time: 2 }], "storyboard", "drm_license"]
})
要验证给定负载是否由 Mux 发送并解析 Webhook 负载以在您的应用程序中使用,您可以使用 mux.webhooks.unwrap 实用方法。
该方法接受原始 body 字符串和一个头部列表。只要您在实例化库时设置了适当的配置属性中的 webhookSecret,所有 Webhook 将自动进行真实性验证。
以下示例展示了如何使用 Next.js 应用目录 API 路由处理 Webhook:
// app/api/mux/webhooks/route.ts
import { revalidatePath } from 'next/cache';
import { headers } from 'next/headers';
import Mux from '@mux/mux-node';
const mux = new Mux({
webhookSecret: process.env.MUX_WEBHOOK_SECRET,
});
export async function POST(request: Request) {
const headersList = headers();
const body = await request.text();
const event = mux.webhooks.unwrap(body, headersList);
switch (event.type) {
case 'video.live_stream.active':
case 'video.live_stream.idle':
case 'video.live_stream.disabled':
/**
* `event` 现在被理解为以下类型之一:
*
* | Mux.Webhooks.VideoLiveStreamActiveWebhookEvent
* | Mux.Webhooks.VideoLiveStreamIdleWebhookEvent
* | Mux.Webhooks.VideoLiveStreamDisabledWebhookEvent
*/
if (event.data.id === 'MySpecialTVLiveStreamID') {
revalidatePath('/tv');
}
break;
default:
break;
}
return Response.json({ message: 'ok' });
}
验证 Webhook 签名是 可选但建议 的。了解更多,请参阅我们的 Webhook 安全指南
/*
如果头部有效,此函数不会抛出错误也不会返回值。
如果头部无效,此函数会抛出以下错误之一:
- new Error(
"Webhook 密钥必须通过环境变量 MUX_WEBHOOK_SECRET 设置,或在客户端类 Mux({ webhookSecret: '123' }) 中设置,或传递给此函数",
);
- new Error('无法找到 mux-signature 头部');
- new Error(
'Webhook 负载必须作为原始 JSON 字符串传递(不要先解析它)。',
);
- new Error('无法从头部提取时间戳和签名')
- new Error('未找到 v1 签名')
- new Error('未找到与预期负载匹配的签名')
- new Error('Webhook 时间戳过旧')
*/
/*
`body` 是原始请求正文。它应该是一个 JSON 对象的字符串表示形式。
`headers` 是请求头部的值。
`secret` 是为此配置的 Webhook 的签名密钥。您可以在 Webhook 仪表板中找到这个密钥
(请注意,这个密钥不同于您的 API 密钥)
*/
mux.webhooks.verifySignature(body, headers, secret);
请注意,在传递负载(body)时,您需要传递原始未解析的请求正文,而不是解析后的 JSON。这里是一个使用 Express 的示例。
const Mux = require('@mux/mux-node');
const mux = new Mux();
const express = require('express');
const bodyParser = require('body-parser');
/**
* 您需要确保这是外部可访问的。ngrok (https://ngrok.com/) 可以轻松实现这一点。
*/
const webhookSecret = process.env.WEBHOOK_SECRET;
const app = express();
app.post('/webhooks', bodyParser.raw({ type: 'application/json' }), async (req, res) => {
try {
// 如果签名无效,将引发异常
const isValidSignature = mux.webhooks.verifySignature(req.body, req.headers, webhookSecret);
console.log('成功:', isValidSignature);
// 将原始 req.body 转换为 JSON,最初是 Buffer(原始)
const jsonFormattedBody = JSON.parse(req.body);
// await doSomething();
res.json({ received: true });
} catch (err) {
// 出错时返回错误消息
return res.status(400).send(`Webhook 错误: ${err.message}`);
}
});
app.listen(3000, () => {
console.log('示例应用正在监听端口 3000!');
});
当库无法连接到 API,或者 API 返回非成功状态码(即 4xx 或 5xx 响应)时,将抛出 APIError 的子类:
const liveStream = await client.video.liveStreams
.create({ playback_policies: ['public'] })
.catch(async (err) => {
if (err instanceof Mux.APIError) {
console.log(err.status); // 400
console.log(err.name); // BadRequestError
console.log(err.headers); // {server: 'nginx', ...}
} else {
throw err;
}
});
错误代码如下:
| 状态码 | 错误类型 |
|---|---|
| 400 | BadRequestError |
| 401 | AuthenticationError |
| 403 | PermissionDeniedError |
| 404 | NotFoundError |
| 422 | UnprocessableEntityError |
| 429 | RateLimitError |
| >=500 | InternalServerError |
| N/A | APIConnectionError |
某些错误默认情况下会自动重试 2 次,带有短指数退避。连接错误(例如由于网络连接问题)、408 请求超时、409 冲突、429 速率限制以及 >=500 内部错误都会默认重试。
您可以使用 maxRetries 选项来配置或禁用此功能:
// 配置所有请求的默认值:
const client = new Mux({
maxRetries: 0, // 默认值为 2
});
// 或者,按请求配置:
await client.video.assets.retrieve('t02rm...', {
maxRetries: 5,
});
请求默认超时时间为 1 分钟。您可以使用 timeout 选项来配置:
// 配置所有请求的默认值:
const client = new Mux({
timeout: 20 * 1000, // 20 秒(默认值为 1 分钟)
});
// 按请求覆盖:
await client.video.assets.retrieve('t02rm...', {
timeout: 5 * 1000,
});
超时时,将抛出 APIConnectionTimeoutError。
请注意,超时的请求将默认重试两次(参见 #重试)。
Mux API 中的列表方法是分页的。您可以使用 for await … of 语法遍历所有页面上的项目:
async function fetchAllDeliveryReports(params) {
const allDeliveryReports = [];
// 根据需要自动获取更多页面。
for await (const deliveryReport of client.video.deliveryUsage.list()) {
allDeliveryReports.push(deliveryReport);
}
return allDeliveryReports;
}
或者,您可以一次请求一页:
let page = await client.video.deliveryUsage.list();
for (const deliveryReport of page.data) {
console.log(deliveryReport);
}
// 提供了手动分页的便利方法:
while (page.hasNextPage()) {
page = await page.getNextPage();
// ...
}
通过 APIPromise 类型的所有方法返回的 .asResponse() 方法可以访问 fetch() 返回的“原始”Response。
您也可以使用 .withResponse() 方法来获取原始 Response 以及解析的数据。
const client = new Mux();
const response = await client.video.assets
.create({
inputs: [{ url: 'https://storage.googleapis.com/muxdemofiles/mux-video-intro.mp4' }],
playback_policies: ['public'],
})
.asResponse();
console.log(response.headers.get('X-My-Header'));
console.log(response.statusText); // 访问底层 Response 对象
const { data: asset, response: raw } = await client.video.assets
.create({
inputs: [{ url: 'https://storage.googleapis.com/muxdemofiles/mux-video-intro.mp4' }],
playback_policies: ['public'],
})
.withResponse();
console.log(raw.headers.get('X-My-Header'));
console.log(asset.id);
此库为方便访问记录的 API 提供了类型。如果您需要访问未记录的端点、参数或响应属性,仍然可以使用此库。
要向未记录的端点发送请求,您可以使用 client.get、client.post 和其他 HTTP 动词。客户端上的选项,如重试,在发送这些请求时将被尊重。
await client.post('/some/path', {
body: { some_prop: 'foo' },
query: { some_query_arg: 'bar' },
});
要使用未记录的参数发送请求,您可以在未记录的参数上使用 // @ts-expect-error。此库不会在运行时验证请求是否符合类型,因此您发送的任何额外值都将原样发送。
client.foo.create({
foo: 'my_param',
bar: 12,
// @ts-expect-error baz 尚未公开
baz: '未记录的选项',
});
对于使用 GET 动词的请求,任何额外的参数都会在查询中,所有其他请求都会将额外参数放在正文中。
如果您想明确发送额外参数,可以使用 query、body 和 headers 请求选项。
要访问未记录的响应属性,您可以在响应对象上使用 // @ts-expect-error,或将响应对象转换为所需的类型。就像请求参数一样,我们不会验证或剥离来自 API 的响应中的额外属性。
默认情况下,此库在 Node 中使用 node-fetch,并在其他环境中期望全局 fetch 函数。
如果您希望即使在 Node 环境中也使用全局、符合 Web 标准的 fetch 函数(例如,如果您使用 --experimental-fetch 运行 Node 或使用 NextJS,后者使用 undici 进行多填充),请在首次导入 from "Mux" 之前添加以下导入:
// 告诉 TypeScript 和包使用全局 Web fetch 而不是 node-fetch。
// 注意,尽管名称如此,这不会添加任何多填充,而是期望在需要时提供它们。
import '@mux/mux-node/shims/web';
import Mux from '@mux/mux-node';
要执行相反的操作,请添加 import "@mux/mux-node/shims/node"(这确实会导入多填充)。如果您的 Response 类型不正确,这也可能有用(更多详细信息)。
您还可以在实例化客户端时提供自定义 fetch 函数,该函数可用于在每次请求前/后检查或修改 Request 或 Response:
import { fetch } from 'undici'; // 作为一个例子
import Mux from '@mux/mux-node';
const client = new Mux({
fetch: async (url: RequestInfo, init?: RequestInit): Promise<Response> => {
console.log('即将发出请求', url, init);
const response = await fetch(url, init);
console.log('收到响应', response);
return response;
},
});
请注意,如果给定 DEBUG=true 环境变量,此库将自动记录所有请求和响应。这只是为了调试目的,未来可能会更改而无需通知。
默认情况下,此库为所有 http/https 请求使用稳定的代理,以复