My Second script

Hello, this is my first week with applescript and programming in general. I’m learning applescript to automate some mundane parts of my job so I can focus on more interesting work. Although I’ve walked through a tutorial series on Lynda.com and some of the educational posts here, I find that the best way I really learn is by jumping into things.

So, I’ve started working on a script that will automate the process of taking video files from camera sd, CF and XDCam cards, renaming them and ingesting them for editing. I’ve got a few bits of the script to work, but I feel like I’m getting stuck when it comes to referencing the files that are dropped on the application and then manipulating those files in the script. Just to give you an idea of the script, I want the user to be able to drop a camera card over a droplet and then the droplet asks them for information about the video. That information is used to create a file name that I then use to create a .zip of the camera card (with that file name) After that I wanted to sift through the camera card folders and find the video files. (Camera’s often have many layers of hierarchy in their cards where the video file is deep down inside there). I want to name the video files using info from the queries and then I want to copy the video files to a watch folder on the desktop that will get sucked up into Adobe Media encoder. Any advice is appreciated. Not sure if this is too long of a piece of code to post but I figured I’d give it a try.

-- I want to have someone take a camera card and drag it over the droplet.
property movieFiles : {"mov", "m4v", "MXF", "mp4"}
on open these_items
	(choose from list {"SHOW_A", "SHOW_B", "SHOW_C"} ¬
		with prompt "What is the Show Title?")
	set showtitle to result as text
	display dialog "Short Description" default answer "EnterShortDescriptionHere"
	set shortdescription to text returned of result
	display dialog "Date" default answer "MMDDYY"
	set dateofclip to text returned of result
	display dialog "Camera Letter?" default answer "CamA"
	set cameraletter to text returned of result
	(choose from list {"INT", "MASTER", "HOST", "SHOOT"} ¬
		with prompt "Category")
	set category to result as text
	global cardName
	set cardName to showtitle & "_" & shortdescription & "_" & dateofclip & "_" & cameraletter & "_" & category
	display dialog cardName
	
	-- Create a Backup of the Card
	tell application "Finder"
		make new folder at (path to desktop) with properties {name:(cardName as string) & "_CardBackup"}
		set cardBackupPath to (path to desktop as string) & (cardName as string) & "_CardBackup" as string as alias
		set theItem to these_items
		set itemPath to quoted form of POSIX path of theItem
		set fileName to cardName
		set theFolder to POSIX path of (container of theItem as alias)
		set zipFile to quoted form of (theFolder & fileName & ".zip")
		do shell script "zip -r " & zipFile & " " & "Macintosh HD:Users:myself:Desktop:CardBackups"
	end tell
	
	-- Parse out the media files from the card and send to a watchfolder
	repeat with aFile in these_items
		tell application "System Events"
			if name extension of aFile is in movieFiles then
				move aFile to "Macintosh HD:Users:myself:Desktop:Watchfolder"
			end if
		end tell
	end repeat
end open

Hi.

Not bad for a first week with AppleScript. :slight_smile:

‘choose from list’ returns either a list or ‘false’. Coercing these results to text is generally frowned upon, although you can get away with it if only one item is chosen. While the “Cancel” button in most types of dialog stops a script immediately, the one in ‘choose from list’ uniquely returns ‘false’, so you need to put in a catch for that.

on open these_items
	set theChoice to (choose from list {"SHOW_A", "SHOW_B", "SHOW_C"} ¬
		with prompt "What is the Show Title?")
	if (theChoice is false) then error number -128 -- The "User Canceled" error generated by other dialogs' "Cancel" buttons.
	set showtitle to item 1 of theChoice
	
	-- The 'display dialog's are OK.
	
	set theChoice to (choose from list {"INT", "MASTER", "HOST", "SHOOT"} ¬
		with prompt "Category")
	if (theChoice is false) then error number -128
	set category to item 1 of theChoice
	
	-- Rest of script.
end open

There’s no need to make cardName a global. And since the value you give it is already a string (or text, as we call it nowadays), there’s no need for the ‘as string’ used with it twice later on.

The Finder has its own reference to the desktop, so the StandardAdditions ‘path to desktop’ command needn’t be used here. Also, the Finder’s ‘make’ command returns a reference to the item created, so you could simply coerce this result to alias in the following line to get cardBackupPath.

tell application "Finder"
	set cardBackupFolder to (make new folder at desktop with properties {name:cardName & "_CardBackup"})
	set cardBackupPath to cardBackupFolder as alias
	-- etc.
end tell

However, since neither cardBackupPath nor the new folder are ever mentioned again, I’m not sure what the point is. Apart from that, only the ‘container’ line needs to be addressed to the Finder, since that’s the only other Finder thing in the script.

The parameter of an ‘open’ handler (these_items in this case) is a list of the dropped items. Even when there’s only one item, it’s a list containing that item. A couple of your commands fail because they’re trying to get the POSIX path and container of the list (which doesn’t have such properties), not of the item it contains. (You’ve set theItem to these_items.)

