]> git.r.bdr.sh - rbdr/captura/commitdiff
Add AppleScript support
authorRuben Beltran del Rio <redacted>
Wed, 2 Aug 2023 08:30:51 +0000 (10:30 +0200)
committerRuben Beltran del Rio <redacted>
Wed, 2 Aug 2023 08:30:51 +0000 (10:30 +0200)
15 files changed:
Captura.xcodeproj/project.pbxproj
Captura.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved [new file with mode: 0644]
Captura/Captura.entitlements
Captura/CapturaApp.swift
Captura/Core Extensions/Notification+AppEvents.swift
Captura/Data/CapturaSettings.swift
Captura/Data/OutputFormatSetting.swift [moved from Captura/Data/SettingsStructs.swift with 73% similarity]
Captura/Info.plist
Captura/Intents/CapturaShortcutsProvider.swift [new file with mode: 0644]
Captura/Intents/GetRemoteCaptures.swift [new file with mode: 0644]
Captura/Presentation/Settings/AdvancedSettings.swift
Captura/Presentation/Settings/OutputSettings.swift
Captura/Scripting/Captura.sdef [new file with mode: 0644]
Captura/Scripting/RecordCommand.swift [new file with mode: 0644]
Captura/Scripting/ScriptedPreferences.swift [new file with mode: 0644]

index 4c6ba9a48e7d24b3af17066e3a03748c3edec52b..35a5e104261396746ce5ad012111f0e1924954fd 100644 (file)
@@ -9,7 +9,7 @@
 /* Begin PBXBuildFile section */
                B5278B172A71528F009F6462 /* HelpPopoverViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5278B162A71528F009F6462 /* HelpPopoverViewController.swift */; };
                B5278B1F2A71BD9B009F6462 /* OutputSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5278B1E2A71BD9B009F6462 /* OutputSettings.swift */; };
-               B5278B212A71BFC3009F6462 /* SettingsStructs.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5278B202A71BFC3009F6462 /* SettingsStructs.swift */; };
+               B5278B212A71BFC3009F6462 /* OutputFormatSetting.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5278B202A71BFC3009F6462 /* OutputFormatSetting.swift */; };
                B5278B232A71C140009F6462 /* PreferencesWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5278B222A71C140009F6462 /* PreferencesWindow.swift */; };
                B5278B252A71CA80009F6462 /* AdvancedSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5278B242A71CA80009F6462 /* AdvancedSettings.swift */; };
                B5278B282A739871009F6462 /* CGImage+resize.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5278B272A739871009F6462 /* CGImage+resize.swift */; };
                B5278B422A779CDB009F6462 /* BackendResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5278B412A779CDB009F6462 /* BackendResponse.swift */; };
                B5278B452A77D924009F6462 /* CaptureSessionConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5278B442A77D924009F6462 /* CaptureSessionConfiguration.swift */; };
                B5278B472A77E8D7009F6462 /* CapturaURLDecoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5278B462A77E8D7009F6462 /* CapturaURLDecoder.swift */; };
+               B5278B4B2A7995AA009F6462 /* CloudStorage in Frameworks */ = {isa = PBXBuildFile; productRef = B5278B4A2A7995AA009F6462 /* CloudStorage */; };
+               B5278B4E2A799AA3009F6462 /* GetRemoteCaptures.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5278B4D2A799AA3009F6462 /* GetRemoteCaptures.swift */; };
+               B55403E72A79A08C004BCBAB /* CapturaShortcutsProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = B55403E62A79A08C004BCBAB /* CapturaShortcutsProvider.swift */; };
+               B55403EA2A79A434004BCBAB /* ScriptedPreferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = B55403E92A79A434004BCBAB /* ScriptedPreferences.swift */; };
+               B55403EB2A7A2AD2004BCBAB /* Captura.sdef in Resources */ = {isa = PBXBuildFile; fileRef = B5278B482A781B78009F6462 /* Captura.sdef */; };
+               B55403ED2A7A388B004BCBAB /* RecordCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = B55403EC2A7A388B004BCBAB /* RecordCommand.swift */; };
                B55DDFCC2A6F0253001A5E76 /* Notification+AppEvents.swift in Sources */ = {isa = PBXBuildFile; fileRef = B55DDFCB2A6F0253001A5E76 /* Notification+AppEvents.swift */; };
                B55DDFCE2A6F069D001A5E76 /* RecordingWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = B55DDFCD2A6F069D001A5E76 /* RecordingWindow.swift */; };
                B56C70CD2A6EFDF4009B97EB /* CaptureState.swift in Sources */ = {isa = PBXBuildFile; fileRef = B56C70CC2A6EFDF4009B97EB /* CaptureState.swift */; };
