From c771f83351086c35610b967dfccb4b100003c647 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9A=D0=B8=D1=80=D0=B8=D0=BB=D0=BB=20=D0=91=D0=BB=D0=B8?= =?UTF-8?q?=D0=BD=D0=BE=D0=B2?= Date: Fri, 29 May 2026 11:39:13 +0300 Subject: [PATCH] Add multi-format output support (docx + md simultaneously) --- README.md | 29 ++++++++++++++++++++++++----- config.yaml | 2 +- run.py | 50 +++++++++++++++++++++++++++++++++++++++----------- 3 files changed, 64 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 3c310ce..e0b2187 100644 --- a/README.md +++ b/README.md @@ -155,24 +155,27 @@ python run.py -i meeting.wav -o meeting.docx | Аргумент | Описание | |----------|----------| | `-i, --input` | Путь к аудиофайлу (обязательный) | -| `-o, --output` | Путь к выходному файлу (docx/md/txt) | +| `-o, --output` | Путь к выходному файлу (если один формат) | | `-p, --profile` | Профиль: `mac_m4`, `gpu_8gb`, `cpu_best` | | `-c, --config` | Путь к `config.yaml` | | `-d, --device` | Принудительно: `cpu`, `cuda`, `mps` | | `-m, --model` | Модель: `tiny`, `base`, `small`, `medium`, `large-v3` | | `-l, --language` | Язык: `ru`, `en`, ... | -| `-f, --format` | Формат: `docx`, `md`, `txt` | +| `-f, --format` | Форматы через запятую: `docx,md,txt` | ### Примеры **Аудио файлы:** ```bash -# Базовый запуск +# Базовый запуск (по умолчанию: docx + md) python run.py -i meeting.wav # Быстрый тест на маленькой модели python run.py -i meeting.wav -m base +# Только один формат +python run.py -i meeting.wav -f docx + # Markdown выход python run.py -i meeting.wav -f md -o meeting.md ``` @@ -189,6 +192,22 @@ python run.py -i zoom_meeting.mp4 -o protocol.docx python run.py -i teams_recording.mkv ``` +**Вывод в несколько форматов одновременно:** +```bash +# docx + md (по умолчанию из конфига) +python run.py -i meeting.wav + +# docx + md + txt — все сразу +python run.py -i meeting.wav -f docx,md,txt + +# Только docx и md +python run.py -i meeting.wav -f docx,md + +# Явно указать выход только для одного формата, остальные рядом +python run.py -i meeting.wav -f docx,md -o output/meeting.docx +# Создаст: output/meeting.docx и output/meeting.md +``` + **Продвинутые опции:** ```bash # Сменить профиль @@ -234,9 +253,9 @@ profiles: compute_type: int8 model: large-v3 language: ru - + output: - format: docx + formats: [docx, md] # Можно указать один или несколько include_timestamps: true paragraph_pause_sec: 2.0 ``` diff --git a/config.yaml b/config.yaml index ed8526d..9cc0b46 100644 --- a/config.yaml +++ b/config.yaml @@ -37,7 +37,7 @@ hf_token: null # HuggingFace токен для pyannote. Установите # Настройки выходного документа output: - format: docx # docx | md | txt + formats: [docx, md] # Список форматов: docx, md, txt. Можно указать один или несколько. include_timestamps: true speaker_label_style: name # name | id | none paragraph_pause_sec: 2.0 # новый абзац, если пауза > N секунд diff --git a/run.py b/run.py index cb981a2..6a165dd 100644 --- a/run.py +++ b/run.py @@ -37,19 +37,26 @@ def print_first_run_info(): print("") +def parse_formats(fmt_arg: str | None, config_formats: list) -> list[str]: + """Парсит строку форматов в список.""" + if fmt_arg: + return [f.strip().lower() for f in fmt_arg.split(",")] + return config_formats + + def main(): parser = argparse.ArgumentParser( description="Транскрибация совещаний с диаризацией и таймкодами. " "Поддерживает аудио (wav, mp3, m4a, ogg, flac) и видео (mp4, avi, mkv, mov, etc.)." ) parser.add_argument("--input", "-i", required=True, help="Путь к аудио или видео файлу") - parser.add_argument("--output", "-o", default=None, help="Путь к выходному файлу (docx/md/txt)") + parser.add_argument("--output", "-o", default=None, help="Путь к выходному файлу (если указан, переопределяет формат)") parser.add_argument("--profile", "-p", default=None, help="Профиль конфигурации (mac_m4, gpu_8gb, cpu_best)") parser.add_argument("--config", "-c", default=None, help="Путь к config.yaml") parser.add_argument("--device", "-d", default=None, help="Принудительно: cpu/cuda/mps") parser.add_argument("--model", "-m", default=None, help="Принудительно: tiny/base/small/medium/large-v3") parser.add_argument("--language", "-l", default=None, help="Язык (ru, en, ...)") - parser.add_argument("--format", "-f", default=None, help="Формат выхода: docx, md, txt") + parser.add_argument("--format", "-f", default=None, help="Форматы через запятую: docx,md,txt (по умолчанию из конфига)") args = parser.parse_args() @@ -90,14 +97,28 @@ def main(): profile["language"] = args.language output_cfg = config.get("output", {}) - fmt = args.format or output_cfg.get("format", "docx") + config_formats = output_cfg.get("formats", ["docx"]) + formats = parse_formats(args.format, config_formats) + # Определение путей выходных файлов + output_paths: list[str] = [] if args.output: - output_path = args.output + # Если указан --output, используем его для первого формата + # Остальные форматы — рядом с тем же именем + base = Path(args.output) + output_dir = base.parent + stem = base.stem + first_ext = base.suffix.lstrip(".") + # Первый файл с явным путём + output_paths.append(str(base)) + # Остальные форматы рядом + for fmt in formats[1:]: + output_paths.append(str(output_dir / f"{stem}.{fmt}")) else: stem = input_path.stem output_dir = Path(config.get("paths", {}).get("output_dir", "./output")) - output_path = str(output_dir / f"{stem}.{fmt}") + for fmt in formats: + output_paths.append(str(output_dir / f"{stem}.{fmt}")) # Проверка HF токена hf_token = os.environ.get("HF_TOKEN") or config.get("hf_token") @@ -118,22 +139,29 @@ def main(): print(f"Модель: {profile['model']}") print(f"Язык: {profile['language']}") print(f"Вход: {args.input}") - print(f"Выход: {output_path}") + print(f"Форматы: {', '.join(formats)}") + print(f"Выход: {', '.join(output_paths)}") print("-" * 40) - # Запуск пайплайна + # Запуск пайплайна (один раз для всех форматов) result = run_pipeline( input_path=args.input, profile_name=args.profile, config_path=args.config, - output_path=output_path, + output_path=output_paths[0] if output_paths else None, ) - # Генерация документа - build_document(result["segments"], output_path, config) + # Генерация документов для всех форматов + print("-" * 40) + print("Генерация документов...") + for out_path in output_paths: + build_document(result["segments"], out_path, config) + print(f" ✓ {out_path}") print("-" * 40) - print(f"Готово! Сохранено: {output_path}") + print(f"Готово! Сохранено {len(output_paths)} файл(а):") + for p in output_paths: + print(f" → {p}") if __name__ == "__main__":