Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions .cursorrules

Large diffs are not rendered by default.

9 changes: 9 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,15 @@ Tests/ExcelReportTest/Output/*.xlsx
Tests/ExcelReportTest/Output/*.pdf
Tests/ExcelReportTest/Output/~$*

# Local Shot Extraction integration fixtures and generated exports (not committed).
Tests/ShotExtractionTest/*.fcpxmld/
Tests/ShotExtractionTest/*.fcpxml
Tests/ShotExtractionTest/Output/*.fcpxmld/
Tests/ShotExtractionTest/Output/*.fcpxml
Tests/ShotExtractionTest/Output/*
!Tests/ShotExtractionTest/Output/README.md
!Tests/ShotExtractionTest/Output/.gitkeep

# Private user-submitted FCPXML inbox (never commit contents — only README / .gitkeep).
Tests/Submitted FCPXML/Inbox/**
!Tests/Submitted FCPXML/Inbox/.gitkeep
Expand Down
14 changes: 8 additions & 6 deletions AGENT.md

Large diffs are not rendered by default.

24 changes: 17 additions & 7 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ OpenFCPXMLKit is a **Swift 6** framework for Final Cut Pro FCPXML: parsing, crea
- **Repository:** https://github.com/TheAcharya/OpenFCPXMLKit
- **Dependencies:** SwiftTimecode 3.1.2+, SwiftExtensions 3.0.0+, SwiftSemanticVersion 1.0.0+, swift-log 1.14.0+, AEXML 4.7.0+, XLKit 1.1.7+, TextFile ([swift-textfile](https://github.com/orchetect/swift-textfile) 0.5.2+, Shot Extraction CSV), swift-argument-parser 1.8.2+ (CLI only), Foundation, CoreMedia, ImageIO.
- **FCPXML:** Versions 1.5–1.14 (DTDs included); Final Cut Pro frame rates (23.976, 24, 25, 29.97, 30, 50, 59.94, 60).
- **Tests:** **1169** tests listed in `swift test list` — **1161** in `OpenFCPXMLKitTests` + **8** optional `ExcelReportTest` (all Swift Testing `@Test`; no XCTest); **60** sample `.fcpxml` files under `Tests/FCPXML Samples/FCPXML/`; private local inbox under `Tests/Submitted FCPXML/` (gitignored — never commit private FCPXML).
- **Tests:** **1173** tests listed in `swift test list` — **1161** in `OpenFCPXMLKitTests` + **8** optional `ExcelReportTest` + **4** optional `ShotExtractionTest` (all Swift Testing `@Test`; no XCTest); **60** sample `.fcpxml` files under `Tests/FCPXML Samples/FCPXML/`; private local inbox under `Tests/Submitted FCPXML/` (gitignored — never commit private FCPXML).

---

Expand Down Expand Up @@ -227,6 +227,7 @@ flowchart TB

TST --> OKT["OpenFCPXMLKitTests — 1161 Swift Testing"]
TST --> ERT["ExcelReportTest — 8 optional Swift Testing"]
TST --> SET["ShotExtractionTest — 4 optional Swift Testing"]
TST --> SMP["FCPXML Samples/ — 60 .fcpxml files"]
TST --> SUB["Submitted FCPXML/ — private inbox gitignored"]
```
Expand Down Expand Up @@ -266,8 +267,17 @@ flowchart TB
FIX --> EXP --> OUT
end

subgraph SET_TREE["ShotExtractionTest/"]
direction TB
SFIX["ShotExtractionFixture — requireFixtureURL"]
SEXP["ShotExtractionExportTests — 4 @Test exports"]
SOUT["Output/ — gitignored PNG/CSV/JSON"]
SFIX --> SEXP --> SOUT
end

HARNESS --> OKT_TREE
FIX -.->|"optional reporting fixture"| HARNESS
SFIX -.->|"optional stills fixture"| HARNESS
```

#### Library layer stack (bottom → top)
Expand Down Expand Up @@ -399,7 +409,7 @@ flowchart TB

**Cross-cutting library folders** (alongside the layer stack): Analysis, Annotations, Authoring, Classes (incl. `VersionFeatureGate`), Delegates, Errors, Extensions (+Modular, +Codable), Implementations, Protocols, Services, Utilities, Export, Timeline, Timing, Validation, FileIO, Media, Logging, Format. Root: `Version.swift`.

**Tests** (see §8 and the Tests layout mermaid above): Swift Testing only; harness under `OpenFCPXMLKitTests/`; optional `ExcelReportTest/`; public `FCPXML Samples/`; private `Submitted FCPXML/`.
**Tests** (see §8 and the Tests layout mermaid above): Swift Testing only; harness under `OpenFCPXMLKitTests/`; optional `ExcelReportTest/` and `ShotExtractionTest/`; public `FCPXML Samples/`; private `Submitted FCPXML/`.

### 3.2 Library folders

Expand Down Expand Up @@ -517,20 +527,20 @@ Source layout under **`Sources/OpenFCPXMLKit/`**:

Binary name: **`OpenFCPXMLKit-CLI`**. Mutually exclusive modes: `--check-version`, `--convert-version`, `--extension-type` (fcpxmld | fcpxml), `--validate`, `--media-copy`, `--extract-shots`, `--report`, `--create-project` (requires `--width`, `--height`, `--rate`, `--project-version`, output-dir).

**`--extract-shots`** extracts primary-timeline still-image shots to PNG + CSV or Notion JSON (`--scene-number` required; `--extract-format csv|notion`; `--folder-format`; optional `--icon`, `--result-file-path`, `--extract-project`; **`--dry-run`** validates and reports shot count without writes — `output-dir` optional). Rejects primary-spine video, titles/generators/Motion templates, and audio. Notion JSON follows the [csv2notion-neo](https://github.com/TheAcharya/csv2notion-neo) JSON import convention. See Manual [21 — Shot Extraction](Documentation/Manual/21-Shot-Extraction.md).
**`--extract-shots`** extracts primary-timeline still-image shots to PNG + CSV or Notion JSON (`--scene-number` required; `--extract-format csv|notion`; `--folder-format`; optional `--icon`, `--result-file-path`, `--extract-project`; **`--dry-run`** validates and reports shot count without writes — `output-dir` optional). Rejects primary-spine video, titles/generators/Motion templates, and audio. Notion JSON follows the [csv2notion-neo](https://github.com/TheAcharya/csv2notion-neo) JSON import convention with **CSV column key order** and Shot ID / timeline array order. Optional local integration: `Tests/ShotExtractionTest/`. See Manual [21 — Shot Extraction](Documentation/Manual/21-Shot-Extraction.md).

**`--report`** builds an Excel workbook from a normal project **or** a standalone compound-clip export (role inventory by default — **Selected Roles Inventory** + per-role sheets). `--report-full` adds every optional sheet. Per-section flags: `--report-markers`, `--report-keywords`, `--report-titles-generators`, `--report-transitions`, `--report-non-standard-effects`, `--report-effects`, `--report-speed-change-effects`, `--report-summary`, `--report-media-summary`. **`--create-pdf`** also writes a `.pdf` from the same built `Report` (sections, column exclusions, timecode format). Filtering: `--exclude-role` (repeatable), `--exclude-column` (repeatable; global column omission including Duplicate Frames / Codecs / Ingest Date / Frame Size), `--exclude-disabled-clips` (omit `enabled="0"` clips), `--include-markers-outside-clip-boundaries` (out-of-bounds markers + Markers **Hidden** column), `--protect-sheets` (Excel worksheet edit lock on every sheet — not encryption; PDF unaffected), `--report-project` (project or compound-clip name), `--label-copyright`. Timecode cells: `--timecode-format` (`HH:MM:SS:FF` default, `Frames`, `Feet+Frames`, `HH:MM:SS`). Progress labels follow `ReportBuildPhase.enabledPhases(for:)` (inventory first; Non-Std before Effects), then Saving Workbook, then Saving PDF when `--create-pdf` is set. Log options: `--log`, `--log-level`, `--quiet`. See `Sources/OpenFCPXMLKitCLI/README.md` and `Documentation/Manual/19-CLI.md`.

---

## 8. Tests

- **Count:** **1169** listed in `swift test list` — **1161** in `OpenFCPXMLKitTests` + **8** in optional `ExcelReportTest` (all Swift Testing `@Test`; **no XCTest** in `Tests/`). ExcelReportTest **cancels** via `Test.cancel` without a local `.fcpxml`/`.fcpxmld` fixture.
- **Count:** **1173** listed in `swift test list` — **1161** in `OpenFCPXMLKitTests` + **8** in optional `ExcelReportTest` + **4** in optional `ShotExtractionTest` (all Swift Testing `@Test`; **no XCTest** in `Tests/`). Optional targets **cancel** via `Test.cancel` without a local `.fcpxml`/`.fcpxmld` fixture.
- **Framework:** Swift Testing exclusively (`@Suite` / `@Test` / `#expect` / `#require`). See GUARDRAILS Sign: `swift-testing-only`.
- **Location:** `Tests/OpenFCPXMLKitTests/`; public samples in `Tests/FCPXML Samples/FCPXML/` (60 files, including `HiddenMarkers.fcpxml`); optional integration under `Tests/ExcelReportTest/`; private investigation inbox under `Tests/Submitted FCPXML/` (gitignored `Inbox/` / `Notes/` — never commit private FCPXML to GitHub; see `Tests/Submitted FCPXML/README.md`).
- **Harness:** `FCPXMLTestResources.swift` (paths); `FCPXMLTestSampleLoading.swift` + `FCPXMLTestSampleError.swift` (`tryLoad*`); `FCPXMLTestingSampleSupport.swift` (`require*` — bundled samples **fail** if missing; optional fixtures use `Test.cancel`); `FCPXMLReportingReportFixture.swift` / `FCPXMLReportingReportTestSupport.swift` for optional reporting fixtures; `FCPXMLSubmittedFCPXMLSmokeTests` for optional Inbox parse smoke; `FCPXMLShotExtractionTests` (**10** `@Test`) for still-image Shot Extraction (reject video/titles/audio; dry-run); `ExcelReportFixture` for the ExcelReportTest target.
- **Location:** `Tests/OpenFCPXMLKitTests/`; public samples in `Tests/FCPXML Samples/FCPXML/` (60 files, including `HiddenMarkers.fcpxml`); optional integration under `Tests/ExcelReportTest/` and `Tests/ShotExtractionTest/`; private investigation inbox under `Tests/Submitted FCPXML/` (gitignored `Inbox/` / `Notes/` — never commit private FCPXML to GitHub; see `Tests/Submitted FCPXML/README.md`).
- **Harness:** `FCPXMLTestResources.swift` (paths); `FCPXMLTestSampleLoading.swift` + `FCPXMLTestSampleError.swift` (`tryLoad*`); `FCPXMLTestingSampleSupport.swift` (`require*` — bundled samples **fail** if missing; optional fixtures use `Test.cancel`); `FCPXMLReportingReportFixture.swift` / `FCPXMLReportingReportTestSupport.swift` for optional reporting fixtures; `FCPXMLSubmittedFCPXMLSmokeTests` for optional Inbox parse smoke; `FCPXMLShotExtractionTests` (**10** `@Test`) for still-image Shot Extraction (reject video/titles/audio; dry-run); `ExcelReportFixture` for the ExcelReportTest target; `ShotExtractionFixture` for the ShotExtractionTest target.
- **Performance:** `FCPXMLPerformanceTests` uses `ContinuousClock().measure` with generous sanity budgets (hang guards), not XCTest `measure` baselines.
- **Reporting tests:** `FCPXMLCompoundClipReportTests` (standalone compound-clip FCPXML / `allReportTimelineSources()`), `FCPXMLMarkersReportTests` / `FCPXMLFileTest_HiddenMarkers` (chapter markers default on; out-of-bounds markers + **Hidden** column), `FCPXMLMarkersKeywordsProjectionTests` (mc-clip / connected-clip hosts; zero-row Extraction fallback), `FCPXMLReportTimecodeFormatTests` (DF/NDF, all four formats, format-aware headers, full-report shape), `FCPXMLReportBuildPhaseTests` (inventory-first `enabledPhases` / Non-Std before Effects / `onPhaseStarted` order), `FCPXMLRoleInventoryColumnLayoutTests` (**26** fixed columns including Duplicate Frames / Codecs / Ingest Date / Frame Size / Audio Config), `FCPXMLRoleInventorySheetTotalTests`, `FCPXMLRoleInventoryDuplicateFramesTests`, `FCPXMLRoleInventoryClipCollectorTests` / `FCPXMLClipParsingCarriesAudioTests` (nested connected own-assignment hosts; occluded retention; Sign `connected-role-inventory-survives-nesting`; under-spine connected titles with custom `Title.role` and under-spine leaf video/generators; Sign `title-roles-honor-attribute`), `FCPXMLNonStandardEffectsTemplatesReportTests`, `FCPXMLReportColumnExclusionTests` (including `ensuringRowColumn` / `allowsInjectedRowColumn`, suffixed Timeline In headers, Row on tabular sheets, Role `>` aliases, per-role sheets keep data when Role ▸ Subrole excluded, colours survive Role+Category exclusion), `FCPXMLReportExcludeDisabledClipsTests`, `FCPXMLRoleDisplayPreferenceTests` (effects type-filter + `.builtIn` priorities), `FCPXMLSummaryReportTests` / `FCPXMLSummaryRoleDurationAggregatorTests` (`formattedPercentOfTotal`, subtotals), `FCPXMLReportExcelExportTests` (workbook cell formatting; Summary **B1** banner / visual-section subtotal; Non-Std Kind colours; effects role colours; per-role **Total:** footer; section-sheet Row columns; empty-sheet status rows; **`protectSheets`**), `FCPXMLReportPDFExportTests` (cover notes / black header + `info.circle`, TOC, Summary subtotal + `% of Total` parity, section parity including Non-Std when present, empty-sheet status rows, pagination, branding), `FCPXMLReportPDFSheetPlanTests` (TOC accent chips share sequential `colorIndex` with content-page tints; empty enabled sections remain in plan), `FCPXMLReportPDFTableLayoutTests` (remaining columns expand to fill page width after exclusions; pinned Row; `allowInjectedRowColumn`; horizontal chunks still fill `contentWidth`), `FCPXMLReportFormattingTests` (SMPTE / Frames / Feet+Frames / HH:MM:SS formatting and numeric sort guardrails; effects Video/Dialogue defaults), plus role inventory, section, Projection-first section tests, and related support tests. Optional `ExcelReportTest` (**8** `@Test`) writes `OFK-Default` / `OFK-Full` (xlsx + pdf) / `OFK-ExcludedColumns` / `OFK-Copyright` / `OFK-OutsideClipBoundaries` / `OFK-ProtectedSheets` / `OFK-ExcludeRoleSubrole` among other outputs. See **Tests/README.md** for the full file tree.
- **Reporting tests:** `FCPXMLCompoundClipReportTests` (standalone compound-clip FCPXML / `allReportTimelineSources()`), `FCPXMLMarkersReportTests` / `FCPXMLFileTest_HiddenMarkers` (chapter markers default on; out-of-bounds markers + **Hidden** column), `FCPXMLMarkersKeywordsProjectionTests` (mc-clip / connected-clip hosts; zero-row Extraction fallback), `FCPXMLReportTimecodeFormatTests` (DF/NDF, all four formats, format-aware headers, full-report shape), `FCPXMLReportBuildPhaseTests` (inventory-first `enabledPhases` / Non-Std before Effects / `onPhaseStarted` order), `FCPXMLRoleInventoryColumnLayoutTests` (**26** fixed columns including Duplicate Frames / Codecs / Ingest Date / Frame Size / Audio Config), `FCPXMLRoleInventorySheetTotalTests`, `FCPXMLRoleInventoryDuplicateFramesTests`, `FCPXMLRoleInventoryClipCollectorTests` / `FCPXMLClipParsingCarriesAudioTests` (nested connected own-assignment hosts; occluded retention; Sign `connected-role-inventory-survives-nesting`; under-spine connected titles with custom `Title.role` and under-spine leaf video/generators; Sign `title-roles-honor-attribute`), `FCPXMLNonStandardEffectsTemplatesReportTests`, `FCPXMLReportColumnExclusionTests` (including `ensuringRowColumn` / `allowsInjectedRowColumn`, suffixed Timeline In headers, Row on tabular sheets, Role `>` aliases, per-role sheets keep data when Role ▸ Subrole excluded, colours survive Role+Category exclusion), `FCPXMLReportExcludeDisabledClipsTests`, `FCPXMLRoleDisplayPreferenceTests` (effects type-filter + `.builtIn` priorities), `FCPXMLSummaryReportTests` / `FCPXMLSummaryRoleDurationAggregatorTests` (`formattedPercentOfTotal`, subtotals), `FCPXMLReportExcelExportTests` (workbook cell formatting; Summary **B1** banner / visual-section subtotal; Non-Std Kind colours; effects role colours; per-role **Total:** footer; section-sheet Row columns; empty-sheet status rows; **`protectSheets`**), `FCPXMLReportPDFExportTests` (cover notes / black header + `info.circle`, TOC, Summary subtotal + `% of Total` parity, section parity including Non-Std when present, empty-sheet status rows, pagination, branding), `FCPXMLReportPDFSheetPlanTests` (TOC accent chips share sequential `colorIndex` with content-page tints; empty enabled sections remain in plan), `FCPXMLReportPDFTableLayoutTests` (remaining columns expand to fill page width after exclusions; pinned Row; `allowInjectedRowColumn`; horizontal chunks still fill `contentWidth`), `FCPXMLReportFormattingTests` (SMPTE / Frames / Feet+Frames / HH:MM:SS formatting and numeric sort guardrails; effects Video/Dialogue defaults), plus role inventory, section, Projection-first section tests, and related support tests. Optional `ExcelReportTest` (**8** `@Test`) writes `OFK-Default` / `OFK-Full` (xlsx + pdf) / `OFK-ExcludedColumns` / `OFK-Copyright` / `OFK-OutsideClipBoundaries` / `OFK-ProtectedSheets` / `OFK-ExcludeRoleSubrole` among other outputs. Optional `ShotExtractionTest` (**4** `@Test`) writes PNG + CSV / Notion JSON aliases (`OFK-Shots.csv` / `.json`). See **Tests/README.md** for the full file tree.
- **Coverage:** Unit, integration, and performance smoke tests; sync and async; all supported frame rates and FCPXML versions. See **Tests/README.md** for categories and how to run tests.
- **Manual cross-links:** [12 — Timeline Projection](Documentation/Manual/12-Timeline-Projection.md), [17 — Cross-Platform & iOS](Documentation/Manual/17-Cross-Platform-iOS.md), [19 — CLI](Documentation/Manual/19-CLI.md), [20 — Reporting](Documentation/Manual/20-Reporting.md), [21 — Shot Extraction](Documentation/Manual/21-Shot-Extraction.md), [22 — Examples](Documentation/Manual/22-Examples.md).

Expand Down
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,24 @@ OpenFCPXMLKit uses **New Features**, **Improvements**, and **Bug Fixes** for eac

---

## [3.3.4](https://github.com/TheAcharya/OpenFCPXMLKit/releases/tag/3.3.4) - 2026-08-06

### ✨ New Features

- None in this release.

### 🔧 Improvements

- **Shot Extraction Notion JSON key order:** Notion (`.json`) manifests emit object keys in the same order as the CSV Shot Data columns (`ShotManifestSchema.columns`), and keep the shot array in Shot ID / timeline order. Previously `JSONSerialization` + `.sortedKeys` alphabetised keys.
- **ShotExtractionTest:** Optional Swift Testing integration target (**4** `@Test`) mirroring `ExcelReportTest` — local stills fixture → PNG + CSV / Notion JSON under `Tests/ShotExtractionTest/Output/` (`OFK-Shots.csv` / `.json` aliases); cancels without fixture or when media is missing.
- **Documentation sync:** Manual 19 / 21, Coverage, Documentation README, Tests READMEs (incl. Submitted FCPXML), README, ARCHITECTURE (Mermaid `ShotExtractionTest` **4**), AGENT, `.cursorrules`, and GUARDRAILS refreshed for Notion CSV-parity key order and the new integration target. Suite counts **1173** listed (**1161** + **8** + **4**).

### 🐛 Bug Fixes

- None in this release.

---

## [3.3.3](https://github.com/TheAcharya/OpenFCPXMLKit/releases/tag/3.3.3) - 2026-07-28

### ✨ New Features
Expand Down
Loading