@@ -56,7 +62,7 @@
 /* Begin PBXFileReference section */
                B5278B162A71528F009F6462 /* HelpPopoverViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HelpPopoverViewController.swift; sourceTree = "<group>"; };
                B5278B1E2A71BD9B009F6462 /* OutputSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OutputSettings.swift; sourceTree = "<group>"; };
-               B5278B202A71BFC3009F6462 /* SettingsStructs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsStructs.swift; sourceTree = "<group>"; };
+               B5278B202A71BFC3009F6462 /* OutputFormatSetting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OutputFormatSetting.swift; sourceTree = "<group>"; };
                B5278B222A71C140009F6462 /* PreferencesWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesWindow.swift; sourceTree = "<group>"; };
                B5278B242A71CA80009F6462 /* AdvancedSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdvancedSettings.swift; sourceTree = "<group>"; };
                B5278B272A739871009F6462 /* CGImage+resize.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CGImage+resize.swift"; sourceTree = "<group>"; };
                B5278B432A77B43A009F6462 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
                B5278B442A77D924009F6462 /* CaptureSessionConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CaptureSessionConfiguration.swift; sourceTree = "<group>"; };
                B5278B462A77E8D7009F6462 /* CapturaURLDecoder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CapturaURLDecoder.swift; sourceTree = "<group>"; };
+               B5278B482A781B78009F6462 /* Captura.sdef */ = {isa = PBXFileReference; lastKnownFileType = text; path = Captura.sdef; sourceTree = "<group>"; };
+               B5278B4D2A799AA3009F6462 /* GetRemoteCaptures.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetRemoteCaptures.swift; sourceTree = "<group>"; };
+               B55403E62A79A08C004BCBAB /* CapturaShortcutsProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CapturaShortcutsProvider.swift; sourceTree = "<group>"; };
+               B55403E92A79A434004BCBAB /* ScriptedPreferences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScriptedPreferences.swift; sourceTree = "<group>"; };
+               B55403EC2A7A388B004BCBAB /* RecordCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecordCommand.swift; sourceTree = "<group>"; };
                B55DDFCB2A6F0253001A5E76 /* Notification+AppEvents.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Notification+AppEvents.swift"; sourceTree = "<group>"; };
                B55DDFCD2A6F069D001A5E76 /* RecordingWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecordingWindow.swift; sourceTree = "<group>"; };
                B56C70CC2A6EFDF4009B97EB /* CaptureState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CaptureState.swift; sourceTree = "<group>"; };
                        isa = PBXFrameworksBuildPhase;
                        buildActionMask = 2147483647;
                        files = (
+                               B5278B4B2A7995AA009F6462 /* CloudStorage in Frameworks */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                B5278B1C2A71BD3C009F6462 /* Data */ = {
                        isa = PBXGroup;
                        children = (
-                               B5278B202A71BFC3009F6462 /* SettingsStructs.swift */,
+                               B5278B202A71BFC3009F6462 /* OutputFormatSetting.swift */,
                                B5278B292A73992D009F6462 /* GifRenderer.swift */,
                                B5278B2B2A739B3A009F6462 /* CapturaFile.swift */,
                                B5F9155A2A6EF80E007ECE8E /* CapturaRemoteFile+name.swift */,
                        path = Domain;
                        sourceTree = "<group>";
                };
+               B5278B4C2A799A90009F6462 /* Intents */ = {
+                       isa = PBXGroup;
+                       children = (
+                               B5278B4D2A799AA3009F6462 /* GetRemoteCaptures.swift */,
+                               B55403E62A79A08C004BCBAB /* CapturaShortcutsProvider.swift */,
+                       );
+                       path = Intents;
+                       sourceTree = "<group>";
+               };
+               B55403E82A79A3D5004BCBAB /* Scripting */ = {
+                       isa = PBXGroup;
+                       children = (
+                               B5278B482A781B78009F6462 /* Captura.sdef */,
+                               B55403E92A79A434004BCBAB /* ScriptedPreferences.swift */,
+                               B55403EC2A7A388B004BCBAB /* RecordCommand.swift */,
+                       );
+                       path = Scripting;
+                       sourceTree = "<group>";
+               };
                B5F915452A6EF80D007ECE8E = {
                        isa = PBXGroup;
                        children = (
                B5F915502A6EF80D007ECE8E /* Captura */ = {
                        isa = PBXGroup;
                        children = (
+                               B55403E82A79A3D5004BCBAB /* Scripting */,
+                               B5278B4C2A799A90009F6462 /* Intents */,
                                B5278B432A77B43A009F6462 /* Info.plist */,
                                B5F915512A6EF80D007ECE8E /* CapturaApp.swift */,
                                B5278B262A739862009F6462 /* Core Extensions */,
                        );
                        name = Captura;
                        packageProductDependencies = (
+                               B5278B4A2A7995AA009F6462 /* CloudStorage */,
                        );
                        productName = Captura;
                        productReference = B5F9154E2A6EF80D007ECE8E /* Captura.app */;
                        );
                        mainGroup = B5F915452A6EF80D007ECE8E;
                        packageReferences = (
+                               B5278B492A7995AA009F6462 /* XCRemoteSwiftPackageReference "CloudStorage" */,
                        );
                        productRefGroup = B5F9154F2A6EF80D007ECE8E /* Products */;
                        projectDirPath = "";
                        files = (
                                B5F915592A6EF80E007ECE8E /* Preview Assets.xcassets in Resources */,
                                B5F915562A6EF80E007ECE8E /* Assets.xcassets in Resources */,
+                               B55403EB2A7A2AD2004BCBAB /* Captura.sdef in Resources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                                B5278B382A73D1EE009F6462 /* CapturaSettings.swift in Sources */,
                                B5278B282A739871009F6462 /* CGImage+resize.swift in Sources */,
                                B5278B422A779CDB009F6462 /* BackendResponse.swift in Sources */,
+                               B5278B4E2A799AA3009F6462 /* GetRemoteCaptures.swift in Sources */,
                                B5278B452A77D924009F6462 /* CaptureSessionConfiguration.swift in Sources */,
                                B5278B2C2A739B3A009F6462 /* CapturaFile.swift in Sources */,
+                               B55403E72A79A08C004BCBAB /* CapturaShortcutsProvider.swift in Sources */,
                                B5278B1F2A71BD9B009F6462 /* OutputSettings.swift in Sources */,
                                B5278B2A2A73992D009F6462 /* GifRenderer.swift in Sources */,
-                               B5278B212A71BFC3009F6462 /* SettingsStructs.swift in Sources */,
+                               B5278B212A71BFC3009F6462 /* OutputFormatSetting.swift in Sources */,
                                B55DDFCE2A6F069D001A5E76 /* RecordingWindow.swift in Sources */,
+                               B55403ED2A7A388B004BCBAB /* RecordCommand.swift in Sources */,
                                B5278B402A744297009F6462 /* Persistence.swift in Sources */,
                                B55DDFCC2A6F0253001A5E76 /* Notification+AppEvents.swift in Sources */,
                                B5F9155B2A6EF80E007ECE8E /* CapturaRemoteFile+name.swift in Sources */,
                                B5278B252A71CA80009F6462 /* AdvancedSettings.swift in Sources */,
                                B5278B362A73B3AA009F6462 /* CapturaCaptureSession.swift in Sources */,
                                B5278B3E2A74420F009F6462 /* Captura.xcdatamodeld in Sources */,
+                               B55403EA2A79A434004BCBAB /* ScriptedPreferences.swift in Sources */,
                                B5F915522A6EF80D007ECE8E /* CapturaApp.swift in Sources */,
                                B5278B472A77E8D7009F6462 /* CapturaURLDecoder.swift in Sources */,
                        );
                };
 /* End XCConfigurationList section */
 
+/* Begin XCRemoteSwiftPackageReference section */
+               B5278B492A7995AA009F6462 /* XCRemoteSwiftPackageReference "CloudStorage" */ = {
+                       isa = XCRemoteSwiftPackageReference;
+                       repositoryURL = "https://github.com/nonstrict-hq/CloudStorage";
+                       requirement = {
+                               kind = upToNextMajorVersion;
+                               minimumVersion = 0.4.0;
+                       };
+               };
+/* End XCRemoteSwiftPackageReference section */
+
+/* Begin XCSwiftPackageProductDependency section */
+               B5278B4A2A7995AA009F6462 /* CloudStorage */ = {
+                       isa = XCSwiftPackageProductDependency;
+                       package = B5278B492A7995AA009F6462 /* XCRemoteSwiftPackageReference "CloudStorage" */;
+                       productName = CloudStorage;
+               };
+/* End XCSwiftPackageProductDependency section */
+
 /* Begin XCVersionGroup section */
                B5278B3C2A74420F009F6462 /* Captura.xcdatamodeld */ = {
                        isa = XCVersionGroup;
diff --git a/Captura.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Captura.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
new file mode 100644 (file)
index 0000000..c5bc2ce
--- /dev/null
@@ -0,0 +1,14 @@
+{
+  "pins" : [
+    {
+      "identity" : "cloudstorage",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/nonstrict-hq/CloudStorage",
+      "state" : {
+        "revision" : "35700218d35579df091974b6e7765db94c83448b",
+        "version" : "0.4.0"
+      }
+    }
+  ],
+  "version" : 2
+}
index 2391ad2592b5cd42188403af857e3b0a1b792873..f6cc65ed7f8d9b4aa170c04fd0b6cb873219fcab 100644 (file)
@@ -12,6 +12,8 @@
        <array>
                <string>CloudKit</string>
        </array>
+       <key>com.apple.developer.ubiquity-kvstore-identifier</key>
+       <string>$(TeamIdentifierPrefix)$(CFBundleIdentifier)</string>
        <key>com.apple.security.app-sandbox</key>
        <true/>
        <key>com.apple.security.assets.pictures.read-write</key>
index eb6f02ecb264a24147165745a7944435a176ca9b..c6ec9f05c6316a5074a02aa20a1a85a899a3c1f4 100644 (file)
@@ -39,6 +39,8 @@ class CapturaAppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate {
   var remoteFiles: [CapturaRemoteFile] = []
   var captureSessionConfiguration: CaptureSessionConfiguration = CaptureSessionConfiguration()
   
+  @objc var scriptedPreferences: ScriptedPreferences?
+  
   func applicationDidFinishLaunching(_ notification: Notification) {
     setupStatusBar()
     NotificationCenter.default.addObserver(
@@ -48,6 +50,8 @@ class CapturaAppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate {
       object: nil)
     closeWindow()
     fetchRemoteItems()
+    
+    scriptedPreferences = ScriptedPreferences()
   }
   
   // MARK: - Setup Functions
@@ -108,7 +112,10 @@ class CapturaAppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate {
             case let .configure(config):
               CapturaSettings.apply(config)
             case let .record(config):
-              captureSessionConfiguration = CaptureSessionConfiguration(from: config)
+              let config = CaptureSessionConfiguration(from: config)
+            NotificationCenter.default.post(name: .startAreaSelection, object: nil, userInfo: [
+              "config": config
+            ])
               NotificationCenter.default.post(name: .startAreaSelection, object: nil, userInfo: nil)
           }
         }
@@ -196,6 +203,14 @@ class CapturaAppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate {
       if let frame = notification.userInfo?["frame"] {
         receivedFrame(frame as! CVImageBuffer)
       }
+    case .reloadConfiguration:
+      reloadConfiguration()
+    case .setCaptureSessionConfiguration:
+      if let userInfo = notification.userInfo {
+        if let config = userInfo["config"] as? RecordAction {
+          setCaptureSessionConfiguration(config)
+        }
+      }
     case .NSManagedObjectContextObjectsDidChange:
       DispatchQueue.main.async {
         self.fetchRemoteItems()
@@ -324,6 +339,14 @@ class CapturaAppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate {
     }
   }
   
+  func reloadConfiguration() {
+    self.captureSessionConfiguration = CaptureSessionConfiguration()
+  }
+  
+  func setCaptureSessionConfiguration(_ config: RecordAction) {
+    self.captureSessionConfiguration = CaptureSessionConfiguration(from: config)
+  }
+  
   // MARK: - CoreData
   
   private func fetchRemoteItems() {
index 0a0f820dde0a70402baaf8054611dcb99e78d06a..82b46e11bcfb2b2cee23276cd9a3139ca81bc718 100644 (file)
@@ -9,4 +9,6 @@ extension Notification.Name {
   static let failedToStart = Notification.Name("failedToStart")
   static let receivedFrame = Notification.Name("receivedFrame")
   static let failedtoUpload = Notification.Name("failedToUpload")
+  static let reloadConfiguration = Notification.Name("reloadConfiguration")
+  static let setCaptureSessionConfiguration = Notification.Name("setCaptureSessionConfiguration")
 }
index 85368ea0695240404ce303ef403f6ccd82bed62e..2d0e70b63bfe80babfecbcbd67fba88a938eba72 100644 (file)
@@ -3,19 +3,23 @@ import Foundation
 struct CapturaSettings {
   static var frameRate: Int {
     get {
-      UserDefaults.standard.integer(forKey: "frameRate")
+      if NSUbiquitousKeyValueStore.default.object(forKey: "frameRate") == nil {
+        return 10
+      } else {
+        return min(10, max(4, Int(NSUbiquitousKeyValueStore.default.longLong(forKey: "frameRate"))))
+      }
     }
     set {
-      UserDefaults.standard.setValue(newValue, forKey: "frameRate")
+      NSUbiquitousKeyValueStore.default.setValue(newValue, forKey: "frameRate")
     }
   }
   
   static var outputFormats: OutputFormatSetting {
     get {
-      OutputFormatSetting(rawValue: UserDefaults.standard.integer(forKey: "outputFormats")) ?? .all
+      OutputFormatSetting(rawValue: Int(NSUbiquitousKeyValueStore.default.longLong(forKey: "outputFormats"))) ?? .all
     }
     set {
-      UserDefaults.standard.setValue(newValue.rawValue, forKey: "outputFormats")
+      NSUbiquitousKeyValueStore.default.setValue(newValue.rawValue, forKey: "outputFormats")
     }
   }
   
@@ -41,36 +45,45 @@ struct CapturaSettings {
   
   static var backend: URL? {
     get {
-      if let url = UserDefaults.standard.string(forKey: "backendUrl") {
+      if let url = NSUbiquitousKeyValueStore.default.string(forKey: "backendUrl") {
         return URL(string: url)
       }
       return nil
     }
     set {
-      UserDefaults.standard.setValue(newValue?.absoluteString, forKey: "backendUrl")
+      NSUbiquitousKeyValueStore.default.setValue(newValue?.absoluteString, forKey: "backendUrl")
     }
   }
   
   static var backendFormat: OutputFormatSetting {
     get {
-      OutputFormatSetting(rawValue: UserDefaults.standard.integer(forKey: "backendFormat")) ?? .gifOnly
+      OutputFormatSetting(rawValue: Int(NSUbiquitousKeyValueStore.default.longLong(forKey: "backendFormat"))) ?? .gifOnly
     }
     set {
-      UserDefaults.standard.setValue(newValue.rawValue, forKey: "backendFormat")
+      NSUbiquitousKeyValueStore.default.setValue(newValue.rawValue, forKey: "backendFormat")
     }
   }
   
   static var shouldKeepLocalFiles: Bool {
     get {
-      UserDefaults.standard.bool(forKey: "keepFiles")
+      if NSUbiquitousKeyValueStore.default.object(forKey: "keepFiles") == nil {
+        return true
+      } else {
+        return NSUbiquitousKeyValueStore.default.bool(forKey: "keepFiles")
+      }
     }
     set {
-      UserDefaults.standard.set(newValue, forKey: "keepFiles")
+      NSUbiquitousKeyValueStore.default.set(newValue, forKey: "keepFiles")
     }
   }
   
   static var shouldAllowURLAutomation: Bool {
-    UserDefaults.standard.bool(forKey: "allowURLAutomation")
+    get {
+      NSUbiquitousKeyValueStore.default.bool(forKey: "allowURLAutomation")
+    }
+    set {
+      NSUbiquitousKeyValueStore.default.setValue(newValue, forKey: "allowURLAutomation")
+    }
   }
   
   static func apply(_ config: ConfigureAction) {
similarity index 73%
rename from Captura/Data/SettingsStructs.swift
rename to Captura/Data/OutputFormatSetting.swift
index 16172cad331e17fe0d5e57c72b149d11d2f15d08..211a08e596c8f9017b480c6190544531907ebded 100644 (file)
@@ -25,4 +25,15 @@ enum OutputFormatSetting: Int {
   func shouldSaveMp4() -> Bool {
     return self == .mp4Only || self == .all
   }
+  
+  func toString() -> String {
+    switch(self) {
+    case .gifOnly:
+      return "gif"
+    case .mp4Only:
+      return "mp4"
+    case .all:
+      return "all"
+    }
+  }
 }
index 6110e45bad87250097b917aaede32eaa1d8cd5e9..17891551f310b240dc914d9496417fcbb6fcb904 100644 (file)
@@ -11,5 +11,9 @@
                        </array>
                </dict>
        </array>
+       <key>NSAppleScriptEnabled</key>
+       <true/>
+       <key>OSAScriptingDefinition</key>
+       <string>Captura.sdef</string>
 </dict>
 </plist>
diff --git a/Captura/Intents/CapturaShortcutsProvider.swift b/Captura/Intents/CapturaShortcutsProvider.swift
new file mode 100644 (file)
index 0000000..2727325
--- /dev/null
@@ -0,0 +1,10 @@
+import AppIntents
+
+struct CapturaShortcutsProvider: AppShortcutsProvider {
+
+    static var appShortcuts: [AppShortcut] {
+
+        AppShortcut(intent: GetRemoteCaptures(), phrases: ["Get \(.applicationName) remote captures"])
+
+    }
+}
diff --git a/Captura/Intents/GetRemoteCaptures.swift b/Captura/Intents/GetRemoteCaptures.swift
new file mode 100644 (file)
index 0000000..58d9583
--- /dev/null
@@ -0,0 +1,29 @@
+import AppIntents
+import CoreData
+
+struct GetRemoteCaptures: AppIntent {
+    static var title: LocalizedStringResource = "Get remote captures"
+    
+    static var description =
+        IntentDescription("Return a list of remote captures")
+  
+  @Parameter(title: "Count") var count: Int?
+  
+  static var parameterSummary: some ParameterSummary {
+      Summary("Get \(\.$count) latest captures.")
+  }
+  
+  func perform() async throws -> some IntentResult & ReturnsValue {
+    let viewContext = PersistenceController.shared.container.viewContext
+    let fetchRequest = NSFetchRequest<CapturaRemoteFile>(entityName: "CapturaRemoteFile")
+    fetchRequest.fetchLimit = min(10, max(1, count ?? 5))
+    fetchRequest.sortDescriptors = [NSSortDescriptor(key: "timestamp", ascending: false)]
+    
+    let results = await viewContext.perform {
+      return try? viewContext.fetch(fetchRequest)
+    }
+    
+    let finalResults = results?.compactMap { URL(string: $0.url ?? "")} ?? []
+    return .result(value: finalResults)
+  }
+}
index 8799436781ff8a7a4e86bcde88b5a0faf12feecc..66314b481582ced4359fa2a2e8b2bc1f39c92a91 100644 (file)
@@ -1,13 +1,16 @@
 import SwiftUI
+import CloudStorage
 
 struct AdvancedSettings: View {
   
-  @AppStorage("backendUrl") var backendUrl: String = ""
-  @AppStorage("backendFormat") var outputFormats: OutputFormatSetting = .gifOnly
-  @AppStorage("keepFiles") var keepFiles = true
-  @AppStorage("allowURLAutomation") var allowURLAutomation = false
+  @CloudStorage("backendUrl") var backendUrl: String = ""
+  @CloudStorage("backendFormat") var backendFormat: OutputFormatSetting = .gifOnly
+  @CloudStorage("keepFiles") var keepFiles = true
+  @CloudStorage("allowURLAutomation") var allowURLAutomation = false
   @State var showConfirmation = false
   
+  private var anyState: String { "\(backendUrl), \(backendFormat), \(keepFiles), \(allowURLAutomation)" }
+  
   var parsedBackendUrl: URL? {
     URL(string: backendUrl)
   }
@@ -21,7 +24,7 @@ struct AdvancedSettings: View {
               TextField("", text: $backendUrl).font(.body)
             }.font(.headline)
               .help("The Backend URL to use. If this is empty, no backend will be used and the options below won't have an effect.")
-            Picker(selection: $outputFormats, label: Text("Backend Format").font(.headline)) {
+            Picker(selection: $backendFormat, label: Text("Backend Format").font(.headline)) {
               Text("GIF")
                 .tag(OutputFormatSetting.gifOnly)
                 .padding(.horizontal, 4.0)
@@ -72,6 +75,9 @@ struct AdvancedSettings: View {
         Spacer()
       }
     }
+    .onChange(of: anyState) { _ in
+      NotificationCenter.default.post(name: .reloadConfiguration, object: nil, userInfo: nil)
+    }
   }
 }
 
index df125419bbd9b945e50f61d5837233b6add7fc06..cce34ead395f64153bd52c3a7f587745310ab140 100644 (file)
@@ -1,9 +1,12 @@
 import SwiftUI
+import CloudStorage
 
 struct OutputSettings: View {
   
-  @AppStorage("outputFormats") var outputFormats: OutputFormatSetting = .all
-  @AppStorage("frameRate") var frameRate = 10.0
+  @CloudStorage("outputFormats") var outputFormats: OutputFormatSetting = .all
+  @CloudStorage("frameRate") var frameRate = 10.0
+  
+  private var anyState: String { "\(outputFormats), \(frameRate)" }
   
   var body: some View {
     Form {
@@ -35,6 +38,9 @@ struct OutputSettings: View {
       }
       Spacer()
     }
+    .onChange(of: anyState) { _ in
+      NotificationCenter.default.post(name: .reloadConfiguration, object: nil, userInfo: nil)
+    }
   }
 }
 
diff --git a/Captura/Scripting/Captura.sdef b/Captura/Scripting/Captura.sdef
new file mode 100644 (file)
index 0000000..82cf06f
--- /dev/null
@@ -0,0 +1,40 @@
+<!DOCTYPE dictionary SYSTEM "file://localhost/System/Library/DTDs/sdef.dtd">
+<dictionary title="CapturaScripting">
+    <suite name="Captura Suite" code="CAPT" description="Controls Captura">
+      <class name="application" code="Capp" description="The application's top-level scripting object.">
+        <cocoa class="NSApplication"/>
+        <property name="preferences" code="Cprp" type="preferences" access="r">
+          <cocoa key="scriptedPreferences"/>
+        </property>
+      </class>
+      <class name="preferences" code="Cprf" description="The application preferences">
+        <cocoa class="ScriptedPreferences"/>
+        <property name="fps" code="Cpfp" type="integer" description="Sets the FPS of the recording" access="rw" />
+        <property name="outputs" code="Cpou" type="text" description="Which outputs get generated locally" access="rw" />
+        <property name="backend" code="Cpbk" type="file url" description="Updates the backend URL that will be used" access="rw" />
+        <property name="backend_output" code="Cpbo" type="text" description="Which output should be sent to the backend" access="rw" />
+        <property name="keep_local_files" code="Cplf" type="boolean" description="Whether to keep local files after a successful backend upload." access="rw"/>
+      </class>
+      <record-type code="CCRr" name="recording_configuration">
+          <property name="x" code="CCrx" type="integer" description="Sets the starting horizontal position of the recording frame from the left of the screen. Defaults to the center of the screen." />
+          <property name="y" code="CCry" type="integer" description="Sets the starting vertical position of the recording frame from the bottom of the screen. Defaults to the center of the screen." />
+          <property name="width" code="CCrw" type="integer" description="Sets the width of the recording frame. Defaults to 400" />
+          <property name="height" code="CCrh" type="integer" description="Sets the height of the recording frame. Defaults to 400" />
+          <property name="prevent_resize" code="CCRz" type="boolean" description="Prevents the recording frame from being resized. Defaults to false." />
+          <property name="prevent_move" code="CCRm" type="boolean" description="Prevents the recording frame from being moved. Defaults to false." />
+          <property name="fps" code="CCrf" type="integer" description="Sets the FPS of the recording" />
+          <property name="outputs" code="CCRo" type="text" description="Which outputs get generated locally" />
+          <property name="backend" code="CCRb" type="file url" description="Updates the backend URL that will be used" />
+          <property name="backend_output" code="CCRp" type="text" description="Which output should be sent to the backend" />
+          <property name="keep_local_files" code="CCRk" type="boolean" description="Whether to keep local files after a successful backend upload." />
+          <property name="auto_start" code="CCRa" type="boolean" description="Whether the recording session should start automatically or not. It takes 3 seconds to start." />
+          <property name="max_length" code="CCrl" type="integer" description="The max length for the recording in seconds, default and max is 300." />
+      </record-type>
+      <command name="start_recording" code="CCCMStRc">
+        <cocoa class="RecordCommand" />
+        <direct-parameter description="The settings to be configured">
+          <type type="recording_configuration" />
+        </direct-parameter>
+      </command>
+    </suite>
+</dictionary>
diff --git a/Captura/Scripting/RecordCommand.swift b/Captura/Scripting/RecordCommand.swift
new file mode 100644 (file)
index 0000000..8db2656
--- /dev/null
@@ -0,0 +1,64 @@
+import Foundation
+
+@objc(RecordCommand)
+class RecordCommand: NSScriptCommand {
+    override func performDefaultImplementation() -> Any? {
+      print("AAAH \(self.directParameter)")
+      
+      guard let args = self.directParameter as? [String: Any] else {
+          return nil
+      }
+      
+      print("AAH COMMANDS \(args)")
+      
+      // Here you can extract the parameters from the args dictionary and configure your settings
+      let x = args["x"] as? Int
+      let y = args["y"] as? Int
+      let width = args["width"] as? Int
+      let height = args["height"] as? Int
+      let preventResize = args["prevent_resize"] as? Bool
+      let preventMove = args["prevent_move"] as? Bool
+      let fps = args["fps"] as? Int
+      let outputs = OutputFormatSetting(args["outputs"] as? String ?? "")
+      let backend = URL(string: args["backend"] as? String ?? "")
+      let backendOutput = OutputFormatSetting(args["backend_output"] as? String ?? "")
+      let keepLocalFiles = args["keep_local_files"] as? Bool
+      let autoStart = args["auto_start"] as? Bool
+      let maxLength = args["max_length"] as? Int
+      
+      print("AAH WIDTH \(width)")
+      
+      var skipBackend = false
+      if let backendString = args["backend"] as? String {
+        if backendString == "" {
+          skipBackend = true
+        }
+      }
+    
+      let config = RecordAction(
+        action: "record",
+        x: x,
+        y: y,
+        width: width,
+        height: height,
+        preventResize: preventResize,
+        preventMove: preventMove,
+        fps: fps,
+        outputs: outputs,
+        backend: backend,
+        backendOutput: backendOutput,
+        keepLocalFiles: keepLocalFiles,
+        autoStart: autoStart,
+        skipBackend: skipBackend,
+        maxLength: maxLength
+      )
+    
+      NotificationCenter.default.post(name: .setCaptureSessionConfiguration, object: nil, userInfo: [
+        "config": config
+      ])
+      NotificationCenter.default.post(name: .startAreaSelection, object: nil, userInfo: nil)
+
+        // Return a result if needed
+        return nil
+    }
+}
diff --git a/Captura/Scripting/ScriptedPreferences.swift b/Captura/Scripting/ScriptedPreferences.swift
new file mode 100644 (file)
index 0000000..b6aa719
--- /dev/null
@@ -0,0 +1,47 @@
+import Foundation
+
+class ScriptedPreferences: NSObject {
+  @objc dynamic var fps: Int {
+    get {
+      CapturaSettings.frameRate
+    }
+    set {
+      CapturaSettings.frameRate = newValue
+    }
+  }
+  @objc dynamic var outputs: String {
+    get {
+      CapturaSettings.outputFormats.toString()
+    }
+    set {
+      CapturaSettings.outputFormats = OutputFormatSetting(newValue) ?? .gifOnly
+    }
+  }
+  
+  @objc dynamic var backend: String {
+    get {
+      CapturaSettings.backend?.absoluteString ?? ""
+    }
+    set {
+      CapturaSettings.backend = URL(string: newValue)
+    }
+  }
+  
+  @objc dynamic var backend_output: String {
+    get {
+      CapturaSettings.backendFormat.toString()
+    }
+    set {
+      CapturaSettings.backendFormat = OutputFormatSetting(newValue) ?? .gifOnly
+    }
+  }
+  
+  @objc dynamic var keep_local_files: Bool {
+    get {
+      CapturaSettings.shouldKeepLocalFiles
+    }
+    set {
+      CapturaSettings.shouldKeepLocalFiles = newValue
+    }
+  }
+}