Skip to content

Commit

Permalink
add AppleScript support
Browse files Browse the repository at this point in the history
  • Loading branch information
lihaoyun6 committed Sep 24, 2024
1 parent 23ad404 commit 34633a6
Show file tree
Hide file tree
Showing 11 changed files with 345 additions and 11 deletions.
16 changes: 12 additions & 4 deletions QuickRecorder.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
objects = {

/* Begin PBXBuildFile section */
1809EDEE2CA1116A00D8BDD7 /* Scriptable.sdef in Resources */ = {isa = PBXBuildFile; fileRef = 1809EDED2CA1116A00D8BDD7 /* Scriptable.sdef */; };
1809EDF02CA112D200D8BDD7 /* AppleScript.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1809EDEF2CA112D200D8BDD7 /* AppleScript.swift */; };
181724052BE3BEA600F5F539 /* Sparkle in Frameworks */ = {isa = PBXBuildFile; productRef = 181724042BE3BEA600F5F539 /* Sparkle */; };
181724082BE3BF7300F5F539 /* Sparkle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 181724072BE3BF7300F5F539 /* Sparkle.swift */; };
184506CE2C69213400AFDA45 /* MatrixColorSelector in Frameworks */ = {isa = PBXBuildFile; productRef = 184506CD2C69213400AFDA45 /* MatrixColorSelector */; };
Expand Down Expand Up @@ -38,6 +40,8 @@
/* End PBXBuildFile section */

