Source code for camtasia.audiate.project
"""Audiate project reader for .audiate files (same JSON schema as .tscproj)."""
from __future__ import annotations
import json
from pathlib import Path
from camtasia.audiate.transcript import Transcript
from camtasia.timing import EDIT_RATE
[docs]
class AudiateProject:
"""Loads and exposes data from a TechSmith Audiate .audiate file.
The .audiate format uses the same JSON schema as Camtasia .tscproj files.
The transcript lives at ``tracks[0].parameters.transcription.keyframes``.
Args:
file_path: Path to the .audiate file or its containing directory.
"""
def __init__(self, file_path: str | Path) -> None:
path = Path(file_path).resolve()
if path.is_dir():
matches = list(path.glob("*.audiate"))
if not matches:
raise FileNotFoundError(f"No .audiate file found in {path}")
path = matches[0]
self._file_path = path
self._data: dict = json.loads(path.read_text())
@property
def transcript(self) -> Transcript:
"""Word-level transcript parsed from the first track's transcription keyframes."""
keyframes = (
self._data["timeline"]["sceneTrack"]["scenes"][0]["csml"]
["tracks"][0]["parameters"]["transcription"]["keyframes"]
)
return Transcript.from_audiate_keyframes(keyframes)
@property
def language(self) -> str:
"""Project language code (e.g. 'en')."""
return self._data["metadata"]["projectLanguage"] # type: ignore[no-any-return]
@property
def session_id(self) -> str:
"""Camtasia linking UUID (caiCamtasiaSessionId)."""
return self._data["metadata"]["caiCamtasiaSessionId"] # type: ignore[no-any-return]
@property
def audio_duration(self) -> float:
"""Total audio duration in seconds, from the first track's first media clip."""
track = (
self._data["timeline"]["sceneTrack"]["scenes"][0]["csml"]["tracks"][0]
)
media = track["medias"][0]
return float(media["duration"] / EDIT_RATE)
@property
def source_audio_path(self) -> Path:
"""Path to the source audio file (resolved relative to the .audiate file)."""
src_id = (
self._data["timeline"]["sceneTrack"]["scenes"][0]["csml"]
["tracks"][0]["medias"][0]["src"]
)
for entry in self._data.get("sourceBin", []):
if entry["id"] == src_id:
return (self._file_path.parent / entry["src"]).resolve() # type: ignore[no-any-return]
raise FileNotFoundError(f"Source with id={src_id} not found in sourceBin")
def __repr__(self) -> str:
return f'AudiateProject(file_path="{self._file_path}")'