Skip to content

Latest commit

 

History

9 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Conspect

Конспект собирается потоком: субтитры, анализ, структурированный конспект

Конспект YouTube по клику. Расширение Chrome запускает сборку структурированного конспекта видео на вашем сервере: сервер скачивает субтитры, чистит транскрипт и отдаёт конспект потоком. Расширение — тонкий клиент: кнопка под плеером, живой превью по мере генерации, экспорт в Markdown.

Self-host: аккаунтов, оплаты и базы данных нет. Сервер не хранит ничего. Готовый конспект кэшируется в браузере и сохраняется как .md.

Архитектура

  • backend/ — сервер Node на Hono (TypeScript). Скачивание субтитров и саммари через LLM целиком на сервере.
  • extension/ — расширение Chrome MV3. Адрес сервера и пароль доступа читает из config.json, в интерфейсе настроек сервера нет.

Конспект собирается в четыре шага: yt-dlp скачивает субтитры, cleanTranscript.ts чистит транскрипт, LLM пишет конспект, сервер стримит текст в расширение по SSE.

Требования

  • Node.js 20+.
  • yt-dlp в PATH (либо рядом с сервером, либо путь через YTDLP_BIN).
  • Python для запуска не нужен. Он нужен только чтобы прогонять тесты чистки транскрипта — сверка cleanTranscript.ts с эталоном clean_transcript.py. Без Python тест сверяется с зафиксированным текстом.

Развёртывание

Три сценария. Выбирайте по тому, где будет считаться конспект и с каких устройств нужен доступ.

Сценарий Сервер Доступ Кому подходит
Локально ваш компьютер localhost попробовать; один компьютер
Свой VPS + SSH-туннель сервер localhost через ssh нужен работающий сервер, но открывать его в интернет не хочется
Публичный адрес сервер https://ваш-домен несколько устройств; нужен домен и HTTPS

Локально

Docker:

cd backend
cp .env.example .env
# заполните LLM_API_KEY и SHARED_TOKEN
docker compose up -d --build
curl http://127.0.0.1:3000/health   # {"ok":true,"version":"1"}

Без Docker:

cd backend
cp .env.example .env
# заполните LLM_API_KEY и SHARED_TOKEN
npm install
npm run build
npm start

yt-dlp должен быть в PATH: pip install yt-dlp либо бинарник из релизов yt-dlp. Путь можно задать явно через YTDLP_BIN.

В config.json расширения оставьте baseUrl равным http://localhost:3000.

Свой VPS + SSH-туннель

Порт бэкенда остаётся привязанным к 127.0.0.1 сервера, то есть снаружи закрыт. Ваш компьютер достаёт до него по ssh. Ни портов открывать, ни сертификаты выпускать не нужно: шифрование даёт сам ssh.

На сервере:

git clone https://github.com/vonavikon/conspect.git
cd conspect/backend
cp .env.example .env
# заполните LLM_API_KEY и SHARED_TOKEN
docker compose up -d --build
curl http://127.0.0.1:3000/health

На своём компьютере:

ssh -N -L 3000:localhost:3000 user@your-server

Пока эта команда работает, http://localhost:3000 на вашем компьютере — это бэкенд на сервере. baseUrl в config.json расширения остаётся http://localhost:3000.

Чтобы туннель поднимался сам при входе в систему и переживал обрывы связи, возьмите готовые скрипты: scripts/tunnel/ (Windows, Linux, macOS).

Если порт 3000 на сервере занят чем-то другим, поменяйте проброс в docker-compose.yml ("127.0.0.1:3010:3000") и в команде ssh — внутри контейнера порт остаётся прежним.

Публичный адрес

Нужен, если пользуетесь с нескольких устройств и туннель на каждом заводить не хочется. Готовьтесь к тому, что сервис увидят сканеры: адрес станет публичным, а защита — один токен.

  1. В docker-compose.yml замените 127.0.0.1:3000:3000 на 3000:3000.
  2. Поставьте reverse-proxy с HTTPS (Caddy, nginx) на свой домен и проксируйте на 127.0.0.1:3000. Без HTTPS токен и транскрипты идут открытым текстом: их прочитает любой на маршруте, от Wi-Fi в кафе до провайдера.
  3. Пропишите домен в host_permissions в manifest.json и пересоберите расширение. Как вариант, вместо этого укажите в CORS_ORIGIN origin расширения chrome-extension://<id> (id виден на chrome://extensions).
  4. Откройте в файрволе только порт reverse-proxy (443), не порт бэкенда.

Сертификат Let's Encrypt требует домена: на голый IP публичные CA сертификаты не выдают, а ACME-проверка хочет либо домен (DNS-01), либо свободные порты 80/443 (HTTP-01, TLS-ALPN).

Перед публикацией прочитайте «Известные ограничения» ниже: на публичном адресе вся защита сводится к одному токену, а лимиты ограничивают ущерб от его утечки, но не исключают его.

Расширение

