Разделы, фильтры и просмотр текстов в UI; опция «не хранить видео»
- таблица sections (дерево через parent_id), у видео section_id
- API: CRUD /sections, фильтры /videos (поиск, раздел с потомками, метод),
GET /videos/{id}/text, PATCH раздела, DELETE вместе с файлами
- pipeline: keep_video=false удаляет mp4 после расшифровки, section_id
- UI: панель разделов с вложенностью и счётчиками, поиск, фильтр по методу,
диалоги полного текста/выжимки, привязка к разделу, удаление,
переключатель «Сохранять видео на диске»
- compose: bind-mount ./web для правок без пересборки
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -68,7 +68,12 @@ def chat_complete(cfg: LLMConfig, messages: list[dict]) -> str:
|
||||
"max_tokens": cfg.max_tokens,
|
||||
"temperature": cfg.temperature,
|
||||
}
|
||||
headers = {"Content-Type": "application/json", "Accept": "application/json"}
|
||||
headers = {
|
||||
"Content-Type": "application/json",
|
||||
"Accept": "application/json",
|
||||
# urllib's default "Python-urllib/x.y" UA is blocked by some CDNs (Cloudflare 1010)
|
||||
"User-Agent": "LLM-infa/0.3",
|
||||
}
|
||||
if cfg.api_key:
|
||||
headers["Authorization"] = f"Bearer {cfg.api_key}"
|
||||
headers.update(cfg.extra_headers or {})
|
||||
@@ -121,7 +126,7 @@ def list_models(cfg: LLMConfig) -> list[dict]:
|
||||
Compatible with OpenAI, OpenRouter, Ollama OpenAI-compat, vLLM, LM Studio.
|
||||
"""
|
||||
url = cfg.base_url.rstrip("/") + "/models"
|
||||
headers = {"Accept": "application/json"}
|
||||
headers = {"Accept": "application/json", "User-Agent": "LLM-infa/0.3"}
|
||||
if cfg.api_key:
|
||||
headers["Authorization"] = f"Bearer {cfg.api_key}"
|
||||
headers.update(cfg.extra_headers or {})
|
||||
|
||||
@@ -38,20 +38,30 @@ def download_video_with_subs(
|
||||
|
||||
outtmpl = str(video_dir / f"{video_id}.%(ext)s")
|
||||
|
||||
ydl_opts = {
|
||||
base_opts = {
|
||||
"outtmpl": outtmpl,
|
||||
"format": "bv*+ba/b",
|
||||
"merge_output_format": "mp4",
|
||||
"quiet": True,
|
||||
"no_warnings": True,
|
||||
}
|
||||
sub_opts = {
|
||||
**base_opts,
|
||||
"writesubtitles": True,
|
||||
"writeautomaticsub": True,
|
||||
"subtitleslangs": langs,
|
||||
"subtitlesformat": "vtt",
|
||||
"quiet": True,
|
||||
"no_warnings": True,
|
||||
}
|
||||
|
||||
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
|
||||
info = ydl.extract_info(url, download=True)
|
||||
# YouTube often 429s the subtitle endpoints; a failed subtitle download must not
|
||||
# kill the pipeline — retry without subs and let the Vosk fallback transcribe.
|
||||
try:
|
||||
with yt_dlp.YoutubeDL(sub_opts) as ydl:
|
||||
info = ydl.extract_info(url, download=True)
|
||||
except yt_dlp.utils.DownloadError as e:
|
||||
logger.warning("Download with subtitles failed (%s); retrying without subs", e)
|
||||
with yt_dlp.YoutubeDL(base_opts) as ydl:
|
||||
info = ydl.extract_info(url, download=True)
|
||||
|
||||
title = info.get("title") or video_id
|
||||
|
||||
|
||||
Reference in New Issue
Block a user