Applescript -- make capture methodology for videos match the process used for still photos
Hi -- I wanted to put in a request to streamline the method by which requests for continuous capture videos are obtained in Applescript. The method for capturing photos is super easy and clean, but requests for videos are very tricky and require a lot of additional gymnastics if you're needing to put the file in a particular place or give it a specific name.
For capturing still photos, the process is very simple and straightforward:
-------------------
property my_camera_name: "" -- REPLACE WITH THE NAME OF THE CAMERA AS LABELED IN SECURITY SPY
property PosixPath : "" -- REPLACE WITH THE POSIX PATH TO WHERE YOU'D LIKE THE STILL IMAGE TO BE SAVED.
-- Note when capturing still photos that SecuritySpy REQUIRES a POSIX-style path. If you try to use an HFS-style path, the script will throw an Error -120.
tell application "SecuritySpy"
capture image as PosixPath camera name my_camera_name with overwrite
end tell
-------------------
So far, so good. You propose a location where the photo will be stored (in the above script, it's PosixPath), and SecuritySpy writes it there. No muss, no fuss.
Unfortunately, when you're capturing video, the process is completely different and much, much more difficult. The command, also, is different:
--------------------------------
tell application "SecuritySpy"
arm camera number 0 mode ContinuousCapture -- starts recording a continuous capture video
delay 3 -- or however long you'd like your video to be
disarm camera number 0 mode ContinuousCapture -- shuts camera off, closes file
end tell
----------------------------------
The first thing to note is that when capturing video with Applescript: you do not have the option to tell SecuritySpy *where* to save the file. By default, the file will be saved in the location specified in SecuritySpy's preferences. Not necessarily a big deal by itself, because it just means you have to set the path in the script to match the path in SecuritySpy's preferences.
The next issue is where things start to get complicated. You also do not have the option to tell SS *what* the incoming video file should be called. By default, SS will use the naming convention established in the SS preferences, usually something like [date] [time with seconds] C [name of camera].m4v
====================
[AS AN ASIDE, this file structure is very, very tricky to pick up cleanly in Applescript:
1. You have to write code to determine what the current date is, and express it in the format SS prefers. Not too bad by itself.
2. Since SS creates a new folder each time the calendar turns over to the next day, the path to the automatically created files changes, too. So you can't simply declare the enclosing folder where the ingest files will be located, but rather have to declare the folder one level ABOVE THAT (the one you've specified in SS's preferences) and write date handling code to interpret the next level down -- the folder for the current day's files -- before you can look for the file you want.
3. SS and Applescript treat leading zeros in the date differently. So while SS tacks a leading zero onto single-digit days and months, e.g. "02" for February, when you ask Applescript to return the month as an integer, it gives you a single digit, e.g. "2" for February. This can be trapped in the code, but it's more code to write and execute each time we're looking for the currently recording file.
4. The videos are next labeled with the time, down to the seconds. Assuming (I haven't checked this) that SS and the cameras keep their clocks in sync with the Mac, trying to guess in script means going all the way down to the second looking for a matching filename -- IF the times line up correctly.
It makes perfect sense that the videos are labeled this way -- it makes it easy to find them in the file system, and they line up correctly, etc. But my point is simply that if you're trying to glean file names by guessing when a file was created, you've got a LOT of gymnastics to do in your code].
=====================
Now, fortunately, we don't usually have to deal with all of the above, because in the current SS Applescript dictionary we have the option to ask SS what the name of the currently-recording video file is. The Applescript to do that is:
get current continuous capture file camera number
Great, you're thinking: I can just ask SS what the name of the current file is, and then manipulate it further as I need. That IS, in fact, the way you do it, but it's still way harder than it should be. Why? For a couple of reasons.
First, unlike with still photos, SS DOES NOT allow you to propose filenames in Applescript for video files as they're being captured. So there's no equivalent of the "capture image as " command I laid out above. That's the biggest issue and really, I think, the thing I'm asking for here.
The other problem, though, has to do with what SS reports to you when you "get current continuous capture file." Take a look at the following script:
--------------------------------
property rawcapturefile : "" -- name of the capture file as proposed by SS
tell application "SecuritySpy"
arm camera number 0 mode ContinuousCapture
delay 2 -- note that it seems to take SS a beat or two to generate the capture file's name. Adjust this delay depending on your system.
get current continuous capture file camera number 0
set rawcapturefile to result -- drop SS's response into a container variable
set rawcapturefile to a reference to file rawcapturefile -- since this file does not yet exist (it's being written at the time this command is invoked and its file extension is .unf) the variable must be coerced into a reference or the script will choke
delay 3 -- or however long you'd like your video to be. Note that the total time will include the 2 second delay included above.
disarm camera number 0 mode ContinuousCapture
end tell
----------------------------------
A couple of things:
1. You can only ask for the name of the CURRENT capture file WHILE the file is recording. If the recording hasn't started yet, or the recording has already ended, the script throws an error.
2. As I note in my code above, it takes SS a beat to report the filename it's currently recording to. You have to include a short delay (in my example, 2 seconds), which slows the cycle down.
3. As I also note in my file above, the other MAJOR "gotcha" that SS throws at you is that when you ask it to report the name of the currently reporting file, it will tell you something like "2017-02-14 14-30-04 C CamA.m4v" BUT you can't actually do anything with that filename yet, because the actual file on disk has an extension of ".unf" (unfinished) RIGHT UP UNTIL it stops recording. So in reality, asking for the current capture file yields you what the file name WILL be when the recording stops. If you don't handle it correctly as "a reference to" as I've described above, the script will choke.
4. And then, one last gotcha: once the file has been closed, it's no longer "current" and SS can NOT tell you what file it's just saved. So if you forget to ask while it's actually being recorded, the file is already dead to SS.
To summarize, you have to ask SS what file it's currently recording, but a) not do it too quickly after the file starts recording, b) only while a file is actually recording, recognizing that c) the file name SS reports back to you, by definition, does not yet exist on disk at the time you're asking for it.
This is way too much work. Would it be possible to simply add a parallel "capture continuous as }" command, so that videos can be captured in the same way we capture still images? In my current application, it would shave several seconds off the cycle time and eliminate about 4-5 failure points in my script.
Matt
For capturing still photos, the process is very simple and straightforward:
-------------------
property my_camera_name: "" -- REPLACE WITH THE NAME OF THE CAMERA AS LABELED IN SECURITY SPY
property PosixPath : "" -- REPLACE WITH THE POSIX PATH TO WHERE YOU'D LIKE THE STILL IMAGE TO BE SAVED.
-- Note when capturing still photos that SecuritySpy REQUIRES a POSIX-style path. If you try to use an HFS-style path, the script will throw an Error -120.
tell application "SecuritySpy"
capture image as PosixPath camera name my_camera_name with overwrite
end tell
-------------------
So far, so good. You propose a location where the photo will be stored (in the above script, it's PosixPath), and SecuritySpy writes it there. No muss, no fuss.
Unfortunately, when you're capturing video, the process is completely different and much, much more difficult. The command, also, is different:
--------------------------------
tell application "SecuritySpy"
arm camera number 0 mode ContinuousCapture -- starts recording a continuous capture video
delay 3 -- or however long you'd like your video to be
disarm camera number 0 mode ContinuousCapture -- shuts camera off, closes file
end tell
----------------------------------
The first thing to note is that when capturing video with Applescript: you do not have the option to tell SecuritySpy *where* to save the file. By default, the file will be saved in the location specified in SecuritySpy's preferences. Not necessarily a big deal by itself, because it just means you have to set the path in the script to match the path in SecuritySpy's preferences.
The next issue is where things start to get complicated. You also do not have the option to tell SS *what* the incoming video file should be called. By default, SS will use the naming convention established in the SS preferences, usually something like [date] [time with seconds] C [name of camera].m4v
====================
[AS AN ASIDE, this file structure is very, very tricky to pick up cleanly in Applescript:
1. You have to write code to determine what the current date is, and express it in the format SS prefers. Not too bad by itself.
2. Since SS creates a new folder each time the calendar turns over to the next day, the path to the automatically created files changes, too. So you can't simply declare the enclosing folder where the ingest files will be located, but rather have to declare the folder one level ABOVE THAT (the one you've specified in SS's preferences) and write date handling code to interpret the next level down -- the folder for the current day's files -- before you can look for the file you want.
3. SS and Applescript treat leading zeros in the date differently. So while SS tacks a leading zero onto single-digit days and months, e.g. "02" for February, when you ask Applescript to return the month as an integer, it gives you a single digit, e.g. "2" for February. This can be trapped in the code, but it's more code to write and execute each time we're looking for the currently recording file.
4. The videos are next labeled with the time, down to the seconds. Assuming (I haven't checked this) that SS and the cameras keep their clocks in sync with the Mac, trying to guess in script means going all the way down to the second looking for a matching filename -- IF the times line up correctly.
It makes perfect sense that the videos are labeled this way -- it makes it easy to find them in the file system, and they line up correctly, etc. But my point is simply that if you're trying to glean file names by guessing when a file was created, you've got a LOT of gymnastics to do in your code].
=====================
Now, fortunately, we don't usually have to deal with all of the above, because in the current SS Applescript dictionary we have the option to ask SS what the name of the currently-recording video file is. The Applescript to do that is:
get current continuous capture file camera number
Great, you're thinking: I can just ask SS what the name of the current file is, and then manipulate it further as I need. That IS, in fact, the way you do it, but it's still way harder than it should be. Why? For a couple of reasons.
First, unlike with still photos, SS DOES NOT allow you to propose filenames in Applescript for video files as they're being captured. So there's no equivalent of the "capture image as " command I laid out above. That's the biggest issue and really, I think, the thing I'm asking for here.
The other problem, though, has to do with what SS reports to you when you "get current continuous capture file." Take a look at the following script:
--------------------------------
property rawcapturefile : "" -- name of the capture file as proposed by SS
tell application "SecuritySpy"
arm camera number 0 mode ContinuousCapture
delay 2 -- note that it seems to take SS a beat or two to generate the capture file's name. Adjust this delay depending on your system.
get current continuous capture file camera number 0
set rawcapturefile to result -- drop SS's response into a container variable
set rawcapturefile to a reference to file rawcapturefile -- since this file does not yet exist (it's being written at the time this command is invoked and its file extension is .unf) the variable must be coerced into a reference or the script will choke
delay 3 -- or however long you'd like your video to be. Note that the total time will include the 2 second delay included above.
disarm camera number 0 mode ContinuousCapture
end tell
----------------------------------
A couple of things:
1. You can only ask for the name of the CURRENT capture file WHILE the file is recording. If the recording hasn't started yet, or the recording has already ended, the script throws an error.
2. As I note in my code above, it takes SS a beat to report the filename it's currently recording to. You have to include a short delay (in my example, 2 seconds), which slows the cycle down.
3. As I also note in my file above, the other MAJOR "gotcha" that SS throws at you is that when you ask it to report the name of the currently reporting file, it will tell you something like "2017-02-14 14-30-04 C CamA.m4v" BUT you can't actually do anything with that filename yet, because the actual file on disk has an extension of ".unf" (unfinished) RIGHT UP UNTIL it stops recording. So in reality, asking for the current capture file yields you what the file name WILL be when the recording stops. If you don't handle it correctly as "a reference to" as I've described above, the script will choke.
4. And then, one last gotcha: once the file has been closed, it's no longer "current" and SS can NOT tell you what file it's just saved. So if you forget to ask while it's actually being recorded, the file is already dead to SS.
To summarize, you have to ask SS what file it's currently recording, but a) not do it too quickly after the file starts recording, b) only while a file is actually recording, recognizing that c) the file name SS reports back to you, by definition, does not yet exist on disk at the time you're asking for it.
This is way too much work. Would it be possible to simply add a parallel "capture continuous as }" command, so that videos can be captured in the same way we capture still images? In my current application, it would shave several seconds off the cycle time and eliminate about 4-5 failure points in my script.
Matt
Comments
-
Hi Matt,
Thanks for the detailed report! I certainly see the problem here, but there is a reason for doing it this way.
While the "capture image" command is for the precise task of capturing an image file, the "arm" command is not. When arming ContinuousCapture mode, this arms ALL the features under Preferences -> Cameras -> Continuous Capture - i.e. the movie capture that you are interested in, but also continuous image capture, and also webcam image upload. So it's a different type of command that is not specifically for the task of movie capture.
There is no "capture continuous movie" command because this does not fit within the current model of how SecuritySpy captures movie files, whereby movie files are captured when a particular mode (continuous capture or motion capture) is armed. So there is no way to modify SecuritySpy's AppleScript interface to do what you want without changing the whole model of how SecuritySpy captures movie files.
One solution that other users have come up with is to periodically scan the current day folder (name as date with format YYYY-MM-DD) for any file ending ".m4v", and then move this to a different location and do whatever processing needed. This way, no matter how the capture is initiated or stopped, and no matter what the name of the file is, the script will reliably find it. -
Hi Ben, thanks for the response. All fair points. I think the other thing that's tricky about requesting video files vs. still images is that still images don't require a time attribute, whereas asking for a video with a single command such as "capture continuous movie" would also require the script to pass a time attribute (how long should the video run for?) to SS and make SS hold the file recording open for a set period of time.
As to the solution that's mentioned above -- yes, that's essentially what I'm doing -- I DID get my script to work. But the amount of gymnastics necessary to do so is pretty dramatic compared to how easy it is to capture a still file. In my application, I'm dealing with a lot of approximately 10-15 second long files that I need to record, locate, post-process and display back to the user fairly quickly, so I'm trying to do anything I can to speed the cycle time up.
What about this: would it be possible to add some sort of a specifier to the "arm" command, like "arm camera number 0 to {file}" for individual cameras, or, when generically arming all cameras, something like "arm to {path}"? That would get us a couple of things: a) a lot of downstream code dealing with locating and renaming could be eliminated, because the file would already be where it should ultimately wind up, and b) we would not have to rely on SS to report back to us what the name of the current capture files are (I note your point about scanning the folder for the most recently created file, and I *definitely* use it, but I'd prefer not to because of how often Applescripts get stuck in situations like these, looping for precious seconds waiting for a file that never shows up).
Alternatively, would it be possible to improve SS's handling of the "get current capture" command? As I noted in my original post, if you say "arm" and then immediately ask SS what the file name is, you get an empty response. That feels like an error to me, because yes, at that moment SS is recording, but it will return a null response as if no recording is underway. We can write a "loop while" event into the script, but...that's more gymnastics. I don't know if there's any system variability in how long SS takes to return that information, but wouldn't it be more correct programming-wise for SS to internally take the beat it needs to provide that information, then return the correct name back to Applescript, rather than answering in the negative for a couple of seconds until it catches up?
Another thought: what about some sort of command like "get last capture", something like that, that returns the most-recently completed capture? It's not "current" in the currently recording sense of the word, but it's the last action performed by SS and it would greatly simplify the process of looking backward in recent time for whatever files SS has just created.
And lastly -- given that SS already labels every file it creates with YYYY-MM-DD, PLUS time down to the second, would it be possible to include an option to have SS *not* create YYYY-MM-DD folders below the path we've specified in the preferences? It's nice for organizational purposes, I suppose, but strictly speaking it's unnecessary and forces us to chase the target folder from one day to the next.
Thanks for a great program, Ben! And thank you for humoring me on all my stream of conscious ramblings.
Matt