The ‘do shell script’ command will need both path parameters to be POSIX paths. “Macintosh HD:Users:myself:Desktop:CardBackups” is an HFS path.

I haven’t tried running the script, but I expect that the ‘move’ line near the bottom will fail because it tries to move aFile to the text “Macintosh HD:Users:myself:Desktop:CardBackups” rather than to a folder with that path. The path should be preceded by the keyword ‘folder’ to turn in into a folder specifier. But in fact System Events also has its own term for the desktop ” ‘desktop folder’ ” so a better version might be:

repeat with aFile in these_items
	tell application "System Events"
		if name extension of aFile is in movieFiles then
			move aFile to folder "Watchfolder" of desktop folder
		end if
	end tell
end repeat

What happens when someone drops folders and/or loose files instead of a camera card? Are there multiple archives to be made or should the script abort?

If I understand your intent, you want to zip to a backup folder. If you want to use zip, you need to duplicate in destination to source order and use the joption to prevent the archive from having superfluous directory stuctures. You also need to create the backup directory, if it doesn’t already exist. If you use ditto, it can create the missing directory for you and is (more logically) arranged in source to destination order.

on open (these_items) --an alias list
	
	repeat with anItem in these_items
		
		#Prepare long name
		set showtitle to (choose from list {"SHOW_A", "SHOW_B", "SHOW_C"} with prompt "What is the Show Title?")'s item 1
		set shortdescription to (display dialog "Short Description" default answer "EnterShortDescriptionHere")'s text returned
		set dateofclip to (display dialog "Date" default answer "MMDDYY")'s text returned
		set cameraletter to (display dialog "Camera Letter?" default answer "CamA")'s text returned
		set category to (choose from list {"INT", "MASTER", "HOST", "SHOOT"} with prompt "Category")'s item 1
		set cardName to showtitle & "_" & shortdescription & "_" & dateofclip & "_" & cameraletter & "_" & category
		
		#Create an archive
		do shell script "ditto -ck " & (anItem's POSIX path)'s quoted form & space & (((path to desktop) as text) & "CardBackups:" & cardName & ".zip")'s POSIX path's quoted form
		
	end repeat
end open

I have further questions about the feasibility of the move, as it seems to rely on the event that the files are either loose or just one level down, rather than deeply nested. You either need to recurse all the folders or use a higher level method, such as a shell find or some ASObjC fanciness.

Edit: Here is an easy method, but using entire contents may be slow with many files or folders. This assumes the destination folder exists.

#Filter and move
		tell application "Finder" to if anItem's kind is in {"Folder", "Volume"} then
			move (anItem's entire contents's files whose name extension is in {"mov", "m4v", "MXF", "mp4"}) to folder "Watchfolder" --easy method may be slow with many files/folders
		else
			if anItem's name extension is in {"mov", "m4v", "MXF", "mp4"} then move anItem to folder "Watchfolder"
		end if

Thank you, Marc and Nigel!

After processing your advice I was able to learn a lot and move my script further! I decided to do a copy instead of a .zip in the interest of time for the user. I seem to be stuck though on how to identify the camera card or folder they are dropping on top of the droplet. Since they are dragging what is a container onto the droplet, how do I refer to it?

Here’s what I have that doesn’t work

-- Create a Backup of the Card
	tell application "Finder"
		set cardBackupFolder to (make new folder at desktop with properties {name:cardName & "_CardBackup"})
		set source to the container of these_items
		set destination to cardBackupFolder
		duplicate the entire contents of source to destination
	end tell

Apart from that, I added renaming script I found here

	-- Rename media files
	tell application "Finder"
		set all_videos to every item of folder "TEMP_FILES" of desktop as list
		repeat with i from 1 to the count of all_videos
			set this_video to item i of all_videos
			set the name of this_video to cardName & "_" & i & "." & name extension of this_video as string
		end repeat
	end tell

This works but I don’t know how to zero pad them when I’m at this part of the code.

Anyway, I’m pretty close! Now this makes me want to try all sorts of crazy stuff. Thanks again for your replies and especially for your extremely helpful explanaitions.

Hi.

I don’t know what changes you’ve made to your script, but it looks as if you may have missed this in my previous post:

If theseItems is still the parameter from on open theseItems, then it’s still a list. You need to get each item (or just one) from the list and get the container of that. If there’s only one item, or you know all the items are in the same container, you could probably just change .

set source to the container of these_items

. to .

set source to the container of item 1 of these_items

There are a few ways to do this. My particular favourite is:

text -3 thru -1 of ((100000000 + i) as text) -- Padding to three digits. 

The -3 can be anything from -8 to -1, depending on the number of digits required. It doesn’t work for more than eight digits.

In addition to Nigel’s comments… why are you trying to back up the container and not the directly dropped item? If that item is the SD card itself, its container will be the computer. Entire contents is used to get a target’s children; it was needed for my whose filter example, but it’s implied for duplication and is not needed.

Thanks again! I got my script up and running! Everyone is very impressed and (most importantly) I’ve got more time to do the things I like! I’ll put in the code exchange shortly!