Skip to main content
Glama

ossicle

Транскрибируйте локальные медиафайлы и URL-адреса с помощью Deepgram, как MCP-сервер для Claude и как автономный CLI. Расшифровки сохраняются на диск в формате Markdown; ничего объёмного никогда не возвращается инлайн.

Каждый заказ оценивается по измеренной длительности до отправки, и заказ, чья оценка превышает установленный предел стоимости, отклоняется сразу. Именно эта защита и есть суть пакета.

Требования

  • Node >= 20

  • ffprobe и ffmpeg в PATH (измерение длительности и загрузка в моно 16 кГц opus)

  • yt-dlp в PATH, если нужен ввод по URL

  • Ключ API Deepgram

Related MCP server: whipscribe-mcp

Установка

npm install
npm run build
cp .env.example .env    # then fill in DEEPGRAM_API_KEY

Конфигурация

Конфигурация читается только из файла .env в корне пакета. Переменные окружения, переданные из шелла, и флаги claude mcp add --env намеренно игнорируются, так что сервер ведёт себя одинаково независимо от того, из какого проекта он запущен.

Переменная

Обязательная

По умолчанию

Назначение

DEEPGRAM_API_KEY

да

Ключ API Deepgram

DEEPGRAM_MODEL

нет

nova-3

Модель транскрипции

DEEPGRAM_USD_PER_MINUTE

нет

0.0043

Цена за минуту аудио, используется для оценки

MAX_COST_PER_JOB_USD

нет

1.00

Жёсткий предел стоимости заказа. Превышение — отказ, а не запрос

TRANSCRIPTION_OUTPUT_DIR

нет

./output

Куда пишутся папки заказов. Относительные пути разрешаются от корня пакета

OPENROUTER_API_KEY

только для форматирования

Ключ для прохода форматирования. Транскрипции он не нужен

OPENROUTER_MODEL

нет

openai/gpt-4o-mini

Модель, которую проход форматирования просит о структуре

FORMAT_HEADINGS_MIN_SENTENCES

нет

120

Ниже этого числа предложений форматирование добавляет абзацы и теги, но не заголовки

MCP-сервер

claude mcp add ossicle -- node "<absolute path to this repo>/dist/index.js"

transcribe

Вход

Тип

По умолчанию

Примечания

source

string

обязательно

Локальный путь к файлу или любой URL, который умеет скачивать yt-dlp

diarize

boolean

false

Экспериментально. Блоки с метками говорящих ## Speaker N [mm:ss]

model

string

модель из конфига

Переопределение модели Deepgram

language

string

en

Код языка речи

force

boolean

false

Повторная транскрипция даже при попадании в кэш. Снова стоит денег

Возвращает путь к расшифровке, папку заказа, длительность, фактически потраченные и оценочные USD, флаг cached и превью длиной не более 500 символов. Полная расшифровка остаётся на диске.

Диаризация

Диаризация экспериментальна и по умолчанию выключена. На реальных записях Deepgram часто ошибается в приписывании реплик, так что вывод с метками говорящих читается хуже, чем простые абзацы. Поэтому флаг оставлен для тех случаев, где разделение по говорящим стоит этого риска, а не рекомендован как обычный режим. Он остаётся частью ключа кэша, так что его переключение никогда не вернёт устаревшую расшифровку.

format_transcript

Вход

Тип

По умолчанию

Примечания

target

string

обязательно

job_dir из результата transcribe или исходный локальный путь к файлу

force

boolean

false

Повторный запрос структуры у модели. Снова стоит денег

Второй, необязательный проход по уже готовой расшифровке на диске. См. Форматирование.

estimate_cost

Принимает тот же source и возвращает длительность, оценочные USD, предел и то, был бы заказ разрешён. Запрос к Deepgram не выполняется. URL всё равно скачивается, потому что иначе длительность неизвестна, так что эта операция бесплатна по части Deepgram, но не мгновенна.

CLI

transcribe ./interview.mp4 --diarize   # experimental, labels are often wrong
transcribe ./lecture.mp3 --estimate
transcribe ./clip.mp4 --json | jq .transcript_path

Флаг

По умолчанию

Назначение

--diarize

выкл

Экспериментально. Метки говорящих

--model <name>

DEEPGRAM_MODEL, иначе nova-3

Модель Deepgram

--language <code>

en

Язык речи

--out <dir>

