关于 zahori
zahori 通过真实浏览器操作网页并监视网络请求,解析页面背后的媒体流,供需要获取 HLS、DASH、MP4 或音频流的用户使用。它支持用 JSON 配置可复用的网站配置文件,并拒绝 DRM 内容。
主要功能
- 01浏览器驱动页面并捕获媒体请求
- 02返回流地址及重放请求所需标头
- 03识别 HLS、DASH、MP4 和音频
- 04按语言选择音轨
- 05用 JSON 配置并修复网站配置文件
- 06检测并拒绝 DRM 内容
适用场景
- 01获取网页播放器背后的媒体流地址
- 02为下载工具提供重放所需请求标头
- 03为难处理的网站建立可复用配置
- 04发现网站变更后测试并修复配置
MCP 相关
作为 MCP 服务向 Agent 提供探测网站、测试及保存配置文件等工具;Agent 可据此发现媒体流并学习网站操作流程。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
6收录的工具列表 · 更新于 2 周前
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| url | string | 是 | 包含播放器的页面。 |
| lang | string | 否 | 多语言流的首选音频语言(例如 es)。 |
详细介绍
$ npx zahori get https://example.org/live
https://cdn.example.org/hls/live/master.m3u8
无需为每个网站单独配置,也无需等待提取器。如果浏览器能播放,zahori 就能找到它。
目录
功能
- 开箱即用,适用于任意 URL。 内置的通用流程无需配置即可处理大多数网页。
- 真实浏览器引擎。 Playwright 在后台驱动无头浏览器;自动处理同意横幅、播放按钮和 iframe 播放器,并支持多种语言。
- 包含重放所需的请求头。 从真实请求中捕获 Referer/Origin/UA/Cookie,因此 ffmpeg 等工具不会遇到 403 错误。
- 支持 HLS。 可区分主播放列表和媒体播放列表、直播和 VOD,并按语言为多语言流选择音频版本。
- 可学习。 将难处理的网站转换成小型 JSON 配置文件,保存一次,之后即可始终确定性地使用。
- Agent 原生(MCP)。 编码 Agent 可以探测网站、编写配置文件、测试并保存。
- 自我修复。 当网站发生变化时,
zahori heal会使用自带密钥的模型修复配置文件,并在保存前通过客观预言机验证。 - 从设计上拒绝 DRM。 始终检测并拒绝受保护的内容。
为什么选择 zahori
网络上大量不常见的网站通过嵌入式播放器提供流媒体服务,而没有任何下载器认识这些播放器:地方电视台和广播电台、活动直播页面、小型视频门户。yt-dlp 对它精心维护了提取器的大型平台支持出色。但对于这些不常见的网站,没有现成的提取器;逐个手写也无法扩展。
| yt-dlp | zahori | |
|---|---|---|
| 覆盖范围 | 约 1,800 个精选网站 | 浏览器能打开的任意网页 |
| 方法 | 针对各网站的提取器代码 | 真实浏览器 + 网络嗅探 |
| 新网站 | 等待维护者添加 | 通用流程即可运行,或几分钟内完成学习 |
| 网站发生变化 | 提取器会失效,直到修复补丁发布 | zahori heal 会修复配置文件 |
两者互为补充:用 yt-dlp 下载 YouTube,用 zahori 处理那些没人编写过提取器的网页。
安装
npm install -g zahori # CLI
npm install zahori # library
要求
| Node | ≥ 22 |
| 浏览器 | 运行一次 npx playwright install chromium --only-shell;否则会使用已安装的 Google Chrome |
| ffmpeg | 可选,仅用于 --validate、--download 和发现预言机 |
pnpm 用户: pnpm 默认会阻止依赖项的构建脚本,因此 Playwright 不会自动下载浏览器。运行上面的
playwright install命令一次,或者使用系统 Chrome 作为备用浏览器。
CLI
# Resolve the stream URL behind a page (URL to stdout, everything else to stderr)
zahori get https://tv.example.org/live
# Full result as JSON: url, kind, live/VOD, replay headers, all candidates
zahori get https://tv.example.org/live --json
# Prefer an audio language on multilingual streams
zahori get https://tv.example.eu/live --lang es
# Record it (replays the captured headers; needs ffmpeg)
zahori get https://tv.example.org/live --download session.mp4
zahori get https://tv.example.org/live --audio session.m4a
# Check the stream is real media, not a bumper or geo-block slate
zahori get https://tv.example.org/live --validate
# Watch the browser work
zahori get https://tv.example.org/live --headful
--json 输出示例如下:
$ zahori get https://example.org/watch --json
{
"url": "https://cdn.example.org/hls/master.m3u8",
"kind": "hls",
"headers": {
"referer": "https://example.org/",
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) ... Chrome/150.0.0.0 Safari/537.36"
},
"profileId": "generic",
"candidates": [
"https://cdn.example.org/hls/master.m3u8",
"https://cdn.example.org/hls/720p/index.m3u8",
"https://cdn.example.org/hls/1080p/index.m3u8"
]
}
解析出的 URL 通常带有签名,且有效期很短。zahori 每次运行时都会重新获取流,因此请立即使用该 URL,切勿保存。
库
import { resolve } from 'zahori';
const stream = await resolve('https://tv.example.org/live', {
audioLanguage: 'es', // optional, for multilingual masters
});
stream.url; // the media URL
stream.kind; // 'hls' | 'dash' | 'mp4' | 'audio'
stream.live; // true = live, false = VOD, undefined = unknown
stream.headers; // Referer/Origin/UA/Cookie to replay (many CDNs 403 without them)
将 stream.headers 传给任何用于获取该 URL 的工具。使用 ffmpeg 的示例:
import { download } from 'zahori';
await download(stream, 'session.mp4');
错误采用类型化处理:NoStreamError(未捕获到内容)和 DrmError(检测到受保护内容,已拒绝)。
自带浏览器
对于需要登录的网站,可在你控制的 Playwright 上下文中(例如包含存储状态的上下文)运行 zahori:
import { runProfileOnPage, GENERIC_PROFILE } from 'zahori';
const ctx = await browser.newContext({ storageState: 'auth.json' });
const page = await ctx.newPage();
const stream = await runProfileOnPage(page, GENERIC_PROFILE, url);
配置文件:zahori 如何学习网站
zahori 不内置任何网站知识。大多数页面都能通过内置的通用流程解析。遇到无法解析的页面时,解决方法是创建一个 配置文件:一个描述如何打开该网站的小型 JSON 文件(要点击哪些元素、如何识别媒体请求、要选择哪个候选项)。配置文件是数据,而非代码;引擎会以确定性的方式解释它们,运行时不会使用 AI。
{
"id": "tv-example-org",
"match": ["^https?://([^/]*\\.)?tv\\.example\\.org([/:?#]|$)"],
"steps": [
{ "action": "goto" },
{ "action": "dismissOverlays", "selectors": [] },
{ "action": "click", "selector": ".broadcast-start" }
],
"sniff": { "kinds": ["hls"], "urlPattern": "/live/" }
}
配置文件存放在项目中的 .zahori/profiles/(请将它们提交到版本库,以便在生产环境和团队成员之间共享),或全局存放在 ~/.zahori/profiles/。配置文件保存的是 如何重新获取 流媒体,而不是流媒体 URL,因此签名 URL 或过期 URL 都不会造成问题。
教 zahori 识别网站
使用你的编码 Agent(MCP)
zahori 是一个 MCP 服务:任何支持 MCP 的 Agent 都可以探测网站、提出配置文件、使用预言机进行测试,并在测试通过后保存配置文件。整个过程使用你已有的订阅,无需 API 密钥。服务会在协议握手期间向 Agent 发送工作流说明,因此在所有客户端中的操作方式都相同。
大多数客户端使用标准配置:
{
"mcpServers": {
"zahori": { "command": "npx", "args": ["-y", "zahori", "mcp"] }
}
}
| 客户端 | 设置 |
|---|---|
| Cursor | 将上面的代码块添加到 ~/.cursor/mcp.json(或项目级 .cursor/mcp.json) |
| VS Code / Copilot | 使用 .vscode/mcp.json,并将配置放在 "servers" 键下,而非 "mcpServers" |
| Windsurf | ~/.codeium/windsurf/mcp_config.json |
| Claude Code | claude mcp add zahori -- npx -y zahori mcp |
| Codex CLI | codex mcp add zahori -- npx -y zahori mcp |
| Gemini CLI | gemini mcp add zahori npx -y zahori mcp |
| Claude Desktop | 将上面的代码块添加到 claude_desktop_config.json |
然后提出请求:"找出 https://tricky.example.org 上的流媒体,并保存配置文件"。提供的工具:zahori_get、zahori_probe、zahori_test_profile、zahori_save_profile、zahori_validate、zahori_list_profiles。
无头模式,使用你自己的密钥
对于无人值守的流水线(cron、CI),zahori 可以自行驱动模型。它总是先尝试免费的确定性路径,只有失败时才会消耗令牌:
export ANTHROPIC_API_KEY=... # or OPENAI_API_KEY, or a local Ollama
zahori discover https://tricky.example.org --model anthropic:claude-sonnet-5
zahori heal https://tricky.example.org # when a saved profile stops working
模型规格:anthropic:<model>、openai:<model>、ollama:<model>、compat:<model>(任何兼容 OpenAI 的端点),也可以只设置一次 ZAHORI_MODEL。模型只负责 提出 配置文件;引擎会运行每个提案,并由预言机进行评估,因此绝不会保存未经验证的配置文件。
预言机
“是否有内容下载”是一个不可靠的测试:它可能会把广告片头、背景音乐和地理区域限制提示页误判为有效内容。预言机会综合更可靠的信号,再判断流媒体是否可信。
- 解码探测。 ffmpeg 会解码一个有时长限制的片段并测量音量。
- 清单检查。 正常的播放会话包含许多分段;提示页则只有 4 个。
- 静音容忍。 如果检测到的片段没有声音,会在更靠后的位置再次探测(静音可能是正常的中场休息);仅凭静音绝不会判定流媒体失败。
- 明确报告失败。 对 403/401 响应会报告为标头问题,而不是无法解释的异常。
判定结果为 pass、fail 或 inconclusive;发现流程只会保存通过预言机测试的配置文件。
负责任地使用
zahori 专为公开可访问、未受保护的流打造:这些页面在普通浏览器中可供任何访客播放。它解析浏览器本就能播放的内容,不会入侵任何系统。
zahori 的设计原则是拒绝 DRM。 它会检测清单中的 Widevine、PlayReady 和 FairPlay 标记,并通过明确的 DrmError 拒绝处理。它绝不会尝试绕过加密或访问控制;这是不可逾越的底线,不是可调整的设置。
安全范围。 最明确的适用场景是网站向所有访客提供的开放 HLS/DASH,并将其用于合法目的,例如存档、无障碍访问、转录和研究。在使用前,有几件事值得了解:
- “公开”不等于“公有领域”。 流可以免费观看,并不代表可以自由重复使用;有些网站会对其视听内容单独授权。请查看网站的重复使用条款。
- 没有 DRM 不等于法律上不受保护。 在某些司法管辖区,绕过轻量级令牌或签名机制也可能引发反规避方面的问题(美国《数字千年版权法》第 1201 条)。zahori 通过拒绝 DRM 且绝不绕过访问限制来确保安全。请继续遵守这一原则。
- 在欧盟,文本和数据挖掘例外(DSM 指令第 3 和第 4 条)允许复制合法访问的作品,除非权利人以机器可读的方式选择退出。请尊重退出声明。
您有责任遵守所用网站的服务条款、版权规定和当地法律。如有疑问,请优先选择明确开放的内容,并咨询相关机构。
API
| 导出项 | 功能 |
|---|---|
resolve(url, opts?) | 解析页面背后的流(配置文件或通用流程) |
validate(stream, opts?) | 验证:这是真实媒体吗? |
download(stream, path, opts?) | 使用 ffmpeg 录制,并重放请求头 |
probe(url, opts?) | 结构化页面盘点,用于编写配置文件 |
testProfile(profile, url, opts?) | 运行候选配置文件并对其评分 |
saveProfile(profile, dir) / loadProfiles() | 保存 / 加载配置文件 |
discoverProfile(url, opts?) / healProfile(profile, url, opts?) | 借助模型学习 / 修复 |
runProfile(profile, url, opts?) / runProfileOnPage(page, ...) | 执行配置文件(使用自有浏览器 / 您的浏览器) |
hostPattern(url) / profileIdFromUrl(url) | 编写配置文件的辅助函数 |
软件包附带完整的 TypeScript 类型。
文档
完整指南位于 docs/:
贡献
欢迎提交问题和 PR。有关开发环境设置、测试套件和发布流程,请参阅 CONTRIBUTING.md。
许可证
MIT © Jose Sepulveda
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。