Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0f45f2d066 |
@@ -276,7 +276,7 @@
|
|||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
CURRENT_PROJECT_VERSION = 26;
|
CURRENT_PROJECT_VERSION = 27;
|
||||||
DEVELOPMENT_ASSET_PATHS = "\"meetingnotes/Preview Content\"";
|
DEVELOPMENT_ASSET_PATHS = "\"meetingnotes/Preview Content\"";
|
||||||
DEVELOPMENT_TEAM = G9LVHZAJNX;
|
DEVELOPMENT_TEAM = G9LVHZAJNX;
|
||||||
ENABLE_HARDENED_RUNTIME = YES;
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
@@ -290,7 +290,7 @@
|
|||||||
"@executable_path/../Frameworks",
|
"@executable_path/../Frameworks",
|
||||||
);
|
);
|
||||||
MACOSX_DEPLOYMENT_TARGET = 15.0;
|
MACOSX_DEPLOYMENT_TARGET = 15.0;
|
||||||
MARKETING_VERSION = 1.1.14;
|
MARKETING_VERSION = 1.1.15;
|
||||||
ONLY_ACTIVE_ARCH = NO;
|
ONLY_ACTIVE_ARCH = NO;
|
||||||
OTHER_SWIFT_FLAGS = "$(inherited) -D ENABLE_TCC_SPI";
|
OTHER_SWIFT_FLAGS = "$(inherited) -D ENABLE_TCC_SPI";
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = net.jamesbone.meetingnotes;
|
PRODUCT_BUNDLE_IDENTIFIER = net.jamesbone.meetingnotes;
|
||||||
@@ -312,7 +312,7 @@
|
|||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
CURRENT_PROJECT_VERSION = 26;
|
CURRENT_PROJECT_VERSION = 27;
|
||||||
DEVELOPMENT_ASSET_PATHS = "\"meetingnotes/Preview Content\"";
|
DEVELOPMENT_ASSET_PATHS = "\"meetingnotes/Preview Content\"";
|
||||||
DEVELOPMENT_TEAM = G9LVHZAJNX;
|
DEVELOPMENT_TEAM = G9LVHZAJNX;
|
||||||
ENABLE_HARDENED_RUNTIME = YES;
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
@@ -326,7 +326,7 @@
|
|||||||
"@executable_path/../Frameworks",
|
"@executable_path/../Frameworks",
|
||||||
);
|
);
|
||||||
MACOSX_DEPLOYMENT_TARGET = 15.0;
|
MACOSX_DEPLOYMENT_TARGET = 15.0;
|
||||||
MARKETING_VERSION = 1.1.14;
|
MARKETING_VERSION = 1.1.15;
|
||||||
ONLY_ACTIVE_ARCH = YES;
|
ONLY_ACTIVE_ARCH = YES;
|
||||||
OTHER_SWIFT_FLAGS = "$(inherited) -D ENABLE_TCC_SPI";
|
OTHER_SWIFT_FLAGS = "$(inherited) -D ENABLE_TCC_SPI";
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = net.jamesbone.meetingnotes;
|
PRODUCT_BUNDLE_IDENTIFIER = net.jamesbone.meetingnotes;
|
||||||
|
|||||||
@@ -102,12 +102,14 @@ final class AudioManager: NSObject, ObservableObject {
|
|||||||
let audioFolder = preserveAudioFiles(completedFiles, meetingID: completedMeetingID)
|
let audioFolder = preserveAudioFiles(completedFiles, meetingID: completedMeetingID)
|
||||||
lastRecoveryAudioFolderName = audioFolder?.lastPathComponent
|
lastRecoveryAudioFolderName = audioFolder?.lastPathComponent
|
||||||
if !failures.isEmpty {
|
if !failures.isEmpty {
|
||||||
|
let retentionDays = UserDefaultsManager.shared.audioRetentionDays
|
||||||
|
let retentionUnit = retentionDays == 1 ? "day" : "days"
|
||||||
let recoveryMessage = audioFolder == nil
|
let recoveryMessage = audioFolder == nil
|
||||||
? " The audio remains in the app's temporary folder."
|
? " The audio remains in the app's temporary folder."
|
||||||
: " Audio was kept for three days. Use Show Audio Folder in the Meetingnotes menu to find it."
|
: " Audio was kept for \(retentionDays) \(retentionUnit). Use Show Audio Folder in the Meetingnotes menu to find it."
|
||||||
errorMessage = "Transcription failed for " + failures.joined(separator: "; ") + recoveryMessage
|
errorMessage = "Transcription failed for " + failures.joined(separator: "; ") + recoveryMessage
|
||||||
} else if audioFolder == nil, !completedFiles.isEmpty {
|
} else if audioFolder == nil, !completedFiles.isEmpty {
|
||||||
errorMessage = "The transcript completed, but Meetingnotes could not move the audio into its three-day storage folder."
|
errorMessage = "The transcript completed, but Meetingnotes could not move the audio into its retention folder."
|
||||||
}
|
}
|
||||||
return updated
|
return updated
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ class LocalStorageManager {
|
|||||||
private let meetingsDirectory: URL
|
private let meetingsDirectory: URL
|
||||||
private let templatesDirectory: URL
|
private let templatesDirectory: URL
|
||||||
private let recoveryDirectory: URL
|
private let recoveryDirectory: URL
|
||||||
private let audioRetentionInterval: TimeInterval = 3 * 24 * 60 * 60
|
|
||||||
|
|
||||||
private init() {
|
private init() {
|
||||||
// Get the app's documents directory
|
// Get the app's documents directory
|
||||||
@@ -189,7 +188,8 @@ class LocalStorageManager {
|
|||||||
options: [.skipsHiddenFiles]
|
options: [.skipsHiddenFiles]
|
||||||
) else { return }
|
) else { return }
|
||||||
|
|
||||||
let expirationDate = now.addingTimeInterval(-audioRetentionInterval)
|
let retentionInterval = TimeInterval(UserDefaultsManager.shared.audioRetentionDays) * 24 * 60 * 60
|
||||||
|
let expirationDate = now.addingTimeInterval(-retentionInterval)
|
||||||
for folder in folders {
|
for folder in folders {
|
||||||
guard (try? folder.resourceValues(forKeys: [.isDirectoryKey]).isDirectory) == true else { continue }
|
guard (try? folder.resourceValues(forKeys: [.isDirectoryKey]).isDirectory) == true else { continue }
|
||||||
let audioFiles = recoveryAudioFiles(in: folder)
|
let audioFiles = recoveryAudioFiles(in: folder)
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ class UserDefaultsManager {
|
|||||||
static let transcriptionModel = "transcriptionModel"
|
static let transcriptionModel = "transcriptionModel"
|
||||||
static let muteDeckAPIEnabled = "muteDeckAPIEnabled"
|
static let muteDeckAPIEnabled = "muteDeckAPIEnabled"
|
||||||
static let muteDeckAPIPort = "muteDeckAPIPort"
|
static let muteDeckAPIPort = "muteDeckAPIPort"
|
||||||
|
static let audioRetentionDays = "audioRetentionDays"
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - User Blurb
|
// MARK: - User Blurb
|
||||||
@@ -94,4 +95,12 @@ class UserDefaultsManager {
|
|||||||
}
|
}
|
||||||
set { userDefaults.set(newValue, forKey: Keys.muteDeckAPIPort) }
|
set { userDefaults.set(newValue, forKey: Keys.muteDeckAPIPort) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var audioRetentionDays: Int {
|
||||||
|
get {
|
||||||
|
guard userDefaults.object(forKey: Keys.audioRetentionDays) != nil else { return 3 }
|
||||||
|
return min(max(userDefaults.integer(forKey: Keys.audioRetentionDays), 1), 365)
|
||||||
|
}
|
||||||
|
set { userDefaults.set(min(max(newValue, 1), 365), forKey: Keys.audioRetentionDays) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,6 +54,11 @@ struct Settings: Codable {
|
|||||||
set { UserDefaultsManager.shared.muteDeckAPIPort = newValue }
|
set { UserDefaultsManager.shared.muteDeckAPIPort = newValue }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var audioRetentionDays: Int {
|
||||||
|
get { UserDefaultsManager.shared.audioRetentionDays }
|
||||||
|
set { UserDefaultsManager.shared.audioRetentionDays = newValue }
|
||||||
|
}
|
||||||
|
|
||||||
// System prompt default loading
|
// System prompt default loading
|
||||||
static func defaultSystemPrompt() -> String {
|
static func defaultSystemPrompt() -> String {
|
||||||
guard let path = Bundle.main.path(forResource: "DefaultSystemPrompt", ofType: "txt"),
|
guard let path = Bundle.main.path(forResource: "DefaultSystemPrompt", ofType: "txt"),
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ class SettingsViewModel: ObservableObject {
|
|||||||
// via computed properties when they're modified
|
// via computed properties when they're modified
|
||||||
let coderSaved = KeychainHelper.shared.saveCoderAPIKey(settings.coderAPIKey)
|
let coderSaved = KeychainHelper.shared.saveCoderAPIKey(settings.coderAPIKey)
|
||||||
LocalAPIServer.shared.applyConfiguration()
|
LocalAPIServer.shared.applyConfiguration()
|
||||||
|
LocalStorageManager.shared.purgeExpiredAudioFolders()
|
||||||
|
|
||||||
if showMessage {
|
if showMessage {
|
||||||
if coderSaved {
|
if coderSaved {
|
||||||
|
|||||||
@@ -128,6 +128,23 @@ struct SettingsView: View {
|
|||||||
Text("Meeting Storage")
|
Text("Meeting Storage")
|
||||||
.font(.headline)
|
.font(.headline)
|
||||||
|
|
||||||
|
LabeledContent("Audio retention") {
|
||||||
|
Stepper(
|
||||||
|
value: $viewModel.settings.audioRetentionDays,
|
||||||
|
in: 1...365
|
||||||
|
) {
|
||||||
|
Text("\(viewModel.settings.audioRetentionDays) \(viewModel.settings.audioRetentionDays == 1 ? "day" : "days")")
|
||||||
|
.monospacedDigit()
|
||||||
|
.frame(minWidth: 70, alignment: .trailing)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
LocalStorageManager.shared.showAudioFolderInFinder()
|
||||||
|
} label: {
|
||||||
|
Label("Show Audio Folder", systemImage: "folder")
|
||||||
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
showingMeetingImporter = true
|
showingMeetingImporter = true
|
||||||
} label: {
|
} label: {
|
||||||
|
|||||||
Reference in New Issue
Block a user