Skip to content

Commit

Permalink
Added TSV export profile (#74)
Browse files Browse the repository at this point in the history
  • Loading branch information
orchetect committed Jan 1, 2024
1 parent e35c00f commit 5543293
Show file tree
Hide file tree
Showing 10 changed files with 242 additions and 23 deletions.
2 changes: 2 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ let package = Package(
.package(url: "https://github.com/apple/swift-log.git", from: "1.0.0"),
.package(url: "https://github.com/apple/swift-collections.git", from: "1.0.0"),
.package(url: "https://github.com/dehesa/CodableCSV.git", from: "0.6.7"),
.package(url: "https://github.com/orchetect/TextFileKit.git", from: "0.1.5"),
.package(url: "https://github.com/orchetect/TimecodeKit.git", from: "2.0.7"),
.package(url: "https://github.com/orchetect/DAWFileKit.git", from: "0.4.0")
],
Expand All @@ -34,6 +35,7 @@ let package = Package(
.product(name: "Logging", package: "swift-log"),
.product(name: "Collections", package: "swift-collections"),
.product(name: "CodableCSV", package: "CodableCSV"),
.product(name: "TextFileKit", package: "TextFileKit"),
.product(name: "TimecodeKit", package: "TimecodeKit"),
.product(name: "DAWFileKit", package: "DAWFileKit")
]
Expand Down
5 changes: 5 additions & 0 deletions Sources/MarkersExtractor/Export/ExportProfileFormat.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ public enum ExportProfileFormat: String, CaseIterable, Equatable, Hashable {
case csv
case midi
case notion
case tsv
}

extension ExportProfileFormat {
Expand All @@ -24,6 +25,8 @@ extension ExportProfileFormat {
return "MIDI File"
case .notion:
return "Notion"
case .tsv:
return "TSV"
}
}

Expand All @@ -37,6 +40,8 @@ extension ExportProfileFormat {
return MIDIFileExportProfile.self
case .notion:
return NotionExportProfile.self
case .tsv:
return TSVProfile.self
}
}
}
24 changes: 1 addition & 23 deletions Sources/MarkersExtractor/Export/Manifest/CSV Export Utils.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,30 +14,8 @@ extension ExportProfile {
noMedia: Bool,
_ preparedMarkers: [PreparedMarker]
) throws {
let rows = csvDictsToRows(preparedMarkers, noMedia: noMedia)
let rows = dictsToRows(preparedMarkers, noMedia: noMedia)
let csvData = try CSVWriter.encode(rows: rows, into: Data.self)
try csvData.write(to: csvPath)
}

// MARK: Helpers

