AI 工具

Descript API:导出项目转写文本

官方能力:导出项目转写为 txt/markdown/html/rtf/docx/srt。教学客户端对 POST /v1/export/transcript 发 JSON(projectid+format),保存响应正文。

BINBHO · 图文教程

官方能力:导出项目转写为 txt/markdown/html/rtf/docx/srt。教学客户端对 POST /v1/export/transcript 发 JSON(project_id+format),保存响应正文。

先看清成品

  • mock 导出含 Speaker 文本与 X-Composition-Id。
  • 1 项 pytest 通过。

合规提醒:Overdub / 声音克隆仅限本人或已获明确授权并完成平台验证的声音,禁止冒充他人。API token 绑定单一 Drive,可消耗 media minutes 与 AI credits;耗尽可能返回 402。导入勿用未授权媒体;YouTube URL 不作 import 源(官方限制)。本文不编造套餐价格。

运行说明:无正式 DESCRIPT_API_TOKEN。A 用 mock 导出转写;B 本地口头禅扫描;C Overdub 同意门禁。未调用真实 Descript API。2026-10-23(Asia/Shanghai)pytest 通过。

做完之后你会得到什么?

成果 说明
export_transcript.py 导出客户端
mock_descript.py 假服务
test_110a.py 测试

需要准备哪些环境?

项目 说明
Python 3.10+
环境变量 DESCRIPT_API_TOKEN;可选 DESCRIPT_BASE_URL

关键事实

事实 来源
Token 在 App Settings → API tokens 创建,绑定一个 Drive 官方 Help
导出格式含 srt 等;响应可带 X-Composition-Id 官方 API 文档
无发布则不能仅靠 API 下载成片媒体文件 官方 Known limitations

文件结构

110-descript-A-src/
├── export_transcript.py
├── mock_descript.py
└── test_110a.py

第 1 步:客户端

#!/usr/bin/env python3
"""Descript API: export project transcript (txt/markdown/html/rtf/docx/srt)."""
from __future__ import annotations
import argparse, json, os, sys, urllib.error, urllib.request

FORMATS = {"txt", "markdown", "html", "rtf", "docx", "srt"}

def export(base, token, project_id, fmt="txt", speaker_labels="changes"):
    if fmt not in FORMATS:
        raise SystemExit(f"format 须为 {sorted(FORMATS)}")
    payload = {"project_id": project_id, "format": fmt, "include_speaker_labels": speaker_labels}
    req = urllib.request.Request(
        f"{base.rstrip('/')}/v1/export/transcript",
        data=json.dumps(payload).encode(),
        headers={"Authorization": f"Bearer {token}", "Content-Type": "application/json"},
        method="POST",
    )
    try:
        with urllib.request.urlopen(req, timeout=120) as resp:
            data = resp.read()
            return {"composition_id": resp.headers.get("X-Composition-Id"), "body": data, "content_type": resp.headers.get("Content-Type")}
    except urllib.error.HTTPError as e:
        raise SystemExit(f"HTTP {e.code}: {e.read().decode(errors='replace')}") from e

def main(argv=None) -> int:
    ap = argparse.ArgumentParser(description=__doc__)
    ap.add_argument("project_id")
    ap.add_argument("--format", default="txt", choices=sorted(FORMATS))
    ap.add_argument("-o", "--out", default="transcript.txt")
    ap.add_argument("--base", default=os.environ.get("DESCRIPT_BASE_URL", "https://descriptapi.com"))
    ap.add_argument("--token", default=os.environ.get("DESCRIPT_API_TOKEN", ""))
    args = ap.parse_args(argv)
    if not args.token:
        print("设置 DESCRIPT_API_TOKEN(App Settings → API tokens,绑定单一 Drive)", file=sys.stderr); return 2
    r = export(args.base, args.token, args.project_id, args.format)
    with open(args.out, "wb") as f:
        f.write(r["body"])
    print(json.dumps({"out": args.out, "composition_id": r["composition_id"], "bytes": len(r["body"])}, ensure_ascii=False))
    return 0

if __name__ == "__main__":
    raise SystemExit(main())

第 2 步:Mock

#!/usr/bin/env python3
"""Mock Descript transcript export API."""
from __future__ import annotations
import json
from http.server import BaseHTTPRequestHandler, HTTPServer

TOKEN = "descript-test-token"

class H(BaseHTTPRequestHandler):
    def log_message(self, *a):
        pass
    def do_POST(self):
        if self.path != "/v1/export/transcript":
            self.send_error(404); return
        auth = self.headers.get("Authorization", "")
        if auth != f"Bearer {TOKEN}":
            self.send_response(401); self.end_headers(); return
        n = int(self.headers.get("Content-Length", 0))
        body = json.loads(self.rfile.read(n) or b"{}")
        fmt = body.get("format") or "txt"
        text = "Speaker 1: Hello, welcome to the show.\n\nSpeaker 2: Thanks for having me.\n"
        if fmt == "srt":
            text = "1\n00:00:00,000 --> 00:00:02,000\nHello, welcome to the show.\n"
        elif fmt == "markdown":
            text = "# Transcript\n\n**Speaker 1:** Hello\n"
        data = text.encode()
        self.send_response(200)
        self.send_header("Content-Type", "text/plain; charset=utf-8")
        self.send_header("Content-Disposition", "attachment; filename=transcript.txt")
        self.send_header("X-Composition-Id", "comp-mock-001")
        self.send_header("Content-Length", str(len(data)))
        self.end_headers()
        self.wfile.write(data)

def main():
    import argparse
    ap = argparse.ArgumentParser(); ap.add_argument("--port", type=int, default=9111)
    args = ap.parse_args()
    HTTPServer(("127.0.0.1", args.port), H).serve_forever()

if __name__ == "__main__":
    main()

第 3 步:测试

import subprocess, sys, time
from pathlib import Path
HERE = Path(__file__).resolve().parent

def test_export():
    port = 19200
    proc = subprocess.Popen([sys.executable, str(HERE/"mock_descript.py"), "--port", str(port)], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
    try:
        time.sleep(0.3)
        out = HERE / "_t.txt"
        r = subprocess.run([sys.executable, str(HERE/"export_transcript.py"), "proj-1", "-o", str(out), "--base", f"http://127.0.0.1:{port}", "--token", "descript-test-token"], capture_output=True, text=True, check=True)
        assert out.exists() and "Speaker" in out.read_text()
        assert "composition_id" in r.stdout
    finally:
        proc.terminate(); proc.wait(timeout=5)
        if (HERE/"_t.txt").exists():
            (HERE/"_t.txt").unlink()

第 4 步:运行

$ pytest export
.                                                                        [100%]
1 passed in 0.36s

怎么检查结果?

  • 输出文件含台词。
  • 正式 base/path 以当前 OpenAPI 为准。

调试示例

402。 媒体分钟或 AI credits 用尽。到账户查看用量后再试;导入与 Underlord 都会耗额度。

常见问题

现象 原因 处理
端点路径变了 对照 dev-docs 改客户端
docx 二进制 用 -o out.docx
错 Drive token 绑死 Drive 换 token

模板规则

  • token 保密。
  • 导出稿仍要隐私审查。
  • 正式路径随文档更新。

项目资源

用途 地址
Descript API Help(官方) https://help.descript.com/api-and-mcp/api
API 文档(官方) https://dev-docs.descriptapi.com/
descriptapi.com https://descriptapi.com/

文字导出通了,文字剪辑工作流才接得上。