Skip to content

Commit 51c6078

Browse files
authored
Merge pull request #237 from daaain/support-more-formats
Add OGG support and dynamically query supported audio formats
2 parents 52f1458 + 840fdc6 commit 51c6078

2 files changed

Lines changed: 32 additions & 16 deletions

File tree

Sources/Fluid/Services/MeetingTranscriptionService.swift

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import AVFoundation
22
import Combine
33
import CoreMedia
44
import Foundation
5+
import UniformTypeIdentifiers
56

67
/// Result of a transcription operation
78
struct TranscriptionResult: Identifiable, Sendable, Codable {
@@ -67,6 +68,29 @@ final class MeetingTranscriptionService: ObservableObject {
6768
@Published var error: String?
6869
@Published var result: TranscriptionResult?
6970

71+
// MARK: - Supported Formats
72+
73+
/// File extensions the OS can actually decode, queried dynamically from AVFoundation.
74+
/// Filtered to audio/video types only — excludes subtitles, playlists, etc.
75+
static let supportedFileExtensions: Set<String> = {
76+
let avTypes = AVURLAsset.audiovisualTypes()
77+
let extensions = avTypes.compactMap { fileType -> String? in
78+
guard let utType = UTType(fileType.rawValue) else { return nil }
79+
guard utType.conforms(to: .audio) || utType.conforms(to: .movie) else { return nil }
80+
return utType.preferredFilenameExtension
81+
}
82+
return Set(extensions)
83+
}()
84+
85+
/// Content types accepted by the file picker — broad categories so the OS filters naturally.
86+
static let allowedContentTypes: [UTType] = [.audio, .movie]
87+
88+
/// User-facing description of supported formats (curated for readability).
89+
static let supportedFormatsDescription = "Supported: WAV, MP3, M4A, OGG, MP4, MOV, and more"
90+
91+
/// Error copy shown when a dropped file is not accepted.
92+
static let dropErrorCopy = "Accepted file types: WAV, MP3, M4A, OGG, MP4, MOV, and more."
93+
7094
/// Share the ASR service instance to avoid loading models twice
7195
private let asrService: ASRService
7296

@@ -159,11 +183,10 @@ final class MeetingTranscriptionService: ObservableObject {
159183

160184
// Check file extension
161185
let fileExtension = fileURL.pathExtension.lowercased()
162-
let supportedFormats = ["wav", "mp3", "m4a", "ogg", "aac", "flac", "aiff", "caf", "mp4", "mov"]
163186

164-
guard supportedFormats.contains(fileExtension) else {
187+
guard Self.supportedFileExtensions.contains(fileExtension) else {
165188
throw TranscriptionError
166-
.fileNotSupported("Format .\(fileExtension) not supported. Supported: \(supportedFormats.joined(separator: ", "))")
189+
.fileNotSupported("Format .\(fileExtension) not supported. \(Self.supportedFormatsDescription)")
167190
}
168191

169192
// Get audio duration for progress display
@@ -181,7 +204,8 @@ final class MeetingTranscriptionService: ObservableObject {
181204
DebugLogger.shared.warning("Could not determine audio duration: \(error.localizedDescription)", source: "MeetingTranscriptionService")
182205
}
183206

184-
let isVideoContainer = ["mp4", "mov"].contains(fileExtension)
207+
let isVideoContainer = UTType(filenameExtension: fileExtension)
208+
.map { $0.conforms(to: .movie) } ?? false
185209

186210
if provider.prefersNativeFileTranscription && !isVideoContainer {
187211
self.currentStatus = duration > 0 ? "Transcribing audio (\(Int(duration))s)..." : "Transcribing audio..."

Sources/Fluid/UI/MeetingTranscriptionView.swift

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ struct MeetingTranscriptionView: View {
192192
Text("Choose Audio or Video File")
193193
.font(.headline)
194194

195-
Text("Supported: WAV, MP3, M4A, OGG, MP4, MOV, and more")
195+
Text(MeetingTranscriptionService.supportedFormatsDescription)
196196
.font(.caption)
197197
.foregroundColor(.secondary)
198198
}
@@ -221,15 +221,7 @@ struct MeetingTranscriptionView: View {
221221
}
222222
.fileImporter(
223223
isPresented: self.$showingFilePicker,
224-
allowedContentTypes: [
225-
.audio,
226-
.movie,
227-
.mpeg4Movie,
228-
UTType(filenameExtension: "wav") ?? .audio,
229-
UTType(filenameExtension: "mp3") ?? .audio,
230-
UTType(filenameExtension: "m4a") ?? .audio,
231-
UTType(filenameExtension: "ogg") ?? .audio,
232-
],
224+
allowedContentTypes: MeetingTranscriptionService.allowedContentTypes,
233225
allowsMultipleSelection: false
234226
) { result in
235227
switch result {
@@ -545,9 +537,9 @@ struct MeetingTranscriptionView: View {
545537

546538
// MARK: - Helper Functions
547539

548-
private static let supportedFileExtensions = ["wav", "mp3", "m4a", "ogg", "aac", "flac", "aiff", "caf", "mp4", "mov"]
540+
private static let supportedFileExtensions = MeetingTranscriptionService.supportedFileExtensions
549541

550-
private static let dropErrorCopy = "Accepted file types: WAV, MP3, M4A, OGG, MP4, MOV, and more."
542+
private static let dropErrorCopy = MeetingTranscriptionService.dropErrorCopy
551543

552544
private func handleDrop(providers: [NSItemProvider]) -> Bool {
553545
guard let provider = providers.first else { return false }

0 commit comments

Comments
 (0)