private func csvDictsToRows(
_ preparedMarkers: [PreparedMarker],
noMedia: Bool
) -> [[String]] {
let dicts = preparedMarkers.map {
tableManifestFields(for: $0, noMedia: noMedia)
}
guard !dicts.isEmpty else { return [] }

// header
var result = [Array(dicts[0].keys.map { $0.name })]

// marker rows
result += dicts.map { row in
Array(row.values)
}

return result
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//
// Delimited Text Export Utils.swift
// MarkersExtractor • https://github.com/TheAcharya/MarkersExtractor
// Licensed under MIT License
//

import CodableCSV
import Foundation
import OrderedCollections

extension ExportProfile {
func dictsToRows(
_ preparedMarkers: [PreparedMarker],
noMedia: Bool
) -> [[String]] {
let dicts = preparedMarkers.map {
tableManifestFields(for: $0, noMedia: noMedia)
}
guard !dicts.isEmpty else { return [] }

// header
var result = [Array(dicts[0].keys.map { $0.name })]

// marker rows
result += dicts.map { row in
Array(row.values)
}

return result
}
}
28 changes: 28 additions & 0 deletions Sources/MarkersExtractor/Export/Manifest/TSV Export Utils.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//
// TSV Export Utils.swift
// MarkersExtractor • https://github.com/TheAcharya/MarkersExtractor
// Licensed under MIT License
//

import Foundation
import OrderedCollections
import TextFileKit

extension ExportProfile {
func tsvWriteManifest(
tsvPath: URL,
noMedia: Bool,
_ preparedMarkers: [PreparedMarker]
) throws {
let rows = dictsToRows(preparedMarkers, noMedia: noMedia)

guard let tsvData = TextFile.TSV(table: rows).rawText.data(using: .utf8)
else {
throw MarkersExtractorError.extraction(.fileWrite(
"Could not encode TSV file."
))
}

try tsvData.write(to: tsvPath)
}
}
16 changes: 16 additions & 0 deletions Sources/MarkersExtractor/Export/Payload/TSVExportPayload.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//
// TSVExportPayload.swift
// MarkersExtractor • https://github.com/TheAcharya/MarkersExtractor
// Licensed under MIT License
//

import Foundation

public struct TSVExportPayload: ExportPayload {
let tsvPath: URL

init(projectName: String, outputURL: URL) {
let tsvName = "\(projectName).tsv"
tsvPath = outputURL.appendingPathComponent(tsvName)
}
}
108 changes: 108 additions & 0 deletions Sources/MarkersExtractor/Export/Profile/TSV/TSVProfile Export.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
//
// TSVProfile Export.swift
// MarkersExtractor • https://github.com/TheAcharya/MarkersExtractor
// Licensed under MIT License
//

import AVFoundation
import Foundation
import Logging
import OrderedCollections
import TextFileKit
import TimecodeKit

extension TSVProfile {
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 tsvWriteManifest(
tsvPath: payload.tsvPath,
noMedia: noMedia,
preparedMarkers
)
}

public func resultFileContent(payload: Payload) throws -> ExportResult.ResultDictionary {
[
.tsvManifestPath: .url(payload.tsvPath)
]
}

public func tableManifestFields(
for marker: PreparedMarker,
noMedia: Bool
) -> OrderedDictionary<ExportField, String> {
var dict: OrderedDictionary<ExportField, String> = [:]

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<ExportField, ExportFieldValue> {
var dict: OrderedDictionary<ExportField, ExportFieldValue> = [:]

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
}
}
26 changes: 26 additions & 0 deletions Sources/MarkersExtractor/Export/Profile/TSV/TSVProfile.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//
// TSVProfile.swift
// MarkersExtractor • https://github.com/TheAcharya/MarkersExtractor
// Licensed under MIT License
//

import Foundation
import Logging

public class TSVProfile: NSObject, ProgressReporting, ExportProfile {
// ExportProfile
public typealias Payload = TSVExportPayload
public typealias Icon = EmptyExportIcon
public typealias PreparedMarker = StandardExportMarker
public static let profile: ExportProfileFormat = .tsv
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
}
}
12 changes: 12 additions & 0 deletions Sources/MarkersExtractor/Export/Result/ExportResult.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ public struct ExportResult: Equatable, Hashable {
/// CSV manifest file path, if applicable to the profile. `nil` if not applicable.
public var csvManifestPath: URL?

/// TSV manifest file path, if applicable to the profile. `nil` if not applicable.
public var tsvManifestPath: URL?

/// JSON manifest file path, if applicable to the profile. `nil` if not applicable.
public var jsonManifestPath: URL?

Expand All @@ -36,13 +39,15 @@ public struct ExportResult: Equatable, Hashable {
profile: ExportProfileFormat,
exportFolder: URL,
csvManifestPath: URL? = nil,
tsvManifestPath: URL? = nil,
jsonManifestPath: URL? = nil,
midiFilePath: URL? = nil
) {
self.date = date
self.profile = profile
self.exportFolder = exportFolder
self.csvManifestPath = csvManifestPath
self.tsvManifestPath = tsvManifestPath
self.jsonManifestPath = jsonManifestPath
self.midiFilePath = midiFilePath
version = packageVersion
Expand All @@ -66,6 +71,9 @@ extension ExportResult {
/// CSV manifest file path, if applicable to the profile. `nil` if not applicable.
case csvManifestPath

/// TSV manifest file path, if applicable to the profile. `nil` if not applicable.
case tsvManifestPath

/// JSON manifest file path, if applicable to the profile. `nil` if not applicable.
case jsonManifestPath

Expand Down Expand Up @@ -113,6 +121,9 @@ extension ExportResult {
if let value = dict[.csvManifestPath], case let .url(url) = value {
csvManifestPath = url
}
if let value = dict[.tsvManifestPath], case let .url(url) = value {
tsvManifestPath = url
}
if let value = dict[.jsonManifestPath], case let .url(url) = value {
jsonManifestPath = url
}
Expand All @@ -129,6 +140,7 @@ extension ExportResult {
dict[.profile] = profile.rawValue
dict[.exportFolder] = exportFolder.path
dict[.csvManifestPath] = csvManifestPath?.path
dict[.tsvManifestPath] = tsvManifestPath?.path
dict[.jsonManifestPath] = jsonManifestPath?.path
dict[.midiFilePath] = midiFilePath?.path
dict[.version] = version
Expand Down
13 changes: 13 additions & 0 deletions Sources/MarkersExtractor/MarkersExtractor Export.swift
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,19 @@ extension MarkersExtractor {
payload: .init(projectName: projectName, outputURL: outputURL),
parentProgress: parentProgress
)

case .tsv:
return try await export(
for: TSVProfile.self,
media: media,
markers: markers,
outputURL: outputURL,
payload: .init(
projectName: projectName,
outputURL: outputURL
),
parentProgress: parentProgress
)
}
}

Expand Down

0 comments on commit 5543293

Please sign in to comment.