diff --git a/Sources/MarkersExtractor/Export/Support/StandardExportMarker.swift b/Sources/MarkersExtractor/Export/Support/StandardExportMarker.swift index 4f54291..373950f 100644 --- a/Sources/MarkersExtractor/Export/Support/StandardExportMarker.swift +++ b/Sources/MarkersExtractor/Export/Support/StandardExportMarker.swift @@ -20,9 +20,14 @@ public struct StandardExportMarker: ExportMarker { public let checked: String public let status: String public let notes: String + public let reel: String + public let scene: String + public let take: String public let position: String public let clipType: String public let clipName: String + public let clipIn: String + public let clipOut: String public let clipDuration: String public let clipKeywords: (flat: String, array: [String]) public let audioRole: (flat: String, array: [String]) @@ -49,9 +54,14 @@ public struct StandardExportMarker: ExportMarker { checked = String(marker.isChecked()) status = NotionExportProfile.Status(marker.type).rawValue notes = marker.notes + reel = marker.metadata.reel + scene = marker.metadata.scene + take = marker.metadata.take position = marker.positionTimeString(format: timeFormat, offsetToProjectStart: offsetToProjectStart) clipType = marker.parentInfo.clipType clipName = marker.parentInfo.clipName + clipIn = marker.parentInfo.clipInTimeString(format: timeFormat) + clipOut = marker.parentInfo.clipOutTimeString(format: timeFormat) clipDuration = marker.parentInfo.clipDurationTimeString(format: timeFormat) clipKeywords = marker.parentInfo.clipKeywordsFormatted() videoRole = marker.roles.videoFormatted() diff --git a/Sources/MarkersExtractor/Extractors/FCPXMLMarkerExtractor.swift b/Sources/MarkersExtractor/Extractors/FCPXMLMarkerExtractor.swift index f8900c2..475da03 100644 --- a/Sources/MarkersExtractor/Extractors/FCPXMLMarkerExtractor.swift +++ b/Sources/MarkersExtractor/Extractors/FCPXMLMarkerExtractor.swift @@ -196,13 +196,16 @@ class FCPXMLMarkerExtractor: NSObject, ProgressReporting { return nil } + let markerMetadata = metadata(for: extractedMarker) + return Marker( type: .marker(extractedMarker.configuration), name: extractedMarker.name, notes: extractedMarker.note ?? "", roles: roles, position: position, - parentInfo: parentInfo + parentInfo: parentInfo, + metadata: markerMetadata ) } @@ -226,13 +229,16 @@ class FCPXMLMarkerExtractor: NSObject, ProgressReporting { return nil } + let markerMetadata = metadata(for: extractedCaption) + return Marker( type: .caption, name: name, notes: extractedCaption.element.fcpNote ?? "", roles: roles, position: position, - parentInfo: parentInfo + parentInfo: parentInfo, + metadata: markerMetadata ) } @@ -259,6 +265,42 @@ class FCPXMLMarkerExtractor: NSObject, ProgressReporting { ) } + private func metadata( + for extractedMarker: FinalCutPro.FCPXML.ExtractedMarker + ) -> Marker.Metadata { + let rawMetadata = extractedMarker.value(forContext: .metadata) + + return convertMetadata(rawMetadata: rawMetadata) + } + + private func metadata( + for extractedCaption: FinalCutPro.FCPXML.ExtractedCaption + ) -> Marker.Metadata { + let rawMetadata = extractedCaption.value(forContext: .metadata) + + return convertMetadata(rawMetadata: rawMetadata) + } + + private func convertMetadata( + rawMetadata: [FinalCutPro.FCPXML.Metadata.Metadatum] + ) -> Marker.Metadata { + // map metadata key/value pairs to a dictionary for easy access + let metadataDict: [FinalCutPro.FCPXML.Metadata.Key: String] = rawMetadata + .compactMapDictionary { element in + guard let key = element.key else { return nil } + let value = element.value ?? element.valueArray?.joined(separator: ",") ?? "" + return (key, value) + } + + let markerMetadata = Marker.Metadata( + reel: metadataDict[.reel] ?? "", + scene: metadataDict[.scene] ?? "", + take: metadataDict[.take] ?? "" + ) + + return markerMetadata + } + func getClipRoles(_ element: any FCPXMLExtractedModelElement) -> MarkerRoles { var markerRoles = MarkerRoles() diff --git a/Sources/MarkersExtractor/Import/Marker/Marker.swift b/Sources/MarkersExtractor/Import/Marker/Marker.swift index 8093f47..dd98b86 100644 --- a/Sources/MarkersExtractor/Import/Marker/Marker.swift +++ b/Sources/MarkersExtractor/Import/Marker/Marker.swift @@ -25,15 +25,29 @@ public struct Marker: Equatable, Hashable, Sendable { var projectStartTime: Timecode var libraryName: String + func clipInTimeString(format: ExportMarkerTimeFormat) -> String { + Self.timeString(from: clipInTime, format: format) + } + + func clipOutTimeString(format: ExportMarkerTimeFormat) -> String { + Self.timeString(from: clipOutTime, format: format) + } + func clipDurationTimeString(format: ExportMarkerTimeFormat) -> String { let dur = clipOutTime - clipInTime - + return Self.timeString(from: dur, format: format) + } + + static func timeString( + from timecode: Timecode, + format: ExportMarkerTimeFormat + ) -> String { switch format { case .timecode(let stringFormat): - return dur.stringValue(format: stringFormat) + return timecode.stringValue(format: stringFormat) case .realTime(let stringFormat): // convert timecode to real time (wall time) - return Time(seconds: dur.realTimeValue).stringValue(format: stringFormat) + return Time(seconds: timecode.realTimeValue).stringValue(format: stringFormat) } } @@ -46,7 +60,6 @@ public struct Marker: Equatable, Hashable, Sendable { } } - // raw metadata-related var type: InterpretedMarkerType var name: String var notes: String @@ -57,6 +70,15 @@ public struct Marker: Equatable, Hashable, Sendable { /// Cached parent information. var parentInfo: ParentInfo + struct Metadata: Equatable, Hashable { + var reel: String + var scene: String + var take: String + } + + /// Cached metadata. + var metadata: Metadata + /// Used only when uniquing marker IDs to avoid duplicate IDs. var idSuffix: String? }