Metadata-Version: 2.4
Name: pyetool
Version: 0.53.1
Summary: Add your description here
Requires-Python: >=3.13
Requires-Dist: bilibili-api-python>=17.3.0
Requires-Dist: click>=8.3.3
Requires-Dist: google-genai>=2.0.1
Requires-Dist: httpx[socks]>=0.27
Requires-Dist: lzstring>=1.0.4
Requires-Dist: pillow>=11.0
Requires-Dist: playwright>=1.55
Requires-Dist: pydantic-ai-slim[google,openai]>=1.96.1
Requires-Dist: pydantic>=2.13.4
Requires-Dist: python-dotenv>=1.2.2
Requires-Dist: rich>=15.0.0
Requires-Dist: tenacity>=9.0.0
Requires-Dist: toolz>=1.1.0
Requires-Dist: tos>=2.9.0
Requires-Dist: yt-dlp>=2026.3.17
Description-Content-Type: text/markdown

# Pyetool

A personal efficiency enhancing tool.

# TODOs

## support more resource types
- GIF recognizing tool
- Music mood recognizing tool

## Video composition and editing
- Remotion descriptive video clip

## AI generation
- 用 Pydantic AI 封装文生图,接 OpenRouter API

---

# 改进建议清单(来自"B站效率博主采集"实跑)

> 背景:用 pyetool 编排了一次较长的 B 站效率博主数据采集(搜索关键词 → 筛播放 → 抓投稿 → 取≤30min top3 → 下载 → 转录 → 总表)。下面是这次实跑暴露出的、**可加进 pyetool 以减少调用方胶水代码 / 提速 / 提鲁棒性**的改进项,按优先级排列,均附实测证据。勾选框供拍板。

## P0 — 正确性 / 安全(便宜、影响大,建议先做)

- [x] **ASR 返回结构化错误,而非抛原始异常 + 支持熔断**(v0.48)
  - 现状:`seedasrf recognize` 在配额耗尽时抛 `Exception: status=45000292, message=quota exceeded for types: audio_duration_lifetime`,调用方拿到的是一坨堆栈。
  - 证据:实跑中配额悄悄耗尽后,**连续 2320 次转录全部失败**却没有任何"该停了"的信号,一个批次空转 ~3 小时(每次还白下载一份音频)。
  - 建议:返回带类型的错误(`quota_exceeded` / `auth_failed` / `audio_too_long` / `rate_limited`),或提供"连续失败 N 次即中止"的内建熔断,让调用方能快速止损。
  - 落地(v0.48):API 已返回 `Result`(`Err`),不再抛原始异常;新增通用 `monad.Fatal(Err)` 标记"终止类"错误,配额耗尽(`45000292`)/ 订阅未激活(`45000010`)归为 `Fatal`。`cli_util.emit_or_halt` 让所有 `-all` 命令遇 `Fatal` 立即中止整批(非零退出 + 上游收 SIGPIPE 停止白下载),其余错误仍 per-item 继续。细粒度 kind taxonomy 暂缓(当前都不改变控制流,等真有按 kind 分流需求再在 domain 层加)。

- [ ] **`site bili` 识别并显式上报风控(-799 / -352)**
  - 现状:被风控时 `arc/search` 返回 `-799 请求过于频繁`,页面 0 卡 → playwright 等选择器 15s 超时,对外只表现为"空结果/超时"。
  - 证据:连续打多个 uid 时"每个都坏",排查很久才定位是风控而非登录失效/选择器漂移。
  - 建议:把 -799/-352 识别出来,作为 `error: rate_limited` 字段或带类型异常返回。

- [ ] **ASR 暴露稳定的文字稿字段**
  - 现状:转录文本藏在 `result.text`(还有 `utterances`),`--help` 未说明。
  - 证据:只能写探测函数依次试 `text` / `result.text` / `utterances[].text`。
  - 建议:稳定暴露顶层 `text`,或在文档/help 写明路径。

- [ ] **修文档不一致**:`seedasrf --help` 写 "audio ≤ 2h",实测真实上限是 **3h(10800000ms)/ 100MB**。

## P1 — 性能(本次最大的耗时来源)

> 实测耗时大头:**Playwright 抓取 ~187 min**(submissions 103min@均172s/号 + search 84min@均209s/词),而 sleep 仅 19min、退避仅 10min。慢主要慢在浏览器抓取,不在等待。

- [ ] **提供 API 版的 `site bili submissions` / `search`(最高价值,一箭三雕)**
  - 现状:走 Playwright 抓"空间页",要冷启动浏览器 + 翻完该号全部视频的所有页(2287 视频的号要翻几十页)。
  - 收益:用官方 web API(如 `x/space/arc/search`)几百毫秒拿到全量;支持 `--order views` / `--limit N` / `--max-pages`,调用方就不必"抓全量再自己排序取 topN";同时摆脱选择器漂移的脆弱性。**单 172s/号 → 亚秒级**。

- [ ] **复用浏览器 / 登录会话(常驻模式 或 稳定的 Python API)**
  - 现状:每次 CLI 调用都**冷启动一个无头 Chromium**;一次长采集要启动几十上百次。
  - 证据:冷启动是 187min 抓取耗时的重要组成。
  - 建议:支持一个常驻 session 跨多次调用复用浏览器+登录态;或提供**有文档、稳定的 Python API**,让调用方 `import pyetool` 在同一进程内复用会话(见下方"CLI vs 库"说明)。

- [ ] **内建节流 + 退避重试**(min-interval + 抖动 + 遇 -799 自动退避),让调用方不必各自重造 sleep/重试逻辑。

## P1 — ASR 鲁棒性

- [ ] **长音频自动切片转录**:`seedasrf recognize` 对超限(>3h/100MB)音频自动用 ffmpeg 切片 → 分段识别 → 拼接(它本就会转码,切片是自然延伸)。证据:一条 4h 视频被原始 API 直接拒,只能在外面按时长预过滤。
- [ ] **下载/转录前预检**:`vidkit ytdlp download-audio --max-duration / --max-filesize`,超限直接跳过、不下载。证据:一条 4h 视频白下了 113MB 才被 ASR 拒。

## P2 — 便利 / 高层封装

- [ ] **高层 recipe / 命令**:把 `search → 筛播放 → 取 topN → 下载 → 转录` 这条常见链路做成一条 pyetool 命令或配方(目前 skill 里是 jq 拼的示例)。
- [ ] **`--ascii` / ensure_ascii 输出开关**(可选):生产者可选输出纯 ASCII 转义的 JSONL,规避部分终端吞掉 CJK 输出的问题(偏 harness 特定,优先级低)。

## 关于 "CLI vs 直接 import 库"(给 pyetool 的定位建议)

这次默认走了 CLI(子进程),原因是:CLI 是稳定有文档的契约、且**进程隔离**(一次 Playwright 崩溃不会拖垮长编排)。代价是**每次调用冷启动浏览器**,在"大量串行 submissions"场景下损耗明显。

- [ ] **建议 pyetool 同时提供"稳定、有文档的 Python API"**(而不仅是内部会漂移的 `page_obj` 等),这样调用方可按场景二选一:CLI(隔离、简单) vs 库(复用会话、更快)。当前内部 API 文档少、被 skill 标注"会漂移",不敢直接耦合。

## 不建议进 pyetool(属于调用方的"业务/编排",非通用能力)

- 断点续跑的磁盘状态机(disk-as-truth)、UP主去重注册表、关键词队列(append-only)、主题标签过滤、按播放量挑 topN 的研究规则、总表(master.csv)聚合。这些是具体研究流程,留在调用方的胶水里更合适。

---

