-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathNames.swift
More file actions
236 lines (195 loc) · 8.49 KB
/
Copy pathNames.swift
File metadata and controls
236 lines (195 loc) · 8.49 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
import Cocoa
import Foundation
import CoreGraphics
enum AIModel: String {
case moondream = "moondream:v2" // Updated to use v2 model
case llava = "llava:7b"
case minicpm = "minicpm-v"
}
class ScreenshotMonitor: NSObject, NSApplicationDelegate {
private let fileManager = FileManager.default
private var desktopPath: String
private let ollamaPath = "/usr/local/bin/ollama"
private var selectedModel: AIModel = .moondream
private var statusItem: NSStatusItem!
private var modelMenu: NSMenu!
override init() {
let homeDir = FileManager.default.homeDirectoryForCurrentUser.path
self.desktopPath = "\(homeDir)/Desktop"
super.init()
print("Monitoring Desktop folder at: \(desktopPath)")
setupStatusBarItem()
startMonitoring()
}
private func setupStatusBarItem() {
statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
statusItem.button?.title = "📷"
modelMenu = NSMenu()
// Add model selection menu items
let moondreamItem = NSMenuItem(title: "Moondream", action: #selector(selectModel(_:)), keyEquivalent: "")
moondreamItem.representedObject = AIModel.moondream
moondreamItem.state = .on
let llavaItem = NSMenuItem(title: "LLaVA", action: #selector(selectModel(_:)), keyEquivalent: "")
llavaItem.representedObject = AIModel.llava
let minicpmItem = NSMenuItem(title: "MiniCPM", action: #selector(selectModel(_:)), keyEquivalent: "")
minicpmItem.representedObject = AIModel.minicpm
modelMenu.addItem(moondreamItem)
modelMenu.addItem(llavaItem)
modelMenu.addItem(minicpmItem)
modelMenu.addItem(NSMenuItem.separator())
modelMenu.addItem(NSMenuItem(title: "Process Screenshots", action: #selector(processExistingScreenshots), keyEquivalent: ""))
modelMenu.addItem(NSMenuItem(title: "Quit", action: #selector(NSApplication.terminate(_:)), keyEquivalent: "q"))
statusItem.menu = modelMenu
}
@objc private func selectModel(_ sender: NSMenuItem) {
// Update checkmarks
modelMenu.items.forEach { item in
if let model = item.representedObject as? AIModel {
item.state = (sender == item) ? .on : .off
if sender == item {
selectedModel = model
}
}
}
}
private func startMonitoring() {
// Create a file descriptor for the Desktop directory
let fileDescriptor = open(desktopPath, O_EVTONLY)
if fileDescriptor < 0 {
print("Error: Could not create file descriptor for Desktop folder")
return
}
let source = DispatchSource.makeFileSystemObjectSource(
fileDescriptor: fileDescriptor,
eventMask: [.write, .link],
queue: .main
)
source.setEventHandler { [weak self] in
print("Change detected in Desktop folder")
self?.checkForNewScreenshots()
}
source.setCancelHandler {
close(fileDescriptor)
}
source.resume()
}
private func checkForNewScreenshots() {
do {
let files = try fileManager.contentsOfDirectory(atPath: desktopPath)
let screenshots = files.filter { $0.hasPrefix("Screenshot ") }
if !screenshots.isEmpty {
print("Found \(screenshots.count) screenshots: \(screenshots)")
for screenshot in screenshots {
let fullPath = (desktopPath as NSString).appendingPathComponent(screenshot)
processScreenshot(at: fullPath)
}
}
} catch {
print("Error reading directory: \(error)")
}
}
private func processScreenshot(at path: String) {
print("Processing screenshot at: \(path)")
// Check if file exists
guard fileManager.fileExists(atPath: path) else {
print("Error: File not found at path: \(path)")
return
}
// Check if Ollama exists
guard fileManager.fileExists(atPath: ollamaPath) else {
print("Error: Ollama not found at path: \(ollamaPath)")
return
}
// First pull the model to ensure it's available
let pullProcess = Process()
pullProcess.executableURL = URL(fileURLWithPath: ollamaPath)
pullProcess.arguments = ["pull", selectedModel.rawValue]
do {
try pullProcess.run()
pullProcess.waitUntilExit()
} catch {
print("Error pulling model: \(error)")
return
}
// Create and configure process
let process = Process()
process.executableURL = URL(fileURLWithPath: ollamaPath)
process.arguments = [
"run",
selectedModel.rawValue,
"Generate a title in less than four words for this image",
path
]
let pipe = Pipe()
process.standardOutput = pipe
let errorPipe = Pipe()
process.standardError = errorPipe
do {
try process.run()
process.waitUntilExit()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
let errorData = errorPipe.fileHandleForReading.readDataToEndOfFile()
if !errorData.isEmpty {
if let errorString = String(data: errorData, encoding: .utf8) {
print("Ollama Error: \(errorString)")
// If we get a GGML error, try with LLaVA as fallback
if errorString.contains("GGML_ASSERT") {
print("Retrying with LLaVA model...")
let fallbackProcess = Process()
fallbackProcess.executableURL = URL(fileURLWithPath: ollamaPath)
fallbackProcess.arguments = [
"run",
AIModel.llava.rawValue,
"Generate a title in less than four words for this image",
path
]
let fallbackPipe = Pipe()
fallbackProcess.standardOutput = fallbackPipe
try fallbackProcess.run()
fallbackProcess.waitUntilExit()
let fallbackData = fallbackPipe.fileHandleForReading.readDataToEndOfFile()
if let fallbackOutput = String(data: fallbackData, encoding: .utf8) {
processOutput(fallbackOutput, forPath: path)
}
}
return
}
}
if let output = String(data: data, encoding: .utf8) {
processOutput(output, forPath: path)
}
} catch {
print("Error running Ollama: \(error)")
}
}
private func processOutput(_ output: String, forPath path: String) {
print("Ollama Output: \(output)")
let title = output.trimmingCharacters(in: .whitespacesAndNewlines)
.replacingOccurrences(of: " ", with: "_")
.replacingOccurrences(of: "/", with: "_")
// Only proceed if we got a non-empty title
if !title.isEmpty {
print("Generated Title: \(title)")
do {
let directory = (path as NSString).deletingLastPathComponent
let newPath = (directory as NSString).appendingPathComponent("\(title).png")
print("Attempting to rename file to: \(newPath)")
try fileManager.moveItem(atPath: path, toPath: newPath)
print("Successfully renamed file")
} catch {
print("Error renaming file: \(error)")
}
} else {
print("Empty title generated, skipping file rename")
}
}
@objc func processExistingScreenshots() {
print("Processing existing screenshots...")
checkForNewScreenshots()
}
}
// Create and start the app
let app = NSApplication.shared
let monitor = ScreenshotMonitor()
app.delegate = monitor
app.run()