diff --git a/.cursorrules b/.cursorrules index aad7b1e..f904914 100644 --- a/.cursorrules +++ b/.cursorrules @@ -40,7 +40,7 @@ OpenFCPXMLKit targets macOS 26+, iOS 26+, Xcode 26+, and Swift 6.3 with full con **Backward compatibility:** The entire codebase must remain backward compatible with FCPXML 1.5. Optional attributes and elements introduced in later versions (e.g. 1.11, 1.13) must be omitted or ignored when reading/writing or converting to 1.5; mark such features in code comments with the minimum FCPXML version (e.g. `FCPXML 1.13+`). -Current status: **925** tests listed in `swift test --list-tests` (**924** in `OpenFCPXMLKitTests`: 921 XCTest + 3 Swift Testing `@Test`; plus **1** optional `ExcelReportTest` integration); FCPXML versions 1.5–1.14 supported (DTDs included, full parsing, typed element-type coverage for all DTD elements via FCPXMLElementType); Final Cut Pro frame rates (23.976, 24, 25, 29.97, 30, 50, 59.94, 60); thread-safe and concurrency-compliant with comprehensive async/await support; no known security vulnerabilities. Version conversion automatically drops elements not in the target version’s DTD (e.g. adjust-colorConform, adjust-stereo-3D); DTD validation runs per version (validateDocumentAgainstDTD, validateDocumentAgainstDeclaredVersion) and after CLI convert. FCPXMLVersion.supportsBundleFormat is true for 1.10+ (.fcpxmld bundle); 1.5–1.9 support only single-file .fcpxml. FCPXML creation: create FCPXML documents from scratch with events, projects, resources, and clips via XMLDocumentManager, XMLDocument initializers, or FCPXMLService. Timeline manipulation: ripple insert (shifts subsequent clips), auto lane assignment, clip queries (by lane, time range, asset ID), lane range computation, secondary storylines. Timeline metadata: markers, chapter markers, keywords, ratings, custom metadata, timestamps (createdAt, modifiedAt). FCPXMLTimecode: custom timecode type (arithmetic, frame alignment, CMTime conversion, FCPXML string parsing). MIME type detection, asset validation, silence detection, asset duration measurement, parallel file I/O, still image asset support. TimelineFormat enhancements: presets (hd720p, dci4K, hd1080i, hd720i), computed properties (aspectRatio, isHD, isUHD, interlaced). Typed adjustment models: Crop, Transform, Blend, Stabilization, Volume, Loudness, NoiseReduction, HumReduction, Equalization, MatchEqualization, Transform360, ColorConform, Stereo3D, VoiceIsolation with full clip integration. Typed effect/filter models: VideoFilter, AudioFilter, VideoFilterMask with FilterParameter support and keyframe animation (auxValue support FCPXML 1.11+). Typed caption/title models: Caption and Title with TextStyle and TextStyleDefinition for full text formatting. SmartCollection models: SmartCollection with match-clip, match-media, match-ratings, match-text, match-usage (1.9+), match-representation (1.10+), match-markers (1.10+), match-analysis-type (1.14). Keyframe animation: KeyframeAnimation, Keyframe with interpolation types, FadeIn/FadeOut with fade types, integrated with FilterParameter. CMTime Codable extension: Direct CMTime encoding/decoding as FCPXML time strings. Collection organization: CollectionFolder and KeywordCollection models for organizing clips and media. Live Drawing (FCPXML 1.11+): LiveDrawing model for live-drawing story elements. HiddenClipMarker (FCPXML 1.13+): HiddenClipMarker model for hidden clip markers. Format/Asset 1.13+: Format heroEye, Asset heroEyeOverride, Asset mediaReps (multiple media-rep). Cross-platform XML abstraction: protocol layer (OFKXMLNode, OFKXMLElement, OFKXMLDocument, OFKXMLFactory); Foundation backend on macOS; AEXML backend on iOS; OFKXMLDefaultFactory() for platform dispatch; FCPXMLStructuralValidator for cross-platform structural validation; FCPXMLDTDValidator platform-conditional (full DTD on macOS, structural fallback on iOS). Comprehensive test coverage: **925** tests across 58 FCPXML sample files including 360 video, auditions, conform-rate, still images, multicam, secondary storylines, audio keyframes (FCPXMLAudioKeyframeTests: adjust-volume param keyframeAnimation parsing, decibel/time validation, fadeIn/fadeOut integration, secondary storyline detection), keyword collections/folders, Photoshop integration, smart collections, and reporting column layout/exclusion/disabled-clip/workbook-formatting tests. Excel reporting: multi-sheet `.xlsx` workbooks via `FinalCutPro.FCPXML.buildReport(options:)` (ReportBuilder, ReportOptions presets, ReportExcelExport on XLKit); sheets for Role Inventory (**Selected Roles Inventory** + per-role sheets with expanded column layout and dynamic metadata keys), Markers, Keywords, Titles & Generators, Transitions, Video & Audio Effects, Speed Change Effects, **Summary** (project title header row, black role-duration data), and **Media Summary** (red missing-media paths); inventory and section-sheet cell formatting, role exclusions, global column exclusion (`ReportColumn` / `excludedColumns`), disabled-clip filtering (`excludeDisabledClips`), project-name filtering, `ReportTimecodeFormat` / `--timecode-format`, inventory-first `ReportBuildPhase` progress callbacks. Extraction presets: Captions, Effects, FrameData, Markers, Roles, Titles. Experimental CLI (OpenFCPXMLKit-CLI): single binary with embedded DTDs; --check-version, --convert-version (stripping + DTD validation), --extension-type (fcpxmld | fcpxml; default fcpxmld; 1.5–1.9 always .fcpxml), --validate, --media-copy, --create-project (new empty FCPXML project: --width, --height, --rate, --project-version, output-dir; DTD validation before write; FCP-style output with DOCTYPE, colorSpace, default smart collections), --report (Excel report: role inventory by default; --report-full, per-section flags including --report-markers, --report-keywords, --report-titles-generators, --report-transitions, --report-effects, --report-speed-change-effects, --report-summary, --report-media-summary, --exclude-role, --exclude-column, --exclude-disabled-clips, --timecode-format, --report-project); --log writes user-visible output for all commands to the log file; see Sources/OpenFCPXMLKitCLI/README.md. +Current status: **933** tests listed in `swift test --list-tests` (**932** in `OpenFCPXMLKitTests`: 929 XCTest + 3 Swift Testing `@Test`; plus **1** optional `ExcelReportTest` integration); FCPXML versions 1.5–1.14 supported (DTDs included, full parsing, typed element-type coverage for all DTD elements via FCPXMLElementType); Final Cut Pro frame rates (23.976, 24, 25, 29.97, 30, 50, 59.94, 60); thread-safe and concurrency-compliant with comprehensive async/await support; no known security vulnerabilities. Version conversion automatically drops elements not in the target version’s DTD (e.g. adjust-colorConform, adjust-stereo-3D); DTD validation runs per version (validateDocumentAgainstDTD, validateDocumentAgainstDeclaredVersion) and after CLI convert. FCPXMLVersion.supportsBundleFormat is true for 1.10+ (.fcpxmld bundle); 1.5–1.9 support only single-file .fcpxml. FCPXML creation: create FCPXML documents from scratch with events, projects, resources, and clips via XMLDocumentManager, XMLDocument initializers, or FCPXMLService. Timeline manipulation: ripple insert (shifts subsequent clips), auto lane assignment, clip queries (by lane, time range, asset ID), lane range computation, secondary storylines. Timeline metadata: markers, chapter markers, keywords, ratings, custom metadata, timestamps (createdAt, modifiedAt). FCPXMLTimecode: custom timecode type (arithmetic, frame alignment, CMTime conversion, FCPXML string parsing). MIME type detection, asset validation, silence detection, asset duration measurement, parallel file I/O, still image asset support. TimelineFormat enhancements: presets (hd720p, dci4K, hd1080i, hd720i), computed properties (aspectRatio, isHD, isUHD, interlaced). Typed adjustment models: Crop, Transform, Blend, Stabilization, Volume, Loudness, NoiseReduction, HumReduction, Equalization, MatchEqualization, Transform360, ColorConform, Stereo3D, VoiceIsolation with full clip integration. Typed effect/filter models: VideoFilter, AudioFilter, VideoFilterMask with FilterParameter support and keyframe animation (auxValue support FCPXML 1.11+). Typed caption/title models: Caption and Title with TextStyle and TextStyleDefinition for full text formatting. SmartCollection models: SmartCollection with match-clip, match-media, match-ratings, match-text, match-usage (1.9+), match-representation (1.10+), match-markers (1.10+), match-analysis-type (1.14). Keyframe animation: KeyframeAnimation, Keyframe with interpolation types, FadeIn/FadeOut with fade types, integrated with FilterParameter. CMTime Codable extension: Direct CMTime encoding/decoding as FCPXML time strings. Collection organization: CollectionFolder and KeywordCollection models for organizing clips and media. Live Drawing (FCPXML 1.11+): LiveDrawing model for live-drawing story elements. HiddenClipMarker (FCPXML 1.13+): HiddenClipMarker model for hidden clip markers. Format/Asset 1.13+: Format heroEye, Asset heroEyeOverride, Asset mediaReps (multiple media-rep). Cross-platform XML abstraction: protocol layer (OFKXMLNode, OFKXMLElement, OFKXMLDocument, OFKXMLFactory); Foundation backend on macOS; AEXML backend on iOS; OFKXMLDefaultFactory() for platform dispatch; FCPXMLStructuralValidator for cross-platform structural validation; FCPXMLDTDValidator platform-conditional (full DTD on macOS, structural fallback on iOS). Comprehensive test coverage: **933** tests across 58 FCPXML sample files including 360 video, auditions, conform-rate, still images, multicam, secondary storylines, audio keyframes (FCPXMLAudioKeyframeTests: adjust-volume param keyframeAnimation parsing, decibel/time validation, fadeIn/fadeOut integration, secondary storyline detection), keyword collections/folders, Photoshop integration, smart collections, and reporting column layout/exclusion/disabled-clip/workbook-formatting tests. Excel reporting: multi-sheet `.xlsx` workbooks via `FinalCutPro.FCPXML.buildReport(options:)` (ReportBuilder, ReportOptions presets, ReportExcelExport on XLKit); sheets for Role Inventory (**Selected Roles Inventory** + per-role sheets with expanded column layout and dynamic metadata keys), Markers, Keywords, Titles & Generators, Transitions, Video & Audio Effects, Speed Change Effects, **Summary** (project title header row, black role-duration data), and **Media Summary** (red missing-media paths); inventory and section-sheet cell formatting, role exclusions, global column exclusion (`ReportColumn` / `excludedColumns`), disabled-clip filtering (`excludeDisabledClips`), project-name / compound-clip-name filtering (`allReportTimelineSources()`; standalone compound-clip exports without ``), `ReportTimecodeFormat` / `--timecode-format`, inventory-first `ReportBuildPhase` progress callbacks. Extraction presets: Captions, Effects, FrameData, Markers, Roles, Titles. Experimental CLI (OpenFCPXMLKit-CLI): single binary with embedded DTDs; --check-version, --convert-version (stripping + DTD validation), --extension-type (fcpxmld | fcpxml; default fcpxmld; 1.5–1.9 always .fcpxml), --validate, --media-copy, --create-project (new empty FCPXML project: --width, --height, --rate, --project-version, output-dir; DTD validation before write; FCP-style output with DOCTYPE, colorSpace, default smart collections), --report (Excel report: role inventory by default; --report-full, per-section flags including --report-markers, --report-keywords, --report-titles-generators, --report-transitions, --report-effects, --report-speed-change-effects, --report-summary, --report-media-summary, --exclude-role, --exclude-column, --exclude-disabled-clips, --timecode-format, --report-project); --log writes user-visible output for all commands to the log file; see Sources/OpenFCPXMLKitCLI/README.md. Xcode 26 dynamic linking compatibility: `swift-log` (`Logging`) is an explicit direct dependency in `Package.swift` to satisfy stricter transitive dylib linking rules when building OpenFCPXMLKit as a dynamic framework. @@ -52,7 +52,7 @@ The project was fully rewritten and refactored to achieve: - A protocol-oriented design: parsing, timecode conversion, XML manipulation, error handling, MIME type detection, asset validation, silence detection, asset duration measurement, and parallel file I/O are defined as protocols (e.g. FCPXMLParsing, TimecodeConversion, XMLDocumentOperations, ErrorHandling, MIMETypeDetection, AssetValidation, SilenceDetection, AssetDurationMeasurement, ParallelFileIO) with sync and async/await methods. - A single injection point for extension APIs that cannot take parameters: FCPXMLUtility.defaultForExtensions (concurrency-safe). No hidden concrete types in extensions; for custom services use the modular API with the using: parameter. -- Consistent source layout: Analysis, Classes, Delegates, Errors, Extensions (including +Modular and +Codable), Implementations, Protocols, Services, Utilities, Annotations, Export, Timeline, Timing, Validation, FileIO, Logging, Format, Model (with subfolders), Parsing, Extraction, Reporting (including Excel/ for XLKit workbook export), XML (Protocols: OFKXMLNode, OFKXMLElement, OFKXMLDocument, OFKXMLDTDProtocol, OFKXMLFactory; Foundation/ and AEXML/ backends; OFKXMLDefaultFactory), and FCPXML DTDs. +- Consistent source layout: Analysis, Classes, Delegates, Errors, Extensions (including +Modular and +Codable), Implementations, Protocols, Services, Utilities, Annotations, Export, Timeline, Timing, Validation, FileIO, Logging, Format, Model (with subfolders), Parsing, Extraction, Reporting (including Excel/ for XLKit workbook export; `ReportBuilder` resolves via `allReportTimelineSources()`), XML (Protocols: OFKXMLNode, OFKXMLElement, OFKXMLDocument, OFKXMLDTDProtocol, OFKXMLFactory; Foundation/ and AEXML/ backends; OFKXMLDefaultFactory), and FCPXML DTDs. - A structured test suite: shared resources, file tests per sample, logic/parsing tests, timeline/export/validation tests, API and edge-case tests, and performance tests, all documented in Tests/README.md. Foundation XML types (XMLDocument, XMLElement), protocol types (OFKXMLDocument, OFKXMLElement), and SwiftTimecode types are not Sendable. The codebase avoids Task-based concurrency for these types but provides async/await APIs that are concurrency-safe for Swift 6. If these dependencies become Sendable in the future, further parallelisation can be introduced. @@ -67,7 +67,7 @@ Foundation XML types (XMLDocument, XMLElement), protocol types (OFKXMLDocument, - ModularUtilities.validateDocument delegates to FCPXMLValidator for semantic validation (root element, resources, ref resolution). convertTimecodes uses the injected FCPXMLTimeStringConversion protocol rather than inline parsing. - Logging: ServiceLogger (ServiceLogLevel: trace, debug, info, notice, warning, error, critical); NoOpServiceLogger, PrintServiceLogger, FileServiceLogger. FCPXMLUtility and FCPXMLService use injected logger; CLI supports --log, --log-level, --quiet. When --log is set, all CLI commands (check-version, convert-version, validate, media-copy, create-project) write their user-visible messages to the log file. Extension types use #if canImport(Logging) as fallback without DI. - FCPXMLVersion (DTD validation, 1.5-1.14) and FinalCutPro.FCPXML.Version (parsing, 1.0-1.14) are bridged via .fcpxmlVersion, .dtdVersion, and init(from:) converters. -- Reporting: Excel report builders in `Reporting/` consume Extraction/Model; extend Model/Parsing and Extraction before adding report-only XML walks. Sheet formatting, column layout (`RoleInventoryColumnLayout`), global column exclusion (`ReportColumnExclusion`, including format-suffixed headers), disabled-clip filtering (`excludeDisabledClips`), `ReportTimecodeFormat` / format-aware headers, inventory-first `ReportBuildPhase.enabledPhases(for:)` progress, workbook cell colours (`FCPXMLReportWorkbookExporter`, `RoleRowColorContext`), and inclusion rules stay in Reporting. Summary and Media Summary are separate sheets. See ARCHITECTURE.md §2.7. +- Reporting: Excel report builders in `Reporting/` consume Extraction/Model; extend Model/Parsing and Extraction before adding report-only XML walks. Sheet formatting, column layout (`RoleInventoryColumnLayout`), global column exclusion (`ReportColumnExclusion`, including format-suffixed headers), disabled-clip filtering (`excludeDisabledClips`), `ReportTimecodeFormat` / format-aware headers, inventory-first `ReportBuildPhase.enabledPhases(for:)` progress, workbook cell colours (`FCPXMLReportWorkbookExporter`, `RoleRowColorContext`), and inclusion rules stay in Reporting. Summary and Media Summary are separate sheets. Timeline resolution via `allReportTimelineSources()` / `ReportTimelineSource` (projects + standalone compound-clip exports). See ARCHITECTURE.md §2.7. - Module-scoped errors: FCPXMLError for parsing, FCPXMLLoadError for file I/O (notAFile, readFailed), FCPXMLExportError/FCPXMLBundleExportError for export, FinalCutPro.FCPXML.ParseError (with LocalizedError). Parse failures from all layers surface as FCPXMLError.parsingFailed. FCPXMLElementError uses String element names for Sendable compliance. FCPXMLDocumentError uses camelCase cases (dtdResourceNotFound, dtdResourceUnreadable). - All code is Sendable where appropriate; `@unchecked Sendable` removed from delegates (internal-only, used synchronously). The project builds and tests with Swift 6 strict concurrency (-strict-concurrency=complete). CI runs a job that enforces this. - No known vulnerabilities in dependencies (including SwiftTimecode 3.1.2) as of July 2025. No unsafe pointers, dynamic code execution, or C APIs; concurrency is structured and type-safe. @@ -128,7 +128,7 @@ SwiftTimecode usage: use Timecode(.realTime(seconds: seconds), at: frameRate) in ## File Organisation -Source structure: layout is Analysis (EditPoint, CutDetectionResult), Classes (FinalCutPro, FCPXML core types, FCPXMLElementType, FCPXMLUtility, FCPXMLVersion, FCPXMLRoot, FCPXMLRootVersion), Delegates, Errors (FCPXMLError, FCPXMLParseError, TimelineError), Extensions (including +Modular and +Codable; FCPXML extensions operate on OFKXMLDocument/OFKXMLElement), Implementations (FCPXMLParser, TimecodeConverter, XMLDocumentManager, ErrorHandler, CutDetector, FCPXMLVersionConverter, MediaExtractor, MIMETypeDetector, AssetValidator, SilenceDetector, AssetDurationMeasurer, ParallelFileIOExecutor), Protocols (FCPXMLParsing, TimecodeConversion, XMLDocumentOperations, ErrorHandling, CutDetection, FCPXMLVersionConverting, MediaExtraction, MIMETypeDetection, AssetValidation, SilenceDetection, AssetDurationMeasurement, ParallelFileIO), Services, Utilities (ModularUtilities, FCPXMLTimeUtilities, SequencePlusAnySequence, XMLElementAncestorWalking, XMLElementSequenceAttributes), Annotations (creation-oriented value types; for parsing models see Model/), Export (FCPXMLExporter, FCPXMLBundleExporter, FCPXMLExportAsset), Timeline (Timeline with manipulation methods, TimelineClip with asset validation methods, TimelineFormat with presets and computed properties), Timing (FCPXMLTimecode), Validation (FCPXMLValidator, FCPXMLDTDValidator, FCPXMLStructuralValidator, ValidationResult, ValidationError/Warning), FileIO (FCPXMLFileLoader), Media (MediaReference, MediaExtractionResult, MediaCopyResult), Logging (ServiceLogger, ServiceLogLevel, NoOpServiceLogger, PrintServiceLogger, FileServiceLogger), Format (ColorSpace), Model (element models with subfolders: Adjustments (CropAdjustment, TransformAdjustment, BlendAdjustment, StabilizationAdjustment, VolumeAdjustment, LoudnessAdjustment, NoiseReductionAdjustment, HumReductionAdjustment, EqualizationAdjustment, MatchEqualizationAdjustment, Transform360Adjustment), Animations (KeyframeAnimation, Keyframe, FadeIn, FadeOut, FadeType), Attributes, Clips including Clip+Adjustments, Title+Typed, CommonElements including Text, TextStyle, TextStyleDefinition, ElementTypes, Filters (VideoFilter, AudioFilter, VideoFilterMask, FilterParameter), Occlusion, Protocols, Resources, Roles, Structure including CollectionFolder, KeywordCollection), Parsing (XML parsing extensions), Extraction (extraction logic with Context/, Effects/, and Presets/ subfolders; presets: Captions, Effects, FrameData, Markers, Roles, Titles), Reporting (Excel workbook reports: Report/ReportOptions/ReportBuilder/ReportTimecodeFormat/ReportBuildProgress, Builders/, Sections/, Rows/, Support/ including RoleInventoryColumnLayout, ReportColumnExclusion, ReportFormatting; Excel/ for XLKit workbook export; consumes Extraction, owns presentation only — see ARCHITECTURE.md §2.7), XML (Protocols: OFKXMLNode, OFKXMLElement, OFKXMLDocument, OFKXMLDTDProtocol, OFKXMLFactory; Foundation/ and AEXML/ backends; OFKXMLDefaultFactory), FCPXML DTDs. Group related functionality in extensions; keep files focused on single responsibilities; use clear file naming conventions; organise imports logically; maintain the existing directory structure. +Source structure: layout is Analysis (EditPoint, CutDetectionResult), Classes (FinalCutPro, FCPXML core types including `allReportTimelineSources` / `ReportTimelineSource`, FCPXMLElementType, FCPXMLUtility, FCPXMLVersion, FCPXMLRoot, FCPXMLRootVersion), Delegates, Errors (FCPXMLError, FCPXMLParseError, TimelineError), Extensions (including +Modular and +Codable; FCPXML extensions operate on OFKXMLDocument/OFKXMLElement), Implementations (FCPXMLParser, TimecodeConverter, XMLDocumentManager, ErrorHandler, CutDetector, FCPXMLVersionConverter, MediaExtractor, MIMETypeDetector, AssetValidator, SilenceDetector, AssetDurationMeasurer, ParallelFileIOExecutor), Protocols (FCPXMLParsing, TimecodeConversion, XMLDocumentOperations, ErrorHandling, CutDetection, FCPXMLVersionConverting, MediaExtraction, MIMETypeDetection, AssetValidation, SilenceDetection, AssetDurationMeasurement, ParallelFileIO), Services, Utilities (ModularUtilities, FCPXMLTimeUtilities, SequencePlusAnySequence, XMLElementAncestorWalking, XMLElementSequenceAttributes), Annotations (creation-oriented value types; for parsing models see Model/), Export (FCPXMLExporter, FCPXMLBundleExporter, FCPXMLExportAsset), Timeline (Timeline with manipulation methods, TimelineClip with asset validation methods, TimelineFormat with presets and computed properties), Timing (FCPXMLTimecode), Validation (FCPXMLValidator, FCPXMLDTDValidator, FCPXMLStructuralValidator, ValidationResult, ValidationError/Warning), FileIO (FCPXMLFileLoader), Media (MediaReference, MediaExtractionResult, MediaCopyResult), Logging (ServiceLogger, ServiceLogLevel, NoOpServiceLogger, PrintServiceLogger, FileServiceLogger), Format (ColorSpace), Model (element models with subfolders: Adjustments (CropAdjustment, TransformAdjustment, BlendAdjustment, StabilizationAdjustment, VolumeAdjustment, LoudnessAdjustment, NoiseReductionAdjustment, HumReductionAdjustment, EqualizationAdjustment, MatchEqualizationAdjustment, Transform360Adjustment), Animations (KeyframeAnimation, Keyframe, FadeIn, FadeOut, FadeType), Attributes, Clips including Clip+Adjustments, Title+Typed, CommonElements including Text, TextStyle, TextStyleDefinition, ElementTypes, Filters (VideoFilter, AudioFilter, VideoFilterMask, FilterParameter), Occlusion, Protocols, Resources, Roles, Structure including CollectionFolder, KeywordCollection), Parsing (XML parsing extensions), Extraction (extraction logic with Context/, Effects/, and Presets/ subfolders; presets: Captions, Effects, FrameData, Markers, Roles, Titles), Reporting (Excel workbook reports: Report/ReportOptions/ReportBuilder/ReportTimecodeFormat/ReportBuildProgress, Builders/, Sections/, Rows/, Support/ including RoleInventoryColumnLayout, ReportColumnExclusion, ReportFormatting; Excel/ for XLKit workbook export; consumes Extraction, owns presentation only — see ARCHITECTURE.md §2.7), XML (Protocols: OFKXMLNode, OFKXMLElement, OFKXMLDocument, OFKXMLDTDProtocol, OFKXMLFactory; Foundation/ and AEXML/ backends; OFKXMLDefaultFactory), FCPXML DTDs. Group related functionality in extensions; keep files focused on single responsibilities; use clear file naming conventions; organise imports logically; maintain the existing directory structure. --- @@ -142,9 +142,9 @@ Tests live under Tests/. Full description is in Tests/README.md. Summary: - Tests/FCPXML Samples/FCPXML/: sample .fcpxml files (58 samples). File tests and logic tests load these via shared utilities; tests that require a missing sample use XCTSkip. -- Tests/OpenFCPXMLKitTests/: FCPXMLTestResources.swift (packageRoot, fcpxmlSamplesDirectory, urlForFCPXMLSample, FCPXMLSampleName); FCPXMLTestUtilities.swift (loadFCPXMLSampleData, loadFCPXMLSample, fcpxmlFrameRateSampleNames, allFCPXMLSampleNames; XCTSkip when sample missing); FCPXMLReportingReportFixture.swift and FCPXMLReportingReportTestSupport.swift (optional reporting integration fixtures/assertions). OpenFCPXMLKitTests.swift: main test class with injected dependencies; MARK sections group tests. FileTests/: one test class per sample or category (e.g. FCPXMLFileTest_24, FCPXMLFileTest_Structure, FCPXMLFileTest_AllSamples, FCPXMLFileTest_FrameRates, FCPXMLFileTest_360Video, FCPXMLFileTest_AuditionSample, FCPXMLFileTest_ImageSample, FCPXMLFileTest_Multicam, FCPXMLFileTest_Photoshop, FCPXMLFileTest_SmartCollection). LogicAndParsing/: FCPXMLRootVersionTests, FCPXMLStructureTests, FCPXMLFormatAssetTests. Every test-case class is FCPXML-prefixed except the module-named umbrella OpenFCPXMLKitTests. FCPXMLTimelineManipulationTests: ripple insert, auto lane assignment, clip queries, lane range, timestamps, metadata, secondary storylines, audio keyframes; injectable "now" for timestamp tests via lock-based NowBox (no DispatchSemaphore); do-catch for insertClipAutoLane/insertingClipAutoLane to verify success. FCPXMLTimecodeTests: initialization, arithmetic, comparison, CMTime conversion, frame alignment, hashing, codable. FCPXMLMIMETypeDetectionTests: sync and async detection for various file types. FCPXMLAssetValidationTests: asset existence, lane compatibility, TimelineClip integration. FCPXMLSilenceDetectionTests: silence detection at start/end of audio files. FCPXMLAssetDurationMeasurementTests: duration measurement for audio/video/images. FCPXMLParallelFileIOTests: concurrent read/write operations. FCPXMLAudioEnhancementTests: NoiseReduction, HumReduction, Equalization, MatchEqualization, Clip integration. FCPXMLTransform360Tests: coordinate types, spherical/cartesian, clip integration. FCPXMLCaptionTitleTests: TextStyle, TextStyleDefinition, Caption/Title integration, CaptionSample file test. FCPXMLKeyframeAnimationTests: FadeIn, FadeOut, Keyframe, KeyframeAnimation, FilterParameter integration. FCPXMLAudioKeyframeTests: audio keyframes in adjust-volume (param name="amount" with keyframeAnimation); parsing from FCPXML samples; decibel values (-3dB, -37dB); time values (FCPXML fractional format); fadeIn/fadeOut integration; multiple keyframes in sequence; secondary storyline and nested clip detection; TimelineWithSecondaryStorylineWithAudioKeyframes, TimelineSample file tests. FCPXMLCMTimeCodableTests: CMTime encoding/decoding as FCPXML time strings. FCPXMLCollectionTests: CollectionFolder, KeywordCollection, nested structures. FCPXMLSmartCollectionTests: SmartCollection models, match rules (MatchUsage, MatchRepresentation, MatchMarkers, MatchAnalysisType), round-trip, version stripping. FCPXMLAdjustmentTests: typed adjustment models and clip integration. FCPXMLFilterTests: VideoFilter, AudioFilter, VideoFilterMask, FilterParameter. FCPXMLImportOptionsTests: import options and library location parsing. FCPXMLCodableTests: Codable round-trip for model types. FCPXMLMediaExtractionTests: media reference extraction and copy (CLI --media-copy flow). FCPXMLDTDValidatorTests: per-version DTD validation. FCPXMLStructuralValidatorTests: cross-platform structural validation. FCPXMLAEXMLSerializationParityTests: AEXML vs Foundation serialization parity. FCPXMLTimelineExportValidationTests: timeline, exporters (empty timeline creation and project-creation export at different sizes and frame rates; includeDefaultSmartCollections and DTD validation), validators, file loader. FCPXMLAPIAndEdgeCaseTests: async load API, ServiceLogger injection, edge cases, Live Drawing (1.11+), HiddenClipMarker (1.13+). FCPXMLCutDetectionTests: edit points, transitions, gaps, CutSample file test. FCPXMLPerformanceTests: parameterised and basic performance tests. Reporting/extraction tests are all FCPXML-prefixed (FCPXMLRoleInventoryReportTests, FCPXMLRoleInventoryColumnLayoutTests, FCPXMLMarkersReportTests, FCPXMLKeywordsReportTests, FCPXMLTitlesReportTests, FCPXMLTransitionsReportTests, FCPXMLEffectsReportTests, FCPXMLSpeedChangeEffectsReportTests, FCPXMLSummaryReportTests, FCPXMLReportExcelExportTests, FCPXMLReportFormattingTests, FCPXMLReportRoleExclusionTests, FCPXMLReportTimecodeFormatTests, FCPXMLReportBuildPhaseTests, FCPXMLReportColumnExclusionTests, FCPXMLReportExcludeDisabledClipsTests, FCPXMLRoleDisplayPreferenceTests, FCPXMLRoleInventoryClipCollectorTests, FCPXMLRoleInventoryRoleSheetOrderingTests, FCPXMLSummaryRoleDurationAggregatorTests, FCPXMLEffectsReportPolicyTests, FCPXMLSpeedChangeFormattingTests, FCPXMLDisplayClipNameTests, FCPXMLTitleDisplayTests, FCPXMLExtractionScopeTests, FCPXMLExtractedElementTests, FCPXMLEffectsCollectorTests, FCPXMLRolesExtractionPresetTests, FCPXMLEffectAppleSuppliedTests, FCPXMLClipParsingCarriesAudioTests, FCPXMLTransformAdjustmentParsingTests, FCPXMLTransitionSpinePlacementTests). +- Tests/OpenFCPXMLKitTests/: FCPXMLTestResources.swift (packageRoot, fcpxmlSamplesDirectory, urlForFCPXMLSample, FCPXMLSampleName); FCPXMLTestUtilities.swift (loadFCPXMLSampleData, loadFCPXMLSample, fcpxmlFrameRateSampleNames, allFCPXMLSampleNames; XCTSkip when sample missing); FCPXMLReportingReportFixture.swift and FCPXMLReportingReportTestSupport.swift (optional reporting integration fixtures/assertions). OpenFCPXMLKitTests.swift: main test class with injected dependencies; MARK sections group tests. FileTests/: one test class per sample or category (e.g. FCPXMLFileTest_24, FCPXMLFileTest_Structure, FCPXMLFileTest_AllSamples, FCPXMLFileTest_FrameRates, FCPXMLFileTest_360Video, FCPXMLFileTest_AuditionSample, FCPXMLFileTest_ImageSample, FCPXMLFileTest_Multicam, FCPXMLFileTest_Photoshop, FCPXMLFileTest_SmartCollection). LogicAndParsing/: FCPXMLRootVersionTests, FCPXMLStructureTests, FCPXMLFormatAssetTests. Every test-case class is FCPXML-prefixed except the module-named umbrella OpenFCPXMLKitTests. FCPXMLTimelineManipulationTests: ripple insert, auto lane assignment, clip queries, lane range, timestamps, metadata, secondary storylines, audio keyframes; injectable "now" for timestamp tests via lock-based NowBox (no DispatchSemaphore); do-catch for insertClipAutoLane/insertingClipAutoLane to verify success. FCPXMLTimecodeTests: initialization, arithmetic, comparison, CMTime conversion, frame alignment, hashing, codable. FCPXMLMIMETypeDetectionTests: sync and async detection for various file types. FCPXMLAssetValidationTests: asset existence, lane compatibility, TimelineClip integration. FCPXMLSilenceDetectionTests: silence detection at start/end of audio files. FCPXMLAssetDurationMeasurementTests: duration measurement for audio/video/images. FCPXMLParallelFileIOTests: concurrent read/write operations. FCPXMLAudioEnhancementTests: NoiseReduction, HumReduction, Equalization, MatchEqualization, Clip integration. FCPXMLTransform360Tests: coordinate types, spherical/cartesian, clip integration. FCPXMLCaptionTitleTests: TextStyle, TextStyleDefinition, Caption/Title integration, CaptionSample file test. FCPXMLKeyframeAnimationTests: FadeIn, FadeOut, Keyframe, KeyframeAnimation, FilterParameter integration. FCPXMLAudioKeyframeTests: audio keyframes in adjust-volume (param name="amount" with keyframeAnimation); parsing from FCPXML samples; decibel values (-3dB, -37dB); time values (FCPXML fractional format); fadeIn/fadeOut integration; multiple keyframes in sequence; secondary storyline and nested clip detection; TimelineWithSecondaryStorylineWithAudioKeyframes, TimelineSample file tests. FCPXMLCMTimeCodableTests: CMTime encoding/decoding as FCPXML time strings. FCPXMLCollectionTests: CollectionFolder, KeywordCollection, nested structures. FCPXMLSmartCollectionTests: SmartCollection models, match rules (MatchUsage, MatchRepresentation, MatchMarkers, MatchAnalysisType), round-trip, version stripping. FCPXMLAdjustmentTests: typed adjustment models and clip integration. FCPXMLFilterTests: VideoFilter, AudioFilter, VideoFilterMask, FilterParameter. FCPXMLImportOptionsTests: import options and library location parsing. FCPXMLCodableTests: Codable round-trip for model types. FCPXMLMediaExtractionTests: media reference extraction and copy (CLI --media-copy flow). FCPXMLDTDValidatorTests: per-version DTD validation. FCPXMLStructuralValidatorTests: cross-platform structural validation. FCPXMLAEXMLSerializationParityTests: AEXML vs Foundation serialization parity. FCPXMLTimelineExportValidationTests: timeline, exporters (empty timeline creation and project-creation export at different sizes and frame rates; includeDefaultSmartCollections and DTD validation), validators, file loader. FCPXMLAPIAndEdgeCaseTests: async load API, ServiceLogger injection, edge cases, Live Drawing (1.11+), HiddenClipMarker (1.13+). FCPXMLCutDetectionTests: edit points, transitions, gaps, CutSample file test. FCPXMLPerformanceTests: parameterised and basic performance tests. Reporting/extraction tests are all FCPXML-prefixed (FCPXMLCompoundClipReportTests, FCPXMLRoleInventoryReportTests, FCPXMLRoleInventoryColumnLayoutTests, FCPXMLMarkersReportTests, FCPXMLKeywordsReportTests, FCPXMLTitlesReportTests, FCPXMLTransitionsReportTests, FCPXMLEffectsReportTests, FCPXMLSpeedChangeEffectsReportTests, FCPXMLSummaryReportTests, FCPXMLReportExcelExportTests, FCPXMLReportFormattingTests, FCPXMLReportRoleExclusionTests, FCPXMLReportTimecodeFormatTests, FCPXMLReportBuildPhaseTests, FCPXMLReportColumnExclusionTests, FCPXMLReportExcludeDisabledClipsTests, FCPXMLRoleDisplayPreferenceTests, FCPXMLRoleInventoryClipCollectorTests, FCPXMLRoleInventoryRoleSheetOrderingTests, FCPXMLSummaryRoleDurationAggregatorTests, FCPXMLEffectsReportPolicyTests, FCPXMLSpeedChangeFormattingTests, FCPXMLDisplayClipNameTests, FCPXMLTitleDisplayTests, FCPXMLExtractionScopeTests, FCPXMLExtractedElementTests, FCPXMLEffectsCollectorTests, FCPXMLRolesExtractionPresetTests, FCPXMLEffectAppleSuppliedTests, FCPXMLClipParsingCarriesAudioTests, FCPXMLTransformAdjustmentParsingTests, FCPXMLTransitionSpinePlacementTests). -Use descriptive test method names; group related tests logically; include setup and teardown; use meaningful assertions. Current total: **925** tests listed in `swift test --list-tests` (**924** in `OpenFCPXMLKitTests` + **1** optional `ExcelReportTest`) covering all functionality including async/await, timeline manipulation, metadata, timestamps, FCPXMLTimecode, MIME type detection, asset validation, silence detection, asset duration measurement, parallel file I/O, version conversion stripping, per-version DTD validation, extract-then-copy (CLI --media-copy flow), synchronized clip matching, secondary storyline traversal, clip identification, URL resolution, version conversion edge cases, typed adjustment models (including Transform360, ColorConform, Stereo3D, VoiceIsolation), typed effect/filter models, typed caption/title models, smart collections (match-clip, match-media, match-ratings, match-text, match-usage, match-representation, match-markers, match-analysis-type), keyframe animation, audio keyframes (FCPXMLAudioKeyframeTests: adjust-volume param keyframeAnimation parsing, decibel/time validation, fadeIn/fadeOut integration), CMTime Codable extension, collection organization, Live Drawing (1.11+), HiddenClipMarker (1.13+), Format/Asset 1.13+ (heroEye, heroEyeOverride, mediaReps), FCPXMLExporter clip-level metadata export and XML declaration standalone="no", FCPXMLTimelineManipulationTests refactor (lock-based NowBox, do-catch for throwing APIs), Excel reporting (Selected Roles Inventory column layout, Summary/Media Summary split, ReportTimecodeFormat / format-aware headers, inventory-first ReportBuildPhase progress, global column exclusion, disabled-clip filtering, workbook cell formatting via FCPXMLReportWorkbookExporter), 360 video features, auditions, conform-rate, still images, multicam, secondary storylines, audio keyframes, keyword collections/folders, and Photoshop integration. +Use descriptive test method names; group related tests logically; include setup and teardown; use meaningful assertions. Current total: **933** tests listed in `swift test --list-tests` (**932** in `OpenFCPXMLKitTests` + **1** optional `ExcelReportTest`) covering all functionality including async/await, timeline manipulation, metadata, timestamps, FCPXMLTimecode, MIME type detection, asset validation, silence detection, asset duration measurement, parallel file I/O, version conversion stripping, per-version DTD validation, extract-then-copy (CLI --media-copy flow), synchronized clip matching, secondary storyline traversal, clip identification, URL resolution, version conversion edge cases, typed adjustment models (including Transform360, ColorConform, Stereo3D, VoiceIsolation), typed effect/filter models, typed caption/title models, smart collections (match-clip, match-media, match-ratings, match-text, match-usage, match-representation, match-markers, match-analysis-type), keyframe animation, audio keyframes (FCPXMLAudioKeyframeTests: adjust-volume param keyframeAnimation parsing, decibel/time validation, fadeIn/fadeOut integration), CMTime Codable extension, collection organization, Live Drawing (1.11+), HiddenClipMarker (1.13+), Format/Asset 1.13+ (heroEye, heroEyeOverride, mediaReps), FCPXMLExporter clip-level metadata export and XML declaration standalone="no", FCPXMLTimelineManipulationTests refactor (lock-based NowBox, do-catch for throwing APIs), Excel reporting (Selected Roles Inventory column layout, Summary/Media Summary split, ReportTimecodeFormat / format-aware headers, inventory-first ReportBuildPhase progress, global column exclusion, disabled-clip filtering, workbook cell formatting via FCPXMLReportWorkbookExporter), 360 video features, auditions, conform-rate, still images, multicam, secondary storylines, audio keyframes, keyword collections/folders, and Photoshop integration. --- @@ -160,7 +160,7 @@ SwiftTimecode integration: use SwiftTimecode for all timecode operations; suppor ## Testing Requirements -Test coverage: unit tests for all public APIs; integration tests for complex workflows; performance tests for time-critical operations; concurrency tests for async operations; test all supported frame rates (Final Cut Pro compatible). Current: **925** tests listed in `swift test --list-tests` (including AEXML parity, FCPXMLDTDValidatorTests, FCPXMLStructuralValidatorTests, FCPXMLReportTimecodeFormatTests, FCPXMLReportBuildPhaseTests, FCPXMLReportColumnExclusionTests, FCPXMLReportExcludeDisabledClipsTests, FCPXMLRoleInventoryColumnLayoutTests, FCPXMLReportExcelExportTests workbook cell formatting, optional ExcelReportTest integration) covering all functionality including async/await, timeline manipulation, metadata, timestamps, FCPXMLTimecode, MIME type detection, asset validation, silence detection, asset duration measurement, parallel file I/O, version conversion, DTD validation, extract-then-copy flow, synchronized clip matching, secondary storyline traversal, clip identification, URL resolution, version conversion edge cases, typed adjustment models (including Transform360, ColorConform, Stereo3D, VoiceIsolation), typed effect/filter models, typed caption/title models, smart collections (match-clip, match-media, match-ratings, match-text, match-usage, match-representation, match-markers, match-analysis-type), keyframe animation, audio keyframes (FCPXMLAudioKeyframeTests: adjust-volume param keyframeAnimation parsing, decibel/time validation, fadeIn/fadeOut integration), CMTime Codable extension, collection organization, Live Drawing (1.11+), HiddenClipMarker (1.13+), Format/Asset 1.13+ (heroEye, heroEyeOverride, mediaReps), FCPXMLExporter clip-level metadata export and XML declaration standalone="no", FCPXMLTimelineManipulationTests (lock-based NowBox, do-catch), Excel reporting enhancements, 360 video features, auditions, conform-rate, still images, multicam, secondary storylines, audio keyframes, keyword collections/folders, and Photoshop integration. +Test coverage: unit tests for all public APIs; integration tests for complex workflows; performance tests for time-critical operations; concurrency tests for async operations; test all supported frame rates (Final Cut Pro compatible). Current: **933** tests listed in `swift test --list-tests` (including AEXML parity, FCPXMLDTDValidatorTests, FCPXMLStructuralValidatorTests, FCPXMLReportTimecodeFormatTests, FCPXMLReportBuildPhaseTests, FCPXMLReportColumnExclusionTests, FCPXMLReportExcludeDisabledClipsTests, FCPXMLRoleInventoryColumnLayoutTests, FCPXMLReportExcelExportTests workbook cell formatting, optional ExcelReportTest integration) covering all functionality including async/await, timeline manipulation, metadata, timestamps, FCPXMLTimecode, MIME type detection, asset validation, silence detection, asset duration measurement, parallel file I/O, version conversion, DTD validation, extract-then-copy flow, synchronized clip matching, secondary storyline traversal, clip identification, URL resolution, version conversion edge cases, typed adjustment models (including Transform360, ColorConform, Stereo3D, VoiceIsolation), typed effect/filter models, typed caption/title models, smart collections (match-clip, match-media, match-ratings, match-text, match-usage, match-representation, match-markers, match-analysis-type), keyframe animation, audio keyframes (FCPXMLAudioKeyframeTests: adjust-volume param keyframeAnimation parsing, decibel/time validation, fadeIn/fadeOut integration), CMTime Codable extension, collection organization, Live Drawing (1.11+), HiddenClipMarker (1.13+), Format/Asset 1.13+ (heroEye, heroEyeOverride, mediaReps), FCPXMLExporter clip-level metadata export and XML declaration standalone="no", FCPXMLTimelineManipulationTests (lock-based NowBox, do-catch), Excel reporting enhancements, 360 video features, auditions, conform-rate, still images, multicam, secondary storylines, audio keyframes, keyword collections/folders, and Photoshop integration. Test data: use realistic FCPXML samples; include edge cases and error conditions; test all supported frame rates; validate against actual Final Cut Pro output where applicable. @@ -253,7 +253,7 @@ Code review process: review for Swift 6.3 compliance; check concurrency implemen ## Documentation Sync -Keep this file in sync with AGENT.md. Both must reflect: changelog styling (CHANGELOG.md: Keep a Changelog format, version links to release tags, ✨ New Features / 🔧 Improvements / 🐛 Bug Fixes); project overview and codebase rewrite/refactor; architecture and single injection point (FCPXMLUtility.defaultForExtensions); source layout (Analysis, Classes, Delegates, Errors, Extensions including +Modular and +Codable, Implementations, Protocols, Services, Utilities, Annotations, Export, Timeline, Timing, Validation, FileIO, Logging, Format, Model including Adjustments, Animations, Filters, Clips with +Adjustments and +Typed, CommonElements with TextStyle/TextStyleDefinition, Structure with CollectionFolder/KeywordCollection/SmartCollection, Parsing, Extraction, Reporting including Excel/, XML with Protocols/Foundation/AEXML/OFKXMLDefaultFactory, FCPXML DTDs; reporting vs core layers in ARCHITECTURE.md §2.7); test structure (Tests/ layout, FCPXMLTestResources, FCPXMLTestUtilities, FileTests/ including FCPXMLFileTest_360Video, FCPXMLFileTest_AuditionSample, FCPXMLFileTest_ImageSample, FCPXMLFileTest_Multicam, FCPXMLFileTest_Photoshop, FCPXMLFileTest_SmartCollection, LogicAndParsing/ including FCPXMLFormatAssetTests, FCPXMLCutDetectionTests, FCPXMLVersionConversionTests, FCPXMLMediaExtractionTests, FCPXMLTimelineManipulationTests, FCPXMLTimecodeTests, FCPXMLMIMETypeDetectionTests, FCPXMLAssetValidationTests, FCPXMLSilenceDetectionTests, FCPXMLAssetDurationMeasurementTests, FCPXMLParallelFileIOTests, FCPXMLAudioEnhancementTests, FCPXMLTransform360Tests, FCPXMLCaptionTitleTests, FCPXMLKeyframeAnimationTests, FCPXMLAudioKeyframeTests, FCPXMLCMTimeCodableTests, FCPXMLCollectionTests, FCPXMLSmartCollectionTests, FCPXMLAdjustmentTests, FCPXMLFilterTests, FCPXMLImportOptionsTests, FCPXMLCodableTests, FCPXMLAEXMLSerializationParityTests, FCPXMLDTDValidatorTests, FCPXMLStructuralValidatorTests, OpenFCPXMLKitTests.swift, FCPXMLTimelineExportValidationTests, FCPXMLAPIAndEdgeCaseTests, FCPXMLPerformanceTests, and FCPXML-prefixed reporting/extraction tests (FCPXMLRoleInventoryReportTests, FCPXMLMarkersReportTests, FCPXMLKeywordsReportTests, FCPXMLTitlesReportTests, FCPXMLTransitionsReportTests, FCPXMLEffectsReportTests, FCPXMLSpeedChangeEffectsReportTests, FCPXMLSummaryReportTests, FCPXMLReportExcelExportTests, FCPXMLReportFormattingTests, FCPXMLReportRoleExclusionTests, FCPXMLReportTimecodeFormatTests, FCPXMLReportBuildPhaseTests, FCPXMLRoleDisplayPreferenceTests, FCPXMLRoleInventoryClipCollectorTests, FCPXMLRoleInventoryRoleSheetOrderingTests, FCPXMLSummaryRoleDurationAggregatorTests, FCPXMLEffectsReportPolicyTests, FCPXMLSpeedChangeFormattingTests, FCPXMLDisplayClipNameTests, FCPXMLTitleDisplayTests, FCPXMLExtractionScopeTests, FCPXMLExtractedElementTests, FCPXMLEffectsCollectorTests, FCPXMLRolesExtractionPresetTests, FCPXMLEffectAppleSuppliedTests, FCPXMLClipParsingCarriesAudioTests, FCPXMLTransformAdjustmentParsingTests, FCPXMLTransitionSpinePlacementTests); every test-case class is FCPXML-prefixed except the module-named umbrella OpenFCPXMLKitTests; empty timeline creation and project-creation export at different sizes and frame rates in FCPXMLTimelineExportValidationTests (clip-level metadata export, XML declaration standalone="no")). Cross-platform XML (OFKXML*, FCPXMLStructuralValidator, iOS). FCPXML 1.5–1.14 and FCPXMLElementType; FCPXMLVersion.supportsBundleFormat (1.10+); version conversion with element stripping and per-version DTD validation; FCPXML creation from scratch; timeline manipulation (ripple insert, auto lane assignment, clip queries, lane range, secondary storylines); timeline metadata (markers, chapter markers, keywords, ratings, timestamps); FCPXMLTimecode custom type; MIME type detection; asset validation (including still images); silence detection; asset duration measurement; parallel file I/O; TimelineFormat enhancements; typed adjustment models (Crop, Transform, Blend, Stabilization, Volume, Loudness, NoiseReduction, HumReduction, Equalization, MatchEqualization, Transform360, ColorConform, Stereo3D, VoiceIsolation); typed effect/filter models (VideoFilter, AudioFilter, VideoFilterMask, FilterParameter with keyframe animation and auxValue 1.11+); typed caption/title models (Caption, Title with TextStyle, TextStyleDefinition); smart collections (SmartCollection with match-clip, match-media, match-ratings, match-text, match-usage, match-representation, match-markers, match-analysis-type); keyframe animation (KeyframeAnimation, Keyframe, FadeIn, FadeOut); audio keyframes (FCPXMLAudioKeyframeTests: adjust-volume param keyframeAnimation parsing, decibel/time validation, fadeIn/fadeOut integration); CMTime Codable extension; collection organization (CollectionFolder, KeywordCollection); Live Drawing (1.11+); HiddenClipMarker (1.13+); Format/Asset 1.13+ (heroEye, heroEyeOverride, mediaReps); experimental CLI (OpenFCPXMLKit-CLI, single binary, embedded DTDs, --check-version, --convert-version, --extension-type fcpxml|fcpxmld, --validate, --media-copy, --create-project with DTD validation and FCP-style output, --report Excel report with --report-full/per-section flags/--report-summary/--report-media-summary/--exclude-role/--exclude-column/--exclude-disabled-clips/--timecode-format/--report-project, --log/--log-level/--quiet with log file capturing all command output); Excel reporting subsystem (Reporting/ builders, Sections/Rows, Support including RoleInventoryColumnLayout, ReportColumnExclusion, ReportFormatting, ReportTimecodeFormat, ReportBuildProgress; Excel/ via XLKit; buildReport/ReportExcelExport; Summary and Media Summary sheets; excludeDisabledClips/excludedColumns/timecodeFormat; inventory-first enabledPhases); extraction presets (Captions, Effects, FrameData, Markers, Roles, Titles); Tests/ExcelReportTest optional integration; Final Cut Pro frame rates; Swift 6 concurrency (Sendable, async/await, CI strict-concurrency job); Xcode 26 dynamic framework linking compatibility via explicit `swift-log` (`Logging`) dependency in `Package.swift`. When updating either file, update both and keep terminology and examples consistent. +Keep this file in sync with AGENT.md. Both must reflect: changelog styling (CHANGELOG.md: Keep a Changelog format, version links to release tags, ✨ New Features / 🔧 Improvements / 🐛 Bug Fixes); project overview and codebase rewrite/refactor; architecture and single injection point (FCPXMLUtility.defaultForExtensions); source layout (Analysis, Classes, Delegates, Errors, Extensions including +Modular and +Codable, Implementations, Protocols, Services, Utilities, Annotations, Export, Timeline, Timing, Validation, FileIO, Logging, Format, Model including Adjustments, Animations, Filters, Clips with +Adjustments and +Typed, CommonElements with TextStyle/TextStyleDefinition, Structure with CollectionFolder/KeywordCollection/SmartCollection, Parsing, Extraction, Reporting including Excel/, XML with Protocols/Foundation/AEXML/OFKXMLDefaultFactory, FCPXML DTDs; reporting vs core layers in ARCHITECTURE.md §2.7); test structure (Tests/ layout, FCPXMLTestResources, FCPXMLTestUtilities, FileTests/ including FCPXMLFileTest_360Video, FCPXMLFileTest_AuditionSample, FCPXMLFileTest_ImageSample, FCPXMLFileTest_Multicam, FCPXMLFileTest_Photoshop, FCPXMLFileTest_SmartCollection, LogicAndParsing/ including FCPXMLFormatAssetTests, FCPXMLCutDetectionTests, FCPXMLVersionConversionTests, FCPXMLMediaExtractionTests, FCPXMLTimelineManipulationTests, FCPXMLTimecodeTests, FCPXMLMIMETypeDetectionTests, FCPXMLAssetValidationTests, FCPXMLSilenceDetectionTests, FCPXMLAssetDurationMeasurementTests, FCPXMLParallelFileIOTests, FCPXMLAudioEnhancementTests, FCPXMLTransform360Tests, FCPXMLCaptionTitleTests, FCPXMLKeyframeAnimationTests, FCPXMLAudioKeyframeTests, FCPXMLCMTimeCodableTests, FCPXMLCollectionTests, FCPXMLSmartCollectionTests, FCPXMLAdjustmentTests, FCPXMLFilterTests, FCPXMLImportOptionsTests, FCPXMLCodableTests, FCPXMLAEXMLSerializationParityTests, FCPXMLDTDValidatorTests, FCPXMLStructuralValidatorTests, OpenFCPXMLKitTests.swift, FCPXMLTimelineExportValidationTests, FCPXMLAPIAndEdgeCaseTests, FCPXMLPerformanceTests, and FCPXML-prefixed reporting/extraction tests (FCPXMLRoleInventoryReportTests, FCPXMLMarkersReportTests, FCPXMLKeywordsReportTests, FCPXMLTitlesReportTests, FCPXMLTransitionsReportTests, FCPXMLEffectsReportTests, FCPXMLSpeedChangeEffectsReportTests, FCPXMLSummaryReportTests, FCPXMLReportExcelExportTests, FCPXMLReportFormattingTests, FCPXMLReportRoleExclusionTests, FCPXMLReportTimecodeFormatTests, FCPXMLReportBuildPhaseTests, FCPXMLRoleDisplayPreferenceTests, FCPXMLRoleInventoryClipCollectorTests, FCPXMLRoleInventoryRoleSheetOrderingTests, FCPXMLSummaryRoleDurationAggregatorTests, FCPXMLEffectsReportPolicyTests, FCPXMLSpeedChangeFormattingTests, FCPXMLDisplayClipNameTests, FCPXMLTitleDisplayTests, FCPXMLExtractionScopeTests, FCPXMLExtractedElementTests, FCPXMLEffectsCollectorTests, FCPXMLRolesExtractionPresetTests, FCPXMLEffectAppleSuppliedTests, FCPXMLClipParsingCarriesAudioTests, FCPXMLTransformAdjustmentParsingTests, FCPXMLTransitionSpinePlacementTests); every test-case class is FCPXML-prefixed except the module-named umbrella OpenFCPXMLKitTests; empty timeline creation and project-creation export at different sizes and frame rates in FCPXMLTimelineExportValidationTests (clip-level metadata export, XML declaration standalone="no")). Cross-platform XML (OFKXML*, FCPXMLStructuralValidator, iOS). FCPXML 1.5–1.14 and FCPXMLElementType; FCPXMLVersion.supportsBundleFormat (1.10+); version conversion with element stripping and per-version DTD validation; FCPXML creation from scratch; timeline manipulation (ripple insert, auto lane assignment, clip queries, lane range, secondary storylines); timeline metadata (markers, chapter markers, keywords, ratings, timestamps); FCPXMLTimecode custom type; MIME type detection; asset validation (including still images); silence detection; asset duration measurement; parallel file I/O; TimelineFormat enhancements; typed adjustment models (Crop, Transform, Blend, Stabilization, Volume, Loudness, NoiseReduction, HumReduction, Equalization, MatchEqualization, Transform360, ColorConform, Stereo3D, VoiceIsolation); typed effect/filter models (VideoFilter, AudioFilter, VideoFilterMask, FilterParameter with keyframe animation and auxValue 1.11+); typed caption/title models (Caption, Title with TextStyle, TextStyleDefinition); smart collections (SmartCollection with match-clip, match-media, match-ratings, match-text, match-usage, match-representation, match-markers, match-analysis-type); keyframe animation (KeyframeAnimation, Keyframe, FadeIn, FadeOut); audio keyframes (FCPXMLAudioKeyframeTests: adjust-volume param keyframeAnimation parsing, decibel/time validation, fadeIn/fadeOut integration); CMTime Codable extension; collection organization (CollectionFolder, KeywordCollection); Live Drawing (1.11+); HiddenClipMarker (1.13+); Format/Asset 1.13+ (heroEye, heroEyeOverride, mediaReps); experimental CLI (OpenFCPXMLKit-CLI, single binary, embedded DTDs, --check-version, --convert-version, --extension-type fcpxml|fcpxmld, --validate, --media-copy, --create-project with DTD validation and FCP-style output, --report Excel report with --report-full/per-section flags/--report-summary/--report-media-summary/--exclude-role/--exclude-column/--exclude-disabled-clips/--timecode-format/--report-project, --log/--log-level/--quiet with log file capturing all command output); Excel reporting subsystem (`allReportTimelineSources` / compound-clip timelines) (Reporting/ builders, Sections/Rows, Support including RoleInventoryColumnLayout, ReportColumnExclusion, ReportFormatting, ReportTimecodeFormat, ReportBuildProgress; Excel/ via XLKit; buildReport/ReportExcelExport; Summary and Media Summary sheets; excludeDisabledClips/excludedColumns/timecodeFormat; inventory-first enabledPhases); extraction presets (Captions, Effects, FrameData, Markers, Roles, Titles); Tests/ExcelReportTest optional integration; Final Cut Pro frame rates; Swift 6 concurrency (Sendable, async/await, CI strict-concurrency job); Xcode 26 dynamic framework linking compatibility via explicit `swift-log` (`Logging`) dependency in `Package.swift`. When updating either file, update both and keep terminology and examples consistent. --- diff --git a/AGENT.md b/AGENT.md index 31fad95..ef0ceca 100644 --- a/AGENT.md +++ b/AGENT.md @@ -41,7 +41,7 @@ OpenFCPXMLKit targets macOS 26+, iOS 26+, Xcode 26+, and Swift 6.3 with full con **Backward compatibility:** The entire codebase must remain backward compatible with FCPXML 1.5. Optional attributes and elements introduced in later versions (e.g. 1.11, 1.13) must be omitted or ignored when reading/writing or converting to 1.5; mark such features in code comments with the minimum FCPXML version (e.g. `FCPXML 1.13+`). -Current status: **925** tests listed in `swift test --list-tests` (**924** in `OpenFCPXMLKitTests`: 921 XCTest + 3 Swift Testing `@Test`; plus **1** optional `ExcelReportTest` integration); FCPXML versions 1.5–1.14 supported (DTDs included, full parsing, typed element-type coverage for all DTD elements via FCPXMLElementType); Final Cut Pro frame rates (23.976, 24, 25, 29.97, 30, 50, 59.94, 60); thread-safe and concurrency-compliant with comprehensive async/await support; no known security vulnerabilities. Version conversion automatically drops elements not in the target version’s DTD (e.g. adjust-colorConform, adjust-stereo-3D); DTD validation runs per version (validateDocumentAgainstDTD, validateDocumentAgainstDeclaredVersion) and after CLI convert. FCPXMLVersion.supportsBundleFormat is true for 1.10+ (.fcpxmld bundle); 1.5–1.9 support only single-file .fcpxml. FCPXML creation: create FCPXML documents from scratch with events, projects, resources, and clips via XMLDocumentManager, XMLDocument initializers, or FCPXMLService. Timeline manipulation: ripple insert (shifts subsequent clips), auto lane assignment, clip queries (by lane, time range, asset ID), lane range computation, secondary storylines. Timeline metadata: markers, chapter markers, keywords, ratings, custom metadata, timestamps (createdAt, modifiedAt). FCPXMLTimecode: custom timecode type (arithmetic, frame alignment, CMTime conversion, FCPXML string parsing). MIME type detection, asset validation, silence detection, asset duration measurement, parallel file I/O, still image asset support. TimelineFormat enhancements: presets (hd720p, dci4K, hd1080i, hd720i), computed properties (aspectRatio, isHD, isUHD, interlaced). Typed adjustment models: Crop, Transform, Blend, Stabilization, Volume, Loudness, NoiseReduction, HumReduction, Equalization, MatchEqualization, Transform360, ColorConform, Stereo3D, VoiceIsolation with full clip integration. Typed effect/filter models: VideoFilter, AudioFilter, VideoFilterMask with FilterParameter support and keyframe animation (auxValue support FCPXML 1.11+). Typed caption/title models: Caption and Title with TextStyle and TextStyleDefinition for full text formatting. SmartCollection models: SmartCollection with match-clip, match-media, match-ratings, match-text, match-usage (1.9+), match-representation (1.10+), match-markers (1.10+), match-analysis-type (1.14). Keyframe animation: KeyframeAnimation, Keyframe with interpolation types, FadeIn/FadeOut with fade types, integrated with FilterParameter. CMTime Codable extension: Direct CMTime encoding/decoding as FCPXML time strings. Collection organization: CollectionFolder and KeywordCollection models for organizing clips and media. Live Drawing (FCPXML 1.11+): LiveDrawing model for live-drawing story elements. HiddenClipMarker (FCPXML 1.13+): HiddenClipMarker model for hidden clip markers. Format/Asset 1.13+: Format heroEye, Asset heroEyeOverride, Asset mediaReps (multiple media-rep). Cross-platform XML abstraction: protocol layer (OFKXMLNode, OFKXMLElement, OFKXMLDocument, OFKXMLFactory); Foundation backend on macOS (unchanged behaviour); AEXML backend on iOS; OFKXMLDefaultFactory() for platform dispatch; FCPXMLStructuralValidator for cross-platform structural validation; FCPXMLDTDValidator is platform-conditional (full DTD on macOS, structural fallback on iOS). Comprehensive test coverage: **925** tests across 58 FCPXML sample files including 360 video, auditions, conform-rate, still images, multicam, secondary storylines, audio keyframes (FCPXMLAudioKeyframeTests: adjust-volume param keyframeAnimation parsing, decibel/time validation, fadeIn/fadeOut integration, secondary storyline detection), keyword collections/folders, Photoshop integration, smart collections, and reporting column layout/exclusion/disabled-clip/workbook-formatting tests. Excel reporting: multi-sheet `.xlsx` workbooks via `FinalCutPro.FCPXML.buildReport(options:)` (ReportBuilder, ReportOptions presets, ReportExcelExport on XLKit); sheets for Role Inventory (**Selected Roles Inventory** + per-role sheets with expanded column layout and dynamic metadata keys), Markers, Keywords, Titles & Generators, Transitions, Video & Audio Effects, Speed Change Effects, **Summary** (project title header row, black role-duration data), and **Media Summary** (red missing-media paths); inventory and section-sheet cell formatting, role exclusions, global column exclusion (`ReportColumn` / `excludedColumns`), disabled-clip filtering (`excludeDisabledClips`), project-name filtering, `ReportTimecodeFormat` / `--timecode-format`, inventory-first `ReportBuildPhase` progress callbacks. Extraction presets: Captions, Effects, FrameData, Markers, Roles, Titles. Experimental CLI (OpenFCPXMLKit-CLI): single binary with embedded DTDs; --check-version, --convert-version (stripping + DTD validation), --extension-type (fcpxmld | fcpxml; default fcpxmld; 1.5–1.9 always .fcpxml), --validate, --media-copy, --create-project (new empty FCPXML project: --width, --height, --rate, --project-version, output-dir; DTD validation before write; FCP-style output with DOCTYPE, colorSpace, default smart collections), --report (Excel report: role inventory by default; --report-full, per-section flags including --report-markers, --report-keywords, --report-titles-generators, --report-transitions, --report-effects, --report-speed-change-effects, --report-summary, --report-media-summary, --exclude-role, --exclude-column, --exclude-disabled-clips, --timecode-format, --report-project); --log writes user-visible output for all commands to the log file; see Sources/OpenFCPXMLKitCLI/README.md. +Current status: **933** tests listed in `swift test --list-tests` (**932** in `OpenFCPXMLKitTests`: 929 XCTest + 3 Swift Testing `@Test`; plus **1** optional `ExcelReportTest` integration); FCPXML versions 1.5–1.14 supported (DTDs included, full parsing, typed element-type coverage for all DTD elements via FCPXMLElementType); Final Cut Pro frame rates (23.976, 24, 25, 29.97, 30, 50, 59.94, 60); thread-safe and concurrency-compliant with comprehensive async/await support; no known security vulnerabilities. Version conversion automatically drops elements not in the target version’s DTD (e.g. adjust-colorConform, adjust-stereo-3D); DTD validation runs per version (validateDocumentAgainstDTD, validateDocumentAgainstDeclaredVersion) and after CLI convert. FCPXMLVersion.supportsBundleFormat is true for 1.10+ (.fcpxmld bundle); 1.5–1.9 support only single-file .fcpxml. FCPXML creation: create FCPXML documents from scratch with events, projects, resources, and clips via XMLDocumentManager, XMLDocument initializers, or FCPXMLService. Timeline manipulation: ripple insert (shifts subsequent clips), auto lane assignment, clip queries (by lane, time range, asset ID), lane range computation, secondary storylines. Timeline metadata: markers, chapter markers, keywords, ratings, custom metadata, timestamps (createdAt, modifiedAt). FCPXMLTimecode: custom timecode type (arithmetic, frame alignment, CMTime conversion, FCPXML string parsing). MIME type detection, asset validation, silence detection, asset duration measurement, parallel file I/O, still image asset support. TimelineFormat enhancements: presets (hd720p, dci4K, hd1080i, hd720i), computed properties (aspectRatio, isHD, isUHD, interlaced). Typed adjustment models: Crop, Transform, Blend, Stabilization, Volume, Loudness, NoiseReduction, HumReduction, Equalization, MatchEqualization, Transform360, ColorConform, Stereo3D, VoiceIsolation with full clip integration. Typed effect/filter models: VideoFilter, AudioFilter, VideoFilterMask with FilterParameter support and keyframe animation (auxValue support FCPXML 1.11+). Typed caption/title models: Caption and Title with TextStyle and TextStyleDefinition for full text formatting. SmartCollection models: SmartCollection with match-clip, match-media, match-ratings, match-text, match-usage (1.9+), match-representation (1.10+), match-markers (1.10+), match-analysis-type (1.14). Keyframe animation: KeyframeAnimation, Keyframe with interpolation types, FadeIn/FadeOut with fade types, integrated with FilterParameter. CMTime Codable extension: Direct CMTime encoding/decoding as FCPXML time strings. Collection organization: CollectionFolder and KeywordCollection models for organizing clips and media. Live Drawing (FCPXML 1.11+): LiveDrawing model for live-drawing story elements. HiddenClipMarker (FCPXML 1.13+): HiddenClipMarker model for hidden clip markers. Format/Asset 1.13+: Format heroEye, Asset heroEyeOverride, Asset mediaReps (multiple media-rep). Cross-platform XML abstraction: protocol layer (OFKXMLNode, OFKXMLElement, OFKXMLDocument, OFKXMLFactory); Foundation backend on macOS (unchanged behaviour); AEXML backend on iOS; OFKXMLDefaultFactory() for platform dispatch; FCPXMLStructuralValidator for cross-platform structural validation; FCPXMLDTDValidator is platform-conditional (full DTD on macOS, structural fallback on iOS). Comprehensive test coverage: **933** tests across 58 FCPXML sample files including 360 video, auditions, conform-rate, still images, multicam, secondary storylines, audio keyframes (FCPXMLAudioKeyframeTests: adjust-volume param keyframeAnimation parsing, decibel/time validation, fadeIn/fadeOut integration, secondary storyline detection), keyword collections/folders, Photoshop integration, smart collections, and reporting column layout/exclusion/disabled-clip/workbook-formatting tests. Excel reporting: multi-sheet `.xlsx` workbooks via `FinalCutPro.FCPXML.buildReport(options:)` (ReportBuilder, ReportOptions presets, ReportExcelExport on XLKit); sheets for Role Inventory (**Selected Roles Inventory** + per-role sheets with expanded column layout and dynamic metadata keys), Markers, Keywords, Titles & Generators, Transitions, Video & Audio Effects, Speed Change Effects, **Summary** (project title header row, black role-duration data), and **Media Summary** (red missing-media paths); inventory and section-sheet cell formatting, role exclusions, global column exclusion (`ReportColumn` / `excludedColumns`), disabled-clip filtering (`excludeDisabledClips`), project-name / compound-clip-name filtering (`allReportTimelineSources()`; standalone compound-clip exports without ``), `ReportTimecodeFormat` / `--timecode-format`, inventory-first `ReportBuildPhase` progress callbacks. Extraction presets: Captions, Effects, FrameData, Markers, Roles, Titles. Experimental CLI (OpenFCPXMLKit-CLI): single binary with embedded DTDs; --check-version, --convert-version (stripping + DTD validation), --extension-type (fcpxmld | fcpxml; default fcpxmld; 1.5–1.9 always .fcpxml), --validate, --media-copy, --create-project (new empty FCPXML project: --width, --height, --rate, --project-version, output-dir; DTD validation before write; FCP-style output with DOCTYPE, colorSpace, default smart collections), --report (Excel report: role inventory by default; --report-full, per-section flags including --report-markers, --report-keywords, --report-titles-generators, --report-transitions, --report-effects, --report-speed-change-effects, --report-summary, --report-media-summary, --exclude-role, --exclude-column, --exclude-disabled-clips, --timecode-format, --report-project); --log writes user-visible output for all commands to the log file; see Sources/OpenFCPXMLKitCLI/README.md. Xcode 26 dynamic linking compatibility: `swift-log` (`Logging`) is an explicit direct dependency in `Package.swift` to satisfy stricter transitive dylib linking rules when building OpenFCPXMLKit as a dynamic framework. @@ -73,7 +73,7 @@ Foundation XML types (XMLDocument, XMLElement) and the protocol types that wrap - Logging: ServiceLogger protocol with levels trace, debug, info, notice, warning, error, critical (ServiceLogLevel); NoOpServiceLogger, PrintServiceLogger, FileServiceLogger (file + optional console, quiet). FCPXMLUtility and FCPXMLService use injected logger for parse, conversion, validation, save, and media operations. CLI: --log, --log-level, --quiet; when --log is set, all CLI commands (check-version, convert-version, validate, media-copy, create-project) write their user-visible messages to the log file. Extension types use #if canImport(Logging) as fallback where DI is not available. - Versioning: FCPXMLVersion (DTD validation, 1.5-1.14) and FinalCutPro.FCPXML.Version (parsing, 1.0-1.14) are bridged via .fcpxmlVersion, .dtdVersion, and init(from:) converters. init(from:) uses safe fallback to `.latest` instead of force unwrap. - Errors: Module-scoped error types (FCPXMLError for parsing, FCPXMLLoadError for file I/O, FCPXMLExportError/FCPXMLBundleExportError for export, FinalCutPro.FCPXML.ParseError with LocalizedError). Parse failures from FCPXMLFileLoader surface as FCPXMLError.parsingFailed so consumers handle a single parse-error type. FCPXMLElementError uses String element names for Sendable compliance. -- Reporting: Excel report builders in `Reporting/` consume Extraction/Model; they map extracted facts to row/section models (role inventory, markers, keywords, titles, transitions, effects, speed-change effects, summary, media summary) and serialise to XLKit workbooks under `Reporting/Excel/` via `FinalCutPro.FCPXML.buildReport(options:)` and `ReportExcelExport`. Key options: `excludeDisabledClips`, `excludedColumns` (resolved to `ReportColumn` on `Report`), `timecodeFormat` (`ReportTimecodeFormat`: SMPTE frames default with DF/NDF via SwiftTimecode `stringValue()`, Frames, Feet+Frames, HH:MM:SS), `includeMediaSummary`, `mediaBaseURL`. Build / progress order is `ReportBuildPhase.enabledPhases(for:)` (inventory-first product order shared by builder, CLI, and GUI). Support types include `RoleInventoryColumnLayout`, `ReportColumnExclusion` (matches format-suffixed headers), `ReportFormatting` (format-aware cells; numeric sort for Frames / Feet+Frames). **`FCPXMLReportWorkbookExporter`** owns workbook cell formatting only: format-aware column headers, inventory rows tinted by role category (video/caption blue, titles purple, audio green, gap gray); section sheets use `RoleRowColorContext` when Category is absent (Keywords always blue; Effects/Speed Change infer from role name; Titles purple; Transitions gray); Summary uses a header-style project title and black data rows; Media Summary uses red missing-media paths; Markers use marker-type colours; tabular headers use black fill and white text. When FCPXML grows richer, extend Model/Parsing first, then Extraction, then Reporting presentation only. See ARCHITECTURE.md §2.7. +- Reporting: Excel report builders in `Reporting/` consume Extraction/Model; they map extracted facts to row/section models (role inventory, markers, keywords, titles, transitions, effects, speed-change effects, summary, media summary) and serialise to XLKit workbooks under `Reporting/Excel/` via `FinalCutPro.FCPXML.buildReport(options:)` and `ReportExcelExport`. Timeline resolution: `FinalCutPro.FCPXML.allReportTimelineSources()` / `ReportTimelineSource` (project sequences plus event-level compound clips `ref-clip` → `media`/`sequence` when no ``); `ReportOptions.projectName` / CLI `--report-project` match project or compound-clip names; prefer a real project when both exist. Key options: `excludeDisabledClips`, `excludedColumns` (resolved to `ReportColumn` on `Report`), `timecodeFormat` (`ReportTimecodeFormat`: SMPTE frames default with DF/NDF via SwiftTimecode `stringValue()`, Frames, Feet+Frames, HH:MM:SS), `includeMediaSummary`, `mediaBaseURL`. Build / progress order is `ReportBuildPhase.enabledPhases(for:)` (inventory-first product order shared by builder, CLI, and GUI). Support types include `RoleInventoryColumnLayout`, `ReportColumnExclusion` (matches format-suffixed headers), `ReportFormatting` (format-aware cells; numeric sort for Frames / Feet+Frames). **`FCPXMLReportWorkbookExporter`** owns workbook cell formatting only: format-aware column headers, inventory rows tinted by role category (video/caption blue, titles purple, audio green, gap gray); section sheets use `RoleRowColorContext` when Category is absent (Keywords always blue; Effects/Speed Change infer from role name; Titles purple; Transitions gray); Summary uses a header-style project title and black data rows; Media Summary uses red missing-media paths; Markers use marker-type colours; tabular headers use black fill and white text. When FCPXML grows richer, extend Model/Parsing first, then Extraction, then Reporting presentation only. See ARCHITECTURE.md §2.7. --- @@ -127,7 +127,7 @@ Rules: Source layout under Sources/OpenFCPXMLKit/: - Analysis: EditPoint (edit type, source relationship), CutDetectionResult (edit points and counts). -- Classes: FinalCutPro (namespace enum), FCPXML (core struct, init, properties), FCPXMLRoot, FCPXMLRootVersion, FCPXMLElementType, FCPXMLUtility, FCPXMLVersion. +- Classes: FinalCutPro (namespace enum), FCPXML (core struct, init, properties including `allProjects`, `allTimelines`, `allReportTimelineSources` / `ReportTimelineSource` for project + standalone compound-clip report timelines), FCPXMLRoot, FCPXMLRootVersion, FCPXMLElementType, FCPXMLUtility, FCPXMLVersion. - XML: Platform-agnostic XML layer — Protocols (OFKXMLNode, OFKXMLElement, OFKXMLDocument, OFKXMLDTDProtocol, OFKXMLFactory), Foundation/ (FoundationXMLElement, FoundationXMLDocument, FoundationXMLDTD, FoundationXMLFactory), AEXML/ (AEXMLBackendElement, AEXMLBackendDocument, AEXMLBackendFactory), OFKXMLDefaultFactory (platform dispatch: Foundation on macOS, AEXML on iOS). - Delegates: AttributeParserDelegate (property: `values`), FCPXMLParserDelegate (properties: `roles`, `resourceIDs`, `textStyleIDs`; O(1) deduplication via Set). - Errors: FCPXMLError, FCPXMLParseError, TimelineError. @@ -148,7 +148,7 @@ Source layout under Sources/OpenFCPXMLKit/: - Model: FCPXML element models for the parsing layer (previously nested under FinalCutPro/FCPXML/). Subfolders: Adjustments (CropAdjustment, TransformAdjustment, BlendAdjustment, StabilizationAdjustment, VolumeAdjustment, LoudnessAdjustment, NoiseReductionAdjustment, HumReductionAdjustment, EqualizationAdjustment, MatchEqualizationAdjustment, Transform360Adjustment, ColorConformAdjustment, Stereo3DAdjustment, VoiceIsolationAdjustment), Animations (KeyframeAnimation, Keyframe, FadeIn, FadeOut, FadeType), Attributes (AudioLayout, AudioRate, ClipSourceEnable, FrameSampling, TimecodeFormat), Clips (AssetClip, Audio, Audition, Clip including Clip+Adjustments, Gap, MCClip, MulticamSource, RefClip, SyncClip, SyncSource, Title including Title+Typed, Transition, Video), CommonElements (AudioChannelSource, AudioRoleSource, ConformRate, MediaRep, Metadata, Text, TextStyle, TextStyleDefinition, TimeMap), ElementTypes (AnyElementModelType, ElementModelType, ElementType, protocols), Filters (VideoFilter, AudioFilter, VideoFilterMask, FilterParameter), Occlusion (ElementOcclusion, Element Occlusion), Protocols (FCPXMLElement, FCPXMLAttribute, element attribute/children/story protocols), Resources (Asset, Effect, Format, Locator, Media, MediaMulticam, ObjectTracker), Roles (AudioRole, CaptionRole, VideoRole, AncestorRoles, AnyRole, RoleType, FCPXMLRole), Structure (Event, Library, Project, CollectionFolder, KeywordCollection, SmartCollection). Root files: AnyTimeline, Caption including Caption+Typed, Keyword, Marker, Sequence, Spine. - Parsing: XML parsing extensions (Attributes, Clip Parsing, Elements Parsing, Metadata Parsing, Resources Parsing, Roles Parsing, Root Parsing, Time and Frame Rate Parsing). - Extraction: Element extraction logic. Subfolders: Context (DisplayClipName, ElementContext, ElementContextItems, ElementContextTools, FrameRateSource), Effects (EffectsCollector, ExtractedEffect), Presets (CaptionsExtractionPreset, EffectsExtractionPreset, FrameDataPreset, MarkersExtractionPreset, RolesExtractionPreset, TitlesExtractionPreset, FCPXMLExtractionPreset). Root files: Extract, ExtractableChildren, ExtractedElement, ExtractedElementStruct, ExtractedModelElement, Extraction, ExtractionScope. -- Reporting: Excel workbook reports. Top-level: Report, ReportOptions, ReportBuilder, ReportTimecodeFormat, ReportBuildProgress (`ReportBuildPhase.enabledPhases(for:)` — inventory-first). Subfolders: Builders (RoleInventory, Markers, Keywords, Titles, Transitions, Effects, SpeedChangeEffects, Summary, MediaSummary), Sections and Rows (typed section/row models with `columnHeaders(timecodeFormat:)`), Support (RoleInventoryClipCollector, RoleInventoryRowBuilder, RoleInventoryColumnLayout, RoleInventoryRoleSheetOrdering, RoleInventoryTimelineBounds, ReportFormatting, ReportRoleExclusion, ReportColumnExclusion, ReportClipCategory, EffectsReportPolicy, SpeedChangeFormatting, SummaryRoleDurationAggregator), Excel (ReportExcelExport, FCPXMLReportWorkbookExporter with format-aware headers and RoleRowColorContext cell formatting, ReportWorkbookColumnAutoFit via XLKit). Consumes Extraction; owns presentation only; see ARCHITECTURE.md §2.7 for layer boundaries. +- Reporting: Excel workbook reports. Top-level: Report, ReportOptions, ReportBuilder (resolves timelines via `allReportTimelineSources()` / `ReportTimelineSource`), ReportTimecodeFormat, ReportBuildProgress (`ReportBuildPhase.enabledPhases(for:)` — inventory-first). Subfolders: Builders (RoleInventory, Markers, Keywords, Titles, Transitions, Effects, SpeedChangeEffects, Summary, MediaSummary), Sections and Rows (typed section/row models with `columnHeaders(timecodeFormat:)`), Support (RoleInventoryClipCollector, RoleInventoryRowBuilder, RoleInventoryColumnLayout, RoleInventoryRoleSheetOrdering, RoleInventoryTimelineBounds, ReportFormatting, ReportRoleExclusion, ReportColumnExclusion, ReportClipCategory, EffectsReportPolicy, SpeedChangeFormatting, SummaryRoleDurationAggregator), Excel (ReportExcelExport, FCPXMLReportWorkbookExporter with format-aware headers and RoleRowColorContext cell formatting, ReportWorkbookColumnAutoFit via XLKit). Consumes Extraction; owns presentation only; see ARCHITECTURE.md §2.7 for layer boundaries. - FCPXML DTDs: version 1.5-1.14 and README. Maintain this structure; do not introduce new top-level categories without aligning both AGENT.md and .cursorrules. @@ -193,7 +193,7 @@ Tests live under Tests/. The suite is organised as follows. - FCPXMLDTDValidatorTests.swift: Per-version DTD validation. - FCPXMLStructuralValidatorTests.swift: Cross-platform structural validation (iOS fallback path). - FCPXMLAEXMLSerializationParityTests.swift: AEXML vs Foundation XML serialization parity. - - Report and extraction tests (FCPXML-prefixed): FCPXMLRoleInventoryReportTests, FCPXMLRoleInventoryColumnLayoutTests, FCPXMLMarkersReportTests, FCPXMLKeywordsReportTests, FCPXMLTitlesReportTests, FCPXMLTransitionsReportTests, FCPXMLEffectsReportTests, FCPXMLSpeedChangeEffectsReportTests, FCPXMLSummaryReportTests, FCPXMLReportExcelExportTests, FCPXMLReportFormattingTests, FCPXMLReportRoleExclusionTests, FCPXMLReportTimecodeFormatTests, FCPXMLReportBuildPhaseTests, FCPXMLReportColumnExclusionTests, FCPXMLReportExcludeDisabledClipsTests, FCPXMLRoleDisplayPreferenceTests, FCPXMLRoleInventoryClipCollectorTests, FCPXMLRoleInventoryRoleSheetOrderingTests, FCPXMLSummaryRoleDurationAggregatorTests, FCPXMLEffectsReportPolicyTests, FCPXMLSpeedChangeFormattingTests, FCPXMLDisplayClipNameTests, FCPXMLTitleDisplayTests, FCPXMLExtractionScopeTests, FCPXMLExtractedElementTests, FCPXMLEffectsCollectorTests, FCPXMLRolesExtractionPresetTests, FCPXMLEffectAppleSuppliedTests, FCPXMLClipParsingCarriesAudioTests, FCPXMLTransformAdjustmentParsingTests, FCPXMLTransitionSpinePlacementTests. + - Report and extraction tests (FCPXML-prefixed): FCPXMLCompoundClipReportTests, FCPXMLRoleInventoryReportTests, FCPXMLRoleInventoryColumnLayoutTests, FCPXMLMarkersReportTests, FCPXMLKeywordsReportTests, FCPXMLTitlesReportTests, FCPXMLTransitionsReportTests, FCPXMLEffectsReportTests, FCPXMLSpeedChangeEffectsReportTests, FCPXMLSummaryReportTests, FCPXMLReportExcelExportTests, FCPXMLReportFormattingTests, FCPXMLReportRoleExclusionTests, FCPXMLReportTimecodeFormatTests, FCPXMLReportBuildPhaseTests, FCPXMLReportColumnExclusionTests, FCPXMLReportExcludeDisabledClipsTests, FCPXMLRoleDisplayPreferenceTests, FCPXMLRoleInventoryClipCollectorTests, FCPXMLRoleInventoryRoleSheetOrderingTests, FCPXMLSummaryRoleDurationAggregatorTests, FCPXMLEffectsReportPolicyTests, FCPXMLSpeedChangeFormattingTests, FCPXMLDisplayClipNameTests, FCPXMLTitleDisplayTests, FCPXMLExtractionScopeTests, FCPXMLExtractedElementTests, FCPXMLEffectsCollectorTests, FCPXMLRolesExtractionPresetTests, FCPXMLEffectAppleSuppliedTests, FCPXMLClipParsingCarriesAudioTests, FCPXMLTransformAdjustmentParsingTests, FCPXMLTransitionSpinePlacementTests. - OpenFCPXMLKitTests.swift: Main test class; setUpWithError injects parser, timecodeConverter, documentManager, errorHandler, FCPXMLUtility, FCPXMLService. MARK sections group tests (FCPXMLUtility, FCPXMLService, modular components, async/concurrency, performance, frame rates, time values, FCPXML time strings, time conforming, error handling, document management, element filtering, modular extensions, edge cases, FCPXMLElementType, FCPXMLError, ModularUtilities API, XMLDocument extension, XMLElement extension, parser filter). - FileTests/: One test class per sample or category (e.g. FCPXMLFileTest_24, FCPXMLFileTest_AllSamples, FCPXMLFileTest_FrameRates, FCPXMLFileTest_360Video, FCPXMLFileTest_AuditionSample, FCPXMLFileTest_ImageSample, FCPXMLFileTest_Multicam, FCPXMLFileTest_Photoshop, FCPXMLFileTest_SmartCollection). Each loads one or more samples and asserts parse success, root, version, events, projects, or resources as appropriate. - FCPXMLAudioKeyframeTests: Audio keyframes in adjust-volume (param name="amount" with keyframeAnimation); parsing from FCPXML samples; decibel values (-3dB, -37dB); time values (FCPXML fractional format); fadeIn/fadeOut integration; multiple keyframes in sequence; secondary storyline and nested clip detection; TimelineWithSecondaryStorylineWithAudioKeyframes, TimelineSample file tests. @@ -203,7 +203,7 @@ Tests live under Tests/. The suite is organised as follows. - FCPXMLAPIAndEdgeCaseTests: FCPXMLFileLoader async load(from:), ServiceLogger injection (NoOp, Print), edge cases (empty/invalid/malformed XML, invalid paths), validation types, Live Drawing (1.11+), HiddenClipMarker (1.13+). - FCPXMLPerformanceTests: Parameterised and basic performance tests (timecode conversion, document creation, element filtering). -Test organisation: use descriptive test method names; group related tests logically; include setup and teardown; use meaningful assertions. Test all supported frame rates (Final Cut Pro compatible). Use realistic FCPXML samples and edge cases; validate against actual FCP behaviour where applicable. Current total: **925** tests listed in `swift test --list-tests` (**924** in `OpenFCPXMLKitTests` + **1** optional `ExcelReportTest`) covering all functionality including async/await, timeline manipulation, metadata, timestamps, FCPXMLTimecode, MIME type detection, asset validation, silence detection, asset duration measurement, parallel file I/O, cut detection, version conversion stripping, per-version DTD validation, extract-then-copy (CLI --media-copy flow), synchronized clip matching, secondary storyline traversal, clip identification, URL resolution, version conversion edge cases, typed adjustment models (including Transform360, ColorConform, Stereo3D, VoiceIsolation), typed effect/filter models, typed caption/title models, smart collections (match-clip, match-media, match-ratings, match-text, match-usage, match-representation, match-markers, match-analysis-type), keyframe animation, audio keyframes (FCPXMLAudioKeyframeTests: adjust-volume param keyframeAnimation parsing, decibel/time validation, fadeIn/fadeOut integration), CMTime Codable extension, collection organization, Live Drawing (1.11+), HiddenClipMarker (1.13+), Format/Asset 1.13+ (heroEye, heroEyeOverride, mediaReps), FCPXMLExporter clip-level metadata export and XML declaration standalone="no", FCPXMLTimelineManipulationTests refactor (lock-based NowBox, do-catch for throwing APIs), Excel reporting (Selected Roles Inventory column layout, Summary/Media Summary split, ReportTimecodeFormat / format-aware headers, inventory-first ReportBuildPhase progress, global column exclusion, disabled-clip filtering, workbook cell formatting via FCPXMLReportWorkbookExporter), 360 video features, auditions, conform-rate, still images, multicam, secondary storylines, audio keyframes, keyword collections/folders, and Photoshop integration. +Test organisation: use descriptive test method names; group related tests logically; include setup and teardown; use meaningful assertions. Test all supported frame rates (Final Cut Pro compatible). Use realistic FCPXML samples and edge cases; validate against actual FCP behaviour where applicable. Current total: **933** tests listed in `swift test --list-tests` (**932** in `OpenFCPXMLKitTests` + **1** optional `ExcelReportTest`) covering all functionality including async/await, timeline manipulation, metadata, timestamps, FCPXMLTimecode, MIME type detection, asset validation, silence detection, asset duration measurement, parallel file I/O, cut detection, version conversion stripping, per-version DTD validation, extract-then-copy (CLI --media-copy flow), synchronized clip matching, secondary storyline traversal, clip identification, URL resolution, version conversion edge cases, typed adjustment models (including Transform360, ColorConform, Stereo3D, VoiceIsolation), typed effect/filter models, typed caption/title models, smart collections (match-clip, match-media, match-ratings, match-text, match-usage, match-representation, match-markers, match-analysis-type), keyframe animation, audio keyframes (FCPXMLAudioKeyframeTests: adjust-volume param keyframeAnimation parsing, decibel/time validation, fadeIn/fadeOut integration), CMTime Codable extension, collection organization, Live Drawing (1.11+), HiddenClipMarker (1.13+), Format/Asset 1.13+ (heroEye, heroEyeOverride, mediaReps), FCPXMLExporter clip-level metadata export and XML declaration standalone="no", FCPXMLTimelineManipulationTests refactor (lock-based NowBox, do-catch for throwing APIs), Excel reporting (Selected Roles Inventory column layout, Summary/Media Summary split, ReportTimecodeFormat / format-aware headers, inventory-first ReportBuildPhase progress, global column exclusion, disabled-clip filtering, workbook cell formatting via FCPXMLReportWorkbookExporter), 360 video features, auditions, conform-rate, still images, multicam, secondary storylines, audio keyframes, keyword collections/folders, and Photoshop integration. --- @@ -223,7 +223,7 @@ SwiftTimecode usage: use `Timecode(.realTime(seconds: seconds), at: frameRate)` ## Testing Requirements -Test coverage: unit tests for all public APIs; integration tests for complex workflows; performance tests for time-critical operations; concurrency tests for async operations; test all supported frame rates (Final Cut Pro compatible). Current: **925** tests listed in `swift test --list-tests` (including AEXML serialization parity, FCPXMLDTDValidatorTests, FCPXMLStructuralValidatorTests, FCPXMLReportTimecodeFormatTests, FCPXMLReportBuildPhaseTests, FCPXMLReportColumnExclusionTests, FCPXMLReportExcludeDisabledClipsTests, FCPXMLRoleInventoryColumnLayoutTests, FCPXMLReportExcelExportTests workbook cell formatting, optional ExcelReportTest integration) covering all functionality including async/await, timeline manipulation, metadata, timestamps, FCPXMLTimecode, MIME type detection, asset validation, silence detection, asset duration measurement, parallel file I/O, version conversion, DTD validation, extract-then-copy flow, synchronized clip matching, secondary storyline traversal, clip identification, URL resolution, version conversion edge cases, typed adjustment models (including Transform360, ColorConform, Stereo3D, VoiceIsolation), typed effect/filter models, typed caption/title models, smart collections (match-clip, match-media, match-ratings, match-text, match-usage, match-representation, match-markers, match-analysis-type), keyframe animation, audio keyframes (FCPXMLAudioKeyframeTests: adjust-volume param keyframeAnimation parsing, decibel/time validation, fadeIn/fadeOut integration), CMTime Codable extension, collection organization, Live Drawing (1.11+), HiddenClipMarker (1.13+), Format/Asset 1.13+ (heroEye, heroEyeOverride, mediaReps), FCPXMLExporter clip-level metadata export and XML declaration standalone="no", FCPXMLTimelineManipulationTests (lock-based NowBox, do-catch), Excel reporting enhancements, 360 video features, auditions, conform-rate, still images, multicam, secondary storylines, audio keyframes, keyword collections/folders, and Photoshop integration. +Test coverage: unit tests for all public APIs; integration tests for complex workflows; performance tests for time-critical operations; concurrency tests for async operations; test all supported frame rates (Final Cut Pro compatible). Current: **933** tests listed in `swift test --list-tests` (including AEXML serialization parity, FCPXMLDTDValidatorTests, FCPXMLStructuralValidatorTests, FCPXMLReportTimecodeFormatTests, FCPXMLReportBuildPhaseTests, FCPXMLReportColumnExclusionTests, FCPXMLReportExcludeDisabledClipsTests, FCPXMLRoleInventoryColumnLayoutTests, FCPXMLReportExcelExportTests workbook cell formatting, optional ExcelReportTest integration) covering all functionality including async/await, timeline manipulation, metadata, timestamps, FCPXMLTimecode, MIME type detection, asset validation, silence detection, asset duration measurement, parallel file I/O, version conversion, DTD validation, extract-then-copy flow, synchronized clip matching, secondary storyline traversal, clip identification, URL resolution, version conversion edge cases, typed adjustment models (including Transform360, ColorConform, Stereo3D, VoiceIsolation), typed effect/filter models, typed caption/title models, smart collections (match-clip, match-media, match-ratings, match-text, match-usage, match-representation, match-markers, match-analysis-type), keyframe animation, audio keyframes (FCPXMLAudioKeyframeTests: adjust-volume param keyframeAnimation parsing, decibel/time validation, fadeIn/fadeOut integration), CMTime Codable extension, collection organization, Live Drawing (1.11+), HiddenClipMarker (1.13+), Format/Asset 1.13+ (heroEye, heroEyeOverride, mediaReps), FCPXMLExporter clip-level metadata export and XML declaration standalone="no", FCPXMLTimelineManipulationTests (lock-based NowBox, do-catch), Excel reporting enhancements, 360 video features, auditions, conform-rate, still images, multicam, secondary storylines, audio keyframes, keyword collections/folders, and Photoshop integration. Test data: use realistic FCPXML samples; include edge cases and error conditions; test all supported frame rates; validate against actual Final Cut Pro output where applicable. @@ -312,7 +312,7 @@ Keep AGENT.md and .cursorrules in sync. Both must reflect: changelog styling (CH - Architecture (protocols, implementations, extensions, service, utilities) and single injection point (FCPXMLUtility.defaultForExtensions). - Source layout (Analysis, Classes, Delegates, Errors, Extensions including +Modular and +Codable, Implementations, Protocols, Services, Utilities, Annotations, Export, Timeline, Timing, Validation, FileIO, Logging, Format, Model including Adjustments, Animations, Filters, Clips with +Adjustments and +Typed, CommonElements with TextStyle/TextStyleDefinition, Structure with CollectionFolder/KeywordCollection/SmartCollection, Parsing, Extraction, Reporting including Excel/, XML with Protocols/Foundation/AEXML/OFKXMLDefaultFactory, FCPXML DTDs; reporting vs core layers in ARCHITECTURE.md §2.7). - Test structure (Tests/ layout, FCPXMLTestResources, FCPXMLTestUtilities, FileTests/ including FCPXMLFileTest_360Video, FCPXMLFileTest_AuditionSample, FCPXMLFileTest_ImageSample, FCPXMLFileTest_Multicam, FCPXMLFileTest_Photoshop, FCPXMLFileTest_SmartCollection, LogicAndParsing/ including FCPXMLFormatAssetTests, FCPXMLCutDetectionTests, FCPXMLVersionConversionTests, FCPXMLMediaExtractionTests, FCPXMLTimelineManipulationTests, FCPXMLTimecodeTests, FCPXMLMIMETypeDetectionTests, FCPXMLAssetValidationTests, FCPXMLSilenceDetectionTests, FCPXMLAssetDurationMeasurementTests, FCPXMLParallelFileIOTests, FCPXMLAudioEnhancementTests, FCPXMLTransform360Tests, FCPXMLCaptionTitleTests, FCPXMLKeyframeAnimationTests, FCPXMLAudioKeyframeTests, FCPXMLCMTimeCodableTests, FCPXMLCollectionTests, FCPXMLSmartCollectionTests, FCPXMLAdjustmentTests, FCPXMLFilterTests, FCPXMLImportOptionsTests, FCPXMLCodableTests, FCPXMLAEXMLSerializationParityTests, FCPXMLDTDValidatorTests, FCPXMLStructuralValidatorTests, OpenFCPXMLKitTests.swift, FCPXMLTimelineExportValidationTests, FCPXMLAPIAndEdgeCaseTests, FCPXMLPerformanceTests, and report/extraction tests (FCPXMLRoleInventoryReportTests, FCPXMLMarkersReportTests, FCPXMLKeywordsReportTests, FCPXMLTitlesReportTests, FCPXMLTransitionsReportTests, FCPXMLEffectsReportTests, FCPXMLSpeedChangeEffectsReportTests, FCPXMLSummaryReportTests, FCPXMLReportExcelExportTests, FCPXMLReportFormattingTests, FCPXMLReportRoleExclusionTests, FCPXMLReportTimecodeFormatTests, FCPXMLReportBuildPhaseTests, FCPXMLRoleDisplayPreferenceTests, FCPXMLRoleInventoryClipCollectorTests, FCPXMLRoleInventoryRoleSheetOrderingTests, FCPXMLSummaryRoleDurationAggregatorTests, FCPXMLEffectsReportPolicyTests, FCPXMLSpeedChangeFormattingTests, FCPXMLDisplayClipNameTests, FCPXMLTitleDisplayTests, FCPXMLExtractionScopeTests, FCPXMLExtractedElementTests, FCPXMLEffectsCollectorTests, FCPXMLRolesExtractionPresetTests, FCPXMLEffectAppleSuppliedTests, FCPXMLClipParsingCarriesAudioTests, FCPXMLTransformAdjustmentParsingTests, FCPXMLTransitionSpinePlacementTests); every test-case class is FCPXML-prefixed except the module-named umbrella OpenFCPXMLKitTests; empty timeline creation and project-creation export at different sizes and frame rates in FCPXMLTimelineExportValidationTests (clip-level metadata export, XML declaration standalone="no")). Cross-platform XML (OFKXML*, Foundation vs AEXML, FCPXMLStructuralValidator, iOS). -- FCPXML 1.5–1.14 and FCPXMLElementType; FCPXMLVersion.supportsBundleFormat (1.10+); version conversion with element stripping and per-version DTD validation; FCPXML creation from scratch; timeline manipulation (ripple insert, auto lane assignment, clip queries, lane range, secondary storylines); timeline metadata (markers, chapter markers, keywords, ratings, timestamps); FCPXMLTimecode custom type; MIME type detection; asset validation (including still images); silence detection; asset duration measurement; parallel file I/O; TimelineFormat enhancements; typed adjustment models (Crop, Transform, Blend, Stabilization, Volume, Loudness, NoiseReduction, HumReduction, Equalization, MatchEqualization, Transform360, ColorConform, Stereo3D, VoiceIsolation); typed effect/filter models (VideoFilter, AudioFilter, VideoFilterMask, FilterParameter with keyframe animation and auxValue 1.11+); typed caption/title models (Caption, Title with TextStyle, TextStyleDefinition); smart collections (SmartCollection with match-clip, match-media, match-ratings, match-text, match-usage, match-representation, match-markers, match-analysis-type); keyframe animation (KeyframeAnimation, Keyframe, FadeIn, FadeOut); audio keyframes (FCPXMLAudioKeyframeTests: adjust-volume param keyframeAnimation parsing, decibel/time validation, fadeIn/fadeOut integration); CMTime Codable extension; collection organization (CollectionFolder, KeywordCollection); Live Drawing (1.11+); HiddenClipMarker (1.13+); Format/Asset 1.13+ (heroEye, heroEyeOverride, mediaReps); experimental CLI (OpenFCPXMLKit-CLI, single binary, embedded DTDs, --check-version, --convert-version, --extension-type fcpxml|fcpxmld, --validate, --media-copy, --create-project with DTD validation and FCP-style output, --report Excel report with --report-full/per-section flags/--report-summary/--report-media-summary/--exclude-role/--exclude-column/--exclude-disabled-clips/--timecode-format/--report-project, --log/--log-level/--quiet with log file capturing all command output); Excel reporting subsystem (Reporting/ builders, Sections/Rows, Support including RoleInventoryColumnLayout, ReportColumnExclusion, ReportFormatting, ReportTimecodeFormat, ReportBuildProgress; Excel/ via XLKit; buildReport/ReportExcelExport; Summary and Media Summary sheets; excludeDisabledClips/excludedColumns/timecodeFormat; inventory-first enabledPhases); extraction presets (Captions, Effects, FrameData, Markers, Roles, Titles); Tests/ExcelReportTest optional integration; Final Cut Pro frame rates; Swift 6 concurrency (Sendable, async/await, CI strict-concurrency job); Xcode 26 dynamic framework linking compatibility via explicit `swift-log` (`Logging`) dependency in `Package.swift`. +- FCPXML 1.5–1.14 and FCPXMLElementType; FCPXMLVersion.supportsBundleFormat (1.10+); version conversion with element stripping and per-version DTD validation; FCPXML creation from scratch; timeline manipulation (ripple insert, auto lane assignment, clip queries, lane range, secondary storylines); timeline metadata (markers, chapter markers, keywords, ratings, timestamps); FCPXMLTimecode custom type; MIME type detection; asset validation (including still images); silence detection; asset duration measurement; parallel file I/O; TimelineFormat enhancements; typed adjustment models (Crop, Transform, Blend, Stabilization, Volume, Loudness, NoiseReduction, HumReduction, Equalization, MatchEqualization, Transform360, ColorConform, Stereo3D, VoiceIsolation); typed effect/filter models (VideoFilter, AudioFilter, VideoFilterMask, FilterParameter with keyframe animation and auxValue 1.11+); typed caption/title models (Caption, Title with TextStyle, TextStyleDefinition); smart collections (SmartCollection with match-clip, match-media, match-ratings, match-text, match-usage, match-representation, match-markers, match-analysis-type); keyframe animation (KeyframeAnimation, Keyframe, FadeIn, FadeOut); audio keyframes (FCPXMLAudioKeyframeTests: adjust-volume param keyframeAnimation parsing, decibel/time validation, fadeIn/fadeOut integration); CMTime Codable extension; collection organization (CollectionFolder, KeywordCollection); Live Drawing (1.11+); HiddenClipMarker (1.13+); Format/Asset 1.13+ (heroEye, heroEyeOverride, mediaReps); experimental CLI (OpenFCPXMLKit-CLI, single binary, embedded DTDs, --check-version, --convert-version, --extension-type fcpxml|fcpxmld, --validate, --media-copy, --create-project with DTD validation and FCP-style output, --report Excel report with --report-full/per-section flags/--report-summary/--report-media-summary/--exclude-role/--exclude-column/--exclude-disabled-clips/--timecode-format/--report-project, --log/--log-level/--quiet with log file capturing all command output); Excel reporting subsystem (`allReportTimelineSources` / compound-clip timelines) (Reporting/ builders, Sections/Rows, Support including RoleInventoryColumnLayout, ReportColumnExclusion, ReportFormatting, ReportTimecodeFormat, ReportBuildProgress; Excel/ via XLKit; buildReport/ReportExcelExport; Summary and Media Summary sheets; excludeDisabledClips/excludedColumns/timecodeFormat; inventory-first enabledPhases); extraction presets (Captions, Effects, FrameData, Markers, Roles, Titles); Tests/ExcelReportTest optional integration; Final Cut Pro frame rates; Swift 6 concurrency (Sendable, async/await, CI strict-concurrency job); Xcode 26 dynamic framework linking compatibility via explicit `swift-log` (`Logging`) dependency in `Package.swift`. When updating either file, apply the same information to both and keep terminology and examples consistent. diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index f8d001a..f0475e4 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -16,7 +16,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 2.2.0+, swift-log 1.14.0+, AEXML 4.7.0+, swift-argument-parser 1.8.2+ (CLI only), Foundation, CoreMedia. - **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:** **925** tests listed in `swift test --list-tests` — **924** in `OpenFCPXMLKitTests` (921 XCTest `func test` + 3 Swift Testing `@Test`) and **1** optional `ExcelReportTest` integration; **58** sample `.fcpxml` files under `Tests/FCPXML Samples/FCPXML/`. +- **Tests:** **933** tests listed in `swift test --list-tests` — **932** in `OpenFCPXMLKitTests` (929 XCTest `func test` + 3 Swift Testing `@Test`) and **1** optional `ExcelReportTest` integration; **58** sample `.fcpxml` files under `Tests/FCPXML Samples/FCPXML/`. --- @@ -110,6 +110,7 @@ Reporting/ Row models, builders, sheet-specific presentation rules - `ReportOptions.excludeDisabledClips` flows into `ExtractionScope.includeDisabled` at build time so disabled clips are omitted consistently across all timeline-based sections. - `ReportOptions.timecodeFormat` is stored on `Report.timecodeFormat` and drives cell strings plus Excel header suffixes (e.g. `Timeline In (frames)`). - Build / progress order is **`ReportBuildPhase.enabledPhases(for:)`** (product / workbook order: Selected Roles Inventory first, then Markers … Media Summary). `ReportBuilder` and CLI/GUI progress share this list. +- **Timeline resolution** for `buildReport` / `ReportBuilder` uses **`FinalCutPro.FCPXML.allReportTimelineSources()`** (defined on `FCPXMLProperties`): every `` sequence, plus event-level compound clips (`ref-clip` → `media`/`sequence`) when FCP exported a compound clip with no ``. Prefer a real project when both exist. `ReportOptions.projectName` / CLI `--report-project` match project or compound-clip display names. Discovery belongs on `FCPXML` (Classes); Reporting only consumes `ReportTimelineSource`. - Do **not** duplicate timeline math, role resolution, or element traversal that belongs in Extraction/Model. **Where to put a change** @@ -118,6 +119,7 @@ Reporting/ Row models, builders, sheet-specific presentation rules |--------|--------| | New `adjust-*` or `filter-*` element understood from XML | Model, Parsing | | Correct absolute timeline for a nested clip or effect span | Extraction | +| Discover project vs standalone compound-clip report timelines | Classes (`allReportTimelineSources` / `ReportTimelineSource`); Reporting resolves via that API | | Which rows appear on a given workbook sheet | Reporting | | Column labels, timecode strings, enabled checkmarks | Reporting | | Timecode display mode (SMPTE / Frames / Feet+Frames / HH:MM:SS) and header suffixes | `ReportOptions.timecodeFormat` → `Report.timecodeFormat` → Formatting + Excel export | @@ -158,7 +160,7 @@ flowchart TB SRC --> CLI["OpenFCPXMLKitCLI → OpenFCPXMLKit-CLI"] SRC --> GEN["GenerateEmbeddedDTDs"] - TST --> OKT["OpenFCPXMLKitTests — 924 tests"] + TST --> OKT["OpenFCPXMLKitTests — 932 tests"] TST --> ERT["ExcelReportTest — 1 optional integration"] TST --> SMP["FCPXML Samples/ — 58 .fcpxml files"] ``` @@ -243,7 +245,7 @@ Source layout under **`Sources/OpenFCPXMLKit/`**: | Folder | Purpose | |--------|---------| | **Analysis** | EditPoint, CutDetectionResult (cut detection). | -| **Classes** | FinalCutPro, FCPXML, FCPXMLElementType, FCPXMLUtility, FCPXMLVersion, FCPXMLRoot, FCPXMLRootVersion, FCPXMLInit, FCPXMLProperties. | +| **Classes** | FinalCutPro, FCPXML, FCPXMLElementType, FCPXMLUtility, FCPXMLVersion, FCPXMLRoot, FCPXMLRootVersion, FCPXMLInit, FCPXMLProperties (`allProjects`, `allTimelines`, `allReportTimelineSources` / `ReportTimelineSource` for project + standalone compound-clip report timelines). | | **Delegates** | AttributeParserDelegate, FCPXMLParserDelegate (internal). | | **Errors** | FCPXMLError, FCPXMLParseError, TimelineError. | | **Extensions** | CMTime, XMLElement, XMLDocument (+Modular, +Codable, and non-modular). FCPXML extensions operate on OFKXMLElement/OFKXMLDocument protocol types. | @@ -263,7 +265,7 @@ Source layout under **`Sources/OpenFCPXMLKit/`**: | **Model** | FCPXML element models: Adjustments, Animations, Attributes, Clips, CommonElements, ElementTypes, Filters, Occlusion, Protocols, Resources, Roles, Structure (CollectionFolder, KeywordCollection, etc.). | | **Parsing** | XML parsing extensions (Attributes, Clip, Elements, Metadata, Resources, Roles, Root, Time and Frame Rate). | | **Extraction** | `fcpExtract`, ExtractedElement, ExtractionScope, ExtractableChildren. **Context/** (DisplayClipName, ElementContext, ElementContextItems/Tools, FrameRateSource), **Effects/** (EffectsCollector, ExtractedEffect), **Presets/** (Captions, Effects, FrameData, Markers, Roles, Titles, plus the base ExtractionPreset). | -| **Reporting** | Excel workbook reports. Top-level: `Report`, `ReportOptions`, `ReportBuilder`, `ReportTimecodeFormat` (`.smpteFrames` / `.frames` / `.feetAndFrames` / `.smpteNoFrames`), `ReportBuildProgress` (`ReportBuildPhase.enabledPhases(for:)` — inventory-first product order shared by builder, CLI, and GUI progress). **Builders/** — per-sheet builders including `MediaSummaryReportBuilder` (missing media paths) and `SummaryReportBuilder` (project metrics + role durations); build loop follows enabled phases. **Sections/** and **Rows/** — typed section/row models with `columnHeaders(timecodeFormat:)` (Selected Roles Inventory, Markers, Keywords, Titles & Generators, Transitions, Effects, Speed Change Effects, Summary, Media Summary). **Support/** — `RoleInventoryClipCollector`, `RoleInventoryRowBuilder`, `RoleInventoryColumnLayout` (Row + 23 fixed columns + dynamic metadata keys; format-aware headers), `RoleInventoryRoleSheetOrdering`, `RoleInventoryTimelineBounds`, `ReportFormatting` (SMPTE DF/NDF via SwiftTimecode `stringValue()`, Frames / Feet+Frames / HH:MM:SS; numeric `compareTimelinePositions` for Frames and Feet+Frames), `ReportRoleExclusion`, `ReportColumnExclusion` (`ReportColumn` global column omission; matches format-suffixed headers), `ReportClipCategory`, `EffectsReportPolicy`, `SpeedChangeFormatting`, `SummaryRoleDurationAggregator`. **Excel/** — `ReportExcelExport`, `FCPXMLReportWorkbookExporter` (applies `Report.excludedColumns`, `Report.timecodeFormat` header suffixes, `RoleRowColorContext` sheet-specific row colours, inventory role-category tints, Summary project-title header + black data rows, Media Summary red missing-media paths, marker-type colours), `ReportWorkbookColumnAutoFit`. `ReportOptions`: `excludeDisabledClips`, `excludedColumns`, `timecodeFormat`, `includeMediaSummary`, `mediaBaseURL`. Consumes Extraction; owns presentation only — see §2.7. | +| **Reporting** | Excel workbook reports. Top-level: `Report`, `ReportOptions`, `ReportBuilder`, `ReportTimecodeFormat` (`.smpteFrames` / `.frames` / `.feetAndFrames` / `.smpteNoFrames`), `ReportBuildProgress` (`ReportBuildPhase.enabledPhases(for:)` — inventory-first product order shared by builder, CLI, and GUI progress). **Builders/** — per-sheet builders including `MediaSummaryReportBuilder` (missing media paths) and `SummaryReportBuilder` (project metrics + role durations); build loop follows enabled phases. **Sections/** and **Rows/** — typed section/row models with `columnHeaders(timecodeFormat:)` (Selected Roles Inventory, Markers, Keywords, Titles & Generators, Transitions, Effects, Speed Change Effects, Summary, Media Summary). **Support/** — `RoleInventoryClipCollector`, `RoleInventoryRowBuilder`, `RoleInventoryColumnLayout` (Row + 23 fixed columns + dynamic metadata keys; format-aware headers), `RoleInventoryRoleSheetOrdering`, `RoleInventoryTimelineBounds`, `ReportFormatting` (SMPTE DF/NDF via SwiftTimecode `stringValue()`, Frames / Feet+Frames / HH:MM:SS; numeric `compareTimelinePositions` for Frames and Feet+Frames), `ReportRoleExclusion`, `ReportColumnExclusion` (`ReportColumn` global column omission; matches format-suffixed headers), `ReportClipCategory`, `EffectsReportPolicy`, `SpeedChangeFormatting`, `SummaryRoleDurationAggregator`. **Excel/** — `ReportExcelExport`, `FCPXMLReportWorkbookExporter` (applies `Report.excludedColumns`, `Report.timecodeFormat` header suffixes, `RoleRowColorContext` sheet-specific row colours, inventory role-category tints, Summary project-title header + black data rows, Media Summary red missing-media paths, marker-type colours), `ReportWorkbookColumnAutoFit`. `ReportOptions`: `excludeDisabledClips`, `excludedColumns`, `timecodeFormat`, `includeMediaSummary`, `mediaBaseURL`, `projectName` (project or compound-clip name). Timeline pick via `allReportTimelineSources()` (see §2.7). Consumes Extraction; owns presentation only. | | **XML** | Platform-agnostic XML layer: Protocols (OFKXMLNode, OFKXMLElement, OFKXMLDocument, OFKXMLDTDProtocol, OFKXMLFactory), Foundation/ (Foundation backends), AEXML/ (AEXML backends), OFKXMLDefaultFactory. | | **FCPXML DTDs** | Version 1.5–1.14 DTDs. | @@ -341,6 +343,7 @@ Source layout under **`Sources/OpenFCPXMLKit/`**: - **Cross-platform XML:** Use `OFKXMLDefaultFactory()` when creating documents/elements so iOS gets the AEXML backend. All parsing and model code uses `any OFKXMLDocument` / `any OFKXMLElement`; the concrete type is chosen at runtime. - **Logging:** `ServiceLogger` protocol with `ServiceLogLevel`; inject via `FCPXMLService` / `FCPXMLUtility` or build from CLI `LogOptions.makeLogger()`. - **Service factory:** `ModularUtilities.createService()` returns a fully configured `FCPXMLService`; `createCustomService(...)` accepts custom protocol implementations. +- **Report timeline sources:** `allReportTimelineSources()` discovers project sequences and event-level compound-clip sequences so Excel reporting works for FCP “Export XML” of a compound clip (no ``). --- @@ -348,16 +351,16 @@ Source layout under **`Sources/OpenFCPXMLKit/`**: Binary name: **`OpenFCPXMLKit-CLI`**. Mutually exclusive modes: `--check-version`, `--convert-version`, `--extension-type` (fcpxmld | fcpxml), `--validate`, `--media-copy`, `--report`, `--create-project` (requires `--width`, `--height`, `--rate`, `--project-version`, output-dir). -**`--report`** builds an Excel workbook (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-effects`, `--report-speed-change-effects`, `--report-summary`, `--report-media-summary`. Filtering: `--exclude-role` (repeatable), `--exclude-column` (repeatable; global column omission), `--exclude-disabled-clips` (omit `enabled="0"` clips), `--report-project`. Timecode cells: `--timecode-format` (`HH:MM:SS:FF` default, `Frames`, `Feet+Frames`, `HH:MM:SS`). Progress labels follow `ReportBuildPhase.enabledPhases(for:)` (inventory first). Log options: `--log`, `--log-level`, `--quiet`. See `Sources/OpenFCPXMLKitCLI/README.md` and `Documentation/Manual/16-CLI.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-effects`, `--report-speed-change-effects`, `--report-summary`, `--report-media-summary`. Filtering: `--exclude-role` (repeatable), `--exclude-column` (repeatable; global column omission), `--exclude-disabled-clips` (omit `enabled="0"` clips), `--report-project` (project or compound-clip name). Timecode cells: `--timecode-format` (`HH:MM:SS:FF` default, `Frames`, `Feet+Frames`, `HH:MM:SS`). Progress labels follow `ReportBuildPhase.enabledPhases(for:)` (inventory first). Log options: `--log`, `--log-level`, `--quiet`. See `Sources/OpenFCPXMLKitCLI/README.md` and `Documentation/Manual/16-CLI.md`. --- ## 8. Tests -- **Count:** **925** listed in `swift test --list-tests` — **924** in `OpenFCPXMLKitTests` (921 XCTest + 3 Swift Testing `@Test` in `FCPXMLReportRoleExclusionTests`) and **1** in optional `ExcelReportTest` (skips without a local `.fcpxml`/`.fcpxmld` fixture). +- **Count:** **933** listed in `swift test --list-tests` — **932** in `OpenFCPXMLKitTests` (929 XCTest + 3 Swift Testing `@Test` in `FCPXMLReportRoleExclusionTests`) and **1** in optional `ExcelReportTest` (skips without a local `.fcpxml`/`.fcpxmld` fixture). - **Location:** `Tests/OpenFCPXMLKitTests/`; samples in `Tests/FCPXML Samples/FCPXML/` (58 files); optional integration under `Tests/ExcelReportTest/`. - **Utilities:** `FCPXMLTestResources.swift`, `FCPXMLTestUtilities.swift` (path resolution, sample loading; `XCTSkip` when a sample is missing); `FCPXMLReportingReportFixture.swift` / `FCPXMLReportingReportTestSupport.swift` for optional reporting fixtures. -- **Reporting tests:** `FCPXMLReportTimecodeFormatTests` (DF/NDF, all four formats, format-aware headers, full-report shape), `FCPXMLReportBuildPhaseTests` (inventory-first `enabledPhases` / `onPhaseStarted` order), `FCPXMLRoleInventoryColumnLayoutTests`, `FCPXMLReportColumnExclusionTests` (including suffixed Timeline In headers), `FCPXMLReportExcludeDisabledClipsTests`, `FCPXMLReportExcelExportTests` (workbook cell formatting: Summary title header, black role-duration data, red missing-media paths, inventory/section-sheet colour rules), `FCPXMLReportFormattingTests` (SMPTE / Frames / Feet+Frames / HH:MM:SS formatting and numeric sort guardrails), plus role inventory, section, and related support tests. See **Tests/README.md** §1 for the full file tree. +- **Reporting tests:** `FCPXMLCompoundClipReportTests` (standalone compound-clip FCPXML / `allReportTimelineSources()`), `FCPXMLReportTimecodeFormatTests` (DF/NDF, all four formats, format-aware headers, full-report shape), `FCPXMLReportBuildPhaseTests` (inventory-first `enabledPhases` / `onPhaseStarted` order), `FCPXMLRoleInventoryColumnLayoutTests`, `FCPXMLReportColumnExclusionTests` (including suffixed Timeline In headers), `FCPXMLReportExcludeDisabledClipsTests`, `FCPXMLReportExcelExportTests` (workbook cell formatting: Summary title header, black role-duration data, red missing-media paths, inventory/section-sheet colour rules), `FCPXMLReportFormattingTests` (SMPTE / Frames / Feet+Frames / HH:MM:SS formatting and numeric sort guardrails), plus role inventory, section, and related support tests. See **Tests/README.md** §1 for the full file tree. - **Coverage:** Unit, integration, and performance tests; sync and async; all supported frame rates and FCPXML versions. See **Tests/README.md** for categories and how to run tests. --- diff --git a/CHANGELOG.md b/CHANGELOG.md index 304e23e..e7051c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,22 @@ OpenFCPXMLKit uses **New Features**, **Improvements**, and **Bug Fixes** for eac --- +## [3.0.3](https://github.com/TheAcharya/OpenFCPXMLKit/releases/tag/3.0.3) - 2026-07-10 + +### ✨ New Features + +- **Compound-clip Excel reports:** `FinalCutPro.FCPXML.allReportTimelineSources()` discovers both project sequences and standalone compound-clip timelines (event-level `ref-clip` → `media`/`sequence`). `ReportBuilder` / `buildReport` and Summary use this so FCP “Export XML” of a compound clip (no ``) produces role inventory, markers, and other sheets like a normal project. + +### 🔧 Improvements + +- **Test suite:** Expanded to **933** tests (**932** in `OpenFCPXMLKitTests` + **1** optional `ExcelReportTest`), including `FCPXMLCompoundClipReportTests`. + +### 🐛 Bug Fixes + +- **Report timeline resolution:** Documents that contain only an event-level compound clip no longer fail with `ReportError.noProjectsFound`; reporting walks the compound clip’s `media` sequence instead. + +--- + ## [3.0.2](https://github.com/TheAcharya/OpenFCPXMLKit/releases/tag/3.0.2) - 2026-07-08 ### ✨ New Features diff --git a/Documentation/Manual/01-Overview.md b/Documentation/Manual/01-Overview.md index 5ae5b14..7a0e4ce 100644 --- a/Documentation/Manual/01-Overview.md +++ b/Documentation/Manual/01-Overview.md @@ -61,7 +61,7 @@ Inject a logger into `FCPXMLService` or `FCPXMLUtility` to observe parse, conver ## Extensions and high-level model - **OFKXMLDocument** and **OFKXMLElement** (protocol types) have FCPXML-specific `fcpx*` properties and methods. On macOS the concrete types wrap Foundation XML; on iOS they wrap AEXML. Use modular overloads (e.g. `addResource(_:using: documentManager)`) when injecting dependencies. -- **FinalCutPro.FCPXML** wraps a document for high-level access: `root`, `version`, `allEvents()`, `allProjects()`. +- **FinalCutPro.FCPXML** wraps a document for high-level access: `root`, `version`, `allEvents()`, `allProjects()`, `allTimelines()`, `allReportTimelineSources()` (projects plus standalone compound-clip timelines for reporting). --- diff --git a/Documentation/Manual/14-High-Level-Model.md b/Documentation/Manual/14-High-Level-Model.md index 1fb52de..44f5158 100644 --- a/Documentation/Manual/14-High-Level-Model.md +++ b/Documentation/Manual/14-High-Level-Model.md @@ -17,8 +17,10 @@ For quick inspection and high-level access without walking the XML tree, use **F - **root** — Root wrapper - **version** — Version (FinalCutPro.FCPXML.Version) -- **allEvents()** — Event names (or equivalent) -- **allProjects()** — Project names (or equivalent) +- **allEvents()** — All events in the document +- **allProjects()** — All projects in the document +- **allTimelines()** — Top-level timelines (sequences, clips, etc.) in document order +- **allReportTimelineSources()** — Timelines suitable for Excel reporting: every project sequence, plus event-level compound clips (`ref-clip` → `media`/`sequence`) when FCP exported a compound clip without a `` ```swift let data = try loader.loadData(from: url) @@ -26,12 +28,15 @@ let fcpxml = try FinalCutPro.FCPXML(fileContent: data) let eventNames = fcpxml.allEvents() let projectNames = fcpxml.allProjects() +let reportSources = fcpxml.allReportTimelineSources() let root = fcpxml.root let version = fcpxml.version ``` Bridging with **FCPXMLVersion** (DTD/validation): use `.fcpxmlVersion` and `.dtdVersion` and `init(from:)` converters where provided. +For building Excel reports from either a project or a standalone compound clip, see [19 — Reporting & Excel Export](19-Reporting.md). + --- ## Next diff --git a/Documentation/Manual/16-CLI.md b/Documentation/Manual/16-CLI.md index 1e6a0fd..db24162 100644 --- a/Documentation/Manual/16-CLI.md +++ b/Documentation/Manual/16-CLI.md @@ -29,7 +29,7 @@ Use **one** of: `--check-version`, `--convert-version`, `--validate`, `--media-c ### REPORT -Build an Excel (`.xlsx`) report workbook from FCPXML/FCPXMLD. The workbook is written to ``; its file name is derived from the project name. See [19 — Reporting & Excel Export](19-Reporting.md) for the underlying API. +Build an Excel (`.xlsx`) report workbook from FCPXML/FCPXMLD. Works for normal project timelines and for standalone compound-clip exports (event `ref-clip` with no ``). The workbook is written to ``; its file name is derived from the project or compound-clip name. See [19 — Reporting & Excel Export](19-Reporting.md) for the underlying API. | Option | Description | |--------|-------------| @@ -43,7 +43,7 @@ Build an Excel (`.xlsx`) report workbook from FCPXML/FCPXMLD. The workbook is wr | **--report-speed-change-effects** | Include the Speed Change Effects sheet (with `--report`). | | **--report-summary** | Include the Summary sheet (project metrics and role-duration totals; with `--report`). | | **--report-media-summary** | Include the Media Summary sheet (missing media file paths; with `--report`). | -| **--report-project <name>** | Project name filter when the FCPXML contains multiple projects. | +| **--report-project <name>** | Timeline name filter: matches a `` name or a standalone compound-clip / `ref-clip` name when the document has more than one reportable timeline. | | **--exclude-role <role>** | Exclude a role or subrole from the role inventory (repeatable). Excluding a main role also excludes its subroles. | | **--exclude-disabled-clips** | Omit disabled clips (`enabled="0"`) from all timeline-based report sections (with `--report`). | | **--exclude-column <column>** | Exclude a workbook column from every applicable report sheet (repeatable; with `--report`). | diff --git a/Documentation/Manual/18-Cross-Platform-iOS.md b/Documentation/Manual/18-Cross-Platform-iOS.md index 5de54ba..b499bfa 100644 --- a/Documentation/Manual/18-Cross-Platform-iOS.md +++ b/Documentation/Manual/18-Cross-Platform-iOS.md @@ -51,7 +51,7 @@ let version = root?.stringValue(forAttributeNamed: "version") ## Testing -- The `OpenFCPXMLKitTests` suite (**924 tests** listed under that target: 921 XCTest + 3 Swift Testing `@Test`) runs on **macOS** and uses the Foundation backend. `swift test --list-tests` also lists **1** optional `ExcelReportTest` integration test (**925** total). +- The `OpenFCPXMLKitTests` suite (**932 tests** listed under that target: 929 XCTest + 3 Swift Testing `@Test`) runs on **macOS** and uses the Foundation backend. `swift test --list-tests` also lists **1** optional `ExcelReportTest` integration test (**933** total). - **iOS** is supported for building the library (e.g. iOS Simulator); running the same tests on iOS is not required for CI because they depend on Foundation XML. AEXML parity and structural validation are covered by tests that run on macOS. --- diff --git a/Documentation/Manual/19-Reporting.md b/Documentation/Manual/19-Reporting.md index 15211fc..4e546cc 100644 --- a/Documentation/Manual/19-Reporting.md +++ b/Documentation/Manual/19-Reporting.md @@ -38,7 +38,7 @@ let outputURL = URL(fileURLWithPath: "/path/to/Report.xlsx") try await FinalCutPro.FCPXML.ReportExcelExport.export(report, to: outputURL) ``` -`buildReport` throws **ReportError** (`noProjectsFound`, `projectNotFound(name)`) when the requested project cannot be resolved. +`buildReport` throws **ReportError** (`noProjectsFound`, `projectNotFound(name)`) when no reportable timeline can be resolved. Resolution uses ``FinalCutPro/FCPXML/allReportTimelineSources()``: normal `` sequences first, then event-level compound clips (`ref-clip` → `media`/`sequence`) when the document has no project (FCP “Export XML” of a compound clip). --- @@ -65,7 +65,7 @@ try await FinalCutPro.FCPXML.ReportExcelExport.export(report, to: outputURL) | Property | Default | Purpose | |----------|---------|---------| -| `projectName` | `nil` | Pick a project by name when the document has more than one (otherwise the first project is used). | +| `projectName` | `nil` | Pick a timeline by name when the document has more than one. Matches a `` name or a standalone compound-clip / `ref-clip` name. When `nil`, the first project is preferred; if there is no project, the first event-level compound clip is used. | | `mediaBaseURL` | `nil` | Base URL for resolving relative media paths when building the Media Summary missing-media list. The CLI defaults this to the document/bundle location. | | `roleDisplayPreference` | `.builtIn` | Which inherited role to surface on compound clips (see [Role display preference](#role-display-preference)). | | `workbookCoverSheet` | `.openFCPXMLKitDefault` | Optional cover sheet; set to `nil` to omit. | @@ -445,7 +445,7 @@ The same reports are available through **OpenFCPXMLKit-CLI**: | `--report-markers`, `--report-keywords`, … | Individual optional sheets | | `--report-summary` | Summary sheet | | `--report-media-summary` | Media Summary sheet | -| `--report-project ` | Project name filter | +| `--report-project ` | Timeline name filter (project or standalone compound-clip name) | | `--exclude-role ` | Omit roles from role inventory (repeatable) | | `--exclude-disabled-clips` | Omit `enabled="0"` clips from all timeline sections | | `--exclude-column ` | Omit a column from every applicable sheet (repeatable) | diff --git a/README.md b/README.md index bf8468b..66aac29 100755 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ A modern Swift 6 framework for working with Final Cut Pro's FCPXML with full concurrency support, SwiftTimecode integration, and [XLKit](https://github.com/TheAcharya/XLKit) integration. -OpenFCPXMLKit provides a comprehensive API for parsing, creating, and manipulating FCPXML files with advanced timecode operations, async/await patterns, and robust error handling. Built with Swift 6.3 and targeting **macOS 26+** and **iOS 26+**, it offers type-safe operations, comprehensive test coverage (**925** tests listed in `swift test --list-tests`: **924** in `OpenFCPXMLKitTests`, plus **1** optional `ExcelReportTest` integration), and seamless integration with SwiftTimecode and XLKit for professional video editing workflows. A cross-platform XML abstraction layer (Foundation on macOS, AEXML on iOS) keeps the library usable on both platforms. +OpenFCPXMLKit provides a comprehensive API for parsing, creating, and manipulating FCPXML files with advanced timecode operations, async/await patterns, and robust error handling. Built with Swift 6.3 and targeting **macOS 26+** and **iOS 26+**, it offers type-safe operations, comprehensive test coverage (**933** tests listed in `swift test --list-tests`: **932** in `OpenFCPXMLKitTests`, plus **1** optional `ExcelReportTest` integration), and seamless integration with SwiftTimecode and XLKit for professional video editing workflows. A cross-platform XML abstraction layer (Foundation on macOS, AEXML on iOS) keeps the library usable on both platforms. OpenFCPXMLKit is currently in an experimental stage. It covers most core FCPXML attributes and parameters and provides a solid foundation for parsing, creation, and manipulation, with room for future expansion and additional feature coverage. @@ -45,7 +45,7 @@ This codebase is developed using AI agents. ## Core Features - **FCPXML I/O**: Read, create, modify documents (.fcpxml/.fcpxmld bundles); load via `FCPXMLFileLoader` (sync/async); create FCPXML from scratch with events, projects, resources, and clips. -- **Parsing & Validation**: Parse and validate against bundled DTDs (1.5–1.14); structural/reference and DTD schema validation (full DTD on macOS; cross-platform structural validation on iOS via `FCPXMLStructuralValidator`); **925** tests (**924** in `OpenFCPXMLKitTests` + **1** optional Excel report integration) across 58 FCPXML sample files. +- **Parsing & Validation**: Parse and validate against bundled DTDs (1.5–1.14); structural/reference and DTD schema validation (full DTD on macOS; cross-platform structural validation on iOS via `FCPXMLStructuralValidator`); **933** tests (**932** in `OpenFCPXMLKitTests` + **1** optional Excel report integration) across 58 FCPXML sample files. - **Timecode Operations**: SwiftTimecode integration (`CMTime`, `Timecode`, FCPXML time strings); `FCPXMLTimecode` custom type (arithmetic, frame alignment, conversion); all FCP frame rates (23.976, 24, 25, 29.97, 30, 50, 59.94, 60 fps). - **Typed Models**: Resources, events, clips, projects, adjustments (Crop, Transform, Blend, Stabilization, Volume, Loudness, NoiseReduction, HumReduction, Equalization, MatchEqualization, Transform360, ColorConform, Stereo3D, VoiceIsolation), filters (VideoFilter, AudioFilter, VideoFilterMask with FilterParameter), transitions, multicam (Media.Multicam, Angle, MulticamSource, MCClip), captions/titles (Caption, Title with TextStyle/TextStyleDefinition), smart collections (SmartCollection with match-clip, match-media, match-ratings, match-text, match-usage, match-representation, match-markers, match-analysis-type), collections (CollectionFolder, KeywordCollection). - **Timeline Operations**: Build `Timeline`; create valid projects with custom or preset dimensions and frame rate (via `TimelineFormat`); export to FCPXML/.fcpxmld (including zero-clip/empty timelines); optional event/project UIDs and library location (`FCPXMLUID`); ripple insert, auto lane assignment, clip queries (lane/time range/asset ID), lane range computation; metadata (markers, chapter markers, keywords, ratings, custom metadata, timestamps); secondary storylines; `TimelineFormat` presets and computed properties. diff --git a/Sources/OpenFCPXMLKit/Classes/FCPXMLProperties.swift b/Sources/OpenFCPXMLKit/Classes/FCPXMLProperties.swift index 803aee0..48aeeb9 100644 --- a/Sources/OpenFCPXMLKit/Classes/FCPXMLProperties.swift +++ b/Sources/OpenFCPXMLKit/Classes/FCPXMLProperties.swift @@ -116,6 +116,91 @@ extension FinalCutPro.FCPXML { public func allTimelines() -> [FinalCutPro.FCPXML.AnyTimeline] { root.element._fcpMetaTimelinesAsAnyTimelines() } + + /// Reportable timeline sources in document order. + /// + /// Includes: + /// - every ``Project`` (via its required `sequence`) + /// - every event-level ``RefClip`` whose `media` resource contains a `sequence` + /// (standalone compound-clip exports that have no ``) + /// + /// Prefer ``allProjects()`` when only project timelines are needed. Use this when + /// consumers (for example Excel reporting) must also support compound-clip-only FCPXML. + public func allReportTimelineSources() -> [ReportTimelineSource] { + var sources: [ReportTimelineSource] = [] + + for project in allProjects() { + let eventName = project.element + .ancestorElements(includingSelf: false) + .first(whereFCPElementType: .event)? + .fcpName + sources.append( + ReportTimelineSource( + displayName: project.name ?? "", + eventName: eventName, + sequence: project.sequence, + project: project + ) + ) + } + + for event in allEvents() { + for element in event.storyElements { + guard let refClip = element.fcpAsRefClip, + let sequence = refClip.mediaSequence + else { continue } + + let displayName = refClip.name + ?? refClip.mediaResource?.name + ?? "" + sources.append( + ReportTimelineSource( + displayName: displayName, + eventName: event.name.isEmpty ? nil : event.name, + sequence: sequence, + project: nil + ) + ) + } + } + + return sources + } +} + +extension FinalCutPro.FCPXML { + /// A timeline that Excel reporting (and similar consumers) can build from. + /// + /// Either a normal ``Project`` sequence, or a compound-clip `media`/`sequence` + /// referenced by an event-level ``RefClip`` when the document has no ``. + /// + /// Not `Sendable`: wraps ``Sequence`` / ``Project`` which hold non-Sendable OFKXML elements. + public struct ReportTimelineSource { + /// Display name used for workbook naming and Summary title + /// (`project` name, or compound clip / media name). + public let displayName: String + + /// Parent event name when the timeline lives under an event. + public let eventName: String? + + /// The sequence whose spine is walked for report sections. + public let sequence: Sequence + + /// The owning project when this source came from a ``; otherwise `nil`. + public let project: Project? + + public init( + displayName: String, + eventName: String?, + sequence: Sequence, + project: Project? + ) { + self.displayName = displayName + self.eventName = eventName + self.sequence = sequence + self.project = project + } + } } extension FinalCutPro.FCPXML.Library { diff --git a/Sources/OpenFCPXMLKit/Reporting/Builders/FCPXMLSummaryReportBuilder.swift b/Sources/OpenFCPXMLKit/Reporting/Builders/FCPXMLSummaryReportBuilder.swift index c9e11a9..c1061d1 100644 --- a/Sources/OpenFCPXMLKit/Reporting/Builders/FCPXMLSummaryReportBuilder.swift +++ b/Sources/OpenFCPXMLKit/Reporting/Builders/FCPXMLSummaryReportBuilder.swift @@ -4,6 +4,7 @@ // © 2026 • Licensed under MIT License // + // // Builds the Summary report section from FCPXML. // @@ -20,12 +21,38 @@ extension FinalCutPro.FCPXML { scope: ExtractionScope, roleDisplayPreference: RoleDisplayPreference = .builtIn, timecodeFormat: ReportTimecodeFormat = .smpteFrames + ) async -> SummaryReportSection { + let eventName = project.element + .ancestorElements(includingSelf: false) + .first(whereFCPElementType: .event)? + .fcpName + let source = ReportTimelineSource( + displayName: project.name ?? "", + eventName: eventName, + sequence: project.sequence, + project: project + ) + return await build( + from: source, + document: document, + scope: scope, + roleDisplayPreference: roleDisplayPreference, + timecodeFormat: timecodeFormat + ) + } + + static func build( + from source: ReportTimelineSource, + document: any OFKXMLDocument, + scope: ExtractionScope, + roleDisplayPreference: RoleDisplayPreference = .builtIn, + timecodeFormat: ReportTimecodeFormat = .smpteFrames ) async -> SummaryReportSection { let resources = resourcesElement(in: document) - let sequence = project.sequence + let sequence = source.sequence let timelineElement = sequence.element - let projectSummary = projectSummary( - for: project, + let projectSummary = timelineSummary( + title: source.displayName, sequence: sequence, resources: resources, timecodeFormat: timecodeFormat @@ -60,8 +87,8 @@ extension FinalCutPro.FCPXML { document.rootElement()?.firstChildElement(named: "resources") } - private static func projectSummary( - for project: Project, + private static func timelineSummary( + title: String, sequence: Sequence, resources: (any OFKXMLElement)?, timecodeFormat: ReportTimecodeFormat @@ -95,7 +122,7 @@ extension FinalCutPro.FCPXML { let audioSampleRate = sequence.audioRate.map(summaryAudioSampleRateDisplay) ?? "" return ProjectSummary( - title: project.name ?? "", + title: title, duration: duration, resolution: resolution, frameRate: frameRate, diff --git a/Sources/OpenFCPXMLKit/Reporting/FCPXMLReportBuilder.swift b/Sources/OpenFCPXMLKit/Reporting/FCPXMLReportBuilder.swift index ae5063f..0945a95 100644 --- a/Sources/OpenFCPXMLKit/Reporting/FCPXMLReportBuilder.swift +++ b/Sources/OpenFCPXMLKit/Reporting/FCPXMLReportBuilder.swift @@ -4,6 +4,7 @@ // © 2026 • Licensed under MIT License // + // // Builds structured reports from FCPXML documents. // @@ -21,6 +22,10 @@ extension FinalCutPro.FCPXML { /// Sections are built in ``ReportBuildPhase/enabledPhases(for:)`` order (product / /// workbook order). ``onPhaseStarted`` is invoked once per enabled phase before that /// section is assembled. + /// + /// Supports both normal project timelines (`library` → `event` → `project` → `sequence`) + /// and standalone compound-clip exports (`event` → `ref-clip` → `media`/`sequence`) via + /// ``allReportTimelineSources()``. public struct ReportBuilder: Sendable { public var options: ReportOptions public var scope: ExtractionScope @@ -38,8 +43,8 @@ extension FinalCutPro.FCPXML { /// Build a report from a parsed FCPXML document. public func build(from fcpxml: FinalCutPro.FCPXML) async throws -> Report { - let project = try resolveProject(in: fcpxml) - return await build(from: project, fcpxml: fcpxml) + let source = try resolveTimelineSource(in: fcpxml) + return await build(from: source, fcpxml: fcpxml) } /// Build a report from a single project. @@ -51,11 +56,25 @@ extension FinalCutPro.FCPXML { .ancestorElements(includingSelf: false) .first(whereFCPElementType: .event)? .fcpName + let source = ReportTimelineSource( + displayName: project.name ?? "", + eventName: eventName, + sequence: project.sequence, + project: project + ) + return await build(from: source, fcpxml: fcpxml) + } + + /// Build a report from a resolved timeline source (project or compound clip). + public func build( + from source: ReportTimelineSource, + fcpxml: FinalCutPro.FCPXML + ) async -> Report { let extractionScope = reportExtractionScope() var report = Report( - projectName: project.name ?? "", - eventName: eventName, + projectName: source.displayName, + eventName: source.eventName, workbookCoverSheet: options.workbookCoverSheet, excludedColumns: ReportColumnExclusion.resolve(options.excludedColumns), timecodeFormat: options.timecodeFormat @@ -63,7 +82,7 @@ extension FinalCutPro.FCPXML { for phase in ReportBuildPhase.enabledPhases(for: options) { onPhaseStarted?(phase) - await build(phase, into: &report, project: project, fcpxml: fcpxml, scope: extractionScope) + await build(phase, into: &report, source: source, fcpxml: fcpxml, scope: extractionScope) } return report @@ -72,11 +91,11 @@ extension FinalCutPro.FCPXML { private func build( _ phase: ReportBuildPhase, into report: inout Report, - project: Project, + source: ReportTimelineSource, fcpxml: FinalCutPro.FCPXML, scope extractionScope: ExtractionScope ) async { - let timelineElement = project.sequence.element + let timelineElement = source.sequence.element switch phase { case .roleInventory: @@ -144,7 +163,7 @@ extension FinalCutPro.FCPXML { case .summary: report.summary = await SummaryReportBuilder.build( - from: project, + from: source, document: fcpxml.xml, scope: extractionScope, roleDisplayPreference: options.roleDisplayPreference, @@ -166,21 +185,27 @@ extension FinalCutPro.FCPXML { return effectiveScope } - private func resolveProject(in fcpxml: FinalCutPro.FCPXML) throws -> Project { - let projects = fcpxml.allProjects() + private func resolveTimelineSource(in fcpxml: FinalCutPro.FCPXML) throws -> ReportTimelineSource { + let sources = fcpxml.allReportTimelineSources() if let name = options.projectName { - guard let project = projects.first(where: { $0.name == name }) else { + guard let source = sources.first(where: { $0.displayName == name }) else { throw ReportError.projectNotFound(name) } - return project + return source + } + + // Prefer a real project when both projects and event-level compound clips exist + // (e.g. CompoundClipSample embeds a compound inside a project timeline). + if let projectSource = sources.first(where: { $0.project != nil }) { + return projectSource } - guard let project = projects.first else { + guard let source = sources.first else { throw ReportError.noProjectsFound } - return project + return source } } @@ -192,9 +217,9 @@ extension FinalCutPro.FCPXML { public var errorDescription: String? { switch self { case .noProjectsFound: - return "No projects were found in the FCPXML document." + return "No projects or compound-clip timelines were found in the FCPXML document." case let .projectNotFound(name): - return "No project named \"\(name)\" was found in the FCPXML document." + return "No project or compound clip named \"\(name)\" was found in the FCPXML document." } } } diff --git a/Sources/OpenFCPXMLKit/Reporting/FCPXMLReportOptions.swift b/Sources/OpenFCPXMLKit/Reporting/FCPXMLReportOptions.swift index c390c86..198095c 100644 --- a/Sources/OpenFCPXMLKit/Reporting/FCPXMLReportOptions.swift +++ b/Sources/OpenFCPXMLKit/Reporting/FCPXMLReportOptions.swift @@ -47,7 +47,9 @@ extension FinalCutPro.FCPXML { /// Chapter markers are omitted from the Markers sheet by default. public var includeChapterMarkersInMarkersReport: Bool - /// Optional project name filter. When `nil`, the first project in the document is used. + /// Optional timeline name filter. When `nil`, the first project is preferred; if the + /// document has no ``, the first event-level compound clip (`ref-clip` → + /// `media`/`sequence`) is used. Matching uses the project name or compound clip name. public var projectName: String? /// Base URL for resolving relative media paths when building the Media Summary missing-media list. diff --git a/Sources/OpenFCPXMLKitCLI/README.md b/Sources/OpenFCPXMLKitCLI/README.md index 65dab84..cd6babd 100644 --- a/Sources/OpenFCPXMLKitCLI/README.md +++ b/Sources/OpenFCPXMLKitCLI/README.md @@ -100,7 +100,7 @@ OpenFCPXMLKit-CLI --quiet --media-copy /path/to/project.fcpxml /path/to/media | Option | Description | |--------|-------------| -| `--report` | Build an Excel report workbook from FCPXML/FCPXMLD. Default: role inventory only (Selected Roles Inventory and per-role sheets). Writes `{project-name}.xlsx` to output-dir; prints the output path to stdout. | +| `--report` | Build an Excel report workbook from FCPXML/FCPXMLD (normal projects or standalone compound-clip exports). Default: role inventory only (Selected Roles Inventory and per-role sheets). Writes `{project-or-clip-name}.xlsx` to output-dir; prints the output path to stdout. | | `--report-full` | Include every optional report sheet (requires `--report`). | | `--report-markers` | Include the Markers sheet (requires `--report`). | | `--report-keywords` | Include the Keywords sheet (requires `--report`). | @@ -110,7 +110,7 @@ OpenFCPXMLKit-CLI --quiet --media-copy /path/to/project.fcpxml /path/to/media | `--report-speed-change-effects` | Include the Speed Change Effects sheet (requires `--report`). | | `--report-summary` | Include the Summary sheet (project metrics and role-duration totals; requires `--report`). | | `--report-media-summary` | Include the Media Summary sheet (missing media file paths; requires `--report`). | -| `--report-project ` | Project name filter when the FCPXML contains multiple projects. | +| `--report-project ` | Timeline name filter: matches a `` name or a standalone compound-clip / `ref-clip` name when the document has more than one reportable timeline. | | `--exclude-role ` | Exclude a role or subrole from role inventory (repeatable). Excluding a main role also excludes its subroles. Case-insensitive. | | `--exclude-disabled-clips` | Omit disabled clips (`enabled="0"`) from all timeline-based report sections (requires `--report`). | | `--exclude-column ` | Exclude a workbook column from every applicable report sheet (repeatable; requires `--report`). Case-insensitive; see [19 — Reporting](../../Documentation/Manual/19-Reporting.md#column-exclusion) for accepted names. | diff --git a/Tests/ExcelReportTest/Output/README.md b/Tests/ExcelReportTest/Output/README.md index 67a3d71..046ed22 100644 --- a/Tests/ExcelReportTest/Output/README.md +++ b/Tests/ExcelReportTest/Output/README.md @@ -43,5 +43,5 @@ Then open `OFK-Default.xlsx` or `OFK-Full.xlsx` in Excel, Numbers, or your diff ## Notes - Output file names are fixed (`OFK-Default.xlsx`, `OFK-Full.xlsx`) so paths stay stable for scripts and future parity tests. -- The CLI names files after the **project name** inside the FCPXML; test output uses these constant names instead. +- The CLI names files after the **project or compound-clip name** inside the FCPXML; test output uses these constant names instead. - Do not commit large generated workbooks unless you intentionally add golden files for regression testing. diff --git a/Tests/ExcelReportTest/README.md b/Tests/ExcelReportTest/README.md index 541462d..cd9ab2a 100644 --- a/Tests/ExcelReportTest/README.md +++ b/Tests/ExcelReportTest/README.md @@ -1,12 +1,12 @@ # Excel report integration tests -Optional integration tests that build real `.xlsx` workbooks from a local FCPXML project. Use this target when you want to compare OpenFCPXMLKit output against reference exports without running the CLI each time. +Optional integration tests that build real `.xlsx` workbooks from a local FCPXML fixture. Use a normal **project** export or a standalone **compound-clip** export (event `ref-clip` with no ``). Use this target when you want to compare OpenFCPXMLKit output against reference exports without running the CLI each time. **Target:** `ExcelReportTest` **Depends on:** `OpenFCPXMLKit`, `XLKit` **Tests:** 1 (`ExcelReportExportTests`) -Unit-level reporting behaviour (column layout, column exclusion, disabled-clip filtering, timecode formats / DF·NDF, format-aware headers, build-phase order, workbook cell formatting and sheet-specific colour rules) lives in **`OpenFCPXMLKitTests`** — see [Tests/README.md](../README.md#reporting--excel-export). +Unit-level reporting behaviour (column layout, column exclusion, disabled-clip filtering, timecode formats / DF·NDF, format-aware headers, build-phase order, workbook cell formatting and sheet-specific colour rules, **standalone compound-clip timeline resolution**) lives in **`OpenFCPXMLKitTests`** — see [Tests/README.md](../README.md#reporting--excel-export) (`FCPXMLCompoundClipReportTests` and related files). --- @@ -27,11 +27,11 @@ Relative media paths for the **Media Summary** sheet are resolved from the bundl 2. **Preferred local names** — `Sample.fcpxmld` then `Sample.fcpxml` in this directory 3. **Auto-discovery** — first valid `.fcpxml` / `.fcpxmld` in this directory (excluding `Output/`, Swift sources, and markdown) -If no fixture is found, tests **skip** (`XCTSkip`) so CI can pass without a local project. +If no fixture is found, tests **skip** (`XCTSkip`) so CI can pass without a local fixture. ### Setup (local) -Place your project here (not committed — see `.gitignore`): +Place your project or compound-clip export here (not committed — see `.gitignore`): ``` Tests/ExcelReportTest/ @@ -109,7 +109,7 @@ swift test --filter ExcelReportExportTests swift test --filter ExcelReportTest ``` -First run on a large project can take ~1–2 minutes (report build + XLKit save). +First run on a large fixture can take ~1–2 minutes (report build + XLKit save). --- @@ -147,4 +147,4 @@ Good candidates for this target: - Filtered exports (`excludeDisabledClips`, `excludedColumns`, `excludedRoles`) written to additional `Output/` files - Sheet/column-count or cell-format smoke checks on a known fixture -Prefer **`OpenFCPXMLKitTests`** for logic that does not need a full local project (column resolution, layout, `ReportTimecodeFormat`, `ReportBuildPhase` order, synthetic workbook structure, Summary/Media Summary/Keywords/Effects colour rules). +Prefer **`OpenFCPXMLKitTests`** for logic that does not need a full local fixture (column resolution, layout, `ReportTimecodeFormat`, `ReportBuildPhase` order, synthetic workbook structure, Summary/Media Summary/Keywords/Effects colour rules, compound-clip-only timeline discovery). diff --git a/Tests/OpenFCPXMLKitTests/FCPXMLCompoundClipReportTests.swift b/Tests/OpenFCPXMLKitTests/FCPXMLCompoundClipReportTests.swift new file mode 100644 index 0000000..4668ac1 --- /dev/null +++ b/Tests/OpenFCPXMLKitTests/FCPXMLCompoundClipReportTests.swift @@ -0,0 +1,226 @@ +// +// FCPXMLCompoundClipReportTests.swift +// OpenFCPXMLKit • https://github.com/TheAcharya/OpenFCPXMLKit +// © 2026 • Licensed under MIT License +// + + +// +// Excel reporting for standalone compound-clip FCPXML (event ref-clip, no project). +// + +import XCTest +@testable import OpenFCPXMLKit + +@available(macOS 26.0, *) +final class FCPXMLCompoundClipReportTests: XCTestCase { + + private static let compoundClipName = "Standalone Compound Clip" + private static let eventName = "Sample Event" + private static let projectName = "Normal Project" + + // MARK: - Discovery + + func testAllReportTimelineSourcesIncludesEventLevelCompoundClip() throws { + let fcpxml = try parseInlineFCPXML(standaloneCompoundClipFixture) + + XCTAssertTrue(fcpxml.allProjects().isEmpty) + + let sources = fcpxml.allReportTimelineSources() + XCTAssertEqual(sources.count, 1) + XCTAssertEqual(sources[0].displayName, Self.compoundClipName) + XCTAssertEqual(sources[0].eventName, Self.eventName) + XCTAssertNil(sources[0].project) + XCTAssertNotNil(sources[0].sequence.spine) + } + + func testAllReportTimelineSourcesUsesProjectWhenCompoundIsOnlyOnSpine() throws { + // Compound clip lives inside the project spine, not as an event-level browser clip. + // Discovery must report the project timeline only (not double-count the media sequence). + let fcpxml = try parseInlineFCPXML(projectWithEmbeddedCompoundFixture) + + let sources = fcpxml.allReportTimelineSources() + XCTAssertEqual(sources.count, 1) + XCTAssertNotNil(sources[0].project) + XCTAssertEqual(sources[0].displayName, "Project Timeline") + } + + // MARK: - Report build + + func testBuildRoleInventoryFromStandaloneCompoundClip() async throws { + let fcpxml = try parseInlineFCPXML(standaloneCompoundClipFixture) + var options = FinalCutPro.FCPXML.ReportOptions.roleInventoryOnly + options.workbookCoverSheet = nil + + let report = try await fcpxml.buildReport(options: options) + + XCTAssertEqual(report.projectName, Self.compoundClipName) + XCTAssertEqual(report.eventName, Self.eventName) + + let rows = report.roleInventory?.selectedRoles ?? [] + XCTAssertFalse(rows.isEmpty, "Expected role inventory rows from compound clip sequence") + + let clipNames = Set(rows.map(\.clipName)) + XCTAssertTrue(clipNames.contains("Video Clip A")) + + let roles = Set(rows.map(\.roleSubrole)) + XCTAssertTrue( + roles.contains(where: { $0.localizedCaseInsensitiveContains("dialogue") }), + "Expected dialogue role from compound clip timeline; got \(roles)" + ) + } + + func testBuildMarkersFromStandaloneCompoundClip() async throws { + let fcpxml = try parseInlineFCPXML(standaloneCompoundClipFixture) + var options = FinalCutPro.FCPXML.ReportOptions.markersOnly + options.workbookCoverSheet = nil + + let report = try await fcpxml.buildReport(options: options) + let rows = report.markers?.rows ?? [] + + XCTAssertFalse(rows.isEmpty) + XCTAssertTrue(rows.contains { $0.markerName == "Marker One" }) + } + + func testBuildSummaryFromStandaloneCompoundClip() async throws { + let fcpxml = try parseInlineFCPXML(standaloneCompoundClipFixture) + var options = FinalCutPro.FCPXML.ReportOptions.summaryOnly + options.workbookCoverSheet = nil + + let report = try await fcpxml.buildReport(options: options) + + XCTAssertEqual(report.summary?.projectSummary?.title, Self.compoundClipName) + FCPXMLReportingReportTestSupport.assertValidTimecode( + report.summary?.projectSummary?.duration ?? "" + ) + XCTAssertFalse(report.summary?.roleDurations.isEmpty ?? true) + } + + func testProjectNameFilterMatchesCompoundClipName() async throws { + let fcpxml = try parseInlineFCPXML(standaloneCompoundClipFixture) + var options = FinalCutPro.FCPXML.ReportOptions.roleInventoryOnly + options.workbookCoverSheet = nil + options.projectName = Self.compoundClipName + + let report = try await fcpxml.buildReport(options: options) + XCTAssertEqual(report.projectName, Self.compoundClipName) + XCTAssertFalse(report.roleInventory?.selectedRoles.isEmpty ?? true) + } + + func testProjectNameFilterThrowsWhenCompoundClipNameMissing() async throws { + let fcpxml = try parseInlineFCPXML(standaloneCompoundClipFixture) + var options = FinalCutPro.FCPXML.ReportOptions.roleInventoryOnly + options.workbookCoverSheet = nil + options.projectName = "Does Not Exist" + + do { + _ = try await fcpxml.buildReport(options: options) + XCTFail("Expected projectNotFound") + } catch FinalCutPro.FCPXML.ReportError.projectNotFound(let name) { + XCTAssertEqual(name, "Does Not Exist") + } + } + + func testNormalProjectReportStillWorks() async throws { + let fcpxml = try parseInlineFCPXML(normalProjectFixture) + var options = FinalCutPro.FCPXML.ReportOptions.roleInventoryOnly + options.workbookCoverSheet = nil + + let report = try await fcpxml.buildReport(options: options) + XCTAssertEqual(report.projectName, Self.projectName) + XCTAssertFalse(report.roleInventory?.selectedRoles.isEmpty ?? true) + } + + // MARK: - Fixtures + + /// Mirrors FCP “Export XML” of a compound clip: no ``, event holds `ref-clip`, + /// timeline lives under `resources` → `media` → `sequence`. + private var standaloneCompoundClipFixture: String { + """ + + + + + + + + + + + + + + + + + + + + + + + + + + + + """ + } + + private var normalProjectFixture: String { + """ + + + + + + + + + + + + + + + + + + + + + + """ + } + + private var projectWithEmbeddedCompoundFixture: String { + """ + + + + + + + + + + + + + + + + + + + + + + + + + + + """ + } +} diff --git a/Tests/README.md b/Tests/README.md index f29cb1c..7a78be3 100644 --- a/Tests/README.md +++ b/Tests/README.md @@ -2,8 +2,8 @@ This directory contains the test suite for OpenFCPXMLKit, a Swift 6 framework for Final Cut Pro FCPXML processing with SwiftTimecode integration. The suite runs on **macOS** (Foundation XML backend). The library also supports **iOS 26+** (AEXML backend); CI builds for iOS Simulator; the same tests are not run on iOS because they rely on Foundation XML. -- **Test count:** **925** tests listed in `swift test --list-tests` — **924** in `OpenFCPXMLKitTests` (921 XCTest `func test` methods + 3 Swift Testing `@Test` in `FCPXMLReportRoleExclusionTests`) and **1** in `ExcelReportTest` (optional integration; skips without a local fixture) -- **Scope:** Parsing, timecode, document operations, file loading, timeline export, validation (semantic, DTD, structural), timeline manipulation, media processing, typed models (adjustments, filters, captions/titles, keyframe animation), CMTime Codable, collections, Live Drawing (1.11+), HiddenClipMarker (1.13+), Format/Asset 1.13+ (heroEye, heroEyeOverride, mediaReps), SmartCollection match rules, 360 video (projection, stereoscopic), auditions, conform-rate, still images, multicam, secondary storylines, audio keyframes, keyword collections/folders, empty timeline creation at different sizes and frame rates, project-creation export at different sizes and frame rates (with DTD validation), FCPXMLExporter clip-level metadata export (markers, chapter-markers, keywords, ratings, metadata as asset-clip children; DTD and xmllint-compatible XML declaration), cross-platform XML (AEXML serialization parity, DTD validator behaviour, structural validator), Excel reporting (role inventory columns, Summary and Media Summary sheets, configurable `ReportTimecodeFormat` / DF·NDF notation, format-aware headers, Frames/Feet+Frames sort order, inventory-first `ReportBuildPhase` progress, global column exclusion, disabled-clip filtering, workbook export and cell formatting), and all supported FCPXML versions and frame rates +- **Test count:** **933** tests listed in `swift test --list-tests` — **932** in `OpenFCPXMLKitTests` (929 XCTest `func test` methods + 3 Swift Testing `@Test` in `FCPXMLReportRoleExclusionTests`) and **1** in `ExcelReportTest` (optional integration; skips without a local fixture) +- **Scope:** Parsing, timecode, document operations, file loading, timeline export, validation (semantic, DTD, structural), timeline manipulation, media processing, typed models (adjustments, filters, captions/titles, keyframe animation), CMTime Codable, collections, Live Drawing (1.11+), HiddenClipMarker (1.13+), Format/Asset 1.13+ (heroEye, heroEyeOverride, mediaReps), SmartCollection match rules, 360 video (projection, stereoscopic), auditions, conform-rate, still images, multicam, secondary storylines, audio keyframes, keyword collections/folders, empty timeline creation at different sizes and frame rates, project-creation export at different sizes and frame rates (with DTD validation), FCPXMLExporter clip-level metadata export (markers, chapter-markers, keywords, ratings, metadata as asset-clip children; DTD and xmllint-compatible XML declaration), cross-platform XML (AEXML serialization parity, DTD validator behaviour, structural validator), Excel reporting (role inventory columns, Summary and Media Summary sheets, configurable `ReportTimecodeFormat` / DF·NDF notation, format-aware headers, Frames/Feet+Frames sort order, inventory-first `ReportBuildPhase` progress, global column exclusion, disabled-clip filtering, workbook export and cell formatting, standalone compound-clip timelines via `allReportTimelineSources()` / `FCPXMLCompoundClipReportTests`), and all supported FCPXML versions and frame rates - **Layout:** Shared utilities for sample paths; file tests per sample; logic/parsing tests for model types and structure; validation and cross-platform XML tests; optional Excel report integration tests under `ExcelReportTest/` --- @@ -124,6 +124,7 @@ Tests/ ├── FCPXMLRoleDisplayPreferenceTests.swift ├── FCPXMLRoleInventoryClipCollectorTests.swift ├── FCPXMLRoleInventoryColumnLayoutTests.swift + ├── FCPXMLCompoundClipReportTests.swift ├── FCPXMLRoleInventoryReportTests.swift ├── FCPXMLRoleInventoryRoleSheetOrderingTests.swift ├── FCPXMLRolesExtractionPresetTests.swift @@ -171,8 +172,8 @@ swift test --filter OpenFCPXMLKitTests # By pattern To verify the documented test counts: ```bash -swift test --list-tests 2>/dev/null | grep -c '\.' # 925 -swift test --list-tests 2>/dev/null | grep -c 'OpenFCPXMLKitTests\.' # 924 +swift test --list-tests 2>/dev/null | grep -c '\.' # 933 +swift test --list-tests 2>/dev/null | grep -c 'OpenFCPXMLKitTests\.' # 932 swift test --list-tests 2>/dev/null | grep -c 'ExcelReportTest\.' # 1 ``` @@ -265,6 +266,7 @@ Tests are discovered automatically by Swift PM. Run `swift test` in an environme **Reporting & Excel export** (see [19 — Reporting & Excel Export](../Documentation/Manual/19-Reporting.md)) +- **FCPXMLCompoundClipReportTests** — Standalone compound-clip FCPXML (event `ref-clip` → `media`/`sequence`, no ``): `allReportTimelineSources()`, role inventory / markers / summary via `buildReport`, project-name filter, and regression that normal project reports still resolve. - **FCPXMLRoleInventoryReportTests** — Role inventory section: Selected Roles Inventory rows and per-role sheets, categories, columns. - **FCPXMLRoleInventoryColumnLayoutTests** — Inventory column order (Row + 23 fixed columns), dynamic metadata key discovery, row index values, audio rate display. - **FCPXMLMarkersReportTests** — Markers report rows (type, position, clip name, role ▸ subrole), chapter-marker inclusion. @@ -416,7 +418,7 @@ Document manager tests create documents for **FCPXML 1.5 through 1.14** and asse ## 12. Excel report integration tests -The **`ExcelReportTest`** target (separate from `OpenFCPXMLKitTests`) builds real `.xlsx` workbooks from a **local** FCPXML fixture. It is optional: without a fixture, tests skip and CI stays green. +The **`ExcelReportTest`** target (separate from `OpenFCPXMLKitTests`) builds real `.xlsx` workbooks from a **local** FCPXML fixture (a normal project **or** a standalone compound-clip export). It is optional: without a fixture, tests skip and CI stays green. | Item | Detail | |------|--------| @@ -427,7 +429,7 @@ The **`ExcelReportTest`** target (separate from `OpenFCPXMLKitTests`) builds rea Full setup, output description, and CI notes: **[ExcelReportTest/README.md](ExcelReportTest/README.md)**. -Use this target for end-to-end workbook generation on a real project (open `Output/OFK-Full.xlsx` to visually verify sheet layout and cell colours on your fixture). Use **`OpenFCPXMLKitTests`** reporting files (listed under **Reporting & Excel export** in [§3.2](#32-dedicated-test-files-by-theme)) for unit and integration tests against bundled FCPXML samples and synthetic workbook structure. +Use this target for end-to-end workbook generation on a real fixture (open `Output/OFK-Full.xlsx` to visually verify sheet layout and cell colours). Standalone compound-clip reporting (no ``) is covered in unit form by **`FCPXMLCompoundClipReportTests`** in `OpenFCPXMLKitTests`. Use other **`OpenFCPXMLKitTests`** reporting files (listed under **Reporting & Excel export** in [§3.2](#32-dedicated-test-files-by-theme)) for unit and integration tests against bundled FCPXML samples and synthetic workbook structure. ---