# PodNotes
**Repository Path**: richie12/pod-notes
## Basic Information
- **Project Name**: PodNotes
- **Description**: 一个完全本地运行的 PyQt5 桌面工具:把 B 站、播客等音视频链接变成一份可检索、可复习的中文 Markdown 问答笔记。
- **Primary Language**: Python
- **License**: MIT
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 1
- **Forks**: 0
- **Created**: 2026-08-03
- **Last Updated**: 2026-08-04
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# PodNotes — 视频/播客 → 结构化 Markdown 笔记
一个完全本地运行的 PyQt5 桌面工具:把 B 站、播客等音视频链接变成一份可检索、可复习的中文 Markdown 问答笔记。
> 核心理念:**听完了就忘**,于是做出来一边听、一边自动产出结构化笔记。
---
## 目录
- [功能特性](#功能特性)
- [处理流水线](#处理流水线)
- [安装与运行](#安装与运行)
- [界面与基本使用](#界面与基本使用)
- [重点:总结模型的三种用法](#重点总结模型的三种用法)
- [对比速览](#对比速览)
- [用法 A:本地进程 GGUF(零配置首选)](#用法-a本地进程-gguf零配置首选)
- [用法 B:本地服务 vLLM / sglang(高吞吐)](#用法-b本地服务-vllm--sglang高吞吐)
- [用法 C:云端 OpenRouter(不占本机算力)](#用法-c云端-openrouter不占本机算力)
- [如何在 UI 中切换](#如何在-ui-中切换)
- [配置文件示例](#配置文件示例)
- [ASR 语音识别的两种方式](#asr-语音识别的两种方式)
- [输出文件与目录结构](#输出文件与目录结构)
- [常见问题](#常见问题)
- [依赖说明](#依赖说明)
---
## 应用截图
| 主界面 · 粘贴链接一键处理 |
 |
| 输出效果 · 结构化 Markdown 笔记(左文件列表 + 右渲染预览) |
 |
---
## 功能特性
- 一键端到端:粘贴视频/播客链接 → 自动下载 → 自动转写 → 自动总结成 Markdown
- 完全本地可控:默认无需联网、无需 API Key、无需 GPU 也可跑(GGUF 路径)
- ASR 引擎可选:本地 faster-whisper(离线)/ OpenAI Whisper 云端 API
- **总结模型三选一**(核心):进程内 GGUF(零配置)/ 本地 vLLM·sglang 服务 / 云端 OpenRouter
- 自动按上下文窗口切分 + 多级合并:长播客也能稳定产出
- 下载缓存:相同链接二次运行秒级跳过
- 可选视觉模型:将视频截图与文字笔记融合,补充图示信息
- 中文/英文/日文/韩文识别语言可切换
- 自定义系统提示词:可改写整理风格
---
## 处理流水线
```
视频/播客 URL
│
▼
① 媒体下载(yt-dlp) ─→ outputs/_audio.mp3
│
▼
② 语音转写 ASR ─→ outputs/_audio_transcript.txt
│ ├─ local : faster-whisper (CPU/GPU,离线)
│ └─ cloud : OpenAI Whisper API
│
▼
③ 大模型整理 ─→ outputs/_audio_summary.md
├─ local_gguf : llama-cpp-python 进程内 qwen3-4b-GGUF
├─ local : vLLM / sglang OpenAI 兼容 HTTP 服务
└─ cloud : OpenRouter(OpenAI 兼容)
```
每一步都有进度条与日志实时显示在界面下方。
---
## 安装与运行
### 1. 准备 Python 环境
推荐 Python 3.10+。Windows / macOS / Linux 均可。
### 2. 安装依赖
```bash
pip install -r requirements.txt
```
依赖一览(见 `requirements.txt`):
| 包 | 用途 |
|---|---|
| PyQt5 | 桌面 UI |
| yt-dlp | 视频/音频下载 |
| requests | HTTP 调用(OpenAI 兼容接口) |
| imageio-ffmpeg | 自带 ffmpeg 二进制,无需系统安装 |
| faster-whisper | 本地 ASR(CPU/GPU) |
| modelscope | 国内镜像下载模型(优先级高) |
| llama-cpp-python | 进程内 GGUF 推理(用法 A 必装) |
> **第一次跑 GGUF 路径**时,`llama-cpp-python` 会尝试加载 GPU 加速层,Windows 上若缺 `cublas64_12.dll` 等,UI 启动时会自动注入 `nvidia/*/bin` 路径;如仍失败,可仅用 CPU 跑(速度慢但稳定)。
### 3. 启动
```bash
python ui.py
```
启动后直接粘贴视频链接 → 点 **开始处理** 即可。第一次使用会自动下载模型,耐心等待。
---
## 界面与基本使用
主界面三步式流程:
1. **粘贴链接**:支持 Bilibili、YouTube 等 yt-dlp 支持的站点;窗口置顶时会自动识别剪贴板里的链接。
2. **开始处理**:实时显示「下载 → 语音转写 → LLM 整理」三步状态与进度。
3. **查看结果**:处理完成后自动跳转到结果页,左侧文件列表、右侧 Markdown 预览。
菜单:
- **文件 → 新建任务**:清空状态,开始下一个
- **文件 → 查看已解析文件**:浏览 `outputs/` 目录
- **文件 → 清空下载缓存**:删除 `.cache/download_cache.json`(下次运行将重新下载)
- **设置 → 高级配置...**:弹出所有可调参数
---
## 重点:总结模型的三种用法
总结模型的三种用法对应 UI 中 **设置 → 高级配置 → 总结后端** 下拉框的三个选项:
```
chat_backend 取值 UI 显示 实际含义
───────────────── ───────────────────── ───────────────────────
"local_gguf" 本地进程(GGUF) llama-cpp-python 进程内 GGUF
"local" 本地服务(vLLM) 本地 OpenAI 兼容 HTTP 服务
"cloud" 云端(OpenRouter) OpenRouter 云端 API
```
下面分别讲清三者。
### 对比速览
| 维度 | 本地 GGUF(零配置) | 本地 vLLM 服务 | 云端 OpenRouter |
|---|---|---|---|
| 底模(默认) | qwen3-4b (GGUF q4_k_m) | qwen3-4b (高精) | 任意 OpenRouter 模型(如 `openrouter/free`) |
| 配置门槛 | **零**:装好包就能用 | 需自行启动 vLLM / sglang 服务 | 需注册 OpenRouter 拿 Key |
| API Key | ❌ 不需要 | 通常不需要 | ✅ 需要 |
| Base URL | ❌ 留空 | `http://localhost:/v1` | `https://openrouter.ai/api/v1` |
| 是否联网 | 仅**首次**下载权重(约 2~3GB) | 否 | ✅ 每次 |
| 上下文窗口 | 较小(默认 16K,需分块合并) | 大(通常 32K~128K) | **极大**(一次读完不切分) |
| 速度 | 慢(看硬件) | 快(GPU + 连续批处理) | 秒级(取决于模型) |
| 成本 | 免费 | 免费(占你的硬件) | 按量计费 |
| 隐私 | **完全本地** | **完全本地** | 上传云端 |
| 适用场景 | 个人离线、临时用、零配置 | 已部署推理服务的环境 | 长内容、最干净、最快 |
---
### 用法 A:本地进程 GGUF(零配置首选)
**核心思想**:用 `llama-cpp-python` 把 GGUF 量化模型加载到当前 Python 进程内推理,不依赖任何外部服务、不消耗 API 额度、不上传数据。
#### 适合谁
- 没有显卡/服务器,希望**装上就能跑**
- 不想配 API Key,不想折腾 vLLM
- 笔记内容**敏感**(完全离线)
#### 工作机制(pipeline.py 中 `_chat_local_gguf`)
1. **首次运行**:自动从 ModelScope 仓库下载 `qwen/Qwen3-4B-GGUF` 的 GGUF 权重到项目 `models/qwen--Qwen3-4B-GGUF/`(备选 HuggingFace)。
2. **量化挑选**:默认 `q4_k_m`(约 2~3GB)。找不到目标量化时按 `q4_k_m → q4_k_s → q5_k_m → q8_0 → f16` 兜底;再不行就取目录下任意 `.gguf`。
3. **进程内加载**:通过 `llama_cpp.Llama(model_path=..., n_ctx=16384, n_gpu_layers=32)` 实例化,自动检测 CUDA / Metal 支持;纯 CPU 环境退化为 `n_gpu_layers=0`。
4. **同一进程只加载一次**:`_gguf_cache` 模块级缓存,第二次推理直接复用,无需重新读盘。
5. **长文本处理**:根据 `n_ctx`(默认 8192,留 2048 给 system+输出)把转写稿切成 token 预算内的多块,**逐块整理 → 合并 → 再合并**,递归处理任意长度。
6. **退出释放**:`closeEvent` 中调用 `pipeline.release_gguf()` 显式释放 llama.cpp 资源,避免解释器销毁阶段的告警。
#### 配置项
打开 **设置 → 高级配置**:
| 项 | 默认值 | 说明 |
|---|---|---|
| 总结后端 | `本地进程(GGUF)` | 必选 |
| Base URL | (留空) | **不用填**,程序忽略 |
| API Key | (留空) | **不用填** |
| 总结模型 | `qwen/Qwen3-4B-GGUF` | ModelScope/HF 仓库 ID;也支持本地 `.gguf` 文件绝对路径或目录路径 |
| 量化等级 | `q4_k_m` | 可选:`q4_k_m` / `q4_k_s` / `q5_k_m` / `q8_0` / `f16`(按需改 `chat_local_quant`) |
#### 高级用法:手动放权重
如果你已经下载好了权重,避免重复下载:
```text
PodNotes/
├── models/
│ └── Qwen3-4B-GGUF/ # 把 .gguf 放在这里(或任意上述候选目录)
│ ├── qwen3-4b-q4_k_m.gguf
│ └── ...
```
或者把"总结模型"框填成本地绝对路径:`D:\models\qwen3-4b-q4_k_m.gguf`。
#### 性能与注意事项
- **速度取决于 CPU/内存**。CPU 跑 q4_k_m 的 4B 模型整理一份 1 小时播客通常十几分钟到半小时。
- **首次运行慢**:下载 2~3GB 权重;UI 会通过 `tqdm` 转发的进度条实时显示。
- 长文本需要分块 + 合并,最终笔记在主题交界处偶尔会"接缝不顺",属正常现象;想要更干净的输出换用法 B/C。
---
### 用法 B:本地服务 vLLM / sglang(高吞吐)
**核心思想**:你已经在本地(或内网机器)启动了一个 OpenAI 兼容的 HTTP 服务(如 vLLM、sglang、Ollama 的 OpenAI 兼容模式),PodNotes 只作为客户端调它的 `/v1/chat/completions`。
#### 适合谁
- 已部署 GPU 服务器,**长跑一个推理服务**给多个工具共享
- 需要**高吞吐 / 低延迟**(vLLM 的连续批处理优势)
- 想要用 qwen3-4b 的**非量化**精度
- 不想让 PodNotes 进程独占内存(服务独立,PodNotes 只是 HTTP 客户端)
#### 工作机制(pipeline.py 中 `_chat` 走 `chat_backend="local"` 分支)
1. 拼接 `base_url` + `/v1/chat/completions`(也接受已经带 `/chat/completions` 的)。
2. POST 发送 OpenAI 兼容 messages:
- `temperature=0.3`、`top_p=0.8`、`top_k=20`、`presence_penalty=1.5`
- `chat_template_kwargs={"enable_thinking": False}` —— 告诉 Qwen3 关闭思考模式
3. 文本超长时按 80,000 字符切分,逐段总结后再合并(上下文足够大时通常**不必切**)。
4. UI 提供 **「获取模型」** 按钮:直接请求 `/v1/models` 拉取真实模型 ID 填回下拉框,避免手抄出错(vLLM 服务的模型 ID 往往是 `/workspace/models/qwen3-4b/` 这种长串)。
#### 启动本地服务(参考命令)
**vLLM**:
```bash
vllm serve Qwen/Qwen3-4B \
--host 0.0.0.0 \
--port 8001 \
--max-model-len 32768
```
**sglang**:
```bash
python -m sglang.launch_server \
--model-path Qwen/Qwen3-4B \
--host 0.0.0.0 --port 8001 \
--context-length 32768
```
启动后用 curl 自测:
```bash
curl http://localhost:8001/v1/models
```
看到 JSON 里有模型 ID 即表示服务可用。
#### 配置项
| 项 | 默认值 | 说明 |
|---|---|---|
| 总结后端 | `本地服务(vLLM)` | 必选 |
| Base URL | `http://localhost:8001/v1` | 按你的服务地址修改 |
| API Key | (留空) | 大多数本地服务无鉴权,留空即可 |
| 总结模型 | (点击 **获取模型** 自动填) | 也可手动输入 `Qwen/Qwen3-4B` 或服务真实 ID |
#### 进阶
- 把 `Base URL` 指向**局域网另一台机器**即可:`http://192.168.x.x:8001/v1`,多机协作。
- 如果服务要求鉴权,把 API Key 填上即可。
---
### 用法 C:云端 OpenRouter(不占本机算力)
**核心思想**:把转写稿直接发给 OpenRouter(统一转发多家模型的 API 聚合服务),享受云端超大上下文窗口。
#### 适合谁
- 想要**最快、最干净**的整理结果(一次读完,不必分块合并)
- 没有 GPU 或 GPU 显存不够
- 不在意**内容上云**(注意隐私)
- 想换不同模型试效果(OpenRouter 聚合了 GPT、Claude、Gemini、Qwen、DeepSeek 等上百家)
#### 工作机制(pipeline.py 中 `_chat` 走 `chat_backend="cloud"` 分支)
与用法 B 共用 `_chat` 函数,但有两条 OpenRouter 特有的处理:
1. 自动关闭推理:
```python
if "openrouter.ai" in url or "openrouter" in model:
payload["reasoning"] = {"enabled": False}
headers["X-Title"] = "PodNotes"
```
防止 Qwen3 / DeepSeek-R1 等带思考模式的模型输出大量 `` 标签浪费时间。
2. 自动附带 `X-Title: PodNotes`(OpenRouter 用来在排行榜上识别来源,可选)。
#### 配置项
| 项 | 默认值 | 说明 |
|---|---|---|
| 总结后端 | `云端(OpenRouter)` | 必选 |
| Base URL | `https://openrouter.ai/api/v1` | 一般不用改 |
| API Key | (留空 → 会弹窗提示) | 去 https://openrouter.ai/keys 申请;勾选 **记住密钥** 可明文存到本地 `config.json` |
| 总结模型 | `openrouter/free` | 任意 OpenRouter 支持的模型 ID,如 `google/gemini-2.5-flash`、`deepseek/deepseek-chat-v3`、`qwen/qwen3-4b:free` |
#### 模型选择建议
- `openrouter/free`:零成本试水,但质量随机
- `google/gemini-2.5-flash`:长文本极强,速度快
- `deepseek/deepseek-chat-v3`:中文质量优秀
- `qwen/qwen3-235b-a22b:free`:强大的开源大模型
- `anthropic/claude-sonnet-4.5`:英文内容首选
去 https://openrouter.ai/models 看完整列表。
---
### 如何在 UI 中切换
打开 **设置 → 高级配置**,在 **总结后端** 下拉里选择:
```
┌──────────────────────────────────────────────────────────────┐
│ 总结后端: [本地进程(GGUF) ▼] │
│ │
│ 切到「本地进程(GGUF)」:Base URL 自动清空,无需 Key, │
│ 首次使用自动下载 GGUF 权重 │
│ │
│ 切到「本地服务(vLLM)」:Base URL 自动填 localhost:8001/v1, │
│ 「获取模型」按钮亮起,点了可自动取模型 │
│ │
│ 切到「云端(OpenRouter)」:Base URL 自动填 openrouter.ai/api/v1,
│ 必须填 API Key,否则开始时弹窗提醒 │
└──────────────────────────────────────────────────────────────┘
```
切完后点 **确定** 保存。下次启动自动应用。
### 配置文件示例
`config.json`(位于项目根目录,由程序自动写入):
```json
{
"openai_api_key": "",
"base_url": "https://openrouter.ai/api/v1",
"whisper_model": "whisper-1",
"asr_backend": "local",
"asr_model": "small",
"chat_backend": "local_gguf",
"chat_model": "qwen/Qwen3-4B-GGUF",
"chat_local_model": "qwen/Qwen3-4B-GGUF",
"chat_local_quant": "q4_k_m",
"language": "auto",
"output_dir": "E:\\Mine\\Work\\ai\\PodNotes\\outputs",
"chunk_minutes": 10,
"screenshot": false,
"frame_interval": 120
}
```
> 默认**不写入 API Key**(`save_config(remember_key=False)` 时清空)。需要记住时勾选界面上的 **「记住密钥」** 复选框,会以明文形式写入本地。
## 输出文件与目录结构
```
PodNotes/
├── config.json # 自动生成的配置文件
├── outputs/ # 默认输出目录(可在高级配置里改)
│ ├── BV1xxx_audio.mp3 # 下载的音频
│ ├── BV1xxx_audio_transcript.txt # 逐字稿(中间产物,可关掉总结直接拿)
│ └── BV1xxx_audio_summary.md # 最终结构化笔记
├── models/ # 自动管理的模型目录
│ ├── faster-whisper-small/ # ASR 模型
│ └── qwen--Qwen3-4B-GGUF/ # GGUF 权重(仅 A 路径)
└── .cache/
└── download_cache.json # 已下载链接缓存
```
`outputs/_audio_summary.md` 就是最终产物:核心摘要 + 主题分组 + 逐问逐答(Q/A),可直接拿去复习、检索、喂给别的工具。
---
## 常见问题
**Q1:第一次跑 GGUF 一直没反应?**
A:等下载。q4_k_m 的 qwen3-4b 约 2.4GB。日志区会显示 `下载进度: x%|...`,若完全没有日志,确认 `modelscope` 安装成功,且本机可访问 `modelscope.cn`。
**Q2:GGUF 跑得太慢?**
A:换用法 B 起 vLLM 服务,或换用法 C 上云。GGUF 进程内路径适合"有就行、不急"的场景。
**Q3:vLLM 服务起来了但提示 `model does not exist`?**
A:点 **「获取模型」** 按钮,它会请求 `/v1/models` 把真实模型 ID 拉出来填进下拉框。
**Q4:OpenRouter 报 `User not found` / 401?**
A:API Key 没填或填错,去 https://openrouter.ai/keys 复制正确的 sk-or-... 前缀的 key。
**Q5:Qwen3 输出里出现 `...` 一大段推理?**
A:用法 C 已自动发 `reasoning.enabled=False`;用法 A 用的是 GGUF 路径,本身不会触发思考模式。若仍出现,检查系统提示词里有没有误用 `enable_thinking=True`。
**Q6:长播客(>2 小时)总结的笔记中间有断章?**
A:这是分块合并的副作用。换用法 C(OpenRouter 上下文极大)或用法 B(vLLM 长上下文)能显著改善。
**Q7:想用其他模型(如本地 Qwen2.5-7B-Instruct 的 GGUF)?**
A:在 **总结后端 = 本地进程(GGUF)** 下,把"总结模型"改成:
- ModelScope/HF 仓库 ID,如 `qwen/Qwen2.5-7B-Instruct-GGUF`
- 或本地绝对路径:`D:\models\qwen2.5-7b-instruct-q4_k_m.gguf`
然后调整"量化等级"为你下载的对应量化。
**Q8:怎么切换系统提示词?**
A:**设置 → 高级配置 → 系统提示词**。可以重写整理风格、输出语言、Q/A 模板等。
**Q9:下载失败 / 网速慢?**
A:日志里能看到 yt-dlp 详细错误。程序已内置 `retries=10, fragment_retries=10` 自动重试。可换网络/换时间再试;或把链接放进下载缓存目录手动下完再处理。
---
## 依赖说明
```txt
PyQt5>=5.15
yt-dlp
requests
imageio-ffmpeg
faster-whisper
modelscope
llama-cpp-python
```
- `imageio-ffmpeg` 用来在打包环境提供 ffmpeg 二进制,避免系统依赖
- `modelscope` 国内下载速度远胜 HuggingFace,国内用户强烈建议保留
- `llama-cpp-python` 仅在 **用法 A** 真正用到;不需要 GGUF 路径可以 `pip uninstall`,但保留无副作用
---
> 做给自己听播客时用的工具。如果你也在密集听播客却懒得记笔记,欢迎直接用。