TRANSCRIPTION_OUTPUT_DIR, иначе ./output

Каталог вывода

--force

выкл

Повторная транскрипция даже при попадании в кэш

--estimate

выкл

Вывести длительность и оценочные USD и выйти

--json

выкл

Вывести один JSON-объект и ничего больше

--help

Показать все флаги

Коды выхода: 0 — успех, 2 — отказ из-за превышения предела стоимости, 3 — ошибка конфигурации или отсутствующего бинарника, 1 — всё остальное.

transcribe format ./output/interview-final-8a2c1d0b7e64
transcribe format ./interview.mp4 --force

Подкоманда format принимает папку заказа или локальный файл, который её создал, и принимает флаги --force и --json.

Форматирование

Сырая расшифровка точна, но почти нечитаема: либо один сплошной текст, либо абзацы, рубящиеся каждые четыре предложения правилом, которое не слышит говорящего. Проход форматирования исправляет это, не подпуская языковую модель к самим словам.

Расшифровка разбивается на предложения с номерами и отправляется дешёвой модели OpenRouter, которая возвращает только структуру: индексы, после которых идут разрывы абзацев, опциональные заголовки { startIndex, title } и от трёх до восьми тегов в kebab-case. Markdown затем пересобирается из сохранённого массива предложений. Потерять, переписать или выдумать предложение невозможно конструктивно, а не проверкой, потому что текст от модели вообще не возвращается.

  • Только по запросу. transcribe никогда не форматирует за вас. Запускайте format_transcript или подкоманду format.

  • Сбой только частичный. Предложения отправляются окнами. Окно, чей план невалиден или чей запрос стабильно не удаётся, повторяется, затем остаётся как простые абзацы и помечается в отчёте как пропущенный диапазон. Расшифровка никогда не становится хуже, чем сырой вывод.

  • Короткие расшифровки — без заголовков. Ниже FORMAT_HEADINGS_MIN_SENTENCES модель просят только об абзацах и тегах. Четырёхминутная голосовая заметка не нуждается в трёх выдуманных заголовках.

  • Кэшируется как транскрипция. План записывается в format.json в папке заказа. Повторный вызов перерисовывает из него и не тратит ничего; force снова вызывает модель. Повторный transcribe по уже отформатированному заказу применяет сохранённый план заново, а не затирает его.

  • Тот же защитный предел стоимости. Форматирование оценивается до запроса и отклоняется сверх MAX_COST_PER_JOB_USD. Каждый вызов — отдельный заказ для этой проверки: он никогда не суммируется с тем, что уже потрачено на Deepgram.

Структура вывода

<TRANSCRIPTION_OUTPUT_DIR>/<slug>-<key12>/

  URL sources:   never-gonna-give-you-up-dQw4w9WgXcQ-1f3b9c2d4e5a/
  Local files:   interview-final-8a2c1d0b7e64/
  audio.opus       the 16 kHz mono upload
  audio.<ext>      the yt-dlp download, for URL sources, kept so re-runs never re-fetch
  response.json    Deepgram's raw response
  format.json      the structure plan, once the transcript has been formatted
  transcript.md    YAML front matter plus the rendered transcript

Кэширование

Ключ кэша — это идентичность источника плюс опции, меняющие расшифровку: model, diarize и language. Локальные файлы опознаются по SHA-256 их байтов; URL — по идентификатору экстрактора yt-dlp, так что параметры отслеживания и сокращённые ссылки никогда не вызывают вторую платную транскрипцию.

Имя папки косметическое: для URL это название видео с последующим идентификатором видео, а для локального файла — имя файла. Заказ находится по хвостовому <key12> в одиночку, так что папка переиспользуется, что бы ни говорила её читаемая половина. Видео, переименованное его автором, или папка, названная старой версией этого инструмента, всё равно попадают в кэш, а не платят за ту же расшифровку дважды.

При попадании в кэш transcript.md перерисовывается из сохранённого response.json, а не возвращается старый Markdown, так что улучшения форматтера достигают старых заказов без затрат. Только --force / force: true вызывает Deepgram повторно.

Разработка

npm test          # vitest
npm run typecheck
npm run build

Ни один тест не запускает бинарник и не касается сети: ffprobe, ffmpeg и yt-dlp проходят через инъекцируемый исполнитель команд, а Deepgram — через инъекцируемый fetch.

Related MCP Connectors

Related MCP Servers