返回 MCP 目录
public公开dns本地运行

mux-node-sdk

Mux Node API库是一个用于从服务器端TypeScript或JavaScript方便地访问Mux REST API的库。它提供了完整的API文档、类型定义、错误处理、自动分页、JWT助手、Webhook验证等功能,支持多种运行时环境。

article

README

🚀 Mux Node API 库

本库为从服务器端 TypeScript 或 JavaScript 访问 Mux REST API 提供了便捷途径。

NPM 版本 npm 打包大小

⚠️ 重要提示

2024 年 2 月,此 SDK 更新至 8.0 版本。如需升级到 8.x 版本,请参阅 UPGRADE_8.x.md

REST API 文档可在 docs.mux.com 上找到。本库的完整 API 文档可在 api.md 中查看。

🚀 快速开始

此库为从服务器端 TypeScript 或 JavaScript 访问 Mux REST API 提供了便捷途径。可参考以下的安装和使用示例快速上手。

✨ 主要特性

  • 提供便捷的 REST API 访问方式。
  • 包含 TypeScript 定义,方便类型检查。
  • 提供 JWT 助手函数,简化 JWT 操作。
  • 支持自动重试和超时设置。
  • 支持分页操作。
  • 可访问原始响应数据。
  • 支持自定义请求。
  • 可自定义 fetch 客户端。
  • 支持日志记录和中间件。
  • 可配置 HTTP(S) 代理。

📦 安装指南

使用以下命令安装 Mux Node API 库:

npm install @mux/mux-node

💻 使用示例

基础用法

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);

每个方法、请求参数和响应字段的文档都在文档字符串中提供,并且在大多数现代编辑器中悬停时会显示。

📚 详细文档

JWT 助手 (API 参考)

你可以使用任何与 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 参与计数 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}

一次签署多个 JWT

在需要多个令牌的情况下,例如使用 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"]
})

解析 Webhook 有效负载

为验证给定的有效负载是否由 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 签名是_可选但建议的_。在我们的 Webhook 安全指南 中了解更多信息。

/*
  如果标头有效,此函数不会抛出错误,也不会返回值。
  如果标头无效,此函数将抛出以下错误之一:
    - new Error(
      "The webhook secret must either be set using the env var, MUX_WEBHOOK_SECRET, on the client class, Mux({ webhookSecret: '123' }), or passed to this function",
    );
    - new Error('Could not find a mux-signature header');
    - new Error(
      'Webhook body must be passed as the raw JSON string sent from the server (do not parse it first).',
    );
    - new Error('Unable to extract timestamp and signatures from header')
    - new Error('No v1 signatures found');
    - new Error('No signatures found matching the expected signature for payload.')
    - new Error('Webhook timestamp is too old')
*/

/*
  `body` 是原始请求体。它应该是 JSON 对象的字符串表示形式。
  `headers` 是 request.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('Success:', 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 Error: ${err.message}`);
  }
});

app.listen(3000, () => {
  console.log('Example app listening on port 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();
  // ...
}

高级用法

访问原始响应数据(例如,标头)

fetch() 返回的“原始”Response 可以通过所有方法返回的 APIPromise 类型的 .asResponse() 方法访问。 你还可以使用 .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.getclient.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: 'undocumented option',
});

对于使用 GET 动词的请求,任何额外的参数将在查询中,所有其他请求将在正文中发送额外的参数。 如果你想显式发送额外的参数,可以使用 querybodyheaders 请求选项。

未记录的属性

要访问未记录的响应属性,你可以在响应对象上使用 // @ts-expect-error 访问响应对象,或将响应对象强制转换为所需的类型。与请求参数一样,我们不会验证或剥离 API 响应中的额外属性。

自定义 fetch 客户端

默认情况下,此库在 Node 中使用 node-fetch,并期望在其他环境中有全局 fetch 函数。 如果你希望即使在 Node 环境中也使用符合 Web 标准的全局 fetch 函数(例如,如果你使用 --experimental-fetch 运行 Node 或使用 NextJS 并使用 undici 进行填充),在你第一次从 "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 的错误 TypeScript 类型时,这也很有用(更多详细信息)。

日志记录和中间件

你还可以在实例化客户端时提供自定义的 fetch 函数,该函数可用于在每个请求之前/之后检查或更改 RequestResponse

import { fetch } from 'undici'; // 作为一个示例
import Mux from '@mux/mux-node';

const client = new Mux({
  fetch: async (url: RequestInfo, init?: RequestInit): Promise<Response> => {
    console.log('About to make a request', url, init);
    const response = await fetch(url, init);
    console.log('Got response', response);
    return response;
  },
});

请注意,如果设置了 DEBUG=true 环境变量,此库将自动记录所有请求和响应。 这仅用于调试目的,未来可能会在不通知的情况下更改。

配置 HTTP(S) 代理(例如,用于代理)

默认情况下,此库对所有 http/https 请求使用稳定的代理,以重用 TCP 连接,消除许多 TCP 和 TLS 握手,并为大多数请求节省约 100 毫秒。 如果你想禁用或自定义此行为,例如在代理后使用 API,你可以传递一个 httpAgent,它将用于所有请求(无论是 http 还是 https),例如:

import http from 'http';
import { HttpsProxyAgent } from 'https-proxy-agent';

// 为所有请求配置默认值:
const client = new Mux({
  httpAgent: new HttpsProxyAgent(process.env.PROXY_URL),
});

// 为每个请求覆盖:
await client.video.assets.retrieve('t02rm...', {
  httpAgent: new http.Agent({ keepAlive: false }),
});

🔧 技术细节

语义版本控制

此包通常遵循 SemVer 约定,但某些向后不兼容的更改可能会作为次要版本发布:

  1. 仅影响静态类型,不破坏运行时行为的更改。
  2. 对库内部的更改,这些更改在技术上是公开的,但不打算或未记录供外部使用。(如果你依赖此类内部,请在 GitHub 上打开一个问题告知我们。)
  3. 我们预计在实践中不会影响绝大多数用户的更改。 我们非常重视向后兼容性,并努力确保你能够依赖平滑的升级体验。 我们渴望得到你的反馈;请打开一个 问题 提出问题、报告错误或提供建议。

要求

支持 TypeScript >= 4.5。 支持以下运行时环境:

  • 网页浏览器(最新版本的 Chrome、Firefox、Safari、Edge 等)
  • Node.js 18 LTS 或更高版本(非 EOL 版本)。
  • Deno v1.28.0 或更高版本。
  • Bun 1.0 或更高版本。
  • Cloudflare Workers。
  • Vercel Edge Runtime。
  • Jest 28 或更高版本,使用 "node" 环境(目前不支持 "jsdom")。
  • Nitro v2.6 或更高版本。 请注意,目前不支持 React Native。 如果你对其他运行时环境感兴趣,请在 GitHub 上打开或投票支持一个问题。

🤝 贡献

请参阅 贡献文档

help

运行方式说明

cloud

托管运行

托管运行通常表示这个 MCP Server 由服务方环境承载,用户一般按页面提供的连接方式或授权流程接入,不需要在本地长期启动一个 MCP 进程

  1. 打开服务方连接页
  2. 完成授权或复制端点
  3. 在 MCP 客户端中连接
terminal

本地运行 / 其它方式

本地运行通常需要用户在自己的电脑或服务器上安装依赖,把 server_config 复制到 MCP 客户端,并按 env_schema 补齐环境变量、密钥或其它配置

  1. 复制 server_config
  2. 安装所需依赖
  3. 补齐环境变量后重启客户端