AI_Development_Automation_Manager / adam /transcript_dataset.py
SyntheticMDProductions's picture
Update ADAM safety, UI, and model workflows (#1)
c61c435
Raw
History Blame Contribute Delete
4.91 kB
from __future__ import annotations
import json
import re
import shutil
from dataclasses import dataclass
from importlib.util import find_spec
from pathlib import Path
from typing import Any
@dataclass(slots=True)
class TranscriptResult:
available: bool
message: str
samples: list[dict[str, Any]]
output_folder: str = ""
backend: str = ""
@dataclass(frozen=True, slots=True)
class TranscriptionBackend:
id: str
name: str
available: bool
requirement: str
def clean_transcript(text: str) -> str:
text = re.sub(r"\s+", " ", text.replace("\r", " ").replace("\n", " ")).strip()
text = re.sub(r"\s+([,.!?;:])", r"\1", text)
return text
def split_transcript(text: str, *, max_chars: int = 900) -> list[str]:
cleaned = clean_transcript(text)
if not cleaned:
return []
sentences = re.split(r"(?<=[.!?])\s+", cleaned)
samples: list[str] = []
current = ""
for sentence in sentences:
if not sentence:
continue
if current and len(current) + 1 + len(sentence) > max_chars:
samples.append(current.strip())
current = sentence
else:
current = f"{current} {sentence}".strip()
if current:
samples.append(current.strip())
return samples
def available_transcription_backends() -> list[TranscriptionBackend]:
return [
TranscriptionBackend("whisper", "OpenAI Whisper", find_spec("whisper") is not None, "pip install openai-whisper"),
TranscriptionBackend("faster_whisper", "faster-whisper", find_spec("faster_whisper") is not None, "pip install faster-whisper"),
]
def _select_backend(preferred: str = "auto") -> TranscriptionBackend | None:
backends = available_transcription_backends()
if preferred != "auto":
return next((backend for backend in backends if backend.id == preferred and backend.available), None)
return next((backend for backend in backends if backend.available), None)
def _transcribe_with_backend(video: Path, backend: TranscriptionBackend) -> str:
if backend.id == "whisper":
import whisper
model = whisper.load_model("base")
result = model.transcribe(str(video))
return str(result.get("text", ""))
if backend.id == "faster_whisper":
from faster_whisper import WhisperModel
model = WhisperModel("base", device="auto", compute_type="auto")
segments, _info = model.transcribe(str(video))
return " ".join(segment.text for segment in segments)
raise RuntimeError(f"Unsupported transcription backend: {backend.id}")
def transcript_videos_to_dataset(
videos: list[str | Path],
output_folder: str | Path,
*,
backend: str = "auto",
max_chars: int = 900,
preserve_metadata: bool = True,
) -> TranscriptResult:
output = Path(output_folder).expanduser().resolve()
if not videos:
return TranscriptResult(False, "Choose at least one local video.", [])
if not shutil.which("ffmpeg"):
return TranscriptResult(
False,
"FFmpeg was not found. ADAM can keep the workflow ready, but audio extraction/transcription needs FFmpeg.",
[],
)
selected_backend = _select_backend(backend)
if selected_backend is None:
requirements = ", ".join(item.requirement for item in available_transcription_backends())
return TranscriptResult(
False,
f"No local transcription backend is installed. Install one of: {requirements}.",
[],
)
samples: list[dict[str, Any]] = []
try:
for video in videos:
path = Path(video).expanduser().resolve()
if not path.is_file():
return TranscriptResult(False, f"Video not found: {path}", [])
text = _transcribe_with_backend(path, selected_backend)
for index, sample in enumerate(split_transcript(text, max_chars=max_chars), 1):
item = {"text": sample}
if preserve_metadata:
item.update({"source_video": str(path), "sample_index": index})
samples.append(item)
except Exception as exc:
return TranscriptResult(False, f"{selected_backend.name} could not transcribe the selected video(s): {exc}", [], backend=selected_backend.id)
output.mkdir(parents=True, exist_ok=True)
txt_path = output / "transcript_samples.txt"
jsonl_path = output / "transcript_samples.jsonl"
txt_path.write_text("\n\n".join(item["text"] for item in samples), encoding="utf-8")
jsonl_path.write_text(
"\n".join(json.dumps(item, ensure_ascii=False) for item in samples) + ("\n" if samples else ""),
encoding="utf-8",
)
return TranscriptResult(True, f"Exported {len(samples):,} transcript sample(s) with {selected_backend.name}.", samples, str(output), selected_backend.id)