/* Begin PBXFileReference section */
1809EDED2CA1116A00D8BDD7 /* Scriptable.sdef */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = Scriptable.sdef; sourceTree = "<group>"; };
1809EDEF2CA112D200D8BDD7 /* AppleScript.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppleScript.swift; sourceTree = "<group>"; };
181724072BE3BF7300F5F539 /* Sparkle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Sparkle.swift; sourceTree = "<group>"; };
184CAEC62BDCCC2300D61D57 /* AVContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AVContext.swift; sourceTree = "<group>"; };
184CAEC82BDDEC6800D61D57 /* AppBlockSelector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppBlockSelector.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -119,6 +123,8 @@
18FEBDA92BCF8200003F09BC /* RecordEngine.swift */,
18D3BE032BCEC847006CFFC0 /* SCContext.swift */,
184CAEC62BDCCC2300D61D57 /* AVContext.swift */,
1809EDED2CA1116A00D8BDD7 /* Scriptable.sdef */,
1809EDEF2CA112D200D8BDD7 /* AppleScript.swift */,
181724072BE3BF7300F5F539 /* Sparkle.swift */,
18D3BE022BCEB1D4006CFFC0 /* ViewModel */,
18D3BDFD2BCE5DF5006CFFC0 /* Localizable.strings */,
Expand Down Expand Up @@ -233,6 +239,7 @@
files = (
18D3BDF22BCE5DC2006CFFC0 /* Preview Assets.xcassets in Resources */,
18D3BDFB2BCE5DF5006CFFC0 /* Localizable.strings in Resources */,
1809EDEE2CA1116A00D8BDD7 /* Scriptable.sdef in Resources */,
18D3BDEF2BCE5DC2006CFFC0 /* Assets.xcassets in Resources */,
1862BF8D2BD5494E003ED522 /* Credits.rtf in Resources */,
189BD5E62BDE34B80056D06C /* InfoPlist.strings in Resources */,
Expand Down Expand Up @@ -265,6 +272,7 @@
18D3BDEB2BCE5DC1006CFFC0 /* QuickRecorderApp.swift in Sources */,
181724082BE3BF7300F5F539 /* Sparkle.swift in Sources */,
18FEBDAC2BD01E82003F09BC /* WinSelector.swift in Sources */,
1809EDF02CA112D200D8BDD7 /* AppleScript.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -432,7 +440,7 @@
CODE_SIGN_ENTITLEMENTS = QuickRecorder/QuickRecorder.entitlements;
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 147;
CURRENT_PROJECT_VERSION = 148;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_ASSET_PATHS = "\"QuickRecorder/Preview Content\"";
DEVELOPMENT_TEAM = L4T783637F;
Expand All @@ -450,7 +458,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 12.3;
MARKETING_VERSION = 1.4.7;
MARKETING_VERSION = 1.4.8;
PRODUCT_BUNDLE_IDENTIFIER = com.lihaoyun6.QuickRecorder;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES;
Expand All @@ -466,7 +474,7 @@
CODE_SIGN_ENTITLEMENTS = QuickRecorder/QuickRecorder.entitlements;
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 147;
CURRENT_PROJECT_VERSION = 148;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_ASSET_PATHS = "\"QuickRecorder/Preview Content\"";
DEVELOPMENT_TEAM = L4T783637F;
Expand All @@ -484,7 +492,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 12.3;
MARKETING_VERSION = 1.4.7;
MARKETING_VERSION = 1.4.8;
PRODUCT_BUNDLE_IDENTIFIER = com.lihaoyun6.QuickRecorder;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES;
Expand Down
233 changes: 233 additions & 0 deletions QuickRecorder/AppleScript.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
//
// AppleScript.swift
// QuickRecorder
//
// Created by apple on 2024/9/23.
//

import Foundation
import AppKit
import ScreenCaptureKit

class selectScreen: NSScriptCommand {
override func performDefaultImplementation() -> Any? {
if SCContext.stream != nil {
AppDelegate.shared.createAlert(title: "Error".local, message: "Already recording!".local, button1: "OK".local).runModal()
return nil
}
SCContext.updateAvailableContent{
DispatchQueue.main.async {
AppDelegate.shared.closeAllWindow()
if var index = self.evaluatedArguments!["index"] as? Int {
guard let screens = SCContext.availableContent?.displays else { return }
index -= 1
AppDelegate.shared.closeAllWindow()
if index >= screens.count || index < 0 {
AppDelegate.shared.createAlert(title: "Error".local, message: "Invalid screen number!".local, button1: "OK".local).runModal()
return
} else {
let screen = screens[index]
AppDelegate.shared.createCountdownPanel(screen: screen) {
AppDelegate.shared.prepRecord(type: "display", screens: screen, windows: nil, applications: nil)
}
}
} else {
AppDelegate.shared.closeAllWindow()
AppDelegate.shared.createNewWindow(view: ScreenSelector(), title: "Screen Selector".local)
}
}
}
return nil
}
}

class selectArea: NSScriptCommand {
override func performDefaultImplementation() -> Any? {
if SCContext.stream != nil {
AppDelegate.shared.createAlert(title: "Error".local, message: "Already recording!".local, button1: "OK".local).runModal()
return nil
}
SCContext.updateAvailableContent{
DispatchQueue.main.async {
AppDelegate.shared.closeAllWindow()
DispatchQueue.main.async {
AppDelegate.shared.showAreaSelector(size: NSSize(width: 600, height: 450))
var currentDisplay = SCContext.getSCDisplayWithMouse()
mouseMonitor = NSEvent.addGlobalMonitorForEvents(matching: [.mouseMoved, .rightMouseDown, .leftMouseDown, .otherMouseDown]) { event in
let display = SCContext.getSCDisplayWithMouse()
if display != currentDisplay {
currentDisplay = display
AppDelegate.shared.closeAllWindow()
AppDelegate.shared.showAreaSelector(size: NSSize(width: 600, height: 450))
}
}
}
}
}
return nil
}
}

class selectApps: NSScriptCommand {
override func performDefaultImplementation() -> Any? {
if SCContext.stream != nil {
AppDelegate.shared.createAlert(title: "Error".local, message: "Already recording!".local, button1: "OK".local).runModal()
return nil
}
SCContext.updateAvailableContent{
DispatchQueue.main.async {
AppDelegate.shared.closeAllWindow()
if let name = self.evaluatedArguments!["name"] as? String {
guard let app = SCContext.availableContent?.applications.first(where: { $0.applicationName == name }) else {
AppDelegate.shared.createAlert(title: "Error".local, message: "No such application!".local, button1: "OK".local).runModal()
return
}
AppDelegate.shared.closeAllWindow()
guard let screens = SCContext.availableContent?.displays else { return }
guard let windows = SCContext.availableContent?.windows.filter({
guard let title = $0.title else { return false }
return !title.contains("Item-0")
&& title != "Window"
&& $0.frame.width > 40
&& $0.frame.height > 40
}) else { return }
var s = [SCDisplay]()
for screen in screens {
for w in windows {
if NSIntersectsRect(screen.frame, w.frame) { if !s.contains(screen) { s.append(screen) }}
}
}
if s.isEmpty {
AppDelegate.shared.createAlert(title: "Error".local, message: "This application has no windows!".local, button1: "OK".local).runModal()
return
}
if s.count != 1 {
AppDelegate.shared.createNewWindow(view: AppSelector(), title: "App Selector".local)
AppDelegate.shared.createAlert(title: "Error".local, message: "This app exists in multiple screens, please select it manually!".local, button1: "OK".local).runModal()
} else {
AppDelegate.shared.createCountdownPanel(screen: s.first!) {
AppDelegate.shared.prepRecord(type: "application", screens: s.first!, windows: nil, applications: [app])
}
}
} else {
AppDelegate.shared.closeAllWindow()
AppDelegate.shared.createNewWindow(view: AppSelector(), title: "App Selector".local)
}
}
}
return nil
}
}

class selectWindows: NSScriptCommand {
override func performDefaultImplementation() -> Any? {
if SCContext.stream != nil {
AppDelegate.shared.createAlert(title: "Error".local, message: "Already recording!".local, button1: "OK".local).runModal()
return nil
}
SCContext.updateAvailableContent{
DispatchQueue.main.async {
AppDelegate.shared.closeAllWindow()
if let title = self.evaluatedArguments!["title"] as? String {
var windows = [SCWindow]()
guard let w = SCContext.availableContent?.windows.filter({ $0.title == title }) else { return }
windows = w
if let app = self.evaluatedArguments!["app"] as? String {
guard let w = SCContext.availableContent?.windows.filter({ $0.title == title && $0.owningApplication?.applicationName == app }) else { return }
windows = w
}
AppDelegate.shared.closeAllWindow()
if windows.isEmpty {
AppDelegate.shared.createAlert(title: "Error".local, message: "No such window!".local, button1: "OK".local).runModal()
return
}
if windows.count > 1 {
AppDelegate.shared.createNewWindow(view: WinSelector(), title: "Window Selector".local)
AppDelegate.shared.createAlert(title: "Error".local, message: "Duplicate window exists, please select it manually!".local, button1: "OK".local).runModal()
return
}
let window = windows.first!
guard let screens = SCContext.availableContent?.displays else { return }
var s = [SCDisplay]()
for screen in screens {
if NSIntersectsRect(screen.frame, window.frame) { if !s.contains(screen) { s.append(screen) }}
}
if s.isEmpty {
AppDelegate.shared.createAlert(title: "Error".local, message: "Unable to find the screen this window belongs to!".local, button1: "OK".local).runModal()
return
}
if let display = SCContext.getSCDisplayWithMouse() {
if s.contains(display) {
AppDelegate.shared.createCountdownPanel(screen: display) {
AppDelegate.shared.prepRecord(type: "window" , screens: s.first!, windows: [window], applications: nil)
}
} else {
AppDelegate.shared.createCountdownPanel(screen: s.first!) {
AppDelegate.shared.prepRecord(type: "window" , screens: s.first!, windows: [window], applications: nil)
}
}
}
} else {
AppDelegate.shared.closeAllWindow()
AppDelegate.shared.createNewWindow(view: WinSelector(), title: "Window Selector".local)
}
}
}
return nil
}
}

class recordAudio: NSScriptCommand {
override func performDefaultImplementation() -> Any? {
if SCContext.stream != nil {
AppDelegate.shared.createAlert(title: "Error".local, message: "Already recording!".local, button1: "OK".local).runModal()
return nil
}
SCContext.updateAvailableContent{
DispatchQueue.main.async {
let m = UserDefaults.standard.bool(forKey: "recordMic")
if let mic = self.evaluatedArguments!["mic"] as? Bool {
UserDefaults.standard.set(mic, forKey: "recordMic")
}
AppDelegate.shared.closeAllWindow()
AppDelegate.shared.prepRecord(type: "audio", screens: SCContext.getSCDisplayWithMouse(), windows: nil, applications: nil)
UserDefaults.standard.set(m, forKey: "recordMic")
}
}
return nil
}
}

class setPreferences: NSScriptCommand {
override func performDefaultImplementation() -> Any? {
if SCContext.stream != nil {
AppDelegate.shared.createAlert(title: "Error".local, message: "Already recording!".local, button1: "OK".local).runModal()
return nil
}
if let hires = self.evaluatedArguments!["hires"] as? Bool { UserDefaults.standard.set(hires, forKey: "highRes") }
if let fps = self.evaluatedArguments!["fps"] as? Int { UserDefaults.standard.set(fps, forKey: "frameRate") }
if let cursor = self.evaluatedArguments!["cursor"] as? Bool { UserDefaults.standard.set(cursor, forKey: "showMouse") }
if let sound = self.evaluatedArguments!["sound"] as? Bool { UserDefaults.standard.set(sound, forKey: "recordWinSound") }
if let microphone = self.evaluatedArguments!["microphone"] as? Bool { UserDefaults.standard.set(microphone, forKey: "recordMic") }
if let quality = self.evaluatedArguments!["quality"] as? Int {
if [1,2,3].contains(quality) {
switch quality {
case 1: UserDefaults.standard.set(0.3, forKey: "videoQuality")
case 2: UserDefaults.standard.set(0.7, forKey: "videoQuality")
default: UserDefaults.standard.set(1.0, forKey: "videoQuality")
}
}
}
if let micname = self.evaluatedArguments!["micname"] as? String {
if SCContext.getMicrophone().map({$0.localizedName}).contains(micname) || micname == "default" {
UserDefaults.standard.set(micname, forKey: "micDevice")
}
}
if let hdr = self.evaluatedArguments!["hdr"] as? Bool {
if #available(macOS 15.0, *) {
UserDefaults.standard.set(hdr, forKey: "recordHDR")
}
}
return nil
}
}
4 changes: 4 additions & 0 deletions QuickRecorder/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@
<true/>
</dict>
</array>
<key>NSAppleScriptEnabled</key>
<true/>
<key>OSAScriptingDefinition</key>
<string>Scriptable</string>
<key>SUFeedURL</key>
<string>https://raw.githubusercontent.com/lihaoyun6/QuickRecorder/main/appcast.xml</string>
<key>SUPublicEDKey</key>
Expand Down
Loading

0 comments on commit 34633a6

Please sign in to comment.