Подключение к серверу задаётся файлом extension/config.json. Расширение читает его при первом запуске; настроек сервера в интерфейсе нет.

cd extension
npm install
# сгенерирует токен, выведет его и запишет config.json
node scripts/configure.mjs --base-url https://conspect.example.com --gen-token
npm run build

Токен, который напечатал скрипт, впишите в backend/.env строкой SHARED_TOKEN=<токен> (если ещё не делали). Он должен совпадать в обоих местах.

Либо создайте config.json рядом с build.mjs вручную:

{ "baseUrl": "https://conspect.example.com", "sharedToken": "тот же токен, что в backend/.env" }

Откройте chrome://extensions, включите режим разработчика, «Загрузить распакованное», выберите extension/dist.

API

  • GET /health — проверка доступности.
  • POST /digest/stream — SSE-стрим конспекта. Заголовок Authorization: Bearer <SHARED_TOKEN>. Тело {"url": "..."}.

События SSE: meta (название, канал, длительность), delta (кусок конспекта), done (готово, число токенов), error (причина), ping (heartbeat каждые 10 секунд).

Провайдеры LLM

Подойдёт любой OpenAI-совместимый эндпоинт. Сервер не держит списка моделей, имя модели указывается в .env.

Провайдер LLM_BASE_URL Пример LLM_MODEL
bothub.chat https://openai.bothub.chat/v1 deepseek-v4-flash
OpenAI https://api.openai.com/v1 gpt-4o
Google Gemini https://generativelanguage.googleapis.com/v1beta/openai gemini-2.5-flash
Groq https://api.groq.com/openai/v1 llama-3.3-70b-versatile
OpenRouter https://openrouter.ai/api/v1 openai/gpt-4o

Настройки

Все переменные читаются из .env (образец — .env.example).

Переменная Назначение По умолчанию
LLM_API_KEY ключ OpenAI-совместимого API обязательна
LLM_BASE_URL базовый URL LLM API https://openai.bothub.chat/v1
LLM_MODEL имя модели deepseek-v4-flash
MAX_TOKENS лимит токенов ответа LLM 8192
LLM_TIMEOUT_SEC таймаут запроса к LLM 300
MAX_DURATION_MIN максимум длины видео в минутах 180
YTDLP_PROBE_TIMEOUT_SEC таймаут проверки видео 90
YTDLP_DOWNLOAD_TIMEOUT_SEC таймаут скачивания субтитров 180
PORT порт сервера 3000
HOST интерфейс, на котором слушает сервер (в Docker перекрывается на 0.0.0.0 из docker-compose.yml) 127.0.0.1
CORS_ORIGIN разрешённые origin через запятую, пусто — запрет пусто
SHARED_TOKEN общий секрет между сервером и расширением обязателен, от 24 символов
MAX_CONCURRENT_DIGESTS максимум одновременных генераций 2
RATE_LIMIT_PER_MIN запусков в минуту 10
YTDLP_AUTO_UPDATE автообновление yt-dlp при старте (1 — включено) 0

Безопасность

SHARED_TOKEN — единственная защита: сервер не различает пользователей. Любой, кто узнал адрес публичного сервера без токена, потратит ваш LLM-ключ. Задайте длинную случайную строку (от 24 символов).

Сравнение токена — константное по времени (crypto.timingSafeEqual).

Rate limit ограничивает ущерб от утечки токена, но не отменяет его: сервер держит максимум одновременных генераций (MAX_CONCURRENT_DIGESTS) и запусков в минуту (RATE_LIMIT_PER_MIN). Лимиты общие, потому что сервер знает один токен, а не пользователей. Токен держите в секрете.

Возьмите для сервера отдельный ключ LLM-провайдера и поставьте на него лимит расходов, если провайдер это умеет. Это единственная мера, которая ограничивает убыток, когда токен уже утёк.

Известные ограничения

  • Rate limit общий, не по адресам. Счётчики глобальные, но считают только запросы с верным токеном: без него запрос отбивается на 401 раньше проверки лимита и квоту не тратит, SSE-соединение тоже не открывается. Пока токен известен только вам, ограничение незаметно. Если токен утёк, чужие запуски съедают общую квоту и ваш LLM-бюджет — лечится сменой токена, а не настройкой лимитов.
  • Сервер не терминирует TLS. HTTPS — задача reverse-proxy перед ним.
  • Токен нельзя отозвать частично. Он один. После смены обновите config.json на каждом устройстве (достаточно заменить файл в dist и перезагрузить расширение).
  • yt-dlp просит JS-рантайм. В логах видно No supported JavaScript runtime could be found ... has been deprecated. Субтитры при этом скачиваются, проверено. Если YouTube начнёт отдавать меньше данных, поставьте deno рядом с yt-dlp (в системе или в образе).
  • Конспекты живут в браузере. Сервер не хранит ничего, поэтому при переустановке браузера или переходе на другое устройство история не переезжает. Нужное сохраняйте в .md.

Лицензия

MIT.

About

Расширение для браузера, которое поможет сделать конспект видео из ютуб

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages