diff --git a/VoiceInk/Services/CloudTranscription/CustomModelManager.swift b/VoiceInk/Services/CloudTranscription/CustomModelManager.swift index f62257c..581a693 100644 --- a/VoiceInk/Services/CloudTranscription/CustomModelManager.swift +++ b/VoiceInk/Services/CloudTranscription/CustomModelManager.swift @@ -19,21 +19,18 @@ class CustomModelManager: ObservableObject { func addCustomModel(_ model: CustomCloudModel) { customModels.append(model) saveCustomModels() - logger.info("Added custom model: \(model.displayName)") } - + func removeCustomModel(withId id: UUID) { customModels.removeAll { $0.id == id } saveCustomModels() APIKeyManager.shared.deleteCustomModelAPIKey(forModelId: id) - logger.info("Removed custom model with ID: \(id)") } - + func updateCustomModel(_ updatedModel: CustomCloudModel) { if let index = customModels.firstIndex(where: { $0.id == updatedModel.id }) { customModels[index] = updatedModel saveCustomModels() - logger.info("Updated custom model: \(updatedModel.displayName)") } } @@ -41,7 +38,6 @@ class CustomModelManager: ObservableObject { private func loadCustomModels() { guard let data = userDefaults.data(forKey: customModelsKey) else { - logger.info("No custom models found in UserDefaults") return } diff --git a/VoiceInk/Services/ScreenCaptureService.swift b/VoiceInk/Services/ScreenCaptureService.swift index 153d376..5b0a29f 100644 --- a/VoiceInk/Services/ScreenCaptureService.swift +++ b/VoiceInk/Services/ScreenCaptureService.swift @@ -1,7 +1,6 @@ import Foundation import AppKit import Vision -import os import ScreenCaptureKit @MainActor @@ -9,11 +8,6 @@ class ScreenCaptureService: ObservableObject { @Published var isCapturing = false @Published var lastCapturedText: String? - private let logger = Logger( - subsystem: "com.prakashjoshipax.voiceink", - category: "aienhancement" - ) - private struct WindowCandidate { let title: String let ownerName: String @@ -88,7 +82,6 @@ class ScreenCaptureService: ObservableObject { return NSImage(cgImage: cgImage, size: NSSize(width: cgImage.width, height: cgImage.height)) } catch { - logger.notice("📸 Screen capture failed: \(error.localizedDescription, privacy: .public)") return nil } } @@ -125,8 +118,7 @@ class ScreenCaptureService: ObservableObject { switch result { case .success(let text): return text - case .failure(let error): - logger.notice("📸 Text recognition failed: \(error.localizedDescription, privacy: .public)") + case .failure: return nil } } @@ -144,11 +136,8 @@ class ScreenCaptureService: ObservableObject { } guard let windowInfo = getActiveWindowInfo() else { - logger.notice("📸 No active window found") return nil } - - logger.notice("📸 Capturing: \(windowInfo.title, privacy: .public) (\(windowInfo.ownerName, privacy: .public))") var contextText = """ Active Window: \(windowInfo.title) @@ -161,11 +150,8 @@ class ScreenCaptureService: ObservableObject { if let extractedText = extractedText, !extractedText.isEmpty { contextText += "Window Content:\n\(extractedText)" - let preview = String(extractedText.prefix(100)) - logger.notice("📸 Text extracted: \(preview, privacy: .public)\(extractedText.count > 100 ? "..." : "")") } else { contextText += "Window Content:\nNo text detected via OCR" - logger.notice("📸 No text extracted from window") } await MainActor.run { @@ -174,8 +160,7 @@ class ScreenCaptureService: ObservableObject { return contextText } - - logger.notice("📸 Window capture failed") + return nil } } diff --git a/VoiceInk/Views/Settings/AudioCleanupManager.swift b/VoiceInk/Views/Settings/AudioCleanupManager.swift index a500c3a..60dcced 100644 --- a/VoiceInk/Views/Settings/AudioCleanupManager.swift +++ b/VoiceInk/Views/Settings/AudioCleanupManager.swift @@ -1,181 +1,138 @@ import Foundation import SwiftData -import OSLog /// A utility class that manages automatic cleanup of audio files while preserving transcript data class AudioCleanupManager { static let shared = AudioCleanupManager() - - private let logger = Logger(subsystem: "com.prakashjoshipax.voiceink", category: "AudioCleanupManager") + private var cleanupTimer: Timer? // Default cleanup settings private let defaultRetentionDays = 7 private let cleanupCheckInterval: TimeInterval = 86400 // Check once per day (in seconds) - private init() { - logger.info("AudioCleanupManager initialized") - } + private init() {} /// Start the automatic cleanup process func startAutomaticCleanup(modelContext: ModelContext) { - logger.info("Starting automatic audio cleanup") - // Cancel any existing timer cleanupTimer?.invalidate() - + // Perform initial cleanup Task { await performCleanup(modelContext: modelContext) } - + // Schedule regular cleanup cleanupTimer = Timer.scheduledTimer(withTimeInterval: cleanupCheckInterval, repeats: true) { [weak self] _ in Task { [weak self] in await self?.performCleanup(modelContext: modelContext) } } - - logger.info("Automatic cleanup scheduled") } /// Stop the automatic cleanup process func stopAutomaticCleanup() { - logger.info("Stopping automatic audio cleanup") cleanupTimer?.invalidate() cleanupTimer = nil } /// Get information about the files that would be cleaned up func getCleanupInfo(modelContext: ModelContext) async -> (fileCount: Int, totalSize: Int64, transcriptions: [Transcription]) { - logger.info("Analyzing potential audio cleanup") - // Get retention period from UserDefaults let retentionDays = UserDefaults.standard.integer(forKey: "AudioRetentionPeriod") let effectiveRetentionDays = retentionDays > 0 ? retentionDays : defaultRetentionDays - + // Calculate the cutoff date let calendar = Calendar.current guard let cutoffDate = calendar.date(byAdding: .day, value: -effectiveRetentionDays, to: Date()) else { - logger.error("Failed to calculate cutoff date") return (0, 0, []) } - + do { // Execute SwiftData operations on the main thread return try await MainActor.run { // Create a predicate to find transcriptions with audio files older than the cutoff date let descriptor = FetchDescriptor( predicate: #Predicate { transcription in - transcription.timestamp < cutoffDate && + transcription.timestamp < cutoffDate && transcription.audioFileURL != nil } ) - + let transcriptions = try modelContext.fetch(descriptor) - + // Calculate stats (can be done on any thread) var fileCount = 0 var totalSize: Int64 = 0 var eligibleTranscriptions: [Transcription] = [] - + for transcription in transcriptions { if let urlString = transcription.audioFileURL, let url = URL(string: urlString), FileManager.default.fileExists(atPath: url.path) { - do { - // Get file attributes to determine size - let attributes = try FileManager.default.attributesOfItem(atPath: url.path) - if let fileSize = attributes[.size] as? Int64 { - totalSize += fileSize - fileCount += 1 - eligibleTranscriptions.append(transcription) - } - } catch { - self.logger.error("Failed to get attributes for \(url.lastPathComponent): \(error.localizedDescription)") + if let attributes = try? FileManager.default.attributesOfItem(atPath: url.path), + let fileSize = attributes[.size] as? Int64 { + totalSize += fileSize + fileCount += 1 + eligibleTranscriptions.append(transcription) } } } - - self.logger.info("Found \(fileCount) files eligible for cleanup, totaling \(self.formatFileSize(totalSize))") + return (fileCount, totalSize, eligibleTranscriptions) } } catch { - logger.error("Error analyzing files for cleanup: \(error.localizedDescription)") return (0, 0, []) } } /// Perform the cleanup operation private func performCleanup(modelContext: ModelContext) async { - logger.info("Performing audio cleanup") - // Get retention period from UserDefaults let retentionDays = UserDefaults.standard.integer(forKey: "AudioRetentionPeriod") let effectiveRetentionDays = retentionDays > 0 ? retentionDays : defaultRetentionDays - + // Check if automatic cleanup is enabled let isCleanupEnabled = UserDefaults.standard.bool(forKey: "IsAudioCleanupEnabled") - guard isCleanupEnabled else { - logger.info("Audio cleanup is disabled, skipping") - return - } - - logger.info("Audio retention period: \(effectiveRetentionDays) days") - + guard isCleanupEnabled else { return } + // Calculate the cutoff date let calendar = Calendar.current guard let cutoffDate = calendar.date(byAdding: .day, value: -effectiveRetentionDays, to: Date()) else { - logger.error("Failed to calculate cutoff date") return } - - logger.info("Cutoff date for audio cleanup: \(cutoffDate)") - + do { // Execute SwiftData operations on the main thread try await MainActor.run { // Create a predicate to find transcriptions with audio files older than the cutoff date let descriptor = FetchDescriptor( predicate: #Predicate { transcription in - transcription.timestamp < cutoffDate && + transcription.timestamp < cutoffDate && transcription.audioFileURL != nil } ) - + let transcriptions = try modelContext.fetch(descriptor) - self.logger.info("Found \(transcriptions.count) transcriptions with audio files to clean up") - var deletedCount = 0 - var errorCount = 0 - + for transcription in transcriptions { if let urlString = transcription.audioFileURL, let url = URL(string: urlString), FileManager.default.fileExists(atPath: url.path) { - do { - // Delete the audio file - try FileManager.default.removeItem(at: url) - - // Update the transcription to remove the audio file reference - transcription.audioFileURL = nil - - deletedCount += 1 - self.logger.debug("Deleted audio file: \(url.lastPathComponent)") - } catch { - errorCount += 1 - self.logger.error("Failed to delete audio file \(url.lastPathComponent): \(error.localizedDescription)") - } + try? FileManager.default.removeItem(at: url) + transcription.audioFileURL = nil + deletedCount += 1 } } - - if deletedCount > 0 || errorCount > 0 { + + if deletedCount > 0 { try modelContext.save() - self.logger.info("Cleanup complete. Deleted \(deletedCount) files. Failed: \(errorCount)") } } } catch { - logger.error("Error during audio cleanup: \(error.localizedDescription)") + // Silently fail - cleanup is non-critical } } @@ -186,47 +143,33 @@ class AudioCleanupManager { /// Run cleanup on the specified transcriptions func runCleanupForTranscriptions(modelContext: ModelContext, transcriptions: [Transcription]) async -> (deletedCount: Int, errorCount: Int) { - logger.info("Running cleanup for \(transcriptions.count) specific transcriptions") - do { // Execute SwiftData operations on the main thread return try await MainActor.run { var deletedCount = 0 var errorCount = 0 - + for transcription in transcriptions { if let urlString = transcription.audioFileURL, let url = URL(string: urlString), FileManager.default.fileExists(atPath: url.path) { do { - // Delete the audio file try FileManager.default.removeItem(at: url) - - // Update the transcription to remove the audio file reference transcription.audioFileURL = nil - deletedCount += 1 - self.logger.debug("Deleted audio file: \(url.lastPathComponent)") } catch { errorCount += 1 - self.logger.error("Failed to delete audio file \(url.lastPathComponent): \(error.localizedDescription)") } } } - + if deletedCount > 0 || errorCount > 0 { - do { - try modelContext.save() - self.logger.info("Cleanup complete. Deleted \(deletedCount) files. Failed: \(errorCount)") - } catch { - self.logger.error("Error saving model context after cleanup: \(error.localizedDescription)") - } + try? modelContext.save() } - + return (deletedCount, errorCount) } } catch { - logger.error("Error during targeted cleanup: \(error.localizedDescription)") return (0, 0) } } diff --git a/VoiceInk/VoiceInk.swift b/VoiceInk/VoiceInk.swift index 7e095fd..f656d93 100644 --- a/VoiceInk/VoiceInk.swift +++ b/VoiceInk/VoiceInk.swift @@ -49,13 +49,6 @@ struct VoiceInkApp: App { // Attempt 1: Try persistent storage if let persistentContainer = Self.createPersistentContainer(schema: schema, logger: logger) { container = persistentContainer - - #if DEBUG - // Print SwiftData storage location in debug builds only - if let url = persistentContainer.mainContext.container.configurations.first?.url { - print("💾 SwiftData storage location: \(url.path)") - } - #endif } // Attempt 2: Try in-memory storage else if let memoryContainer = Self.createInMemoryContainer(schema: schema, logger: logger) {