123 lines
3.6 KiB
Swift
123 lines
3.6 KiB
Swift
import Foundation
|
|
import AppKit
|
|
|
|
enum BrowserType {
|
|
case safari
|
|
case arc
|
|
case chrome
|
|
case edge
|
|
case firefox
|
|
case brave
|
|
case opera
|
|
case vivaldi
|
|
case orion
|
|
case zen
|
|
|
|
var scriptName: String {
|
|
switch self {
|
|
case .safari: return "safariURL"
|
|
case .arc: return "arcURL"
|
|
case .chrome: return "chromeURL"
|
|
case .edge: return "edgeURL"
|
|
case .firefox: return "firefoxURL"
|
|
case .brave: return "braveURL"
|
|
case .opera: return "operaURL"
|
|
case .vivaldi: return "vivaldiURL"
|
|
case .orion: return "orionURL"
|
|
case .zen: return "zenURL"
|
|
}
|
|
}
|
|
|
|
var bundleIdentifier: String {
|
|
switch self {
|
|
case .safari: return "com.apple.Safari"
|
|
case .arc: return "company.thebrowser.Browser"
|
|
case .chrome: return "com.google.Chrome"
|
|
case .edge: return "com.microsoft.edgemac"
|
|
case .firefox: return "org.mozilla.firefox"
|
|
case .brave: return "com.brave.Browser"
|
|
case .opera: return "com.operasoftware.Opera"
|
|
case .vivaldi: return "com.vivaldi.Vivaldi"
|
|
case .orion: return "com.kagi.kagimacOS"
|
|
case .zen: return "app.zen-browser.zen"
|
|
}
|
|
}
|
|
|
|
var displayName: String {
|
|
switch self {
|
|
case .safari: return "Safari"
|
|
case .arc: return "Arc"
|
|
case .chrome: return "Google Chrome"
|
|
case .edge: return "Microsoft Edge"
|
|
case .firefox: return "Firefox"
|
|
case .brave: return "Brave"
|
|
case .opera: return "Opera"
|
|
case .vivaldi: return "Vivaldi"
|
|
case .orion: return "Orion"
|
|
case .zen: return "Zen Browser"
|
|
}
|
|
}
|
|
|
|
static var allCases: [BrowserType] {
|
|
[.safari, .arc, .chrome, .edge, .firefox, .brave, .opera, .vivaldi, .orion, .zen]
|
|
}
|
|
|
|
static var installedBrowsers: [BrowserType] {
|
|
allCases.filter { browser in
|
|
let workspace = NSWorkspace.shared
|
|
return workspace.urlForApplication(withBundleIdentifier: browser.bundleIdentifier) != nil
|
|
}
|
|
}
|
|
}
|
|
|
|
enum BrowserURLError: Error {
|
|
case scriptNotFound
|
|
case executionFailed
|
|
case browserNotRunning
|
|
case noActiveWindow
|
|
case noActiveTab
|
|
}
|
|
|
|
class BrowserURLService {
|
|
static let shared = BrowserURLService()
|
|
|
|
private init() {}
|
|
|
|
func getCurrentURL(from browser: BrowserType) async throws -> String {
|
|
guard let scriptURL = Bundle.main.url(forResource: browser.scriptName, withExtension: "scpt") else {
|
|
throw BrowserURLError.scriptNotFound
|
|
}
|
|
|
|
let task = Process()
|
|
task.launchPath = "/usr/bin/osascript"
|
|
task.arguments = [scriptURL.path]
|
|
|
|
let pipe = Pipe()
|
|
task.standardOutput = pipe
|
|
task.standardError = pipe
|
|
|
|
do {
|
|
try task.run()
|
|
task.waitUntilExit()
|
|
|
|
let data = pipe.fileHandleForReading.readDataToEndOfFile()
|
|
if let output = String(data: data, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines) {
|
|
if output.isEmpty {
|
|
throw BrowserURLError.noActiveTab
|
|
}
|
|
return output
|
|
} else {
|
|
throw BrowserURLError.executionFailed
|
|
}
|
|
} catch {
|
|
throw BrowserURLError.executionFailed
|
|
}
|
|
}
|
|
|
|
func isRunning(_ browser: BrowserType) -> Bool {
|
|
let workspace = NSWorkspace.shared
|
|
let runningApps = workspace.runningApplications
|
|
return runningApps.contains { $0.bundleIdentifier == browser.bundleIdentifier }
|
|
}
|
|
}
|