Spaces:
Paused
Paused
| import AVFoundation | |
| import Commander | |
| import Foundation | |
| import Swabble | |
| struct MicCommand: ParsableCommand { | |
| static var commandDescription: CommandDescription { | |
| CommandDescription( | |
| commandName: "mic", | |
| abstract: "Microphone management", | |
| subcommands: [MicList.self, MicSet.self]) | |
| } | |
| } | |
| struct MicList: ParsableCommand { | |
| static var commandDescription: CommandDescription { | |
| CommandDescription(commandName: "list", abstract: "List input devices") | |
| } | |
| init() {} | |
| init(parsed: ParsedValues) {} | |
| mutating func run() async throws { | |
| let session = AVCaptureDevice.DiscoverySession( | |
| deviceTypes: [.microphone, .external], | |
| mediaType: .audio, | |
| position: .unspecified) | |
| let devices = session.devices | |
| if devices.isEmpty { print("no audio inputs found"); return } | |
| for (idx, device) in devices.enumerated() { | |
| print("[\(idx)] \(device.localizedName)") | |
| } | |
| } | |
| } | |
| struct MicSet: ParsableCommand { | |
| (help: "Device index from list") var index: Int = 0 | |
| (name: .long("config"), help: "Path to config JSON") var configPath: String? | |
| static var commandDescription: CommandDescription { | |
| CommandDescription(commandName: "set", abstract: "Set default input device index") | |
| } | |
| init() {} | |
| init(parsed: ParsedValues) { | |
| self.init() | |
| if let value = parsed.positional.first, let intVal = Int(value) { index = intVal } | |
| if let cfg = parsed.options["config"]?.last { configPath = cfg } | |
| } | |
| mutating func run() async throws { | |
| var cfg = try ConfigLoader.load(at: configURL) | |
| cfg.audio.deviceIndex = index | |
| try ConfigLoader.save(cfg, at: configURL) | |
| print("saved device index \(index)") | |
| } | |
| private var configURL: URL? { configPath.map { URL(fileURLWithPath: $0) } } | |
| } | |