add extra formats

This commit is contained in:
Mariano Gabriel
2026-06-09 17:46:37 -03:00
parent 307f261284
commit 3c96de85ad
2 changed files with 89 additions and 2 deletions

View File

@@ -18,6 +18,10 @@ from .transcript_merger import TranscriptMerger
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
# Transcript output formats supported by whisper/whisperx (besides the json base).
# json is always produced because it is the base for the enhanced transcript.
VALID_TRANSCRIPT_FORMATS = {'json', 'srt', 'vtt', 'txt', 'tsv'}
class WorkflowConfig: class WorkflowConfig:
"""Configuration for the processing workflow.""" """Configuration for the processing workflow."""
@@ -36,6 +40,12 @@ class WorkflowConfig:
self.diarize = kwargs.get('diarize', False) self.diarize = kwargs.get('diarize', False)
self.language = kwargs.get('language') self.language = kwargs.get('language')
# Additional transcript output formats (json is always written as the
# base for the enhanced transcript).
self.transcript_formats = self._parse_transcript_formats(
kwargs.get('transcript_formats')
)
# Frame extraction # Frame extraction
self.scene_detection = kwargs.get('scene_detection', False) self.scene_detection = kwargs.get('scene_detection', False)
self.scene_threshold = kwargs.get('scene_threshold', 15.0) self.scene_threshold = kwargs.get('scene_threshold', 15.0)
@@ -69,12 +79,44 @@ class WorkflowConfig:
self.embed_images = kwargs.get('embed_images', False) self.embed_images = kwargs.get('embed_images', False)
self.embed_quality = kwargs.get('embed_quality', 80) self.embed_quality = kwargs.get('embed_quality', 80)
@staticmethod
def _parse_transcript_formats(raw) -> list:
"""Parse the --transcript-formats value into a deduplicated list.
Accepts a comma-separated string (e.g. "srt,txt") or 'all'. json is
always included since it is the base for the enhanced transcript.
"""
formats = ['json']
if not raw:
return formats
if isinstance(raw, str):
requested = [f.strip().lower() for f in raw.split(',') if f.strip()]
else:
requested = [str(f).strip().lower() for f in raw if str(f).strip()]
if 'all' in requested:
requested = [f for f in VALID_TRANSCRIPT_FORMATS if f != 'json']
for fmt in requested:
if fmt not in VALID_TRANSCRIPT_FORMATS:
raise ValueError(
f"Invalid transcript format '{fmt}'. "
f"Choose from: {', '.join(sorted(VALID_TRANSCRIPT_FORMATS))}, all"
)
if fmt not in formats:
formats.append(fmt)
return formats
def to_dict(self) -> Dict[str, Any]: def to_dict(self) -> Dict[str, Any]:
"""Convert config to dictionary for manifest.""" """Convert config to dictionary for manifest."""
return { return {
"whisper": { "whisper": {
"enabled": self.run_whisper, "enabled": self.run_whisper,
"model": self.whisper_model "model": self.whisper_model,
"diarize": self.diarize,
"transcript_formats": self.transcript_formats
}, },
"frame_extraction": { "frame_extraction": {
"method": "scene_detection" if self.scene_detection else "interval", "method": "scene_detection" if self.scene_detection else "interval",
@@ -217,12 +259,21 @@ class ProcessingWorkflow:
logger.info(f"Running Whisper transcription (model: {self.config.whisper_model})...") logger.info(f"Running Whisper transcription (model: {self.config.whisper_model})...")
logger.info("This may take a few minutes depending on video length...") logger.info("This may take a few minutes depending on video length...")
# Determine output format. json is always produced (base for the
# enhanced transcript). If extra formats are requested we ask whisper
# for "all" in a single transcription pass, then prune the formats the
# user didn't request (whisper's CLI only accepts one --output_format).
extra_formats = [f for f in self.config.transcript_formats if f != 'json']
output_format = "all" if extra_formats else "json"
if extra_formats:
logger.info(f"Additional transcript formats requested: {', '.join(extra_formats)}")
# Build command # Build command
cmd = [ cmd = [
transcribe_cmd, transcribe_cmd,
str(self.config.video_path), str(self.config.video_path),
"--model", self.config.whisper_model, "--model", self.config.whisper_model,
"--output_format", "json", "--output_format", output_format,
"--output_dir", str(self.output_mgr.output_dir), "--output_dir", str(self.output_mgr.output_dir),
] ]
if use_diarize: if use_diarize:
@@ -244,6 +295,10 @@ class ProcessingWorkflow:
transcript_path = self.output_mgr.get_path(f"{self.config.video_path.stem}.json") transcript_path = self.output_mgr.get_path(f"{self.config.video_path.stem}.json")
# Prune any formats produced by "all" that the user didn't request.
if extra_formats:
self._prune_transcript_formats()
if transcript_path.exists(): if transcript_path.exists():
logger.info(f"✓ Whisper transcription completed: {transcript_path.name}") logger.info(f"✓ Whisper transcription completed: {transcript_path.name}")
@@ -275,6 +330,29 @@ class ProcessingWorkflow:
logger.error(f"Whisper failed: {e.stderr}") logger.error(f"Whisper failed: {e.stderr}")
raise raise
def _prune_transcript_formats(self):
"""Remove transcript files produced by "all" that weren't requested.
whisper/whisperx only accept a single --output_format, so we run with
"all" and delete the formats the user didn't ask for. json is always
kept since it is the base for the enhanced transcript.
"""
keep = set(self.config.transcript_formats)
stem = self.config.video_path.stem
for fmt in VALID_TRANSCRIPT_FORMATS:
if fmt in keep:
continue
stray = self.output_mgr.get_path(f"{stem}.{fmt}")
if stray.exists():
try:
stray.unlink()
logger.debug(f"Removed unrequested transcript format: {stray.name}")
except OSError as e:
logger.warning(f"Could not remove {stray.name}: {e}")
written = [f for f in self.config.transcript_formats]
logger.info(f"✓ Transcript formats written: {', '.join(written)}")
def _extract_frames(self): def _extract_frames(self):
"""Extract frames from video.""" """Extract frames from video."""
logger.info("Step 1: Extracting frames from video...") logger.info("Step 1: Extracting frames from video...")

View File

@@ -82,6 +82,15 @@ Examples:
help='Language of the audio (e.g. en, es, fr). Default: auto-detect', help='Language of the audio (e.g. en, es, fr). Default: auto-detect',
default=None default=None
) )
parser.add_argument(
'--transcript-formats',
help='Comma-separated extra transcript/diarization formats to write '
'alongside JSON (choices: srt, vtt, txt, tsv, or all). JSON is '
'always written as the base for the enhanced transcript. '
'Only produced when transcription actually runs (use '
'--skip-cache-whisper to regenerate). Example: --transcript-formats srt,txt',
default=None
)
# Output options # Output options
parser.add_argument( parser.add_argument(