From 5fea811d5c6ccb8d9ff80cb55702f51cbe81f590 Mon Sep 17 00:00:00 2001 From: Steffan Andrews Date: Sun, 31 Dec 2023 23:07:20 -0800 Subject: [PATCH] Added CSV export profile (#74) --- .../Export/ExportProfileFormat.swift | 5 + .../Profile/CSV/CSVProfile Export.swift | 108 ++++++++++++++++++ .../Export/Profile/CSV/CSVProfile.swift | 26 +++++ .../MarkersExtractor Export.swift | 13 +++ 4 files changed, 152 insertions(+) create mode 100644 Sources/MarkersExtractor/Export/Profile/CSV/CSVProfile Export.swift create mode 100644 Sources/MarkersExtractor/Export/Profile/CSV/CSVProfile.swift diff --git a/Sources/MarkersExtractor/Export/ExportProfileFormat.swift b/Sources/MarkersExtractor/Export/ExportProfileFormat.swift index 9503e48..a09fbfe 100644 --- a/Sources/MarkersExtractor/Export/ExportProfileFormat.swift +++ b/Sources/MarkersExtractor/Export/ExportProfileFormat.swift @@ -8,6 +8,7 @@ import Foundation public enum ExportProfileFormat: String, CaseIterable, Equatable, Hashable { case airtable + case csv case midi case notion } @@ -17,6 +18,8 @@ extension ExportProfileFormat { switch self { case .airtable: return "Airtable" + case .csv: + return "CSV" case .midi: return "MIDI File" case .notion: @@ -28,6 +31,8 @@ extension ExportProfileFormat { switch self { case .airtable: return AirtableExportProfile.self + case .csv: + return CSVProfile.self case .midi: return MIDIFileExportProfile.self case .notion: diff --git a/Sources/MarkersExtractor/Export/Profile/CSV/CSVProfile Export.swift b/Sources/MarkersExtractor/Export/Profile/CSV/CSVProfile Export.swift new file mode 100644 index 0000000..065392c --- /dev/null +++ b/Sources/MarkersExtractor/Export/Profile/CSV/CSVProfile Export.swift @@ -0,0 +1,108 @@ +// +// CSVProfile Export.swift +// MarkersExtractor • https://github.com/TheAcharya/MarkersExtractor +// Licensed under MIT License +// + +import AVFoundation +import CodableCSV +import Foundation +import Logging +import OrderedCollections +import TimecodeKit + +extension CSVProfile { + public func prepareMarkers( + markers: [Marker], + idMode: MarkerIDMode, + tcStringFormat: Timecode.StringFormat, + payload: Payload, + mediaInfo: ExportMarkerMediaInfo? + ) -> [PreparedMarker] { + markers.map { + PreparedMarker( + $0, + idMode: idMode, + mediaInfo: mediaInfo, tcStringFormat: tcStringFormat + ) + } + } + + public func writeManifests( + _ preparedMarkers: [PreparedMarker], + payload: Payload, + noMedia: Bool + ) throws { + try csvWriteManifest( + csvPath: payload.csvPath, + noMedia: noMedia, + preparedMarkers + ) + } + + public func resultFileContent(payload: Payload) throws -> ExportResult.ResultDictionary { + [ + .csvManifestPath: .url(payload.csvPath) + ] + } + + public func tableManifestFields( + for marker: PreparedMarker, + noMedia: Bool + ) -> OrderedDictionary { + var dict: OrderedDictionary = [:] + + dict[.id] = marker.id + dict[.name] = marker.name + dict[.type] = marker.type + dict[.checked] = marker.checked + dict[.status] = marker.status + dict[.notes] = marker.notes + dict[.position] = marker.position + dict[.clipType] = marker.clipType + dict[.clipName] = marker.clipName + dict[.clipDuration] = marker.clipDuration + dict[.videoRole] = marker.videoRole + dict[.audioRole] = marker.audioRole.flat + dict[.eventName] = marker.eventName + dict[.projectName] = marker.projectName + dict[.libraryName] = marker.libraryName + // no iconImage + + if !noMedia { + dict[.imageFileName] = marker.imageFileName + } + + return dict + } + + public func nestedManifestFields( + for marker: PreparedMarker, + noMedia: Bool + ) -> OrderedDictionary { + var dict: OrderedDictionary = [:] + + dict[.id] = .string(marker.id) + dict[.name] = .string(marker.name) + dict[.type] = .string(marker.type) + dict[.checked] = .string(marker.checked) + dict[.status] = .string(marker.status) + dict[.notes] = .string(marker.notes) + dict[.position] = .string(marker.position) + dict[.clipType] = .string(marker.clipType) + dict[.clipName] = .string(marker.clipName) + dict[.clipDuration] = .string(marker.clipDuration) + dict[.videoRole] = .string(marker.videoRole) + dict[.audioRole] = .array(marker.audioRole.array) + dict[.eventName] = .string(marker.eventName) + dict[.projectName] = .string(marker.projectName) + dict[.libraryName] = .string(marker.libraryName) + // no iconImage + + if !noMedia { + dict[.imageFileName] = .string(marker.imageFileName) + } + + return dict + } +} diff --git a/Sources/MarkersExtractor/Export/Profile/CSV/CSVProfile.swift b/Sources/MarkersExtractor/Export/Profile/CSV/CSVProfile.swift new file mode 100644 index 0000000..dd13804 --- /dev/null +++ b/Sources/MarkersExtractor/Export/Profile/CSV/CSVProfile.swift @@ -0,0 +1,26 @@ +// +// CSVProfile.swift +// MarkersExtractor • https://github.com/TheAcharya/MarkersExtractor +// Licensed under MIT License +// + +import Foundation +import Logging + +public class CSVProfile: NSObject, ProgressReporting, ExportProfile { + // ExportProfile + public typealias Payload = CSVExportPayload + public typealias Icon = EmptyExportIcon + public typealias PreparedMarker = StandardExportMarker + public static let profile: ExportProfileFormat = .csv + public static let isMediaCapable: Bool = false + public var logger: Logger? + + // ProgressReporting + public let progress: Progress + + public required init(logger: Logger? = nil) { + self.logger = logger + progress = Self.defaultProgress + } +} diff --git a/Sources/MarkersExtractor/MarkersExtractor Export.swift b/Sources/MarkersExtractor/MarkersExtractor Export.swift index aeaaef3..f76139d 100644 --- a/Sources/MarkersExtractor/MarkersExtractor Export.swift +++ b/Sources/MarkersExtractor/MarkersExtractor Export.swift @@ -26,6 +26,19 @@ extension MarkersExtractor { payload: .init(projectName: projectName, outputURL: outputURL), parentProgress: parentProgress ) + case .csv: + return try await export( + for: CSVProfile.self, + media: media, + markers: markers, + outputURL: outputURL, + payload: .init( + projectName: projectName, + outputURL: outputURL + ), + parentProgress: parentProgress + ) + case .midi: return try await export( for: MIDIFileExportProfile.self,