AAFinfo is a local, read-only command-line inspector for Advanced Authoring Format (AAF) files. It reads one AAF, builds a schema-versioned report model, then emits both JSON and a self-contained HTML report.
AAFinfo does not modify input files, write AAFs, call external AAF tools, make network requests, run telemetry, or fetch remote assets. The HTML report is a single file with inline CSS and no JavaScript.
This project uses uv and Python 3.11 or newer.
uv sync --dev --frozenThe parser dependency is pinned to upstream markreidvfx/pyaaf2 tag v1.7.1.
Generate local fixtures:
uv run python examples/_generate.pyInspect a fixture and write reports:
uv run aafinfo examples/_generated/simple_stereo.aafDefault output goes to ./aafinfo-report/:
aafinfo-report/simple-stereo-report.json
aafinfo-report/simple-stereo-report.html
Print JSON only:
uv run aafinfo examples/_generated/simple_stereo.aaf --jsonRun tests and the smoke flow:
uv run pytest
uv run python examples/_smoke.pyuv run aafinfo <file.aaf> [--out <dir>] [--json] [--filter <text>]
[--no-clips] [--name <slug>] [--version]Options:
--out <dir>: directory for report artifacts. Defaults to./aafinfo-report/.--json: write nothing; print schema-versioned JSON to stdout. Mutually exclusive with explicit--out.--json-onlyis accepted as a synonym.--filter <text>: case-insensitive substring filter for the HTML clips table. It matches track name, clip name, source file name, and source basename. JSON still contains every displayable clip.--no-clips: omit the per-clip table from the HTML report. JSON is unaffected.--name <slug>: explicit output filename slug. Without this, the slug is derived from the input filename.--version: print the installed AAFinfo version.
If an output artifact already exists, AAFinfo writes a numbered sibling such as
simple-stereo-report-01.json and simple-stereo-report-01.html.
JSON reports use schema version "2.5" and include:
- input path, basename, size, and SHA-256
- source properties: name, type, start time, timecode format, creating application, audio format summary, and video frame rate
- composition summary
- source-file summary counts under
summary.source_files, counting onlysource_mobs[]entries whererole == "source"andhas_essence == true - total filler/gap count under
summary.filler_segments - total unresolved zero-source SourceClip count under
summary.unresolved_segments - tracks, including
filler_count,unresolved_count, and displayable-onlyclip_count - displayable clips, excluding AAF Filler gaps and nameless unresolved
SourceClips, and including
source_file_namewhen the underlying source file identity can be resolved separately from the editor-facing clip name - source mobs with explicit
rolevalues:composition,master,source, orunknown - source mob file metadata for GUI consumers:
container,data_size_bytes,has_essence,format_summary, andname_source - markers
- warnings
For clips[], name is the clip or region label from the timeline, or
null when the source clip has no explicit label. AAFinfo does not generate
synthetic "Clip N" names. source_file_name is the resolved source-file
identity, using descriptor locators, BWF bext metadata, SourceMob names,
then MasterMob names. It is null when the chain only exposes generic
placeholders.
AAF Filler segments are not emitted in clips[]. They are counted
structurally as pyaaf2 components.Filler objects in tracks[].filler_count
and summary.filler_segments. Transition segments are also excluded from
clips[], but are not counted as filler.
Nameless SourceClip segments whose source mob cannot be resolved are also not
emitted in clips[]. They are counted separately in
tracks[].unresolved_count and summary.unresolved_segments. Named
sourceless SourceClip segments remain in clips[] because they are still
displayable.
For source_mobs[], container is one of WAV, BWF, AIFF, MP3, or
null; BWF is reported when a WAV Summary contains a RIFF bext chunk.
data_size_bytes is populated only for embedded essence stored in the AAF.
has_essence is true when audio sample rate, bit depth, and channel count are
known, and format_summary is the display string such as WAV 24/48.
Source mob name is now the best available source-file identity rather than
verbatim SourceMob.Name; name_source reports whether it came from
locator, bext, sourcemob_name, mastermob_name, or placeholder.
HTML reports contain the same report data in this order:
- Source properties header
- Report details
- Tracks
- Clips
- Source mobs
- Markers
- Warnings
The HTML is designed as a factual datasheet and can be printed to PDF by a browser. The CLI does not generate PDF files directly.
AAFinfo v0.5.1 is the inspection MVP plus additive JSON fields for downstream consumers such as AAFpeek.
In scope:
- one AAF input at a time
- pyaaf2-backed reading only
- composition, tracks, displayable clips, filler and unresolved segment counts, resolved source-file names, source mobs, mob roles, source-file summary counts, source file format metadata, markers, and warnings
- JSON and self-contained HTML report output
- generated test fixtures only, no real-show media in the repository
Out of scope:
- writing or editing AAFs
- embedded essence extraction
- transition and automation deep parsing
- LibAAF or
aaftoolintegration
The engine and model layers are importable without Click or Jinja2 coupling:
from pathlib import Path
from aafinfo import build_report
report = build_report(Path("example.aaf"))
print(report.model_dump(mode="json"))The model layer is the contract for the forthcoming AAFpeek app.
Useful commands:
uv run aafinfo --help
uv run python examples/_generate.py
uv run pytest
uv run python examples/_smoke.pyCI runs uv sync --dev --frozen, uv run pytest, and the smoke flow on
Python 3.11 and 3.12.
MIT. See LICENSE.