add extra formats
This commit is contained in:
@@ -18,6 +18,10 @@ from .transcript_merger import TranscriptMerger
|
||||
|
||||
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:
|
||||
"""Configuration for the processing workflow."""
|
||||
@@ -36,6 +40,12 @@ class WorkflowConfig:
|
||||
self.diarize = kwargs.get('diarize', False)
|
||||
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
|
||||
self.scene_detection = kwargs.get('scene_detection', False)
|
||||
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_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]:
|
||||
"""Convert config to dictionary for manifest."""
|
||||
return {
|
||||
"whisper": {
|
||||
"enabled": self.run_whisper,
|
||||
"model": self.whisper_model
|
||||
"model": self.whisper_model,
|
||||
"diarize": self.diarize,
|
||||
"transcript_formats": self.transcript_formats
|
||||
},
|
||||
"frame_extraction": {
|
||||
"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("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
|
||||
cmd = [
|
||||
transcribe_cmd,
|
||||
str(self.config.video_path),
|
||||
"--model", self.config.whisper_model,
|
||||
"--output_format", "json",
|
||||
"--output_format", output_format,
|
||||
"--output_dir", str(self.output_mgr.output_dir),
|
||||
]
|
||||
if use_diarize:
|
||||
@@ -244,6 +295,10 @@ class ProcessingWorkflow:
|
||||
|
||||
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():
|
||||
logger.info(f"✓ Whisper transcription completed: {transcript_path.name}")
|
||||
|
||||
@@ -275,6 +330,29 @@ class ProcessingWorkflow:
|
||||
logger.error(f"Whisper failed: {e.stderr}")
|
||||
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):
|
||||
"""Extract frames from video."""
|
||||
logger.info("Step 1: Extracting frames from video...")
|
||||
|
||||
@@ -82,6 +82,15 @@ Examples:
|
||||
help='Language of the audio (e.g. en, es, fr). Default: auto-detect',
|
||||
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
|
||||
parser.add_argument(
|
||||
|
||||
Reference in New Issue
Block a user