audio-transcribe-cli-mcp 0.1.0__tar.gz

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,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 OstrichHermit
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,164 @@
1
+ Metadata-Version: 2.4
2
+ Name: audio-transcribe-cli-mcp
3
+ Version: 0.1.0
4
+ Summary: 音视频转文字 CLI / MCP Server:阿里云百炼录音文件识别,支持说话人分离与微信语音 SILK
5
+ Author: OstrichHermit
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/OstrichHermit/audio-transcribe
8
+ Project-URL: Issues, https://github.com/OstrichHermit/audio-transcribe/issues
9
+ Keywords: asr,speech-to-text,transcription,dashscope,qwen,audio,video,mcp,whisper-alternative
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Environment :: Console
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Topic :: Multimedia :: Sound/Audio :: Speech
21
+ Classifier: Topic :: Utilities
22
+ Requires-Python: >=3.10
23
+ Description-Content-Type: text/markdown
24
+ License-File: LICENSE
25
+ Requires-Dist: dashscope
26
+ Dynamic: license-file
27
+
28
+ # audio-transcribe
29
+
30
+ **音视频转文字,支持 CLI、MCP Server 两种形态:说话人分离,长音频免切片,原生支持微信语音 SILK 格式。**
31
+
32
+ Audio/video transcription in both CLI and MCP Server flavors: speaker diarization, long-audio support with no manual splitting, and native WeChat SILK decoding.
33
+
34
+ [English](README_EN.md) | 简体中文
35
+
36
+ 基于阿里云百炼录音文件识别接口(qwen-audio-asr 系列 / paraformer)。不依赖 Claude Code:可作为独立命令行工具,也可接入任意 MCP 客户端(Claude Desktop、Cursor 等)。
37
+
38
+ ## 功能特性
39
+
40
+ - **全格式输入**:mp3 / wav / m4a / aac / flac / ogg / opus / amr / wma 及 mp4 / mkv / mov 等所有 ffmpeg 支持的音视频格式(视频自动抽取音轨)
41
+ - **微信语音 SILK 原生支持**:自动识别并剥离微信 `0x02 + #!SILK_V3` 文件头,通过 [uv](https://docs.astral.sh/uv/) 临时环境调用 [pilk](https://github.com/foyoux/pilk) 解码(仅转写 silk 文件时需要 uv,其余格式零额外依赖)
42
+ - **说话人分离**:多人对话按 `说话人N:内容` 分段输出;单一说话人自动退化为整段纯文本
43
+ - **长音频免切片**:走百炼录音文件识别(filetrans)异步接口,一次上传整段处理,DashScope 临时存储转完自动删除
44
+ - **清晰的输出约定**:stdout 只输出转写文本,进度 / 耗时 / 计费估算走 stderr,方便脚本和 Agent 调用
45
+ - **内置 MCP Server**:`audio-transcribe-mcp` 命令,手写 MCP stdio 协议(JSON-RPC 2.0),仅标准库零第三方依赖,任意 MCP 客户端即插即用
46
+
47
+ ## 安装
48
+
49
+ ```bash
50
+ pip install audio-transcribe-cli-mcp
51
+ ```
52
+
53
+ 也可从源码安装:
54
+
55
+ ```bash
56
+ git clone https://github.com/OstrichHermit/audio-transcribe.git
57
+ cd audio-transcribe
58
+ pip install .
59
+ ```
60
+
61
+ 安装后会得到两个命令:`asr`(CLI)和 `audio-transcribe-mcp`(MCP Server)。
62
+
63
+ ### 安装为 Agent Skill(可选)
64
+
65
+ 仓库内附带 Agent Skill(`skills/audio-transcribe/SKILL.md`),把它复制到你所用 AI Agent 的 skills 目录,Agent 即可自动掌握本工具的用法。以 Claude Code 为例:
66
+
67
+ ```bash
68
+ cp -r skills/audio-transcribe ~/.claude/skills/audio-transcribe
69
+ ```
70
+
71
+ ### 1. 安装依赖
72
+
73
+ - Python 3.10+(`pip install .` 会自动安装 dashscope 依赖)
74
+ - [ffmpeg / ffprobe](https://ffmpeg.org/) 自行安装并加入 PATH
75
+ - 仅转写微信 SILK 文件时额外需要 [uv](https://docs.astral.sh/uv/)(脚本自动创建 Python 3.11 临时环境安装 pilk,无需手动操作)
76
+
77
+ ### 2. 配置 API Key
78
+
79
+ 在[阿里云百炼控制台](https://bailian.console.aliyun.com/)创建 API Key,配置到环境变量:
80
+
81
+ ```bash
82
+ # Windows
83
+ setx DASHSCOPE_API_KEY "sk-xxxx"
84
+
85
+ # macOS / Linux
86
+ echo 'export DASHSCOPE_API_KEY="sk-xxxx"' >> ~/.bashrc
87
+ ```
88
+
89
+ 默认直连百炼官方主域名,开箱即用。使用业务空间专属 key(`sk-ws-` 前缀)且分配了专属域名的用户,额外设置 `DASHSCOPE_BASE_URL` 即可(见下文[环境变量](#环境变量))。
90
+
91
+ ## 使用方法
92
+
93
+ ```bash
94
+ asr "<音频或视频文件路径>"
95
+ asr "<文件>" --no-speakers # 关闭说话人分离
96
+ asr "<文件>" --out 结果.txt # 保存到文件
97
+ asr "<文件>" --keep-workdir # 保留中间文件(调试)
98
+ ```
99
+
100
+ 示例:
101
+
102
+ ```bash
103
+ $ asr meeting.mp4
104
+ [1/4] ffmpeg 预处理: meeting.mp4
105
+ [2/4] 上传音频(32.5 分钟)...
106
+ [3/4] 提交识别任务...
107
+ 识别模型: qwen-audio-3.1-asr-flash-filetrans
108
+ 识别中... 45s
109
+ [4/4] 完成,耗时 78s,音频 32.5 分钟(计费约 0.06 元,按 3.1 Token 计费估算)
110
+ 说话人1:大家好,今天我们讨论一下项目排期……
111
+ 说话人2:好的,我先说下我这边的情况……
112
+ ```
113
+
114
+ ### 微信语音(SILK)
115
+
116
+ 微信语音消息文件(`*.silk`,含 `0x02 + #!SILK_V3` 头)直接作为输入传入即可,脚本自动完成剥头、pilk 解码(24kHz PCM)→ ffmpeg 封装 WAV → 正常识别管线。
117
+
118
+ ## 作为 MCP Server 接入
119
+
120
+ 任意支持 MCP 的客户端(Claude Code、Claude Desktop、Cursor 等)都能接入:
121
+
122
+ ```bash
123
+ # Claude Code
124
+ claude mcp add audio-transcribe -- audio-transcribe-mcp
125
+ ```
126
+
127
+ 其他客户端在 MCP 配置 JSON 中添加(`env` 里按需配置 API Key,也可继承系统环境变量):
128
+
129
+ ```json
130
+ {
131
+ "mcpServers": {
132
+ "audio-transcribe": {
133
+ "command": "audio-transcribe-mcp",
134
+ "env": {
135
+ "DASHSCOPE_API_KEY": "sk-xxxx"
136
+ }
137
+ }
138
+ }
139
+ }
140
+ ```
141
+
142
+ 也可以用模块方式启动:`python -m audio_transcribe_cli.mcp_server`。
143
+
144
+ 接入后客户端会得到一个 `transcribe` 工具:`file_path` 必填,`speakers`(说话人分离,默认开)和 `out_path`(结果另存)可选。
145
+
146
+ ## 环境变量
147
+
148
+ | 变量 | 说明 |
149
+ |------|------|
150
+ | `DASHSCOPE_API_KEY` | 百炼 API Key(必需) |
151
+ | `DASHSCOPE_BASE_URL` | API 域名(可选)。默认 `https://dashscope.aliyuncs.com/api/v1`(官方主域名,开箱即用);使用业务空间专属域名的用户设置为你的专属域名,例如 `https://xxxx.cn-beijing.maas.aliyuncs.com/api/v1` |
152
+ | `ASR_WORKSPACE` | 中间文件(转码 / 切片临时目录)的根目录(可选)。默认系统临时目录,转写完成自动清理 |
153
+
154
+ ## 计费说明
155
+
156
+ - 主模型 `qwen-audio-3.1-asr-flash-filetrans` 按 Token 计费(输入 25 Token/秒 × 0.8 元/百万 + 输出 2.7 元/百万),约 **0.11 元/小时**音频
157
+ - 识别失败自动降级尝试 `paraformer-v2`
158
+ - 脚本结束会在 stderr 输出本次计费估算
159
+
160
+ ## 已知限制
161
+
162
+ - 同步提交的文件大小受 DashScope 限制,超长音频依赖 filetrans 异步接口(脚本已处理,无需切片)
163
+ - 说话人编号(说话人1 / 说话人2)与真实人物的对应关系需要结合内容自行判断
164
+ - SILK 解码依赖 pilk 的预编译 wheel(cp311 及以下),通过 uv 临时环境解决,首次运行需下载环境,之后走缓存
@@ -0,0 +1,137 @@
1
+ # audio-transcribe
2
+
3
+ **音视频转文字,支持 CLI、MCP Server 两种形态:说话人分离,长音频免切片,原生支持微信语音 SILK 格式。**
4
+
5
+ Audio/video transcription in both CLI and MCP Server flavors: speaker diarization, long-audio support with no manual splitting, and native WeChat SILK decoding.
6
+
7
+ [English](README_EN.md) | 简体中文
8
+
9
+ 基于阿里云百炼录音文件识别接口(qwen-audio-asr 系列 / paraformer)。不依赖 Claude Code:可作为独立命令行工具,也可接入任意 MCP 客户端(Claude Desktop、Cursor 等)。
10
+
11
+ ## 功能特性
12
+
13
+ - **全格式输入**:mp3 / wav / m4a / aac / flac / ogg / opus / amr / wma 及 mp4 / mkv / mov 等所有 ffmpeg 支持的音视频格式(视频自动抽取音轨)
14
+ - **微信语音 SILK 原生支持**:自动识别并剥离微信 `0x02 + #!SILK_V3` 文件头,通过 [uv](https://docs.astral.sh/uv/) 临时环境调用 [pilk](https://github.com/foyoux/pilk) 解码(仅转写 silk 文件时需要 uv,其余格式零额外依赖)
15
+ - **说话人分离**:多人对话按 `说话人N:内容` 分段输出;单一说话人自动退化为整段纯文本
16
+ - **长音频免切片**:走百炼录音文件识别(filetrans)异步接口,一次上传整段处理,DashScope 临时存储转完自动删除
17
+ - **清晰的输出约定**:stdout 只输出转写文本,进度 / 耗时 / 计费估算走 stderr,方便脚本和 Agent 调用
18
+ - **内置 MCP Server**:`audio-transcribe-mcp` 命令,手写 MCP stdio 协议(JSON-RPC 2.0),仅标准库零第三方依赖,任意 MCP 客户端即插即用
19
+
20
+ ## 安装
21
+
22
+ ```bash
23
+ pip install audio-transcribe-cli-mcp
24
+ ```
25
+
26
+ 也可从源码安装:
27
+
28
+ ```bash
29
+ git clone https://github.com/OstrichHermit/audio-transcribe.git
30
+ cd audio-transcribe
31
+ pip install .
32
+ ```
33
+
34
+ 安装后会得到两个命令:`asr`(CLI)和 `audio-transcribe-mcp`(MCP Server)。
35
+
36
+ ### 安装为 Agent Skill(可选)
37
+
38
+ 仓库内附带 Agent Skill(`skills/audio-transcribe/SKILL.md`),把它复制到你所用 AI Agent 的 skills 目录,Agent 即可自动掌握本工具的用法。以 Claude Code 为例:
39
+
40
+ ```bash
41
+ cp -r skills/audio-transcribe ~/.claude/skills/audio-transcribe
42
+ ```
43
+
44
+ ### 1. 安装依赖
45
+
46
+ - Python 3.10+(`pip install .` 会自动安装 dashscope 依赖)
47
+ - [ffmpeg / ffprobe](https://ffmpeg.org/) 自行安装并加入 PATH
48
+ - 仅转写微信 SILK 文件时额外需要 [uv](https://docs.astral.sh/uv/)(脚本自动创建 Python 3.11 临时环境安装 pilk,无需手动操作)
49
+
50
+ ### 2. 配置 API Key
51
+
52
+ 在[阿里云百炼控制台](https://bailian.console.aliyun.com/)创建 API Key,配置到环境变量:
53
+
54
+ ```bash
55
+ # Windows
56
+ setx DASHSCOPE_API_KEY "sk-xxxx"
57
+
58
+ # macOS / Linux
59
+ echo 'export DASHSCOPE_API_KEY="sk-xxxx"' >> ~/.bashrc
60
+ ```
61
+
62
+ 默认直连百炼官方主域名,开箱即用。使用业务空间专属 key(`sk-ws-` 前缀)且分配了专属域名的用户,额外设置 `DASHSCOPE_BASE_URL` 即可(见下文[环境变量](#环境变量))。
63
+
64
+ ## 使用方法
65
+
66
+ ```bash
67
+ asr "<音频或视频文件路径>"
68
+ asr "<文件>" --no-speakers # 关闭说话人分离
69
+ asr "<文件>" --out 结果.txt # 保存到文件
70
+ asr "<文件>" --keep-workdir # 保留中间文件(调试)
71
+ ```
72
+
73
+ 示例:
74
+
75
+ ```bash
76
+ $ asr meeting.mp4
77
+ [1/4] ffmpeg 预处理: meeting.mp4
78
+ [2/4] 上传音频(32.5 分钟)...
79
+ [3/4] 提交识别任务...
80
+ 识别模型: qwen-audio-3.1-asr-flash-filetrans
81
+ 识别中... 45s
82
+ [4/4] 完成,耗时 78s,音频 32.5 分钟(计费约 0.06 元,按 3.1 Token 计费估算)
83
+ 说话人1:大家好,今天我们讨论一下项目排期……
84
+ 说话人2:好的,我先说下我这边的情况……
85
+ ```
86
+
87
+ ### 微信语音(SILK)
88
+
89
+ 微信语音消息文件(`*.silk`,含 `0x02 + #!SILK_V3` 头)直接作为输入传入即可,脚本自动完成剥头、pilk 解码(24kHz PCM)→ ffmpeg 封装 WAV → 正常识别管线。
90
+
91
+ ## 作为 MCP Server 接入
92
+
93
+ 任意支持 MCP 的客户端(Claude Code、Claude Desktop、Cursor 等)都能接入:
94
+
95
+ ```bash
96
+ # Claude Code
97
+ claude mcp add audio-transcribe -- audio-transcribe-mcp
98
+ ```
99
+
100
+ 其他客户端在 MCP 配置 JSON 中添加(`env` 里按需配置 API Key,也可继承系统环境变量):
101
+
102
+ ```json
103
+ {
104
+ "mcpServers": {
105
+ "audio-transcribe": {
106
+ "command": "audio-transcribe-mcp",
107
+ "env": {
108
+ "DASHSCOPE_API_KEY": "sk-xxxx"
109
+ }
110
+ }
111
+ }
112
+ }
113
+ ```
114
+
115
+ 也可以用模块方式启动:`python -m audio_transcribe_cli.mcp_server`。
116
+
117
+ 接入后客户端会得到一个 `transcribe` 工具:`file_path` 必填,`speakers`(说话人分离,默认开)和 `out_path`(结果另存)可选。
118
+
119
+ ## 环境变量
120
+
121
+ | 变量 | 说明 |
122
+ |------|------|
123
+ | `DASHSCOPE_API_KEY` | 百炼 API Key(必需) |
124
+ | `DASHSCOPE_BASE_URL` | API 域名(可选)。默认 `https://dashscope.aliyuncs.com/api/v1`(官方主域名,开箱即用);使用业务空间专属域名的用户设置为你的专属域名,例如 `https://xxxx.cn-beijing.maas.aliyuncs.com/api/v1` |
125
+ | `ASR_WORKSPACE` | 中间文件(转码 / 切片临时目录)的根目录(可选)。默认系统临时目录,转写完成自动清理 |
126
+
127
+ ## 计费说明
128
+
129
+ - 主模型 `qwen-audio-3.1-asr-flash-filetrans` 按 Token 计费(输入 25 Token/秒 × 0.8 元/百万 + 输出 2.7 元/百万),约 **0.11 元/小时**音频
130
+ - 识别失败自动降级尝试 `paraformer-v2`
131
+ - 脚本结束会在 stderr 输出本次计费估算
132
+
133
+ ## 已知限制
134
+
135
+ - 同步提交的文件大小受 DashScope 限制,超长音频依赖 filetrans 异步接口(脚本已处理,无需切片)
136
+ - 说话人编号(说话人1 / 说话人2)与真实人物的对应关系需要结合内容自行判断
137
+ - SILK 解码依赖 pilk 的预编译 wheel(cp311 及以下),通过 uv 临时环境解决,首次运行需下载环境,之后走缓存
@@ -0,0 +1,41 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "audio-transcribe-cli-mcp"
7
+ version = "0.1.0"
8
+ description = "音视频转文字 CLI / MCP Server:阿里云百炼录音文件识别,支持说话人分离与微信语音 SILK"
9
+ readme = "README.md"
10
+ license = { text = "MIT" }
11
+ authors = [{ name = "OstrichHermit" }]
12
+ requires-python = ">=3.10"
13
+ keywords = ["asr", "speech-to-text", "transcription", "dashscope", "qwen", "audio", "video", "mcp", "whisper-alternative"]
14
+ classifiers = [
15
+ "Development Status :: 4 - Beta",
16
+ "Environment :: Console",
17
+ "Intended Audience :: Developers",
18
+ "License :: OSI Approved :: MIT License",
19
+ "Operating System :: OS Independent",
20
+ "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3.10",
22
+ "Programming Language :: Python :: 3.11",
23
+ "Programming Language :: Python :: 3.12",
24
+ "Programming Language :: Python :: 3.13",
25
+ "Topic :: Multimedia :: Sound/Audio :: Speech",
26
+ "Topic :: Utilities",
27
+ ]
28
+ dependencies = [
29
+ "dashscope",
30
+ ]
31
+
32
+ [project.urls]
33
+ Homepage = "https://github.com/OstrichHermit/audio-transcribe"
34
+ Issues = "https://github.com/OstrichHermit/audio-transcribe/issues"
35
+
36
+ [project.scripts]
37
+ asr = "audio_transcribe_cli.cli:main"
38
+ audio-transcribe-mcp = "audio_transcribe_cli.mcp_server:main"
39
+
40
+ [tool.setuptools.packages.find]
41
+ where = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,5 @@
1
+ """audio-transcribe:音视频转文字(阿里云百炼,支持说话人分离)
2
+
3
+ CLI 入口:``asr``;MCP Server 入口:``audio-transcribe-mcp``。
4
+ """
5
+ __version__ = "0.1.0"
@@ -0,0 +1,257 @@
1
+ #!/usr/bin/env python3
2
+ """音视频转文字(带说话人分离):阿里云百炼录音文件识别 + diarization
3
+
4
+ 流程:ffmpeg 统一音频 → DashScope 临时存储上传 → 录音文件识别(说话人分离)→ 轮询取结果
5
+ stdout 输出转写文本,stderr 输出进度信息。
6
+
7
+ 依赖:dashscope SDK(Python314 环境)、ffmpeg、DASHSCOPE_API_KEY 环境变量。
8
+ 微信语音 SILK 格式:自动剥 0x02 前缀 → uv+pilk 解码为 wav(pilk 仅 cp311,走 uv 临时环境)。
9
+ """
10
+ import argparse
11
+ import json
12
+ import os
13
+ import shutil
14
+ import subprocess
15
+ import sys
16
+ import tempfile
17
+ import time
18
+ import urllib.request
19
+ from pathlib import Path
20
+
21
+ import dashscope
22
+ from dashscope.audio.asr import Transcription
23
+
24
+ # DashScope API 域名:默认官方主域名,开箱即用;使用业务空间专属域名(sk-ws- 前缀的
25
+ # 业务空间 key)的用户请设置环境变量 DASHSCOPE_BASE_URL 覆盖
26
+ dashscope.base_http_url = os.environ.get(
27
+ "DASHSCOPE_BASE_URL",
28
+ "https://dashscope.aliyuncs.com/api/v1",
29
+ )
30
+
31
+ # 中间文件存放的临时根目录,默认系统临时目录,可通过 ASR_WORKSPACE 环境变量覆盖
32
+ WORKSPACE = Path(os.environ.get("ASR_WORKSPACE", tempfile.gettempdir()))
33
+ MODEL = "qwen-audio-3.1-asr-flash-filetrans"
34
+ FALLBACK_MODELS = ["paraformer-v2"]
35
+ SUPPORTED_EXTS = {
36
+ ".mp3", ".wav", ".m4a", ".aac", ".flac", ".ogg", ".opus", ".amr", ".wma",
37
+ ".mp4", ".mkv", ".mov", ".avi", ".webm", ".flv", ".ts", ".m4v", ".wmv",
38
+ ".silk",
39
+ }
40
+
41
+ # uv 用于 SILK 解码(pilk 仅提供 cp311 及以下 wheel,本机 Python314 装不上)
42
+ _UV_FALLBACK = Path(r"C:\Users\ASUS\AppData\Local\Programs\Python\Python314\Scripts\uv.exe")
43
+
44
+
45
+ def eprint(*a):
46
+ print(*a, file=sys.stderr, flush=True)
47
+
48
+
49
+ def run(cmd):
50
+ r = subprocess.run([str(c) for c in cmd], capture_output=True, text=True,
51
+ encoding="utf-8", errors="replace")
52
+ if r.returncode != 0:
53
+ raise RuntimeError(f"命令失败: {' '.join(str(c) for c in cmd)}\n{r.stderr[-2000:]}")
54
+ return r.stdout.strip()
55
+
56
+
57
+ def prepare_audio(src: Path, workdir: Path) -> Path:
58
+ """视频抽音轨、统一转 16kHz 单声道 mp3(小体积、通用格式)"""
59
+ audio = workdir / "audio.mp3"
60
+ run(["ffmpeg", "-y", "-hide_banner", "-loglevel", "error", "-i", src,
61
+ "-vn", "-ac", "1", "-ar", "16000", "-b:a", "64k", audio])
62
+ return audio
63
+
64
+
65
+ def decode_silk(src: Path, workdir: Path) -> Path:
66
+ """微信语音 SILK 解码为 wav:剥 0x02 前缀 → pilk 解码 24kHz mono pcm → ffmpeg 封装
67
+
68
+ 微信 silk 头部为 0x02 + "#!SILK_V3";pilk.decode 输出 24kHz 16bit 单声道 pcm。
69
+ """
70
+ data = src.read_bytes()
71
+ if data[:10] == b"\x02#!SILK_V3":
72
+ data = data[1:]
73
+ if data[:9] != b"#!SILK_V3":
74
+ raise RuntimeError("不是有效的 SILK 文件(头部不匹配 #!SILK_V3)")
75
+ clean_silk = workdir / "clean.silk"
76
+ clean_silk.write_bytes(data)
77
+
78
+ uv_exe = shutil.which("uv") or (_UV_FALLBACK if _UV_FALLBACK.exists() else None)
79
+ if uv_exe is None:
80
+ raise RuntimeError("未找到 uv 可执行文件,无法解码 SILK")
81
+ eprint(" SILK 格式:pilk 解码中(uv 临时环境,首次运行较慢)...")
82
+ pcm = workdir / "audio.pcm"
83
+ code = f"import pilk\npilk.decode(r'{clean_silk}', r'{pcm}')\n"
84
+ run([uv_exe, "run", "--python", "3.11", "--with", "pilk", "python", "-c", code])
85
+ if not pcm.exists() or pcm.stat().st_size == 0:
86
+ raise RuntimeError("SILK 解码失败:pcm 输出为空")
87
+
88
+ wav = workdir / "silk_audio.wav"
89
+ run(["ffmpeg", "-y", "-hide_banner", "-loglevel", "error",
90
+ "-f", "s16le", "-ar", "24000", "-ac", "1", "-i", pcm, wav])
91
+ return wav
92
+
93
+
94
+ def upload_to_dashscope(audio: Path):
95
+ """上传到 DashScope 托管存储,返回 (file_id, 签名下载URL)"""
96
+ f = dashscope.Files.upload(file_path=str(audio), purpose="file-extract",
97
+ api_key=dashscope.api_key)
98
+ if f.status_code != 200:
99
+ raise RuntimeError(f"上传失败: {f.code} {f.message}")
100
+ file_id = f.output["uploaded_files"][0]["file_id"]
101
+ g = dashscope.Files.get(file_id=file_id, api_key=dashscope.api_key)
102
+ if g.status_code != 200:
103
+ raise RuntimeError(f"获取下载链接失败: {g.code} {g.message}")
104
+ return file_id, g.output["url"]
105
+
106
+
107
+ def submit_task(url: str):
108
+ """提交识别任务,依次尝试主模型和备选模型"""
109
+ models = [MODEL] + FALLBACK_MODELS
110
+ last = None
111
+ for model in models:
112
+ try:
113
+ resp = Transcription.async_call(model=model, file_urls=[url],
114
+ diarization_enabled=True)
115
+ except Exception as ex:
116
+ last = f"{model}: {ex}"
117
+ continue
118
+ if resp.status_code == 200:
119
+ eprint(f"识别模型: {model}")
120
+ return model, resp.output.task_id
121
+ last = f"{model}: {resp.status_code} {resp.code} {resp.message}"
122
+ raise RuntimeError(f"所有识别模型提交失败: {last}")
123
+
124
+
125
+ def wait_result(task_id: str, audio_seconds: float, timeout: float = None):
126
+ """轮询任务直到完成。filetrans 排队+识别一般数十秒到数分钟"""
127
+ if timeout is None:
128
+ timeout = max(180, min(audio_seconds * 2, 3600))
129
+ t0 = time.time()
130
+ while time.time() - t0 < timeout:
131
+ time.sleep(3)
132
+ r = Transcription.fetch(task=task_id)
133
+ if r.output.task_status == "SUCCEEDED":
134
+ t_url = getattr(r.output, "transcription_url", None)
135
+ if not t_url:
136
+ t_url = r.output["results"][0]["transcription_url"]
137
+ with urllib.request.urlopen(t_url, timeout=60) as fh:
138
+ return json.load(fh)
139
+ if r.output.task_status == "FAILED":
140
+ code = ""
141
+ msg = ""
142
+ try:
143
+ code = r.output["code"]
144
+ msg = r.output["message"]
145
+ except Exception:
146
+ pass
147
+ raise RuntimeError(f"识别任务失败: {code} {msg}")
148
+ eprint(f" 识别中... {time.time() - t0:.0f}s")
149
+ raise RuntimeError("等待识别结果超时")
150
+
151
+
152
+ def format_text(data: dict, with_speakers: bool) -> str:
153
+ """按说话人分段输出;若只有单一说话人则输出整段纯文本"""
154
+ transcripts = data.get("transcripts", [])
155
+ if not with_speakers:
156
+ return "\n".join(tr.get("text", "").strip() for tr in transcripts if tr.get("text"))
157
+
158
+ speakers_used = set()
159
+ all_sentences = []
160
+ for tr in transcripts:
161
+ all_sentences.extend(tr.get("sentences", []))
162
+ for s in tr.get("sentences", []):
163
+ if s.get("speaker_id") is not None:
164
+ speakers_used.add(s["speaker_id"])
165
+
166
+ if len(speakers_used) < 2:
167
+ return "\n".join(tr.get("text", "").strip() for tr in transcripts if tr.get("text"))
168
+
169
+ lines = []
170
+ cur_spk, buf = None, []
171
+ for s in all_sentences:
172
+ spk = s.get("speaker_id")
173
+ if spk != cur_spk:
174
+ if buf:
175
+ lines.append(f"说话人{cur_spk}:{''.join(buf)}")
176
+ cur_spk, buf = spk, []
177
+ buf.append(s["text"])
178
+ if buf:
179
+ lines.append(f"说话人{cur_spk}:{''.join(buf)}")
180
+ return "\n".join(lines)
181
+
182
+
183
+ def transcribe_file(src: Path, with_speakers: bool = True, out: str = None,
184
+ keep_workdir: bool = False) -> str:
185
+ """核心流程:预处理 → 上传 → 识别 → 格式化,返回转写文本(CLI 与 MCP 共用)"""
186
+ tmp_root = WORKSPACE / "temp"
187
+ tmp_root.mkdir(parents=True, exist_ok=True)
188
+ workdir = Path(tempfile.mkdtemp(prefix="asr_", dir=tmp_root))
189
+ file_id = None
190
+ t0 = time.time()
191
+
192
+ try:
193
+ eprint(f"[1/4] ffmpeg 预处理: {src.name}")
194
+ if src.suffix.lower() == ".silk":
195
+ src = decode_silk(src, workdir)
196
+ audio = prepare_audio(src, workdir)
197
+ dur_out = run(["ffprobe", "-v", "error", "-show_entries", "format=duration",
198
+ "-of", "default=nw=1:nk=1", audio])
199
+ dur = float(dur_out)
200
+ eprint(f"[2/4] 上传音频({dur / 60:.1f} 分钟)...")
201
+ file_id, dl_url = upload_to_dashscope(audio)
202
+
203
+ eprint("[3/4] 提交识别任务...")
204
+ _, task_id = submit_task(dl_url)
205
+ result = wait_result(task_id, dur)
206
+
207
+ text = format_text(result, with_speakers=with_speakers)
208
+ eprint(f"[4/4] 完成,耗时 {time.time() - t0:.0f}s,音频 {dur / 60:.1f} 分钟"
209
+ f"(计费约 {dur / 3600 * 0.11:.2f} 元,按 3.1 Token 计费估算)")
210
+
211
+ if out:
212
+ out_path = Path(out).expanduser().resolve()
213
+ out_path.parent.mkdir(parents=True, exist_ok=True)
214
+ out_path.write_text(text + "\n", encoding="utf-8")
215
+ eprint(f"已保存: {out_path}")
216
+ return text
217
+ finally:
218
+ if file_id:
219
+ try:
220
+ dashscope.Files.delete(file_id=file_id, api_key=dashscope.api_key)
221
+ except Exception:
222
+ pass
223
+ if keep_workdir:
224
+ eprint(f"中间文件保留: {workdir}")
225
+ else:
226
+ shutil.rmtree(workdir, ignore_errors=True)
227
+
228
+
229
+ def main():
230
+ ap = argparse.ArgumentParser(description="音视频转文字(阿里云百炼,支持说话人分离)")
231
+ ap.add_argument("input", help="音频或视频文件路径")
232
+ ap.add_argument("--no-speakers", action="store_true", help="关闭说话人分离,输出纯文本")
233
+ ap.add_argument("--out", default=None, help="结果保存路径(默认只打印)")
234
+ ap.add_argument("--keep-workdir", action="store_true", help="保留中间文件(调试用)")
235
+ args = ap.parse_args()
236
+
237
+ if sys.stdout.encoding and sys.stdout.encoding.lower() != "utf-8":
238
+ sys.stdout.reconfigure(encoding="utf-8")
239
+ sys.stderr.reconfigure(encoding="utf-8")
240
+
241
+ src = Path(args.input).expanduser().resolve()
242
+ if not src.exists():
243
+ eprint(f"文件不存在: {src}")
244
+ sys.exit(1)
245
+ if src.suffix.lower() not in SUPPORTED_EXTS:
246
+ eprint(f"不支持的格式: {src.suffix}")
247
+ sys.exit(1)
248
+ if not os.environ.get("DASHSCOPE_API_KEY"):
249
+ eprint("未设置 DASHSCOPE_API_KEY 环境变量")
250
+ sys.exit(1)
251
+
252
+ print(transcribe_file(src, with_speakers=not args.no_speakers,
253
+ out=args.out, keep_workdir=args.keep_workdir))
254
+
255
+
256
+ if __name__ == "__main__":
257
+ main()
@@ -0,0 +1,134 @@
1
+ #!/usr/bin/env python3
2
+ """MCP stdio server:音视频转文字工具
3
+
4
+ 手写 MCP 协议(newline-delimited JSON-RPC 2.0),仅用标准库,无第三方 MCP 依赖。
5
+ 复用包内 cli 模块的转写流程。进度与日志全部输出到 stderr,
6
+ stdout 仅输出协议消息。
7
+
8
+ 依赖:dashscope SDK、ffmpeg、DASHSCOPE_API_KEY 环境变量(同 cli 模块)。
9
+ """
10
+ import json
11
+ import os
12
+ import sys
13
+ from pathlib import Path
14
+
15
+ from . import cli as transcribe
16
+
17
+ PROTOCOL_VERSION = "2024-11-05"
18
+ SERVER_INFO = {"name": "audio-transcribe", "version": "1.0.0"}
19
+
20
+ TOOLS = [
21
+ {
22
+ "name": "transcribe",
23
+ "description": (
24
+ "音视频转文字,支持说话人分离。支持 mp3/wav/m4a/flac/ogg/aac/amr/wma、"
25
+ "mp4/mkv/mov/avi/webm/flv/ts 等常见音视频格式及微信语音 silk。"
26
+ "返回转写文本,多说话人音频按说话人分段(说话人0/1/2...)。"
27
+ ),
28
+ "inputSchema": {
29
+ "type": "object",
30
+ "properties": {
31
+ "file_path": {"type": "string", "description": "音频或视频文件路径"},
32
+ "speakers": {
33
+ "type": "boolean",
34
+ "description": "是否启用说话人分离(默认 true,单说话人时输出纯文本)",
35
+ "default": True,
36
+ },
37
+ "out_path": {"type": "string", "description": "可选,结果另存为此路径"},
38
+ },
39
+ "required": ["file_path"],
40
+ },
41
+ }
42
+ ]
43
+
44
+
45
+ def log(*a):
46
+ print(*a, file=sys.stderr, flush=True)
47
+
48
+
49
+ def write(obj):
50
+ sys.stdout.write(json.dumps(obj, ensure_ascii=False) + "\n")
51
+ sys.stdout.flush()
52
+
53
+
54
+ def reply(msg_id, result):
55
+ write({"jsonrpc": "2.0", "id": msg_id, "result": result})
56
+
57
+
58
+ def reply_error(msg_id, code, message):
59
+ write({"jsonrpc": "2.0", "id": msg_id, "error": {"code": code, "message": message}})
60
+
61
+
62
+ def run_tool(args) -> str:
63
+ if "file_path" not in args:
64
+ raise ValueError("缺少必填参数: file_path")
65
+ src = Path(str(args["file_path"])).expanduser().resolve()
66
+ if not src.exists():
67
+ raise FileNotFoundError(f"文件不存在: {src}")
68
+ if src.suffix.lower() not in transcribe.SUPPORTED_EXTS:
69
+ raise ValueError(f"不支持的格式: {src.suffix}")
70
+ if not os.environ.get("DASHSCOPE_API_KEY"):
71
+ raise RuntimeError("未设置 DASHSCOPE_API_KEY 环境变量")
72
+ return transcribe.transcribe_file(
73
+ src,
74
+ with_speakers=bool(args.get("speakers", True)),
75
+ out=args.get("out_path"),
76
+ )
77
+
78
+
79
+ def handle(msg: dict):
80
+ method = msg.get("method")
81
+ msg_id = msg.get("id")
82
+ params = msg.get("params") or {}
83
+
84
+ if method == "initialize":
85
+ requested = params.get("protocolVersion")
86
+ version = requested if isinstance(requested, str) else PROTOCOL_VERSION
87
+ reply(msg_id, {
88
+ "protocolVersion": version,
89
+ "capabilities": {"tools": {}},
90
+ "serverInfo": SERVER_INFO,
91
+ })
92
+ elif method == "notifications/initialized" or method == "notifications/cancelled":
93
+ pass
94
+ elif method == "tools/list":
95
+ reply(msg_id, {"tools": TOOLS})
96
+ elif method == "tools/call":
97
+ name = params.get("name")
98
+ args = params.get("arguments") or {}
99
+ if name != "transcribe":
100
+ reply_error(msg_id, -32602, f"未知工具: {name}")
101
+ return
102
+ try:
103
+ text = run_tool(args)
104
+ reply(msg_id, {"content": [{"type": "text", "text": text}]})
105
+ except Exception as ex:
106
+ reply(msg_id, {
107
+ "content": [{"type": "text", "text": f"转写失败: {ex}"}],
108
+ "isError": True,
109
+ })
110
+ elif method == "ping":
111
+ reply(msg_id, {})
112
+ elif msg_id is not None:
113
+ reply_error(msg_id, -32601, f"方法不存在: {method}")
114
+
115
+
116
+ def main():
117
+ if sys.stdout.encoding and sys.stdout.encoding.lower() != "utf-8":
118
+ sys.stdout.reconfigure(encoding="utf-8")
119
+ sys.stderr.reconfigure(encoding="utf-8")
120
+ log(f"{SERVER_INFO['name']} v{SERVER_INFO['version']} 已启动(stdio,等待客户端)")
121
+ for line in sys.stdin:
122
+ line = line.strip()
123
+ if not line:
124
+ continue
125
+ try:
126
+ msg = json.loads(line)
127
+ except json.JSONDecodeError as ex:
128
+ reply_error(None, -32700, f"JSON 解析失败: {ex}")
129
+ continue
130
+ handle(msg)
131
+
132
+
133
+ if __name__ == "__main__":
134
+ main()
@@ -0,0 +1,164 @@
1
+ Metadata-Version: 2.4
2
+ Name: audio-transcribe-cli-mcp
3
+ Version: 0.1.0
4
+ Summary: 音视频转文字 CLI / MCP Server:阿里云百炼录音文件识别,支持说话人分离与微信语音 SILK
5
+ Author: OstrichHermit
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/OstrichHermit/audio-transcribe
8
+ Project-URL: Issues, https://github.com/OstrichHermit/audio-transcribe/issues
9
+ Keywords: asr,speech-to-text,transcription,dashscope,qwen,audio,video,mcp,whisper-alternative
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Environment :: Console
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Topic :: Multimedia :: Sound/Audio :: Speech
21
+ Classifier: Topic :: Utilities
22
+ Requires-Python: >=3.10
23
+ Description-Content-Type: text/markdown
24
+ License-File: LICENSE
25
+ Requires-Dist: dashscope
26
+ Dynamic: license-file
27
+
28
+ # audio-transcribe
29
+
30
+ **音视频转文字,支持 CLI、MCP Server 两种形态:说话人分离,长音频免切片,原生支持微信语音 SILK 格式。**
31
+
32
+ Audio/video transcription in both CLI and MCP Server flavors: speaker diarization, long-audio support with no manual splitting, and native WeChat SILK decoding.
33
+
34
+ [English](README_EN.md) | 简体中文
35
+
36
+ 基于阿里云百炼录音文件识别接口(qwen-audio-asr 系列 / paraformer)。不依赖 Claude Code:可作为独立命令行工具,也可接入任意 MCP 客户端(Claude Desktop、Cursor 等)。
37
+
38
+ ## 功能特性
39
+
40
+ - **全格式输入**:mp3 / wav / m4a / aac / flac / ogg / opus / amr / wma 及 mp4 / mkv / mov 等所有 ffmpeg 支持的音视频格式(视频自动抽取音轨)
41
+ - **微信语音 SILK 原生支持**:自动识别并剥离微信 `0x02 + #!SILK_V3` 文件头,通过 [uv](https://docs.astral.sh/uv/) 临时环境调用 [pilk](https://github.com/foyoux/pilk) 解码(仅转写 silk 文件时需要 uv,其余格式零额外依赖)
42
+ - **说话人分离**:多人对话按 `说话人N:内容` 分段输出;单一说话人自动退化为整段纯文本
43
+ - **长音频免切片**:走百炼录音文件识别(filetrans)异步接口,一次上传整段处理,DashScope 临时存储转完自动删除
44
+ - **清晰的输出约定**:stdout 只输出转写文本,进度 / 耗时 / 计费估算走 stderr,方便脚本和 Agent 调用
45
+ - **内置 MCP Server**:`audio-transcribe-mcp` 命令,手写 MCP stdio 协议(JSON-RPC 2.0),仅标准库零第三方依赖,任意 MCP 客户端即插即用
46
+
47
+ ## 安装
48
+
49
+ ```bash
50
+ pip install audio-transcribe-cli-mcp
51
+ ```
52
+
53
+ 也可从源码安装:
54
+
55
+ ```bash
56
+ git clone https://github.com/OstrichHermit/audio-transcribe.git
57
+ cd audio-transcribe
58
+ pip install .
59
+ ```
60
+
61
+ 安装后会得到两个命令:`asr`(CLI)和 `audio-transcribe-mcp`(MCP Server)。
62
+
63
+ ### 安装为 Agent Skill(可选)
64
+
65
+ 仓库内附带 Agent Skill(`skills/audio-transcribe/SKILL.md`),把它复制到你所用 AI Agent 的 skills 目录,Agent 即可自动掌握本工具的用法。以 Claude Code 为例:
66
+
67
+ ```bash
68
+ cp -r skills/audio-transcribe ~/.claude/skills/audio-transcribe
69
+ ```
70
+
71
+ ### 1. 安装依赖
72
+
73
+ - Python 3.10+(`pip install .` 会自动安装 dashscope 依赖)
74
+ - [ffmpeg / ffprobe](https://ffmpeg.org/) 自行安装并加入 PATH
75
+ - 仅转写微信 SILK 文件时额外需要 [uv](https://docs.astral.sh/uv/)(脚本自动创建 Python 3.11 临时环境安装 pilk,无需手动操作)
76
+
77
+ ### 2. 配置 API Key
78
+
79
+ 在[阿里云百炼控制台](https://bailian.console.aliyun.com/)创建 API Key,配置到环境变量:
80
+
81
+ ```bash
82
+ # Windows
83
+ setx DASHSCOPE_API_KEY "sk-xxxx"
84
+
85
+ # macOS / Linux
86
+ echo 'export DASHSCOPE_API_KEY="sk-xxxx"' >> ~/.bashrc
87
+ ```
88
+
89
+ 默认直连百炼官方主域名,开箱即用。使用业务空间专属 key(`sk-ws-` 前缀)且分配了专属域名的用户,额外设置 `DASHSCOPE_BASE_URL` 即可(见下文[环境变量](#环境变量))。
90
+
91
+ ## 使用方法
92
+
93
+ ```bash
94
+ asr "<音频或视频文件路径>"
95
+ asr "<文件>" --no-speakers # 关闭说话人分离
96
+ asr "<文件>" --out 结果.txt # 保存到文件
97
+ asr "<文件>" --keep-workdir # 保留中间文件(调试)
98
+ ```
99
+
100
+ 示例:
101
+
102
+ ```bash
103
+ $ asr meeting.mp4
104
+ [1/4] ffmpeg 预处理: meeting.mp4
105
+ [2/4] 上传音频(32.5 分钟)...
106
+ [3/4] 提交识别任务...
107
+ 识别模型: qwen-audio-3.1-asr-flash-filetrans
108
+ 识别中... 45s
109
+ [4/4] 完成,耗时 78s,音频 32.5 分钟(计费约 0.06 元,按 3.1 Token 计费估算)
110
+ 说话人1:大家好,今天我们讨论一下项目排期……
111
+ 说话人2:好的,我先说下我这边的情况……
112
+ ```
113
+
114
+ ### 微信语音(SILK)
115
+
116
+ 微信语音消息文件(`*.silk`,含 `0x02 + #!SILK_V3` 头)直接作为输入传入即可,脚本自动完成剥头、pilk 解码(24kHz PCM)→ ffmpeg 封装 WAV → 正常识别管线。
117
+
118
+ ## 作为 MCP Server 接入
119
+
120
+ 任意支持 MCP 的客户端(Claude Code、Claude Desktop、Cursor 等)都能接入:
121
+
122
+ ```bash
123
+ # Claude Code
124
+ claude mcp add audio-transcribe -- audio-transcribe-mcp
125
+ ```
126
+
127
+ 其他客户端在 MCP 配置 JSON 中添加(`env` 里按需配置 API Key,也可继承系统环境变量):
128
+
129
+ ```json
130
+ {
131
+ "mcpServers": {
132
+ "audio-transcribe": {
133
+ "command": "audio-transcribe-mcp",
134
+ "env": {
135
+ "DASHSCOPE_API_KEY": "sk-xxxx"
136
+ }
137
+ }
138
+ }
139
+ }
140
+ ```
141
+
142
+ 也可以用模块方式启动:`python -m audio_transcribe_cli.mcp_server`。
143
+
144
+ 接入后客户端会得到一个 `transcribe` 工具:`file_path` 必填,`speakers`(说话人分离,默认开)和 `out_path`(结果另存)可选。
145
+
146
+ ## 环境变量
147
+
148
+ | 变量 | 说明 |
149
+ |------|------|
150
+ | `DASHSCOPE_API_KEY` | 百炼 API Key(必需) |
151
+ | `DASHSCOPE_BASE_URL` | API 域名(可选)。默认 `https://dashscope.aliyuncs.com/api/v1`(官方主域名,开箱即用);使用业务空间专属域名的用户设置为你的专属域名,例如 `https://xxxx.cn-beijing.maas.aliyuncs.com/api/v1` |
152
+ | `ASR_WORKSPACE` | 中间文件(转码 / 切片临时目录)的根目录(可选)。默认系统临时目录,转写完成自动清理 |
153
+
154
+ ## 计费说明
155
+
156
+ - 主模型 `qwen-audio-3.1-asr-flash-filetrans` 按 Token 计费(输入 25 Token/秒 × 0.8 元/百万 + 输出 2.7 元/百万),约 **0.11 元/小时**音频
157
+ - 识别失败自动降级尝试 `paraformer-v2`
158
+ - 脚本结束会在 stderr 输出本次计费估算
159
+
160
+ ## 已知限制
161
+
162
+ - 同步提交的文件大小受 DashScope 限制,超长音频依赖 filetrans 异步接口(脚本已处理,无需切片)
163
+ - 说话人编号(说话人1 / 说话人2)与真实人物的对应关系需要结合内容自行判断
164
+ - SILK 解码依赖 pilk 的预编译 wheel(cp311 及以下),通过 uv 临时环境解决,首次运行需下载环境,之后走缓存
@@ -0,0 +1,12 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ src/audio_transcribe_cli/__init__.py
5
+ src/audio_transcribe_cli/cli.py
6
+ src/audio_transcribe_cli/mcp_server.py
7
+ src/audio_transcribe_cli_mcp.egg-info/PKG-INFO
8
+ src/audio_transcribe_cli_mcp.egg-info/SOURCES.txt
9
+ src/audio_transcribe_cli_mcp.egg-info/dependency_links.txt
10
+ src/audio_transcribe_cli_mcp.egg-info/entry_points.txt
11
+ src/audio_transcribe_cli_mcp.egg-info/requires.txt
12
+ src/audio_transcribe_cli_mcp.egg-info/top_level.txt
@@ -0,0 +1,3 @@
1
+ [console_scripts]
2
+ asr = audio_transcribe_cli.cli:main
3
+ audio-transcribe-mcp = audio_transcribe_cli.mcp_server:main