@eogee-tech/eosearch 0.0.0-stage → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,119 @@
1
+ ---
2
+ name: eosearch-video
3
+ description: EoSearch 视频搜索与下载:先用 eosearch_search 找视频源,再在用户本机用浏览器自动化 MCP 拦截视频流、ffmpeg 合成 mp4(点对点直连平台 CDN,不经服务端)。当用户要求"搜视频并下载""下载视频""抓取视频""把视频拿下来""保存这个视频",或从 EoSearch 结果中带 isVideoPage/videoPlatform 标记的链接要求下载时使用。
4
+ argument-hint: "[视频主题或页面URL] [输出文件名]"
5
+ ---
6
+
7
+ # EoSearch 视频搜索与下载(eosearch-video)
8
+
9
+ 分工明确的两段式:**EoSearch 负责"发现"**(搜到视频页链接),**用户本机负责"下载"**(浏览器自动化 MCP 拦截流地址 + ffmpeg 合成)。下载是点对点直连:视频字节从平台 CDN 直接进用户本机,不经过 EoSearch 服务器——不占服务带宽、不计费,带签名时效的流地址只在用户浏览器里出现,服务端看不到也存不到。
10
+
11
+ ## 第 0 步:环境检查(会话内首次用到时执行一次,结果记住)
12
+
13
+ **ffmpeg 检查**:
14
+
15
+ ```bash
16
+ ffmpeg -version 2>/dev/null | head -1 || /c/ffmpeg/bin/ffmpeg.exe -version 2>/dev/null | head -1
17
+ ```
18
+
19
+ 有输出即可用(grab.sh 会自动探测路径,无需额外配置);无输出视为缺失。
20
+
21
+ **Playwright MCP 检查**:查看本会话工具列表,`browser_navigate` 与 `browser_network_requests` 存在即可。
22
+
23
+ **四种组合的走法(任一缺失立即降级,不硬跑)**:
24
+
25
+ | ffmpeg | Playwright MCP | 走法 |
26
+ |--------|----------------|------|
27
+ | 有 | 有 | 全流程 |
28
+ | 无 | 有 | 照常拦截,把直链交付给用户(浏览器打开直链可另存),附下方 ffmpeg 安装指引;装好后只需重跑第 4 步 |
29
+ | 有 | 无 | 降级尝试**一次**:部分平台(网易视频、腾讯新闻)流地址嵌在页面源码里,`eosearch_fetch` 取 HTML 后正则提取直链再 ffmpeg 直下;失败即降级,不恋战;提示按下方指引接入 Playwright MCP 后可全自动 |
30
+ | 无 | 无 | 只交付搜索结果与视频页链接,说明本机缺少自动下载条件,并给出下方 ffmpeg 与 Playwright MCP 的安装指引 |
31
+
32
+ **缺失时的安装指引**(引导用户补齐,装好后从对应步骤继续):
33
+
34
+ - **ffmpeg**:Windows `winget install Gyan.FFmpeg`;macOS `brew install ffmpeg`;Linux `sudo apt install ffmpeg`(或用发行版包管理器)。装完 `ffmpeg -version` 有输出即可,重跑第 4 步。
35
+ - **Playwright MCP**(需 Node.js 18+):客户端 MCP 配置写入 `{"mcpServers":{"playwright":{"command":"npx","args":["@playwright/mcp@latest"]}}}`;需要复用本机 Chrome 的登录态时在 args 末尾追加 `"--browser","chrome"`。说明见 https://github.com/microsoft/playwright-mcp
36
+ - Playwright MCP 装完重启客户端,确认工具列表出现第 2 步表格里的工具,再从第 2 步继续。
37
+
38
+ 降级话术必须"给出路":说清缺什么、怎么装、装好后从哪一步继续。
39
+
40
+ CLI 有新版时会在 stderr 提示;升级后执行 `eosearch skill install --video` 刷新技能文档(每日版本检查由 CLI 负责,本技能不重复检查)。
41
+
42
+ ## 第 1 步:拿到视频页 URL
43
+
44
+ - 用户给了 URL 直接用,跳到第 2 步。
45
+ - **B站定向检索是首选路径**:`eosearch_search` 带 `includeDomains: ["bilibili.com"]`(对应"去B站搜XX"),自动接入 B站站内搜索引擎,返回真实 `/video/` 页直链(描述含 UP主/时长/播放数/发布日期),全部带 `isVideoPage` 标记。
46
+ - 常规视频搜索:视频类查询自动优先 B站垂直引擎;结果里带 `isVideoPage: true` + `videoPlatform` 标记的是已验证下载配方的视频页,**优先挑这些**。百度/搜狗的跳转链接已由服务端解包为真实域名,标记不会失明。
47
+ - **平台边界(服务端定向刻意不做)**:抖音有登录墙、小红书强反爬+签名,二者的站内搜索不接服务端。通用搜索仍能收录部分抖音视频页(取 `douyin.com/video/{id}` 直链);小红书内容只能走客户端浏览器路线(Playwright MCP 打开其站内搜索,需登录)。
48
+ - 搜索结果里没有具体视频页时:把查询换成具体视频标题/主题关键词再搜一次;或用 Playwright MCP 直接打开平台站内搜索页找目标视频(B站搜索无需登录)。
49
+
50
+ ## 第 2 步:浏览器打开视频页并触发播放
51
+
52
+ 使用 Playwright MCP 的对应工具:
53
+
54
+ | 能力 | 工具 |
55
+ |------|------|
56
+ | 打开页面 | `browser_navigate` |
57
+ | 找播放按钮并点击 | `browser_snapshot` + `browser_click` |
58
+ | 等待起流 | `browser_wait_for` |
59
+ | 列网络请求 | `browser_network_requests`(filter 填 URL 正则) |
60
+ | 读接口响应体 | `browser_network_request`(`part: "response-body"`) |
61
+ | 页面内取值 | `browser_evaluate` |
62
+
63
+ 视频是点击后才加载的,先 snapshot 找到播放按钮 click 触发播放再进第 3 步。
64
+
65
+ ## 第 3 步:拦截视频流地址
66
+
67
+ 在网络请求列表里找:
68
+
69
+ - `.m3u8` 结尾的地址(首选,HLS 切片流索引)
70
+ - `.mp4` 直链(次选)
71
+ - 找不到时看视频信息 API 的 JSON 响应(如腾讯 `i.news.qq.com/getWebVideo?id=...`),读响应体提取视频地址
72
+
73
+ `video.src` 是 `blob:` 开头说明走 MSE 流式播放,DOM 里拿不到直链:改从网络请求里搜媒体域名(`douyinvod.com`/`upos` 等),或读视频信息 API 响应体。
74
+
75
+ ## 第 4 步:ffmpeg 下载合成
76
+
77
+ ```bash
78
+ bash <本技能目录>/helpers/grab.sh "<流地址>" 输出.mp4
79
+ ```
80
+
81
+ 手写命令时的硬规则(grab.sh 已内置处理):
82
+
83
+ - **URL 必须加引号**,内含 `&` 会被 shell 截断;
84
+ - 403 时**来源头与 UA 常缺一不可**(B站实测只带 Referer 仍 403):`-headers "Referer: https://<视频页域名>/" -user_agent "Mozilla/5.0 ... Chrome/131 ..."`;
85
+ - **`-headers`/`-user_agent` 只作用于紧随其后的那一个 `-i`**,音视频分离流(DASH)多输入时每组选项各带一份,否则第二个输入 403;合并用 `-c copy -bsf:a aac_adtstoasc`。
86
+
87
+ ## 第 5 步:验证产物
88
+
89
+ ```bash
90
+ ffprobe -v error -show_entries format=duration,size -of default=nw=1 输出.mp4
91
+ ```
92
+
93
+ 时长与页面标注一致即成功;不一致(如明显偏短)多为切片中途断流,重下一次。
94
+
95
+ ## 变体与排障
96
+
97
+ | 情况 | 处理 |
98
+ |------|------|
99
+ | 流地址带 token / 签名 | **有时效,拦截到后立刻下载**;过期就刷新页面重新拦截 |
100
+ | 多个清晰度 | URL 路径含 `SD`/`HD` 字样选高清;master m3u8 默认取第一个子流,需指定时把子流 URL 单独喂给 ffmpeg |
101
+ | 音视频分离流(DASH,B站) | grab.sh `--audio <音频流>` 一次合并 |
102
+ | 下载报 403 | 见第 4 步硬规则:Referer + UA 齐上 |
103
+ | 中途断流 | 去掉 `-loglevel error` 看卡在哪个切片;重试一次通常可过(切片 CDN 抖动) |
104
+ | m3u8 在页面里却拦不到 | 视频未开始播放:先触发播放,或页面内取值在 DOM 里搜 `m3u8` |
105
+ | 无头浏览器突然弹登录墙/验证 | 平台指纹检测:改有头模式(`--headless=false`)或指定真 Chrome(`--browser chrome`)重试 |
106
+ | 每次都要重新登录 | Playwright MCP 用了隔离模式(`--isolated`):改用持久化 profile,登录一次目标平台长期有效 |
107
+
108
+ ## 已支持平台(2026-10 实测)
109
+
110
+ | 平台 | 页面 URL 模式 | 抓取要点 |
111
+ |------|--------------|---------|
112
+ | B站 | `www.bilibili.com/video/BV*` | DASH 分离流:`*-1-100023.m4s` 视频、`*-1-30216.m4s` 音频,grab.sh `--audio` 合并;**必须同时带 Referer + 浏览器 UA**,缺 UA 即 403;登录前清晰度受限 |
113
+ | 抖音 | `www.douyin.com/video/{id}` | 未登录可播;页面内取值读 `video.currentSrc` 直接得签名 mp4(单文件含音视频);`src` 显示 `blob:` 时改从网络请求搜 `douyinvod.com`;须带 Referer + UA |
114
+ | 腾讯视频 | `v.qq.com/x/cover/*.html` | 播放器走 MSE(`blob:` src),网络请求里直接有 m3u8(`ltshsy.gtimg.com`,与腾讯新闻同体系);带 Referer `https://v.qq.com/` + UA;花絮/预告类短视频无需登录 |
115
+ | 腾讯新闻 | `news.qq.com/rain/a/*` | m3u8 在 `ltshsy.gtimg.com`,带 token,拦截后即时下载 |
116
+ | 网易视频 | `163.com/v/video/*.html` | m3u8 在 `flv3.bn.netease.com`,长签名 URL,无需额外请求头 |
117
+ | YouTube | `youtube.com/watch?v=` / `youtu.be/` | 已识别标记,但下载依赖用户网络环境,不在保证清单内 |
118
+
119
+ 清单外的平台不保证可用:照标准流程试一遍,通了就补进本表。
@@ -0,0 +1,58 @@
1
+ #!/usr/bin/env bash
2
+ # grab.sh — 从视频流地址(m3u8/mp4)下载合成本地 mp4
3
+ # 用法:
4
+ # bash grab.sh <流地址> <输出.mp4> [选项]
5
+ # 选项:
6
+ # --audio <音频流地址> 音视频分离时同时合并
7
+ # --referer <URL> 403 时加来源头
8
+ # --ua <UA字符串> 自定义 User-Agent
9
+ # --quality <子流URL> master m3u8 里选定清晰度的子流(等价于直接把子流当第1参数)
10
+
11
+ set -uo pipefail
12
+
13
+ # ffmpeg 路径探测:先 PATH,再常见安装位置;找不到时给出安装指引
14
+ FFMPEG="$(command -v ffmpeg 2>/dev/null || true)"
15
+ [ -z "$FFMPEG" ] && [ -x /c/ffmpeg/bin/ffmpeg.exe ] && FFMPEG="/c/ffmpeg/bin/ffmpeg.exe"
16
+ [ -z "$FFMPEG" ] && { echo "错误: 未找到 ffmpeg。安装: winget install Gyan.FFmpeg(或从 ffmpeg.org 下载解压后加入 PATH),装好后只需重跑本脚本" >&2; exit 1; }
17
+ FFPROBE="$(command -v ffprobe 2>/dev/null || true)"
18
+ [ -z "$FFPROBE" ] && FFPROBE="$(dirname "$FFMPEG")/ffprobe"
19
+
20
+ usage() { grep '^#' "$0" | sed 's/^# \{0,1\}//'; }
21
+
22
+ [ $# -lt 2 ] && { usage; exit 1; }
23
+
24
+ URL="$1"; OUT="$2"; shift 2
25
+ AUDIO=""; REF=""; UA=""; EXTRA=()
26
+
27
+ while [ $# -gt 0 ]; do
28
+ case "$1" in
29
+ --audio) AUDIO="$2"; shift 2 ;;
30
+ --referer) REF="$2"; shift 2 ;;
31
+ --ua) UA="$2"; shift 2 ;;
32
+ --quality) URL="$2"; shift 2 ;;
33
+ *) echo "未知参数: $1" >&2; exit 1 ;;
34
+ esac
35
+ done
36
+
37
+ # 流地址含 & ? 等字符,必须整体作为单个参数传入,此处不再拆分
38
+ [ -n "$REF" ] && EXTRA+=(-headers "Referer: $REF")
39
+ [ -n "$UA" ] && EXTRA+=(-user_agent "$UA")
40
+
41
+ echo "[grab] 输入: $URL"
42
+ [ -n "$AUDIO" ] && echo "[grab] 音频流: $AUDIO"
43
+ echo "[grab] 输出: $OUT"
44
+
45
+ # -headers/-user_agent 只作用于紧随其后的那一个 -i,多输入时每组选项必须各带一份
46
+ if [ -n "$AUDIO" ]; then
47
+ "$FFMPEG" -y -loglevel error "${EXTRA[@]}" -i "$URL" "${EXTRA[@]}" -i "$AUDIO" \
48
+ -c copy -bsf:a aac_adtstoasc "$OUT"
49
+ else
50
+ "$FFMPEG" -y -loglevel error "${EXTRA[@]}" -i "$URL" \
51
+ -c copy -bsf:a aac_adtstoasc "$OUT"
52
+ fi
53
+ RC=$?
54
+
55
+ [ $RC -ne 0 ] && { echo "[grab] ffmpeg 退出码 $RC(token 过期就刷新页面重新拦截;403 就补 Referer+UA)" >&2; exit $RC; }
56
+
57
+ echo "[grab] 校验产物:"
58
+ "$FFPROBE" -v error -show_entries format=duration,size -of default=nw=1 "$OUT"