Files

64 lines
2.7 KiB
Swift

import Foundation
import Security
final class AppSettings: @unchecked Sendable {
static let shared = AppSettings()
private let defaults = UserDefaults.standard
var localBinaryPath: String {
get { defaults.string(forKey: "localBinaryPath") ?? "\(NSHomeDirectory())/Library/Application Support/WhisperKey/whisper-cli" }
set { defaults.set(newValue, forKey: "localBinaryPath") }
}
var localModelPath: String {
get { defaults.string(forKey: "localModelPath") ?? "\(NSHomeDirectory())/Library/Application Support/WhisperKey/ggml-large-v3-turbo.bin" }
set { defaults.set(newValue, forKey: "localModelPath") }
}
var glossary: String {
get { defaults.string(forKey: "glossary") ?? "CampusAgro, PocketBase, Caddy, SvelteKit, Bitrix, Hetzner, Timeweb, OpenAI, Codex, Claude, VLESS, Reality, Hiddify, CNS8000D, RX8000, RX8500, BX8000, BX8500, BX8500S" }
set { defaults.set(newValue, forKey: "glossary") }
}
var removeFillers: Bool {
get { defaults.object(forKey: "removeFillers") as? Bool ?? true }
set { defaults.set(newValue, forKey: "removeFillers") }
}
var cloudModel: String {
get { defaults.string(forKey: "cloudModel") ?? "gpt-4o-transcribe" }
set { defaults.set(newValue, forKey: "cloudModel") }
}
func apiKey() -> String? { KeychainStore.read(account: "openai-api-key") }
func saveAPIKey(_ value: String) throws { try KeychainStore.write(value, account: "openai-api-key") }
}
enum KeychainStore {
private static let service = "com.romanv.WhisperKey"
static func write(_ value: String, account: String) throws {
let data = Data(value.utf8)
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: service,
kSecAttrAccount as String: account,
]
SecItemDelete(query as CFDictionary)
var add = query
add[kSecValueData as String] = data
let status = SecItemAdd(add as CFDictionary, nil)
guard status == errSecSuccess else { throw WhisperKeyError.processFailed("Keychain: \(status)") }
}
static func read(account: String) -> String? {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: service,
kSecAttrAccount as String: account,
kSecReturnData as String: true,
kSecMatchLimit as String: kSecMatchLimitOne,
]
var item: CFTypeRef?
guard SecItemCopyMatching(query as CFDictionary, &item) == errSecSuccess,
let data = item as? Data else { return nil }
return String(data: data, encoding: .utf8)
}
}