v1.3.0: 新增 Claude Code Skill,支持批量提取文案

- 新增 douyin-video Skill,支持命令行批量提取视频文案
- 文案自动保存为 Markdown 格式
- 每个视频独立文件夹存储
- 支持 --save-video 同时保存视频
- 统一环境变量名为 API_KEY
- 更新 README 文档
This commit is contained in:
yzfly
2026-01-30 14:42:17 +08:00
parent 653f3cfe75
commit 97c7dbdc69
8 changed files with 679 additions and 220 deletions
+5 -1
View File
@@ -45,4 +45,8 @@ htmlcov/
*.whl
.claude
temp/
temp/
# Local
.env
output/
+123 -211
View File
@@ -1,48 +1,41 @@
# 抖音无水印视频文本提取 MCP 服务器
# 抖音无水印视频下载与文案提取
[![PyPI version](https://badge.fury.io/py/douyin-mcp-server.svg)](https://badge.fury.io/py/douyin-mcp-server)
[![Python version](https://img.shields.io/pypi/pyversions/douyin-mcp-server.svg)](https://pypi.org/project/douyin-mcp-server/)
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
一个基于 Model Context Protocol (MCP) 的服务器,可以从抖音分享链接下载无水印视频,提取音频并转换为文本。
从抖音分享链接下载无水印视频,提取音频并转换为文本。
<a href="https://glama.ai/mcp/servers/@yzfly/douyin-mcp-server">
<img width="380" height="200" src="https://glama.ai/mcp/servers/@yzfly/douyin-mcp-server/badge" alt="douyin-mcp-server MCP server" />
</a>
## 📋 项目声明
## 📦 两种使用方式
**官方文档地址:** https://github.com/yzfly/douyin-mcp-server
请以本项目的 [README.md](https://github.com/yzfly/douyin-mcp-server/blob/main/README.md) 文件为准了解项目的功能特性、使用方法、API 配置说明等详细信息。
**重要提醒:** 第三方平台如因自身 MCP Server 功能支持度限制而无法正常使用,请联系相应平台方。本项目不提供任何形式的技术支持或保证,用户需自行承担使用本项目可能产生的任何损失或损害。
**法律声明:**
1. 本项目基于 Apache 2.0 协议发布
2. 本项目仅供学习和研究使用,不得用于任何违法或违规目的
3. 本项目的使用必须遵守相关法律法规
4. 本项目的作者和贡献者不对项目的任何部分承担法律责任
| 方式 | 适用场景 | 说明 |
|------|----------|------|
| **MCP Server** | Claude Desktop、Cherry Studio 等 | MCP 协议集成 |
| **Claude Code Skill** | Claude Code CLI | 命令行批量提取Markdown 输出 |
## ✨ 功能特性
- 🎵 **无水印视频获取** - 从抖音分享链接获取高质量无水印视频
- 🎵 **无水印视频获取** - 从抖音分享链接获取高质量无水印视频(无需 API 密钥)
- 🎧 **智能音频提取** - 自动从视频中提取音频内容
- 📝 **AI 文本识别** - 使用先进的语音识别技术提取文本内容
- 📝 **AI 文本识别** - 使用硅基流动 SenseVoice API 提取文案
- 📄 **Markdown 输出** - 文案自动保存为 Markdown 格式Skill 模式)
- 🧹 **自动清理** - 智能清理处理过程中的临时文件
- 🔧 **灵活配置** - 支持自定义 API 配置,默认使用 [阿里云百炼 API](https://help.aliyun.com/zh/model-studio/get-api-key?)
## 🚀 快速开始
---
## 🚀 方式一MCP Server (v1.3.0)
适用于 Claude Desktop、Cherry Studio 等支持 MCP 协议的应用。
### 步骤 1获取 API 密钥
前往 [阿里云百炼 API](https://help.aliyun.com/zh/model-studio/get-api-key?) 获取您的 `DASHSCOPE_API_KEY`
前往 [硅基流动](https://cloud.siliconflow.cn/i/TxUlXG3u) 注册并获取 API Key有免费额度
![获取阿里云百炼API](https://files.mdnice.com/user/43439/36e658be-1ccf-41dd-87cf-d43fefde5c4e.png)
### 步骤 2配置环境变量
在 Claude Desktop、Cherry Studio 等支持 MCP Server 的应用配置文件中添加以下配置:
### 步骤 2配置 MCP Server
```json
{
@@ -51,200 +44,142 @@
"command": "uvx",
"args": ["douyin-mcp-server"],
"env": {
"DASHSCOPE_API_KEY": "sk-xxxx"
"API_KEY": "sk-xxxx"
}
}
}
}
```
### 步骤 3开始使用
### 工具说明
配置完成后,您就可以在支持的应用中正常调用 MCP 工具了。
| 工具 | 功能 | 需要 API |
|------|------|----------|
| `get_douyin_download_link` | 获取无水印下载链接 | ❌ |
| `extract_douyin_text` | 提取视频文案 | ✅ |
| `parse_douyin_video_info` | 解析视频信息 | ❌ |
## ⚙️ API 配置说明
---
### 当前版本(>= 1.2.0
## 🛠️ 方式二Claude Code Skill
最新版本默认使用阿里云百炼 API具有以下优势
- ✅ 识别效果更好
- ✅ 处理速度更快
- ✅ 本地资源消耗更小
适用于 Claude Code CLI支持批量提取文案并保存为 Markdown 文件。
**配置步骤:**
1. 前往 [阿里云百炼](https://help.aliyun.com/zh/model-studio/get-api-key?) 开通 API 服务
2. 获取 API Key 并配置到环境变量 `DASHSCOPE_API_KEY`
### 安装 Skill
### 旧版本兼容(<= 1.1.0
如果您需要使用旧版本,请使用以下配置:
```json
{
"mcpServers": {
"douyin-mcp": {
"command": "uvx",
"args": ["douyin-mcp-server@1.1.0"],
"env": {
"DOUYIN_API_KEY": "your-api-key-here"
}
}
}
}
```
**注意:** 旧版本使用硅基流动 API需要在 [硅基流动](https://cloud.siliconflow.cn/i/TxUlXG3u) 注册账号并获取 API Key。
📖 [1.1.0 版本文档](https://pypi.org/project/douyin-mcp-server/1.1.0/)
## 🛠️ 工具说明
### `get_douyin_download_link`
获取抖音视频的无水印下载链接。
**参数:**
- `share_link` (string): 抖音分享链接或包含链接的文本
**返回:**
- JSON 格式的下载链接和视频信息
**特点:** 无需 API 密钥即可使用
### `extract_douyin_text`
完整的文本提取工具,一站式完成视频到文本的转换。
**处理流程:**
1. 解析抖音分享链接
2. 直接使用视频 URL 进行语音识别
3. 返回提取的文本内容
**参数:**
- `share_link` (string): 抖音分享链接或包含链接的文本
- `model` (string, 可选): 语音识别模型,默认使用 `paraformer-v2`
**环境变量要求:**
- `DASHSCOPE_API_KEY`: 阿里云百炼 API 密钥(必需)
### `parse_douyin_video_info`
轻量级视频信息解析工具。
**参数:**
- `share_link` (string): 抖音分享链接
**特点:** 仅解析视频基本信息,不下载视频文件
### 资源访问
- `douyin://video/{video_id}`: 通过视频 ID 获取详细信息
## 📦 系统要求
### 运行环境
- **Python**: 3.10 或更高版本
### 依赖库
- `requests` - HTTP 请求处理
- `ffmpeg-python` - 音视频处理
- `tqdm` - 进度条显示
- `mcp` - Model Context Protocol 支持
- `dashscope` - 阿里云百炼 API 客户端
## ⚠️ 注意事项
- 🔑 **API 密钥必需**:文本提取功能需要有效的阿里云百炼 API 密钥
- 🆓 **部分功能免费**:获取下载链接功能无需 API 密钥
- 📱 **格式支持**:支持大部分抖音视频格式
- 🚀 **性能优化**:使用阿里云百炼 API 获得更快更准确的识别效果
## 🔧 开发指南
### 本地开发环境搭建
下载 `douyin-video.skill` 文件,解压到 skills 目录:
```bash
# 全局安装
unzip douyin-video.skill -d ~/.claude/skills/
# 或项目级安装
unzip douyin-video.skill -d .claude/skills/
```
### 配置环境变量
```bash
export API_KEY="your-siliconflow-api-key"
```
### 使用方法
```bash
# 获取视频信息(无需 API
python douyin_downloader.py -l "抖音链接" -a info
# 下载视频
python douyin_downloader.py -l "抖音链接" -a download -o ./videos
# 提取文案
python douyin_downloader.py -l "抖音链接" -a extract -o ./output
# 提取文案并保存视频
python douyin_downloader.py -l "抖音链接" -a extract -o ./output --save-video
```
### 输出格式
```
output/
├── 7600361826030865707/
│ └── transcript.md # Markdown 格式文案
└── 7581044356631612699/
├── transcript.md
└── *.mp4 # --save-video 时保存
```
**transcript.md 示例:**
```markdown
# 视频标题
| 属性 | 值 |
|------|-----|
| 视频ID | `7600361826030865707` |
| 提取时间 | 2026-01-30 14:19:00 |
| 下载链接 | [点击下载](url) |
---
## 文案内容
(语音识别文字)
```
---
## 📋 系统要求
- **Python**: 3.8+
- **FFmpeg**: 必须安装
- macOS: `brew install ffmpeg`
- Ubuntu: `apt install ffmpeg`
---
## 🔧 本地开发
```bash
# 克隆项目
git clone https://github.com/yzfly/douyin-mcp-server.git
cd douyin-mcp-server
# 安装依赖(开发模式)
pip install -e .
```
### 运行测试
```bash
# 启动服务器进行测试
python -m douyin_mcp_server.server
```
### Claude Desktop 本地开发配置
在 Claude Desktop 配置文件中添加本地开发配置:
```json
{
"mcpServers": {
"douyin-mcp": {
"command": "uv",
"args": [
"run",
"--directory",
"/path/to/your/douyin-mcp-server",
"python",
"-m",
"douyin_mcp_server"
],
"env": {
"DASHSCOPE_API_KEY": "your-api-key-here"
}
}
}
}
```
---
## ⚠️ 免责声明
### 使用风险
- 使用者对本项目的使用完全自主决定,并自行承担所有风险
- 作者对使用者因使用本项目而产生的任何损失、责任或风险概不负责
- 本项目仅供学习和研究使用
- 使用者需遵守相关法律法规
- 禁止用于侵犯知识产权的行为
- 作者不对使用本项目产生的损失承担责任
### 代码质量
- 本项目基于现有知识和技术开发,作者努力确保代码的正确性和安全性
- 但不保证代码完全没有错误或缺陷,使用者需自行评估和测试
---
### 第三方依赖
- 本项目依赖的第三方库、插件或服务遵循各自的开源或商业许可
- 使用者需自行查阅并遵守相应协议
- 作者不对第三方组件的稳定性、安全性及合规性承担责任
## 📝 更新日志
### 法律合规
- 使用者必须自行研究相关法律法规,确保使用行为合法合规
- 任何违反法律法规导致的法律责任和风险,均由使用者自行承担
- 禁止使用本工具从事任何侵犯知识产权的行为
- 开发者不参与、不支持、不认可任何非法内容的获取或分发
### v1.3.0 (最新)
### 数据处理
- 本项目不对使用者的数据收集、存储、传输等处理活动的合规性承担责任
- 使用者应自行遵守相关法律法规,确保数据处理行为合法正当
-**Claude Code Skill**:新增命令行工具,支持批量提取文案
- 📄 **Markdown 输出**:文案自动保存为 Markdown 格式
- 🎯 **双 API 支持**:同时支持硅基流动和阿里云百炼 API
### 责任限制
- 使用者不得将项目作者、贡献者或相关方与使用行为联系起来
- 不得要求作者对使用项目产生的任何损失或损害负责
- 基于本项目的二次开发、修改或编译程序与原作者无关
### v1.2.0
### 知识产权
- 本项目不授予使用者任何专利许可
- 若使用本项目导致专利纠纷或侵权,使用者自行承担全部风险和责任
- 未经书面授权,不得用于商业宣传、推广或再授权
- 🔄 **API 切换**:升级为阿里云百炼 API环境变量 `DASHSCOPE_API_KEY`
### 服务终止
- 作者保留随时终止向违反声明的使用者提供服务的权利
- 可能要求违规使用者销毁已获取的代码及衍生作品
- 作者保留在不另行通知的情况下更新本声明的权利
### v1.1.0
**⚠️ 重要提醒:在使用本项目前,请认真阅读并完全理解上述免责声明。如有疑问或不同意任何条款,请勿使用本项目。继续使用即视为完全接受上述声明并自愿承担所有风险和后果。**
- 🐛 **问题修复**:修复文件名过长导致的错误
### v1.0.0
- 🎉 首次发布
---
## 📄 许可证
@@ -252,27 +187,4 @@ Apache License 2.0
## 👨‍💻 作者
- **yzfly** - [yz.liu.me@gmail.com](mailto:yz.liu.me@gmail.com)
- GitHub: [https://github.com/yzfly](https://github.com/yzfly)
## 🤝 贡献
欢迎提交 Issue 和 Pull Request我们期待您的参与和贡献。
## 📝 更新日志
### v1.2.0 (最新)
- 🚀 **性能提升**:更快、更准确的视频文案提取
- 🔄 **API 升级**:切换到阿里云百炼 API显著提升识别准确率
- 🔧 **配置更新**:环境变量从 `DOUYIN_API_KEY` 更新为 `DASHSCOPE_API_KEY`
### v1.1.0
- 🐛 **问题修复**:修复提取视频时文件名过长导致的错误
### v1.0.0
- 🎉 **首次发布**:初始版本
-**核心功能**:支持抖音视频文本提取
- 🔗 **链接获取**:支持获取无水印视频下载链接
- 🔐 **环境配置**:从环境变量读取 API 密钥
- 🧹 **自动清理**:自动清理临时文件
- ⚙️ **灵活配置**:支持自定义 API 配置
**yzfly** - [GitHub](https://github.com/yzfly) | [Email](mailto:yz.liu.me@gmail.com)
Binary file not shown.
+153
View File
@@ -0,0 +1,153 @@
---
name: douyin-video
description: "抖音无水印视频下载和文案提取工具. 从抖音分享链接获取无水印视频下载链接, 下载视频, 提取视频中的语音文案并自动保存到文件. 适用场景包括获取抖音视频信息, 下载无水印视频, 批量提取视频文案. 当用户需要处理抖音视频链接或提取视频内容时触发."
---
# 抖音无水印视频下载和文案提取
从抖音分享链接获取无水印视频下载链接, 下载视频, 并使用语音识别提取视频中的文案, 自动保存到文件.
## 功能概述
- **获取下载链接**: 从抖音分享链接解析出无水印视频的直接下载地址 (无需 API 密钥)
- **下载视频**: 将无水印视频下载到本地指定目录
- **提取文案**: 通过语音识别从视频中提取文字内容 (需要硅基流动 API 密钥)
- **自动保存**: 每个视频的文案自动保存到独立文件夹 (视频ID为文件夹名)
## 环境要求
### 依赖安装
```bash
pip install requests ffmpeg-python
```
### 系统要求
- FFmpeg 必须安装在系统中 (用于音视频处理)
- macOS: `brew install ffmpeg`
- Ubuntu: `apt install ffmpeg`
### API 密钥配置 (仅文案提取需要)
文案提取功能使用硅基流动 API, 需要设置环境变量:
```bash
export API_KEY="your-siliconflow-api-key"
```
获取 API 密钥: https://cloud.siliconflow.cn/
## 使用方法
### 方法一: 使用脚本 (推荐)
```bash
# 获取视频信息和下载链接 (无需 API 密钥)
python douyin_downloader.py --link "抖音分享链接" --action info
# 下载视频到指定目录
python douyin_downloader.py --link "抖音分享链接" --action download --output ./videos
# 提取视频文案并保存到文件 (需要 API_KEY 环境变量)
python douyin_downloader.py --link "抖音分享链接" --action extract --output ./output
# 提取文案并同时保存视频
python douyin_downloader.py --link "抖音分享链接" --action extract --output ./output --save-video
# 安静模式 (减少输出)
python douyin_downloader.py --link "抖音分享链接" --action extract --output ./output --quiet
```
### 输出目录结构
提取文案后, 每个视频会保存到独立文件夹:
```
output/
├── 7600361826030865707/ # 视频ID为文件夹名
│ └── transcript.md # Markdown 格式文案文件
├── 7581044356631612699/
│ ├── transcript.md
│ └── 7581044356631612699.mp4 # 使用 --save-video 时保存
└── ...
```
### Markdown 文案格式
```markdown
# 视频标题
| 属性 | 值 |
|------|-----|
| 视频ID | `7600361826030865707` |
| 提取时间 | 2026-01-30 14:19:00 |
| 下载链接 | [点击下载](url) |
---
## 文案内容
(语音识别的文字内容)
```
### 方法二: 在 Python 代码中调用
```python
from douyin_downloader import get_video_info, download_video, extract_text
# 获取视频信息
info = get_video_info("抖音分享链接")
print(f"视频ID: {info['video_id']}")
print(f"标题: {info['title']}")
print(f"下载链接: {info['url']}")
# 下载视频
video_path = download_video("抖音分享链接", output_dir="./videos")
# 提取文案并保存到文件
result = extract_text("抖音分享链接", output_dir="./output")
print(f"文案已保存到: {result['output_path']}")
print(result['text'])
```
## 工作流程
### 获取视频信息
1. 解析抖音分享链接, 提取真实的视频 URL
2. 模拟移动端请求获取页面数据
3. 从页面 JSON 数据中提取无水印视频地址
4. 返回视频 ID, 标题和下载链接
### 提取视频文案
1. 解析分享链接获取视频信息
2. 下载视频到临时目录
3. 使用 FFmpeg 从视频中提取音频 (MP3 格式)
4. 调用硅基流动 SenseVoice API 进行语音识别
5. 清理临时文件, 返回识别的文本
## 常见问题
### 无法解析链接
- 确保链接是有效的抖音分享链接
- 链接格式通常为 `https://v.douyin.com/xxxxx/` 或完整的抖音视频 URL
### 提取文案失败
- 检查 `API_KEY` 环境变量是否已设置
- 确保 API 密钥有效且有足够的配额
- 确保 FFmpeg 已正确安装
### 下载速度慢
- 这取决于网络条件和视频大小
- 脚本会显示下载进度
## 注意事项
- 本工具仅供学习和研究使用
- 使用时需遵守相关法律法规
- 请勿用于任何侵犯版权或违法的目的
+390
View File
@@ -0,0 +1,390 @@
#!/usr/bin/env python3
"""
抖音无水印视频下载和文案提取工具
功能:
1. 从抖音分享链接获取无水印视频下载链接
2. 下载视频并提取音频
3. 使用硅基流动 API 从音频中提取文本
4. 自动保存文案到文件 (一个视频一个文件夹)
环境变量:
- API_KEY: 硅基流动 API 密钥 (用于文案提取功能)
使用示例:
# 获取下载链接 (无需 API 密钥)
python douyin_downloader.py --link "抖音分享链接" --action info
# 下载视频
python douyin_downloader.py --link "抖音分享链接" --action download --output ./videos
# 提取文案并保存到文件 (需要 API_KEY 环境变量)
python douyin_downloader.py --link "抖音分享链接" --action extract --output ./output
"""
import os
import re
import sys
import json
import argparse
import tempfile
import shutil
from pathlib import Path
from typing import Optional
from datetime import datetime
def check_dependencies():
"""检查必要的依赖是否已安装"""
missing = []
try:
import requests
except ImportError:
missing.append("requests")
try:
import ffmpeg
except ImportError:
missing.append("ffmpeg-python")
if missing:
print(f"缺少依赖: {', '.join(missing)}")
print(f"请运行: pip install {' '.join(missing)}")
sys.exit(1)
check_dependencies()
import requests
import ffmpeg
# 请求头,模拟移动端访问
HEADERS = {
'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) EdgiOS/121.0.2277.107 Version/17.0 Mobile/15E148 Safari/604.1'
}
# 硅基流动 API 配置
DEFAULT_API_BASE_URL = "https://api.siliconflow.cn/v1/audio/transcriptions"
DEFAULT_MODEL = "FunAudioLLM/SenseVoiceSmall"
class DouyinProcessor:
"""抖音视频处理器"""
def __init__(self, api_key: str = "", api_base_url: Optional[str] = None, model: Optional[str] = None):
self.api_key = api_key
self.api_base_url = api_base_url or DEFAULT_API_BASE_URL
self.model = model or DEFAULT_MODEL
self.temp_dir = Path(tempfile.mkdtemp())
def __del__(self):
"""清理临时目录"""
if hasattr(self, 'temp_dir') and self.temp_dir.exists():
shutil.rmtree(self.temp_dir, ignore_errors=True)
def parse_share_url(self, share_text: str) -> dict:
"""从分享文本中提取无水印视频链接"""
# 提取分享链接
urls = re.findall(r'http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+', share_text)
if not urls:
raise ValueError("未找到有效的分享链接")
share_url = urls[0]
share_response = requests.get(share_url, headers=HEADERS)
video_id = share_response.url.split("?")[0].strip("/").split("/")[-1]
share_url = f'https://www.iesdouyin.com/share/video/{video_id}'
# 获取视频页面内容
response = requests.get(share_url, headers=HEADERS)
response.raise_for_status()
pattern = re.compile(
pattern=r"window\._ROUTER_DATA\s*=\s*(.*?)</script>",
flags=re.DOTALL,
)
find_res = pattern.search(response.text)
if not find_res or not find_res.group(1):
raise ValueError("从HTML中解析视频信息失败")
# 解析JSON数据
json_data = json.loads(find_res.group(1).strip())
VIDEO_ID_PAGE_KEY = "video_(id)/page"
NOTE_ID_PAGE_KEY = "note_(id)/page"
if VIDEO_ID_PAGE_KEY in json_data["loaderData"]:
original_video_info = json_data["loaderData"][VIDEO_ID_PAGE_KEY]["videoInfoRes"]
elif NOTE_ID_PAGE_KEY in json_data["loaderData"]:
original_video_info = json_data["loaderData"][NOTE_ID_PAGE_KEY]["videoInfoRes"]
else:
raise Exception("无法从JSON中解析视频或图集信息")
data = original_video_info["item_list"][0]
# 获取视频信息
video_url = data["video"]["play_addr"]["url_list"][0].replace("playwm", "play")
desc = data.get("desc", "").strip() or f"douyin_{video_id}"
# 替换文件名中的非法字符
desc = re.sub(r'[\\/:*?"<>|]', '_', desc)
return {
"url": video_url,
"title": desc,
"video_id": video_id
}
def download_video(self, video_info: dict, output_dir: Optional[Path] = None, show_progress: bool = True) -> Path:
"""下载视频"""
if output_dir is None:
output_dir = self.temp_dir
else:
output_dir = Path(output_dir)
output_dir.mkdir(parents=True, exist_ok=True)
filename = f"{video_info['video_id']}.mp4"
filepath = output_dir / filename
if show_progress:
print(f"正在下载视频: {video_info['title']}")
response = requests.get(video_info['url'], headers=HEADERS, stream=True)
response.raise_for_status()
# 获取文件大小
total_size = int(response.headers.get('content-length', 0))
# 下载文件
downloaded = 0
with open(filepath, 'wb') as f:
for chunk in response.iter_content(chunk_size=8192):
if chunk:
f.write(chunk)
downloaded += len(chunk)
if show_progress and total_size > 0:
progress = downloaded / total_size * 100
print(f"\r下载进度: {progress:.1f}%", end="", flush=True)
if show_progress:
print(f"\n视频下载完成: {filepath}")
return filepath
def extract_audio(self, video_path: Path, show_progress: bool = True) -> Path:
"""从视频文件中提取音频"""
audio_path = video_path.with_suffix('.mp3')
if show_progress:
print("正在提取音频...")
try:
(
ffmpeg
.input(str(video_path))
.output(str(audio_path), acodec='libmp3lame', q=0)
.run(capture_stdout=True, capture_stderr=True, overwrite_output=True)
)
if show_progress:
print(f"音频提取完成: {audio_path}")
return audio_path
except Exception as e:
raise Exception(f"提取音频时出错: {str(e)}")
def extract_text_from_audio(self, audio_path: Path, show_progress: bool = True) -> str:
"""从音频文件中提取文字"""
if not self.api_key:
raise ValueError("未设置 API 密钥,请设置环境变量 DOUYIN_API_KEY")
if show_progress:
print("正在识别语音...")
files = {
'file': (audio_path.name, open(audio_path, 'rb'), 'audio/mpeg'),
'model': (None, self.model)
}
headers = {
"Authorization": f"Bearer {self.api_key}"
}
try:
response = requests.post(self.api_base_url, files=files, headers=headers)
response.raise_for_status()
# 解析响应
result = response.json()
if 'text' in result:
return result['text']
else:
return response.text
except Exception as e:
raise Exception(f"提取文字时出错: {str(e)}")
finally:
files['file'][1].close()
def cleanup_files(self, *file_paths: Path):
"""清理指定的文件"""
for file_path in file_paths:
if file_path.exists():
file_path.unlink()
def get_video_info(share_link: str) -> dict:
"""获取视频信息和下载链接"""
processor = DouyinProcessor()
return processor.parse_share_url(share_link)
def download_video(share_link: str, output_dir: str = ".") -> Path:
"""下载视频到指定目录"""
processor = DouyinProcessor()
video_info = processor.parse_share_url(share_link)
return processor.download_video(video_info, Path(output_dir))
def extract_text(share_link: str, api_key: Optional[str] = None, output_dir: Optional[str] = None,
save_video: bool = False, show_progress: bool = True) -> dict:
"""
从视频中提取文案并保存到文件
返回:
dict: 包含 video_info, text, output_path 的字典
"""
api_key = api_key or os.getenv('API_KEY')
if not api_key:
raise ValueError("未设置环境变量 API_KEY请先获取硅基流动 API 密钥")
processor = DouyinProcessor(api_key)
if show_progress:
print("正在解析抖音分享链接...")
video_info = processor.parse_share_url(share_link)
if show_progress:
print("正在下载视频...")
video_path = processor.download_video(video_info, show_progress=show_progress)
if show_progress:
print("正在提取音频...")
audio_path = processor.extract_audio(video_path, show_progress=show_progress)
if show_progress:
print("正在从音频中提取文本...")
text_content = processor.extract_text_from_audio(audio_path, show_progress=show_progress)
result = {
"video_info": video_info,
"text": text_content,
"output_path": None
}
# 保存到文件
if output_dir:
output_base = Path(output_dir)
video_folder = output_base / video_info['video_id']
video_folder.mkdir(parents=True, exist_ok=True)
# 保存文案为 Markdown 格式
transcript_path = video_folder / "transcript.md"
with open(transcript_path, 'w', encoding='utf-8') as f:
f.write(f"# {video_info['title']}\n\n")
f.write(f"| 属性 | 值 |\n")
f.write(f"|------|----|\n")
f.write(f"| 视频ID | `{video_info['video_id']}` |\n")
f.write(f"| 提取时间 | {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} |\n")
f.write(f"| 下载链接 | [点击下载]({video_info['url']}) |\n\n")
f.write(f"---\n\n")
f.write(f"## 文案内容\n\n")
f.write(text_content)
result["output_path"] = str(video_folder)
if show_progress:
print(f"文案已保存到: {transcript_path}")
# 保存视频 (可选)
if save_video:
saved_video_path = video_folder / f"{video_info['video_id']}.mp4"
shutil.copy2(video_path, saved_video_path)
if show_progress:
print(f"视频已保存到: {saved_video_path}")
# 清理临时文件
if show_progress:
print("正在清理临时文件...")
processor.cleanup_files(video_path, audio_path)
return result
def main():
parser = argparse.ArgumentParser(
description="抖音无水印视频下载和文案提取工具",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
示例:
# 获取视频信息和下载链接
python douyin_downloader.py --link "抖音分享链接" --action info
# 下载视频
python douyin_downloader.py --link "抖音分享链接" --action download --output ./videos
# 提取文案并保存到文件 (需要设置 DOUYIN_API_KEY 环境变量)
python douyin_downloader.py --link "抖音分享链接" --action extract --output ./output
# 提取文案并同时保存视频
python douyin_downloader.py --link "抖音分享链接" --action extract --output ./output --save-video
"""
)
parser.add_argument("--link", "-l", required=True, help="抖音分享链接或包含链接的文本")
parser.add_argument("--action", "-a", choices=["info", "download", "extract"],
default="info", help="操作类型: info(获取信息), download(下载视频), extract(提取文案)")
parser.add_argument("--output", "-o", default="./output", help="输出目录 (默认 ./output)")
parser.add_argument("--api-key", "-k", help="硅基流动 API 密钥 (也可通过 DOUYIN_API_KEY 环境变量设置)")
parser.add_argument("--save-video", "-v", action="store_true", help="提取文案时同时保存视频")
parser.add_argument("--quiet", "-q", action="store_true", help="安静模式,减少输出")
args = parser.parse_args()
try:
if args.action == "info":
info = get_video_info(args.link)
print("\n" + "=" * 50)
print("视频信息:")
print("=" * 50)
print(f"视频ID: {info['video_id']}")
print(f"标题: {info['title']}")
print(f"下载链接: {info['url']}")
print("=" * 50)
elif args.action == "download":
video_path = download_video(args.link, args.output)
print(f"\n视频已保存到: {video_path}")
elif args.action == "extract":
result = extract_text(
args.link,
args.api_key,
output_dir=args.output,
save_video=args.save_video,
show_progress=not args.quiet
)
if not args.quiet:
print("\n" + "=" * 50)
print("提取完成!")
print("=" * 50)
print(f"视频ID: {result['video_info']['video_id']}")
print(f"标题: {result['video_info']['title']}")
if result['output_path']:
print(f"保存位置: {result['output_path']}")
print("=" * 50)
print("\n文案内容:\n")
print(result['text'][:500] + "..." if len(result['text']) > 500 else result['text'])
print("\n" + "=" * 50)
except Exception as e:
print(f"\n错误: {e}", file=sys.stderr)
sys.exit(1)
if __name__ == "__main__":
main()
+1 -1
View File
@@ -1,6 +1,6 @@
"""抖音无水印链接提取 MCP 服务器"""
__version__ = "1.2.0"
__version__ = "1.3.0"
__author__ = "yzfly"
__email__ = "yz.liu.me@gmail.com"
+6 -6
View File
@@ -242,13 +242,13 @@ async def extract_douyin_text(
返回:
- 提取的文本内容
注意: 需要设置环境变量 DASHSCOPE_API_KEY
注意: 需要设置环境变量 API_KEY
"""
try:
# 从环境变量获取API密钥
api_key = os.getenv('DASHSCOPE_API_KEY')
api_key = os.getenv('API_KEY')
if not api_key:
raise ValueError("未设置环境变量 DASHSCOPE_API_KEY请在配置中添加阿里云百炼API密钥")
raise ValueError("未设置环境变量 API_KEY请在配置中添加阿里云百炼API密钥")
processor = DouyinProcessor(api_key, model)
@@ -328,11 +328,11 @@ def douyin_text_extraction_guide() -> str:
## 环境变量配置
请确保设置了以下环境变量:
- `DASHSCOPE_API_KEY`: 阿里云百炼API密钥
- `API_KEY`: 阿里云百炼API密钥
## 使用步骤
1. 复制抖音视频的分享链接
2. 在Claude Desktop配置中设置环境变量 DASHSCOPE_API_KEY
2. 在Claude Desktop配置中设置环境变量 API_KEY
3. 使用相应的工具进行操作
## 工具说明
@@ -349,7 +349,7 @@ def douyin_text_extraction_guide() -> str:
"command": "uvx",
"args": ["douyin-mcp-server"],
"env": {
"DASHSCOPE_API_KEY": "your-dashscope-api-key-here"
"API_KEY": "your-api-key-here"
}
}
}
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
[project]
name = "douyin-mcp-server"
version = "1.2.0"
version = "1.3.0"
description = "MCP server for downloading Douyin videos and extracting text"
readme = "README.md"
license = {text = "MIT"}